ethernet.c revision 213150
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#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/mips/cavium/octe/ethernet.c 213150 2010-09-25 01:18:01Z jmallett $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/bus.h>
35#include <sys/conf.h>
36#include <sys/endian.h>
37#include <sys/kernel.h>
38#include <sys/rman.h>
39#include <sys/mbuf.h>
40#include <sys/socket.h>
41#include <sys/module.h>
42#include <sys/smp.h>
43#include <sys/taskqueue.h>
44
45#include <net/ethernet.h>
46#include <net/if.h>
47#include <net/if_types.h>
48
49#include "wrapper-cvmx-includes.h"
50#include "ethernet-headers.h"
51
52#include "octebusvar.h"
53
54/*
55 * XXX/juli
56 * Convert 0444 to tunables, 0644 to sysctls.
57 */
58#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
59int num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS;
60#else
61int num_packet_buffers = 1024;
62#endif
63TUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
64/*
65		 "\t\tNumber of packet buffers to allocate and store in the\n"
66		 "\t\tFPA. By default, 1024 packet buffers are used unless\n"
67		 "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */
68
69int pow_receive_group = 15;
70TUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
71/*
72		 "\t\tPOW group to receive packets from. All ethernet hardware\n"
73		 "\t\twill be configured to send incomming packets to this POW\n"
74		 "\t\tgroup. Also any other software can submit packets to this\n"
75		 "\t\tgroup for the kernel to process." */
76
77int pow_send_group = -1; /* XXX Should be a sysctl.  */
78TUNABLE_INT("hw.octe.pow_send_group", &pow_send_group);
79/*
80		 "\t\tPOW group to send packets to other software on. This\n"
81		 "\t\tcontrols the creation of the virtual device pow0.\n"
82		 "\t\talways_use_pow also depends on this value." */
83
84int always_use_pow;
85TUNABLE_INT("hw.octe.always_use_pow", &always_use_pow);
86/*
87		 "\t\tWhen set, always send to the pow group. This will cause\n"
88		 "\t\tpackets sent to real ethernet devices to be sent to the\n"
89		 "\t\tPOW group instead of the hardware. Unless some other\n"
90		 "\t\tapplication changes the config, packets will still be\n"
91		 "\t\treceived from the low level hardware. Use this option\n"
92		 "\t\tto allow a CVMX app to intercept all packets from the\n"
93		 "\t\tlinux kernel. You must specify pow_send_group along with\n"
94		 "\t\tthis option." */
95
96char pow_send_list[128] = "";
97TUNABLE_STR("hw.octe.pow_send_list", pow_send_list, sizeof pow_send_list);
98/*
99		 "\t\tComma separated list of ethernet devices that should use the\n"
100		 "\t\tPOW for transmit instead of the actual ethernet hardware. This\n"
101		 "\t\tis a per port version of always_use_pow. always_use_pow takes\n"
102		 "\t\tprecedence over this list. For example, setting this to\n"
103		 "\t\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n"
104		 "\t\tusing the pow_send_group." */
105
106
107static int disable_core_queueing = 1;
108TUNABLE_INT("hw.octe.disable_core_queueing", &disable_core_queueing);
109/*
110		"\t\tWhen set the networking core's tx_queue_len is set to zero.  This\n"
111		"\t\tallows packets to be sent without lock contention in the packet scheduler\n"
112		"\t\tresulting in some cases in improved throughput.\n" */
113
114extern int octeon_is_simulation(void);
115
116/**
117 * Exported from the kernel so we can determine board information. It is
118 * passed by the bootloader to the kernel.
119 */
120extern cvmx_bootinfo_t *octeon_bootinfo;
121
122/**
123 * Periodic timer to check auto negotiation
124 */
125static struct callout cvm_oct_poll_timer;
126
127/**
128 * Array of every ethernet device owned by this driver indexed by
129 * the ipd input port number.
130 */
131struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
132
133/**
134 * Task to handle link status changes.
135 */
136static struct taskqueue *cvm_oct_link_taskq;
137
138/**
139 * Function to update link status.
140 */
141static void cvm_oct_update_link(void *context, int pending)
142{
143	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
144	struct ifnet *ifp = priv->ifp;
145	cvmx_helper_link_info_t link_info;
146
147	link_info.u64 = priv->link_info;
148
149	if (link_info.s.link_up) {
150		if_link_state_change(ifp, LINK_STATE_UP);
151		if (priv->queue != -1)
152			DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
153				   if_name(ifp), link_info.s.speed,
154				   (link_info.s.full_duplex) ? "Full" : "Half",
155				   priv->port, priv->queue);
156		else
157			DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, POW\n",
158				   if_name(ifp), link_info.s.speed,
159				   (link_info.s.full_duplex) ? "Full" : "Half",
160				   priv->port);
161	} else {
162		if_link_state_change(ifp, LINK_STATE_DOWN);
163		DEBUGPRINT("%s: Link down\n", if_name(ifp));
164	}
165	priv->need_link_update = 0;
166}
167
168/**
169 * Periodic timer tick for slow management operations
170 *
171 * @param arg    Device to check
172 */
173static void cvm_do_timer(void *arg)
174{
175	static int port;
176	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
177		if (cvm_oct_device[port]) {
178			int queues_per_port;
179			int qos;
180			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
181			if (priv->poll)
182			{
183				/* skip polling if we don't get the lock */
184				if (MDIO_TRYLOCK()) {
185					priv->poll(cvm_oct_device[port]);
186					MDIO_UNLOCK();
187
188					if (priv->need_link_update) {
189						taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
190					}
191				}
192			}
193
194			queues_per_port = cvmx_pko_get_num_queues(port);
195			/* Drain any pending packets in the free list */
196			for (qos = 0; qos < queues_per_port; qos++) {
197				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
198					IF_LOCK(&priv->tx_free_queue[qos]);
199					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
200						struct mbuf *m;
201
202						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
203						m_freem(m);
204					}
205					IF_UNLOCK(&priv->tx_free_queue[qos]);
206
207					/*
208					 * XXX locking!
209					 */
210					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
211				}
212			}
213#if 0
214			cvm_oct_device[port]->get_stats(cvm_oct_device[port]);
215#endif
216		}
217		port++;
218		/* Poll the next port in a 50th of a second.
219		   This spreads the polling of ports out a little bit */
220		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
221	} else {
222		port = 0;
223		/* All ports have been polled. Start the next iteration through
224		   the ports in one second */
225		callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
226	}
227}
228
229
230/**
231 * Configure common hardware for all interfaces
232 */
233static void cvm_oct_configure_common_hw(device_t bus)
234{
235	struct octebus_softc *sc;
236	int error;
237	int rid;
238
239        sc = device_get_softc(bus);
240
241	/* Setup the FPA */
242	cvmx_fpa_enable();
243	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
244	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
245	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
246		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
247
248	if (USE_RED)
249		cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
250
251	/* Enable the MII interface */
252	if (!octeon_is_simulation())
253		cvmx_write_csr(CVMX_SMI_EN, 1);
254
255	/* Register an IRQ hander for to receive POW interrupts */
256        rid = 0;
257        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
258					   CVMX_IRQ_WORKQ0 + pow_receive_group,
259					   CVMX_IRQ_WORKQ0 + pow_receive_group,
260					   1, RF_ACTIVE);
261        if (sc->sc_rx_irq == NULL) {
262                device_printf(bus, "could not allocate workq irq");
263		return;
264        }
265
266        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
267			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
268			       NULL);
269        if (error != 0) {
270                device_printf(bus, "could not setup workq irq");
271		return;
272        }
273
274
275#ifdef SMP
276	if (USE_MULTICORE_RECEIVE) {
277		critical_enter();
278		{
279			int cpu;
280			for (cpu = 0; cpu < mp_maxid; cpu++) {
281				if (!CPU_ABSENT(cpu) &&
282				   (cpu != PCPU_GET(cpuid))) {
283					cvmx_ciu_intx0_t en;
284					en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
285					en.s.workq |= (1<<pow_receive_group);
286					cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
287				}
288			}
289		}
290		critical_exit();
291	}
292#endif
293}
294
295
296/**
297 * Free a work queue entry received in a intercept callback.
298 *
299 * @param work_queue_entry
300 *               Work queue entry to free
301 * @return Zero on success, Negative on failure.
302 */
303int cvm_oct_free_work(void *work_queue_entry)
304{
305	cvmx_wqe_t *work = work_queue_entry;
306
307	int segments = work->word2.s.bufs;
308	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
309
310	while (segments--) {
311		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
312		if (__predict_false(!segment_ptr.s.i))
313			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
314		segment_ptr = next_ptr;
315	}
316	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
317
318	return 0;
319}
320
321
322/**
323 * Module/ driver initialization. Creates the linux network
324 * devices.
325 *
326 * @return Zero on success
327 */
328int cvm_oct_init_module(device_t bus)
329{
330	device_t dev;
331	int ifnum;
332	int num_interfaces;
333	int interface;
334	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
335	int qos;
336
337	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
338
339#if 0
340	cvm_oct_proc_initialize();
341#endif
342	cvm_oct_rx_initialize();
343	cvm_oct_configure_common_hw(bus);
344
345	cvmx_helper_initialize_packet_io_global();
346
347	/* Change the input group for all ports before input is enabled */
348	num_interfaces = cvmx_helper_get_number_of_interfaces();
349	for (interface = 0; interface < num_interfaces; interface++) {
350		int num_ports = cvmx_helper_ports_on_interface(interface);
351		int port;
352
353		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
354			cvmx_pip_prt_tagx_t pip_prt_tagx;
355			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
356			pip_prt_tagx.s.grp = pow_receive_group;
357			cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
358		}
359	}
360
361	cvmx_helper_ipd_and_packet_input_enable();
362
363	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
364
365	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
366	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
367	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
368	    "octe link taskq");
369
370	/* Initialize the FAU used for counting packet buffers that need to be freed */
371	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
372
373	if ((pow_send_group != -1)) {
374		struct ifnet *ifp;
375
376		printf("\tConfiguring device for POW only access\n");
377		dev = BUS_ADD_CHILD(bus, 0, "pow", 0);
378		if (dev != NULL)
379			ifp = if_alloc(IFT_ETHER);
380		if (dev != NULL && ifp != NULL) {
381			/* Initialize the device private structure. */
382			cvm_oct_private_t *priv;
383
384			device_probe(dev);
385			priv = device_get_softc(dev);
386			priv->dev = dev;
387			priv->ifp = ifp;
388			priv->init = cvm_oct_common_init;
389			priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
390			priv->port = CVMX_PIP_NUM_INPUT_PORTS;
391			priv->queue = -1;
392			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
393
394			device_set_desc(dev, "Cavium Octeon POW Ethernet\n");
395
396			ifp->if_softc = priv;
397
398			if (priv->init(ifp) < 0) {
399				printf("\t\tFailed to register ethernet device for POW\n");
400				panic("%s: need to free ifp.", __func__);
401			} else {
402				cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = ifp;
403				printf("\t\t%s: POW send group %d, receive group %d\n",
404				if_name(ifp), pow_send_group, pow_receive_group);
405			}
406		} else {
407			printf("\t\tFailed to allocate ethernet device for POW\n");
408		}
409	}
410
411	ifnum = 0;
412	num_interfaces = cvmx_helper_get_number_of_interfaces();
413	for (interface = 0; interface < num_interfaces; interface++) {
414		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
415		int num_ports = cvmx_helper_ports_on_interface(interface);
416		int port;
417
418		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
419			cvm_oct_private_t *priv;
420			struct ifnet *ifp;
421
422			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
423			if (dev != NULL)
424				ifp = if_alloc(IFT_ETHER);
425			if (dev == NULL || ifp == NULL) {
426				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
427				continue;
428			}
429			/* XXX/juli set max send q len.  */
430#if 0
431			if (disable_core_queueing)
432				ifp->tx_queue_len = 0;
433#endif
434
435			/* Initialize the device private structure. */
436			device_probe(dev);
437			priv = device_get_softc(dev);
438			priv->dev = dev;
439			priv->ifp = ifp;
440			priv->imode = imode;
441			priv->port = port;
442			priv->queue = cvmx_pko_get_base_queue(priv->port);
443			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
444			for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
445				cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
446			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
447
448			switch (priv->imode) {
449
450			/* These types don't support ports to IPD/PKO */
451			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
452			case CVMX_HELPER_INTERFACE_MODE_PCIE:
453			case CVMX_HELPER_INTERFACE_MODE_PICMG:
454				break;
455
456			case CVMX_HELPER_INTERFACE_MODE_NPI:
457				priv->init = cvm_oct_common_init;
458				priv->uninit = cvm_oct_common_uninit;
459				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
460				break;
461
462			case CVMX_HELPER_INTERFACE_MODE_XAUI:
463				priv->init = cvm_oct_xaui_init;
464				priv->uninit = cvm_oct_xaui_uninit;
465				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
466				break;
467
468			case CVMX_HELPER_INTERFACE_MODE_LOOP:
469				priv->init = cvm_oct_common_init;
470				priv->uninit = cvm_oct_common_uninit;
471				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
472				break;
473
474			case CVMX_HELPER_INTERFACE_MODE_SGMII:
475				priv->init = cvm_oct_sgmii_init;
476				priv->uninit = cvm_oct_sgmii_uninit;
477				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
478				break;
479
480			case CVMX_HELPER_INTERFACE_MODE_SPI:
481				priv->init = cvm_oct_spi_init;
482				priv->uninit = cvm_oct_spi_uninit;
483				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
484				break;
485
486			case CVMX_HELPER_INTERFACE_MODE_RGMII:
487				priv->init = cvm_oct_rgmii_init;
488				priv->uninit = cvm_oct_rgmii_uninit;
489				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
490				break;
491
492			case CVMX_HELPER_INTERFACE_MODE_GMII:
493				priv->init = cvm_oct_rgmii_init;
494				priv->uninit = cvm_oct_rgmii_uninit;
495				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
496				break;
497			}
498
499			ifp->if_softc = priv;
500
501			if (!priv->init) {
502				panic("%s: unsupported device type, need to free ifp.", __func__);
503			} else
504			if (priv->init(ifp) < 0) {
505				printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
506				interface, priv->port);
507				panic("%s: init failed, need to free ifp.", __func__);
508			} else {
509				cvm_oct_device[priv->port] = ifp;
510				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
511			}
512		}
513	}
514
515	if (INTERRUPT_LIMIT) {
516		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
517		cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
518
519		/* Enable POW timer interrupt. It will count when there are packets available */
520		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
521	} else {
522		/* Enable POW interrupt when our port has at least one packet */
523		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
524	}
525
526	callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
527	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
528
529	return 0;
530}
531
532
533/**
534 * Module / driver shutdown
535 *
536 * @return Zero on success
537 */
538void cvm_oct_cleanup_module(void)
539{
540	int port;
541
542	/* Disable POW interrupt */
543	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
544
545	cvmx_ipd_disable();
546
547#if 0
548	/* Free the interrupt handler */
549	free_irq(8 + pow_receive_group, cvm_oct_device);
550#endif
551
552	callout_stop(&cvm_oct_poll_timer);
553	cvm_oct_rx_shutdown();
554	cvmx_pko_disable();
555
556	/* Free the ethernet devices */
557	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
558		if (cvm_oct_device[port]) {
559			cvm_oct_tx_shutdown(cvm_oct_device[port]);
560#if 0
561			unregister_netdev(cvm_oct_device[port]);
562			kfree(cvm_oct_device[port]);
563#else
564			panic("%s: need to detach and free interface.", __func__);
565#endif
566			cvm_oct_device[port] = NULL;
567		}
568	}
569
570	cvmx_pko_shutdown();
571#if 0
572	cvm_oct_proc_shutdown();
573#endif
574
575	cvmx_ipd_free_ptr();
576
577	/* Free the HW pools */
578	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
579	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
580	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
581		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
582}
583