ethernet.c revision 215959
1/*************************************************************************
2Copyright (c) 2003-2007  Cavium Networks (support@cavium.com). All rights
3reserved.
4
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are
8met:
9
10    * Redistributions of source code must retain the above copyright
11      notice, this list of conditions and the following disclaimer.
12
13    * Redistributions in binary form must reproduce the above
14      copyright notice, this list of conditions and the following
15      disclaimer in the documentation and/or other materials provided
16      with the distribution.
17
18    * Neither the name of Cavium Networks nor the names of
19      its contributors may be used to endorse or promote products
20      derived from this software without specific prior written
21      permission.
22
23This 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.
24
25TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
26AND 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.
27*************************************************************************/
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/mips/cavium/octe/ethernet.c 215959 2010-11-28 00:26:08Z jmallett $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/bus.h>
35#include <sys/conf.h>
36#include <sys/endian.h>
37#include <sys/kernel.h>
38#include <sys/rman.h>
39#include <sys/mbuf.h>
40#include <sys/socket.h>
41#include <sys/module.h>
42#include <sys/smp.h>
43#include <sys/taskqueue.h>
44
45#include <net/ethernet.h>
46#include <net/if.h>
47#include <net/if_types.h>
48
49#include "wrapper-cvmx-includes.h"
50#include "ethernet-headers.h"
51
52#include "octebusvar.h"
53
54/*
55 * XXX/juli
56 * Convert 0444 to tunables, 0644 to sysctls.
57 */
58#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
59int num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS;
60#else
61int num_packet_buffers = 1024;
62#endif
63TUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
64/*
65		 "\t\tNumber of packet buffers to allocate and store in the\n"
66		 "\t\tFPA. By default, 1024 packet buffers are used unless\n"
67		 "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */
68
69int pow_receive_group = 15;
70TUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
71/*
72		 "\t\tPOW group to receive packets from. All ethernet hardware\n"
73		 "\t\twill be configured to send incomming packets to this POW\n"
74		 "\t\tgroup. Also any other software can submit packets to this\n"
75		 "\t\tgroup for the kernel to process." */
76
77static int disable_core_queueing = 1;
78TUNABLE_INT("hw.octe.disable_core_queueing", &disable_core_queueing);
79/*
80		"\t\tWhen set the networking core's tx_queue_len is set to zero.  This\n"
81		"\t\tallows packets to be sent without lock contention in the packet scheduler\n"
82		"\t\tresulting in some cases in improved throughput.\n" */
83
84extern int octeon_is_simulation(void);
85
86/**
87 * Exported from the kernel so we can determine board information. It is
88 * passed by the bootloader to the kernel.
89 */
90extern cvmx_bootinfo_t *octeon_bootinfo;
91
92/**
93 * Periodic timer to check auto negotiation
94 */
95static struct callout cvm_oct_poll_timer;
96
97/**
98 * Array of every ethernet device owned by this driver indexed by
99 * the ipd input port number.
100 */
101struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
102
103/**
104 * Task to handle link status changes.
105 */
106static struct taskqueue *cvm_oct_link_taskq;
107
108/**
109 * Function to update link status.
110 */
111static void cvm_oct_update_link(void *context, int pending)
112{
113	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
114	struct ifnet *ifp = priv->ifp;
115	cvmx_helper_link_info_t link_info;
116
117	link_info.u64 = priv->link_info;
118
119	if (link_info.s.link_up) {
120		if_link_state_change(ifp, LINK_STATE_UP);
121		DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
122			   if_name(ifp), link_info.s.speed,
123			   (link_info.s.full_duplex) ? "Full" : "Half",
124			   priv->port, priv->queue);
125	} else {
126		if_link_state_change(ifp, LINK_STATE_DOWN);
127		DEBUGPRINT("%s: Link down\n", if_name(ifp));
128	}
129	priv->need_link_update = 0;
130}
131
132/**
133 * Periodic timer tick for slow management operations
134 *
135 * @param arg    Device to check
136 */
137static void cvm_do_timer(void *arg)
138{
139	static int port;
140	static int updated;
141	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
142		if (cvm_oct_device[port]) {
143			int queues_per_port;
144			int qos;
145			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
146			if (priv->poll)
147			{
148				/* skip polling if we don't get the lock */
149				if (MDIO_TRYLOCK()) {
150					priv->poll(cvm_oct_device[port]);
151					MDIO_UNLOCK();
152
153					if (priv->need_link_update) {
154						updated++;
155						taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
156					}
157				}
158			}
159
160			queues_per_port = cvmx_pko_get_num_queues(port);
161			/* Drain any pending packets in the free list */
162			for (qos = 0; qos < queues_per_port; qos++) {
163				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
164					IF_LOCK(&priv->tx_free_queue[qos]);
165					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
166						struct mbuf *m;
167
168						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
169						m_freem(m);
170					}
171					IF_UNLOCK(&priv->tx_free_queue[qos]);
172
173					/*
174					 * XXX locking!
175					 */
176					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
177				}
178			}
179#if 0
180			cvm_oct_device[port]->get_stats(cvm_oct_device[port]);
181#endif
182		}
183		port++;
184		/* Poll the next port in a 50th of a second.
185		   This spreads the polling of ports out a little bit */
186		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
187	} else {
188		port = 0;
189		/* If any updates were made in this run, continue iterating at
190		 * 1/50th of a second, so that if a link has merely gone down
191		 * temporarily (e.g. because of interface reinitialization) it
192		 * will not be forced to stay down for an entire second.
193		 */
194		if (updated > 0) {
195			updated = 0;
196			callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
197		} else {
198			/* All ports have been polled. Start the next iteration through
199			   the ports in one second */
200			callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
201		}
202	}
203}
204
205
206/**
207 * Configure common hardware for all interfaces
208 */
209static void cvm_oct_configure_common_hw(device_t bus)
210{
211	struct octebus_softc *sc;
212	int error;
213	int rid;
214
215        sc = device_get_softc(bus);
216
217	/* Setup the FPA */
218	cvmx_fpa_enable();
219	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
220	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
221	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
222		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
223
224	if (USE_RED)
225		cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
226
227	/* Enable the MII interface */
228	if (!octeon_is_simulation())
229		cvmx_write_csr(CVMX_SMI_EN, 1);
230
231	/* Register an IRQ hander for to receive POW interrupts */
232        rid = 0;
233        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
234					   CVMX_IRQ_WORKQ0 + pow_receive_group,
235					   CVMX_IRQ_WORKQ0 + pow_receive_group,
236					   1, RF_ACTIVE);
237        if (sc->sc_rx_irq == NULL) {
238                device_printf(bus, "could not allocate workq irq");
239		return;
240        }
241
242        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
243			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
244			       NULL);
245        if (error != 0) {
246                device_printf(bus, "could not setup workq irq");
247		return;
248        }
249
250
251#ifdef SMP
252	if (USE_MULTICORE_RECEIVE) {
253		critical_enter();
254		{
255			int cpu;
256			for (cpu = 0; cpu < mp_maxid; cpu++) {
257				if (!CPU_ABSENT(cpu) &&
258				   (cpu != PCPU_GET(cpuid))) {
259					cvmx_ciu_intx0_t en;
260					en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
261					en.s.workq |= (1<<pow_receive_group);
262					cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
263				}
264			}
265		}
266		critical_exit();
267	}
268#endif
269}
270
271
272/**
273 * Free a work queue entry received in a intercept callback.
274 *
275 * @param work_queue_entry
276 *               Work queue entry to free
277 * @return Zero on success, Negative on failure.
278 */
279int cvm_oct_free_work(void *work_queue_entry)
280{
281	cvmx_wqe_t *work = work_queue_entry;
282
283	int segments = work->word2.s.bufs;
284	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
285
286	while (segments--) {
287		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
288		if (__predict_false(!segment_ptr.s.i))
289			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
290		segment_ptr = next_ptr;
291	}
292	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
293
294	return 0;
295}
296
297
298/**
299 * Module/ driver initialization. Creates the linux network
300 * devices.
301 *
302 * @return Zero on success
303 */
304int cvm_oct_init_module(device_t bus)
305{
306	device_t dev;
307	int ifnum;
308	int num_interfaces;
309	int interface;
310	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
311	int qos;
312
313	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
314
315#if 0
316	cvm_oct_proc_initialize();
317#endif
318	cvm_oct_rx_initialize();
319	cvm_oct_configure_common_hw(bus);
320
321	cvmx_helper_initialize_packet_io_global();
322
323	/* Change the input group for all ports before input is enabled */
324	num_interfaces = cvmx_helper_get_number_of_interfaces();
325	for (interface = 0; interface < num_interfaces; interface++) {
326		int num_ports = cvmx_helper_ports_on_interface(interface);
327		int port;
328
329		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
330			cvmx_pip_prt_tagx_t pip_prt_tagx;
331			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
332			pip_prt_tagx.s.grp = pow_receive_group;
333			cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
334		}
335	}
336
337	cvmx_helper_ipd_and_packet_input_enable();
338
339	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
340
341	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
342	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
343	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
344	    "octe link taskq");
345
346	/* Initialize the FAU used for counting packet buffers that need to be freed */
347	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
348
349	ifnum = 0;
350	num_interfaces = cvmx_helper_get_number_of_interfaces();
351	for (interface = 0; interface < num_interfaces; interface++) {
352		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
353		int num_ports = cvmx_helper_ports_on_interface(interface);
354		int port;
355
356		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
357			cvm_oct_private_t *priv;
358			struct ifnet *ifp;
359
360			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
361			if (dev != NULL)
362				ifp = if_alloc(IFT_ETHER);
363			if (dev == NULL || ifp == NULL) {
364				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
365				continue;
366			}
367			/* XXX/juli set max send q len.  */
368#if 0
369			if (disable_core_queueing)
370				ifp->tx_queue_len = 0;
371#endif
372
373			/* Initialize the device private structure. */
374			device_probe(dev);
375			priv = device_get_softc(dev);
376			priv->dev = dev;
377			priv->ifp = ifp;
378			priv->imode = imode;
379			priv->port = port;
380			priv->queue = cvmx_pko_get_base_queue(priv->port);
381			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
382			for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
383				cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
384			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
385
386			switch (priv->imode) {
387
388			/* These types don't support ports to IPD/PKO */
389			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
390			case CVMX_HELPER_INTERFACE_MODE_PCIE:
391			case CVMX_HELPER_INTERFACE_MODE_PICMG:
392				break;
393
394			case CVMX_HELPER_INTERFACE_MODE_NPI:
395				priv->init = cvm_oct_common_init;
396				priv->uninit = cvm_oct_common_uninit;
397				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
398				break;
399
400			case CVMX_HELPER_INTERFACE_MODE_XAUI:
401				priv->init = cvm_oct_xaui_init;
402				priv->uninit = cvm_oct_xaui_uninit;
403				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
404				break;
405
406			case CVMX_HELPER_INTERFACE_MODE_LOOP:
407				priv->init = cvm_oct_common_init;
408				priv->uninit = cvm_oct_common_uninit;
409				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
410				break;
411
412			case CVMX_HELPER_INTERFACE_MODE_SGMII:
413				priv->init = cvm_oct_sgmii_init;
414				priv->uninit = cvm_oct_sgmii_uninit;
415				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
416				break;
417
418			case CVMX_HELPER_INTERFACE_MODE_SPI:
419				priv->init = cvm_oct_spi_init;
420				priv->uninit = cvm_oct_spi_uninit;
421				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
422				break;
423
424			case CVMX_HELPER_INTERFACE_MODE_RGMII:
425				priv->init = cvm_oct_rgmii_init;
426				priv->uninit = cvm_oct_rgmii_uninit;
427				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
428				break;
429
430			case CVMX_HELPER_INTERFACE_MODE_GMII:
431				priv->init = cvm_oct_rgmii_init;
432				priv->uninit = cvm_oct_rgmii_uninit;
433				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
434				break;
435			}
436
437			ifp->if_softc = priv;
438
439			if (!priv->init) {
440				panic("%s: unsupported device type, need to free ifp.", __func__);
441			} else
442			if (priv->init(ifp) < 0) {
443				printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
444				interface, priv->port);
445				panic("%s: init failed, need to free ifp.", __func__);
446			} else {
447				cvm_oct_device[priv->port] = ifp;
448				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
449			}
450		}
451	}
452
453	if (INTERRUPT_LIMIT) {
454		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
455		cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
456
457		/* Enable POW timer interrupt. It will count when there are packets available */
458		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
459	} else {
460		/* Enable POW interrupt when our port has at least one packet */
461		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
462	}
463
464	callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
465	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
466
467	return 0;
468}
469
470
471/**
472 * Module / driver shutdown
473 *
474 * @return Zero on success
475 */
476void cvm_oct_cleanup_module(void)
477{
478	int port;
479
480	/* Disable POW interrupt */
481	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
482
483	cvmx_ipd_disable();
484
485#if 0
486	/* Free the interrupt handler */
487	free_irq(8 + pow_receive_group, cvm_oct_device);
488#endif
489
490	callout_stop(&cvm_oct_poll_timer);
491	cvm_oct_rx_shutdown();
492	cvmx_pko_disable();
493
494	/* Free the ethernet devices */
495	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
496		if (cvm_oct_device[port]) {
497			cvm_oct_tx_shutdown(cvm_oct_device[port]);
498#if 0
499			unregister_netdev(cvm_oct_device[port]);
500			kfree(cvm_oct_device[port]);
501#else
502			panic("%s: need to detach and free interface.", __func__);
503#endif
504			cvm_oct_device[port] = NULL;
505		}
506	}
507
508	cvmx_pko_shutdown();
509#if 0
510	cvm_oct_proc_shutdown();
511#endif
512
513	cvmx_ipd_free_ptr();
514
515	/* Free the HW pools */
516	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
517	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
518	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
519		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
520}
521