ethernet.c revision 210311
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 210311 2010-07-20 19:25:11Z jmallett $"); 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 44#include <net/ethernet.h> 45#include <net/if.h> 46#include <net/if_types.h> 47 48#include "wrapper-cvmx-includes.h" 49#include "ethernet-headers.h" 50 51#include "octebusvar.h" 52 53/* 54 * XXX/juli 55 * Convert 0444 to tunables, 0644 to sysctls. 56 */ 57#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS 58int num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS; 59#else 60int num_packet_buffers = 1024; 61#endif 62TUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers); 63/* 64 "\t\tNumber of packet buffers to allocate and store in the\n" 65 "\t\tFPA. By default, 1024 packet buffers are used unless\n" 66 "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */ 67 68int pow_receive_group = 15; 69TUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group); 70/* 71 "\t\tPOW group to receive packets from. All ethernet hardware\n" 72 "\t\twill be configured to send incomming packets to this POW\n" 73 "\t\tgroup. Also any other software can submit packets to this\n" 74 "\t\tgroup for the kernel to process." */ 75 76int pow_send_group = -1; /* XXX Should be a sysctl. */ 77TUNABLE_INT("hw.octe.pow_send_group", &pow_send_group); 78/* 79 "\t\tPOW group to send packets to other software on. This\n" 80 "\t\tcontrols the creation of the virtual device pow0.\n" 81 "\t\talways_use_pow also depends on this value." */ 82 83int always_use_pow; 84TUNABLE_INT("hw.octe.always_use_pow", &always_use_pow); 85/* 86 "\t\tWhen set, always send to the pow group. This will cause\n" 87 "\t\tpackets sent to real ethernet devices to be sent to the\n" 88 "\t\tPOW group instead of the hardware. Unless some other\n" 89 "\t\tapplication changes the config, packets will still be\n" 90 "\t\treceived from the low level hardware. Use this option\n" 91 "\t\tto allow a CVMX app to intercept all packets from the\n" 92 "\t\tlinux kernel. You must specify pow_send_group along with\n" 93 "\t\tthis option." */ 94 95char pow_send_list[128] = ""; 96TUNABLE_STR("hw.octe.pow_send_list", pow_send_list, sizeof pow_send_list); 97/* 98 "\t\tComma separated list of ethernet devices that should use the\n" 99 "\t\tPOW for transmit instead of the actual ethernet hardware. This\n" 100 "\t\tis a per port version of always_use_pow. always_use_pow takes\n" 101 "\t\tprecedence over this list. For example, setting this to\n" 102 "\t\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n" 103 "\t\tusing the pow_send_group." */ 104 105 106static int disable_core_queueing = 1; 107TUNABLE_INT("hw.octe.disable_core_queueing", &disable_core_queueing); 108/* 109 "\t\tWhen set the networking core's tx_queue_len is set to zero. This\n" 110 "\t\tallows packets to be sent without lock contention in the packet scheduler\n" 111 "\t\tresulting in some cases in improved throughput.\n" */ 112 113extern int octeon_is_simulation(void); 114 115/** 116 * Exported from the kernel so we can determine board information. It is 117 * passed by the bootloader to the kernel. 118 */ 119extern cvmx_bootinfo_t *octeon_bootinfo; 120 121/** 122 * Periodic timer to check auto negotiation 123 */ 124static struct callout cvm_oct_poll_timer; 125 126/** 127 * Array of every ethernet device owned by this driver indexed by 128 * the ipd input port number. 129 */ 130struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; 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 if (port < CVMX_PIP_NUM_INPUT_PORTS) { 142 if (cvm_oct_device[port]) { 143 int queues_per_port; 144 int qos; 145 cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc; 146 if (priv->poll) 147 { 148 /* skip polling if we don't get the lock */ 149 if (MDIO_TRYLOCK()) { 150 priv->poll(cvm_oct_device[port]); 151 MDIO_UNLOCK(); 152 } 153 } 154 155 queues_per_port = cvmx_pko_get_num_queues(port); 156 /* Drain any pending packets in the free list */ 157 for (qos = 0; qos < queues_per_port; qos++) { 158 if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) { 159 IF_LOCK(&priv->tx_free_queue[qos]); 160 while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) { 161 struct mbuf *m; 162 163 _IF_DEQUEUE(&priv->tx_free_queue[qos], m); 164 m_freem(m); 165 } 166 IF_UNLOCK(&priv->tx_free_queue[qos]); 167 168 /* 169 * XXX locking! 170 */ 171 priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 172 } 173 } 174#if 0 175 cvm_oct_device[port]->get_stats(cvm_oct_device[port]); 176#endif 177 } 178 port++; 179 /* Poll the next port in a 50th of a second. 180 This spreads the polling of ports out a little bit */ 181 callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 182 } else { 183 port = 0; 184 /* All ports have been polled. Start the next iteration through 185 the ports in one second */ 186 callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 187 } 188} 189 190 191/** 192 * Configure common hardware for all interfaces 193 */ 194static void cvm_oct_configure_common_hw(device_t bus) 195{ 196 struct octebus_softc *sc; 197 int error; 198 int rid; 199 200 sc = device_get_softc(bus); 201 202 /* Setup the FPA */ 203 cvmx_fpa_enable(); 204 cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); 205 cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); 206 if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) 207 cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); 208 209 if (USE_RED) 210 cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8); 211 212 /* Enable the MII interface */ 213 if (!octeon_is_simulation()) 214 cvmx_write_csr(CVMX_SMI_EN, 1); 215 216 /* Register an IRQ hander for to receive POW interrupts */ 217 rid = 0; 218 sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid, 219 CVMX_IRQ_WORKQ0 + pow_receive_group, 220 CVMX_IRQ_WORKQ0 + pow_receive_group, 221 1, RF_ACTIVE); 222 if (sc->sc_rx_irq == NULL) { 223 device_printf(bus, "could not allocate workq irq"); 224 return; 225 } 226 227 error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE, 228 cvm_oct_do_interrupt, NULL, cvm_oct_device, 229 NULL); 230 if (error != 0) { 231 device_printf(bus, "could not setup workq irq"); 232 return; 233 } 234 235 236#ifdef SMP 237 if (USE_MULTICORE_RECEIVE) { 238 critical_enter(); 239 { 240 int cpu; 241 for (cpu = 0; cpu < mp_maxid; cpu++) { 242 if (!CPU_ABSENT(cpu) && 243 (cpu != PCPU_GET(cpuid))) { 244 cvmx_ciu_intx0_t en; 245 en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2)); 246 en.s.workq |= (1<<pow_receive_group); 247 cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64); 248 } 249 } 250 } 251 critical_exit(); 252 } 253#endif 254} 255 256 257/** 258 * Free a work queue entry received in a intercept callback. 259 * 260 * @param work_queue_entry 261 * Work queue entry to free 262 * @return Zero on success, Negative on failure. 263 */ 264int cvm_oct_free_work(void *work_queue_entry) 265{ 266 cvmx_wqe_t *work = work_queue_entry; 267 268 int segments = work->word2.s.bufs; 269 cvmx_buf_ptr_t segment_ptr = work->packet_ptr; 270 271 while (segments--) { 272 cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8); 273 if (__predict_false(!segment_ptr.s.i)) 274 cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128)); 275 segment_ptr = next_ptr; 276 } 277 cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1)); 278 279 return 0; 280} 281 282 283/** 284 * Module/ driver initialization. Creates the linux network 285 * devices. 286 * 287 * @return Zero on success 288 */ 289int cvm_oct_init_module(device_t bus) 290{ 291 device_t dev; 292 int ifnum; 293 int num_interfaces; 294 int interface; 295 int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; 296 int qos; 297 298 printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING); 299 300#if 0 301 cvm_oct_proc_initialize(); 302#endif 303 cvm_oct_rx_initialize(); 304 cvm_oct_configure_common_hw(bus); 305 306 cvmx_helper_initialize_packet_io_global(); 307 308 /* Change the input group for all ports before input is enabled */ 309 num_interfaces = cvmx_helper_get_number_of_interfaces(); 310 for (interface = 0; interface < num_interfaces; interface++) { 311 int num_ports = cvmx_helper_ports_on_interface(interface); 312 int port; 313 314 for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) { 315 cvmx_pip_prt_tagx_t pip_prt_tagx; 316 pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port)); 317 pip_prt_tagx.s.grp = pow_receive_group; 318 cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64); 319 } 320 } 321 322 cvmx_helper_ipd_and_packet_input_enable(); 323 324 memset(cvm_oct_device, 0, sizeof(cvm_oct_device)); 325 326 /* Initialize the FAU used for counting packet buffers that need to be freed */ 327 cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); 328 329 if ((pow_send_group != -1)) { 330 struct ifnet *ifp; 331 332 printf("\tConfiguring device for POW only access\n"); 333 dev = BUS_ADD_CHILD(bus, 0, "pow", 0); 334 if (dev != NULL) 335 ifp = if_alloc(IFT_ETHER); 336 if (dev != NULL && ifp != NULL) { 337 /* Initialize the device private structure. */ 338 cvm_oct_private_t *priv; 339 340 device_probe(dev); 341 priv = device_get_softc(dev); 342 priv->dev = dev; 343 priv->ifp = ifp; 344 priv->init = cvm_oct_common_init; 345 priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED; 346 priv->port = CVMX_PIP_NUM_INPUT_PORTS; 347 priv->queue = -1; 348 349 device_set_desc(dev, "Cavium Octeon POW Ethernet\n"); 350 351 ifp->if_softc = priv; 352 353 if (priv->init(ifp) < 0) { 354 printf("\t\tFailed to register ethernet device for POW\n"); 355 panic("%s: need to free ifp.", __func__); 356 } else { 357 cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = ifp; 358 printf("\t\t%s: POW send group %d, receive group %d\n", 359 if_name(ifp), pow_send_group, pow_receive_group); 360 } 361 } else { 362 printf("\t\tFailed to allocate ethernet device for POW\n"); 363 } 364 } 365 366 ifnum = 0; 367 num_interfaces = cvmx_helper_get_number_of_interfaces(); 368 for (interface = 0; interface < num_interfaces; interface++) { 369 cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface); 370 int num_ports = cvmx_helper_ports_on_interface(interface); 371 int port; 372 373 for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) { 374 cvm_oct_private_t *priv; 375 struct ifnet *ifp; 376 377 dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++); 378 if (dev != NULL) 379 ifp = if_alloc(IFT_ETHER); 380 if (dev == NULL || ifp == NULL) { 381 printf("\t\tFailed to allocate ethernet device for port %d\n", port); 382 continue; 383 } 384 /* XXX/juli set max send q len. */ 385#if 0 386 if (disable_core_queueing) 387 ifp->tx_queue_len = 0; 388#endif 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 402 switch (priv->imode) { 403 404 /* These types don't support ports to IPD/PKO */ 405 case CVMX_HELPER_INTERFACE_MODE_DISABLED: 406 case CVMX_HELPER_INTERFACE_MODE_PCIE: 407 case CVMX_HELPER_INTERFACE_MODE_PICMG: 408 break; 409 410 case CVMX_HELPER_INTERFACE_MODE_NPI: 411 priv->init = cvm_oct_common_init; 412 priv->uninit = cvm_oct_common_uninit; 413 device_set_desc(dev, "Cavium Octeon NPI Ethernet"); 414 break; 415 416 case CVMX_HELPER_INTERFACE_MODE_XAUI: 417 priv->init = cvm_oct_xaui_init; 418 priv->uninit = cvm_oct_xaui_uninit; 419 device_set_desc(dev, "Cavium Octeon XAUI Ethernet"); 420 break; 421 422 case CVMX_HELPER_INTERFACE_MODE_LOOP: 423 priv->init = cvm_oct_common_init; 424 priv->uninit = cvm_oct_common_uninit; 425 device_set_desc(dev, "Cavium Octeon LOOP Ethernet"); 426 break; 427 428 case CVMX_HELPER_INTERFACE_MODE_SGMII: 429 priv->init = cvm_oct_sgmii_init; 430 priv->uninit = cvm_oct_sgmii_uninit; 431 device_set_desc(dev, "Cavium Octeon SGMII Ethernet"); 432 break; 433 434 case CVMX_HELPER_INTERFACE_MODE_SPI: 435 priv->init = cvm_oct_spi_init; 436 priv->uninit = cvm_oct_spi_uninit; 437 device_set_desc(dev, "Cavium Octeon SPI Ethernet"); 438 break; 439 440 case CVMX_HELPER_INTERFACE_MODE_RGMII: 441 priv->init = cvm_oct_rgmii_init; 442 priv->uninit = cvm_oct_rgmii_uninit; 443 device_set_desc(dev, "Cavium Octeon RGMII Ethernet"); 444 break; 445 446 case CVMX_HELPER_INTERFACE_MODE_GMII: 447 priv->init = cvm_oct_rgmii_init; 448 priv->uninit = cvm_oct_rgmii_uninit; 449 device_set_desc(dev, "Cavium Octeon GMII Ethernet"); 450 break; 451 } 452 453 ifp->if_softc = priv; 454 455 if (!priv->init) { 456 panic("%s: unsupported device type, need to free ifp.", __func__); 457 } else 458 if (priv->init(ifp) < 0) { 459 printf("\t\tFailed to register ethernet device for interface %d, port %d\n", 460 interface, priv->port); 461 panic("%s: init failed, need to free ifp.", __func__); 462 } else { 463 cvm_oct_device[priv->port] = ifp; 464 fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t); 465 } 466 } 467 } 468 469 if (INTERRUPT_LIMIT) { 470 /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */ 471 cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8); 472 473 /* Enable POW timer interrupt. It will count when there are packets available */ 474 cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24); 475 } else { 476 /* Enable POW interrupt when our port has at least one packet */ 477 cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001); 478 } 479 480 callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE); 481 callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 482 483 return 0; 484} 485 486 487/** 488 * Module / driver shutdown 489 * 490 * @return Zero on success 491 */ 492void cvm_oct_cleanup_module(void) 493{ 494 int port; 495 496 /* Disable POW interrupt */ 497 cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0); 498 499 cvmx_ipd_disable(); 500 501#if 0 502 /* Free the interrupt handler */ 503 free_irq(8 + pow_receive_group, cvm_oct_device); 504#endif 505 506 callout_stop(&cvm_oct_poll_timer); 507 cvm_oct_rx_shutdown(); 508 cvmx_pko_disable(); 509 510 /* Free the ethernet devices */ 511 for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { 512 if (cvm_oct_device[port]) { 513 cvm_oct_tx_shutdown(cvm_oct_device[port]); 514#if 0 515 unregister_netdev(cvm_oct_device[port]); 516 kfree(cvm_oct_device[port]); 517#else 518 panic("%s: need to detach and free interface.", __func__); 519#endif 520 cvm_oct_device[port] = NULL; 521 } 522 } 523 524 cvmx_pko_shutdown(); 525#if 0 526 cvm_oct_proc_shutdown(); 527#endif 528 529 cvmx_ipd_free_ptr(); 530 531 /* Free the HW pools */ 532 cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); 533 cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); 534 if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) 535 cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); 536} 537