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$"); 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> 47257324Sglebius#include <net/if_var.h> 48210311Sjmallett#include <net/if_types.h> 49210311Sjmallett 50210311Sjmallett#include "wrapper-cvmx-includes.h" 51210311Sjmallett#include "ethernet-headers.h" 52210311Sjmallett 53210311Sjmallett#include "octebusvar.h" 54210311Sjmallett 55210311Sjmallett/* 56210311Sjmallett * XXX/juli 57210311Sjmallett * Convert 0444 to tunables, 0644 to sysctls. 58210311Sjmallett */ 59210311Sjmallett#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS 60210311Sjmallettint num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS; 61210311Sjmallett#else 62210311Sjmallettint num_packet_buffers = 1024; 63210311Sjmallett#endif 64210311SjmallettTUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers); 65210311Sjmallett/* 66210311Sjmallett "\t\tNumber of packet buffers to allocate and store in the\n" 67210311Sjmallett "\t\tFPA. By default, 1024 packet buffers are used unless\n" 68210311Sjmallett "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */ 69210311Sjmallett 70210311Sjmallettint pow_receive_group = 15; 71210311SjmallettTUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group); 72210311Sjmallett/* 73210311Sjmallett "\t\tPOW group to receive packets from. All ethernet hardware\n" 74210311Sjmallett "\t\twill be configured to send incomming packets to this POW\n" 75210311Sjmallett "\t\tgroup. Also any other software can submit packets to this\n" 76210311Sjmallett "\t\tgroup for the kernel to process." */ 77210311Sjmallett 78210311Sjmallett/** 79210311Sjmallett * Periodic timer to check auto negotiation 80210311Sjmallett */ 81210311Sjmallettstatic struct callout cvm_oct_poll_timer; 82210311Sjmallett 83210311Sjmallett/** 84210311Sjmallett * Array of every ethernet device owned by this driver indexed by 85210311Sjmallett * the ipd input port number. 86210311Sjmallett */ 87210311Sjmallettstruct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; 88210311Sjmallett 89213150Sjmallett/** 90213150Sjmallett * Task to handle link status changes. 91213150Sjmallett */ 92213150Sjmallettstatic struct taskqueue *cvm_oct_link_taskq; 93210311Sjmallett 94219694Sjmallett/* 95219694Sjmallett * Number of buffers in output buffer pool. 96219694Sjmallett */ 97219694Sjmallettstatic int cvm_oct_num_output_buffers; 98219694Sjmallett 99210311Sjmallett/** 100213150Sjmallett * Function to update link status. 101213150Sjmallett */ 102213150Sjmallettstatic void cvm_oct_update_link(void *context, int pending) 103213150Sjmallett{ 104213150Sjmallett cvm_oct_private_t *priv = (cvm_oct_private_t *)context; 105213150Sjmallett struct ifnet *ifp = priv->ifp; 106213150Sjmallett cvmx_helper_link_info_t link_info; 107213150Sjmallett 108213150Sjmallett link_info.u64 = priv->link_info; 109213150Sjmallett 110213150Sjmallett if (link_info.s.link_up) { 111213150Sjmallett if_link_state_change(ifp, LINK_STATE_UP); 112215959Sjmallett DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n", 113215959Sjmallett if_name(ifp), link_info.s.speed, 114215959Sjmallett (link_info.s.full_duplex) ? "Full" : "Half", 115215959Sjmallett priv->port, priv->queue); 116213150Sjmallett } else { 117213150Sjmallett if_link_state_change(ifp, LINK_STATE_DOWN); 118213150Sjmallett DEBUGPRINT("%s: Link down\n", if_name(ifp)); 119213150Sjmallett } 120213150Sjmallett priv->need_link_update = 0; 121213150Sjmallett} 122213150Sjmallett 123213150Sjmallett/** 124210311Sjmallett * Periodic timer tick for slow management operations 125210311Sjmallett * 126210311Sjmallett * @param arg Device to check 127210311Sjmallett */ 128210311Sjmallettstatic void cvm_do_timer(void *arg) 129210311Sjmallett{ 130210311Sjmallett static int port; 131213807Sjmallett static int updated; 132210311Sjmallett if (port < CVMX_PIP_NUM_INPUT_PORTS) { 133210311Sjmallett if (cvm_oct_device[port]) { 134210311Sjmallett int queues_per_port; 135210311Sjmallett int qos; 136210311Sjmallett cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc; 137213150Sjmallett 138216071Sjmallett cvm_oct_common_poll(priv->ifp); 139216071Sjmallett if (priv->need_link_update) { 140216071Sjmallett updated++; 141216071Sjmallett taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task); 142210311Sjmallett } 143210311Sjmallett 144210311Sjmallett queues_per_port = cvmx_pko_get_num_queues(port); 145210311Sjmallett /* Drain any pending packets in the free list */ 146210311Sjmallett for (qos = 0; qos < queues_per_port; qos++) { 147210311Sjmallett if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) { 148210311Sjmallett IF_LOCK(&priv->tx_free_queue[qos]); 149210311Sjmallett while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) { 150210311Sjmallett struct mbuf *m; 151210311Sjmallett 152210311Sjmallett _IF_DEQUEUE(&priv->tx_free_queue[qos], m); 153210311Sjmallett m_freem(m); 154210311Sjmallett } 155210311Sjmallett IF_UNLOCK(&priv->tx_free_queue[qos]); 156210311Sjmallett 157210311Sjmallett /* 158210311Sjmallett * XXX locking! 159210311Sjmallett */ 160210311Sjmallett priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 161210311Sjmallett } 162210311Sjmallett } 163210311Sjmallett } 164210311Sjmallett port++; 165210311Sjmallett /* Poll the next port in a 50th of a second. 166210311Sjmallett This spreads the polling of ports out a little bit */ 167210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 168210311Sjmallett } else { 169210311Sjmallett port = 0; 170213807Sjmallett /* If any updates were made in this run, continue iterating at 171213807Sjmallett * 1/50th of a second, so that if a link has merely gone down 172213807Sjmallett * temporarily (e.g. because of interface reinitialization) it 173213807Sjmallett * will not be forced to stay down for an entire second. 174213807Sjmallett */ 175213807Sjmallett if (updated > 0) { 176213807Sjmallett updated = 0; 177213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 178213807Sjmallett } else { 179213807Sjmallett /* All ports have been polled. Start the next iteration through 180213807Sjmallett the ports in one second */ 181213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 182213807Sjmallett } 183210311Sjmallett } 184210311Sjmallett} 185210311Sjmallett 186210311Sjmallett/** 187210311Sjmallett * Configure common hardware for all interfaces 188210311Sjmallett */ 189210311Sjmallettstatic void cvm_oct_configure_common_hw(device_t bus) 190210311Sjmallett{ 191210311Sjmallett struct octebus_softc *sc; 192219694Sjmallett int pko_queues; 193210311Sjmallett int error; 194210311Sjmallett int rid; 195210311Sjmallett 196210311Sjmallett sc = device_get_softc(bus); 197210311Sjmallett 198210311Sjmallett /* Setup the FPA */ 199210311Sjmallett cvmx_fpa_enable(); 200219694Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, 201219694Sjmallett num_packet_buffers); 202219694Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, 203219694Sjmallett num_packet_buffers); 204219694Sjmallett if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) { 205219694Sjmallett /* 206219694Sjmallett * If the FPA uses different pools for output buffers and 207219694Sjmallett * packets, size the output buffer pool based on the number 208219694Sjmallett * of PKO queues. 209219694Sjmallett */ 210219694Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN38XX)) 211219694Sjmallett pko_queues = 128; 212219694Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) 213219694Sjmallett pko_queues = 32; 214219694Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN50XX)) 215219694Sjmallett pko_queues = 32; 216219694Sjmallett else 217219694Sjmallett pko_queues = 256; 218210311Sjmallett 219219694Sjmallett cvm_oct_num_output_buffers = 4 * pko_queues; 220219694Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, 221219694Sjmallett CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 222219694Sjmallett cvm_oct_num_output_buffers); 223219694Sjmallett } 224219694Sjmallett 225210311Sjmallett if (USE_RED) 226219694Sjmallett cvmx_helper_setup_red(num_packet_buffers/4, 227219694Sjmallett num_packet_buffers/8); 228210311Sjmallett 229210311Sjmallett /* Enable the MII interface */ 230242346Sjmallett if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) 231210311Sjmallett cvmx_write_csr(CVMX_SMI_EN, 1); 232210311Sjmallett 233210311Sjmallett /* Register an IRQ hander for to receive POW interrupts */ 234210311Sjmallett rid = 0; 235210311Sjmallett sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid, 236232812Sjmallett OCTEON_IRQ_WORKQ0 + pow_receive_group, 237232812Sjmallett OCTEON_IRQ_WORKQ0 + pow_receive_group, 238210311Sjmallett 1, RF_ACTIVE); 239210311Sjmallett if (sc->sc_rx_irq == NULL) { 240210311Sjmallett device_printf(bus, "could not allocate workq irq"); 241210311Sjmallett return; 242210311Sjmallett } 243210311Sjmallett 244210311Sjmallett error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE, 245210311Sjmallett cvm_oct_do_interrupt, NULL, cvm_oct_device, 246219695Sjmallett &sc->sc_rx_intr_cookie); 247210311Sjmallett if (error != 0) { 248210311Sjmallett device_printf(bus, "could not setup workq irq"); 249210311Sjmallett return; 250210311Sjmallett } 251210311Sjmallett 252210311Sjmallett 253210311Sjmallett#ifdef SMP 254217664Sjmallett { 255217664Sjmallett cvmx_ciu_intx0_t en; 256217664Sjmallett int core; 257217210Sjmallett 258217664Sjmallett CPU_FOREACH(core) { 259217664Sjmallett if (core == PCPU_GET(cpuid)) 260217664Sjmallett continue; 261217210Sjmallett 262217664Sjmallett en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2)); 263217664Sjmallett en.s.workq |= (1<<pow_receive_group); 264217664Sjmallett cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64); 265210311Sjmallett } 266210311Sjmallett } 267210311Sjmallett#endif 268210311Sjmallett} 269210311Sjmallett 270210311Sjmallett 271210311Sjmallett/** 272210311Sjmallett * Free a work queue entry received in a intercept callback. 273210311Sjmallett * 274210311Sjmallett * @param work_queue_entry 275210311Sjmallett * Work queue entry to free 276210311Sjmallett * @return Zero on success, Negative on failure. 277210311Sjmallett */ 278210311Sjmallettint cvm_oct_free_work(void *work_queue_entry) 279210311Sjmallett{ 280210311Sjmallett cvmx_wqe_t *work = work_queue_entry; 281210311Sjmallett 282210311Sjmallett int segments = work->word2.s.bufs; 283210311Sjmallett cvmx_buf_ptr_t segment_ptr = work->packet_ptr; 284210311Sjmallett 285210311Sjmallett while (segments--) { 286210311Sjmallett cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8); 287210311Sjmallett if (__predict_false(!segment_ptr.s.i)) 288210311Sjmallett cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128)); 289210311Sjmallett segment_ptr = next_ptr; 290210311Sjmallett } 291210311Sjmallett cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1)); 292210311Sjmallett 293210311Sjmallett return 0; 294210311Sjmallett} 295210311Sjmallett 296210311Sjmallett 297210311Sjmallett/** 298210311Sjmallett * Module/ driver initialization. Creates the linux network 299210311Sjmallett * devices. 300210311Sjmallett * 301210311Sjmallett * @return Zero on success 302210311Sjmallett */ 303210311Sjmallettint cvm_oct_init_module(device_t bus) 304210311Sjmallett{ 305210311Sjmallett device_t dev; 306210311Sjmallett int ifnum; 307210311Sjmallett int num_interfaces; 308210311Sjmallett int interface; 309210311Sjmallett int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; 310210311Sjmallett int qos; 311210311Sjmallett 312210311Sjmallett cvm_oct_rx_initialize(); 313210311Sjmallett cvm_oct_configure_common_hw(bus); 314210311Sjmallett 315210311Sjmallett cvmx_helper_initialize_packet_io_global(); 316210311Sjmallett 317210311Sjmallett /* Change the input group for all ports before input is enabled */ 318210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 319210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 320210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 321210311Sjmallett int port; 322210311Sjmallett 323219694Sjmallett for (port = 0; port < num_ports; port++) { 324210311Sjmallett cvmx_pip_prt_tagx_t pip_prt_tagx; 325219694Sjmallett int pkind = cvmx_helper_get_ipd_port(interface, port); 326219694Sjmallett 327219694Sjmallett pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pkind)); 328210311Sjmallett pip_prt_tagx.s.grp = pow_receive_group; 329219694Sjmallett cvmx_write_csr(CVMX_PIP_PRT_TAGX(pkind), pip_prt_tagx.u64); 330210311Sjmallett } 331210311Sjmallett } 332210311Sjmallett 333210311Sjmallett cvmx_helper_ipd_and_packet_input_enable(); 334210311Sjmallett 335210311Sjmallett memset(cvm_oct_device, 0, sizeof(cvm_oct_device)); 336210311Sjmallett 337213150Sjmallett cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT, 338213150Sjmallett taskqueue_thread_enqueue, &cvm_oct_link_taskq); 339213150Sjmallett taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET, 340213150Sjmallett "octe link taskq"); 341213150Sjmallett 342210311Sjmallett /* Initialize the FAU used for counting packet buffers that need to be freed */ 343210311Sjmallett cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); 344210311Sjmallett 345210311Sjmallett ifnum = 0; 346210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 347210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 348210311Sjmallett cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface); 349210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 350210311Sjmallett int port; 351210311Sjmallett 352231987Sgonzo for (port = cvmx_helper_get_ipd_port(interface, 0); 353231987Sgonzo port < cvmx_helper_get_ipd_port(interface, num_ports); 354231987Sgonzo ifnum++, port++) { 355210311Sjmallett cvm_oct_private_t *priv; 356210311Sjmallett struct ifnet *ifp; 357210311Sjmallett 358231987Sgonzo dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum); 359210311Sjmallett if (dev != NULL) 360210311Sjmallett ifp = if_alloc(IFT_ETHER); 361210311Sjmallett if (dev == NULL || ifp == NULL) { 362231987Sgonzo printf("Failed to allocate ethernet device for interface %d port %d\n", interface, port); 363210311Sjmallett continue; 364210311Sjmallett } 365210311Sjmallett 366210311Sjmallett /* Initialize the device private structure. */ 367210311Sjmallett device_probe(dev); 368210311Sjmallett priv = device_get_softc(dev); 369210311Sjmallett priv->dev = dev; 370210311Sjmallett priv->ifp = ifp; 371210311Sjmallett priv->imode = imode; 372210311Sjmallett priv->port = port; 373210311Sjmallett priv->queue = cvmx_pko_get_base_queue(priv->port); 374210311Sjmallett priv->fau = fau - cvmx_pko_get_num_queues(port) * 4; 375210311Sjmallett for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++) 376210311Sjmallett cvmx_fau_atomic_write32(priv->fau+qos*4, 0); 377213150Sjmallett TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv); 378210311Sjmallett 379210311Sjmallett switch (priv->imode) { 380210311Sjmallett 381210311Sjmallett /* These types don't support ports to IPD/PKO */ 382210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_DISABLED: 383210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PCIE: 384210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PICMG: 385210311Sjmallett break; 386210311Sjmallett 387210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_NPI: 388210311Sjmallett priv->init = cvm_oct_common_init; 389210311Sjmallett priv->uninit = cvm_oct_common_uninit; 390210311Sjmallett device_set_desc(dev, "Cavium Octeon NPI Ethernet"); 391210311Sjmallett break; 392210311Sjmallett 393210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_XAUI: 394210311Sjmallett priv->init = cvm_oct_xaui_init; 395215974Sjmallett priv->uninit = cvm_oct_common_uninit; 396210311Sjmallett device_set_desc(dev, "Cavium Octeon XAUI Ethernet"); 397210311Sjmallett break; 398210311Sjmallett 399210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_LOOP: 400210311Sjmallett priv->init = cvm_oct_common_init; 401210311Sjmallett priv->uninit = cvm_oct_common_uninit; 402210311Sjmallett device_set_desc(dev, "Cavium Octeon LOOP Ethernet"); 403210311Sjmallett break; 404210311Sjmallett 405210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SGMII: 406210311Sjmallett priv->init = cvm_oct_sgmii_init; 407215974Sjmallett priv->uninit = cvm_oct_common_uninit; 408210311Sjmallett device_set_desc(dev, "Cavium Octeon SGMII Ethernet"); 409210311Sjmallett break; 410210311Sjmallett 411210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SPI: 412210311Sjmallett priv->init = cvm_oct_spi_init; 413210311Sjmallett priv->uninit = cvm_oct_spi_uninit; 414210311Sjmallett device_set_desc(dev, "Cavium Octeon SPI Ethernet"); 415210311Sjmallett break; 416210311Sjmallett 417210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_RGMII: 418210311Sjmallett priv->init = cvm_oct_rgmii_init; 419210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 420210311Sjmallett device_set_desc(dev, "Cavium Octeon RGMII Ethernet"); 421210311Sjmallett break; 422210311Sjmallett 423210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_GMII: 424210311Sjmallett priv->init = cvm_oct_rgmii_init; 425210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 426210311Sjmallett device_set_desc(dev, "Cavium Octeon GMII Ethernet"); 427210311Sjmallett break; 428210311Sjmallett } 429210311Sjmallett 430210311Sjmallett ifp->if_softc = priv; 431210311Sjmallett 432210311Sjmallett if (!priv->init) { 433231987Sgonzo printf("octe%d: unsupported device type interface %d, port %d\n", 434231987Sgonzo ifnum, interface, priv->port); 435231987Sgonzo if_free(ifp); 436231987Sgonzo } else if (priv->init(ifp) != 0) { 437231987Sgonzo printf("octe%d: failed to register device for interface %d, port %d\n", 438231987Sgonzo ifnum, interface, priv->port); 439231987Sgonzo if_free(ifp); 440210311Sjmallett } else { 441210311Sjmallett cvm_oct_device[priv->port] = ifp; 442210311Sjmallett fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t); 443210311Sjmallett } 444210311Sjmallett } 445210311Sjmallett } 446210311Sjmallett 447210311Sjmallett if (INTERRUPT_LIMIT) { 448210311Sjmallett /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */ 449226024Smarcel cvmx_write_csr(CVMX_POW_WQ_INT_PC, cvmx_clock_get_rate(CVMX_CLOCK_CORE)/(INTERRUPT_LIMIT*16*256)<<8); 450210311Sjmallett 451210311Sjmallett /* Enable POW timer interrupt. It will count when there are packets available */ 452210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24); 453210311Sjmallett } else { 454210311Sjmallett /* Enable POW interrupt when our port has at least one packet */ 455210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001); 456210311Sjmallett } 457210311Sjmallett 458283291Sjkim callout_init(&cvm_oct_poll_timer, 1); 459210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 460210311Sjmallett 461210311Sjmallett return 0; 462210311Sjmallett} 463210311Sjmallett 464210311Sjmallett 465210311Sjmallett/** 466210311Sjmallett * Module / driver shutdown 467210311Sjmallett * 468210311Sjmallett * @return Zero on success 469210311Sjmallett */ 470219695Sjmallettvoid cvm_oct_cleanup_module(device_t bus) 471210311Sjmallett{ 472210311Sjmallett int port; 473219695Sjmallett struct octebus_softc *sc = device_get_softc(bus); 474210311Sjmallett 475210311Sjmallett /* Disable POW interrupt */ 476210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0); 477210311Sjmallett 478210311Sjmallett /* Free the interrupt handler */ 479219695Sjmallett bus_teardown_intr(bus, sc->sc_rx_irq, sc->sc_rx_intr_cookie); 480210311Sjmallett 481210311Sjmallett callout_stop(&cvm_oct_poll_timer); 482210311Sjmallett cvm_oct_rx_shutdown(); 483210311Sjmallett 484215990Sjmallett cvmx_helper_shutdown_packet_io_global(); 485215990Sjmallett 486210311Sjmallett /* Free the ethernet devices */ 487210311Sjmallett for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { 488210311Sjmallett if (cvm_oct_device[port]) { 489210311Sjmallett cvm_oct_tx_shutdown(cvm_oct_device[port]); 490210311Sjmallett#if 0 491210311Sjmallett unregister_netdev(cvm_oct_device[port]); 492210311Sjmallett kfree(cvm_oct_device[port]); 493210311Sjmallett#else 494210311Sjmallett panic("%s: need to detach and free interface.", __func__); 495210311Sjmallett#endif 496210311Sjmallett cvm_oct_device[port] = NULL; 497210311Sjmallett } 498210311Sjmallett } 499219694Sjmallett /* Free the HW pools */ 500219694Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); 501219694Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); 502219694Sjmallett 503219694Sjmallett if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) 504219694Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, cvm_oct_num_output_buffers); 505219694Sjmallett 506219694Sjmallett /* Disable FPA, all buffers are free, not done by helper shutdown. */ 507219694Sjmallett cvmx_fpa_disable(); 508210311Sjmallett} 509