180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney/*********************************************************************
280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * Author: Cavium Networks
380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney *
480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * Contact: support@caviumnetworks.com
580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * This file is part of the OCTEON SDK
680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney *
7166bdaa9aad9903bf4330ef68feb37f220c9eac8David Daney * Copyright (c) 2003-2010 Cavium Networks
880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney *
980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * This file is free software; you can redistribute it and/or modify
1080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * it under the terms of the GNU General Public License, Version 2, as
1180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * published by the Free Software Foundation.
1280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney *
1380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * This file is distributed in the hope that it will be useful, but
1480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
1580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
1680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * NONINFRINGEMENT.  See the GNU General Public License for more
1780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * details.
1880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney *
1980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * You should have received a copy of the GNU General Public License
2080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * along with this file; if not, write to the Free Software
2180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * or visit http://www.gnu.org/licenses/.
2380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney *
2480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * This file may also be available under a different license from Cavium.
2580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * Contact Cavium Networks for more information
2680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney*********************************************************************/
2780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <linux/module.h>
2880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <linux/kernel.h>
2980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <linux/netdevice.h>
3080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <linux/init.h>
3180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <linux/etherdevice.h>
3280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <linux/ip.h>
337a2eaf9358250706672783eb8511835706b0922bChristian Dietrich#include <linux/ratelimit.h>
3480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <linux/string.h>
35dc890df0a77cafe5f4a3d81c0dade637c27f1934Imre Kaloz#include <linux/interrupt.h>
3680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <net/dst.h>
3780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#ifdef CONFIG_XFRM
3880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <linux/xfrm.h>
3980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <net/xfrm.h>
4080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif /* CONFIG_XFRM */
4180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
4260063497a95e716c9a689af3be2687d261f115b4Arun Sharma#include <linux/atomic.h>
4380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
4480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include <asm/octeon/octeon.h>
4580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
4680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include "ethernet-defines.h"
4780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include "octeon-ethernet.h"
48a620c1632629b42369e78448acc7b384fe1faf48David Daney#include "ethernet-tx.h"
4980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#include "ethernet-util.h"
5080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
51af866496c7752d2c0bd97fcbb4627cac72aa9a64David Daney#include <asm/octeon/cvmx-wqe.h>
52af866496c7752d2c0bd97fcbb4627cac72aa9a64David Daney#include <asm/octeon/cvmx-fau.h>
53af866496c7752d2c0bd97fcbb4627cac72aa9a64David Daney#include <asm/octeon/cvmx-pip.h>
54af866496c7752d2c0bd97fcbb4627cac72aa9a64David Daney#include <asm/octeon/cvmx-pko.h>
55af866496c7752d2c0bd97fcbb4627cac72aa9a64David Daney#include <asm/octeon/cvmx-helper.h>
5680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
57af866496c7752d2c0bd97fcbb4627cac72aa9a64David Daney#include <asm/octeon/cvmx-gmxx-defs.h>
5880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
59924cc2680fbe181066ec138d369691d28d913ea2David Daney#define CVM_OCT_SKB_CB(skb)	((u64 *)((skb)->cb))
60924cc2680fbe181066ec138d369691d28d913ea2David Daney
6180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney/*
6280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * You can define GET_SKBUFF_QOS() to override how the skbuff output
6380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * function determines which output queue is used. The default
6480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * implementation always uses the base queue for the port. If, for
6580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * example, you wanted to use the skb->priority fieid, define
6680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * GET_SKBUFF_QOS as: #define GET_SKBUFF_QOS(skb) ((skb)->priority)
6780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney */
6880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#ifndef GET_SKBUFF_QOS
6980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#define GET_SKBUFF_QOS(skb) 0
7080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif
7180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
724898c560103fb8075c10a8e9d70e0ca26873075eDavid Daneystatic void cvm_oct_tx_do_cleanup(unsigned long arg);
734898c560103fb8075c10a8e9d70e0ca26873075eDavid Daneystatic DECLARE_TASKLET(cvm_oct_tx_cleanup_tasklet, cvm_oct_tx_do_cleanup, 0);
744898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
754898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney/* Maximum number of SKBs to try to free per xmit packet. */
764898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney#define MAX_SKB_TO_FREE (MAX_OUT_QUEUE_DEPTH * 2)
776888fc87768eaa218b6244f2e78c55416706981aDavid Daney
786888fc87768eaa218b6244f2e78c55416706981aDavid Daneystatic inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau)
796888fc87768eaa218b6244f2e78c55416706981aDavid Daney{
806888fc87768eaa218b6244f2e78c55416706981aDavid Daney	int32_t undo;
816888fc87768eaa218b6244f2e78c55416706981aDavid Daney	undo = skb_to_free > 0 ? MAX_SKB_TO_FREE : skb_to_free + MAX_SKB_TO_FREE;
826888fc87768eaa218b6244f2e78c55416706981aDavid Daney	if (undo > 0)
836888fc87768eaa218b6244f2e78c55416706981aDavid Daney		cvmx_fau_atomic_add32(fau, -undo);
846888fc87768eaa218b6244f2e78c55416706981aDavid Daney	skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ? MAX_SKB_TO_FREE : -skb_to_free;
856888fc87768eaa218b6244f2e78c55416706981aDavid Daney	return skb_to_free;
866888fc87768eaa218b6244f2e78c55416706981aDavid Daney}
876888fc87768eaa218b6244f2e78c55416706981aDavid Daney
884898c560103fb8075c10a8e9d70e0ca26873075eDavid Daneystatic void cvm_oct_kick_tx_poll_watchdog(void)
894898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney{
904898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	union cvmx_ciu_timx ciu_timx;
914898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	ciu_timx.u64 = 0;
924898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	ciu_timx.s.one_shot = 1;
934898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	ciu_timx.s.len = cvm_oct_tx_poll_interval;
944898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	cvmx_write_csr(CVMX_CIU_TIMX(1), ciu_timx.u64);
954898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney}
964898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
974898c560103fb8075c10a8e9d70e0ca26873075eDavid Daneyvoid cvm_oct_free_tx_skbs(struct net_device *dev)
986888fc87768eaa218b6244f2e78c55416706981aDavid Daney{
996888fc87768eaa218b6244f2e78c55416706981aDavid Daney	int32_t skb_to_free;
1006888fc87768eaa218b6244f2e78c55416706981aDavid Daney	int qos, queues_per_port;
1014898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	int total_freed = 0;
1024898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	int total_remaining = 0;
1034898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	unsigned long flags;
1044898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	struct octeon_ethernet *priv = netdev_priv(dev);
1054898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
1066888fc87768eaa218b6244f2e78c55416706981aDavid Daney	queues_per_port = cvmx_pko_get_num_queues(priv->port);
1076888fc87768eaa218b6244f2e78c55416706981aDavid Daney	/* Drain any pending packets in the free list */
1086888fc87768eaa218b6244f2e78c55416706981aDavid Daney	for (qos = 0; qos < queues_per_port; qos++) {
1096888fc87768eaa218b6244f2e78c55416706981aDavid Daney		if (skb_queue_len(&priv->tx_free_list[qos]) == 0)
1106888fc87768eaa218b6244f2e78c55416706981aDavid Daney			continue;
1116888fc87768eaa218b6244f2e78c55416706981aDavid Daney		skb_to_free = cvmx_fau_fetch_and_add32(priv->fau+qos*4, MAX_SKB_TO_FREE);
1126888fc87768eaa218b6244f2e78c55416706981aDavid Daney		skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4);
1136888fc87768eaa218b6244f2e78c55416706981aDavid Daney
1144898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
1154898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		total_freed += skb_to_free;
1164898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		if (skb_to_free > 0) {
1174898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			struct sk_buff *to_free_list = NULL;
1184898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
1194898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			while (skb_to_free > 0) {
1204898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney				struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]);
1214898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney				t->next = to_free_list;
1224898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney				to_free_list = t;
1234898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney				skb_to_free--;
1244898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			}
1254898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
1264898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			/* Do the actual freeing outside of the lock. */
1274898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			while (to_free_list) {
1284898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney				struct sk_buff *t = to_free_list;
1294898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney				to_free_list = to_free_list->next;
1304898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney				dev_kfree_skb_any(t);
1314898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			}
1326888fc87768eaa218b6244f2e78c55416706981aDavid Daney		}
1334898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		total_remaining += skb_queue_len(&priv->tx_free_list[qos]);
1346888fc87768eaa218b6244f2e78c55416706981aDavid Daney	}
1354898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	if (total_freed >= 0 && netif_queue_stopped(dev))
1366888fc87768eaa218b6244f2e78c55416706981aDavid Daney		netif_wake_queue(dev);
1374898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	if (total_remaining)
1384898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		cvm_oct_kick_tx_poll_watchdog();
1396888fc87768eaa218b6244f2e78c55416706981aDavid Daney}
1406888fc87768eaa218b6244f2e78c55416706981aDavid Daney
14180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney/**
142ec977c5b473e29dbfdac8f2c7477eccc2142e3bcDavid Daney * cvm_oct_xmit - transmit a packet
14380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * @skb:    Packet to send
14480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * @dev:    Device info structure
145ec977c5b473e29dbfdac8f2c7477eccc2142e3bcDavid Daney *
146ec977c5b473e29dbfdac8f2c7477eccc2142e3bcDavid Daney * Returns Always returns NETDEV_TX_OK
14780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney */
14880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daneyint cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
14980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney{
15080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	cvmx_pko_command_word0_t pko_command;
15180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	union cvmx_buf_ptr hw_buffer;
15280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	uint64_t old_scratch;
15380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	uint64_t old_scratch2;
15480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	int qos;
155924cc2680fbe181066ec138d369691d28d913ea2David Daney	int i;
1566888fc87768eaa218b6244f2e78c55416706981aDavid Daney	enum {QUEUE_CORE, QUEUE_HW, QUEUE_DROP} queue_type;
15780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	struct octeon_ethernet *priv = netdev_priv(dev);
1586888fc87768eaa218b6244f2e78c55416706981aDavid Daney	struct sk_buff *to_free_list;
159a620c1632629b42369e78448acc7b384fe1faf48David Daney	int32_t skb_to_free;
16080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	int32_t buffers_to_free;
1614898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	u32 total_to_clean;
1626888fc87768eaa218b6244f2e78c55416706981aDavid Daney	unsigned long flags;
16380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#if REUSE_SKBUFFS_WITHOUT_FREE
16480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	unsigned char *fpa_head;
16580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif
16680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
16780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/*
16880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * Prefetch the private data structure.  It is larger that one
16980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * cache line.
17080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
17180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	prefetch(priv);
17280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
17380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/*
17480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to
17580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * completely remove "qos" in the event neither interface
17680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * supports multiple queues per port.
17780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
17880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if ((CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 > 1) ||
17980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	    (CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 > 1)) {
18080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		qos = GET_SKBUFF_QOS(skb);
18180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		if (qos <= 0)
18280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney			qos = 0;
18380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		else if (qos >= cvmx_pko_get_num_queues(priv->port))
18480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney			qos = 0;
18580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	} else
18680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		qos = 0;
18780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
18880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (USE_ASYNC_IOBDMA) {
18980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* Save scratch in case userspace is using it */
19080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		CVMX_SYNCIOBDMA;
19180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
19280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);
19380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
19480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
195a620c1632629b42369e78448acc7b384fe1faf48David Daney		 * Fetch and increment the number of packets to be
196a620c1632629b42369e78448acc7b384fe1faf48David Daney		 * freed.
19780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
19880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH + 8,
19980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney					       FAU_NUM_PACKET_BUFFERS_TO_FREE,
20080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney					       0);
20180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH,
202a620c1632629b42369e78448acc7b384fe1faf48David Daney					       priv->fau + qos * 4,
203a620c1632629b42369e78448acc7b384fe1faf48David Daney					       MAX_SKB_TO_FREE);
20480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
20580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
20680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/*
207924cc2680fbe181066ec138d369691d28d913ea2David Daney	 * We have space for 6 segment pointers, If there will be more
208924cc2680fbe181066ec138d369691d28d913ea2David Daney	 * than that, we must linearize.
209924cc2680fbe181066ec138d369691d28d913ea2David Daney	 */
210924cc2680fbe181066ec138d369691d28d913ea2David Daney	if (unlikely(skb_shinfo(skb)->nr_frags > 5)) {
211924cc2680fbe181066ec138d369691d28d913ea2David Daney		if (unlikely(__skb_linearize(skb))) {
212924cc2680fbe181066ec138d369691d28d913ea2David Daney			queue_type = QUEUE_DROP;
213924cc2680fbe181066ec138d369691d28d913ea2David Daney			if (USE_ASYNC_IOBDMA) {
214924cc2680fbe181066ec138d369691d28d913ea2David Daney				/* Get the number of skbuffs in use by the hardware */
215924cc2680fbe181066ec138d369691d28d913ea2David Daney				CVMX_SYNCIOBDMA;
216924cc2680fbe181066ec138d369691d28d913ea2David Daney				skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
217924cc2680fbe181066ec138d369691d28d913ea2David Daney			} else {
218924cc2680fbe181066ec138d369691d28d913ea2David Daney				/* Get the number of skbuffs in use by the hardware */
219924cc2680fbe181066ec138d369691d28d913ea2David Daney				skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4,
220924cc2680fbe181066ec138d369691d28d913ea2David Daney								       MAX_SKB_TO_FREE);
221924cc2680fbe181066ec138d369691d28d913ea2David Daney			}
222924cc2680fbe181066ec138d369691d28d913ea2David Daney			skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau + qos * 4);
223924cc2680fbe181066ec138d369691d28d913ea2David Daney			spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
224924cc2680fbe181066ec138d369691d28d913ea2David Daney			goto skip_xmit;
225924cc2680fbe181066ec138d369691d28d913ea2David Daney		}
226924cc2680fbe181066ec138d369691d28d913ea2David Daney	}
227924cc2680fbe181066ec138d369691d28d913ea2David Daney
228924cc2680fbe181066ec138d369691d28d913ea2David Daney	/*
22980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * The CN3XXX series of parts has an errata (GMX-401) which
23080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * causes the GMX block to hang if a collision occurs towards
23180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * the end of a <68 byte packet. As a workaround for this, we
23280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * pad packets to be 68 bytes whenever we are in half duplex
23380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * mode. We don't handle the case of having a small packet but
23480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * no room to add the padding.  The kernel should always give
23580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * us at least a cache line
23680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
23780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if ((skb->len < 64) && OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
23880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		union cvmx_gmxx_prtx_cfg gmx_prt_cfg;
23980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		int interface = INTERFACE(priv->port);
24080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		int index = INDEX(priv->port);
24180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
24280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		if (interface < 2) {
24380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney			/* We only need to pad packet in half duplex mode */
24480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney			gmx_prt_cfg.u64 =
24580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney			    cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
24680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney			if (gmx_prt_cfg.s.duplex == 0) {
24780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney				int add_bytes = 64 - skb->len;
24880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney				if ((skb_tail_pointer(skb) + add_bytes) <=
24980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney				    skb_end_pointer(skb))
25080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney					memset(__skb_put(skb, add_bytes), 0,
25180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney					       add_bytes);
25280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney			}
25380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		}
25480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
25580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
25680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/* Build the PKO command */
25780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	pko_command.u64 = 0;
25880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	pko_command.s.n2 = 1;	/* Don't pollute L2 with the outgoing packet */
25980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	pko_command.s.segs = 1;
26080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	pko_command.s.total_bytes = skb->len;
26180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	pko_command.s.size0 = CVMX_FAU_OP_SIZE_32;
26280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	pko_command.s.subone0 = 1;
26380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
26480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	pko_command.s.dontfree = 1;
265924cc2680fbe181066ec138d369691d28d913ea2David Daney
266924cc2680fbe181066ec138d369691d28d913ea2David Daney	/* Build the PKO buffer pointer */
267924cc2680fbe181066ec138d369691d28d913ea2David Daney	hw_buffer.u64 = 0;
268924cc2680fbe181066ec138d369691d28d913ea2David Daney	if (skb_shinfo(skb)->nr_frags == 0) {
269924cc2680fbe181066ec138d369691d28d913ea2David Daney		hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data);
270924cc2680fbe181066ec138d369691d28d913ea2David Daney		hw_buffer.s.pool = 0;
271924cc2680fbe181066ec138d369691d28d913ea2David Daney		hw_buffer.s.size = skb->len;
272924cc2680fbe181066ec138d369691d28d913ea2David Daney	} else {
273924cc2680fbe181066ec138d369691d28d913ea2David Daney		hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data);
274924cc2680fbe181066ec138d369691d28d913ea2David Daney		hw_buffer.s.pool = 0;
275924cc2680fbe181066ec138d369691d28d913ea2David Daney		hw_buffer.s.size = skb_headlen(skb);
276924cc2680fbe181066ec138d369691d28d913ea2David Daney		CVM_OCT_SKB_CB(skb)[0] = hw_buffer.u64;
277924cc2680fbe181066ec138d369691d28d913ea2David Daney		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
278924cc2680fbe181066ec138d369691d28d913ea2David Daney			struct skb_frag_struct *fs = skb_shinfo(skb)->frags + i;
2798d804d4fdf90b3af62a41a20bf8b21b75529a003David Daney			hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)(page_address(fs->page.p) + fs->page_offset));
280924cc2680fbe181066ec138d369691d28d913ea2David Daney			hw_buffer.s.size = fs->size;
281924cc2680fbe181066ec138d369691d28d913ea2David Daney			CVM_OCT_SKB_CB(skb)[i + 1] = hw_buffer.u64;
282924cc2680fbe181066ec138d369691d28d913ea2David Daney		}
283924cc2680fbe181066ec138d369691d28d913ea2David Daney		hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)CVM_OCT_SKB_CB(skb));
284924cc2680fbe181066ec138d369691d28d913ea2David Daney		hw_buffer.s.size = skb_shinfo(skb)->nr_frags + 1;
285924cc2680fbe181066ec138d369691d28d913ea2David Daney		pko_command.s.segs = skb_shinfo(skb)->nr_frags + 1;
286924cc2680fbe181066ec138d369691d28d913ea2David Daney		pko_command.s.gather = 1;
287924cc2680fbe181066ec138d369691d28d913ea2David Daney		goto dont_put_skbuff_in_hw;
288924cc2680fbe181066ec138d369691d28d913ea2David Daney	}
289924cc2680fbe181066ec138d369691d28d913ea2David Daney
29080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/*
29180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * See if we can put this skb in the FPA pool. Any strange
29280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * behavior from the Linux networking stack will most likely
29380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * be caused by a bug in the following code. If some field is
29480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * in use by the network stack and get carried over when a
29580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * buffer is reused, bad thing may happen.  If in doubt and
29680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * you dont need the absolute best performance, disable the
29780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * define REUSE_SKBUFFS_WITHOUT_FREE. The reuse of buffers has
29880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * shown a 25% increase in performance under some loads.
29980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
30080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#if REUSE_SKBUFFS_WITHOUT_FREE
301166bdaa9aad9903bf4330ef68feb37f220c9eac8David Daney	fpa_head = skb->head + 256 - ((unsigned long)skb->head & 0x7f);
30280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely(skb->data < fpa_head)) {
30380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
30480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 * printk("TX buffer beginning can't meet FPA
30580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 * alignment constraints\n");
30680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
30780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		goto dont_put_skbuff_in_hw;
30880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
30980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely
31080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	    ((skb_end_pointer(skb) - fpa_head) < CVMX_FPA_PACKET_POOL_SIZE)) {
31180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
31280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		   printk("TX buffer isn't large enough for the FPA\n");
31380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
31480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		goto dont_put_skbuff_in_hw;
31580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
31680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely(skb_shared(skb))) {
31780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
31880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		   printk("TX buffer sharing data with someone else\n");
31980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
32080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		goto dont_put_skbuff_in_hw;
32180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
32280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely(skb_cloned(skb))) {
32380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
32480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		   printk("TX buffer has been cloned\n");
32580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
32680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		goto dont_put_skbuff_in_hw;
32780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
32880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely(skb_header_cloned(skb))) {
32980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
33080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		   printk("TX buffer header has been cloned\n");
33180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
33280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		goto dont_put_skbuff_in_hw;
33380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
33480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely(skb->destructor)) {
33580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
33680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		   printk("TX buffer has a destructor\n");
33780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
33880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		goto dont_put_skbuff_in_hw;
33980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
34080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely(skb_shinfo(skb)->nr_frags)) {
34180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
34280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		   printk("TX buffer has fragments\n");
34380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
34480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		goto dont_put_skbuff_in_hw;
34580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
34680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely
34780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	    (skb->truesize !=
34880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	     sizeof(*skb) + skb_end_pointer(skb) - skb->head)) {
34980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
35080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		   printk("TX buffer truesize has been changed\n");
35180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
35280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		goto dont_put_skbuff_in_hw;
35380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
35480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
35580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/*
35680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * We can use this buffer in the FPA.  We don't need the FAU
35780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * update anymore
35880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
35980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	pko_command.s.dontfree = 0;
36080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
361166bdaa9aad9903bf4330ef68feb37f220c9eac8David Daney	hw_buffer.s.back = ((unsigned long)skb->data >> 7) - ((unsigned long)fpa_head >> 7);
36280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	*(struct sk_buff **)(fpa_head - sizeof(void *)) = skb;
36380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
36480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/*
36580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * The skbuff will be reused without ever being freed. We must
366f696a10838ffab85e5bc07e7cff0d0e1870a30d7David Daney	 * cleanup a bunch of core things.
36780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
368f696a10838ffab85e5bc07e7cff0d0e1870a30d7David Daney	dst_release(skb_dst(skb));
369f696a10838ffab85e5bc07e7cff0d0e1870a30d7David Daney	skb_dst_set(skb, NULL);
37080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#ifdef CONFIG_XFRM
37180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	secpath_put(skb->sp);
37280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	skb->sp = NULL;
37380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif
37480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	nf_reset(skb);
37580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
37680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#ifdef CONFIG_NET_SCHED
37780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	skb->tc_index = 0;
37880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#ifdef CONFIG_NET_CLS_ACT
37980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	skb->tc_verd = 0;
38080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif /* CONFIG_NET_CLS_ACT */
38180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif /* CONFIG_NET_SCHED */
3826888fc87768eaa218b6244f2e78c55416706981aDavid Daney#endif /* REUSE_SKBUFFS_WITHOUT_FREE */
38380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
38480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daneydont_put_skbuff_in_hw:
38580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
38680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/* Check if we can use the hardware checksumming */
38780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (USE_HW_TCPUDP_CHECKSUM && (skb->protocol == htons(ETH_P_IP)) &&
38880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	    (ip_hdr(skb)->version == 4) && (ip_hdr(skb)->ihl == 5) &&
38980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	    ((ip_hdr(skb)->frag_off == 0) || (ip_hdr(skb)->frag_off == 1 << 14))
390081f6749ae33f72b4fafea4c02976e163ef6ef37David Daney	    && ((ip_hdr(skb)->protocol == IPPROTO_TCP)
391081f6749ae33f72b4fafea4c02976e163ef6ef37David Daney		|| (ip_hdr(skb)->protocol == IPPROTO_UDP))) {
39280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* Use hardware checksum calc */
39380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		pko_command.s.ipoffp1 = sizeof(struct ethhdr) + 1;
39480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
39580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
39680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (USE_ASYNC_IOBDMA) {
39780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* Get the number of skbuffs in use by the hardware */
39880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		CVMX_SYNCIOBDMA;
399a620c1632629b42369e78448acc7b384fe1faf48David Daney		skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
40080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);
40180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	} else {
40280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* Get the number of skbuffs in use by the hardware */
403a620c1632629b42369e78448acc7b384fe1faf48David Daney		skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4,
404a620c1632629b42369e78448acc7b384fe1faf48David Daney						       MAX_SKB_TO_FREE);
40580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		buffers_to_free =
40680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		    cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
40780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
40880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
4096888fc87768eaa218b6244f2e78c55416706981aDavid Daney	skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4);
410a620c1632629b42369e78448acc7b384fe1faf48David Daney
411a620c1632629b42369e78448acc7b384fe1faf48David Daney	/*
41280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * If we're sending faster than the receive can free them then
41380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * don't do the HW free.
41480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
4154898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	if ((buffers_to_free < -100) && !pko_command.s.dontfree)
41680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		pko_command.s.dontfree = 1;
41780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
4184898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	if (pko_command.s.dontfree) {
4196888fc87768eaa218b6244f2e78c55416706981aDavid Daney		queue_type = QUEUE_CORE;
4204898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		pko_command.s.reg0 = priv->fau+qos*4;
4214898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	} else {
4226888fc87768eaa218b6244f2e78c55416706981aDavid Daney		queue_type = QUEUE_HW;
4234898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	}
4244898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	if (USE_ASYNC_IOBDMA)
4254898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, FAU_TOTAL_TX_TO_CLEAN, 1);
4266888fc87768eaa218b6244f2e78c55416706981aDavid Daney
4276888fc87768eaa218b6244f2e78c55416706981aDavid Daney	spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
42880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
42980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/* Drop this packet if we have too many already queued to the HW */
4306888fc87768eaa218b6244f2e78c55416706981aDavid Daney	if (unlikely(skb_queue_len(&priv->tx_free_list[qos]) >= MAX_OUT_QUEUE_DEPTH)) {
4316888fc87768eaa218b6244f2e78c55416706981aDavid Daney		if (dev->tx_queue_len != 0) {
4326888fc87768eaa218b6244f2e78c55416706981aDavid Daney			/* Drop the lock when notifying the core.  */
4336888fc87768eaa218b6244f2e78c55416706981aDavid Daney			spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
4346888fc87768eaa218b6244f2e78c55416706981aDavid Daney			netif_stop_queue(dev);
4356888fc87768eaa218b6244f2e78c55416706981aDavid Daney			spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
4366888fc87768eaa218b6244f2e78c55416706981aDavid Daney		} else {
4376888fc87768eaa218b6244f2e78c55416706981aDavid Daney			/* If not using normal queueing.  */
4386888fc87768eaa218b6244f2e78c55416706981aDavid Daney			queue_type = QUEUE_DROP;
4396888fc87768eaa218b6244f2e78c55416706981aDavid Daney			goto skip_xmit;
4406888fc87768eaa218b6244f2e78c55416706981aDavid Daney		}
44180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
4426888fc87768eaa218b6244f2e78c55416706981aDavid Daney
4436888fc87768eaa218b6244f2e78c55416706981aDavid Daney	cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos,
4446888fc87768eaa218b6244f2e78c55416706981aDavid Daney				     CVMX_PKO_LOCK_NONE);
4456888fc87768eaa218b6244f2e78c55416706981aDavid Daney
44680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/* Send the packet to the output queue */
4476888fc87768eaa218b6244f2e78c55416706981aDavid Daney	if (unlikely(cvmx_pko_send_packet_finish(priv->port,
4486888fc87768eaa218b6244f2e78c55416706981aDavid Daney						 priv->queue + qos,
4496888fc87768eaa218b6244f2e78c55416706981aDavid Daney						 pko_command, hw_buffer,
4506888fc87768eaa218b6244f2e78c55416706981aDavid Daney						 CVMX_PKO_LOCK_NONE))) {
4517a2eaf9358250706672783eb8511835706b0922bChristian Dietrich		printk_ratelimited("%s: Failed to send the packet\n", dev->name);
4526888fc87768eaa218b6244f2e78c55416706981aDavid Daney		queue_type = QUEUE_DROP;
45380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
4546888fc87768eaa218b6244f2e78c55416706981aDavid Daneyskip_xmit:
4556888fc87768eaa218b6244f2e78c55416706981aDavid Daney	to_free_list = NULL;
45680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
4576888fc87768eaa218b6244f2e78c55416706981aDavid Daney	switch (queue_type) {
4586888fc87768eaa218b6244f2e78c55416706981aDavid Daney	case QUEUE_DROP:
4596888fc87768eaa218b6244f2e78c55416706981aDavid Daney		skb->next = to_free_list;
4606888fc87768eaa218b6244f2e78c55416706981aDavid Daney		to_free_list = skb;
4616888fc87768eaa218b6244f2e78c55416706981aDavid Daney		priv->stats.tx_dropped++;
4626888fc87768eaa218b6244f2e78c55416706981aDavid Daney		break;
4636888fc87768eaa218b6244f2e78c55416706981aDavid Daney	case QUEUE_HW:
4646888fc87768eaa218b6244f2e78c55416706981aDavid Daney		cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, -1);
4656888fc87768eaa218b6244f2e78c55416706981aDavid Daney		break;
4666888fc87768eaa218b6244f2e78c55416706981aDavid Daney	case QUEUE_CORE:
4676888fc87768eaa218b6244f2e78c55416706981aDavid Daney		__skb_queue_tail(&priv->tx_free_list[qos], skb);
4686888fc87768eaa218b6244f2e78c55416706981aDavid Daney		break;
4696888fc87768eaa218b6244f2e78c55416706981aDavid Daney	default:
4706888fc87768eaa218b6244f2e78c55416706981aDavid Daney		BUG();
47180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
47280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
4736888fc87768eaa218b6244f2e78c55416706981aDavid Daney	while (skb_to_free > 0) {
4746888fc87768eaa218b6244f2e78c55416706981aDavid Daney		struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]);
4756888fc87768eaa218b6244f2e78c55416706981aDavid Daney		t->next = to_free_list;
4766888fc87768eaa218b6244f2e78c55416706981aDavid Daney		to_free_list = t;
4776888fc87768eaa218b6244f2e78c55416706981aDavid Daney		skb_to_free--;
47880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
47980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
4806888fc87768eaa218b6244f2e78c55416706981aDavid Daney	spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
4816888fc87768eaa218b6244f2e78c55416706981aDavid Daney
4826888fc87768eaa218b6244f2e78c55416706981aDavid Daney	/* Do the actual freeing outside of the lock. */
4836888fc87768eaa218b6244f2e78c55416706981aDavid Daney	while (to_free_list) {
4846888fc87768eaa218b6244f2e78c55416706981aDavid Daney		struct sk_buff *t = to_free_list;
4856888fc87768eaa218b6244f2e78c55416706981aDavid Daney		to_free_list = to_free_list->next;
4866888fc87768eaa218b6244f2e78c55416706981aDavid Daney		dev_kfree_skb_any(t);
4876888fc87768eaa218b6244f2e78c55416706981aDavid Daney	}
4886888fc87768eaa218b6244f2e78c55416706981aDavid Daney
4896888fc87768eaa218b6244f2e78c55416706981aDavid Daney	if (USE_ASYNC_IOBDMA) {
4904898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		CVMX_SYNCIOBDMA;
4914898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		total_to_clean = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
4926888fc87768eaa218b6244f2e78c55416706981aDavid Daney		/* Restore the scratch area */
4936888fc87768eaa218b6244f2e78c55416706981aDavid Daney		cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
4946888fc87768eaa218b6244f2e78c55416706981aDavid Daney		cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2);
4954898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	} else {
4964898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		total_to_clean = cvmx_fau_fetch_and_add32(FAU_TOTAL_TX_TO_CLEAN, 1);
49780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
49880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
4994898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	if (total_to_clean & 0x3ff) {
5004898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		/*
5014898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		 * Schedule the cleanup tasklet every 1024 packets for
5024898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		 * the pathological case of high traffic on one port
5034898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		 * delaying clean up of packets on a different port
5044898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		 * that is blocked waiting for the cleanup.
5054898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		 */
5064898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
5074898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	}
5084898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
5094898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	cvm_oct_kick_tx_poll_watchdog();
5104898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
5116888fc87768eaa218b6244f2e78c55416706981aDavid Daney	return NETDEV_TX_OK;
51280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney}
51380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
51480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney/**
515ec977c5b473e29dbfdac8f2c7477eccc2142e3bcDavid Daney * cvm_oct_xmit_pow - transmit a packet to the POW
51680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * @skb:    Packet to send
51780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * @dev:    Device info structure
518ec977c5b473e29dbfdac8f2c7477eccc2142e3bcDavid Daney
51980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * Returns Always returns zero
52080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney */
52180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daneyint cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
52280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney{
52380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	struct octeon_ethernet *priv = netdev_priv(dev);
52480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	void *packet_buffer;
52580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	void *copy_location;
52680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
52780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/* Get a work queue entry */
52880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	cvmx_wqe_t *work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL);
52980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely(work == NULL)) {
5307a2eaf9358250706672783eb8511835706b0922bChristian Dietrich		printk_ratelimited("%s: Failed to allocate a work "
5317a2eaf9358250706672783eb8511835706b0922bChristian Dietrich				   "queue entry\n", dev->name);
53280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		priv->stats.tx_dropped++;
53380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		dev_kfree_skb(skb);
53480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		return 0;
53580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
53680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
53780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/* Get a packet buffer */
53880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	packet_buffer = cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL);
53980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (unlikely(packet_buffer == NULL)) {
5407a2eaf9358250706672783eb8511835706b0922bChristian Dietrich		printk_ratelimited("%s: Failed to allocate a packet buffer\n",
5417a2eaf9358250706672783eb8511835706b0922bChristian Dietrich				   dev->name);
54280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
54380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		priv->stats.tx_dropped++;
54480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		dev_kfree_skb(skb);
54580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		return 0;
54680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
54780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
54880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/*
54980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * Calculate where we need to copy the data to. We need to
55080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * leave 8 bytes for a next pointer (unused). We also need to
55180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * include any configure skip. Then we need to align the IP
55280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * packet src and dest into the same 64bit word. The below
55380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * calculation may add a little extra, but that doesn't
55480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * hurt.
55580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
55680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	copy_location = packet_buffer + sizeof(uint64_t);
55780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	copy_location += ((CVMX_HELPER_FIRST_MBUFF_SKIP + 7) & 0xfff8) + 6;
55880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
55980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/*
56080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * We have to copy the packet since whoever processes this
56180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * packet will free it to a hardware pool. We can't use the
56280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * trick of counting outstanding packets like in
56380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * cvm_oct_xmit.
56480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
56580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	memcpy(copy_location, skb->data, skb->len);
56680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
56780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/*
56880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * Fill in some of the work queue fields. We may need to add
56980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 * more if the software at the other end needs them.
57080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	 */
57180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->hw_chksum = skb->csum;
57280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->len = skb->len;
57380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->ipprt = priv->port;
57480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->qos = priv->port & 0x7;
57580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->grp = pow_send_group;
57680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
57780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->tag = pow_send_group;	/* FIXME */
57880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/* Default to zero. Sets of zero later are commented out */
57980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->word2.u64 = 0;
58080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->word2.s.bufs = 1;
58180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->packet_ptr.u64 = 0;
58280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->packet_ptr.s.addr = cvmx_ptr_to_phys(copy_location);
58380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->packet_ptr.s.pool = CVMX_FPA_PACKET_POOL;
58480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->packet_ptr.s.size = CVMX_FPA_PACKET_POOL_SIZE;
58580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	work->packet_ptr.s.back = (copy_location - packet_buffer) >> 7;
58680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
58780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	if (skb->protocol == htons(ETH_P_IP)) {
58880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.ip_offset = 14;
58980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#if 0
59080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.vlan_valid = 0;	/* FIXME */
59180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.vlan_cfi = 0;	/* FIXME */
59280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.vlan_id = 0;	/* FIXME */
59380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.dec_ipcomp = 0;	/* FIXME */
59480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif
59580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.tcp_or_udp =
596081f6749ae33f72b4fafea4c02976e163ef6ef37David Daney		    (ip_hdr(skb)->protocol == IPPROTO_TCP)
597081f6749ae33f72b4fafea4c02976e163ef6ef37David Daney		    || (ip_hdr(skb)->protocol == IPPROTO_UDP);
59880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#if 0
59980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* FIXME */
60080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.dec_ipsec = 0;
60180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* We only support IPv4 right now */
60280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.is_v6 = 0;
60380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* Hardware would set to zero */
60480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.software = 0;
60580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* No error, packet is internal */
60680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.L4_error = 0;
60780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif
60880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.is_frag = !((ip_hdr(skb)->frag_off == 0)
60980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney					  || (ip_hdr(skb)->frag_off ==
61080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney					      1 << 14));
61180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#if 0
61280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* Assume Linux is sending a good packet */
61380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.IP_exc = 0;
61480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif
61580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.is_bcast = (skb->pkt_type == PACKET_BROADCAST);
61680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.is_mcast = (skb->pkt_type == PACKET_MULTICAST);
61780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#if 0
61880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* This is an IP packet */
61980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.not_IP = 0;
62080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* No error, packet is internal */
62180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.rcv_error = 0;
62280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* No error, packet is internal */
62380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.s.err_code = 0;
62480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif
62580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
62680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/*
62780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 * When copying the data, include 4 bytes of the
62880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 * ethernet header to align the same way hardware
62980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 * does.
63080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		 */
63180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		memcpy(work->packet_data, skb->data + 10,
63280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		       sizeof(work->packet_data));
63380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	} else {
63480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#if 0
63580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.vlan_valid = 0;	/* FIXME */
63680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.vlan_cfi = 0;	/* FIXME */
63780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.vlan_id = 0;	/* FIXME */
63880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.software = 0;	/* Hardware would set to zero */
63980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif
64080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.is_rarp = skb->protocol == htons(ETH_P_RARP);
64180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.is_arp = skb->protocol == htons(ETH_P_ARP);
64280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.is_bcast =
64380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		    (skb->pkt_type == PACKET_BROADCAST);
64480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.is_mcast =
64580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		    (skb->pkt_type == PACKET_MULTICAST);
64680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.not_IP = 1;	/* IP was done up above */
64780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#if 0
64880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* No error, packet is internal */
64980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.rcv_error = 0;
65080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		/* No error, packet is internal */
65180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		work->word2.snoip.err_code = 0;
65280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney#endif
65380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		memcpy(work->packet_data, skb->data, sizeof(work->packet_data));
65480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
65580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
65680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	/* Submit the packet to the POW */
65780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	cvmx_pow_work_submit(work, work->tag, work->tag_type, work->qos,
65880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney			     work->grp);
65980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	priv->stats.tx_packets++;
66080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	priv->stats.tx_bytes += skb->len;
66180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	dev_kfree_skb(skb);
66280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	return 0;
66380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney}
66480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
66580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney/**
666ec977c5b473e29dbfdac8f2c7477eccc2142e3bcDavid Daney * cvm_oct_tx_shutdown_dev - free all skb that are currently queued for TX.
66780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney * @dev:    Device being shutdown
668ec977c5b473e29dbfdac8f2c7477eccc2142e3bcDavid Daney *
66980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney */
6704898c560103fb8075c10a8e9d70e0ca26873075eDavid Daneyvoid cvm_oct_tx_shutdown_dev(struct net_device *dev)
67180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney{
67280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	struct octeon_ethernet *priv = netdev_priv(dev);
67380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	unsigned long flags;
67480ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	int qos;
67580ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney
67680ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	for (qos = 0; qos < 16; qos++) {
67780ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
67880ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		while (skb_queue_len(&priv->tx_free_list[qos]))
67980ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney			dev_kfree_skb_any(__skb_dequeue
68080ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney					  (&priv->tx_free_list[qos]));
68180ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney		spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
68280ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney	}
68380ff0fd3ab6451407a20c19b80c1643c4a6d6434David Daney}
6844898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
6854898c560103fb8075c10a8e9d70e0ca26873075eDavid Daneystatic void cvm_oct_tx_do_cleanup(unsigned long arg)
6864898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney{
6874898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	int port;
6884898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
6894898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
6904898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		if (cvm_oct_device[port]) {
6914898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			struct net_device *dev = cvm_oct_device[port];
6924898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			cvm_oct_free_tx_skbs(dev);
6934898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		}
6944898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	}
6954898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney}
6964898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
6974898c560103fb8075c10a8e9d70e0ca26873075eDavid Daneystatic irqreturn_t cvm_oct_tx_cleanup_watchdog(int cpl, void *dev_id)
6984898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney{
6994898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	/* Disable the interrupt.  */
7004898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
7014898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	/* Do the work in the tasklet.  */
7024898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
7034898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	return IRQ_HANDLED;
7044898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney}
7054898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
7064898c560103fb8075c10a8e9d70e0ca26873075eDavid Daneyvoid cvm_oct_tx_initialize(void)
7074898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney{
7084898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	int i;
7094898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
7104898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	/* Disable the interrupt.  */
7114898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
7124898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	/* Register an IRQ hander for to receive CIU_TIMX(1) interrupts */
7134898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	i = request_irq(OCTEON_IRQ_TIMER1,
7144898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			cvm_oct_tx_cleanup_watchdog, 0,
7154898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney			"Ethernet", cvm_oct_device);
7164898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
7174898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	if (i)
7184898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney		panic("Could not acquire Ethernet IRQ %d\n", OCTEON_IRQ_TIMER1);
7194898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney}
7204898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney
7214898c560103fb8075c10a8e9d70e0ca26873075eDavid Daneyvoid cvm_oct_tx_shutdown(void)
7224898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney{
7234898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	/* Free the interrupt handler */
7244898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney	free_irq(OCTEON_IRQ_TIMER1, cvm_oct_device);
7254898c560103fb8075c10a8e9d70e0ca26873075eDavid Daney}
726