ethernet.c revision 217210
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 217210 2011-01-09 23:05:46Z jmallett $"); 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 * Exported from the kernel so we can determine board information. It is 81210311Sjmallett * passed by the bootloader to the kernel. 82210311Sjmallett */ 83210311Sjmallettextern cvmx_bootinfo_t *octeon_bootinfo; 84210311Sjmallett 85210311Sjmallett/** 86210311Sjmallett * Periodic timer to check auto negotiation 87210311Sjmallett */ 88210311Sjmallettstatic struct callout cvm_oct_poll_timer; 89210311Sjmallett 90210311Sjmallett/** 91210311Sjmallett * Array of every ethernet device owned by this driver indexed by 92210311Sjmallett * the ipd input port number. 93210311Sjmallett */ 94210311Sjmallettstruct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; 95210311Sjmallett 96213150Sjmallett/** 97213150Sjmallett * Task to handle link status changes. 98213150Sjmallett */ 99213150Sjmallettstatic struct taskqueue *cvm_oct_link_taskq; 100210311Sjmallett 101210311Sjmallett/** 102213150Sjmallett * Function to update link status. 103213150Sjmallett */ 104213150Sjmallettstatic void cvm_oct_update_link(void *context, int pending) 105213150Sjmallett{ 106213150Sjmallett cvm_oct_private_t *priv = (cvm_oct_private_t *)context; 107213150Sjmallett struct ifnet *ifp = priv->ifp; 108213150Sjmallett cvmx_helper_link_info_t link_info; 109213150Sjmallett 110213150Sjmallett link_info.u64 = priv->link_info; 111213150Sjmallett 112213150Sjmallett if (link_info.s.link_up) { 113213150Sjmallett if_link_state_change(ifp, LINK_STATE_UP); 114215959Sjmallett DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n", 115215959Sjmallett if_name(ifp), link_info.s.speed, 116215959Sjmallett (link_info.s.full_duplex) ? "Full" : "Half", 117215959Sjmallett priv->port, priv->queue); 118213150Sjmallett } else { 119213150Sjmallett if_link_state_change(ifp, LINK_STATE_DOWN); 120213150Sjmallett DEBUGPRINT("%s: Link down\n", if_name(ifp)); 121213150Sjmallett } 122213150Sjmallett priv->need_link_update = 0; 123213150Sjmallett} 124213150Sjmallett 125213150Sjmallett/** 126210311Sjmallett * Periodic timer tick for slow management operations 127210311Sjmallett * 128210311Sjmallett * @param arg Device to check 129210311Sjmallett */ 130210311Sjmallettstatic void cvm_do_timer(void *arg) 131210311Sjmallett{ 132210311Sjmallett static int port; 133213807Sjmallett static int updated; 134210311Sjmallett if (port < CVMX_PIP_NUM_INPUT_PORTS) { 135210311Sjmallett if (cvm_oct_device[port]) { 136210311Sjmallett int queues_per_port; 137210311Sjmallett int qos; 138210311Sjmallett cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc; 139213150Sjmallett 140216071Sjmallett cvm_oct_common_poll(priv->ifp); 141216071Sjmallett if (priv->need_link_update) { 142216071Sjmallett updated++; 143216071Sjmallett taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task); 144210311Sjmallett } 145210311Sjmallett 146210311Sjmallett queues_per_port = cvmx_pko_get_num_queues(port); 147210311Sjmallett /* Drain any pending packets in the free list */ 148210311Sjmallett for (qos = 0; qos < queues_per_port; qos++) { 149210311Sjmallett if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) { 150210311Sjmallett IF_LOCK(&priv->tx_free_queue[qos]); 151210311Sjmallett while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) { 152210311Sjmallett struct mbuf *m; 153210311Sjmallett 154210311Sjmallett _IF_DEQUEUE(&priv->tx_free_queue[qos], m); 155210311Sjmallett m_freem(m); 156210311Sjmallett } 157210311Sjmallett IF_UNLOCK(&priv->tx_free_queue[qos]); 158210311Sjmallett 159210311Sjmallett /* 160210311Sjmallett * XXX locking! 161210311Sjmallett */ 162210311Sjmallett priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 163210311Sjmallett } 164210311Sjmallett } 165210311Sjmallett } 166210311Sjmallett port++; 167210311Sjmallett /* Poll the next port in a 50th of a second. 168210311Sjmallett This spreads the polling of ports out a little bit */ 169210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 170210311Sjmallett } else { 171210311Sjmallett port = 0; 172213807Sjmallett /* If any updates were made in this run, continue iterating at 173213807Sjmallett * 1/50th of a second, so that if a link has merely gone down 174213807Sjmallett * temporarily (e.g. because of interface reinitialization) it 175213807Sjmallett * will not be forced to stay down for an entire second. 176213807Sjmallett */ 177213807Sjmallett if (updated > 0) { 178213807Sjmallett updated = 0; 179213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 180213807Sjmallett } else { 181213807Sjmallett /* All ports have been polled. Start the next iteration through 182213807Sjmallett the ports in one second */ 183213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 184213807Sjmallett } 185210311Sjmallett } 186210311Sjmallett} 187210311Sjmallett 188210311Sjmallett 189210311Sjmallett/** 190210311Sjmallett * Configure common hardware for all interfaces 191210311Sjmallett */ 192210311Sjmallettstatic void cvm_oct_configure_common_hw(device_t bus) 193210311Sjmallett{ 194210311Sjmallett struct octebus_softc *sc; 195210311Sjmallett int error; 196210311Sjmallett int rid; 197210311Sjmallett 198210311Sjmallett sc = device_get_softc(bus); 199210311Sjmallett 200210311Sjmallett /* Setup the FPA */ 201210311Sjmallett cvmx_fpa_enable(); 202210311Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); 203210311Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); 204210311Sjmallett if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) 205210311Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); 206210311Sjmallett 207210311Sjmallett if (USE_RED) 208210311Sjmallett cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8); 209210311Sjmallett 210210311Sjmallett /* Enable the MII interface */ 211210311Sjmallett if (!octeon_is_simulation()) 212210311Sjmallett cvmx_write_csr(CVMX_SMI_EN, 1); 213210311Sjmallett 214210311Sjmallett /* Register an IRQ hander for to receive POW interrupts */ 215210311Sjmallett rid = 0; 216210311Sjmallett sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid, 217210311Sjmallett CVMX_IRQ_WORKQ0 + pow_receive_group, 218210311Sjmallett CVMX_IRQ_WORKQ0 + pow_receive_group, 219210311Sjmallett 1, RF_ACTIVE); 220210311Sjmallett if (sc->sc_rx_irq == NULL) { 221210311Sjmallett device_printf(bus, "could not allocate workq irq"); 222210311Sjmallett return; 223210311Sjmallett } 224210311Sjmallett 225210311Sjmallett error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE, 226210311Sjmallett cvm_oct_do_interrupt, NULL, cvm_oct_device, 227210311Sjmallett NULL); 228210311Sjmallett if (error != 0) { 229210311Sjmallett device_printf(bus, "could not setup workq irq"); 230210311Sjmallett return; 231210311Sjmallett } 232210311Sjmallett 233210311Sjmallett 234210311Sjmallett#ifdef SMP 235210311Sjmallett if (USE_MULTICORE_RECEIVE) { 236210311Sjmallett critical_enter(); 237210311Sjmallett { 238217210Sjmallett cvmx_ciu_intx0_t en; 239217210Sjmallett int core; 240217210Sjmallett 241217210Sjmallett CPU_FOREACH(core) { 242217210Sjmallett if (core == PCPU_GET(cpuid)) 243217210Sjmallett continue; 244217210Sjmallett 245217210Sjmallett en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2)); 246217210Sjmallett en.s.workq |= (1<<pow_receive_group); 247217210Sjmallett cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64); 248210311Sjmallett } 249210311Sjmallett } 250210311Sjmallett critical_exit(); 251210311Sjmallett } 252210311Sjmallett#endif 253210311Sjmallett} 254210311Sjmallett 255210311Sjmallett 256210311Sjmallett/** 257210311Sjmallett * Free a work queue entry received in a intercept callback. 258210311Sjmallett * 259210311Sjmallett * @param work_queue_entry 260210311Sjmallett * Work queue entry to free 261210311Sjmallett * @return Zero on success, Negative on failure. 262210311Sjmallett */ 263210311Sjmallettint cvm_oct_free_work(void *work_queue_entry) 264210311Sjmallett{ 265210311Sjmallett cvmx_wqe_t *work = work_queue_entry; 266210311Sjmallett 267210311Sjmallett int segments = work->word2.s.bufs; 268210311Sjmallett cvmx_buf_ptr_t segment_ptr = work->packet_ptr; 269210311Sjmallett 270210311Sjmallett while (segments--) { 271210311Sjmallett cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8); 272210311Sjmallett if (__predict_false(!segment_ptr.s.i)) 273210311Sjmallett cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128)); 274210311Sjmallett segment_ptr = next_ptr; 275210311Sjmallett } 276210311Sjmallett cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1)); 277210311Sjmallett 278210311Sjmallett return 0; 279210311Sjmallett} 280210311Sjmallett 281210311Sjmallett 282210311Sjmallett/** 283210311Sjmallett * Module/ driver initialization. Creates the linux network 284210311Sjmallett * devices. 285210311Sjmallett * 286210311Sjmallett * @return Zero on success 287210311Sjmallett */ 288210311Sjmallettint cvm_oct_init_module(device_t bus) 289210311Sjmallett{ 290210311Sjmallett device_t dev; 291210311Sjmallett int ifnum; 292210311Sjmallett int num_interfaces; 293210311Sjmallett int interface; 294210311Sjmallett int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; 295210311Sjmallett int qos; 296210311Sjmallett 297210311Sjmallett printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING); 298210311Sjmallett 299210311Sjmallett cvm_oct_rx_initialize(); 300210311Sjmallett cvm_oct_configure_common_hw(bus); 301210311Sjmallett 302210311Sjmallett cvmx_helper_initialize_packet_io_global(); 303210311Sjmallett 304210311Sjmallett /* Change the input group for all ports before input is enabled */ 305210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 306210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 307210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 308210311Sjmallett int port; 309210311Sjmallett 310210311Sjmallett for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) { 311210311Sjmallett cvmx_pip_prt_tagx_t pip_prt_tagx; 312210311Sjmallett pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port)); 313210311Sjmallett pip_prt_tagx.s.grp = pow_receive_group; 314210311Sjmallett cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64); 315210311Sjmallett } 316210311Sjmallett } 317210311Sjmallett 318210311Sjmallett cvmx_helper_ipd_and_packet_input_enable(); 319210311Sjmallett 320210311Sjmallett memset(cvm_oct_device, 0, sizeof(cvm_oct_device)); 321210311Sjmallett 322213150Sjmallett cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT, 323213150Sjmallett taskqueue_thread_enqueue, &cvm_oct_link_taskq); 324213150Sjmallett taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET, 325213150Sjmallett "octe link taskq"); 326213150Sjmallett 327210311Sjmallett /* Initialize the FAU used for counting packet buffers that need to be freed */ 328210311Sjmallett cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); 329210311Sjmallett 330210311Sjmallett ifnum = 0; 331210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 332210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 333210311Sjmallett cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface); 334210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 335210311Sjmallett int port; 336210311Sjmallett 337210311Sjmallett for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) { 338210311Sjmallett cvm_oct_private_t *priv; 339210311Sjmallett struct ifnet *ifp; 340210311Sjmallett 341210311Sjmallett dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++); 342210311Sjmallett if (dev != NULL) 343210311Sjmallett ifp = if_alloc(IFT_ETHER); 344210311Sjmallett if (dev == NULL || ifp == NULL) { 345210311Sjmallett printf("\t\tFailed to allocate ethernet device for port %d\n", port); 346210311Sjmallett continue; 347210311Sjmallett } 348210311Sjmallett 349210311Sjmallett /* Initialize the device private structure. */ 350210311Sjmallett device_probe(dev); 351210311Sjmallett priv = device_get_softc(dev); 352210311Sjmallett priv->dev = dev; 353210311Sjmallett priv->ifp = ifp; 354210311Sjmallett priv->imode = imode; 355210311Sjmallett priv->port = port; 356210311Sjmallett priv->queue = cvmx_pko_get_base_queue(priv->port); 357210311Sjmallett priv->fau = fau - cvmx_pko_get_num_queues(port) * 4; 358210311Sjmallett for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++) 359210311Sjmallett cvmx_fau_atomic_write32(priv->fau+qos*4, 0); 360213150Sjmallett TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv); 361210311Sjmallett 362210311Sjmallett switch (priv->imode) { 363210311Sjmallett 364210311Sjmallett /* These types don't support ports to IPD/PKO */ 365210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_DISABLED: 366210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PCIE: 367210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PICMG: 368210311Sjmallett break; 369210311Sjmallett 370210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_NPI: 371210311Sjmallett priv->init = cvm_oct_common_init; 372210311Sjmallett priv->uninit = cvm_oct_common_uninit; 373210311Sjmallett device_set_desc(dev, "Cavium Octeon NPI Ethernet"); 374210311Sjmallett break; 375210311Sjmallett 376210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_XAUI: 377210311Sjmallett priv->init = cvm_oct_xaui_init; 378215974Sjmallett priv->uninit = cvm_oct_common_uninit; 379210311Sjmallett device_set_desc(dev, "Cavium Octeon XAUI Ethernet"); 380210311Sjmallett break; 381210311Sjmallett 382210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_LOOP: 383210311Sjmallett priv->init = cvm_oct_common_init; 384210311Sjmallett priv->uninit = cvm_oct_common_uninit; 385210311Sjmallett device_set_desc(dev, "Cavium Octeon LOOP Ethernet"); 386210311Sjmallett break; 387210311Sjmallett 388210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SGMII: 389210311Sjmallett priv->init = cvm_oct_sgmii_init; 390215974Sjmallett priv->uninit = cvm_oct_common_uninit; 391210311Sjmallett device_set_desc(dev, "Cavium Octeon SGMII Ethernet"); 392210311Sjmallett break; 393210311Sjmallett 394210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SPI: 395210311Sjmallett priv->init = cvm_oct_spi_init; 396210311Sjmallett priv->uninit = cvm_oct_spi_uninit; 397210311Sjmallett device_set_desc(dev, "Cavium Octeon SPI Ethernet"); 398210311Sjmallett break; 399210311Sjmallett 400210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_RGMII: 401210311Sjmallett priv->init = cvm_oct_rgmii_init; 402210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 403210311Sjmallett device_set_desc(dev, "Cavium Octeon RGMII Ethernet"); 404210311Sjmallett break; 405210311Sjmallett 406210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_GMII: 407210311Sjmallett priv->init = cvm_oct_rgmii_init; 408210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 409210311Sjmallett device_set_desc(dev, "Cavium Octeon GMII Ethernet"); 410210311Sjmallett break; 411210311Sjmallett } 412210311Sjmallett 413210311Sjmallett ifp->if_softc = priv; 414210311Sjmallett 415210311Sjmallett if (!priv->init) { 416210311Sjmallett panic("%s: unsupported device type, need to free ifp.", __func__); 417210311Sjmallett } else 418210311Sjmallett if (priv->init(ifp) < 0) { 419210311Sjmallett printf("\t\tFailed to register ethernet device for interface %d, port %d\n", 420210311Sjmallett interface, priv->port); 421210311Sjmallett panic("%s: init failed, need to free ifp.", __func__); 422210311Sjmallett } else { 423210311Sjmallett cvm_oct_device[priv->port] = ifp; 424210311Sjmallett fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t); 425210311Sjmallett } 426210311Sjmallett } 427210311Sjmallett } 428210311Sjmallett 429210311Sjmallett if (INTERRUPT_LIMIT) { 430210311Sjmallett /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */ 431210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8); 432210311Sjmallett 433210311Sjmallett /* Enable POW timer interrupt. It will count when there are packets available */ 434210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24); 435210311Sjmallett } else { 436210311Sjmallett /* Enable POW interrupt when our port has at least one packet */ 437210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001); 438210311Sjmallett } 439210311Sjmallett 440210311Sjmallett callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE); 441210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 442210311Sjmallett 443210311Sjmallett return 0; 444210311Sjmallett} 445210311Sjmallett 446210311Sjmallett 447210311Sjmallett/** 448210311Sjmallett * Module / driver shutdown 449210311Sjmallett * 450210311Sjmallett * @return Zero on success 451210311Sjmallett */ 452210311Sjmallettvoid cvm_oct_cleanup_module(void) 453210311Sjmallett{ 454210311Sjmallett int port; 455210311Sjmallett 456210311Sjmallett /* Disable POW interrupt */ 457210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0); 458210311Sjmallett 459210311Sjmallett#if 0 460210311Sjmallett /* Free the interrupt handler */ 461210311Sjmallett free_irq(8 + pow_receive_group, cvm_oct_device); 462210311Sjmallett#endif 463210311Sjmallett 464210311Sjmallett callout_stop(&cvm_oct_poll_timer); 465210311Sjmallett cvm_oct_rx_shutdown(); 466210311Sjmallett 467215990Sjmallett cvmx_helper_shutdown_packet_io_global(); 468215990Sjmallett 469210311Sjmallett /* Free the ethernet devices */ 470210311Sjmallett for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { 471210311Sjmallett if (cvm_oct_device[port]) { 472210311Sjmallett cvm_oct_tx_shutdown(cvm_oct_device[port]); 473210311Sjmallett#if 0 474210311Sjmallett unregister_netdev(cvm_oct_device[port]); 475210311Sjmallett kfree(cvm_oct_device[port]); 476210311Sjmallett#else 477210311Sjmallett panic("%s: need to detach and free interface.", __func__); 478210311Sjmallett#endif 479210311Sjmallett cvm_oct_device[port] = NULL; 480210311Sjmallett } 481210311Sjmallett } 482210311Sjmallett} 483