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