ethernet.c revision 226024
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 226024 2011-10-04 20:17:43Z marcel $");
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 * The offset from mac_addr_base that should be used for the next port
102 * that is configured.  By convention, if any mgmt ports exist on the
103 * chip, they get the first mac addresses.  The ports controlled by
104 * this driver are numbered sequencially following any mgmt addresses
105 * that may exist.
106 */
107unsigned int cvm_oct_mac_addr_offset;
108
109/**
110 * Function to update link status.
111 */
112static void cvm_oct_update_link(void *context, int pending)
113{
114	cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
115	struct ifnet *ifp = priv->ifp;
116	cvmx_helper_link_info_t link_info;
117
118	link_info.u64 = priv->link_info;
119
120	if (link_info.s.link_up) {
121		if_link_state_change(ifp, LINK_STATE_UP);
122		DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
123			   if_name(ifp), link_info.s.speed,
124			   (link_info.s.full_duplex) ? "Full" : "Half",
125			   priv->port, priv->queue);
126	} else {
127		if_link_state_change(ifp, LINK_STATE_DOWN);
128		DEBUGPRINT("%s: Link down\n", if_name(ifp));
129	}
130	priv->need_link_update = 0;
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	static int updated;
142	if (port < CVMX_PIP_NUM_INPUT_PORTS) {
143		if (cvm_oct_device[port]) {
144			int queues_per_port;
145			int qos;
146			cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
147
148			cvm_oct_common_poll(priv->ifp);
149			if (priv->need_link_update) {
150				updated++;
151				taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
152			}
153
154			queues_per_port = cvmx_pko_get_num_queues(port);
155			/* Drain any pending packets in the free list */
156			for (qos = 0; qos < queues_per_port; qos++) {
157				if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
158					IF_LOCK(&priv->tx_free_queue[qos]);
159					while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
160						struct mbuf *m;
161
162						_IF_DEQUEUE(&priv->tx_free_queue[qos], m);
163						m_freem(m);
164					}
165					IF_UNLOCK(&priv->tx_free_queue[qos]);
166
167					/*
168					 * XXX locking!
169					 */
170					priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
171				}
172			}
173		}
174		port++;
175		/* Poll the next port in a 50th of a second.
176		   This spreads the polling of ports out a little bit */
177		callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
178	} else {
179		port = 0;
180		/* If any updates were made in this run, continue iterating at
181		 * 1/50th of a second, so that if a link has merely gone down
182		 * temporarily (e.g. because of interface reinitialization) it
183		 * will not be forced to stay down for an entire second.
184		 */
185		if (updated > 0) {
186			updated = 0;
187			callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
188		} else {
189			/* All ports have been polled. Start the next iteration through
190			   the ports in one second */
191			callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
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 pko_queues;
203	int error;
204	int rid;
205
206        sc = device_get_softc(bus);
207
208	/* Setup the FPA */
209	cvmx_fpa_enable();
210	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
211			     num_packet_buffers);
212	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
213			     num_packet_buffers);
214	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) {
215		/*
216		 * If the FPA uses different pools for output buffers and
217		 * packets, size the output buffer pool based on the number
218		 * of PKO queues.
219		 */
220		if (OCTEON_IS_MODEL(OCTEON_CN38XX))
221			pko_queues = 128;
222		else if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
223			pko_queues = 32;
224		else if (OCTEON_IS_MODEL(OCTEON_CN50XX))
225			pko_queues = 32;
226		else
227			pko_queues = 256;
228
229		cvm_oct_num_output_buffers = 4 * pko_queues;
230		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
231				     CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE,
232				     cvm_oct_num_output_buffers);
233	}
234
235	if (USE_RED)
236		cvmx_helper_setup_red(num_packet_buffers/4,
237				      num_packet_buffers/8);
238
239	/* Enable the MII interface */
240	if (!octeon_is_simulation())
241		cvmx_write_csr(CVMX_SMI_EN, 1);
242
243	/* Register an IRQ hander for to receive POW interrupts */
244        rid = 0;
245        sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
246					   CVMX_IRQ_WORKQ0 + pow_receive_group,
247					   CVMX_IRQ_WORKQ0 + pow_receive_group,
248					   1, RF_ACTIVE);
249        if (sc->sc_rx_irq == NULL) {
250                device_printf(bus, "could not allocate workq irq");
251		return;
252        }
253
254        error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
255			       cvm_oct_do_interrupt, NULL, cvm_oct_device,
256			       &sc->sc_rx_intr_cookie);
257        if (error != 0) {
258                device_printf(bus, "could not setup workq irq");
259		return;
260        }
261
262
263#ifdef SMP
264	{
265		cvmx_ciu_intx0_t en;
266		int core;
267
268		CPU_FOREACH(core) {
269			if (core == PCPU_GET(cpuid))
270				continue;
271
272			en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2));
273			en.s.workq |= (1<<pow_receive_group);
274			cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64);
275		}
276	}
277#endif
278}
279
280
281/**
282 * Free a work queue entry received in a intercept callback.
283 *
284 * @param work_queue_entry
285 *               Work queue entry to free
286 * @return Zero on success, Negative on failure.
287 */
288int cvm_oct_free_work(void *work_queue_entry)
289{
290	cvmx_wqe_t *work = work_queue_entry;
291
292	int segments = work->word2.s.bufs;
293	cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
294
295	while (segments--) {
296		cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
297		if (__predict_false(!segment_ptr.s.i))
298			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
299		segment_ptr = next_ptr;
300	}
301	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
302
303	return 0;
304}
305
306
307/**
308 * Module/ driver initialization. Creates the linux network
309 * devices.
310 *
311 * @return Zero on success
312 */
313int cvm_oct_init_module(device_t bus)
314{
315	device_t dev;
316	int ifnum;
317	int num_interfaces;
318	int interface;
319	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
320	int qos;
321
322	printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
323
324	/*
325	 * MAC addresses for this driver start after the management
326	 * ports.
327	 *
328	 * XXX Would be nice if __cvmx_mgmt_port_num_ports() were
329	 *     not static to cvmx-mgmt-port.c.
330	 */
331	if (OCTEON_IS_MODEL(OCTEON_CN56XX))
332		cvm_oct_mac_addr_offset = 1;
333	else if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN63XX))
334		cvm_oct_mac_addr_offset = 2;
335	else
336		cvm_oct_mac_addr_offset = 0;
337
338	cvm_oct_rx_initialize();
339	cvm_oct_configure_common_hw(bus);
340
341	cvmx_helper_initialize_packet_io_global();
342
343	/* Change the input group for all ports before input is enabled */
344	num_interfaces = cvmx_helper_get_number_of_interfaces();
345	for (interface = 0; interface < num_interfaces; interface++) {
346		int num_ports = cvmx_helper_ports_on_interface(interface);
347		int port;
348
349		for (port = 0; port < num_ports; port++) {
350			cvmx_pip_prt_tagx_t pip_prt_tagx;
351			int pkind = cvmx_helper_get_ipd_port(interface, port);
352
353			pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pkind));
354			pip_prt_tagx.s.grp = pow_receive_group;
355			cvmx_write_csr(CVMX_PIP_PRT_TAGX(pkind), pip_prt_tagx.u64);
356		}
357	}
358
359	cvmx_helper_ipd_and_packet_input_enable();
360
361	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
362
363	cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
364	    taskqueue_thread_enqueue, &cvm_oct_link_taskq);
365	taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
366	    "octe link taskq");
367
368	/* Initialize the FAU used for counting packet buffers that need to be freed */
369	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
370
371	ifnum = 0;
372	num_interfaces = cvmx_helper_get_number_of_interfaces();
373	for (interface = 0; interface < num_interfaces; interface++) {
374		cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
375		int num_ports = cvmx_helper_ports_on_interface(interface);
376		int port;
377
378		for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
379			cvm_oct_private_t *priv;
380			struct ifnet *ifp;
381
382			dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
383			if (dev != NULL)
384				ifp = if_alloc(IFT_ETHER);
385			if (dev == NULL || ifp == NULL) {
386				printf("\t\tFailed to allocate ethernet device for port %d\n", port);
387				continue;
388			}
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			TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
402
403			switch (priv->imode) {
404
405			/* These types don't support ports to IPD/PKO */
406			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
407			case CVMX_HELPER_INTERFACE_MODE_PCIE:
408			case CVMX_HELPER_INTERFACE_MODE_PICMG:
409				break;
410
411			case CVMX_HELPER_INTERFACE_MODE_NPI:
412				priv->init = cvm_oct_common_init;
413				priv->uninit = cvm_oct_common_uninit;
414				device_set_desc(dev, "Cavium Octeon NPI Ethernet");
415				break;
416
417			case CVMX_HELPER_INTERFACE_MODE_XAUI:
418				priv->init = cvm_oct_xaui_init;
419				priv->uninit = cvm_oct_common_uninit;
420				device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
421				break;
422
423			case CVMX_HELPER_INTERFACE_MODE_LOOP:
424				priv->init = cvm_oct_common_init;
425				priv->uninit = cvm_oct_common_uninit;
426				device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
427				break;
428
429			case CVMX_HELPER_INTERFACE_MODE_SGMII:
430				priv->init = cvm_oct_sgmii_init;
431				priv->uninit = cvm_oct_common_uninit;
432				device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
433				break;
434
435			case CVMX_HELPER_INTERFACE_MODE_SPI:
436				priv->init = cvm_oct_spi_init;
437				priv->uninit = cvm_oct_spi_uninit;
438				device_set_desc(dev, "Cavium Octeon SPI Ethernet");
439				break;
440
441			case CVMX_HELPER_INTERFACE_MODE_RGMII:
442				priv->init = cvm_oct_rgmii_init;
443				priv->uninit = cvm_oct_rgmii_uninit;
444				device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
445				break;
446
447			case CVMX_HELPER_INTERFACE_MODE_GMII:
448				priv->init = cvm_oct_rgmii_init;
449				priv->uninit = cvm_oct_rgmii_uninit;
450				device_set_desc(dev, "Cavium Octeon GMII Ethernet");
451				break;
452			}
453
454			ifp->if_softc = priv;
455
456			if (!priv->init) {
457				panic("%s: unsupported device type, need to free ifp.", __func__);
458			} else
459			if (priv->init(ifp) < 0) {
460				printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
461				interface, priv->port);
462				panic("%s: init failed, need to free ifp.", __func__);
463			} else {
464				cvm_oct_device[priv->port] = ifp;
465				fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
466			}
467		}
468	}
469
470	if (INTERRUPT_LIMIT) {
471		/* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
472		cvmx_write_csr(CVMX_POW_WQ_INT_PC, cvmx_clock_get_rate(CVMX_CLOCK_CORE)/(INTERRUPT_LIMIT*16*256)<<8);
473
474		/* Enable POW timer interrupt. It will count when there are packets available */
475		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
476	} else {
477		/* Enable POW interrupt when our port has at least one packet */
478		cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
479	}
480
481	callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
482	callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
483
484	return 0;
485}
486
487
488/**
489 * Module / driver shutdown
490 *
491 * @return Zero on success
492 */
493void cvm_oct_cleanup_module(device_t bus)
494{
495	int port;
496	struct octebus_softc *sc = device_get_softc(bus);
497
498	/* Disable POW interrupt */
499	cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
500
501	/* Free the interrupt handler */
502	bus_teardown_intr(bus, sc->sc_rx_irq, sc->sc_rx_intr_cookie);
503
504	callout_stop(&cvm_oct_poll_timer);
505	cvm_oct_rx_shutdown();
506
507	cvmx_helper_shutdown_packet_io_global();
508
509	/* Free the ethernet devices */
510	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
511		if (cvm_oct_device[port]) {
512			cvm_oct_tx_shutdown(cvm_oct_device[port]);
513#if 0
514			unregister_netdev(cvm_oct_device[port]);
515			kfree(cvm_oct_device[port]);
516#else
517			panic("%s: need to detach and free interface.", __func__);
518#endif
519			cvm_oct_device[port] = NULL;
520		}
521	}
522	/* Free the HW pools */
523	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
524	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
525
526	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
527		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, cvm_oct_num_output_buffers);
528
529	/* Disable FPA, all buffers are free, not done by helper shutdown. */
530	cvmx_fpa_disable();
531}
532