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