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>
47257324Sglebius#include <net/if_var.h>
48210311Sjmallett#include <net/if_types.h>
49210311Sjmallett
50210311Sjmallett#include "wrapper-cvmx-includes.h"
51210311Sjmallett#include "ethernet-headers.h"
52210311Sjmallett
53210311Sjmallett#include "octebusvar.h"
54210311Sjmallett
55210311Sjmallett/*
56210311Sjmallett * XXX/juli
57210311Sjmallett * Convert 0444 to tunables, 0644 to sysctls.
58210311Sjmallett */
59210311Sjmallett#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
60210311Sjmallettint num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS;
61210311Sjmallett#else
62210311Sjmallettint num_packet_buffers = 1024;
63210311Sjmallett#endif
64210311SjmallettTUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
65210311Sjmallett/*
66210311Sjmallett		 "\t\tNumber of packet buffers to allocate and store in the\n"
67210311Sjmallett		 "\t\tFPA. By default, 1024 packet buffers are used unless\n"
68210311Sjmallett		 "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */
69210311Sjmallett
70210311Sjmallettint pow_receive_group = 15;
71210311SjmallettTUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
72210311Sjmallett/*
73210311Sjmallett		 "\t\tPOW group to receive packets from. All ethernet hardware\n"
74210311Sjmallett		 "\t\twill be configured to send incomming packets to this POW\n"
75210311Sjmallett		 "\t\tgroup. Also any other software can submit packets to this\n"
76210311Sjmallett		 "\t\tgroup for the kernel to process." */
77210311Sjmallett
78210311Sjmallett/**
79210311Sjmallett * Periodic timer to check auto negotiation
80210311Sjmallett */
81210311Sjmallettstatic struct callout cvm_oct_poll_timer;
82210311Sjmallett
83210311Sjmallett/**
84210311Sjmallett * Array of every ethernet device owned by this driver indexed by
85210311Sjmallett * the ipd input port number.
86210311Sjmallett */
87210311Sjmallettstruct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
88210311Sjmallett
89213150Sjmallett/**
90213150Sjmallett * Task to handle link status changes.
91213150Sjmallett */
92213150Sjmallettstatic struct taskqueue *cvm_oct_link_taskq;
93210311Sjmallett
94219694Sjmallett/*
95219694Sjmallett * Number of buffers in output buffer pool.
96219694Sjmallett */
97219694Sjmallettstatic int cvm_oct_num_output_buffers;
98219694Sjmallett
99210311Sjmallett/**
100213150Sjmallett * Function to update link status.
101213150Sjmallett */
102213150Sjmallettstatic void cvm_oct_update_link(void *context, int pending)
103213150Sjmallett{
104213150Sjmallett	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
105213150Sjmallett	struct ifnet *ifp = priv->ifp;
106213150Sjmallett	cvmx_helper_link_info_t link_info;
107213150Sjmallett
108213150Sjmallett	link_info.u64 = priv->link_info;
109213150Sjmallett
110213150Sjmallett	if (link_info.s.link_up) {
111213150Sjmallett		if_link_state_change(ifp, LINK_STATE_UP);
112215959Sjmallett		DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
113215959Sjmallett			   if_name(ifp), link_info.s.speed,
114215959Sjmallett			   (link_info.s.full_duplex) ? "Full" : "Half",
115215959Sjmallett			   priv->port, priv->queue);
116213150Sjmallett	} else {
117213150Sjmallett		if_link_state_change(ifp, LINK_STATE_DOWN);
118213150Sjmallett		DEBUGPRINT("%s: Link down\n", if_name(ifp));
119213150Sjmallett	}
120213150Sjmallett	priv->need_link_update = 0;
121213150Sjmallett}
122213150Sjmallett
123213150Sjmallett/**
124210311Sjmallett * Periodic timer tick for slow management operations
125210311Sjmallett *
126210311Sjmallett * @param arg    Device to check
127210311Sjmallett */
128210311Sjmallettstatic void cvm_do_timer(void *arg)
129210311Sjmallett{
130210311Sjmallett	static int port;
131213807Sjmallett	static int updated;
132210311Sjmallett	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
133210311Sjmallett		if (cvm_oct_device[port]) {
134210311Sjmallett			int queues_per_port;
135210311Sjmallett			int qos;
136210311Sjmallett			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
137213150Sjmallett
138216071Sjmallett			cvm_oct_common_poll(priv->ifp);
139216071Sjmallett			if (priv->need_link_update) {
140216071Sjmallett				updated++;
141216071Sjmallett				taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
142210311Sjmallett			}
143210311Sjmallett
144210311Sjmallett			queues_per_port = cvmx_pko_get_num_queues(port);
145210311Sjmallett			/* Drain any pending packets in the free list */
146210311Sjmallett			for (qos = 0; qos < queues_per_port; qos++) {
147210311Sjmallett				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
148210311Sjmallett					IF_LOCK(&priv->tx_free_queue[qos]);
149210311Sjmallett					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
150210311Sjmallett						struct mbuf *m;
151210311Sjmallett
152210311Sjmallett						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
153210311Sjmallett						m_freem(m);
154210311Sjmallett					}
155210311Sjmallett					IF_UNLOCK(&priv->tx_free_queue[qos]);
156210311Sjmallett
157210311Sjmallett					/*
158210311Sjmallett					 * XXX locking!
159210311Sjmallett					 */
160210311Sjmallett					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
161210311Sjmallett				}
162210311Sjmallett			}
163210311Sjmallett		}
164210311Sjmallett		port++;
165210311Sjmallett		/* Poll the next port in a 50th of a second.
166210311Sjmallett		   This spreads the polling of ports out a little bit */
167210311Sjmallett		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
168210311Sjmallett	} else {
169210311Sjmallett		port = 0;
170213807Sjmallett		/* If any updates were made in this run, continue iterating at
171213807Sjmallett		 * 1/50th of a second, so that if a link has merely gone down
172213807Sjmallett		 * temporarily (e.g. because of interface reinitialization) it
173213807Sjmallett		 * will not be forced to stay down for an entire second.
174213807Sjmallett		 */
175213807Sjmallett		if (updated > 0) {
176213807Sjmallett			updated = 0;
177213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
178213807Sjmallett		} else {
179213807Sjmallett			/* All ports have been polled. Start the next iteration through
180213807Sjmallett			   the ports in one second */
181213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
182213807Sjmallett		}
183210311Sjmallett	}
184210311Sjmallett}
185210311Sjmallett
186210311Sjmallett/**
187210311Sjmallett * Configure common hardware for all interfaces
188210311Sjmallett */
189210311Sjmallettstatic void cvm_oct_configure_common_hw(device_t bus)
190210311Sjmallett{
191210311Sjmallett	struct octebus_softc *sc;
192219694Sjmallett	int pko_queues;
193210311Sjmallett	int error;
194210311Sjmallett	int rid;
195210311Sjmallett
196210311Sjmallett        sc = device_get_softc(bus);
197210311Sjmallett
198210311Sjmallett	/* Setup the FPA */
199210311Sjmallett	cvmx_fpa_enable();
200219694Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
201219694Sjmallett			     num_packet_buffers);
202219694Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
203219694Sjmallett			     num_packet_buffers);
204219694Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) {
205219694Sjmallett		/*
206219694Sjmallett		 * If the FPA uses different pools for output buffers and
207219694Sjmallett		 * packets, size the output buffer pool based on the number
208219694Sjmallett		 * of PKO queues.
209219694Sjmallett		 */
210219694Sjmallett		if (OCTEON_IS_MODEL(OCTEON_CN38XX))
211219694Sjmallett			pko_queues = 128;
212219694Sjmallett		else if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
213219694Sjmallett			pko_queues = 32;
214219694Sjmallett		else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
215219694Sjmallett			pko_queues = 32;
216219694Sjmallett		else
217219694Sjmallett			pko_queues = 256;
218210311Sjmallett
219219694Sjmallett		cvm_oct_num_output_buffers = 4 * pko_queues;
220219694Sjmallett		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
221219694Sjmallett				     CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE,
222219694Sjmallett				     cvm_oct_num_output_buffers);
223219694Sjmallett	}
224219694Sjmallett
225210311Sjmallett	if (USE_RED)
226219694Sjmallett		cvmx_helper_setup_red(num_packet_buffers/4,
227219694Sjmallett				      num_packet_buffers/8);
228210311Sjmallett
229210311Sjmallett	/* Enable the MII interface */
230242346Sjmallett	if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM)
231210311Sjmallett		cvmx_write_csr(CVMX_SMI_EN, 1);
232210311Sjmallett
233210311Sjmallett	/* Register an IRQ hander for to receive POW interrupts */
234210311Sjmallett        rid = 0;
235210311Sjmallett        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
236232812Sjmallett					   OCTEON_IRQ_WORKQ0 + pow_receive_group,
237232812Sjmallett					   OCTEON_IRQ_WORKQ0 + pow_receive_group,
238210311Sjmallett					   1, RF_ACTIVE);
239210311Sjmallett        if (sc->sc_rx_irq == NULL) {
240210311Sjmallett                device_printf(bus, "could not allocate workq irq");
241210311Sjmallett		return;
242210311Sjmallett        }
243210311Sjmallett
244210311Sjmallett        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
245210311Sjmallett			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
246219695Sjmallett			       &sc->sc_rx_intr_cookie);
247210311Sjmallett        if (error != 0) {
248210311Sjmallett                device_printf(bus, "could not setup workq irq");
249210311Sjmallett		return;
250210311Sjmallett        }
251210311Sjmallett
252210311Sjmallett
253210311Sjmallett#ifdef SMP
254217664Sjmallett	{
255217664Sjmallett		cvmx_ciu_intx0_t en;
256217664Sjmallett		int core;
257217210Sjmallett
258217664Sjmallett		CPU_FOREACH(core) {
259217664Sjmallett			if (core == PCPU_GET(cpuid))
260217664Sjmallett				continue;
261217210Sjmallett
262217664Sjmallett			en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2));
263217664Sjmallett			en.s.workq |= (1<<pow_receive_group);
264217664Sjmallett			cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64);
265210311Sjmallett		}
266210311Sjmallett	}
267210311Sjmallett#endif
268210311Sjmallett}
269210311Sjmallett
270210311Sjmallett
271210311Sjmallett/**
272210311Sjmallett * Free a work queue entry received in a intercept callback.
273210311Sjmallett *
274210311Sjmallett * @param work_queue_entry
275210311Sjmallett *               Work queue entry to free
276210311Sjmallett * @return Zero on success, Negative on failure.
277210311Sjmallett */
278210311Sjmallettint cvm_oct_free_work(void *work_queue_entry)
279210311Sjmallett{
280210311Sjmallett	cvmx_wqe_t *work = work_queue_entry;
281210311Sjmallett
282210311Sjmallett	int segments = work->word2.s.bufs;
283210311Sjmallett	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
284210311Sjmallett
285210311Sjmallett	while (segments--) {
286210311Sjmallett		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
287210311Sjmallett		if (__predict_false(!segment_ptr.s.i))
288210311Sjmallett			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
289210311Sjmallett		segment_ptr = next_ptr;
290210311Sjmallett	}
291210311Sjmallett	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
292210311Sjmallett
293210311Sjmallett	return 0;
294210311Sjmallett}
295210311Sjmallett
296210311Sjmallett
297210311Sjmallett/**
298210311Sjmallett * Module/ driver initialization. Creates the linux network
299210311Sjmallett * devices.
300210311Sjmallett *
301210311Sjmallett * @return Zero on success
302210311Sjmallett */
303210311Sjmallettint cvm_oct_init_module(device_t bus)
304210311Sjmallett{
305210311Sjmallett	device_t dev;
306210311Sjmallett	int ifnum;
307210311Sjmallett	int num_interfaces;
308210311Sjmallett	int interface;
309210311Sjmallett	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
310210311Sjmallett	int qos;
311210311Sjmallett
312210311Sjmallett	cvm_oct_rx_initialize();
313210311Sjmallett	cvm_oct_configure_common_hw(bus);
314210311Sjmallett
315210311Sjmallett	cvmx_helper_initialize_packet_io_global();
316210311Sjmallett
317210311Sjmallett	/* Change the input group for all ports before input is enabled */
318210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
319210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
320210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
321210311Sjmallett		int port;
322210311Sjmallett
323219694Sjmallett		for (port = 0; port < num_ports; port++) {
324210311Sjmallett			cvmx_pip_prt_tagx_t pip_prt_tagx;
325219694Sjmallett			int pkind = cvmx_helper_get_ipd_port(interface, port);
326219694Sjmallett
327219694Sjmallett			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pkind));
328210311Sjmallett			pip_prt_tagx.s.grp = pow_receive_group;
329219694Sjmallett			cvmx_write_csr(CVMX_PIP_PRT_TAGX(pkind), pip_prt_tagx.u64);
330210311Sjmallett		}
331210311Sjmallett	}
332210311Sjmallett
333210311Sjmallett	cvmx_helper_ipd_and_packet_input_enable();
334210311Sjmallett
335210311Sjmallett	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
336210311Sjmallett
337213150Sjmallett	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
338213150Sjmallett	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
339213150Sjmallett	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
340213150Sjmallett	    "octe link taskq");
341213150Sjmallett
342210311Sjmallett	/* Initialize the FAU used for counting packet buffers that need to be freed */
343210311Sjmallett	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
344210311Sjmallett
345210311Sjmallett	ifnum = 0;
346210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
347210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
348210311Sjmallett		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
349210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
350210311Sjmallett		int port;
351210311Sjmallett
352231987Sgonzo		for (port = cvmx_helper_get_ipd_port(interface, 0);
353231987Sgonzo		     port < cvmx_helper_get_ipd_port(interface, num_ports);
354231987Sgonzo		     ifnum++, port++) {
355210311Sjmallett			cvm_oct_private_t *priv;
356210311Sjmallett			struct ifnet *ifp;
357210311Sjmallett
358231987Sgonzo			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum);
359210311Sjmallett			if (dev != NULL)
360210311Sjmallett				ifp = if_alloc(IFT_ETHER);
361210311Sjmallett			if (dev == NULL || ifp == NULL) {
362231987Sgonzo				printf("Failed to allocate ethernet device for interface %d port %d\n", interface, port);
363210311Sjmallett				continue;
364210311Sjmallett			}
365210311Sjmallett
366210311Sjmallett			/* Initialize the device private structure. */
367210311Sjmallett			device_probe(dev);
368210311Sjmallett			priv = device_get_softc(dev);
369210311Sjmallett			priv->dev = dev;
370210311Sjmallett			priv->ifp = ifp;
371210311Sjmallett			priv->imode = imode;
372210311Sjmallett			priv->port = port;
373210311Sjmallett			priv->queue = cvmx_pko_get_base_queue(priv->port);
374210311Sjmallett			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
375210311Sjmallett			for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
376210311Sjmallett				cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
377213150Sjmallett			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
378210311Sjmallett
379210311Sjmallett			switch (priv->imode) {
380210311Sjmallett
381210311Sjmallett			/* These types don't support ports to IPD/PKO */
382210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
383210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PCIE:
384210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PICMG:
385210311Sjmallett				break;
386210311Sjmallett
387210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_NPI:
388210311Sjmallett				priv->init = cvm_oct_common_init;
389210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
390210311Sjmallett				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
391210311Sjmallett				break;
392210311Sjmallett
393210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_XAUI:
394210311Sjmallett				priv->init = cvm_oct_xaui_init;
395215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
396210311Sjmallett				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
397210311Sjmallett				break;
398210311Sjmallett
399210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_LOOP:
400210311Sjmallett				priv->init = cvm_oct_common_init;
401210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
402210311Sjmallett				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
403210311Sjmallett				break;
404210311Sjmallett
405210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SGMII:
406210311Sjmallett				priv->init = cvm_oct_sgmii_init;
407215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
408210311Sjmallett				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
409210311Sjmallett				break;
410210311Sjmallett
411210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SPI:
412210311Sjmallett				priv->init = cvm_oct_spi_init;
413210311Sjmallett				priv->uninit = cvm_oct_spi_uninit;
414210311Sjmallett				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
415210311Sjmallett				break;
416210311Sjmallett
417210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_RGMII:
418210311Sjmallett				priv->init = cvm_oct_rgmii_init;
419210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
420210311Sjmallett				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
421210311Sjmallett				break;
422210311Sjmallett
423210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_GMII:
424210311Sjmallett				priv->init = cvm_oct_rgmii_init;
425210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
426210311Sjmallett				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
427210311Sjmallett				break;
428210311Sjmallett			}
429210311Sjmallett
430210311Sjmallett			ifp->if_softc = priv;
431210311Sjmallett
432210311Sjmallett			if (!priv->init) {
433231987Sgonzo				printf("octe%d: unsupported device type interface %d, port %d\n",
434231987Sgonzo				       ifnum, interface, priv->port);
435231987Sgonzo				if_free(ifp);
436231987Sgonzo			} else if (priv->init(ifp) != 0) {
437231987Sgonzo				printf("octe%d: failed to register device for interface %d, port %d\n",
438231987Sgonzo				       ifnum, interface, priv->port);
439231987Sgonzo				if_free(ifp);
440210311Sjmallett			} else {
441210311Sjmallett				cvm_oct_device[priv->port] = ifp;
442210311Sjmallett				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
443210311Sjmallett			}
444210311Sjmallett		}
445210311Sjmallett	}
446210311Sjmallett
447210311Sjmallett	if (INTERRUPT_LIMIT) {
448210311Sjmallett		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
449226024Smarcel		cvmx_write_csr(CVMX_POW_WQ_INT_PC, cvmx_clock_get_rate(CVMX_CLOCK_CORE)/(INTERRUPT_LIMIT*16*256)<<8);
450210311Sjmallett
451210311Sjmallett		/* Enable POW timer interrupt. It will count when there are packets available */
452210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
453210311Sjmallett	} else {
454210311Sjmallett		/* Enable POW interrupt when our port has at least one packet */
455210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
456210311Sjmallett	}
457210311Sjmallett
458283291Sjkim	callout_init(&cvm_oct_poll_timer, 1);
459210311Sjmallett	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
460210311Sjmallett
461210311Sjmallett	return 0;
462210311Sjmallett}
463210311Sjmallett
464210311Sjmallett
465210311Sjmallett/**
466210311Sjmallett * Module / driver shutdown
467210311Sjmallett *
468210311Sjmallett * @return Zero on success
469210311Sjmallett */
470219695Sjmallettvoid cvm_oct_cleanup_module(device_t bus)
471210311Sjmallett{
472210311Sjmallett	int port;
473219695Sjmallett	struct octebus_softc *sc = device_get_softc(bus);
474210311Sjmallett
475210311Sjmallett	/* Disable POW interrupt */
476210311Sjmallett	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
477210311Sjmallett
478210311Sjmallett	/* Free the interrupt handler */
479219695Sjmallett	bus_teardown_intr(bus, sc->sc_rx_irq, sc->sc_rx_intr_cookie);
480210311Sjmallett
481210311Sjmallett	callout_stop(&cvm_oct_poll_timer);
482210311Sjmallett	cvm_oct_rx_shutdown();
483210311Sjmallett
484215990Sjmallett	cvmx_helper_shutdown_packet_io_global();
485215990Sjmallett
486210311Sjmallett	/* Free the ethernet devices */
487210311Sjmallett	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
488210311Sjmallett		if (cvm_oct_device[port]) {
489210311Sjmallett			cvm_oct_tx_shutdown(cvm_oct_device[port]);
490210311Sjmallett#if 0
491210311Sjmallett			unregister_netdev(cvm_oct_device[port]);
492210311Sjmallett			kfree(cvm_oct_device[port]);
493210311Sjmallett#else
494210311Sjmallett			panic("%s: need to detach and free interface.", __func__);
495210311Sjmallett#endif
496210311Sjmallett			cvm_oct_device[port] = NULL;
497210311Sjmallett		}
498210311Sjmallett	}
499219694Sjmallett	/* Free the HW pools */
500219694Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
501219694Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
502219694Sjmallett
503219694Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
504219694Sjmallett		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, cvm_oct_num_output_buffers);
505219694Sjmallett
506219694Sjmallett	/* Disable FPA, all buffers are free, not done by helper shutdown. */
507219694Sjmallett	cvmx_fpa_disable();
508210311Sjmallett}
509