ethernet.c revision 226024
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 226024 2011-10-04 20:17:43Z marcel $");
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 * Periodic timer to check auto negotiation
81210311Sjmallett */
82210311Sjmallettstatic struct callout cvm_oct_poll_timer;
83210311Sjmallett
84210311Sjmallett/**
85210311Sjmallett * Array of every ethernet device owned by this driver indexed by
86210311Sjmallett * the ipd input port number.
87210311Sjmallett */
88210311Sjmallettstruct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
89210311Sjmallett
90213150Sjmallett/**
91213150Sjmallett * Task to handle link status changes.
92213150Sjmallett */
93213150Sjmallettstatic struct taskqueue *cvm_oct_link_taskq;
94210311Sjmallett
95219694Sjmallett/*
96219694Sjmallett * Number of buffers in output buffer pool.
97219694Sjmallett */
98219694Sjmallettstatic int cvm_oct_num_output_buffers;
99219694Sjmallett
100219706Sjmallett/*
101219706Sjmallett * The offset from mac_addr_base that should be used for the next port
102219706Sjmallett * that is configured.  By convention, if any mgmt ports exist on the
103219706Sjmallett * chip, they get the first mac addresses.  The ports controlled by
104219706Sjmallett * this driver are numbered sequencially following any mgmt addresses
105219706Sjmallett * that may exist.
106219706Sjmallett */
107219706Sjmallettunsigned int cvm_oct_mac_addr_offset;
108219706Sjmallett
109210311Sjmallett/**
110213150Sjmallett * Function to update link status.
111213150Sjmallett */
112213150Sjmallettstatic void cvm_oct_update_link(void *context, int pending)
113213150Sjmallett{
114213150Sjmallett	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
115213150Sjmallett	struct ifnet *ifp = priv->ifp;
116213150Sjmallett	cvmx_helper_link_info_t link_info;
117213150Sjmallett
118213150Sjmallett	link_info.u64 = priv->link_info;
119213150Sjmallett
120213150Sjmallett	if (link_info.s.link_up) {
121213150Sjmallett		if_link_state_change(ifp, LINK_STATE_UP);
122215959Sjmallett		DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
123215959Sjmallett			   if_name(ifp), link_info.s.speed,
124215959Sjmallett			   (link_info.s.full_duplex) ? "Full" : "Half",
125215959Sjmallett			   priv->port, priv->queue);
126213150Sjmallett	} else {
127213150Sjmallett		if_link_state_change(ifp, LINK_STATE_DOWN);
128213150Sjmallett		DEBUGPRINT("%s: Link down\n", if_name(ifp));
129213150Sjmallett	}
130213150Sjmallett	priv->need_link_update = 0;
131213150Sjmallett}
132213150Sjmallett
133213150Sjmallett/**
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;
141213807Sjmallett	static int updated;
142210311Sjmallett	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
143210311Sjmallett		if (cvm_oct_device[port]) {
144210311Sjmallett			int queues_per_port;
145210311Sjmallett			int qos;
146210311Sjmallett			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
147213150Sjmallett
148216071Sjmallett			cvm_oct_common_poll(priv->ifp);
149216071Sjmallett			if (priv->need_link_update) {
150216071Sjmallett				updated++;
151216071Sjmallett				taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
152210311Sjmallett			}
153210311Sjmallett
154210311Sjmallett			queues_per_port = cvmx_pko_get_num_queues(port);
155210311Sjmallett			/* Drain any pending packets in the free list */
156210311Sjmallett			for (qos = 0; qos < queues_per_port; qos++) {
157210311Sjmallett				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
158210311Sjmallett					IF_LOCK(&priv->tx_free_queue[qos]);
159210311Sjmallett					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
160210311Sjmallett						struct mbuf *m;
161210311Sjmallett
162210311Sjmallett						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
163210311Sjmallett						m_freem(m);
164210311Sjmallett					}
165210311Sjmallett					IF_UNLOCK(&priv->tx_free_queue[qos]);
166210311Sjmallett
167210311Sjmallett					/*
168210311Sjmallett					 * XXX locking!
169210311Sjmallett					 */
170210311Sjmallett					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
171210311Sjmallett				}
172210311Sjmallett			}
173210311Sjmallett		}
174210311Sjmallett		port++;
175210311Sjmallett		/* Poll the next port in a 50th of a second.
176210311Sjmallett		   This spreads the polling of ports out a little bit */
177210311Sjmallett		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
178210311Sjmallett	} else {
179210311Sjmallett		port = 0;
180213807Sjmallett		/* If any updates were made in this run, continue iterating at
181213807Sjmallett		 * 1/50th of a second, so that if a link has merely gone down
182213807Sjmallett		 * temporarily (e.g. because of interface reinitialization) it
183213807Sjmallett		 * will not be forced to stay down for an entire second.
184213807Sjmallett		 */
185213807Sjmallett		if (updated > 0) {
186213807Sjmallett			updated = 0;
187213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
188213807Sjmallett		} else {
189213807Sjmallett			/* All ports have been polled. Start the next iteration through
190213807Sjmallett			   the ports in one second */
191213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
192213807Sjmallett		}
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;
202219694Sjmallett	int pko_queues;
203210311Sjmallett	int error;
204210311Sjmallett	int rid;
205210311Sjmallett
206210311Sjmallett        sc = device_get_softc(bus);
207210311Sjmallett
208210311Sjmallett	/* Setup the FPA */
209210311Sjmallett	cvmx_fpa_enable();
210219694Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
211219694Sjmallett			     num_packet_buffers);
212219694Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
213219694Sjmallett			     num_packet_buffers);
214219694Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) {
215219694Sjmallett		/*
216219694Sjmallett		 * If the FPA uses different pools for output buffers and
217219694Sjmallett		 * packets, size the output buffer pool based on the number
218219694Sjmallett		 * of PKO queues.
219219694Sjmallett		 */
220219694Sjmallett		if (OCTEON_IS_MODEL(OCTEON_CN38XX))
221219694Sjmallett			pko_queues = 128;
222219694Sjmallett		else if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
223219694Sjmallett			pko_queues = 32;
224219694Sjmallett		else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
225219694Sjmallett			pko_queues = 32;
226219694Sjmallett		else
227219694Sjmallett			pko_queues = 256;
228210311Sjmallett
229219694Sjmallett		cvm_oct_num_output_buffers = 4 * pko_queues;
230219694Sjmallett		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
231219694Sjmallett				     CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE,
232219694Sjmallett				     cvm_oct_num_output_buffers);
233219694Sjmallett	}
234219694Sjmallett
235210311Sjmallett	if (USE_RED)
236219694Sjmallett		cvmx_helper_setup_red(num_packet_buffers/4,
237219694Sjmallett				      num_packet_buffers/8);
238210311Sjmallett
239210311Sjmallett	/* Enable the MII interface */
240210311Sjmallett	if (!octeon_is_simulation())
241210311Sjmallett		cvmx_write_csr(CVMX_SMI_EN, 1);
242210311Sjmallett
243210311Sjmallett	/* Register an IRQ hander for to receive POW interrupts */
244210311Sjmallett        rid = 0;
245210311Sjmallett        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
246210311Sjmallett					   CVMX_IRQ_WORKQ0 + pow_receive_group,
247210311Sjmallett					   CVMX_IRQ_WORKQ0 + pow_receive_group,
248210311Sjmallett					   1, RF_ACTIVE);
249210311Sjmallett        if (sc->sc_rx_irq == NULL) {
250210311Sjmallett                device_printf(bus, "could not allocate workq irq");
251210311Sjmallett		return;
252210311Sjmallett        }
253210311Sjmallett
254210311Sjmallett        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
255210311Sjmallett			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
256219695Sjmallett			       &sc->sc_rx_intr_cookie);
257210311Sjmallett        if (error != 0) {
258210311Sjmallett                device_printf(bus, "could not setup workq irq");
259210311Sjmallett		return;
260210311Sjmallett        }
261210311Sjmallett
262210311Sjmallett
263210311Sjmallett#ifdef SMP
264217664Sjmallett	{
265217664Sjmallett		cvmx_ciu_intx0_t en;
266217664Sjmallett		int core;
267217210Sjmallett
268217664Sjmallett		CPU_FOREACH(core) {
269217664Sjmallett			if (core == PCPU_GET(cpuid))
270217664Sjmallett				continue;
271217210Sjmallett
272217664Sjmallett			en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2));
273217664Sjmallett			en.s.workq |= (1<<pow_receive_group);
274217664Sjmallett			cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64);
275210311Sjmallett		}
276210311Sjmallett	}
277210311Sjmallett#endif
278210311Sjmallett}
279210311Sjmallett
280210311Sjmallett
281210311Sjmallett/**
282210311Sjmallett * Free a work queue entry received in a intercept callback.
283210311Sjmallett *
284210311Sjmallett * @param work_queue_entry
285210311Sjmallett *               Work queue entry to free
286210311Sjmallett * @return Zero on success, Negative on failure.
287210311Sjmallett */
288210311Sjmallettint cvm_oct_free_work(void *work_queue_entry)
289210311Sjmallett{
290210311Sjmallett	cvmx_wqe_t *work = work_queue_entry;
291210311Sjmallett
292210311Sjmallett	int segments = work->word2.s.bufs;
293210311Sjmallett	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
294210311Sjmallett
295210311Sjmallett	while (segments--) {
296210311Sjmallett		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
297210311Sjmallett		if (__predict_false(!segment_ptr.s.i))
298210311Sjmallett			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
299210311Sjmallett		segment_ptr = next_ptr;
300210311Sjmallett	}
301210311Sjmallett	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
302210311Sjmallett
303210311Sjmallett	return 0;
304210311Sjmallett}
305210311Sjmallett
306210311Sjmallett
307210311Sjmallett/**
308210311Sjmallett * Module/ driver initialization. Creates the linux network
309210311Sjmallett * devices.
310210311Sjmallett *
311210311Sjmallett * @return Zero on success
312210311Sjmallett */
313210311Sjmallettint cvm_oct_init_module(device_t bus)
314210311Sjmallett{
315210311Sjmallett	device_t dev;
316210311Sjmallett	int ifnum;
317210311Sjmallett	int num_interfaces;
318210311Sjmallett	int interface;
319210311Sjmallett	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
320210311Sjmallett	int qos;
321210311Sjmallett
322210311Sjmallett	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
323210311Sjmallett
324219706Sjmallett	/*
325219706Sjmallett	 * MAC addresses for this driver start after the management
326219706Sjmallett	 * ports.
327219706Sjmallett	 *
328219706Sjmallett	 * XXX Would be nice if __cvmx_mgmt_port_num_ports() were
329219706Sjmallett	 *     not static to cvmx-mgmt-port.c.
330219706Sjmallett	 */
331219706Sjmallett	if (OCTEON_IS_MODEL(OCTEON_CN56XX))
332219706Sjmallett		cvm_oct_mac_addr_offset = 1;
333219706Sjmallett	else if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN63XX))
334219706Sjmallett		cvm_oct_mac_addr_offset = 2;
335219706Sjmallett	else
336219706Sjmallett		cvm_oct_mac_addr_offset = 0;
337219706Sjmallett
338210311Sjmallett	cvm_oct_rx_initialize();
339210311Sjmallett	cvm_oct_configure_common_hw(bus);
340210311Sjmallett
341210311Sjmallett	cvmx_helper_initialize_packet_io_global();
342210311Sjmallett
343210311Sjmallett	/* Change the input group for all ports before input is enabled */
344210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
345210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
346210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
347210311Sjmallett		int port;
348210311Sjmallett
349219694Sjmallett		for (port = 0; port < num_ports; port++) {
350210311Sjmallett			cvmx_pip_prt_tagx_t pip_prt_tagx;
351219694Sjmallett			int pkind = cvmx_helper_get_ipd_port(interface, port);
352219694Sjmallett
353219694Sjmallett			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pkind));
354210311Sjmallett			pip_prt_tagx.s.grp = pow_receive_group;
355219694Sjmallett			cvmx_write_csr(CVMX_PIP_PRT_TAGX(pkind), pip_prt_tagx.u64);
356210311Sjmallett		}
357210311Sjmallett	}
358210311Sjmallett
359210311Sjmallett	cvmx_helper_ipd_and_packet_input_enable();
360210311Sjmallett
361210311Sjmallett	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
362210311Sjmallett
363213150Sjmallett	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
364213150Sjmallett	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
365213150Sjmallett	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
366213150Sjmallett	    "octe link taskq");
367213150Sjmallett
368210311Sjmallett	/* Initialize the FAU used for counting packet buffers that need to be freed */
369210311Sjmallett	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
370210311Sjmallett
371210311Sjmallett	ifnum = 0;
372210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
373210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
374210311Sjmallett		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
375210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
376210311Sjmallett		int port;
377210311Sjmallett
378210311Sjmallett		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
379210311Sjmallett			cvm_oct_private_t *priv;
380210311Sjmallett			struct ifnet *ifp;
381210311Sjmallett
382210311Sjmallett			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
383210311Sjmallett			if (dev != NULL)
384210311Sjmallett				ifp = if_alloc(IFT_ETHER);
385210311Sjmallett			if (dev == NULL || ifp == NULL) {
386210311Sjmallett				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
387210311Sjmallett				continue;
388210311Sjmallett			}
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);
401213150Sjmallett			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
402210311Sjmallett
403210311Sjmallett			switch (priv->imode) {
404210311Sjmallett
405210311Sjmallett			/* These types don't support ports to IPD/PKO */
406210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
407210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PCIE:
408210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PICMG:
409210311Sjmallett				break;
410210311Sjmallett
411210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_NPI:
412210311Sjmallett				priv->init = cvm_oct_common_init;
413210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
414210311Sjmallett				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
415210311Sjmallett				break;
416210311Sjmallett
417210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_XAUI:
418210311Sjmallett				priv->init = cvm_oct_xaui_init;
419215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
420210311Sjmallett				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
421210311Sjmallett				break;
422210311Sjmallett
423210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_LOOP:
424210311Sjmallett				priv->init = cvm_oct_common_init;
425210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
426210311Sjmallett				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
427210311Sjmallett				break;
428210311Sjmallett
429210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SGMII:
430210311Sjmallett				priv->init = cvm_oct_sgmii_init;
431215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
432210311Sjmallett				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
433210311Sjmallett				break;
434210311Sjmallett
435210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SPI:
436210311Sjmallett				priv->init = cvm_oct_spi_init;
437210311Sjmallett				priv->uninit = cvm_oct_spi_uninit;
438210311Sjmallett				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
439210311Sjmallett				break;
440210311Sjmallett
441210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_RGMII:
442210311Sjmallett				priv->init = cvm_oct_rgmii_init;
443210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
444210311Sjmallett				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
445210311Sjmallett				break;
446210311Sjmallett
447210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_GMII:
448210311Sjmallett				priv->init = cvm_oct_rgmii_init;
449210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
450210311Sjmallett				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
451210311Sjmallett				break;
452210311Sjmallett			}
453210311Sjmallett
454210311Sjmallett			ifp->if_softc = priv;
455210311Sjmallett
456210311Sjmallett			if (!priv->init) {
457210311Sjmallett				panic("%s: unsupported device type, need to free ifp.", __func__);
458210311Sjmallett			} else
459210311Sjmallett			if (priv->init(ifp) < 0) {
460210311Sjmallett				printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
461210311Sjmallett				interface, priv->port);
462210311Sjmallett				panic("%s: init failed, need to free ifp.", __func__);
463210311Sjmallett			} else {
464210311Sjmallett				cvm_oct_device[priv->port] = ifp;
465210311Sjmallett				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
466210311Sjmallett			}
467210311Sjmallett		}
468210311Sjmallett	}
469210311Sjmallett
470210311Sjmallett	if (INTERRUPT_LIMIT) {
471210311Sjmallett		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
472226024Smarcel		cvmx_write_csr(CVMX_POW_WQ_INT_PC, cvmx_clock_get_rate(CVMX_CLOCK_CORE)/(INTERRUPT_LIMIT*16*256)<<8);
473210311Sjmallett
474210311Sjmallett		/* Enable POW timer interrupt. It will count when there are packets available */
475210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
476210311Sjmallett	} else {
477210311Sjmallett		/* Enable POW interrupt when our port has at least one packet */
478210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
479210311Sjmallett	}
480210311Sjmallett
481210311Sjmallett	callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
482210311Sjmallett	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
483210311Sjmallett
484210311Sjmallett	return 0;
485210311Sjmallett}
486210311Sjmallett
487210311Sjmallett
488210311Sjmallett/**
489210311Sjmallett * Module / driver shutdown
490210311Sjmallett *
491210311Sjmallett * @return Zero on success
492210311Sjmallett */
493219695Sjmallettvoid cvm_oct_cleanup_module(device_t bus)
494210311Sjmallett{
495210311Sjmallett	int port;
496219695Sjmallett	struct octebus_softc *sc = device_get_softc(bus);
497210311Sjmallett
498210311Sjmallett	/* Disable POW interrupt */
499210311Sjmallett	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
500210311Sjmallett
501210311Sjmallett	/* Free the interrupt handler */
502219695Sjmallett	bus_teardown_intr(bus, sc->sc_rx_irq, sc->sc_rx_intr_cookie);
503210311Sjmallett
504210311Sjmallett	callout_stop(&cvm_oct_poll_timer);
505210311Sjmallett	cvm_oct_rx_shutdown();
506210311Sjmallett
507215990Sjmallett	cvmx_helper_shutdown_packet_io_global();
508215990Sjmallett
509210311Sjmallett	/* Free the ethernet devices */
510210311Sjmallett	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
511210311Sjmallett		if (cvm_oct_device[port]) {
512210311Sjmallett			cvm_oct_tx_shutdown(cvm_oct_device[port]);
513210311Sjmallett#if 0
514210311Sjmallett			unregister_netdev(cvm_oct_device[port]);
515210311Sjmallett			kfree(cvm_oct_device[port]);
516210311Sjmallett#else
517210311Sjmallett			panic("%s: need to detach and free interface.", __func__);
518210311Sjmallett#endif
519210311Sjmallett			cvm_oct_device[port] = NULL;
520210311Sjmallett		}
521210311Sjmallett	}
522219694Sjmallett	/* Free the HW pools */
523219694Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
524219694Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
525219694Sjmallett
526219694Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
527219694Sjmallett		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, cvm_oct_num_output_buffers);
528219694Sjmallett
529219694Sjmallett	/* Disable FPA, all buffers are free, not done by helper shutdown. */
530219694Sjmallett	cvmx_fpa_disable();
531210311Sjmallett}
532