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