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