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 */
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 */
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 */
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 */
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 */
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 */
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 }
Cache object: 13f58181bf89fd6e3352f409919627f9
|