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: stable/10/sys/mips/cavium/octe/ethernet.c 314667 2017-03-04 13:03:31Z avg $"); 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 77210311Sjmallett/** 78210311Sjmallett * Periodic timer to check auto negotiation 79210311Sjmallett */ 80210311Sjmallettstatic struct callout cvm_oct_poll_timer; 81210311Sjmallett 82210311Sjmallett/** 83210311Sjmallett * Array of every ethernet device owned by this driver indexed by 84210311Sjmallett * the ipd input port number. 85210311Sjmallett */ 86210311Sjmallettstruct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; 87210311Sjmallett 88213150Sjmallett/** 89213150Sjmallett * Task to handle link status changes. 90213150Sjmallett */ 91213150Sjmallettstatic struct taskqueue *cvm_oct_link_taskq; 92210311Sjmallett 93219694Sjmallett/* 94219694Sjmallett * Number of buffers in output buffer pool. 95219694Sjmallett */ 96219694Sjmallettstatic int cvm_oct_num_output_buffers; 97219694Sjmallett 98210311Sjmallett/** 99213150Sjmallett * Function to update link status. 100213150Sjmallett */ 101213150Sjmallettstatic void cvm_oct_update_link(void *context, int pending) 102213150Sjmallett{ 103213150Sjmallett cvm_oct_private_t *priv = (cvm_oct_private_t *)context; 104213150Sjmallett struct ifnet *ifp = priv->ifp; 105213150Sjmallett cvmx_helper_link_info_t link_info; 106213150Sjmallett 107213150Sjmallett link_info.u64 = priv->link_info; 108213150Sjmallett 109213150Sjmallett if (link_info.s.link_up) { 110213150Sjmallett if_link_state_change(ifp, LINK_STATE_UP); 111215959Sjmallett DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n", 112215959Sjmallett if_name(ifp), link_info.s.speed, 113215959Sjmallett (link_info.s.full_duplex) ? "Full" : "Half", 114215959Sjmallett priv->port, priv->queue); 115213150Sjmallett } else { 116213150Sjmallett if_link_state_change(ifp, LINK_STATE_DOWN); 117213150Sjmallett DEBUGPRINT("%s: Link down\n", if_name(ifp)); 118213150Sjmallett } 119213150Sjmallett priv->need_link_update = 0; 120213150Sjmallett} 121213150Sjmallett 122213150Sjmallett/** 123210311Sjmallett * Periodic timer tick for slow management operations 124210311Sjmallett * 125210311Sjmallett * @param arg Device to check 126210311Sjmallett */ 127210311Sjmallettstatic void cvm_do_timer(void *arg) 128210311Sjmallett{ 129210311Sjmallett static int port; 130213807Sjmallett static int updated; 131210311Sjmallett if (port < CVMX_PIP_NUM_INPUT_PORTS) { 132210311Sjmallett if (cvm_oct_device[port]) { 133210311Sjmallett int queues_per_port; 134210311Sjmallett int qos; 135210311Sjmallett cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc; 136213150Sjmallett 137216071Sjmallett cvm_oct_common_poll(priv->ifp); 138216071Sjmallett if (priv->need_link_update) { 139216071Sjmallett updated++; 140216071Sjmallett taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task); 141210311Sjmallett } 142210311Sjmallett 143210311Sjmallett queues_per_port = cvmx_pko_get_num_queues(port); 144210311Sjmallett /* Drain any pending packets in the free list */ 145210311Sjmallett for (qos = 0; qos < queues_per_port; qos++) { 146210311Sjmallett if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) { 147210311Sjmallett IF_LOCK(&priv->tx_free_queue[qos]); 148210311Sjmallett while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) { 149210311Sjmallett struct mbuf *m; 150210311Sjmallett 151210311Sjmallett _IF_DEQUEUE(&priv->tx_free_queue[qos], m); 152210311Sjmallett m_freem(m); 153210311Sjmallett } 154210311Sjmallett IF_UNLOCK(&priv->tx_free_queue[qos]); 155210311Sjmallett 156210311Sjmallett /* 157210311Sjmallett * XXX locking! 158210311Sjmallett */ 159210311Sjmallett priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 160210311Sjmallett } 161210311Sjmallett } 162210311Sjmallett } 163210311Sjmallett port++; 164210311Sjmallett /* Poll the next port in a 50th of a second. 165210311Sjmallett This spreads the polling of ports out a little bit */ 166210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 167210311Sjmallett } else { 168210311Sjmallett port = 0; 169213807Sjmallett /* If any updates were made in this run, continue iterating at 170213807Sjmallett * 1/50th of a second, so that if a link has merely gone down 171213807Sjmallett * temporarily (e.g. because of interface reinitialization) it 172213807Sjmallett * will not be forced to stay down for an entire second. 173213807Sjmallett */ 174213807Sjmallett if (updated > 0) { 175213807Sjmallett updated = 0; 176213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 177213807Sjmallett } else { 178213807Sjmallett /* All ports have been polled. Start the next iteration through 179213807Sjmallett the ports in one second */ 180213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 181213807Sjmallett } 182210311Sjmallett } 183210311Sjmallett} 184210311Sjmallett 185210311Sjmallett/** 186210311Sjmallett * Configure common hardware for all interfaces 187210311Sjmallett */ 188210311Sjmallettstatic void cvm_oct_configure_common_hw(device_t bus) 189210311Sjmallett{ 190210311Sjmallett struct octebus_softc *sc; 191219694Sjmallett int pko_queues; 192210311Sjmallett int error; 193210311Sjmallett int rid; 194210311Sjmallett 195210311Sjmallett sc = device_get_softc(bus); 196210311Sjmallett 197210311Sjmallett /* Setup the FPA */ 198210311Sjmallett cvmx_fpa_enable(); 199219694Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, 200219694Sjmallett num_packet_buffers); 201219694Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, 202219694Sjmallett num_packet_buffers); 203219694Sjmallett if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) { 204219694Sjmallett /* 205219694Sjmallett * If the FPA uses different pools for output buffers and 206219694Sjmallett * packets, size the output buffer pool based on the number 207219694Sjmallett * of PKO queues. 208219694Sjmallett */ 209219694Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN38XX)) 210219694Sjmallett pko_queues = 128; 211219694Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) 212219694Sjmallett pko_queues = 32; 213219694Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN50XX)) 214219694Sjmallett pko_queues = 32; 215219694Sjmallett else 216219694Sjmallett pko_queues = 256; 217210311Sjmallett 218219694Sjmallett cvm_oct_num_output_buffers = 4 * pko_queues; 219219694Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, 220219694Sjmallett CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 221219694Sjmallett cvm_oct_num_output_buffers); 222219694Sjmallett } 223219694Sjmallett 224210311Sjmallett if (USE_RED) 225219694Sjmallett cvmx_helper_setup_red(num_packet_buffers/4, 226219694Sjmallett num_packet_buffers/8); 227210311Sjmallett 228210311Sjmallett /* Enable the MII interface */ 229242346Sjmallett if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) 230210311Sjmallett cvmx_write_csr(CVMX_SMI_EN, 1); 231210311Sjmallett 232210311Sjmallett /* Register an IRQ hander for to receive POW interrupts */ 233210311Sjmallett rid = 0; 234210311Sjmallett sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid, 235232812Sjmallett OCTEON_IRQ_WORKQ0 + pow_receive_group, 236232812Sjmallett OCTEON_IRQ_WORKQ0 + pow_receive_group, 237210311Sjmallett 1, RF_ACTIVE); 238210311Sjmallett if (sc->sc_rx_irq == NULL) { 239210311Sjmallett device_printf(bus, "could not allocate workq irq"); 240210311Sjmallett return; 241210311Sjmallett } 242210311Sjmallett 243210311Sjmallett error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE, 244210311Sjmallett cvm_oct_do_interrupt, NULL, cvm_oct_device, 245219695Sjmallett &sc->sc_rx_intr_cookie); 246210311Sjmallett if (error != 0) { 247210311Sjmallett device_printf(bus, "could not setup workq irq"); 248210311Sjmallett return; 249210311Sjmallett } 250210311Sjmallett 251210311Sjmallett 252210311Sjmallett#ifdef SMP 253217664Sjmallett { 254217664Sjmallett cvmx_ciu_intx0_t en; 255217664Sjmallett int core; 256217210Sjmallett 257217664Sjmallett CPU_FOREACH(core) { 258217664Sjmallett if (core == PCPU_GET(cpuid)) 259217664Sjmallett continue; 260217210Sjmallett 261217664Sjmallett en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2)); 262217664Sjmallett en.s.workq |= (1<<pow_receive_group); 263217664Sjmallett cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), en.u64); 264210311Sjmallett } 265210311Sjmallett } 266210311Sjmallett#endif 267210311Sjmallett} 268210311Sjmallett 269210311Sjmallett 270210311Sjmallett/** 271210311Sjmallett * Free a work queue entry received in a intercept callback. 272210311Sjmallett * 273210311Sjmallett * @param work_queue_entry 274210311Sjmallett * Work queue entry to free 275210311Sjmallett * @return Zero on success, Negative on failure. 276210311Sjmallett */ 277210311Sjmallettint cvm_oct_free_work(void *work_queue_entry) 278210311Sjmallett{ 279210311Sjmallett cvmx_wqe_t *work = work_queue_entry; 280210311Sjmallett 281210311Sjmallett int segments = work->word2.s.bufs; 282210311Sjmallett cvmx_buf_ptr_t segment_ptr = work->packet_ptr; 283210311Sjmallett 284210311Sjmallett while (segments--) { 285210311Sjmallett cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8); 286210311Sjmallett if (__predict_false(!segment_ptr.s.i)) 287210311Sjmallett cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128)); 288210311Sjmallett segment_ptr = next_ptr; 289210311Sjmallett } 290210311Sjmallett cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1)); 291210311Sjmallett 292210311Sjmallett return 0; 293210311Sjmallett} 294210311Sjmallett 295210311Sjmallett 296210311Sjmallett/** 297210311Sjmallett * Module/ driver initialization. Creates the linux network 298210311Sjmallett * devices. 299210311Sjmallett * 300210311Sjmallett * @return Zero on success 301210311Sjmallett */ 302210311Sjmallettint cvm_oct_init_module(device_t bus) 303210311Sjmallett{ 304210311Sjmallett device_t dev; 305210311Sjmallett int ifnum; 306210311Sjmallett int num_interfaces; 307210311Sjmallett int interface; 308210311Sjmallett int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; 309210311Sjmallett int qos; 310210311Sjmallett 311210311Sjmallett cvm_oct_rx_initialize(); 312210311Sjmallett cvm_oct_configure_common_hw(bus); 313210311Sjmallett 314210311Sjmallett cvmx_helper_initialize_packet_io_global(); 315210311Sjmallett 316210311Sjmallett /* Change the input group for all ports before input is enabled */ 317210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 318210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 319210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 320210311Sjmallett int port; 321210311Sjmallett 322219694Sjmallett for (port = 0; port < num_ports; port++) { 323210311Sjmallett cvmx_pip_prt_tagx_t pip_prt_tagx; 324219694Sjmallett int pkind = cvmx_helper_get_ipd_port(interface, port); 325219694Sjmallett 326219694Sjmallett pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(pkind)); 327210311Sjmallett pip_prt_tagx.s.grp = pow_receive_group; 328219694Sjmallett cvmx_write_csr(CVMX_PIP_PRT_TAGX(pkind), pip_prt_tagx.u64); 329210311Sjmallett } 330210311Sjmallett } 331210311Sjmallett 332210311Sjmallett cvmx_helper_ipd_and_packet_input_enable(); 333210311Sjmallett 334210311Sjmallett memset(cvm_oct_device, 0, sizeof(cvm_oct_device)); 335210311Sjmallett 336213150Sjmallett cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT, 337213150Sjmallett taskqueue_thread_enqueue, &cvm_oct_link_taskq); 338213150Sjmallett taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET, 339213150Sjmallett "octe link taskq"); 340213150Sjmallett 341210311Sjmallett /* Initialize the FAU used for counting packet buffers that need to be freed */ 342210311Sjmallett cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); 343210311Sjmallett 344210311Sjmallett ifnum = 0; 345210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 346210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 347210311Sjmallett cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface); 348210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 349210311Sjmallett int port; 350210311Sjmallett 351231987Sgonzo for (port = cvmx_helper_get_ipd_port(interface, 0); 352231987Sgonzo port < cvmx_helper_get_ipd_port(interface, num_ports); 353231987Sgonzo ifnum++, port++) { 354210311Sjmallett cvm_oct_private_t *priv; 355210311Sjmallett struct ifnet *ifp; 356210311Sjmallett 357231987Sgonzo dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum); 358210311Sjmallett if (dev != NULL) 359210311Sjmallett ifp = if_alloc(IFT_ETHER); 360210311Sjmallett if (dev == NULL || ifp == NULL) { 361231987Sgonzo printf("Failed to allocate ethernet device for interface %d port %d\n", interface, port); 362210311Sjmallett continue; 363210311Sjmallett } 364210311Sjmallett 365210311Sjmallett /* Initialize the device private structure. */ 366210311Sjmallett device_probe(dev); 367210311Sjmallett priv = device_get_softc(dev); 368210311Sjmallett priv->dev = dev; 369210311Sjmallett priv->ifp = ifp; 370210311Sjmallett priv->imode = imode; 371210311Sjmallett priv->port = port; 372210311Sjmallett priv->queue = cvmx_pko_get_base_queue(priv->port); 373210311Sjmallett priv->fau = fau - cvmx_pko_get_num_queues(port) * 4; 374210311Sjmallett for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++) 375210311Sjmallett cvmx_fau_atomic_write32(priv->fau+qos*4, 0); 376213150Sjmallett TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv); 377210311Sjmallett 378210311Sjmallett switch (priv->imode) { 379210311Sjmallett 380210311Sjmallett /* These types don't support ports to IPD/PKO */ 381210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_DISABLED: 382210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PCIE: 383210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PICMG: 384210311Sjmallett break; 385210311Sjmallett 386210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_NPI: 387210311Sjmallett priv->init = cvm_oct_common_init; 388210311Sjmallett priv->uninit = cvm_oct_common_uninit; 389210311Sjmallett device_set_desc(dev, "Cavium Octeon NPI Ethernet"); 390210311Sjmallett break; 391210311Sjmallett 392210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_XAUI: 393210311Sjmallett priv->init = cvm_oct_xaui_init; 394215974Sjmallett priv->uninit = cvm_oct_common_uninit; 395210311Sjmallett device_set_desc(dev, "Cavium Octeon XAUI Ethernet"); 396210311Sjmallett break; 397210311Sjmallett 398210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_LOOP: 399210311Sjmallett priv->init = cvm_oct_common_init; 400210311Sjmallett priv->uninit = cvm_oct_common_uninit; 401210311Sjmallett device_set_desc(dev, "Cavium Octeon LOOP Ethernet"); 402210311Sjmallett break; 403210311Sjmallett 404210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SGMII: 405210311Sjmallett priv->init = cvm_oct_sgmii_init; 406215974Sjmallett priv->uninit = cvm_oct_common_uninit; 407210311Sjmallett device_set_desc(dev, "Cavium Octeon SGMII Ethernet"); 408210311Sjmallett break; 409210311Sjmallett 410210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SPI: 411210311Sjmallett priv->init = cvm_oct_spi_init; 412210311Sjmallett priv->uninit = cvm_oct_spi_uninit; 413210311Sjmallett device_set_desc(dev, "Cavium Octeon SPI Ethernet"); 414210311Sjmallett break; 415210311Sjmallett 416210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_RGMII: 417210311Sjmallett priv->init = cvm_oct_rgmii_init; 418210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 419210311Sjmallett device_set_desc(dev, "Cavium Octeon RGMII Ethernet"); 420210311Sjmallett break; 421210311Sjmallett 422210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_GMII: 423210311Sjmallett priv->init = cvm_oct_rgmii_init; 424210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 425210311Sjmallett device_set_desc(dev, "Cavium Octeon GMII Ethernet"); 426210311Sjmallett break; 427210311Sjmallett } 428210311Sjmallett 429210311Sjmallett ifp->if_softc = priv; 430210311Sjmallett 431210311Sjmallett if (!priv->init) { 432231987Sgonzo printf("octe%d: unsupported device type interface %d, port %d\n", 433231987Sgonzo ifnum, interface, priv->port); 434231987Sgonzo if_free(ifp); 435231987Sgonzo } else if (priv->init(ifp) != 0) { 436231987Sgonzo printf("octe%d: failed to register device for interface %d, port %d\n", 437231987Sgonzo ifnum, interface, priv->port); 438231987Sgonzo if_free(ifp); 439210311Sjmallett } else { 440210311Sjmallett cvm_oct_device[priv->port] = ifp; 441210311Sjmallett fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t); 442210311Sjmallett } 443210311Sjmallett } 444210311Sjmallett } 445210311Sjmallett 446210311Sjmallett if (INTERRUPT_LIMIT) { 447210311Sjmallett /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */ 448226024Smarcel cvmx_write_csr(CVMX_POW_WQ_INT_PC, cvmx_clock_get_rate(CVMX_CLOCK_CORE)/(INTERRUPT_LIMIT*16*256)<<8); 449210311Sjmallett 450210311Sjmallett /* Enable POW timer interrupt. It will count when there are packets available */ 451210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24); 452210311Sjmallett } else { 453210311Sjmallett /* Enable POW interrupt when our port has at least one packet */ 454210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001); 455210311Sjmallett } 456210311Sjmallett 457314667Savg callout_init(&cvm_oct_poll_timer, 1); 458210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 459210311Sjmallett 460210311Sjmallett return 0; 461210311Sjmallett} 462210311Sjmallett 463210311Sjmallett 464210311Sjmallett/** 465210311Sjmallett * Module / driver shutdown 466210311Sjmallett * 467210311Sjmallett * @return Zero on success 468210311Sjmallett */ 469219695Sjmallettvoid cvm_oct_cleanup_module(device_t bus) 470210311Sjmallett{ 471210311Sjmallett int port; 472219695Sjmallett struct octebus_softc *sc = device_get_softc(bus); 473210311Sjmallett 474210311Sjmallett /* Disable POW interrupt */ 475210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0); 476210311Sjmallett 477210311Sjmallett /* Free the interrupt handler */ 478219695Sjmallett bus_teardown_intr(bus, sc->sc_rx_irq, sc->sc_rx_intr_cookie); 479210311Sjmallett 480210311Sjmallett callout_stop(&cvm_oct_poll_timer); 481210311Sjmallett cvm_oct_rx_shutdown(); 482210311Sjmallett 483215990Sjmallett cvmx_helper_shutdown_packet_io_global(); 484215990Sjmallett 485210311Sjmallett /* Free the ethernet devices */ 486210311Sjmallett for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { 487210311Sjmallett if (cvm_oct_device[port]) { 488210311Sjmallett cvm_oct_tx_shutdown(cvm_oct_device[port]); 489210311Sjmallett#if 0 490210311Sjmallett unregister_netdev(cvm_oct_device[port]); 491210311Sjmallett kfree(cvm_oct_device[port]); 492210311Sjmallett#else 493210311Sjmallett panic("%s: need to detach and free interface.", __func__); 494210311Sjmallett#endif 495210311Sjmallett cvm_oct_device[port] = NULL; 496210311Sjmallett } 497210311Sjmallett } 498219694Sjmallett /* Free the HW pools */ 499219694Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); 500219694Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); 501219694Sjmallett 502219694Sjmallett if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) 503219694Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, cvm_oct_num_output_buffers); 504219694Sjmallett 505219694Sjmallett /* Disable FPA, all buffers are free, not done by helper shutdown. */ 506219694Sjmallett cvmx_fpa_disable(); 507210311Sjmallett} 508