ethernet.c revision 226024
1210311Sjmallett/************************************************************************* 2210311SjmallettCopyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights 3210311Sjmallettreserved. 4210311Sjmallett 5210311Sjmallett 6210311SjmallettRedistribution and use in source and binary forms, with or without 7210311Sjmallettmodification, are permitted provided that the following conditions are 8210311Sjmallettmet: 9210311Sjmallett 10210311Sjmallett * Redistributions of source code must retain the above copyright 11210311Sjmallett notice, this list of conditions and the following disclaimer. 12210311Sjmallett 13210311Sjmallett * Redistributions in binary form must reproduce the above 14210311Sjmallett copyright notice, this list of conditions and the following 15210311Sjmallett disclaimer in the documentation and/or other materials provided 16210311Sjmallett with the distribution. 17210311Sjmallett 18210311Sjmallett * Neither the name of Cavium Networks nor the names of 19210311Sjmallett its contributors may be used to endorse or promote products 20210311Sjmallett derived from this software without specific prior written 21210311Sjmallett permission. 22210311Sjmallett 23210311SjmallettThis 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. 24210311Sjmallett 25210311SjmallettTO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 26210311SjmallettAND 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. 27210311Sjmallett*************************************************************************/ 28210311Sjmallett 29210311Sjmallett#include <sys/cdefs.h> 30210311Sjmallett__FBSDID("$FreeBSD: head/sys/mips/cavium/octe/ethernet.c 226024 2011-10-04 20:17:43Z marcel $"); 31210311Sjmallett 32210311Sjmallett#include <sys/param.h> 33210311Sjmallett#include <sys/systm.h> 34210311Sjmallett#include <sys/bus.h> 35210311Sjmallett#include <sys/conf.h> 36210311Sjmallett#include <sys/endian.h> 37210311Sjmallett#include <sys/kernel.h> 38210311Sjmallett#include <sys/rman.h> 39210311Sjmallett#include <sys/mbuf.h> 40210311Sjmallett#include <sys/socket.h> 41210311Sjmallett#include <sys/module.h> 42210311Sjmallett#include <sys/smp.h> 43213150Sjmallett#include <sys/taskqueue.h> 44210311Sjmallett 45210311Sjmallett#include <net/ethernet.h> 46210311Sjmallett#include <net/if.h> 47210311Sjmallett#include <net/if_types.h> 48210311Sjmallett 49210311Sjmallett#include "wrapper-cvmx-includes.h" 50210311Sjmallett#include "ethernet-headers.h" 51210311Sjmallett 52210311Sjmallett#include "octebusvar.h" 53210311Sjmallett 54210311Sjmallett/* 55210311Sjmallett * XXX/juli 56210311Sjmallett * Convert 0444 to tunables, 0644 to sysctls. 57210311Sjmallett */ 58210311Sjmallett#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS 59210311Sjmallettint num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS; 60210311Sjmallett#else 61210311Sjmallettint num_packet_buffers = 1024; 62210311Sjmallett#endif 63210311SjmallettTUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers); 64210311Sjmallett/* 65210311Sjmallett "\t\tNumber of packet buffers to allocate and store in the\n" 66210311Sjmallett "\t\tFPA. By default, 1024 packet buffers are used unless\n" 67210311Sjmallett "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */ 68210311Sjmallett 69210311Sjmallettint pow_receive_group = 15; 70210311SjmallettTUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group); 71210311Sjmallett/* 72210311Sjmallett "\t\tPOW group to receive packets from. All ethernet hardware\n" 73210311Sjmallett "\t\twill be configured to send incomming packets to this POW\n" 74210311Sjmallett "\t\tgroup. Also any other software can submit packets to this\n" 75210311Sjmallett "\t\tgroup for the kernel to process." */ 76210311Sjmallett 77210311Sjmallettextern int octeon_is_simulation(void); 78210311Sjmallett 79210311Sjmallett/** 80210311Sjmallett * Periodic timer to check auto negotiation 81210311Sjmallett */ 82210311Sjmallettstatic struct callout cvm_oct_poll_timer; 83210311Sjmallett 84210311Sjmallett/** 85210311Sjmallett * Array of every ethernet device owned by this driver indexed by 86210311Sjmallett * the ipd input port number. 87210311Sjmallett */ 88210311Sjmallettstruct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; 89210311Sjmallett 90213150Sjmallett/** 91213150Sjmallett * Task to handle link status changes. 92213150Sjmallett */ 93213150Sjmallettstatic struct taskqueue *cvm_oct_link_taskq; 94210311Sjmallett 95219694Sjmallett/* 96219694Sjmallett * Number of buffers in output buffer pool. 97219694Sjmallett */ 98219694Sjmallettstatic int cvm_oct_num_output_buffers; 99219694Sjmallett 100219706Sjmallett/* 101219706Sjmallett * The offset from mac_addr_base that should be used for the next port 102219706Sjmallett * that is configured. By convention, if any mgmt ports exist on the 103219706Sjmallett * chip, they get the first mac addresses. The ports controlled by 104219706Sjmallett * this driver are numbered sequencially following any mgmt addresses 105219706Sjmallett * that may exist. 106219706Sjmallett */ 107219706Sjmallettunsigned int cvm_oct_mac_addr_offset; 108219706Sjmallett 109210311Sjmallett/** 110213150Sjmallett * Function to update link status. 111213150Sjmallett */ 112213150Sjmallettstatic void cvm_oct_update_link(void *context, int pending) 113213150Sjmallett{ 114213150Sjmallett cvm_oct_private_t *priv = (cvm_oct_private_t *)context; 115213150Sjmallett struct ifnet *ifp = priv->ifp; 116213150Sjmallett cvmx_helper_link_info_t link_info; 117213150Sjmallett 118213150Sjmallett link_info.u64 = priv->link_info; 119213150Sjmallett 120213150Sjmallett if (link_info.s.link_up) { 121213150Sjmallett if_link_state_change(ifp, LINK_STATE_UP); 122215959Sjmallett DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n", 123215959Sjmallett if_name(ifp), link_info.s.speed, 124215959Sjmallett (link_info.s.full_duplex) ? "Full" : "Half", 125215959Sjmallett priv->port, priv->queue); 126213150Sjmallett } else { 127213150Sjmallett if_link_state_change(ifp, LINK_STATE_DOWN); 128213150Sjmallett DEBUGPRINT("%s: Link down\n", if_name(ifp)); 129213150Sjmallett } 130213150Sjmallett priv->need_link_update = 0; 131213150Sjmallett} 132213150Sjmallett 133213150Sjmallett/** 134210311Sjmallett * Periodic timer tick for slow management operations 135210311Sjmallett * 136210311Sjmallett * @param arg Device to check 137210311Sjmallett */ 138210311Sjmallettstatic void cvm_do_timer(void *arg) 139210311Sjmallett{ 140210311Sjmallett static int port; 141213807Sjmallett static int updated; 142210311Sjmallett if (port < CVMX_PIP_NUM_INPUT_PORTS) { 143210311Sjmallett if (cvm_oct_device[port]) { 144210311Sjmallett int queues_per_port; 145210311Sjmallett int qos; 146210311Sjmallett cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc; 147213150Sjmallett 148216071Sjmallett cvm_oct_common_poll(priv->ifp); 149216071Sjmallett if (priv->need_link_update) { 150216071Sjmallett updated++; 151216071Sjmallett taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task); 152210311Sjmallett } 153210311Sjmallett 154210311Sjmallett queues_per_port = cvmx_pko_get_num_queues(port); 155210311Sjmallett /* Drain any pending packets in the free list */ 156210311Sjmallett for (qos = 0; qos < queues_per_port; qos++) { 157210311Sjmallett if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) { 158210311Sjmallett IF_LOCK(&priv->tx_free_queue[qos]); 159210311Sjmallett while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) { 160210311Sjmallett struct mbuf *m; 161210311Sjmallett 162210311Sjmallett _IF_DEQUEUE(&priv->tx_free_queue[qos], m); 163210311Sjmallett m_freem(m); 164210311Sjmallett } 165210311Sjmallett IF_UNLOCK(&priv->tx_free_queue[qos]); 166210311Sjmallett 167210311Sjmallett /* 168210311Sjmallett * XXX locking! 169210311Sjmallett */ 170210311Sjmallett priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 171210311Sjmallett } 172210311Sjmallett } 173210311Sjmallett } 174210311Sjmallett port++; 175210311Sjmallett /* Poll the next port in a 50th of a second. 176210311Sjmallett This spreads the polling of ports out a little bit */ 177210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 178210311Sjmallett } else { 179210311Sjmallett port = 0; 180213807Sjmallett /* If any updates were made in this run, continue iterating at 181213807Sjmallett * 1/50th of a second, so that if a link has merely gone down 182213807Sjmallett * temporarily (e.g. because of interface reinitialization) it 183213807Sjmallett * will not be forced to stay down for an entire second. 184213807Sjmallett */ 185213807Sjmallett if (updated > 0) { 186213807Sjmallett updated = 0; 187213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 188213807Sjmallett } else { 189213807Sjmallett /* All ports have been polled. Start the next iteration through 190213807Sjmallett the ports in one second */ 191213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 192213807Sjmallett } 193210311Sjmallett } 194210311Sjmallett} 195210311Sjmallett 196210311Sjmallett/** 197210311Sjmallett * Configure common hardware for all interfaces 198210311Sjmallett */ 199210311Sjmallettstatic void cvm_oct_configure_common_hw(device_t bus) 200210311Sjmallett{ 201210311Sjmallett struct octebus_softc *sc; 202219694Sjmallett int pko_queues; 203210311Sjmallett int error; 204210311Sjmallett int rid; 205210311Sjmallett 206210311Sjmallett sc = device_get_softc(bus); 207210311Sjmallett 208210311Sjmallett /* Setup the FPA */ 209210311Sjmallett cvmx_fpa_enable(); 210219694Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, 211219694Sjmallett num_packet_buffers); 212219694Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, 213219694Sjmallett num_packet_buffers); 214219694Sjmallett if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) { 215219694Sjmallett /* 216219694Sjmallett * If the FPA uses different pools for output buffers and 217219694Sjmallett * packets, size the output buffer pool based on the number 218219694Sjmallett * of PKO queues. 219219694Sjmallett */ 220219694Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN38XX)) 221219694Sjmallett pko_queues = 128; 222219694Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) 223219694Sjmallett pko_queues = 32; 224219694Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN50XX)) 225219694Sjmallett pko_queues = 32; 226219694Sjmallett else 227219694Sjmallett pko_queues = 256; 228210311Sjmallett 229219694Sjmallett cvm_oct_num_output_buffers = 4 * pko_queues; 230219694Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, 231219694Sjmallett CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 232219694Sjmallett cvm_oct_num_output_buffers); 233219694Sjmallett } 234219694Sjmallett 235210311Sjmallett if (USE_RED) 236219694Sjmallett cvmx_helper_setup_red(num_packet_buffers/4, 237219694Sjmallett num_packet_buffers/8); 238210311Sjmallett 239210311Sjmallett /* Enable the MII interface */ 240210311Sjmallett if (!octeon_is_simulation()) 241210311Sjmallett cvmx_write_csr(CVMX_SMI_EN, 1); 242210311Sjmallett 243210311Sjmallett /* Register an IRQ hander for to receive POW interrupts */ 244210311Sjmallett rid = 0; 245210311Sjmallett sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid, 246210311Sjmallett CVMX_IRQ_WORKQ0 + pow_receive_group, 247210311Sjmallett CVMX_IRQ_WORKQ0 + pow_receive_group, 248210311Sjmallett 1, RF_ACTIVE); 249210311Sjmallett if (sc->sc_rx_irq == NULL) { 250210311Sjmallett device_printf(bus, "could not allocate workq irq"); 251210311Sjmallett return; 252210311Sjmallett } 253210311Sjmallett 254210311Sjmallett error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE, 255210311Sjmallett cvm_oct_do_interrupt, NULL, cvm_oct_device, 256219695Sjmallett &sc->sc_rx_intr_cookie); 257210311Sjmallett if (error != 0) { 258210311Sjmallett device_printf(bus, "could not setup workq irq"); 259210311Sjmallett return; 260210311Sjmallett } 261210311Sjmallett 262210311Sjmallett 263210311Sjmallett#ifdef SMP 264217664Sjmallett { 265217664Sjmallett cvmx_ciu_intx0_t en; 266217664Sjmallett int core; 267217210Sjmallett 268217664Sjmallett CPU_FOREACH(core) { 269217664Sjmallett if (core == PCPU_GET(cpuid)) 270217664Sjmallett continue; 271217210Sjmallett 272217664Sjmallett en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2)); 273217664Sjmallett en.s.workq |= (1<<pow_receive_group); 274217664Sjmallett cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64); 275210311Sjmallett } 276210311Sjmallett } 277210311Sjmallett#endif 278210311Sjmallett} 279210311Sjmallett 280210311Sjmallett 281210311Sjmallett/** 282210311Sjmallett * Free a work queue entry received in a intercept callback. 283210311Sjmallett * 284210311Sjmallett * @param work_queue_entry 285210311Sjmallett * Work queue entry to free 286210311Sjmallett * @return Zero on success, Negative on failure. 287210311Sjmallett */ 288210311Sjmallettint cvm_oct_free_work(void *work_queue_entry) 289210311Sjmallett{ 290210311Sjmallett cvmx_wqe_t *work = work_queue_entry; 291210311Sjmallett 292210311Sjmallett int segments = work->word2.s.bufs; 293210311Sjmallett cvmx_buf_ptr_t segment_ptr = work->packet_ptr; 294210311Sjmallett 295210311Sjmallett while (segments--) { 296210311Sjmallett cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8); 297210311Sjmallett if (__predict_false(!segment_ptr.s.i)) 298210311Sjmallett cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128)); 299210311Sjmallett segment_ptr = next_ptr; 300210311Sjmallett } 301210311Sjmallett cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1)); 302210311Sjmallett 303210311Sjmallett return 0; 304210311Sjmallett} 305210311Sjmallett 306210311Sjmallett 307210311Sjmallett/** 308210311Sjmallett * Module/ driver initialization. Creates the linux network 309210311Sjmallett * devices. 310210311Sjmallett * 311210311Sjmallett * @return Zero on success 312210311Sjmallett */ 313210311Sjmallettint cvm_oct_init_module(device_t bus) 314210311Sjmallett{ 315210311Sjmallett device_t dev; 316210311Sjmallett int ifnum; 317210311Sjmallett int num_interfaces; 318210311Sjmallett int interface; 319210311Sjmallett int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; 320210311Sjmallett int qos; 321210311Sjmallett 322210311Sjmallett printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING); 323210311Sjmallett 324219706Sjmallett /* 325219706Sjmallett * MAC addresses for this driver start after the management 326219706Sjmallett * ports. 327219706Sjmallett * 328219706Sjmallett * XXX Would be nice if __cvmx_mgmt_port_num_ports() were 329219706Sjmallett * not static to cvmx-mgmt-port.c. 330219706Sjmallett */ 331219706Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN56XX)) 332219706Sjmallett cvm_oct_mac_addr_offset = 1; 333219706Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN63XX)) 334219706Sjmallett cvm_oct_mac_addr_offset = 2; 335219706Sjmallett else 336219706Sjmallett cvm_oct_mac_addr_offset = 0; 337219706Sjmallett 338210311Sjmallett cvm_oct_rx_initialize(); 339210311Sjmallett cvm_oct_configure_common_hw(bus); 340210311Sjmallett 341210311Sjmallett cvmx_helper_initialize_packet_io_global(); 342210311Sjmallett 343210311Sjmallett /* Change the input group for all ports before input is enabled */ 344210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 345210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 346210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 347210311Sjmallett int port; 348210311Sjmallett 349219694Sjmallett for (port = 0; port < num_ports; port++) { 350210311Sjmallett cvmx_pip_prt_tagx_t pip_prt_tagx; 351219694Sjmallett int pkind = cvmx_helper_get_ipd_port(interface, port); 352219694Sjmallett 353219694Sjmallett pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pkind)); 354210311Sjmallett pip_prt_tagx.s.grp = pow_receive_group; 355219694Sjmallett cvmx_write_csr(CVMX_PIP_PRT_TAGX(pkind), pip_prt_tagx.u64); 356210311Sjmallett } 357210311Sjmallett } 358210311Sjmallett 359210311Sjmallett cvmx_helper_ipd_and_packet_input_enable(); 360210311Sjmallett 361210311Sjmallett memset(cvm_oct_device, 0, sizeof(cvm_oct_device)); 362210311Sjmallett 363213150Sjmallett cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT, 364213150Sjmallett taskqueue_thread_enqueue, &cvm_oct_link_taskq); 365213150Sjmallett taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET, 366213150Sjmallett "octe link taskq"); 367213150Sjmallett 368210311Sjmallett /* Initialize the FAU used for counting packet buffers that need to be freed */ 369210311Sjmallett cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); 370210311Sjmallett 371210311Sjmallett ifnum = 0; 372210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 373210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 374210311Sjmallett cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface); 375210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 376210311Sjmallett int port; 377210311Sjmallett 378210311Sjmallett for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) { 379210311Sjmallett cvm_oct_private_t *priv; 380210311Sjmallett struct ifnet *ifp; 381210311Sjmallett 382210311Sjmallett dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++); 383210311Sjmallett if (dev != NULL) 384210311Sjmallett ifp = if_alloc(IFT_ETHER); 385210311Sjmallett if (dev == NULL || ifp == NULL) { 386210311Sjmallett printf("\t\tFailed to allocate ethernet device for port %d\n", port); 387210311Sjmallett continue; 388210311Sjmallett } 389210311Sjmallett 390210311Sjmallett /* Initialize the device private structure. */ 391210311Sjmallett device_probe(dev); 392210311Sjmallett priv = device_get_softc(dev); 393210311Sjmallett priv->dev = dev; 394210311Sjmallett priv->ifp = ifp; 395210311Sjmallett priv->imode = imode; 396210311Sjmallett priv->port = port; 397210311Sjmallett priv->queue = cvmx_pko_get_base_queue(priv->port); 398210311Sjmallett priv->fau = fau - cvmx_pko_get_num_queues(port) * 4; 399210311Sjmallett for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++) 400210311Sjmallett cvmx_fau_atomic_write32(priv->fau+qos*4, 0); 401213150Sjmallett TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv); 402210311Sjmallett 403210311Sjmallett switch (priv->imode) { 404210311Sjmallett 405210311Sjmallett /* These types don't support ports to IPD/PKO */ 406210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_DISABLED: 407210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PCIE: 408210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PICMG: 409210311Sjmallett break; 410210311Sjmallett 411210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_NPI: 412210311Sjmallett priv->init = cvm_oct_common_init; 413210311Sjmallett priv->uninit = cvm_oct_common_uninit; 414210311Sjmallett device_set_desc(dev, "Cavium Octeon NPI Ethernet"); 415210311Sjmallett break; 416210311Sjmallett 417210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_XAUI: 418210311Sjmallett priv->init = cvm_oct_xaui_init; 419215974Sjmallett priv->uninit = cvm_oct_common_uninit; 420210311Sjmallett device_set_desc(dev, "Cavium Octeon XAUI Ethernet"); 421210311Sjmallett break; 422210311Sjmallett 423210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_LOOP: 424210311Sjmallett priv->init = cvm_oct_common_init; 425210311Sjmallett priv->uninit = cvm_oct_common_uninit; 426210311Sjmallett device_set_desc(dev, "Cavium Octeon LOOP Ethernet"); 427210311Sjmallett break; 428210311Sjmallett 429210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SGMII: 430210311Sjmallett priv->init = cvm_oct_sgmii_init; 431215974Sjmallett priv->uninit = cvm_oct_common_uninit; 432210311Sjmallett device_set_desc(dev, "Cavium Octeon SGMII Ethernet"); 433210311Sjmallett break; 434210311Sjmallett 435210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SPI: 436210311Sjmallett priv->init = cvm_oct_spi_init; 437210311Sjmallett priv->uninit = cvm_oct_spi_uninit; 438210311Sjmallett device_set_desc(dev, "Cavium Octeon SPI Ethernet"); 439210311Sjmallett break; 440210311Sjmallett 441210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_RGMII: 442210311Sjmallett priv->init = cvm_oct_rgmii_init; 443210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 444210311Sjmallett device_set_desc(dev, "Cavium Octeon RGMII Ethernet"); 445210311Sjmallett break; 446210311Sjmallett 447210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_GMII: 448210311Sjmallett priv->init = cvm_oct_rgmii_init; 449210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 450210311Sjmallett device_set_desc(dev, "Cavium Octeon GMII Ethernet"); 451210311Sjmallett break; 452210311Sjmallett } 453210311Sjmallett 454210311Sjmallett ifp->if_softc = priv; 455210311Sjmallett 456210311Sjmallett if (!priv->init) { 457210311Sjmallett panic("%s: unsupported device type, need to free ifp.", __func__); 458210311Sjmallett } else 459210311Sjmallett if (priv->init(ifp) < 0) { 460210311Sjmallett printf("\t\tFailed to register ethernet device for interface %d, port %d\n", 461210311Sjmallett interface, priv->port); 462210311Sjmallett panic("%s: init failed, need to free ifp.", __func__); 463210311Sjmallett } else { 464210311Sjmallett cvm_oct_device[priv->port] = ifp; 465210311Sjmallett fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t); 466210311Sjmallett } 467210311Sjmallett } 468210311Sjmallett } 469210311Sjmallett 470210311Sjmallett if (INTERRUPT_LIMIT) { 471210311Sjmallett /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */ 472226024Smarcel cvmx_write_csr(CVMX_POW_WQ_INT_PC, cvmx_clock_get_rate(CVMX_CLOCK_CORE)/(INTERRUPT_LIMIT*16*256)<<8); 473210311Sjmallett 474210311Sjmallett /* Enable POW timer interrupt. It will count when there are packets available */ 475210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24); 476210311Sjmallett } else { 477210311Sjmallett /* Enable POW interrupt when our port has at least one packet */ 478210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001); 479210311Sjmallett } 480210311Sjmallett 481210311Sjmallett callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE); 482210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 483210311Sjmallett 484210311Sjmallett return 0; 485210311Sjmallett} 486210311Sjmallett 487210311Sjmallett 488210311Sjmallett/** 489210311Sjmallett * Module / driver shutdown 490210311Sjmallett * 491210311Sjmallett * @return Zero on success 492210311Sjmallett */ 493219695Sjmallettvoid cvm_oct_cleanup_module(device_t bus) 494210311Sjmallett{ 495210311Sjmallett int port; 496219695Sjmallett struct octebus_softc *sc = device_get_softc(bus); 497210311Sjmallett 498210311Sjmallett /* Disable POW interrupt */ 499210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0); 500210311Sjmallett 501210311Sjmallett /* Free the interrupt handler */ 502219695Sjmallett bus_teardown_intr(bus, sc->sc_rx_irq, sc->sc_rx_intr_cookie); 503210311Sjmallett 504210311Sjmallett callout_stop(&cvm_oct_poll_timer); 505210311Sjmallett cvm_oct_rx_shutdown(); 506210311Sjmallett 507215990Sjmallett cvmx_helper_shutdown_packet_io_global(); 508215990Sjmallett 509210311Sjmallett /* Free the ethernet devices */ 510210311Sjmallett for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { 511210311Sjmallett if (cvm_oct_device[port]) { 512210311Sjmallett cvm_oct_tx_shutdown(cvm_oct_device[port]); 513210311Sjmallett#if 0 514210311Sjmallett unregister_netdev(cvm_oct_device[port]); 515210311Sjmallett kfree(cvm_oct_device[port]); 516210311Sjmallett#else 517210311Sjmallett panic("%s: need to detach and free interface.", __func__); 518210311Sjmallett#endif 519210311Sjmallett cvm_oct_device[port] = NULL; 520210311Sjmallett } 521210311Sjmallett } 522219694Sjmallett /* Free the HW pools */ 523219694Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); 524219694Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); 525219694Sjmallett 526219694Sjmallett if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) 527219694Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, cvm_oct_num_output_buffers); 528219694Sjmallett 529219694Sjmallett /* Disable FPA, all buffers are free, not done by helper shutdown. */ 530219694Sjmallett cvmx_fpa_disable(); 531210311Sjmallett} 532