ethernet.c revision 210311
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 210311 2010-07-20 19:25:11Z 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
44#include <net/ethernet.h>
45#include <net/if.h>
46#include <net/if_types.h>
47
48#include "wrapper-cvmx-includes.h"
49#include "ethernet-headers.h"
50
51#include "octebusvar.h"
52
53/*
54 * XXX/juli
55 * Convert 0444 to tunables, 0644 to sysctls.
56 */
57#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
58int num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS;
59#else
60int num_packet_buffers = 1024;
61#endif
62TUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
63/*
64		 "\t\tNumber of packet buffers to allocate and store in the\n"
65		 "\t\tFPA. By default, 1024 packet buffers are used unless\n"
66		 "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */
67
68int pow_receive_group = 15;
69TUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
70/*
71		 "\t\tPOW group to receive packets from. All ethernet hardware\n"
72		 "\t\twill be configured to send incomming packets to this POW\n"
73		 "\t\tgroup. Also any other software can submit packets to this\n"
74		 "\t\tgroup for the kernel to process." */
75
76int pow_send_group = -1; /* XXX Should be a sysctl.  */
77TUNABLE_INT("hw.octe.pow_send_group", &pow_send_group);
78/*
79		 "\t\tPOW group to send packets to other software on. This\n"
80		 "\t\tcontrols the creation of the virtual device pow0.\n"
81		 "\t\talways_use_pow also depends on this value." */
82
83int always_use_pow;
84TUNABLE_INT("hw.octe.always_use_pow", &always_use_pow);
85/*
86		 "\t\tWhen set, always send to the pow group. This will cause\n"
87		 "\t\tpackets sent to real ethernet devices to be sent to the\n"
88		 "\t\tPOW group instead of the hardware. Unless some other\n"
89		 "\t\tapplication changes the config, packets will still be\n"
90		 "\t\treceived from the low level hardware. Use this option\n"
91		 "\t\tto allow a CVMX app to intercept all packets from the\n"
92		 "\t\tlinux kernel. You must specify pow_send_group along with\n"
93		 "\t\tthis option." */
94
95char pow_send_list[128] = "";
96TUNABLE_STR("hw.octe.pow_send_list", pow_send_list, sizeof pow_send_list);
97/*
98		 "\t\tComma separated list of ethernet devices that should use the\n"
99		 "\t\tPOW for transmit instead of the actual ethernet hardware. This\n"
100		 "\t\tis a per port version of always_use_pow. always_use_pow takes\n"
101		 "\t\tprecedence over this list. For example, setting this to\n"
102		 "\t\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n"
103		 "\t\tusing the pow_send_group." */
104
105
106static int disable_core_queueing = 1;
107TUNABLE_INT("hw.octe.disable_core_queueing", &disable_core_queueing);
108/*
109		"\t\tWhen set the networking core's tx_queue_len is set to zero.  This\n"
110		"\t\tallows packets to be sent without lock contention in the packet scheduler\n"
111		"\t\tresulting in some cases in improved throughput.\n" */
112
113extern int octeon_is_simulation(void);
114
115/**
116 * Exported from the kernel so we can determine board information. It is
117 * passed by the bootloader to the kernel.
118 */
119extern cvmx_bootinfo_t *octeon_bootinfo;
120
121/**
122 * Periodic timer to check auto negotiation
123 */
124static struct callout cvm_oct_poll_timer;
125
126/**
127 * Array of every ethernet device owned by this driver indexed by
128 * the ipd input port number.
129 */
130struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
131
132
133/**
134 * Periodic timer tick for slow management operations
135 *
136 * @param arg    Device to check
137 */
138static void cvm_do_timer(void *arg)
139{
140	static int port;
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			}
154
155			queues_per_port = cvmx_pko_get_num_queues(port);
156			/* Drain any pending packets in the free list */
157			for (qos = 0; qos < queues_per_port; qos++) {
158				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
159					IF_LOCK(&priv->tx_free_queue[qos]);
160					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
161						struct mbuf *m;
162
163						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
164						m_freem(m);
165					}
166					IF_UNLOCK(&priv->tx_free_queue[qos]);
167
168					/*
169					 * XXX locking!
170					 */
171					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
172				}
173			}
174#if 0
175			cvm_oct_device[port]->get_stats(cvm_oct_device[port]);
176#endif
177		}
178		port++;
179		/* Poll the next port in a 50th of a second.
180		   This spreads the polling of ports out a little bit */
181		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
182	} else {
183		port = 0;
184		/* All ports have been polled. Start the next iteration through
185		   the ports in one second */
186		callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
187	}
188}
189
190
191/**
192 * Configure common hardware for all interfaces
193 */
194static void cvm_oct_configure_common_hw(device_t bus)
195{
196	struct octebus_softc *sc;
197	int error;
198	int rid;
199
200        sc = device_get_softc(bus);
201
202	/* Setup the FPA */
203	cvmx_fpa_enable();
204	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
205	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
206	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
207		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
208
209	if (USE_RED)
210		cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
211
212	/* Enable the MII interface */
213	if (!octeon_is_simulation())
214		cvmx_write_csr(CVMX_SMI_EN, 1);
215
216	/* Register an IRQ hander for to receive POW interrupts */
217        rid = 0;
218        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
219					   CVMX_IRQ_WORKQ0 + pow_receive_group,
220					   CVMX_IRQ_WORKQ0 + pow_receive_group,
221					   1, RF_ACTIVE);
222        if (sc->sc_rx_irq == NULL) {
223                device_printf(bus, "could not allocate workq irq");
224		return;
225        }
226
227        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
228			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
229			       NULL);
230        if (error != 0) {
231                device_printf(bus, "could not setup workq irq");
232		return;
233        }
234
235
236#ifdef SMP
237	if (USE_MULTICORE_RECEIVE) {
238		critical_enter();
239		{
240			int cpu;
241			for (cpu = 0; cpu < mp_maxid; cpu++) {
242				if (!CPU_ABSENT(cpu) &&
243				   (cpu != PCPU_GET(cpuid))) {
244					cvmx_ciu_intx0_t en;
245					en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
246					en.s.workq |= (1<<pow_receive_group);
247					cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
248				}
249			}
250		}
251		critical_exit();
252	}
253#endif
254}
255
256
257/**
258 * Free a work queue entry received in a intercept callback.
259 *
260 * @param work_queue_entry
261 *               Work queue entry to free
262 * @return Zero on success, Negative on failure.
263 */
264int cvm_oct_free_work(void *work_queue_entry)
265{
266	cvmx_wqe_t *work = work_queue_entry;
267
268	int segments = work->word2.s.bufs;
269	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
270
271	while (segments--) {
272		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
273		if (__predict_false(!segment_ptr.s.i))
274			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
275		segment_ptr = next_ptr;
276	}
277	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
278
279	return 0;
280}
281
282
283/**
284 * Module/ driver initialization. Creates the linux network
285 * devices.
286 *
287 * @return Zero on success
288 */
289int cvm_oct_init_module(device_t bus)
290{
291	device_t dev;
292	int ifnum;
293	int num_interfaces;
294	int interface;
295	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
296	int qos;
297
298	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
299
300#if 0
301	cvm_oct_proc_initialize();
302#endif
303	cvm_oct_rx_initialize();
304	cvm_oct_configure_common_hw(bus);
305
306	cvmx_helper_initialize_packet_io_global();
307
308	/* Change the input group for all ports before input is enabled */
309	num_interfaces = cvmx_helper_get_number_of_interfaces();
310	for (interface = 0; interface < num_interfaces; interface++) {
311		int num_ports = cvmx_helper_ports_on_interface(interface);
312		int port;
313
314		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
315			cvmx_pip_prt_tagx_t pip_prt_tagx;
316			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
317			pip_prt_tagx.s.grp = pow_receive_group;
318			cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
319		}
320	}
321
322	cvmx_helper_ipd_and_packet_input_enable();
323
324	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
325
326	/* Initialize the FAU used for counting packet buffers that need to be freed */
327	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
328
329	if ((pow_send_group != -1)) {
330		struct ifnet *ifp;
331
332		printf("\tConfiguring device for POW only access\n");
333		dev = BUS_ADD_CHILD(bus, 0, "pow", 0);
334		if (dev != NULL)
335			ifp = if_alloc(IFT_ETHER);
336		if (dev != NULL && ifp != NULL) {
337			/* Initialize the device private structure. */
338			cvm_oct_private_t *priv;
339
340			device_probe(dev);
341			priv = device_get_softc(dev);
342			priv->dev = dev;
343			priv->ifp = ifp;
344			priv->init = cvm_oct_common_init;
345			priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
346			priv->port = CVMX_PIP_NUM_INPUT_PORTS;
347			priv->queue = -1;
348
349			device_set_desc(dev, "Cavium Octeon POW Ethernet\n");
350
351			ifp->if_softc = priv;
352
353			if (priv->init(ifp) < 0) {
354				printf("\t\tFailed to register ethernet device for POW\n");
355				panic("%s: need to free ifp.", __func__);
356			} else {
357				cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = ifp;
358				printf("\t\t%s: POW send group %d, receive group %d\n",
359				if_name(ifp), pow_send_group, pow_receive_group);
360			}
361		} else {
362			printf("\t\tFailed to allocate ethernet device for POW\n");
363		}
364	}
365
366	ifnum = 0;
367	num_interfaces = cvmx_helper_get_number_of_interfaces();
368	for (interface = 0; interface < num_interfaces; interface++) {
369		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
370		int num_ports = cvmx_helper_ports_on_interface(interface);
371		int port;
372
373		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
374			cvm_oct_private_t *priv;
375			struct ifnet *ifp;
376
377			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
378			if (dev != NULL)
379				ifp = if_alloc(IFT_ETHER);
380			if (dev == NULL || ifp == NULL) {
381				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
382				continue;
383			}
384			/* XXX/juli set max send q len.  */
385#if 0
386			if (disable_core_queueing)
387				ifp->tx_queue_len = 0;
388#endif
389
390			/* Initialize the device private structure. */
391			device_probe(dev);
392			priv = device_get_softc(dev);
393			priv->dev = dev;
394			priv->ifp = ifp;
395			priv->imode = imode;
396			priv->port = port;
397			priv->queue = cvmx_pko_get_base_queue(priv->port);
398			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
399			for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
400				cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
401
402			switch (priv->imode) {
403
404			/* These types don't support ports to IPD/PKO */
405			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
406			case CVMX_HELPER_INTERFACE_MODE_PCIE:
407			case CVMX_HELPER_INTERFACE_MODE_PICMG:
408				break;
409
410			case CVMX_HELPER_INTERFACE_MODE_NPI:
411				priv->init = cvm_oct_common_init;
412				priv->uninit = cvm_oct_common_uninit;
413				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
414				break;
415
416			case CVMX_HELPER_INTERFACE_MODE_XAUI:
417				priv->init = cvm_oct_xaui_init;
418				priv->uninit = cvm_oct_xaui_uninit;
419				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
420				break;
421
422			case CVMX_HELPER_INTERFACE_MODE_LOOP:
423				priv->init = cvm_oct_common_init;
424				priv->uninit = cvm_oct_common_uninit;
425				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
426				break;
427
428			case CVMX_HELPER_INTERFACE_MODE_SGMII:
429				priv->init = cvm_oct_sgmii_init;
430				priv->uninit = cvm_oct_sgmii_uninit;
431				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
432				break;
433
434			case CVMX_HELPER_INTERFACE_MODE_SPI:
435				priv->init = cvm_oct_spi_init;
436				priv->uninit = cvm_oct_spi_uninit;
437				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
438				break;
439
440			case CVMX_HELPER_INTERFACE_MODE_RGMII:
441				priv->init = cvm_oct_rgmii_init;
442				priv->uninit = cvm_oct_rgmii_uninit;
443				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
444				break;
445
446			case CVMX_HELPER_INTERFACE_MODE_GMII:
447				priv->init = cvm_oct_rgmii_init;
448				priv->uninit = cvm_oct_rgmii_uninit;
449				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
450				break;
451			}
452
453			ifp->if_softc = priv;
454
455			if (!priv->init) {
456				panic("%s: unsupported device type, need to free ifp.", __func__);
457			} else
458			if (priv->init(ifp) < 0) {
459				printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
460				interface, priv->port);
461				panic("%s: init failed, need to free ifp.", __func__);
462			} else {
463				cvm_oct_device[priv->port] = ifp;
464				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
465			}
466		}
467	}
468
469	if (INTERRUPT_LIMIT) {
470		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
471		cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
472
473		/* Enable POW timer interrupt. It will count when there are packets available */
474		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
475	} else {
476		/* Enable POW interrupt when our port has at least one packet */
477		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
478	}
479
480	callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
481	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
482
483	return 0;
484}
485
486
487/**
488 * Module / driver shutdown
489 *
490 * @return Zero on success
491 */
492void cvm_oct_cleanup_module(void)
493{
494	int port;
495
496	/* Disable POW interrupt */
497	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
498
499	cvmx_ipd_disable();
500
501#if 0
502	/* Free the interrupt handler */
503	free_irq(8 + pow_receive_group, cvm_oct_device);
504#endif
505
506	callout_stop(&cvm_oct_poll_timer);
507	cvm_oct_rx_shutdown();
508	cvmx_pko_disable();
509
510	/* Free the ethernet devices */
511	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
512		if (cvm_oct_device[port]) {
513			cvm_oct_tx_shutdown(cvm_oct_device[port]);
514#if 0
515			unregister_netdev(cvm_oct_device[port]);
516			kfree(cvm_oct_device[port]);
517#else
518			panic("%s: need to detach and free interface.", __func__);
519#endif
520			cvm_oct_device[port] = NULL;
521		}
522	}
523
524	cvmx_pko_shutdown();
525#if 0
526	cvm_oct_proc_shutdown();
527#endif
528
529	cvmx_ipd_free_ptr();
530
531	/* Free the HW pools */
532	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
533	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
534	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
535		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
536}
537