ethernet.c revision 215974
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 215974 2010-11-28 05:57:24Z 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; 139210311Sjmallett if (priv->poll) 140210311Sjmallett { 141210311Sjmallett /* skip polling if we don't get the lock */ 142210311Sjmallett if (MDIO_TRYLOCK()) { 143210311Sjmallett priv->poll(cvm_oct_device[port]); 144210311Sjmallett MDIO_UNLOCK(); 145213150Sjmallett 146213150Sjmallett if (priv->need_link_update) { 147213807Sjmallett updated++; 148213150Sjmallett taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task); 149213150Sjmallett } 150210311Sjmallett } 151210311Sjmallett } 152210311Sjmallett 153210311Sjmallett queues_per_port = cvmx_pko_get_num_queues(port); 154210311Sjmallett /* Drain any pending packets in the free list */ 155210311Sjmallett for (qos = 0; qos < queues_per_port; qos++) { 156210311Sjmallett if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) { 157210311Sjmallett IF_LOCK(&priv->tx_free_queue[qos]); 158210311Sjmallett while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) { 159210311Sjmallett struct mbuf *m; 160210311Sjmallett 161210311Sjmallett _IF_DEQUEUE(&priv->tx_free_queue[qos], m); 162210311Sjmallett m_freem(m); 163210311Sjmallett } 164210311Sjmallett IF_UNLOCK(&priv->tx_free_queue[qos]); 165210311Sjmallett 166210311Sjmallett /* 167210311Sjmallett * XXX locking! 168210311Sjmallett */ 169210311Sjmallett priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 170210311Sjmallett } 171210311Sjmallett } 172210311Sjmallett } 173210311Sjmallett port++; 174210311Sjmallett /* Poll the next port in a 50th of a second. 175210311Sjmallett This spreads the polling of ports out a little bit */ 176210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 177210311Sjmallett } else { 178210311Sjmallett port = 0; 179213807Sjmallett /* If any updates were made in this run, continue iterating at 180213807Sjmallett * 1/50th of a second, so that if a link has merely gone down 181213807Sjmallett * temporarily (e.g. because of interface reinitialization) it 182213807Sjmallett * will not be forced to stay down for an entire second. 183213807Sjmallett */ 184213807Sjmallett if (updated > 0) { 185213807Sjmallett updated = 0; 186213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL); 187213807Sjmallett } else { 188213807Sjmallett /* All ports have been polled. Start the next iteration through 189213807Sjmallett the ports in one second */ 190213807Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 191213807Sjmallett } 192210311Sjmallett } 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; 202210311Sjmallett int error; 203210311Sjmallett int rid; 204210311Sjmallett 205210311Sjmallett sc = device_get_softc(bus); 206210311Sjmallett 207210311Sjmallett /* Setup the FPA */ 208210311Sjmallett cvmx_fpa_enable(); 209210311Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); 210210311Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); 211210311Sjmallett if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) 212210311Sjmallett cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); 213210311Sjmallett 214210311Sjmallett if (USE_RED) 215210311Sjmallett cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8); 216210311Sjmallett 217210311Sjmallett /* Enable the MII interface */ 218210311Sjmallett if (!octeon_is_simulation()) 219210311Sjmallett cvmx_write_csr(CVMX_SMI_EN, 1); 220210311Sjmallett 221210311Sjmallett /* Register an IRQ hander for to receive POW interrupts */ 222210311Sjmallett rid = 0; 223210311Sjmallett sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid, 224210311Sjmallett CVMX_IRQ_WORKQ0 + pow_receive_group, 225210311Sjmallett CVMX_IRQ_WORKQ0 + pow_receive_group, 226210311Sjmallett 1, RF_ACTIVE); 227210311Sjmallett if (sc->sc_rx_irq == NULL) { 228210311Sjmallett device_printf(bus, "could not allocate workq irq"); 229210311Sjmallett return; 230210311Sjmallett } 231210311Sjmallett 232210311Sjmallett error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE, 233210311Sjmallett cvm_oct_do_interrupt, NULL, cvm_oct_device, 234210311Sjmallett NULL); 235210311Sjmallett if (error != 0) { 236210311Sjmallett device_printf(bus, "could not setup workq irq"); 237210311Sjmallett return; 238210311Sjmallett } 239210311Sjmallett 240210311Sjmallett 241210311Sjmallett#ifdef SMP 242210311Sjmallett if (USE_MULTICORE_RECEIVE) { 243210311Sjmallett critical_enter(); 244210311Sjmallett { 245210311Sjmallett int cpu; 246210311Sjmallett for (cpu = 0; cpu < mp_maxid; cpu++) { 247210311Sjmallett if (!CPU_ABSENT(cpu) && 248210311Sjmallett (cpu != PCPU_GET(cpuid))) { 249210311Sjmallett cvmx_ciu_intx0_t en; 250210311Sjmallett en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2)); 251210311Sjmallett en.s.workq |= (1<<pow_receive_group); 252210311Sjmallett cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64); 253210311Sjmallett } 254210311Sjmallett } 255210311Sjmallett } 256210311Sjmallett critical_exit(); 257210311Sjmallett } 258210311Sjmallett#endif 259210311Sjmallett} 260210311Sjmallett 261210311Sjmallett 262210311Sjmallett/** 263210311Sjmallett * Free a work queue entry received in a intercept callback. 264210311Sjmallett * 265210311Sjmallett * @param work_queue_entry 266210311Sjmallett * Work queue entry to free 267210311Sjmallett * @return Zero on success, Negative on failure. 268210311Sjmallett */ 269210311Sjmallettint cvm_oct_free_work(void *work_queue_entry) 270210311Sjmallett{ 271210311Sjmallett cvmx_wqe_t *work = work_queue_entry; 272210311Sjmallett 273210311Sjmallett int segments = work->word2.s.bufs; 274210311Sjmallett cvmx_buf_ptr_t segment_ptr = work->packet_ptr; 275210311Sjmallett 276210311Sjmallett while (segments--) { 277210311Sjmallett cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8); 278210311Sjmallett if (__predict_false(!segment_ptr.s.i)) 279210311Sjmallett cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128)); 280210311Sjmallett segment_ptr = next_ptr; 281210311Sjmallett } 282210311Sjmallett cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1)); 283210311Sjmallett 284210311Sjmallett return 0; 285210311Sjmallett} 286210311Sjmallett 287210311Sjmallett 288210311Sjmallett/** 289210311Sjmallett * Module/ driver initialization. Creates the linux network 290210311Sjmallett * devices. 291210311Sjmallett * 292210311Sjmallett * @return Zero on success 293210311Sjmallett */ 294210311Sjmallettint cvm_oct_init_module(device_t bus) 295210311Sjmallett{ 296210311Sjmallett device_t dev; 297210311Sjmallett int ifnum; 298210311Sjmallett int num_interfaces; 299210311Sjmallett int interface; 300210311Sjmallett int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; 301210311Sjmallett int qos; 302210311Sjmallett 303210311Sjmallett printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING); 304210311Sjmallett 305210311Sjmallett cvm_oct_rx_initialize(); 306210311Sjmallett cvm_oct_configure_common_hw(bus); 307210311Sjmallett 308210311Sjmallett cvmx_helper_initialize_packet_io_global(); 309210311Sjmallett 310210311Sjmallett /* Change the input group for all ports before input is enabled */ 311210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 312210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 313210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 314210311Sjmallett int port; 315210311Sjmallett 316210311Sjmallett for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) { 317210311Sjmallett cvmx_pip_prt_tagx_t pip_prt_tagx; 318210311Sjmallett pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port)); 319210311Sjmallett pip_prt_tagx.s.grp = pow_receive_group; 320210311Sjmallett cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64); 321210311Sjmallett } 322210311Sjmallett } 323210311Sjmallett 324210311Sjmallett cvmx_helper_ipd_and_packet_input_enable(); 325210311Sjmallett 326210311Sjmallett memset(cvm_oct_device, 0, sizeof(cvm_oct_device)); 327210311Sjmallett 328213150Sjmallett cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT, 329213150Sjmallett taskqueue_thread_enqueue, &cvm_oct_link_taskq); 330213150Sjmallett taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET, 331213150Sjmallett "octe link taskq"); 332213150Sjmallett 333210311Sjmallett /* Initialize the FAU used for counting packet buffers that need to be freed */ 334210311Sjmallett cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); 335210311Sjmallett 336210311Sjmallett ifnum = 0; 337210311Sjmallett num_interfaces = cvmx_helper_get_number_of_interfaces(); 338210311Sjmallett for (interface = 0; interface < num_interfaces; interface++) { 339210311Sjmallett cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface); 340210311Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 341210311Sjmallett int port; 342210311Sjmallett 343210311Sjmallett for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) { 344210311Sjmallett cvm_oct_private_t *priv; 345210311Sjmallett struct ifnet *ifp; 346210311Sjmallett 347210311Sjmallett dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++); 348210311Sjmallett if (dev != NULL) 349210311Sjmallett ifp = if_alloc(IFT_ETHER); 350210311Sjmallett if (dev == NULL || ifp == NULL) { 351210311Sjmallett printf("\t\tFailed to allocate ethernet device for port %d\n", port); 352210311Sjmallett continue; 353210311Sjmallett } 354210311Sjmallett 355210311Sjmallett /* Initialize the device private structure. */ 356210311Sjmallett device_probe(dev); 357210311Sjmallett priv = device_get_softc(dev); 358210311Sjmallett priv->dev = dev; 359210311Sjmallett priv->ifp = ifp; 360210311Sjmallett priv->imode = imode; 361210311Sjmallett priv->port = port; 362210311Sjmallett priv->queue = cvmx_pko_get_base_queue(priv->port); 363210311Sjmallett priv->fau = fau - cvmx_pko_get_num_queues(port) * 4; 364210311Sjmallett for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++) 365210311Sjmallett cvmx_fau_atomic_write32(priv->fau+qos*4, 0); 366213150Sjmallett TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv); 367210311Sjmallett 368210311Sjmallett switch (priv->imode) { 369210311Sjmallett 370210311Sjmallett /* These types don't support ports to IPD/PKO */ 371210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_DISABLED: 372210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PCIE: 373210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_PICMG: 374210311Sjmallett break; 375210311Sjmallett 376210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_NPI: 377210311Sjmallett priv->init = cvm_oct_common_init; 378210311Sjmallett priv->uninit = cvm_oct_common_uninit; 379210311Sjmallett device_set_desc(dev, "Cavium Octeon NPI Ethernet"); 380210311Sjmallett break; 381210311Sjmallett 382210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_XAUI: 383210311Sjmallett priv->init = cvm_oct_xaui_init; 384215974Sjmallett priv->uninit = cvm_oct_common_uninit; 385210311Sjmallett device_set_desc(dev, "Cavium Octeon XAUI Ethernet"); 386210311Sjmallett break; 387210311Sjmallett 388210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_LOOP: 389210311Sjmallett priv->init = cvm_oct_common_init; 390210311Sjmallett priv->uninit = cvm_oct_common_uninit; 391210311Sjmallett device_set_desc(dev, "Cavium Octeon LOOP Ethernet"); 392210311Sjmallett break; 393210311Sjmallett 394210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SGMII: 395210311Sjmallett priv->init = cvm_oct_sgmii_init; 396215974Sjmallett priv->uninit = cvm_oct_common_uninit; 397210311Sjmallett device_set_desc(dev, "Cavium Octeon SGMII Ethernet"); 398210311Sjmallett break; 399210311Sjmallett 400210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_SPI: 401210311Sjmallett priv->init = cvm_oct_spi_init; 402210311Sjmallett priv->uninit = cvm_oct_spi_uninit; 403210311Sjmallett device_set_desc(dev, "Cavium Octeon SPI Ethernet"); 404210311Sjmallett break; 405210311Sjmallett 406210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_RGMII: 407210311Sjmallett priv->init = cvm_oct_rgmii_init; 408210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 409210311Sjmallett device_set_desc(dev, "Cavium Octeon RGMII Ethernet"); 410210311Sjmallett break; 411210311Sjmallett 412210311Sjmallett case CVMX_HELPER_INTERFACE_MODE_GMII: 413210311Sjmallett priv->init = cvm_oct_rgmii_init; 414210311Sjmallett priv->uninit = cvm_oct_rgmii_uninit; 415210311Sjmallett device_set_desc(dev, "Cavium Octeon GMII Ethernet"); 416210311Sjmallett break; 417210311Sjmallett } 418210311Sjmallett 419210311Sjmallett ifp->if_softc = priv; 420210311Sjmallett 421210311Sjmallett if (!priv->init) { 422210311Sjmallett panic("%s: unsupported device type, need to free ifp.", __func__); 423210311Sjmallett } else 424210311Sjmallett if (priv->init(ifp) < 0) { 425210311Sjmallett printf("\t\tFailed to register ethernet device for interface %d, port %d\n", 426210311Sjmallett interface, priv->port); 427210311Sjmallett panic("%s: init failed, need to free ifp.", __func__); 428210311Sjmallett } else { 429210311Sjmallett cvm_oct_device[priv->port] = ifp; 430210311Sjmallett fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t); 431210311Sjmallett } 432210311Sjmallett } 433210311Sjmallett } 434210311Sjmallett 435210311Sjmallett if (INTERRUPT_LIMIT) { 436210311Sjmallett /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */ 437210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8); 438210311Sjmallett 439210311Sjmallett /* Enable POW timer interrupt. It will count when there are packets available */ 440210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24); 441210311Sjmallett } else { 442210311Sjmallett /* Enable POW interrupt when our port has at least one packet */ 443210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001); 444210311Sjmallett } 445210311Sjmallett 446210311Sjmallett callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE); 447210311Sjmallett callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL); 448210311Sjmallett 449210311Sjmallett return 0; 450210311Sjmallett} 451210311Sjmallett 452210311Sjmallett 453210311Sjmallett/** 454210311Sjmallett * Module / driver shutdown 455210311Sjmallett * 456210311Sjmallett * @return Zero on success 457210311Sjmallett */ 458210311Sjmallettvoid cvm_oct_cleanup_module(void) 459210311Sjmallett{ 460210311Sjmallett int port; 461210311Sjmallett 462210311Sjmallett /* Disable POW interrupt */ 463210311Sjmallett cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0); 464210311Sjmallett 465210311Sjmallett cvmx_ipd_disable(); 466210311Sjmallett 467210311Sjmallett#if 0 468210311Sjmallett /* Free the interrupt handler */ 469210311Sjmallett free_irq(8 + pow_receive_group, cvm_oct_device); 470210311Sjmallett#endif 471210311Sjmallett 472210311Sjmallett callout_stop(&cvm_oct_poll_timer); 473210311Sjmallett cvm_oct_rx_shutdown(); 474210311Sjmallett cvmx_pko_disable(); 475210311Sjmallett 476210311Sjmallett /* Free the ethernet devices */ 477210311Sjmallett for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { 478210311Sjmallett if (cvm_oct_device[port]) { 479210311Sjmallett cvm_oct_tx_shutdown(cvm_oct_device[port]); 480210311Sjmallett#if 0 481210311Sjmallett unregister_netdev(cvm_oct_device[port]); 482210311Sjmallett kfree(cvm_oct_device[port]); 483210311Sjmallett#else 484210311Sjmallett panic("%s: need to detach and free interface.", __func__); 485210311Sjmallett#endif 486210311Sjmallett cvm_oct_device[port] = NULL; 487210311Sjmallett } 488210311Sjmallett } 489210311Sjmallett 490210311Sjmallett cvmx_pko_shutdown(); 491210311Sjmallett 492210311Sjmallett cvmx_ipd_free_ptr(); 493210311Sjmallett 494210311Sjmallett /* Free the HW pools */ 495210311Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers); 496210311Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers); 497210311Sjmallett if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) 498210311Sjmallett cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); 499210311Sjmallett} 500