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