1/*************************************************************************
2Copyright (c) 2003-2007  Cavium Networks (support@cavium.com). All rights
3reserved.
4
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are
8met:
9
10    * Redistributions of source code must retain the above copyright
11      notice, this list of conditions and the following disclaimer.
12
13    * Redistributions in binary form must reproduce the above
14      copyright notice, this list of conditions and the following
15      disclaimer in the documentation and/or other materials provided
16      with the distribution.
17
18    * Neither the name of Cavium Networks nor the names of
19      its contributors may be used to endorse or promote products
20      derived from this software without specific prior written
21      permission.
22
23This Software, including technical data, may be subject to U.S. export  control laws, including the U.S. Export Administration Act and its  associated regulations, and may be subject to export or import  regulations in other countries.
24
25TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
26AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
27
28*************************************************************************/
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/bus.h>
36#include <sys/endian.h>
37#include <sys/kernel.h>
38#include <sys/mbuf.h>
39#include <sys/socket.h>
40
41#include <net/bpf.h>
42#include <net/ethernet.h>
43#include <net/if.h>
44#include <net/if_var.h>
45
46#include "wrapper-cvmx-includes.h"
47#include "ethernet-headers.h"
48
49/* You can define GET_MBUF_QOS() to override how the mbuf output function
50   determines which output queue is used. The default implementation
51   always uses the base queue for the port. If, for example, you wanted
52   to use the m->priority fieid, define GET_MBUF_QOS as:
53   #define GET_MBUF_QOS(m) ((m)->priority) */
54#ifndef GET_MBUF_QOS
55    #define GET_MBUF_QOS(m) 0
56#endif
57
58
59/**
60 * Packet transmit
61 *
62 * @param m    Packet to send
63 * @param dev    Device info structure
64 * @return Always returns zero
65 */
66int cvm_oct_xmit(struct mbuf *m, struct ifnet *ifp)
67{
68	cvmx_pko_command_word0_t    pko_command;
69	cvmx_buf_ptr_t              hw_buffer;
70	int                         dropped;
71	int                         qos;
72	cvm_oct_private_t          *priv = (cvm_oct_private_t *)ifp->if_softc;
73	int32_t in_use;
74	int32_t buffers_to_free;
75	cvmx_wqe_t *work;
76
77	/* Prefetch the private data structure.
78	   It is larger that one cache line */
79	CVMX_PREFETCH(priv, 0);
80
81	/* Start off assuming no drop */
82	dropped = 0;
83
84	/* The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to completely
85	   remove "qos" in the event neither interface supports multiple queues
86	   per port */
87	if ((CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 > 1) ||
88	    (CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 > 1)) {
89		qos = GET_MBUF_QOS(m);
90		if (qos <= 0)
91			qos = 0;
92		else if (qos >= cvmx_pko_get_num_queues(priv->port))
93			qos = 0;
94	} else
95		qos = 0;
96
97	/* The CN3XXX series of parts has an errata (GMX-401) which causes the
98	   GMX block to hang if a collision occurs towards the end of a
99	   <68 byte packet. As a workaround for this, we pad packets to be
100	   68 bytes whenever we are in half duplex mode. We don't handle
101	   the case of having a small packet but no room to add the padding.
102	   The kernel should always give us at least a cache line */
103	if (__predict_false(m->m_pkthdr.len < 64) && OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
104		cvmx_gmxx_prtx_cfg_t gmx_prt_cfg;
105		int interface = INTERFACE(priv->port);
106		int index = INDEX(priv->port);
107
108		if (interface < 2) {
109			/* We only need to pad packet in half duplex mode */
110			gmx_prt_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
111			if (gmx_prt_cfg.s.duplex == 0) {
112				static uint8_t pad[64];
113
114				if (!m_append(m, sizeof pad - m->m_pkthdr.len, pad))
115					printf("%s: unable to padd small packet.", __func__);
116			}
117		}
118	}
119
120#ifdef OCTEON_VENDOR_RADISYS
121	/*
122	 * The RSYS4GBE will hang if asked to transmit a packet less than 60 bytes.
123	 */
124	if (__predict_false(m->m_pkthdr.len < 60) &&
125	    cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CUST_RADISYS_RSYS4GBE) {
126		static uint8_t pad[60];
127
128		if (!m_append(m, sizeof pad - m->m_pkthdr.len, pad))
129			printf("%s: unable to pad small packet.", __func__);
130	}
131#endif
132
133	/*
134	 * If the packet is not fragmented.
135	 */
136	if (m->m_pkthdr.len == m->m_len) {
137		/* Build the PKO buffer pointer */
138		hw_buffer.u64 = 0;
139		hw_buffer.s.addr = cvmx_ptr_to_phys(m->m_data);
140		hw_buffer.s.pool = 0;
141		hw_buffer.s.size = m->m_len;
142
143		/* Build the PKO command */
144		pko_command.u64 = 0;
145		pko_command.s.segs = 1;
146		pko_command.s.dontfree = 1; /* Do not put this buffer into the FPA.  */
147
148		work = NULL;
149	} else {
150		struct mbuf *n;
151		unsigned segs;
152		uint64_t *gp;
153
154		/*
155		 * The packet is fragmented, we need to send a list of segments
156		 * in memory we borrow from the WQE pool.
157		 */
158		work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL);
159		if (work == NULL) {
160			m_freem(m);
161			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
162			return 1;
163		}
164
165		segs = 0;
166		gp = (uint64_t *)work;
167		for (n = m; n != NULL; n = n->m_next) {
168			if (segs == CVMX_FPA_WQE_POOL_SIZE / sizeof (uint64_t))
169				panic("%s: too many segments in packet; call m_collapse().", __func__);
170
171			/* Build the PKO buffer pointer */
172			hw_buffer.u64 = 0;
173			hw_buffer.s.i = 1; /* Do not put this buffer into the FPA.  */
174			hw_buffer.s.addr = cvmx_ptr_to_phys(n->m_data);
175			hw_buffer.s.pool = 0;
176			hw_buffer.s.size = n->m_len;
177
178			*gp++ = hw_buffer.u64;
179			segs++;
180		}
181
182		/* Build the PKO buffer gather list pointer */
183		hw_buffer.u64 = 0;
184		hw_buffer.s.addr = cvmx_ptr_to_phys(work);
185		hw_buffer.s.pool = CVMX_FPA_WQE_POOL;
186		hw_buffer.s.size = segs;
187
188		/* Build the PKO command */
189		pko_command.u64 = 0;
190		pko_command.s.segs = segs;
191		pko_command.s.gather = 1;
192		pko_command.s.dontfree = 0; /* Put the WQE above back into the FPA.  */
193	}
194
195	/* Finish building the PKO command */
196	pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */
197	pko_command.s.reg0 = priv->fau+qos*4;
198	pko_command.s.total_bytes = m->m_pkthdr.len;
199	pko_command.s.size0 = CVMX_FAU_OP_SIZE_32;
200	pko_command.s.subone0 = 1;
201
202	/* Check if we can use the hardware checksumming */
203	if ((m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) != 0) {
204		/* Use hardware checksum calc */
205		pko_command.s.ipoffp1 = ETHER_HDR_LEN + 1;
206	}
207
208	/*
209	 * XXX
210	 * Could use a different free queue (and different FAU address) per
211	 * core instead of per QoS, to reduce contention here.
212	 */
213	IF_LOCK(&priv->tx_free_queue[qos]);
214	/* Get the number of mbufs in use by the hardware */
215	in_use = cvmx_fau_fetch_and_add32(priv->fau+qos*4, 1);
216	buffers_to_free = cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
217
218	cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos, CVMX_PKO_LOCK_CMD_QUEUE);
219
220	/* Drop this packet if we have too many already queued to the HW */
221	if (_IF_QFULL(&priv->tx_free_queue[qos])) {
222		dropped = 1;
223	}
224	/* Send the packet to the output queue */
225	else
226	if (__predict_false(cvmx_pko_send_packet_finish(priv->port, priv->queue + qos, pko_command, hw_buffer, CVMX_PKO_LOCK_CMD_QUEUE))) {
227		DEBUGPRINT("%s: Failed to send the packet\n", if_name(ifp));
228		dropped = 1;
229	}
230
231	if (__predict_false(dropped)) {
232		m_freem(m);
233		cvmx_fau_atomic_add32(priv->fau+qos*4, -1);
234		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
235	} else {
236		/* Put this packet on the queue to be freed later */
237		_IF_ENQUEUE(&priv->tx_free_queue[qos], m);
238
239		/* Pass it to any BPF listeners.  */
240		ETHER_BPF_MTAP(ifp, m);
241
242		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
243		if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
244	}
245
246	/* Free mbufs not in use by the hardware */
247	if (_IF_QLEN(&priv->tx_free_queue[qos]) > in_use) {
248		while (_IF_QLEN(&priv->tx_free_queue[qos]) > in_use) {
249			_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
250			m_freem(m);
251		}
252	}
253	IF_UNLOCK(&priv->tx_free_queue[qos]);
254
255	return dropped;
256}
257
258
259/**
260 * This function frees all mbufs that are currenty queued for TX.
261 *
262 * @param dev    Device being shutdown
263 */
264void cvm_oct_tx_shutdown(struct ifnet *ifp)
265{
266	cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
267	int qos;
268
269	for (qos = 0; qos < 16; qos++) {
270		IF_DRAIN(&priv->tx_free_queue[qos]);
271	}
272}
273