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