ethernet.c revision 210311
1210311Sjmallett/*************************************************************************
2210311SjmallettCopyright (c) 2003-2007  Cavium Networks (support@cavium.com). All rights
3210311Sjmallettreserved.
4210311Sjmallett
5210311Sjmallett
6210311SjmallettRedistribution and use in source and binary forms, with or without
7210311Sjmallettmodification, are permitted provided that the following conditions are
8210311Sjmallettmet:
9210311Sjmallett
10210311Sjmallett    * Redistributions of source code must retain the above copyright
11210311Sjmallett      notice, this list of conditions and the following disclaimer.
12210311Sjmallett
13210311Sjmallett    * Redistributions in binary form must reproduce the above
14210311Sjmallett      copyright notice, this list of conditions and the following
15210311Sjmallett      disclaimer in the documentation and/or other materials provided
16210311Sjmallett      with the distribution.
17210311Sjmallett
18210311Sjmallett    * Neither the name of Cavium Networks nor the names of
19210311Sjmallett      its contributors may be used to endorse or promote products
20210311Sjmallett      derived from this software without specific prior written
21210311Sjmallett      permission.
22210311Sjmallett
23210311SjmallettThis 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.
24210311Sjmallett
25210311SjmallettTO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
26210311SjmallettAND 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.
27210311Sjmallett*************************************************************************/
28210311Sjmallett
29210311Sjmallett#include <sys/cdefs.h>
30210311Sjmallett__FBSDID("$FreeBSD: head/sys/mips/cavium/octe/ethernet.c 210311 2010-07-20 19:25:11Z jmallett $");
31210311Sjmallett
32210311Sjmallett#include <sys/param.h>
33210311Sjmallett#include <sys/systm.h>
34210311Sjmallett#include <sys/bus.h>
35210311Sjmallett#include <sys/conf.h>
36210311Sjmallett#include <sys/endian.h>
37210311Sjmallett#include <sys/kernel.h>
38210311Sjmallett#include <sys/rman.h>
39210311Sjmallett#include <sys/mbuf.h>
40210311Sjmallett#include <sys/socket.h>
41210311Sjmallett#include <sys/module.h>
42210311Sjmallett#include <sys/smp.h>
43210311Sjmallett
44210311Sjmallett#include <net/ethernet.h>
45210311Sjmallett#include <net/if.h>
46210311Sjmallett#include <net/if_types.h>
47210311Sjmallett
48210311Sjmallett#include "wrapper-cvmx-includes.h"
49210311Sjmallett#include "ethernet-headers.h"
50210311Sjmallett
51210311Sjmallett#include "octebusvar.h"
52210311Sjmallett
53210311Sjmallett/*
54210311Sjmallett * XXX/juli
55210311Sjmallett * Convert 0444 to tunables, 0644 to sysctls.
56210311Sjmallett */
57210311Sjmallett#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
58210311Sjmallettint num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS;
59210311Sjmallett#else
60210311Sjmallettint num_packet_buffers = 1024;
61210311Sjmallett#endif
62210311SjmallettTUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
63210311Sjmallett/*
64210311Sjmallett		 "\t\tNumber of packet buffers to allocate and store in the\n"
65210311Sjmallett		 "\t\tFPA. By default, 1024 packet buffers are used unless\n"
66210311Sjmallett		 "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */
67210311Sjmallett
68210311Sjmallettint pow_receive_group = 15;
69210311SjmallettTUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
70210311Sjmallett/*
71210311Sjmallett		 "\t\tPOW group to receive packets from. All ethernet hardware\n"
72210311Sjmallett		 "\t\twill be configured to send incomming packets to this POW\n"
73210311Sjmallett		 "\t\tgroup. Also any other software can submit packets to this\n"
74210311Sjmallett		 "\t\tgroup for the kernel to process." */
75210311Sjmallett
76210311Sjmallettint pow_send_group = -1; /* XXX Should be a sysctl.  */
77210311SjmallettTUNABLE_INT("hw.octe.pow_send_group", &pow_send_group);
78210311Sjmallett/*
79210311Sjmallett		 "\t\tPOW group to send packets to other software on. This\n"
80210311Sjmallett		 "\t\tcontrols the creation of the virtual device pow0.\n"
81210311Sjmallett		 "\t\talways_use_pow also depends on this value." */
82210311Sjmallett
83210311Sjmallettint always_use_pow;
84210311SjmallettTUNABLE_INT("hw.octe.always_use_pow", &always_use_pow);
85210311Sjmallett/*
86210311Sjmallett		 "\t\tWhen set, always send to the pow group. This will cause\n"
87210311Sjmallett		 "\t\tpackets sent to real ethernet devices to be sent to the\n"
88210311Sjmallett		 "\t\tPOW group instead of the hardware. Unless some other\n"
89210311Sjmallett		 "\t\tapplication changes the config, packets will still be\n"
90210311Sjmallett		 "\t\treceived from the low level hardware. Use this option\n"
91210311Sjmallett		 "\t\tto allow a CVMX app to intercept all packets from the\n"
92210311Sjmallett		 "\t\tlinux kernel. You must specify pow_send_group along with\n"
93210311Sjmallett		 "\t\tthis option." */
94210311Sjmallett
95210311Sjmallettchar pow_send_list[128] = "";
96210311SjmallettTUNABLE_STR("hw.octe.pow_send_list", pow_send_list, sizeof pow_send_list);
97210311Sjmallett/*
98210311Sjmallett		 "\t\tComma separated list of ethernet devices that should use the\n"
99210311Sjmallett		 "\t\tPOW for transmit instead of the actual ethernet hardware. This\n"
100210311Sjmallett		 "\t\tis a per port version of always_use_pow. always_use_pow takes\n"
101210311Sjmallett		 "\t\tprecedence over this list. For example, setting this to\n"
102210311Sjmallett		 "\t\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n"
103210311Sjmallett		 "\t\tusing the pow_send_group." */
104210311Sjmallett
105210311Sjmallett
106210311Sjmallettstatic int disable_core_queueing = 1;
107210311SjmallettTUNABLE_INT("hw.octe.disable_core_queueing", &disable_core_queueing);
108210311Sjmallett/*
109210311Sjmallett		"\t\tWhen set the networking core's tx_queue_len is set to zero.  This\n"
110210311Sjmallett		"\t\tallows packets to be sent without lock contention in the packet scheduler\n"
111210311Sjmallett		"\t\tresulting in some cases in improved throughput.\n" */
112210311Sjmallett
113210311Sjmallettextern int octeon_is_simulation(void);
114210311Sjmallett
115210311Sjmallett/**
116210311Sjmallett * Exported from the kernel so we can determine board information. It is
117210311Sjmallett * passed by the bootloader to the kernel.
118210311Sjmallett */
119210311Sjmallettextern cvmx_bootinfo_t *octeon_bootinfo;
120210311Sjmallett
121210311Sjmallett/**
122210311Sjmallett * Periodic timer to check auto negotiation
123210311Sjmallett */
124210311Sjmallettstatic struct callout cvm_oct_poll_timer;
125210311Sjmallett
126210311Sjmallett/**
127210311Sjmallett * Array of every ethernet device owned by this driver indexed by
128210311Sjmallett * the ipd input port number.
129210311Sjmallett */
130210311Sjmallettstruct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
131210311Sjmallett
132210311Sjmallett
133210311Sjmallett/**
134210311Sjmallett * Periodic timer tick for slow management operations
135210311Sjmallett *
136210311Sjmallett * @param arg    Device to check
137210311Sjmallett */
138210311Sjmallettstatic void cvm_do_timer(void *arg)
139210311Sjmallett{
140210311Sjmallett	static int port;
141210311Sjmallett	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
142210311Sjmallett		if (cvm_oct_device[port]) {
143210311Sjmallett			int queues_per_port;
144210311Sjmallett			int qos;
145210311Sjmallett			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
146210311Sjmallett			if (priv->poll)
147210311Sjmallett			{
148210311Sjmallett				/* skip polling if we don't get the lock */
149210311Sjmallett				if (MDIO_TRYLOCK()) {
150210311Sjmallett					priv->poll(cvm_oct_device[port]);
151210311Sjmallett					MDIO_UNLOCK();
152210311Sjmallett				}
153210311Sjmallett			}
154210311Sjmallett
155210311Sjmallett			queues_per_port = cvmx_pko_get_num_queues(port);
156210311Sjmallett			/* Drain any pending packets in the free list */
157210311Sjmallett			for (qos = 0; qos < queues_per_port; qos++) {
158210311Sjmallett				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
159210311Sjmallett					IF_LOCK(&priv->tx_free_queue[qos]);
160210311Sjmallett					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
161210311Sjmallett						struct mbuf *m;
162210311Sjmallett
163210311Sjmallett						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
164210311Sjmallett						m_freem(m);
165210311Sjmallett					}
166210311Sjmallett					IF_UNLOCK(&priv->tx_free_queue[qos]);
167210311Sjmallett
168210311Sjmallett					/*
169210311Sjmallett					 * XXX locking!
170210311Sjmallett					 */
171210311Sjmallett					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
172210311Sjmallett				}
173210311Sjmallett			}
174210311Sjmallett#if 0
175210311Sjmallett			cvm_oct_device[port]->get_stats(cvm_oct_device[port]);
176210311Sjmallett#endif
177210311Sjmallett		}
178210311Sjmallett		port++;
179210311Sjmallett		/* Poll the next port in a 50th of a second.
180210311Sjmallett		   This spreads the polling of ports out a little bit */
181210311Sjmallett		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
182210311Sjmallett	} else {
183210311Sjmallett		port = 0;
184210311Sjmallett		/* All ports have been polled. Start the next iteration through
185210311Sjmallett		   the ports in one second */
186210311Sjmallett		callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
187210311Sjmallett	}
188210311Sjmallett}
189210311Sjmallett
190210311Sjmallett
191210311Sjmallett/**
192210311Sjmallett * Configure common hardware for all interfaces
193210311Sjmallett */
194210311Sjmallettstatic void cvm_oct_configure_common_hw(device_t bus)
195210311Sjmallett{
196210311Sjmallett	struct octebus_softc *sc;
197210311Sjmallett	int error;
198210311Sjmallett	int rid;
199210311Sjmallett
200210311Sjmallett        sc = device_get_softc(bus);
201210311Sjmallett
202210311Sjmallett	/* Setup the FPA */
203210311Sjmallett	cvmx_fpa_enable();
204210311Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
205210311Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
206210311Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
207210311Sjmallett		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
208210311Sjmallett
209210311Sjmallett	if (USE_RED)
210210311Sjmallett		cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
211210311Sjmallett
212210311Sjmallett	/* Enable the MII interface */
213210311Sjmallett	if (!octeon_is_simulation())
214210311Sjmallett		cvmx_write_csr(CVMX_SMI_EN, 1);
215210311Sjmallett
216210311Sjmallett	/* Register an IRQ hander for to receive POW interrupts */
217210311Sjmallett        rid = 0;
218210311Sjmallett        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
219210311Sjmallett					   CVMX_IRQ_WORKQ0 + pow_receive_group,
220210311Sjmallett					   CVMX_IRQ_WORKQ0 + pow_receive_group,
221210311Sjmallett					   1, RF_ACTIVE);
222210311Sjmallett        if (sc->sc_rx_irq == NULL) {
223210311Sjmallett                device_printf(bus, "could not allocate workq irq");
224210311Sjmallett		return;
225210311Sjmallett        }
226210311Sjmallett
227210311Sjmallett        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
228210311Sjmallett			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
229210311Sjmallett			       NULL);
230210311Sjmallett        if (error != 0) {
231210311Sjmallett                device_printf(bus, "could not setup workq irq");
232210311Sjmallett		return;
233210311Sjmallett        }
234210311Sjmallett
235210311Sjmallett
236210311Sjmallett#ifdef SMP
237210311Sjmallett	if (USE_MULTICORE_RECEIVE) {
238210311Sjmallett		critical_enter();
239210311Sjmallett		{
240210311Sjmallett			int cpu;
241210311Sjmallett			for (cpu = 0; cpu < mp_maxid; cpu++) {
242210311Sjmallett				if (!CPU_ABSENT(cpu) &&
243210311Sjmallett				   (cpu != PCPU_GET(cpuid))) {
244210311Sjmallett					cvmx_ciu_intx0_t en;
245210311Sjmallett					en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
246210311Sjmallett					en.s.workq |= (1<<pow_receive_group);
247210311Sjmallett					cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
248210311Sjmallett				}
249210311Sjmallett			}
250210311Sjmallett		}
251210311Sjmallett		critical_exit();
252210311Sjmallett	}
253210311Sjmallett#endif
254210311Sjmallett}
255210311Sjmallett
256210311Sjmallett
257210311Sjmallett/**
258210311Sjmallett * Free a work queue entry received in a intercept callback.
259210311Sjmallett *
260210311Sjmallett * @param work_queue_entry
261210311Sjmallett *               Work queue entry to free
262210311Sjmallett * @return Zero on success, Negative on failure.
263210311Sjmallett */
264210311Sjmallettint cvm_oct_free_work(void *work_queue_entry)
265210311Sjmallett{
266210311Sjmallett	cvmx_wqe_t *work = work_queue_entry;
267210311Sjmallett
268210311Sjmallett	int segments = work->word2.s.bufs;
269210311Sjmallett	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
270210311Sjmallett
271210311Sjmallett	while (segments--) {
272210311Sjmallett		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
273210311Sjmallett		if (__predict_false(!segment_ptr.s.i))
274210311Sjmallett			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
275210311Sjmallett		segment_ptr = next_ptr;
276210311Sjmallett	}
277210311Sjmallett	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
278210311Sjmallett
279210311Sjmallett	return 0;
280210311Sjmallett}
281210311Sjmallett
282210311Sjmallett
283210311Sjmallett/**
284210311Sjmallett * Module/ driver initialization. Creates the linux network
285210311Sjmallett * devices.
286210311Sjmallett *
287210311Sjmallett * @return Zero on success
288210311Sjmallett */
289210311Sjmallettint cvm_oct_init_module(device_t bus)
290210311Sjmallett{
291210311Sjmallett	device_t dev;
292210311Sjmallett	int ifnum;
293210311Sjmallett	int num_interfaces;
294210311Sjmallett	int interface;
295210311Sjmallett	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
296210311Sjmallett	int qos;
297210311Sjmallett
298210311Sjmallett	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
299210311Sjmallett
300210311Sjmallett#if 0
301210311Sjmallett	cvm_oct_proc_initialize();
302210311Sjmallett#endif
303210311Sjmallett	cvm_oct_rx_initialize();
304210311Sjmallett	cvm_oct_configure_common_hw(bus);
305210311Sjmallett
306210311Sjmallett	cvmx_helper_initialize_packet_io_global();
307210311Sjmallett
308210311Sjmallett	/* Change the input group for all ports before input is enabled */
309210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
310210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
311210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
312210311Sjmallett		int port;
313210311Sjmallett
314210311Sjmallett		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
315210311Sjmallett			cvmx_pip_prt_tagx_t pip_prt_tagx;
316210311Sjmallett			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
317210311Sjmallett			pip_prt_tagx.s.grp = pow_receive_group;
318210311Sjmallett			cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
319210311Sjmallett		}
320210311Sjmallett	}
321210311Sjmallett
322210311Sjmallett	cvmx_helper_ipd_and_packet_input_enable();
323210311Sjmallett
324210311Sjmallett	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
325210311Sjmallett
326210311Sjmallett	/* Initialize the FAU used for counting packet buffers that need to be freed */
327210311Sjmallett	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
328210311Sjmallett
329210311Sjmallett	if ((pow_send_group != -1)) {
330210311Sjmallett		struct ifnet *ifp;
331210311Sjmallett
332210311Sjmallett		printf("\tConfiguring device for POW only access\n");
333210311Sjmallett		dev = BUS_ADD_CHILD(bus, 0, "pow", 0);
334210311Sjmallett		if (dev != NULL)
335210311Sjmallett			ifp = if_alloc(IFT_ETHER);
336210311Sjmallett		if (dev != NULL && ifp != NULL) {
337210311Sjmallett			/* Initialize the device private structure. */
338210311Sjmallett			cvm_oct_private_t *priv;
339210311Sjmallett
340210311Sjmallett			device_probe(dev);
341210311Sjmallett			priv = device_get_softc(dev);
342210311Sjmallett			priv->dev = dev;
343210311Sjmallett			priv->ifp = ifp;
344210311Sjmallett			priv->init = cvm_oct_common_init;
345210311Sjmallett			priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
346210311Sjmallett			priv->port = CVMX_PIP_NUM_INPUT_PORTS;
347210311Sjmallett			priv->queue = -1;
348210311Sjmallett
349210311Sjmallett			device_set_desc(dev, "Cavium Octeon POW Ethernet\n");
350210311Sjmallett
351210311Sjmallett			ifp->if_softc = priv;
352210311Sjmallett
353210311Sjmallett			if (priv->init(ifp) < 0) {
354210311Sjmallett				printf("\t\tFailed to register ethernet device for POW\n");
355210311Sjmallett				panic("%s: need to free ifp.", __func__);
356210311Sjmallett			} else {
357210311Sjmallett				cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = ifp;
358210311Sjmallett				printf("\t\t%s: POW send group %d, receive group %d\n",
359210311Sjmallett				if_name(ifp), pow_send_group, pow_receive_group);
360210311Sjmallett			}
361210311Sjmallett		} else {
362210311Sjmallett			printf("\t\tFailed to allocate ethernet device for POW\n");
363210311Sjmallett		}
364210311Sjmallett	}
365210311Sjmallett
366210311Sjmallett	ifnum = 0;
367210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
368210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
369210311Sjmallett		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
370210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
371210311Sjmallett		int port;
372210311Sjmallett
373210311Sjmallett		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
374210311Sjmallett			cvm_oct_private_t *priv;
375210311Sjmallett			struct ifnet *ifp;
376210311Sjmallett
377210311Sjmallett			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
378210311Sjmallett			if (dev != NULL)
379210311Sjmallett				ifp = if_alloc(IFT_ETHER);
380210311Sjmallett			if (dev == NULL || ifp == NULL) {
381210311Sjmallett				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
382210311Sjmallett				continue;
383210311Sjmallett			}
384210311Sjmallett			/* XXX/juli set max send q len.  */
385210311Sjmallett#if 0
386210311Sjmallett			if (disable_core_queueing)
387210311Sjmallett				ifp->tx_queue_len = 0;
388210311Sjmallett#endif
389210311Sjmallett
390210311Sjmallett			/* Initialize the device private structure. */
391210311Sjmallett			device_probe(dev);
392210311Sjmallett			priv = device_get_softc(dev);
393210311Sjmallett			priv->dev = dev;
394210311Sjmallett			priv->ifp = ifp;
395210311Sjmallett			priv->imode = imode;
396210311Sjmallett			priv->port = port;
397210311Sjmallett			priv->queue = cvmx_pko_get_base_queue(priv->port);
398210311Sjmallett			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
399210311Sjmallett			for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
400210311Sjmallett				cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
401210311Sjmallett
402210311Sjmallett			switch (priv->imode) {
403210311Sjmallett
404210311Sjmallett			/* These types don't support ports to IPD/PKO */
405210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
406210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PCIE:
407210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PICMG:
408210311Sjmallett				break;
409210311Sjmallett
410210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_NPI:
411210311Sjmallett				priv->init = cvm_oct_common_init;
412210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
413210311Sjmallett				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
414210311Sjmallett				break;
415210311Sjmallett
416210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_XAUI:
417210311Sjmallett				priv->init = cvm_oct_xaui_init;
418210311Sjmallett				priv->uninit = cvm_oct_xaui_uninit;
419210311Sjmallett				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
420210311Sjmallett				break;
421210311Sjmallett
422210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_LOOP:
423210311Sjmallett				priv->init = cvm_oct_common_init;
424210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
425210311Sjmallett				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
426210311Sjmallett				break;
427210311Sjmallett
428210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SGMII:
429210311Sjmallett				priv->init = cvm_oct_sgmii_init;
430210311Sjmallett				priv->uninit = cvm_oct_sgmii_uninit;
431210311Sjmallett				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
432210311Sjmallett				break;
433210311Sjmallett
434210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SPI:
435210311Sjmallett				priv->init = cvm_oct_spi_init;
436210311Sjmallett				priv->uninit = cvm_oct_spi_uninit;
437210311Sjmallett				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
438210311Sjmallett				break;
439210311Sjmallett
440210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_RGMII:
441210311Sjmallett				priv->init = cvm_oct_rgmii_init;
442210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
443210311Sjmallett				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
444210311Sjmallett				break;
445210311Sjmallett
446210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_GMII:
447210311Sjmallett				priv->init = cvm_oct_rgmii_init;
448210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
449210311Sjmallett				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
450210311Sjmallett				break;
451210311Sjmallett			}
452210311Sjmallett
453210311Sjmallett			ifp->if_softc = priv;
454210311Sjmallett
455210311Sjmallett			if (!priv->init) {
456210311Sjmallett				panic("%s: unsupported device type, need to free ifp.", __func__);
457210311Sjmallett			} else
458210311Sjmallett			if (priv->init(ifp) < 0) {
459210311Sjmallett				printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
460210311Sjmallett				interface, priv->port);
461210311Sjmallett				panic("%s: init failed, need to free ifp.", __func__);
462210311Sjmallett			} else {
463210311Sjmallett				cvm_oct_device[priv->port] = ifp;
464210311Sjmallett				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
465210311Sjmallett			}
466210311Sjmallett		}
467210311Sjmallett	}
468210311Sjmallett
469210311Sjmallett	if (INTERRUPT_LIMIT) {
470210311Sjmallett		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
471210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
472210311Sjmallett
473210311Sjmallett		/* Enable POW timer interrupt. It will count when there are packets available */
474210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
475210311Sjmallett	} else {
476210311Sjmallett		/* Enable POW interrupt when our port has at least one packet */
477210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
478210311Sjmallett	}
479210311Sjmallett
480210311Sjmallett	callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
481210311Sjmallett	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
482210311Sjmallett
483210311Sjmallett	return 0;
484210311Sjmallett}
485210311Sjmallett
486210311Sjmallett
487210311Sjmallett/**
488210311Sjmallett * Module / driver shutdown
489210311Sjmallett *
490210311Sjmallett * @return Zero on success
491210311Sjmallett */
492210311Sjmallettvoid cvm_oct_cleanup_module(void)
493210311Sjmallett{
494210311Sjmallett	int port;
495210311Sjmallett
496210311Sjmallett	/* Disable POW interrupt */
497210311Sjmallett	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
498210311Sjmallett
499210311Sjmallett	cvmx_ipd_disable();
500210311Sjmallett
501210311Sjmallett#if 0
502210311Sjmallett	/* Free the interrupt handler */
503210311Sjmallett	free_irq(8 + pow_receive_group, cvm_oct_device);
504210311Sjmallett#endif
505210311Sjmallett
506210311Sjmallett	callout_stop(&cvm_oct_poll_timer);
507210311Sjmallett	cvm_oct_rx_shutdown();
508210311Sjmallett	cvmx_pko_disable();
509210311Sjmallett
510210311Sjmallett	/* Free the ethernet devices */
511210311Sjmallett	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
512210311Sjmallett		if (cvm_oct_device[port]) {
513210311Sjmallett			cvm_oct_tx_shutdown(cvm_oct_device[port]);
514210311Sjmallett#if 0
515210311Sjmallett			unregister_netdev(cvm_oct_device[port]);
516210311Sjmallett			kfree(cvm_oct_device[port]);
517210311Sjmallett#else
518210311Sjmallett			panic("%s: need to detach and free interface.", __func__);
519210311Sjmallett#endif
520210311Sjmallett			cvm_oct_device[port] = NULL;
521210311Sjmallett		}
522210311Sjmallett	}
523210311Sjmallett
524210311Sjmallett	cvmx_pko_shutdown();
525210311Sjmallett#if 0
526210311Sjmallett	cvm_oct_proc_shutdown();
527210311Sjmallett#endif
528210311Sjmallett
529210311Sjmallett	cvmx_ipd_free_ptr();
530210311Sjmallett
531210311Sjmallett	/* Free the HW pools */
532210311Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
533210311Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
534210311Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
535210311Sjmallett		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
536210311Sjmallett}
537