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: stable/10/sys/mips/cavium/octe/ethernet.c 314667 2017-03-04 13:03:31Z avg $");
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
77210311Sjmallett/**
78210311Sjmallett * Periodic timer to check auto negotiation
79210311Sjmallett */
80210311Sjmallettstatic struct callout cvm_oct_poll_timer;
81210311Sjmallett
82210311Sjmallett/**
83210311Sjmallett * Array of every ethernet device owned by this driver indexed by
84210311Sjmallett * the ipd input port number.
85210311Sjmallett */
86210311Sjmallettstruct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
87210311Sjmallett
88213150Sjmallett/**
89213150Sjmallett * Task to handle link status changes.
90213150Sjmallett */
91213150Sjmallettstatic struct taskqueue *cvm_oct_link_taskq;
92210311Sjmallett
93219694Sjmallett/*
94219694Sjmallett * Number of buffers in output buffer pool.
95219694Sjmallett */
96219694Sjmallettstatic int cvm_oct_num_output_buffers;
97219694Sjmallett
98210311Sjmallett/**
99213150Sjmallett * Function to update link status.
100213150Sjmallett */
101213150Sjmallettstatic void cvm_oct_update_link(void *context, int pending)
102213150Sjmallett{
103213150Sjmallett	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
104213150Sjmallett	struct ifnet *ifp = priv->ifp;
105213150Sjmallett	cvmx_helper_link_info_t link_info;
106213150Sjmallett
107213150Sjmallett	link_info.u64 = priv->link_info;
108213150Sjmallett
109213150Sjmallett	if (link_info.s.link_up) {
110213150Sjmallett		if_link_state_change(ifp, LINK_STATE_UP);
111215959Sjmallett		DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
112215959Sjmallett			   if_name(ifp), link_info.s.speed,
113215959Sjmallett			   (link_info.s.full_duplex) ? "Full" : "Half",
114215959Sjmallett			   priv->port, priv->queue);
115213150Sjmallett	} else {
116213150Sjmallett		if_link_state_change(ifp, LINK_STATE_DOWN);
117213150Sjmallett		DEBUGPRINT("%s: Link down\n", if_name(ifp));
118213150Sjmallett	}
119213150Sjmallett	priv->need_link_update = 0;
120213150Sjmallett}
121213150Sjmallett
122213150Sjmallett/**
123210311Sjmallett * Periodic timer tick for slow management operations
124210311Sjmallett *
125210311Sjmallett * @param arg    Device to check
126210311Sjmallett */
127210311Sjmallettstatic void cvm_do_timer(void *arg)
128210311Sjmallett{
129210311Sjmallett	static int port;
130213807Sjmallett	static int updated;
131210311Sjmallett	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
132210311Sjmallett		if (cvm_oct_device[port]) {
133210311Sjmallett			int queues_per_port;
134210311Sjmallett			int qos;
135210311Sjmallett			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
136213150Sjmallett
137216071Sjmallett			cvm_oct_common_poll(priv->ifp);
138216071Sjmallett			if (priv->need_link_update) {
139216071Sjmallett				updated++;
140216071Sjmallett				taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
141210311Sjmallett			}
142210311Sjmallett
143210311Sjmallett			queues_per_port = cvmx_pko_get_num_queues(port);
144210311Sjmallett			/* Drain any pending packets in the free list */
145210311Sjmallett			for (qos = 0; qos < queues_per_port; qos++) {
146210311Sjmallett				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
147210311Sjmallett					IF_LOCK(&priv->tx_free_queue[qos]);
148210311Sjmallett					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
149210311Sjmallett						struct mbuf *m;
150210311Sjmallett
151210311Sjmallett						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
152210311Sjmallett						m_freem(m);
153210311Sjmallett					}
154210311Sjmallett					IF_UNLOCK(&priv->tx_free_queue[qos]);
155210311Sjmallett
156210311Sjmallett					/*
157210311Sjmallett					 * XXX locking!
158210311Sjmallett					 */
159210311Sjmallett					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
160210311Sjmallett				}
161210311Sjmallett			}
162210311Sjmallett		}
163210311Sjmallett		port++;
164210311Sjmallett		/* Poll the next port in a 50th of a second.
165210311Sjmallett		   This spreads the polling of ports out a little bit */
166210311Sjmallett		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
167210311Sjmallett	} else {
168210311Sjmallett		port = 0;
169213807Sjmallett		/* If any updates were made in this run, continue iterating at
170213807Sjmallett		 * 1/50th of a second, so that if a link has merely gone down
171213807Sjmallett		 * temporarily (e.g. because of interface reinitialization) it
172213807Sjmallett		 * will not be forced to stay down for an entire second.
173213807Sjmallett		 */
174213807Sjmallett		if (updated > 0) {
175213807Sjmallett			updated = 0;
176213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
177213807Sjmallett		} else {
178213807Sjmallett			/* All ports have been polled. Start the next iteration through
179213807Sjmallett			   the ports in one second */
180213807Sjmallett			callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
181213807Sjmallett		}
182210311Sjmallett	}
183210311Sjmallett}
184210311Sjmallett
185210311Sjmallett/**
186210311Sjmallett * Configure common hardware for all interfaces
187210311Sjmallett */
188210311Sjmallettstatic void cvm_oct_configure_common_hw(device_t bus)
189210311Sjmallett{
190210311Sjmallett	struct octebus_softc *sc;
191219694Sjmallett	int pko_queues;
192210311Sjmallett	int error;
193210311Sjmallett	int rid;
194210311Sjmallett
195210311Sjmallett        sc = device_get_softc(bus);
196210311Sjmallett
197210311Sjmallett	/* Setup the FPA */
198210311Sjmallett	cvmx_fpa_enable();
199219694Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
200219694Sjmallett			     num_packet_buffers);
201219694Sjmallett	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
202219694Sjmallett			     num_packet_buffers);
203219694Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) {
204219694Sjmallett		/*
205219694Sjmallett		 * If the FPA uses different pools for output buffers and
206219694Sjmallett		 * packets, size the output buffer pool based on the number
207219694Sjmallett		 * of PKO queues.
208219694Sjmallett		 */
209219694Sjmallett		if (OCTEON_IS_MODEL(OCTEON_CN38XX))
210219694Sjmallett			pko_queues = 128;
211219694Sjmallett		else if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
212219694Sjmallett			pko_queues = 32;
213219694Sjmallett		else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
214219694Sjmallett			pko_queues = 32;
215219694Sjmallett		else
216219694Sjmallett			pko_queues = 256;
217210311Sjmallett
218219694Sjmallett		cvm_oct_num_output_buffers = 4 * pko_queues;
219219694Sjmallett		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
220219694Sjmallett				     CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE,
221219694Sjmallett				     cvm_oct_num_output_buffers);
222219694Sjmallett	}
223219694Sjmallett
224210311Sjmallett	if (USE_RED)
225219694Sjmallett		cvmx_helper_setup_red(num_packet_buffers/4,
226219694Sjmallett				      num_packet_buffers/8);
227210311Sjmallett
228210311Sjmallett	/* Enable the MII interface */
229242346Sjmallett	if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM)
230210311Sjmallett		cvmx_write_csr(CVMX_SMI_EN, 1);
231210311Sjmallett
232210311Sjmallett	/* Register an IRQ hander for to receive POW interrupts */
233210311Sjmallett        rid = 0;
234210311Sjmallett        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
235232812Sjmallett					   OCTEON_IRQ_WORKQ0 + pow_receive_group,
236232812Sjmallett					   OCTEON_IRQ_WORKQ0 + pow_receive_group,
237210311Sjmallett					   1, RF_ACTIVE);
238210311Sjmallett        if (sc->sc_rx_irq == NULL) {
239210311Sjmallett                device_printf(bus, "could not allocate workq irq");
240210311Sjmallett		return;
241210311Sjmallett        }
242210311Sjmallett
243210311Sjmallett        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
244210311Sjmallett			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
245219695Sjmallett			       &sc->sc_rx_intr_cookie);
246210311Sjmallett        if (error != 0) {
247210311Sjmallett                device_printf(bus, "could not setup workq irq");
248210311Sjmallett		return;
249210311Sjmallett        }
250210311Sjmallett
251210311Sjmallett
252210311Sjmallett#ifdef SMP
253217664Sjmallett	{
254217664Sjmallett		cvmx_ciu_intx0_t en;
255217664Sjmallett		int core;
256217210Sjmallett
257217664Sjmallett		CPU_FOREACH(core) {
258217664Sjmallett			if (core == PCPU_GET(cpuid))
259217664Sjmallett				continue;
260217210Sjmallett
261217664Sjmallett			en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2));
262217664Sjmallett			en.s.workq |= (1<<pow_receive_group);
263217664Sjmallett			cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64);
264210311Sjmallett		}
265210311Sjmallett	}
266210311Sjmallett#endif
267210311Sjmallett}
268210311Sjmallett
269210311Sjmallett
270210311Sjmallett/**
271210311Sjmallett * Free a work queue entry received in a intercept callback.
272210311Sjmallett *
273210311Sjmallett * @param work_queue_entry
274210311Sjmallett *               Work queue entry to free
275210311Sjmallett * @return Zero on success, Negative on failure.
276210311Sjmallett */
277210311Sjmallettint cvm_oct_free_work(void *work_queue_entry)
278210311Sjmallett{
279210311Sjmallett	cvmx_wqe_t *work = work_queue_entry;
280210311Sjmallett
281210311Sjmallett	int segments = work->word2.s.bufs;
282210311Sjmallett	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
283210311Sjmallett
284210311Sjmallett	while (segments--) {
285210311Sjmallett		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
286210311Sjmallett		if (__predict_false(!segment_ptr.s.i))
287210311Sjmallett			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
288210311Sjmallett		segment_ptr = next_ptr;
289210311Sjmallett	}
290210311Sjmallett	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
291210311Sjmallett
292210311Sjmallett	return 0;
293210311Sjmallett}
294210311Sjmallett
295210311Sjmallett
296210311Sjmallett/**
297210311Sjmallett * Module/ driver initialization. Creates the linux network
298210311Sjmallett * devices.
299210311Sjmallett *
300210311Sjmallett * @return Zero on success
301210311Sjmallett */
302210311Sjmallettint cvm_oct_init_module(device_t bus)
303210311Sjmallett{
304210311Sjmallett	device_t dev;
305210311Sjmallett	int ifnum;
306210311Sjmallett	int num_interfaces;
307210311Sjmallett	int interface;
308210311Sjmallett	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
309210311Sjmallett	int qos;
310210311Sjmallett
311210311Sjmallett	cvm_oct_rx_initialize();
312210311Sjmallett	cvm_oct_configure_common_hw(bus);
313210311Sjmallett
314210311Sjmallett	cvmx_helper_initialize_packet_io_global();
315210311Sjmallett
316210311Sjmallett	/* Change the input group for all ports before input is enabled */
317210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
318210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
319210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
320210311Sjmallett		int port;
321210311Sjmallett
322219694Sjmallett		for (port = 0; port < num_ports; port++) {
323210311Sjmallett			cvmx_pip_prt_tagx_t pip_prt_tagx;
324219694Sjmallett			int pkind = cvmx_helper_get_ipd_port(interface, port);
325219694Sjmallett
326219694Sjmallett			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pkind));
327210311Sjmallett			pip_prt_tagx.s.grp = pow_receive_group;
328219694Sjmallett			cvmx_write_csr(CVMX_PIP_PRT_TAGX(pkind), pip_prt_tagx.u64);
329210311Sjmallett		}
330210311Sjmallett	}
331210311Sjmallett
332210311Sjmallett	cvmx_helper_ipd_and_packet_input_enable();
333210311Sjmallett
334210311Sjmallett	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
335210311Sjmallett
336213150Sjmallett	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
337213150Sjmallett	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
338213150Sjmallett	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
339213150Sjmallett	    "octe link taskq");
340213150Sjmallett
341210311Sjmallett	/* Initialize the FAU used for counting packet buffers that need to be freed */
342210311Sjmallett	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
343210311Sjmallett
344210311Sjmallett	ifnum = 0;
345210311Sjmallett	num_interfaces = cvmx_helper_get_number_of_interfaces();
346210311Sjmallett	for (interface = 0; interface < num_interfaces; interface++) {
347210311Sjmallett		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
348210311Sjmallett		int num_ports = cvmx_helper_ports_on_interface(interface);
349210311Sjmallett		int port;
350210311Sjmallett
351231987Sgonzo		for (port = cvmx_helper_get_ipd_port(interface, 0);
352231987Sgonzo		     port < cvmx_helper_get_ipd_port(interface, num_ports);
353231987Sgonzo		     ifnum++, port++) {
354210311Sjmallett			cvm_oct_private_t *priv;
355210311Sjmallett			struct ifnet *ifp;
356210311Sjmallett
357231987Sgonzo			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum);
358210311Sjmallett			if (dev != NULL)
359210311Sjmallett				ifp = if_alloc(IFT_ETHER);
360210311Sjmallett			if (dev == NULL || ifp == NULL) {
361231987Sgonzo				printf("Failed to allocate ethernet device for interface %d port %d\n", interface, port);
362210311Sjmallett				continue;
363210311Sjmallett			}
364210311Sjmallett
365210311Sjmallett			/* Initialize the device private structure. */
366210311Sjmallett			device_probe(dev);
367210311Sjmallett			priv = device_get_softc(dev);
368210311Sjmallett			priv->dev = dev;
369210311Sjmallett			priv->ifp = ifp;
370210311Sjmallett			priv->imode = imode;
371210311Sjmallett			priv->port = port;
372210311Sjmallett			priv->queue = cvmx_pko_get_base_queue(priv->port);
373210311Sjmallett			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
374210311Sjmallett			for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
375210311Sjmallett				cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
376213150Sjmallett			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
377210311Sjmallett
378210311Sjmallett			switch (priv->imode) {
379210311Sjmallett
380210311Sjmallett			/* These types don't support ports to IPD/PKO */
381210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
382210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PCIE:
383210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_PICMG:
384210311Sjmallett				break;
385210311Sjmallett
386210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_NPI:
387210311Sjmallett				priv->init = cvm_oct_common_init;
388210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
389210311Sjmallett				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
390210311Sjmallett				break;
391210311Sjmallett
392210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_XAUI:
393210311Sjmallett				priv->init = cvm_oct_xaui_init;
394215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
395210311Sjmallett				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
396210311Sjmallett				break;
397210311Sjmallett
398210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_LOOP:
399210311Sjmallett				priv->init = cvm_oct_common_init;
400210311Sjmallett				priv->uninit = cvm_oct_common_uninit;
401210311Sjmallett				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
402210311Sjmallett				break;
403210311Sjmallett
404210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SGMII:
405210311Sjmallett				priv->init = cvm_oct_sgmii_init;
406215974Sjmallett				priv->uninit = cvm_oct_common_uninit;
407210311Sjmallett				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
408210311Sjmallett				break;
409210311Sjmallett
410210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_SPI:
411210311Sjmallett				priv->init = cvm_oct_spi_init;
412210311Sjmallett				priv->uninit = cvm_oct_spi_uninit;
413210311Sjmallett				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
414210311Sjmallett				break;
415210311Sjmallett
416210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_RGMII:
417210311Sjmallett				priv->init = cvm_oct_rgmii_init;
418210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
419210311Sjmallett				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
420210311Sjmallett				break;
421210311Sjmallett
422210311Sjmallett			case CVMX_HELPER_INTERFACE_MODE_GMII:
423210311Sjmallett				priv->init = cvm_oct_rgmii_init;
424210311Sjmallett				priv->uninit = cvm_oct_rgmii_uninit;
425210311Sjmallett				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
426210311Sjmallett				break;
427210311Sjmallett			}
428210311Sjmallett
429210311Sjmallett			ifp->if_softc = priv;
430210311Sjmallett
431210311Sjmallett			if (!priv->init) {
432231987Sgonzo				printf("octe%d: unsupported device type interface %d, port %d\n",
433231987Sgonzo				       ifnum, interface, priv->port);
434231987Sgonzo				if_free(ifp);
435231987Sgonzo			} else if (priv->init(ifp) != 0) {
436231987Sgonzo				printf("octe%d: failed to register device for interface %d, port %d\n",
437231987Sgonzo				       ifnum, interface, priv->port);
438231987Sgonzo				if_free(ifp);
439210311Sjmallett			} else {
440210311Sjmallett				cvm_oct_device[priv->port] = ifp;
441210311Sjmallett				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
442210311Sjmallett			}
443210311Sjmallett		}
444210311Sjmallett	}
445210311Sjmallett
446210311Sjmallett	if (INTERRUPT_LIMIT) {
447210311Sjmallett		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
448226024Smarcel		cvmx_write_csr(CVMX_POW_WQ_INT_PC, cvmx_clock_get_rate(CVMX_CLOCK_CORE)/(INTERRUPT_LIMIT*16*256)<<8);
449210311Sjmallett
450210311Sjmallett		/* Enable POW timer interrupt. It will count when there are packets available */
451210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
452210311Sjmallett	} else {
453210311Sjmallett		/* Enable POW interrupt when our port has at least one packet */
454210311Sjmallett		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
455210311Sjmallett	}
456210311Sjmallett
457314667Savg	callout_init(&cvm_oct_poll_timer, 1);
458210311Sjmallett	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
459210311Sjmallett
460210311Sjmallett	return 0;
461210311Sjmallett}
462210311Sjmallett
463210311Sjmallett
464210311Sjmallett/**
465210311Sjmallett * Module / driver shutdown
466210311Sjmallett *
467210311Sjmallett * @return Zero on success
468210311Sjmallett */
469219695Sjmallettvoid cvm_oct_cleanup_module(device_t bus)
470210311Sjmallett{
471210311Sjmallett	int port;
472219695Sjmallett	struct octebus_softc *sc = device_get_softc(bus);
473210311Sjmallett
474210311Sjmallett	/* Disable POW interrupt */
475210311Sjmallett	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
476210311Sjmallett
477210311Sjmallett	/* Free the interrupt handler */
478219695Sjmallett	bus_teardown_intr(bus, sc->sc_rx_irq, sc->sc_rx_intr_cookie);
479210311Sjmallett
480210311Sjmallett	callout_stop(&cvm_oct_poll_timer);
481210311Sjmallett	cvm_oct_rx_shutdown();
482210311Sjmallett
483215990Sjmallett	cvmx_helper_shutdown_packet_io_global();
484215990Sjmallett
485210311Sjmallett	/* Free the ethernet devices */
486210311Sjmallett	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
487210311Sjmallett		if (cvm_oct_device[port]) {
488210311Sjmallett			cvm_oct_tx_shutdown(cvm_oct_device[port]);
489210311Sjmallett#if 0
490210311Sjmallett			unregister_netdev(cvm_oct_device[port]);
491210311Sjmallett			kfree(cvm_oct_device[port]);
492210311Sjmallett#else
493210311Sjmallett			panic("%s: need to detach and free interface.", __func__);
494210311Sjmallett#endif
495210311Sjmallett			cvm_oct_device[port] = NULL;
496210311Sjmallett		}
497210311Sjmallett	}
498219694Sjmallett	/* Free the HW pools */
499219694Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
500219694Sjmallett	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
501219694Sjmallett
502219694Sjmallett	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
503219694Sjmallett		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, cvm_oct_num_output_buffers);
504219694Sjmallett
505219694Sjmallett	/* Disable FPA, all buffers are free, not done by helper shutdown. */
506219694Sjmallett	cvmx_fpa_disable();
507210311Sjmallett}
508