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