ethernet.c revision 215974
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 215974 2010-11-28 05:57:24Z 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>
43213150Sjmallett#include <sys/taskqueue.h>
44210311Sjmallett
45210311Sjmallett#include <net/ethernet.h>
46210311Sjmallett#include <net/if.h>
47210311Sjmallett#include <net/if_types.h>
48210311Sjmallett
49210311Sjmallett#include "wrapper-cvmx-includes.h"
50210311Sjmallett#include "ethernet-headers.h"
51210311Sjmallett
52210311Sjmallett#include "octebusvar.h"
53210311Sjmallett
54210311Sjmallett/*
55210311Sjmallett * XXX/juli
56210311Sjmallett * Convert 0444 to tunables, 0644 to sysctls.
57210311Sjmallett */
58210311Sjmallett#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
59210311Sjmallettint num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS;
60210311Sjmallett#else
61210311Sjmallettint num_packet_buffers = 1024;
62210311Sjmallett#endif
63210311SjmallettTUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
64210311Sjmallett/*
65210311Sjmallett		 "\t\tNumber of packet buffers to allocate and store in the\n"
66210311Sjmallett		 "\t\tFPA. By default, 1024 packet buffers are used unless\n"
67210311Sjmallett		 "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */
68210311Sjmallett
69210311Sjmallettint pow_receive_group = 15;
70210311SjmallettTUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
71210311Sjmallett/*
72210311Sjmallett		 "\t\tPOW group to receive packets from. All ethernet hardware\n"
73210311Sjmallett		 "\t\twill be configured to send incomming packets to this POW\n"
74210311Sjmallett		 "\t\tgroup. Also any other software can submit packets to this\n"
75210311Sjmallett		 "\t\tgroup for the kernel to process." */
76210311Sjmallett
77210311Sjmallettextern int octeon_is_simulation(void);
78210311Sjmallett
79210311Sjmallett/**
80210311Sjmallett * Exported from the kernel so we can determine board information. It is
81210311Sjmallett * passed by the bootloader to the kernel.
82210311Sjmallett */
83210311Sjmallettextern cvmx_bootinfo_t *octeon_bootinfo;
84210311Sjmallett
85210311Sjmallett/**
86210311Sjmallett * Periodic timer to check auto negotiation
87210311Sjmallett */
88210311Sjmallettstatic struct callout cvm_oct_poll_timer;
89210311Sjmallett
90210311Sjmallett/**
91210311Sjmallett * Array of every ethernet device owned by this driver indexed by
92210311Sjmallett * the ipd input port number.
93210311Sjmallett */
94210311Sjmallettstruct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
95210311Sjmallett
96213150Sjmallett/**
97213150Sjmallett * Task to handle link status changes.
98213150Sjmallett */
99213150Sjmallettstatic struct taskqueue *cvm_oct_link_taskq;
100210311Sjmallett
101210311Sjmallett/**
102213150Sjmallett * Function to update link status.
103213150Sjmallett */
104213150Sjmallettstatic void cvm_oct_update_link(void *context, int pending)
105213150Sjmallett{
106213150Sjmallett	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
107213150Sjmallett	struct ifnet *ifp = priv->ifp;
108213150Sjmallett	cvmx_helper_link_info_t link_info;
109213150Sjmallett
110213150Sjmallett	link_info.u64 = priv->link_info;
111213150Sjmallett
112213150Sjmallett	if (link_info.s.link_up) {
113213150Sjmallett		if_link_state_change(ifp, LINK_STATE_UP);
114215959Sjmallett		DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
115215959Sjmallett			   if_name(ifp), link_info.s.speed,
116215959Sjmallett			   (link_info.s.full_duplex) ? "Full" : "Half",
117215959Sjmallett			   priv->port, priv->queue);
118213150Sjmallett	} else {
119213150Sjmallett		if_link_state_change(ifp, LINK_STATE_DOWN);
120213150Sjmallett		DEBUGPRINT("%s: Link down\n", if_name(ifp));
121213150Sjmallett	}
122213150Sjmallett	priv->need_link_update = 0;
123213150Sjmallett}
124213150Sjmallett
125213150Sjmallett/**
126210311Sjmallett * Periodic timer tick for slow management operations
127210311Sjmallett *
128210311Sjmallett * @param arg    Device to check
129210311Sjmallett */
130210311Sjmallettstatic void cvm_do_timer(void *arg)
131210311Sjmallett{
132210311Sjmallett	static int port;
133213807Sjmallett	static int updated;
134210311Sjmallett	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
135210311Sjmallett		if (cvm_oct_device[port]) {
136210311Sjmallett			int queues_per_port;
137210311Sjmallett			int qos;
138210311Sjmallett			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
139210311Sjmallett			if (priv->poll)
140210311Sjmallett			{
141210311Sjmallett				/* skip polling if we don't get the lock */
142210311Sjmallett				if (MDIO_TRYLOCK()) {
143210311Sjmallett					priv->poll(cvm_oct_device[port]);
144210311Sjmallett					MDIO_UNLOCK();
145213150Sjmallett
146213150Sjmallett					if (priv->need_link_update) {
147213807Sjmallett						updated++;
148213150Sjmallett						taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
149213150Sjmallett					}
150210311Sjmallett				}
151210311Sjmallett			}
152210311Sjmallett
153210311Sjmallett			queues_per_port = cvmx_pko_get_num_queues(port);
154210311Sjmallett			/* Drain any pending packets in the free list */
155210311Sjmallett			for (qos = 0; qos < queues_per_port; qos++) {
156210311Sjmallett				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
157210311Sjmallett					IF_LOCK(&priv->tx_free_queue[qos]);
158210311Sjmallett					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
159210311Sjmallett						struct mbuf *m;
160210311Sjmallett
161210311Sjmallett						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
162210311Sjmallett						m_freem(m);
163210311Sjmallett					}
164210311Sjmallett					IF_UNLOCK(&priv->tx_free_queue[qos]);
165210311Sjmallett
166210311Sjmallett					/*
167210311Sjmallett					 * XXX locking!
168210311Sjmallett					 */
169210311Sjmallett					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
170210311Sjmallett				}
171210311Sjmallett			}
172210311Sjmallett		}
173210311Sjmallett		port++;
174210311Sjmallett		/* Poll the next port in a 50th of a second.
175210311Sjmallett		   This spreads the polling of ports out a little bit */
176210311Sjmallett		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
177210311Sjmallett	} else {
178210311Sjmallett		port = 0;
179213807Sjmallett		/* If any updates were made in this run, continue iterating at
180213807Sjmallett		 * 1/50th of a second, so that if a link has merely gone down
181213807Sjmallett		 * temporarily (e.g. because of interface reinitialization) it
182213807Sjmallett		 * will not be forced to stay down for an entire second.
183213807Sjmallett		 */
184213807Sjmallett		if (updated > 0) {
185213807Sjmallett			updated = 0;
186213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
187213807Sjmallett		} else {
188213807Sjmallett			/* All ports have been polled. Start the next iteration through
189213807Sjmallett			   the ports in one second */
190213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
191213807Sjmallett		}
192210311Sjmallett	}
193210311Sjmallett}
194210311Sjmallett
195210311Sjmallett
196210311Sjmallett/**
197210311Sjmallett * Configure common hardware for all interfaces
198210311Sjmallett */
199210311Sjmallettstatic void cvm_oct_configure_common_hw(device_t bus)
200210311Sjmallett{
201210311Sjmallett	struct octebus_softc *sc;
202210311Sjmallett	int error;
203210311Sjmallett	int rid;
204210311Sjmallett
205210311Sjmallett        sc = device_get_softc(bus);
206210311Sjmallett
207210311Sjmallett	/* Setup the FPA */
208210311Sjmallett	cvmx_fpa_enable();
209210311Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
210210311Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
211210311Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
212210311Sjmallett		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
213210311Sjmallett
214210311Sjmallett	if (USE_RED)
215210311Sjmallett		cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
216210311Sjmallett
217210311Sjmallett	/* Enable the MII interface */
218210311Sjmallett	if (!octeon_is_simulation())
219210311Sjmallett		cvmx_write_csr(CVMX_SMI_EN, 1);
220210311Sjmallett
221210311Sjmallett	/* Register an IRQ hander for to receive POW interrupts */
222210311Sjmallett        rid = 0;
223210311Sjmallett        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
224210311Sjmallett					   CVMX_IRQ_WORKQ0 + pow_receive_group,
225210311Sjmallett					   CVMX_IRQ_WORKQ0 + pow_receive_group,
226210311Sjmallett					   1, RF_ACTIVE);
227210311Sjmallett        if (sc->sc_rx_irq == NULL) {
228210311Sjmallett                device_printf(bus, "could not allocate workq irq");
229210311Sjmallett		return;
230210311Sjmallett        }
231210311Sjmallett
232210311Sjmallett        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
233210311Sjmallett			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
234210311Sjmallett			       NULL);
235210311Sjmallett        if (error != 0) {
236210311Sjmallett                device_printf(bus, "could not setup workq irq");
237210311Sjmallett		return;
238210311Sjmallett        }
239210311Sjmallett
240210311Sjmallett
241210311Sjmallett#ifdef SMP
242210311Sjmallett	if (USE_MULTICORE_RECEIVE) {
243210311Sjmallett		critical_enter();
244210311Sjmallett		{
245210311Sjmallett			int cpu;
246210311Sjmallett			for (cpu = 0; cpu < mp_maxid; cpu++) {
247210311Sjmallett				if (!CPU_ABSENT(cpu) &&
248210311Sjmallett				   (cpu != PCPU_GET(cpuid))) {
249210311Sjmallett					cvmx_ciu_intx0_t en;
250210311Sjmallett					en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
251210311Sjmallett					en.s.workq |= (1<<pow_receive_group);
252210311Sjmallett					cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
253210311Sjmallett				}
254210311Sjmallett			}
255210311Sjmallett		}
256210311Sjmallett		critical_exit();
257210311Sjmallett	}
258210311Sjmallett#endif
259210311Sjmallett}
260210311Sjmallett
261210311Sjmallett
262210311Sjmallett/**
263210311Sjmallett * Free a work queue entry received in a intercept callback.
264210311Sjmallett *
265210311Sjmallett * @param work_queue_entry
266210311Sjmallett *               Work queue entry to free
267210311Sjmallett * @return Zero on success, Negative on failure.
268210311Sjmallett */
269210311Sjmallettint cvm_oct_free_work(void *work_queue_entry)
270210311Sjmallett{
271210311Sjmallett	cvmx_wqe_t *work = work_queue_entry;
272210311Sjmallett
273210311Sjmallett	int segments = work->word2.s.bufs;
274210311Sjmallett	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
275210311Sjmallett
276210311Sjmallett	while (segments--) {
277210311Sjmallett		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
278210311Sjmallett		if (__predict_false(!segment_ptr.s.i))
279210311Sjmallett			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
280210311Sjmallett		segment_ptr = next_ptr;
281210311Sjmallett	}
282210311Sjmallett	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
283210311Sjmallett
284210311Sjmallett	return 0;
285210311Sjmallett}
286210311Sjmallett
287210311Sjmallett
288210311Sjmallett/**
289210311Sjmallett * Module/ driver initialization. Creates the linux network
290210311Sjmallett * devices.
291210311Sjmallett *
292210311Sjmallett * @return Zero on success
293210311Sjmallett */
294210311Sjmallettint cvm_oct_init_module(device_t bus)
295210311Sjmallett{
296210311Sjmallett	device_t dev;
297210311Sjmallett	int ifnum;
298210311Sjmallett	int num_interfaces;
299210311Sjmallett	int interface;
300210311Sjmallett	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
301210311Sjmallett	int qos;
302210311Sjmallett
303210311Sjmallett	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
304210311Sjmallett
305210311Sjmallett	cvm_oct_rx_initialize();
306210311Sjmallett	cvm_oct_configure_common_hw(bus);
307210311Sjmallett
308210311Sjmallett	cvmx_helper_initialize_packet_io_global();
309210311Sjmallett
310210311Sjmallett	/* Change the input group for all ports before input is enabled */
311210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
312210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
313210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
314210311Sjmallett		int port;
315210311Sjmallett
316210311Sjmallett		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
317210311Sjmallett			cvmx_pip_prt_tagx_t pip_prt_tagx;
318210311Sjmallett			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
319210311Sjmallett			pip_prt_tagx.s.grp = pow_receive_group;
320210311Sjmallett			cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
321210311Sjmallett		}
322210311Sjmallett	}
323210311Sjmallett
324210311Sjmallett	cvmx_helper_ipd_and_packet_input_enable();
325210311Sjmallett
326210311Sjmallett	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
327210311Sjmallett
328213150Sjmallett	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
329213150Sjmallett	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
330213150Sjmallett	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
331213150Sjmallett	    "octe link taskq");
332213150Sjmallett
333210311Sjmallett	/* Initialize the FAU used for counting packet buffers that need to be freed */
334210311Sjmallett	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
335210311Sjmallett
336210311Sjmallett	ifnum = 0;
337210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
338210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
339210311Sjmallett		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
340210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
341210311Sjmallett		int port;
342210311Sjmallett
343210311Sjmallett		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
344210311Sjmallett			cvm_oct_private_t *priv;
345210311Sjmallett			struct ifnet *ifp;
346210311Sjmallett
347210311Sjmallett			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
348210311Sjmallett			if (dev != NULL)
349210311Sjmallett				ifp = if_alloc(IFT_ETHER);
350210311Sjmallett			if (dev == NULL || ifp == NULL) {
351210311Sjmallett				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
352210311Sjmallett				continue;
353210311Sjmallett			}
354210311Sjmallett
355210311Sjmallett			/* Initialize the device private structure. */
356210311Sjmallett			device_probe(dev);
357210311Sjmallett			priv = device_get_softc(dev);
358210311Sjmallett			priv->dev = dev;
359210311Sjmallett			priv->ifp = ifp;
360210311Sjmallett			priv->imode = imode;
361210311Sjmallett			priv->port = port;
362210311Sjmallett			priv->queue = cvmx_pko_get_base_queue(priv->port);
363210311Sjmallett			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
364210311Sjmallett			for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
365210311Sjmallett				cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
366213150Sjmallett			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
367210311Sjmallett
368210311Sjmallett			switch (priv->imode) {
369210311Sjmallett
370210311Sjmallett			/* These types don't support ports to IPD/PKO */
371210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
372210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PCIE:
373210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PICMG:
374210311Sjmallett				break;
375210311Sjmallett
376210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_NPI:
377210311Sjmallett				priv->init = cvm_oct_common_init;
378210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
379210311Sjmallett				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
380210311Sjmallett				break;
381210311Sjmallett
382210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_XAUI:
383210311Sjmallett				priv->init = cvm_oct_xaui_init;
384215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
385210311Sjmallett				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
386210311Sjmallett				break;
387210311Sjmallett
388210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_LOOP:
389210311Sjmallett				priv->init = cvm_oct_common_init;
390210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
391210311Sjmallett				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
392210311Sjmallett				break;
393210311Sjmallett
394210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SGMII:
395210311Sjmallett				priv->init = cvm_oct_sgmii_init;
396215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
397210311Sjmallett				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
398210311Sjmallett				break;
399210311Sjmallett
400210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SPI:
401210311Sjmallett				priv->init = cvm_oct_spi_init;
402210311Sjmallett				priv->uninit = cvm_oct_spi_uninit;
403210311Sjmallett				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
404210311Sjmallett				break;
405210311Sjmallett
406210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_RGMII:
407210311Sjmallett				priv->init = cvm_oct_rgmii_init;
408210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
409210311Sjmallett				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
410210311Sjmallett				break;
411210311Sjmallett
412210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_GMII:
413210311Sjmallett				priv->init = cvm_oct_rgmii_init;
414210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
415210311Sjmallett				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
416210311Sjmallett				break;
417210311Sjmallett			}
418210311Sjmallett
419210311Sjmallett			ifp->if_softc = priv;
420210311Sjmallett
421210311Sjmallett			if (!priv->init) {
422210311Sjmallett				panic("%s: unsupported device type, need to free ifp.", __func__);
423210311Sjmallett			} else
424210311Sjmallett			if (priv->init(ifp) < 0) {
425210311Sjmallett				printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
426210311Sjmallett				interface, priv->port);
427210311Sjmallett				panic("%s: init failed, need to free ifp.", __func__);
428210311Sjmallett			} else {
429210311Sjmallett				cvm_oct_device[priv->port] = ifp;
430210311Sjmallett				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
431210311Sjmallett			}
432210311Sjmallett		}
433210311Sjmallett	}
434210311Sjmallett
435210311Sjmallett	if (INTERRUPT_LIMIT) {
436210311Sjmallett		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
437210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
438210311Sjmallett
439210311Sjmallett		/* Enable POW timer interrupt. It will count when there are packets available */
440210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
441210311Sjmallett	} else {
442210311Sjmallett		/* Enable POW interrupt when our port has at least one packet */
443210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
444210311Sjmallett	}
445210311Sjmallett
446210311Sjmallett	callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
447210311Sjmallett	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
448210311Sjmallett
449210311Sjmallett	return 0;
450210311Sjmallett}
451210311Sjmallett
452210311Sjmallett
453210311Sjmallett/**
454210311Sjmallett * Module / driver shutdown
455210311Sjmallett *
456210311Sjmallett * @return Zero on success
457210311Sjmallett */
458210311Sjmallettvoid cvm_oct_cleanup_module(void)
459210311Sjmallett{
460210311Sjmallett	int port;
461210311Sjmallett
462210311Sjmallett	/* Disable POW interrupt */
463210311Sjmallett	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
464210311Sjmallett
465210311Sjmallett	cvmx_ipd_disable();
466210311Sjmallett
467210311Sjmallett#if 0
468210311Sjmallett	/* Free the interrupt handler */
469210311Sjmallett	free_irq(8 + pow_receive_group, cvm_oct_device);
470210311Sjmallett#endif
471210311Sjmallett
472210311Sjmallett	callout_stop(&cvm_oct_poll_timer);
473210311Sjmallett	cvm_oct_rx_shutdown();
474210311Sjmallett	cvmx_pko_disable();
475210311Sjmallett
476210311Sjmallett	/* Free the ethernet devices */
477210311Sjmallett	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
478210311Sjmallett		if (cvm_oct_device[port]) {
479210311Sjmallett			cvm_oct_tx_shutdown(cvm_oct_device[port]);
480210311Sjmallett#if 0
481210311Sjmallett			unregister_netdev(cvm_oct_device[port]);
482210311Sjmallett			kfree(cvm_oct_device[port]);
483210311Sjmallett#else
484210311Sjmallett			panic("%s: need to detach and free interface.", __func__);
485210311Sjmallett#endif
486210311Sjmallett			cvm_oct_device[port] = NULL;
487210311Sjmallett		}
488210311Sjmallett	}
489210311Sjmallett
490210311Sjmallett	cvmx_pko_shutdown();
491210311Sjmallett
492210311Sjmallett	cvmx_ipd_free_ptr();
493210311Sjmallett
494210311Sjmallett	/* Free the HW pools */
495210311Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
496210311Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
497210311Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
498210311Sjmallett		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
499210311Sjmallett}
500