ethernet.c revision 215990
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 215990 2010-11-28 08:18:16Z 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
77extern int octeon_is_simulation(void);
78
79/**
80 * Exported from the kernel so we can determine board information. It is
81 * passed by the bootloader to the kernel.
82 */
83extern cvmx_bootinfo_t *octeon_bootinfo;
84
85/**
86 * Periodic timer to check auto negotiation
87 */
88static struct callout cvm_oct_poll_timer;
89
90/**
91 * Array of every ethernet device owned by this driver indexed by
92 * the ipd input port number.
93 */
94struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
95
96/**
97 * Task to handle link status changes.
98 */
99static struct taskqueue *cvm_oct_link_taskq;
100
101/**
102 * Function to update link status.
103 */
104static void cvm_oct_update_link(void *context, int pending)
105{
106	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
107	struct ifnet *ifp = priv->ifp;
108	cvmx_helper_link_info_t link_info;
109
110	link_info.u64 = priv->link_info;
111
112	if (link_info.s.link_up) {
113		if_link_state_change(ifp, LINK_STATE_UP);
114		DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
115			   if_name(ifp), link_info.s.speed,
116			   (link_info.s.full_duplex) ? "Full" : "Half",
117			   priv->port, priv->queue);
118	} else {
119		if_link_state_change(ifp, LINK_STATE_DOWN);
120		DEBUGPRINT("%s: Link down\n", if_name(ifp));
121	}
122	priv->need_link_update = 0;
123}
124
125/**
126 * Periodic timer tick for slow management operations
127 *
128 * @param arg    Device to check
129 */
130static void cvm_do_timer(void *arg)
131{
132	static int port;
133	static int updated;
134	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
135		if (cvm_oct_device[port]) {
136			int queues_per_port;
137			int qos;
138			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
139			if (priv->poll)
140			{
141				/* skip polling if we don't get the lock */
142				if (MDIO_TRYLOCK()) {
143					priv->poll(cvm_oct_device[port]);
144					MDIO_UNLOCK();
145
146					if (priv->need_link_update) {
147						updated++;
148						taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
149					}
150				}
151			}
152
153			queues_per_port = cvmx_pko_get_num_queues(port);
154			/* Drain any pending packets in the free list */
155			for (qos = 0; qos < queues_per_port; qos++) {
156				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
157					IF_LOCK(&priv->tx_free_queue[qos]);
158					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
159						struct mbuf *m;
160
161						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
162						m_freem(m);
163					}
164					IF_UNLOCK(&priv->tx_free_queue[qos]);
165
166					/*
167					 * XXX locking!
168					 */
169					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
170				}
171			}
172		}
173		port++;
174		/* Poll the next port in a 50th of a second.
175		   This spreads the polling of ports out a little bit */
176		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
177	} else {
178		port = 0;
179		/* If any updates were made in this run, continue iterating at
180		 * 1/50th of a second, so that if a link has merely gone down
181		 * temporarily (e.g. because of interface reinitialization) it
182		 * will not be forced to stay down for an entire second.
183		 */
184		if (updated > 0) {
185			updated = 0;
186			callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
187		} else {
188			/* All ports have been polled. Start the next iteration through
189			   the ports in one second */
190			callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
191		}
192	}
193}
194
195
196/**
197 * Configure common hardware for all interfaces
198 */
199static void cvm_oct_configure_common_hw(device_t bus)
200{
201	struct octebus_softc *sc;
202	int error;
203	int rid;
204
205        sc = device_get_softc(bus);
206
207	/* Setup the FPA */
208	cvmx_fpa_enable();
209	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
210	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
211	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
212		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
213
214	if (USE_RED)
215		cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
216
217	/* Enable the MII interface */
218	if (!octeon_is_simulation())
219		cvmx_write_csr(CVMX_SMI_EN, 1);
220
221	/* Register an IRQ hander for to receive POW interrupts */
222        rid = 0;
223        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
224					   CVMX_IRQ_WORKQ0 + pow_receive_group,
225					   CVMX_IRQ_WORKQ0 + pow_receive_group,
226					   1, RF_ACTIVE);
227        if (sc->sc_rx_irq == NULL) {
228                device_printf(bus, "could not allocate workq irq");
229		return;
230        }
231
232        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
233			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
234			       NULL);
235        if (error != 0) {
236                device_printf(bus, "could not setup workq irq");
237		return;
238        }
239
240
241#ifdef SMP
242	if (USE_MULTICORE_RECEIVE) {
243		critical_enter();
244		{
245			int cpu;
246			for (cpu = 0; cpu < mp_maxid; cpu++) {
247				if (!CPU_ABSENT(cpu) &&
248				   (cpu != PCPU_GET(cpuid))) {
249					cvmx_ciu_intx0_t en;
250					en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
251					en.s.workq |= (1<<pow_receive_group);
252					cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
253				}
254			}
255		}
256		critical_exit();
257	}
258#endif
259}
260
261
262/**
263 * Free a work queue entry received in a intercept callback.
264 *
265 * @param work_queue_entry
266 *               Work queue entry to free
267 * @return Zero on success, Negative on failure.
268 */
269int cvm_oct_free_work(void *work_queue_entry)
270{
271	cvmx_wqe_t *work = work_queue_entry;
272
273	int segments = work->word2.s.bufs;
274	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
275
276	while (segments--) {
277		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
278		if (__predict_false(!segment_ptr.s.i))
279			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
280		segment_ptr = next_ptr;
281	}
282	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
283
284	return 0;
285}
286
287
288/**
289 * Module/ driver initialization. Creates the linux network
290 * devices.
291 *
292 * @return Zero on success
293 */
294int cvm_oct_init_module(device_t bus)
295{
296	device_t dev;
297	int ifnum;
298	int num_interfaces;
299	int interface;
300	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
301	int qos;
302
303	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
304
305	cvm_oct_rx_initialize();
306	cvm_oct_configure_common_hw(bus);
307
308	cvmx_helper_initialize_packet_io_global();
309
310	/* Change the input group for all ports before input is enabled */
311	num_interfaces = cvmx_helper_get_number_of_interfaces();
312	for (interface = 0; interface < num_interfaces; interface++) {
313		int num_ports = cvmx_helper_ports_on_interface(interface);
314		int port;
315
316		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
317			cvmx_pip_prt_tagx_t pip_prt_tagx;
318			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
319			pip_prt_tagx.s.grp = pow_receive_group;
320			cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
321		}
322	}
323
324	cvmx_helper_ipd_and_packet_input_enable();
325
326	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
327
328	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
329	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
330	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
331	    "octe link taskq");
332
333	/* Initialize the FAU used for counting packet buffers that need to be freed */
334	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
335
336	ifnum = 0;
337	num_interfaces = cvmx_helper_get_number_of_interfaces();
338	for (interface = 0; interface < num_interfaces; interface++) {
339		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
340		int num_ports = cvmx_helper_ports_on_interface(interface);
341		int port;
342
343		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
344			cvm_oct_private_t *priv;
345			struct ifnet *ifp;
346
347			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
348			if (dev != NULL)
349				ifp = if_alloc(IFT_ETHER);
350			if (dev == NULL || ifp == NULL) {
351				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
352				continue;
353			}
354
355			/* Initialize the device private structure. */
356			device_probe(dev);
357			priv = device_get_softc(dev);
358			priv->dev = dev;
359			priv->ifp = ifp;
360			priv->imode = imode;
361			priv->port = port;
362			priv->queue = cvmx_pko_get_base_queue(priv->port);
363			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
364			for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
365				cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
366			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
367
368			switch (priv->imode) {
369
370			/* These types don't support ports to IPD/PKO */
371			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
372			case CVMX_HELPER_INTERFACE_MODE_PCIE:
373			case CVMX_HELPER_INTERFACE_MODE_PICMG:
374				break;
375
376			case CVMX_HELPER_INTERFACE_MODE_NPI:
377				priv->init = cvm_oct_common_init;
378				priv->uninit = cvm_oct_common_uninit;
379				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
380				break;
381
382			case CVMX_HELPER_INTERFACE_MODE_XAUI:
383				priv->init = cvm_oct_xaui_init;
384				priv->uninit = cvm_oct_common_uninit;
385				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
386				break;
387
388			case CVMX_HELPER_INTERFACE_MODE_LOOP:
389				priv->init = cvm_oct_common_init;
390				priv->uninit = cvm_oct_common_uninit;
391				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
392				break;
393
394			case CVMX_HELPER_INTERFACE_MODE_SGMII:
395				priv->init = cvm_oct_sgmii_init;
396				priv->uninit = cvm_oct_common_uninit;
397				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
398				break;
399
400			case CVMX_HELPER_INTERFACE_MODE_SPI:
401				priv->init = cvm_oct_spi_init;
402				priv->uninit = cvm_oct_spi_uninit;
403				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
404				break;
405
406			case CVMX_HELPER_INTERFACE_MODE_RGMII:
407				priv->init = cvm_oct_rgmii_init;
408				priv->uninit = cvm_oct_rgmii_uninit;
409				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
410				break;
411
412			case CVMX_HELPER_INTERFACE_MODE_GMII:
413				priv->init = cvm_oct_rgmii_init;
414				priv->uninit = cvm_oct_rgmii_uninit;
415				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
416				break;
417			}
418
419			ifp->if_softc = priv;
420
421			if (!priv->init) {
422				panic("%s: unsupported device type, need to free ifp.", __func__);
423			} else
424			if (priv->init(ifp) < 0) {
425				printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
426				interface, priv->port);
427				panic("%s: init failed, need to free ifp.", __func__);
428			} else {
429				cvm_oct_device[priv->port] = ifp;
430				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
431			}
432		}
433	}
434
435	if (INTERRUPT_LIMIT) {
436		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
437		cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
438
439		/* Enable POW timer interrupt. It will count when there are packets available */
440		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
441	} else {
442		/* Enable POW interrupt when our port has at least one packet */
443		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
444	}
445
446	callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
447	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
448
449	return 0;
450}
451
452
453/**
454 * Module / driver shutdown
455 *
456 * @return Zero on success
457 */
458void cvm_oct_cleanup_module(void)
459{
460	int port;
461
462	/* Disable POW interrupt */
463	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
464
465#if 0
466	/* Free the interrupt handler */
467	free_irq(8 + pow_receive_group, cvm_oct_device);
468#endif
469
470	callout_stop(&cvm_oct_poll_timer);
471	cvm_oct_rx_shutdown();
472
473	cvmx_helper_shutdown_packet_io_global();
474
475	/* Free the ethernet devices */
476	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
477		if (cvm_oct_device[port]) {
478			cvm_oct_tx_shutdown(cvm_oct_device[port]);
479#if 0
480			unregister_netdev(cvm_oct_device[port]);
481			kfree(cvm_oct_device[port]);
482#else
483			panic("%s: need to detach and free interface.", __func__);
484#endif
485			cvm_oct_device[port] = NULL;
486		}
487	}
488}
489