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$");
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
101219694Sjmallett/*
102219694Sjmallett * Number of buffers in output buffer pool.
103219694Sjmallett */
104219694Sjmallettstatic int cvm_oct_num_output_buffers;
105219694Sjmallett
106219706Sjmallett/*
107219706Sjmallett * The offset from mac_addr_base that should be used for the next port
108219706Sjmallett * that is configured.  By convention, if any mgmt ports exist on the
109219706Sjmallett * chip, they get the first mac addresses.  The ports controlled by
110219706Sjmallett * this driver are numbered sequencially following any mgmt addresses
111219706Sjmallett * that may exist.
112219706Sjmallett */
113219706Sjmallettunsigned int cvm_oct_mac_addr_offset;
114219706Sjmallett
115210311Sjmallett/**
116213150Sjmallett * Function to update link status.
117213150Sjmallett */
118213150Sjmallettstatic void cvm_oct_update_link(void *context, int pending)
119213150Sjmallett{
120213150Sjmallett	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
121213150Sjmallett	struct ifnet *ifp = priv->ifp;
122213150Sjmallett	cvmx_helper_link_info_t link_info;
123213150Sjmallett
124213150Sjmallett	link_info.u64 = priv->link_info;
125213150Sjmallett
126213150Sjmallett	if (link_info.s.link_up) {
127213150Sjmallett		if_link_state_change(ifp, LINK_STATE_UP);
128215959Sjmallett		DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
129215959Sjmallett			   if_name(ifp), link_info.s.speed,
130215959Sjmallett			   (link_info.s.full_duplex) ? "Full" : "Half",
131215959Sjmallett			   priv->port, priv->queue);
132213150Sjmallett	} else {
133213150Sjmallett		if_link_state_change(ifp, LINK_STATE_DOWN);
134213150Sjmallett		DEBUGPRINT("%s: Link down\n", if_name(ifp));
135213150Sjmallett	}
136213150Sjmallett	priv->need_link_update = 0;
137213150Sjmallett}
138213150Sjmallett
139213150Sjmallett/**
140210311Sjmallett * Periodic timer tick for slow management operations
141210311Sjmallett *
142210311Sjmallett * @param arg    Device to check
143210311Sjmallett */
144210311Sjmallettstatic void cvm_do_timer(void *arg)
145210311Sjmallett{
146210311Sjmallett	static int port;
147213807Sjmallett	static int updated;
148210311Sjmallett	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
149210311Sjmallett		if (cvm_oct_device[port]) {
150210311Sjmallett			int queues_per_port;
151210311Sjmallett			int qos;
152210311Sjmallett			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
153213150Sjmallett
154216071Sjmallett			cvm_oct_common_poll(priv->ifp);
155216071Sjmallett			if (priv->need_link_update) {
156216071Sjmallett				updated++;
157216071Sjmallett				taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
158210311Sjmallett			}
159210311Sjmallett
160210311Sjmallett			queues_per_port = cvmx_pko_get_num_queues(port);
161210311Sjmallett			/* Drain any pending packets in the free list */
162210311Sjmallett			for (qos = 0; qos < queues_per_port; qos++) {
163210311Sjmallett				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
164210311Sjmallett					IF_LOCK(&priv->tx_free_queue[qos]);
165210311Sjmallett					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
166210311Sjmallett						struct mbuf *m;
167210311Sjmallett
168210311Sjmallett						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
169210311Sjmallett						m_freem(m);
170210311Sjmallett					}
171210311Sjmallett					IF_UNLOCK(&priv->tx_free_queue[qos]);
172210311Sjmallett
173210311Sjmallett					/*
174210311Sjmallett					 * XXX locking!
175210311Sjmallett					 */
176210311Sjmallett					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
177210311Sjmallett				}
178210311Sjmallett			}
179210311Sjmallett		}
180210311Sjmallett		port++;
181210311Sjmallett		/* Poll the next port in a 50th of a second.
182210311Sjmallett		   This spreads the polling of ports out a little bit */
183210311Sjmallett		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
184210311Sjmallett	} else {
185210311Sjmallett		port = 0;
186213807Sjmallett		/* If any updates were made in this run, continue iterating at
187213807Sjmallett		 * 1/50th of a second, so that if a link has merely gone down
188213807Sjmallett		 * temporarily (e.g. because of interface reinitialization) it
189213807Sjmallett		 * will not be forced to stay down for an entire second.
190213807Sjmallett		 */
191213807Sjmallett		if (updated > 0) {
192213807Sjmallett			updated = 0;
193213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
194213807Sjmallett		} else {
195213807Sjmallett			/* All ports have been polled. Start the next iteration through
196213807Sjmallett			   the ports in one second */
197213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
198213807Sjmallett		}
199210311Sjmallett	}
200210311Sjmallett}
201210311Sjmallett
202210311Sjmallett/**
203210311Sjmallett * Configure common hardware for all interfaces
204210311Sjmallett */
205210311Sjmallettstatic void cvm_oct_configure_common_hw(device_t bus)
206210311Sjmallett{
207210311Sjmallett	struct octebus_softc *sc;
208219694Sjmallett	int pko_queues;
209210311Sjmallett	int error;
210210311Sjmallett	int rid;
211210311Sjmallett
212210311Sjmallett        sc = device_get_softc(bus);
213210311Sjmallett
214210311Sjmallett	/* Setup the FPA */
215210311Sjmallett	cvmx_fpa_enable();
216219694Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
217219694Sjmallett			     num_packet_buffers);
218219694Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
219219694Sjmallett			     num_packet_buffers);
220219694Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) {
221219694Sjmallett		/*
222219694Sjmallett		 * If the FPA uses different pools for output buffers and
223219694Sjmallett		 * packets, size the output buffer pool based on the number
224219694Sjmallett		 * of PKO queues.
225219694Sjmallett		 */
226219694Sjmallett		if (OCTEON_IS_MODEL(OCTEON_CN38XX))
227219694Sjmallett			pko_queues = 128;
228219694Sjmallett		else if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
229219694Sjmallett			pko_queues = 32;
230219694Sjmallett		else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
231219694Sjmallett			pko_queues = 32;
232219694Sjmallett		else
233219694Sjmallett			pko_queues = 256;
234210311Sjmallett
235219694Sjmallett		cvm_oct_num_output_buffers = 4 * pko_queues;
236219694Sjmallett		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
237219694Sjmallett				     CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE,
238219694Sjmallett				     cvm_oct_num_output_buffers);
239219694Sjmallett	}
240219694Sjmallett
241210311Sjmallett	if (USE_RED)
242219694Sjmallett		cvmx_helper_setup_red(num_packet_buffers/4,
243219694Sjmallett				      num_packet_buffers/8);
244210311Sjmallett
245210311Sjmallett	/* Enable the MII interface */
246210311Sjmallett	if (!octeon_is_simulation())
247210311Sjmallett		cvmx_write_csr(CVMX_SMI_EN, 1);
248210311Sjmallett
249210311Sjmallett	/* Register an IRQ hander for to receive POW interrupts */
250210311Sjmallett        rid = 0;
251210311Sjmallett        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
252210311Sjmallett					   CVMX_IRQ_WORKQ0 + pow_receive_group,
253210311Sjmallett					   CVMX_IRQ_WORKQ0 + pow_receive_group,
254210311Sjmallett					   1, RF_ACTIVE);
255210311Sjmallett        if (sc->sc_rx_irq == NULL) {
256210311Sjmallett                device_printf(bus, "could not allocate workq irq");
257210311Sjmallett		return;
258210311Sjmallett        }
259210311Sjmallett
260210311Sjmallett        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
261210311Sjmallett			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
262219695Sjmallett			       &sc->sc_rx_intr_cookie);
263210311Sjmallett        if (error != 0) {
264210311Sjmallett                device_printf(bus, "could not setup workq irq");
265210311Sjmallett		return;
266210311Sjmallett        }
267210311Sjmallett
268210311Sjmallett
269210311Sjmallett#ifdef SMP
270217664Sjmallett	{
271217664Sjmallett		cvmx_ciu_intx0_t en;
272217664Sjmallett		int core;
273217210Sjmallett
274217664Sjmallett		CPU_FOREACH(core) {
275217664Sjmallett			if (core == PCPU_GET(cpuid))
276217664Sjmallett				continue;
277217210Sjmallett
278217664Sjmallett			en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2));
279217664Sjmallett			en.s.workq |= (1<<pow_receive_group);
280217664Sjmallett			cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64);
281210311Sjmallett		}
282210311Sjmallett	}
283210311Sjmallett#endif
284210311Sjmallett}
285210311Sjmallett
286210311Sjmallett
287210311Sjmallett/**
288210311Sjmallett * Free a work queue entry received in a intercept callback.
289210311Sjmallett *
290210311Sjmallett * @param work_queue_entry
291210311Sjmallett *               Work queue entry to free
292210311Sjmallett * @return Zero on success, Negative on failure.
293210311Sjmallett */
294210311Sjmallettint cvm_oct_free_work(void *work_queue_entry)
295210311Sjmallett{
296210311Sjmallett	cvmx_wqe_t *work = work_queue_entry;
297210311Sjmallett
298210311Sjmallett	int segments = work->word2.s.bufs;
299210311Sjmallett	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
300210311Sjmallett
301210311Sjmallett	while (segments--) {
302210311Sjmallett		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
303210311Sjmallett		if (__predict_false(!segment_ptr.s.i))
304210311Sjmallett			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
305210311Sjmallett		segment_ptr = next_ptr;
306210311Sjmallett	}
307210311Sjmallett	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
308210311Sjmallett
309210311Sjmallett	return 0;
310210311Sjmallett}
311210311Sjmallett
312210311Sjmallett
313210311Sjmallett/**
314210311Sjmallett * Module/ driver initialization. Creates the linux network
315210311Sjmallett * devices.
316210311Sjmallett *
317210311Sjmallett * @return Zero on success
318210311Sjmallett */
319210311Sjmallettint cvm_oct_init_module(device_t bus)
320210311Sjmallett{
321210311Sjmallett	device_t dev;
322210311Sjmallett	int ifnum;
323210311Sjmallett	int num_interfaces;
324210311Sjmallett	int interface;
325210311Sjmallett	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
326210311Sjmallett	int qos;
327210311Sjmallett
328210311Sjmallett	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
329210311Sjmallett
330219706Sjmallett	/*
331219706Sjmallett	 * MAC addresses for this driver start after the management
332219706Sjmallett	 * ports.
333219706Sjmallett	 *
334219706Sjmallett	 * XXX Would be nice if __cvmx_mgmt_port_num_ports() were
335219706Sjmallett	 *     not static to cvmx-mgmt-port.c.
336219706Sjmallett	 */
337219706Sjmallett	if (OCTEON_IS_MODEL(OCTEON_CN56XX))
338219706Sjmallett		cvm_oct_mac_addr_offset = 1;
339219706Sjmallett	else if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN63XX))
340219706Sjmallett		cvm_oct_mac_addr_offset = 2;
341219706Sjmallett	else
342219706Sjmallett		cvm_oct_mac_addr_offset = 0;
343219706Sjmallett
344210311Sjmallett	cvm_oct_rx_initialize();
345210311Sjmallett	cvm_oct_configure_common_hw(bus);
346210311Sjmallett
347210311Sjmallett	cvmx_helper_initialize_packet_io_global();
348210311Sjmallett
349210311Sjmallett	/* Change the input group for all ports before input is enabled */
350210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
351210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
352210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
353210311Sjmallett		int port;
354210311Sjmallett
355219694Sjmallett		for (port = 0; port < num_ports; port++) {
356210311Sjmallett			cvmx_pip_prt_tagx_t pip_prt_tagx;
357219694Sjmallett			int pkind = cvmx_helper_get_ipd_port(interface, port);
358219694Sjmallett
359219694Sjmallett			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pkind));
360210311Sjmallett			pip_prt_tagx.s.grp = pow_receive_group;
361219694Sjmallett			cvmx_write_csr(CVMX_PIP_PRT_TAGX(pkind), pip_prt_tagx.u64);
362210311Sjmallett		}
363210311Sjmallett	}
364210311Sjmallett
365210311Sjmallett	cvmx_helper_ipd_and_packet_input_enable();
366210311Sjmallett
367210311Sjmallett	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
368210311Sjmallett
369213150Sjmallett	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
370213150Sjmallett	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
371213150Sjmallett	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
372213150Sjmallett	    "octe link taskq");
373213150Sjmallett
374210311Sjmallett	/* Initialize the FAU used for counting packet buffers that need to be freed */
375210311Sjmallett	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
376210311Sjmallett
377210311Sjmallett	ifnum = 0;
378210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
379210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
380210311Sjmallett		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
381210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
382210311Sjmallett		int port;
383210311Sjmallett
384210311Sjmallett		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
385210311Sjmallett			cvm_oct_private_t *priv;
386210311Sjmallett			struct ifnet *ifp;
387210311Sjmallett
388210311Sjmallett			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
389210311Sjmallett			if (dev != NULL)
390210311Sjmallett				ifp = if_alloc(IFT_ETHER);
391210311Sjmallett			if (dev == NULL || ifp == NULL) {
392210311Sjmallett				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
393210311Sjmallett				continue;
394210311Sjmallett			}
395210311Sjmallett
396210311Sjmallett			/* Initialize the device private structure. */
397210311Sjmallett			device_probe(dev);
398210311Sjmallett			priv = device_get_softc(dev);
399210311Sjmallett			priv->dev = dev;
400210311Sjmallett			priv->ifp = ifp;
401210311Sjmallett			priv->imode = imode;
402210311Sjmallett			priv->port = port;
403210311Sjmallett			priv->queue = cvmx_pko_get_base_queue(priv->port);
404210311Sjmallett			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
405210311Sjmallett			for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
406210311Sjmallett				cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
407213150Sjmallett			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
408210311Sjmallett
409210311Sjmallett			switch (priv->imode) {
410210311Sjmallett
411210311Sjmallett			/* These types don't support ports to IPD/PKO */
412210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
413210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PCIE:
414210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PICMG:
415210311Sjmallett				break;
416210311Sjmallett
417210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_NPI:
418210311Sjmallett				priv->init = cvm_oct_common_init;
419210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
420210311Sjmallett				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
421210311Sjmallett				break;
422210311Sjmallett
423210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_XAUI:
424210311Sjmallett				priv->init = cvm_oct_xaui_init;
425215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
426210311Sjmallett				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
427210311Sjmallett				break;
428210311Sjmallett
429210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_LOOP:
430210311Sjmallett				priv->init = cvm_oct_common_init;
431210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
432210311Sjmallett				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
433210311Sjmallett				break;
434210311Sjmallett
435210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SGMII:
436210311Sjmallett				priv->init = cvm_oct_sgmii_init;
437215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
438210311Sjmallett				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
439210311Sjmallett				break;
440210311Sjmallett
441210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SPI:
442210311Sjmallett				priv->init = cvm_oct_spi_init;
443210311Sjmallett				priv->uninit = cvm_oct_spi_uninit;
444210311Sjmallett				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
445210311Sjmallett				break;
446210311Sjmallett
447210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_RGMII:
448210311Sjmallett				priv->init = cvm_oct_rgmii_init;
449210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
450210311Sjmallett				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
451210311Sjmallett				break;
452210311Sjmallett
453210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_GMII:
454210311Sjmallett				priv->init = cvm_oct_rgmii_init;
455210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
456210311Sjmallett				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
457210311Sjmallett				break;
458210311Sjmallett			}
459210311Sjmallett
460210311Sjmallett			ifp->if_softc = priv;
461210311Sjmallett
462210311Sjmallett			if (!priv->init) {
463210311Sjmallett				panic("%s: unsupported device type, need to free ifp.", __func__);
464210311Sjmallett			} else
465210311Sjmallett			if (priv->init(ifp) < 0) {
466210311Sjmallett				printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
467210311Sjmallett				interface, priv->port);
468210311Sjmallett				panic("%s: init failed, need to free ifp.", __func__);
469210311Sjmallett			} else {
470210311Sjmallett				cvm_oct_device[priv->port] = ifp;
471210311Sjmallett				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
472210311Sjmallett			}
473210311Sjmallett		}
474210311Sjmallett	}
475210311Sjmallett
476210311Sjmallett	if (INTERRUPT_LIMIT) {
477210311Sjmallett		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
478210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
479210311Sjmallett
480210311Sjmallett		/* Enable POW timer interrupt. It will count when there are packets available */
481210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
482210311Sjmallett	} else {
483210311Sjmallett		/* Enable POW interrupt when our port has at least one packet */
484210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
485210311Sjmallett	}
486210311Sjmallett
487210311Sjmallett	callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
488210311Sjmallett	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
489210311Sjmallett
490210311Sjmallett	return 0;
491210311Sjmallett}
492210311Sjmallett
493210311Sjmallett
494210311Sjmallett/**
495210311Sjmallett * Module / driver shutdown
496210311Sjmallett *
497210311Sjmallett * @return Zero on success
498210311Sjmallett */
499219695Sjmallettvoid cvm_oct_cleanup_module(device_t bus)
500210311Sjmallett{
501210311Sjmallett	int port;
502219695Sjmallett	struct octebus_softc *sc = device_get_softc(bus);
503210311Sjmallett
504210311Sjmallett	/* Disable POW interrupt */
505210311Sjmallett	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
506210311Sjmallett
507210311Sjmallett	/* Free the interrupt handler */
508219695Sjmallett	bus_teardown_intr(bus, sc->sc_rx_irq, sc->sc_rx_intr_cookie);
509210311Sjmallett
510210311Sjmallett	callout_stop(&cvm_oct_poll_timer);
511210311Sjmallett	cvm_oct_rx_shutdown();
512210311Sjmallett
513215990Sjmallett	cvmx_helper_shutdown_packet_io_global();
514215990Sjmallett
515210311Sjmallett	/* Free the ethernet devices */
516210311Sjmallett	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
517210311Sjmallett		if (cvm_oct_device[port]) {
518210311Sjmallett			cvm_oct_tx_shutdown(cvm_oct_device[port]);
519210311Sjmallett#if 0
520210311Sjmallett			unregister_netdev(cvm_oct_device[port]);
521210311Sjmallett			kfree(cvm_oct_device[port]);
522210311Sjmallett#else
523210311Sjmallett			panic("%s: need to detach and free interface.", __func__);
524210311Sjmallett#endif
525210311Sjmallett			cvm_oct_device[port] = NULL;
526210311Sjmallett		}
527210311Sjmallett	}
528219694Sjmallett	/* Free the HW pools */
529219694Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
530219694Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
531219694Sjmallett
532219694Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
533219694Sjmallett		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, cvm_oct_num_output_buffers);
534219694Sjmallett
535219694Sjmallett	/* Disable FPA, all buffers are free, not done by helper shutdown. */
536219694Sjmallett	cvmx_fpa_disable();
537210311Sjmallett}
538