ethernet.c revision 219695
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 219695 2011-03-16 08:56:22Z 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 * Number of buffers in output buffer pool.
103 */
104static int cvm_oct_num_output_buffers;
105
106/**
107 * Function to update link status.
108 */
109static void cvm_oct_update_link(void *context, int pending)
110{
111	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
112	struct ifnet *ifp = priv->ifp;
113	cvmx_helper_link_info_t link_info;
114
115	link_info.u64 = priv->link_info;
116
117	if (link_info.s.link_up) {
118		if_link_state_change(ifp, LINK_STATE_UP);
119		DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
120			   if_name(ifp), link_info.s.speed,
121			   (link_info.s.full_duplex) ? "Full" : "Half",
122			   priv->port, priv->queue);
123	} else {
124		if_link_state_change(ifp, LINK_STATE_DOWN);
125		DEBUGPRINT("%s: Link down\n", if_name(ifp));
126	}
127	priv->need_link_update = 0;
128}
129
130/**
131 * Periodic timer tick for slow management operations
132 *
133 * @param arg    Device to check
134 */
135static void cvm_do_timer(void *arg)
136{
137	static int port;
138	static int updated;
139	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
140		if (cvm_oct_device[port]) {
141			int queues_per_port;
142			int qos;
143			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
144
145			cvm_oct_common_poll(priv->ifp);
146			if (priv->need_link_update) {
147				updated++;
148				taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
149			}
150
151			queues_per_port = cvmx_pko_get_num_queues(port);
152			/* Drain any pending packets in the free list */
153			for (qos = 0; qos < queues_per_port; qos++) {
154				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
155					IF_LOCK(&priv->tx_free_queue[qos]);
156					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
157						struct mbuf *m;
158
159						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
160						m_freem(m);
161					}
162					IF_UNLOCK(&priv->tx_free_queue[qos]);
163
164					/*
165					 * XXX locking!
166					 */
167					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
168				}
169			}
170		}
171		port++;
172		/* Poll the next port in a 50th of a second.
173		   This spreads the polling of ports out a little bit */
174		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
175	} else {
176		port = 0;
177		/* If any updates were made in this run, continue iterating at
178		 * 1/50th of a second, so that if a link has merely gone down
179		 * temporarily (e.g. because of interface reinitialization) it
180		 * will not be forced to stay down for an entire second.
181		 */
182		if (updated > 0) {
183			updated = 0;
184			callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
185		} else {
186			/* All ports have been polled. Start the next iteration through
187			   the ports in one second */
188			callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
189		}
190	}
191}
192
193/**
194 * Configure common hardware for all interfaces
195 */
196static void cvm_oct_configure_common_hw(device_t bus)
197{
198	struct octebus_softc *sc;
199	int pko_queues;
200	int error;
201	int rid;
202
203        sc = device_get_softc(bus);
204
205	/* Setup the FPA */
206	cvmx_fpa_enable();
207	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
208			     num_packet_buffers);
209	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
210			     num_packet_buffers);
211	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) {
212		/*
213		 * If the FPA uses different pools for output buffers and
214		 * packets, size the output buffer pool based on the number
215		 * of PKO queues.
216		 */
217		if (OCTEON_IS_MODEL(OCTEON_CN38XX))
218			pko_queues = 128;
219		else if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
220			pko_queues = 32;
221		else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
222			pko_queues = 32;
223		else
224			pko_queues = 256;
225
226		cvm_oct_num_output_buffers = 4 * pko_queues;
227		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
228				     CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE,
229				     cvm_oct_num_output_buffers);
230	}
231
232	if (USE_RED)
233		cvmx_helper_setup_red(num_packet_buffers/4,
234				      num_packet_buffers/8);
235
236	/* Enable the MII interface */
237	if (!octeon_is_simulation())
238		cvmx_write_csr(CVMX_SMI_EN, 1);
239
240	/* Register an IRQ hander for to receive POW interrupts */
241        rid = 0;
242        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
243					   CVMX_IRQ_WORKQ0 + pow_receive_group,
244					   CVMX_IRQ_WORKQ0 + pow_receive_group,
245					   1, RF_ACTIVE);
246        if (sc->sc_rx_irq == NULL) {
247                device_printf(bus, "could not allocate workq irq");
248		return;
249        }
250
251        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
252			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
253			       &sc->sc_rx_intr_cookie);
254        if (error != 0) {
255                device_printf(bus, "could not setup workq irq");
256		return;
257        }
258
259
260#ifdef SMP
261	{
262		cvmx_ciu_intx0_t en;
263		int core;
264
265		CPU_FOREACH(core) {
266			if (core == PCPU_GET(cpuid))
267				continue;
268
269			en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2));
270			en.s.workq |= (1<<pow_receive_group);
271			cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64);
272		}
273	}
274#endif
275}
276
277
278/**
279 * Free a work queue entry received in a intercept callback.
280 *
281 * @param work_queue_entry
282 *               Work queue entry to free
283 * @return Zero on success, Negative on failure.
284 */
285int cvm_oct_free_work(void *work_queue_entry)
286{
287	cvmx_wqe_t *work = work_queue_entry;
288
289	int segments = work->word2.s.bufs;
290	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
291
292	while (segments--) {
293		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
294		if (__predict_false(!segment_ptr.s.i))
295			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
296		segment_ptr = next_ptr;
297	}
298	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
299
300	return 0;
301}
302
303
304/**
305 * Module/ driver initialization. Creates the linux network
306 * devices.
307 *
308 * @return Zero on success
309 */
310int cvm_oct_init_module(device_t bus)
311{
312	device_t dev;
313	int ifnum;
314	int num_interfaces;
315	int interface;
316	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
317	int qos;
318
319	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
320
321	cvm_oct_rx_initialize();
322	cvm_oct_configure_common_hw(bus);
323
324	cvmx_helper_initialize_packet_io_global();
325
326	/* Change the input group for all ports before input is enabled */
327	num_interfaces = cvmx_helper_get_number_of_interfaces();
328	for (interface = 0; interface < num_interfaces; interface++) {
329		int num_ports = cvmx_helper_ports_on_interface(interface);
330		int port;
331
332		for (port = 0; port < num_ports; port++) {
333			cvmx_pip_prt_tagx_t pip_prt_tagx;
334			int pkind = cvmx_helper_get_ipd_port(interface, port);
335
336			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pkind));
337			pip_prt_tagx.s.grp = pow_receive_group;
338			cvmx_write_csr(CVMX_PIP_PRT_TAGX(pkind), pip_prt_tagx.u64);
339		}
340	}
341
342	cvmx_helper_ipd_and_packet_input_enable();
343
344	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
345
346	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
347	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
348	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
349	    "octe link taskq");
350
351	/* Initialize the FAU used for counting packet buffers that need to be freed */
352	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
353
354	ifnum = 0;
355	num_interfaces = cvmx_helper_get_number_of_interfaces();
356	for (interface = 0; interface < num_interfaces; interface++) {
357		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
358		int num_ports = cvmx_helper_ports_on_interface(interface);
359		int port;
360
361		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
362			cvm_oct_private_t *priv;
363			struct ifnet *ifp;
364
365			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
366			if (dev != NULL)
367				ifp = if_alloc(IFT_ETHER);
368			if (dev == NULL || ifp == NULL) {
369				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
370				continue;
371			}
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_common_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_common_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(device_t bus)
477{
478	int port;
479	struct octebus_softc *sc = device_get_softc(bus);
480
481	/* Disable POW interrupt */
482	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
483
484	/* Free the interrupt handler */
485	bus_teardown_intr(bus, sc->sc_rx_irq, sc->sc_rx_intr_cookie);
486
487	callout_stop(&cvm_oct_poll_timer);
488	cvm_oct_rx_shutdown();
489
490	cvmx_helper_shutdown_packet_io_global();
491
492	/* Free the ethernet devices */
493	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
494		if (cvm_oct_device[port]) {
495			cvm_oct_tx_shutdown(cvm_oct_device[port]);
496#if 0
497			unregister_netdev(cvm_oct_device[port]);
498			kfree(cvm_oct_device[port]);
499#else
500			panic("%s: need to detach and free interface.", __func__);
501#endif
502			cvm_oct_device[port] = NULL;
503		}
504	}
505	/* Free the HW pools */
506	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
507	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
508
509	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
510		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, cvm_oct_num_output_buffers);
511
512	/* Disable FPA, all buffers are free, not done by helper shutdown. */
513	cvmx_fpa_disable();
514}
515