12f345d8eSLuigi Rizzo /*- 2291a1934SXin LI * Copyright (C) 2013 Emulex 32f345d8eSLuigi Rizzo * All rights reserved. 42f345d8eSLuigi Rizzo * 52f345d8eSLuigi Rizzo * Redistribution and use in source and binary forms, with or without 62f345d8eSLuigi Rizzo * modification, are permitted provided that the following conditions are met: 72f345d8eSLuigi Rizzo * 82f345d8eSLuigi Rizzo * 1. Redistributions of source code must retain the above copyright notice, 92f345d8eSLuigi Rizzo * this list of conditions and the following disclaimer. 102f345d8eSLuigi Rizzo * 112f345d8eSLuigi Rizzo * 2. Redistributions in binary form must reproduce the above copyright 122f345d8eSLuigi Rizzo * notice, this list of conditions and the following disclaimer in the 132f345d8eSLuigi Rizzo * documentation and/or other materials provided with the distribution. 142f345d8eSLuigi Rizzo * 152f345d8eSLuigi Rizzo * 3. Neither the name of the Emulex Corporation nor the names of its 162f345d8eSLuigi Rizzo * contributors may be used to endorse or promote products derived from 172f345d8eSLuigi Rizzo * this software without specific prior written permission. 182f345d8eSLuigi Rizzo * 192f345d8eSLuigi Rizzo * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 202f345d8eSLuigi Rizzo * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 212f345d8eSLuigi Rizzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 222f345d8eSLuigi Rizzo * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 232f345d8eSLuigi Rizzo * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 242f345d8eSLuigi Rizzo * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 252f345d8eSLuigi Rizzo * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 262f345d8eSLuigi Rizzo * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 272f345d8eSLuigi Rizzo * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 282f345d8eSLuigi Rizzo * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 292f345d8eSLuigi Rizzo * POSSIBILITY OF SUCH DAMAGE. 302f345d8eSLuigi Rizzo * 312f345d8eSLuigi Rizzo * Contact Information: 322f345d8eSLuigi Rizzo * freebsd-drivers@emulex.com 332f345d8eSLuigi Rizzo * 342f345d8eSLuigi Rizzo * Emulex 352f345d8eSLuigi Rizzo * 3333 Susan Street 362f345d8eSLuigi Rizzo * Costa Mesa, CA 92626 372f345d8eSLuigi Rizzo */ 382f345d8eSLuigi Rizzo 392f345d8eSLuigi Rizzo /* $FreeBSD$ */ 402f345d8eSLuigi Rizzo 41ad512958SBjoern A. Zeeb #include "opt_inet6.h" 42ad512958SBjoern A. Zeeb #include "opt_inet.h" 43ad512958SBjoern A. Zeeb 442f345d8eSLuigi Rizzo #include "oce_if.h" 452f345d8eSLuigi Rizzo 46*5fbb6830SXin LI /* UE Status Low CSR */ 47*5fbb6830SXin LI static char *ue_status_low_desc[] = { 48*5fbb6830SXin LI "CEV", 49*5fbb6830SXin LI "CTX", 50*5fbb6830SXin LI "DBUF", 51*5fbb6830SXin LI "ERX", 52*5fbb6830SXin LI "Host", 53*5fbb6830SXin LI "MPU", 54*5fbb6830SXin LI "NDMA", 55*5fbb6830SXin LI "PTC ", 56*5fbb6830SXin LI "RDMA ", 57*5fbb6830SXin LI "RXF ", 58*5fbb6830SXin LI "RXIPS ", 59*5fbb6830SXin LI "RXULP0 ", 60*5fbb6830SXin LI "RXULP1 ", 61*5fbb6830SXin LI "RXULP2 ", 62*5fbb6830SXin LI "TIM ", 63*5fbb6830SXin LI "TPOST ", 64*5fbb6830SXin LI "TPRE ", 65*5fbb6830SXin LI "TXIPS ", 66*5fbb6830SXin LI "TXULP0 ", 67*5fbb6830SXin LI "TXULP1 ", 68*5fbb6830SXin LI "UC ", 69*5fbb6830SXin LI "WDMA ", 70*5fbb6830SXin LI "TXULP2 ", 71*5fbb6830SXin LI "HOST1 ", 72*5fbb6830SXin LI "P0_OB_LINK ", 73*5fbb6830SXin LI "P1_OB_LINK ", 74*5fbb6830SXin LI "HOST_GPIO ", 75*5fbb6830SXin LI "MBOX ", 76*5fbb6830SXin LI "AXGMAC0", 77*5fbb6830SXin LI "AXGMAC1", 78*5fbb6830SXin LI "JTAG", 79*5fbb6830SXin LI "MPU_INTPEND" 80*5fbb6830SXin LI }; 81*5fbb6830SXin LI 82*5fbb6830SXin LI /* UE Status High CSR */ 83*5fbb6830SXin LI static char *ue_status_hi_desc[] = { 84*5fbb6830SXin LI "LPCMEMHOST", 85*5fbb6830SXin LI "MGMT_MAC", 86*5fbb6830SXin LI "PCS0ONLINE", 87*5fbb6830SXin LI "MPU_IRAM", 88*5fbb6830SXin LI "PCS1ONLINE", 89*5fbb6830SXin LI "PCTL0", 90*5fbb6830SXin LI "PCTL1", 91*5fbb6830SXin LI "PMEM", 92*5fbb6830SXin LI "RR", 93*5fbb6830SXin LI "TXPB", 94*5fbb6830SXin LI "RXPP", 95*5fbb6830SXin LI "XAUI", 96*5fbb6830SXin LI "TXP", 97*5fbb6830SXin LI "ARM", 98*5fbb6830SXin LI "IPC", 99*5fbb6830SXin LI "HOST2", 100*5fbb6830SXin LI "HOST3", 101*5fbb6830SXin LI "HOST4", 102*5fbb6830SXin LI "HOST5", 103*5fbb6830SXin LI "HOST6", 104*5fbb6830SXin LI "HOST7", 105*5fbb6830SXin LI "HOST8", 106*5fbb6830SXin LI "HOST9", 107*5fbb6830SXin LI "NETC", 108*5fbb6830SXin LI "Unknown", 109*5fbb6830SXin LI "Unknown", 110*5fbb6830SXin LI "Unknown", 111*5fbb6830SXin LI "Unknown", 112*5fbb6830SXin LI "Unknown", 113*5fbb6830SXin LI "Unknown", 114*5fbb6830SXin LI "Unknown", 115*5fbb6830SXin LI "Unknown" 116*5fbb6830SXin LI }; 117*5fbb6830SXin LI 1182f345d8eSLuigi Rizzo 1192f345d8eSLuigi Rizzo /* Driver entry points prototypes */ 1202f345d8eSLuigi Rizzo static int oce_probe(device_t dev); 1212f345d8eSLuigi Rizzo static int oce_attach(device_t dev); 1222f345d8eSLuigi Rizzo static int oce_detach(device_t dev); 1232f345d8eSLuigi Rizzo static int oce_shutdown(device_t dev); 1242f345d8eSLuigi Rizzo static int oce_ioctl(struct ifnet *ifp, u_long command, caddr_t data); 1252f345d8eSLuigi Rizzo static void oce_init(void *xsc); 1262f345d8eSLuigi Rizzo static int oce_multiq_start(struct ifnet *ifp, struct mbuf *m); 1272f345d8eSLuigi Rizzo static void oce_multiq_flush(struct ifnet *ifp); 1282f345d8eSLuigi Rizzo 1292f345d8eSLuigi Rizzo /* Driver interrupt routines protypes */ 1302f345d8eSLuigi Rizzo static void oce_intr(void *arg, int pending); 1312f345d8eSLuigi Rizzo static int oce_setup_intr(POCE_SOFTC sc); 1322f345d8eSLuigi Rizzo static int oce_fast_isr(void *arg); 1332f345d8eSLuigi Rizzo static int oce_alloc_intr(POCE_SOFTC sc, int vector, 1342f345d8eSLuigi Rizzo void (*isr) (void *arg, int pending)); 1352f345d8eSLuigi Rizzo 1362f345d8eSLuigi Rizzo /* Media callbacks prototypes */ 1372f345d8eSLuigi Rizzo static void oce_media_status(struct ifnet *ifp, struct ifmediareq *req); 1382f345d8eSLuigi Rizzo static int oce_media_change(struct ifnet *ifp); 1392f345d8eSLuigi Rizzo 1402f345d8eSLuigi Rizzo /* Transmit routines prototypes */ 1412f345d8eSLuigi Rizzo static int oce_tx(POCE_SOFTC sc, struct mbuf **mpp, int wq_index); 1422f345d8eSLuigi Rizzo static void oce_tx_restart(POCE_SOFTC sc, struct oce_wq *wq); 1432f345d8eSLuigi Rizzo static void oce_tx_complete(struct oce_wq *wq, uint32_t wqe_idx, 1442f345d8eSLuigi Rizzo uint32_t status); 1452f345d8eSLuigi Rizzo static int oce_multiq_transmit(struct ifnet *ifp, struct mbuf *m, 1462f345d8eSLuigi Rizzo struct oce_wq *wq); 1472f345d8eSLuigi Rizzo 1482f345d8eSLuigi Rizzo /* Receive routines prototypes */ 1492f345d8eSLuigi Rizzo static void oce_discard_rx_comp(struct oce_rq *rq, struct oce_nic_rx_cqe *cqe); 1502f345d8eSLuigi Rizzo static int oce_cqe_vtp_valid(POCE_SOFTC sc, struct oce_nic_rx_cqe *cqe); 1512f345d8eSLuigi Rizzo static int oce_cqe_portid_valid(POCE_SOFTC sc, struct oce_nic_rx_cqe *cqe); 1522f345d8eSLuigi Rizzo static void oce_rx(struct oce_rq *rq, uint32_t rqe_idx, 1532f345d8eSLuigi Rizzo struct oce_nic_rx_cqe *cqe); 1542f345d8eSLuigi Rizzo 1552f345d8eSLuigi Rizzo /* Helper function prototypes in this file */ 1562f345d8eSLuigi Rizzo static int oce_attach_ifp(POCE_SOFTC sc); 1572f345d8eSLuigi Rizzo static void oce_add_vlan(void *arg, struct ifnet *ifp, uint16_t vtag); 1582f345d8eSLuigi Rizzo static void oce_del_vlan(void *arg, struct ifnet *ifp, uint16_t vtag); 1592f345d8eSLuigi Rizzo static int oce_vid_config(POCE_SOFTC sc); 1602f345d8eSLuigi Rizzo static void oce_mac_addr_set(POCE_SOFTC sc); 1612f345d8eSLuigi Rizzo static int oce_handle_passthrough(struct ifnet *ifp, caddr_t data); 1622f345d8eSLuigi Rizzo static void oce_local_timer(void *arg); 1632f345d8eSLuigi Rizzo static void oce_if_deactivate(POCE_SOFTC sc); 1642f345d8eSLuigi Rizzo static void oce_if_activate(POCE_SOFTC sc); 1652f345d8eSLuigi Rizzo static void setup_max_queues_want(POCE_SOFTC sc); 1662f345d8eSLuigi Rizzo static void update_queues_got(POCE_SOFTC sc); 1679bd3250aSLuigi Rizzo static void process_link_state(POCE_SOFTC sc, 1689bd3250aSLuigi Rizzo struct oce_async_cqe_link_state *acqe); 169cdaba892SXin LI static int oce_tx_asic_stall_verify(POCE_SOFTC sc, struct mbuf *m); 170291a1934SXin LI static void oce_get_config(POCE_SOFTC sc); 171cdaba892SXin LI static struct mbuf *oce_insert_vlan_tag(POCE_SOFTC sc, struct mbuf *m, boolean_t *complete); 1729bd3250aSLuigi Rizzo 1739bd3250aSLuigi Rizzo /* IP specific */ 1749bd3250aSLuigi Rizzo #if defined(INET6) || defined(INET) 1759bd3250aSLuigi Rizzo static int oce_init_lro(POCE_SOFTC sc); 1769bd3250aSLuigi Rizzo static void oce_rx_flush_lro(struct oce_rq *rq); 1779bd3250aSLuigi Rizzo static struct mbuf * oce_tso_setup(POCE_SOFTC sc, struct mbuf **mpp); 1789bd3250aSLuigi Rizzo #endif 1792f345d8eSLuigi Rizzo 1802f345d8eSLuigi Rizzo static device_method_t oce_dispatch[] = { 1812f345d8eSLuigi Rizzo DEVMETHOD(device_probe, oce_probe), 1822f345d8eSLuigi Rizzo DEVMETHOD(device_attach, oce_attach), 1832f345d8eSLuigi Rizzo DEVMETHOD(device_detach, oce_detach), 1842f345d8eSLuigi Rizzo DEVMETHOD(device_shutdown, oce_shutdown), 18561bfd867SSofian Brabez 18661bfd867SSofian Brabez DEVMETHOD_END 1872f345d8eSLuigi Rizzo }; 1882f345d8eSLuigi Rizzo 1892f345d8eSLuigi Rizzo static driver_t oce_driver = { 1902f345d8eSLuigi Rizzo "oce", 1912f345d8eSLuigi Rizzo oce_dispatch, 1922f345d8eSLuigi Rizzo sizeof(OCE_SOFTC) 1932f345d8eSLuigi Rizzo }; 1942f345d8eSLuigi Rizzo static devclass_t oce_devclass; 1952f345d8eSLuigi Rizzo 1962f345d8eSLuigi Rizzo 1972f345d8eSLuigi Rizzo DRIVER_MODULE(oce, pci, oce_driver, oce_devclass, 0, 0); 1982f345d8eSLuigi Rizzo MODULE_DEPEND(oce, pci, 1, 1, 1); 1992f345d8eSLuigi Rizzo MODULE_DEPEND(oce, ether, 1, 1, 1); 2002f345d8eSLuigi Rizzo MODULE_VERSION(oce, 1); 2012f345d8eSLuigi Rizzo 2022f345d8eSLuigi Rizzo 2032f345d8eSLuigi Rizzo /* global vars */ 2042f345d8eSLuigi Rizzo const char component_revision[32] = {"///" COMPONENT_REVISION "///"}; 2052f345d8eSLuigi Rizzo 2062f345d8eSLuigi Rizzo /* Module capabilites and parameters */ 2072f345d8eSLuigi Rizzo uint32_t oce_max_rsp_handled = OCE_MAX_RSP_HANDLED; 2082f345d8eSLuigi Rizzo uint32_t oce_enable_rss = OCE_MODCAP_RSS; 2092f345d8eSLuigi Rizzo 2102f345d8eSLuigi Rizzo 2112f345d8eSLuigi Rizzo TUNABLE_INT("hw.oce.max_rsp_handled", &oce_max_rsp_handled); 2122f345d8eSLuigi Rizzo TUNABLE_INT("hw.oce.enable_rss", &oce_enable_rss); 2132f345d8eSLuigi Rizzo 2142f345d8eSLuigi Rizzo 2152f345d8eSLuigi Rizzo /* Supported devices table */ 2162f345d8eSLuigi Rizzo static uint32_t supportedDevices[] = { 2172f345d8eSLuigi Rizzo (PCI_VENDOR_SERVERENGINES << 16) | PCI_PRODUCT_BE2, 2182f345d8eSLuigi Rizzo (PCI_VENDOR_SERVERENGINES << 16) | PCI_PRODUCT_BE3, 2192f345d8eSLuigi Rizzo (PCI_VENDOR_EMULEX << 16) | PCI_PRODUCT_BE3, 2202f345d8eSLuigi Rizzo (PCI_VENDOR_EMULEX << 16) | PCI_PRODUCT_XE201, 2212f345d8eSLuigi Rizzo (PCI_VENDOR_EMULEX << 16) | PCI_PRODUCT_XE201_VF, 222291a1934SXin LI (PCI_VENDOR_EMULEX << 16) | PCI_PRODUCT_SH 2232f345d8eSLuigi Rizzo }; 2242f345d8eSLuigi Rizzo 2252f345d8eSLuigi Rizzo 2262f345d8eSLuigi Rizzo 2272f345d8eSLuigi Rizzo 2282f345d8eSLuigi Rizzo /***************************************************************************** 2292f345d8eSLuigi Rizzo * Driver entry points functions * 2302f345d8eSLuigi Rizzo *****************************************************************************/ 2312f345d8eSLuigi Rizzo 2322f345d8eSLuigi Rizzo static int 2332f345d8eSLuigi Rizzo oce_probe(device_t dev) 2342f345d8eSLuigi Rizzo { 2359bd3250aSLuigi Rizzo uint16_t vendor = 0; 2369bd3250aSLuigi Rizzo uint16_t device = 0; 2379bd3250aSLuigi Rizzo int i = 0; 2389bd3250aSLuigi Rizzo char str[256] = {0}; 2392f345d8eSLuigi Rizzo POCE_SOFTC sc; 2402f345d8eSLuigi Rizzo 2412f345d8eSLuigi Rizzo sc = device_get_softc(dev); 2422f345d8eSLuigi Rizzo bzero(sc, sizeof(OCE_SOFTC)); 2432f345d8eSLuigi Rizzo sc->dev = dev; 2442f345d8eSLuigi Rizzo 2452f345d8eSLuigi Rizzo vendor = pci_get_vendor(dev); 2462f345d8eSLuigi Rizzo device = pci_get_device(dev); 2472f345d8eSLuigi Rizzo 2489bd3250aSLuigi Rizzo for (i = 0; i < (sizeof(supportedDevices) / sizeof(uint32_t)); i++) { 2492f345d8eSLuigi Rizzo if (vendor == ((supportedDevices[i] >> 16) & 0xffff)) { 2502f345d8eSLuigi Rizzo if (device == (supportedDevices[i] & 0xffff)) { 2519bd3250aSLuigi Rizzo sprintf(str, "%s:%s", "Emulex CNA NIC function", 2522f345d8eSLuigi Rizzo component_revision); 2532f345d8eSLuigi Rizzo device_set_desc_copy(dev, str); 2542f345d8eSLuigi Rizzo 2552f345d8eSLuigi Rizzo switch (device) { 2562f345d8eSLuigi Rizzo case PCI_PRODUCT_BE2: 2572f345d8eSLuigi Rizzo sc->flags |= OCE_FLAGS_BE2; 2582f345d8eSLuigi Rizzo break; 2592f345d8eSLuigi Rizzo case PCI_PRODUCT_BE3: 2602f345d8eSLuigi Rizzo sc->flags |= OCE_FLAGS_BE3; 2612f345d8eSLuigi Rizzo break; 2622f345d8eSLuigi Rizzo case PCI_PRODUCT_XE201: 2632f345d8eSLuigi Rizzo case PCI_PRODUCT_XE201_VF: 2642f345d8eSLuigi Rizzo sc->flags |= OCE_FLAGS_XE201; 2652f345d8eSLuigi Rizzo break; 266291a1934SXin LI case PCI_PRODUCT_SH: 267291a1934SXin LI sc->flags |= OCE_FLAGS_SH; 268291a1934SXin LI break; 2692f345d8eSLuigi Rizzo default: 2702f345d8eSLuigi Rizzo return ENXIO; 2712f345d8eSLuigi Rizzo } 2722f345d8eSLuigi Rizzo return BUS_PROBE_DEFAULT; 2732f345d8eSLuigi Rizzo } 2742f345d8eSLuigi Rizzo } 2752f345d8eSLuigi Rizzo } 2762f345d8eSLuigi Rizzo 2772f345d8eSLuigi Rizzo return ENXIO; 2782f345d8eSLuigi Rizzo } 2792f345d8eSLuigi Rizzo 2802f345d8eSLuigi Rizzo 2812f345d8eSLuigi Rizzo static int 2822f345d8eSLuigi Rizzo oce_attach(device_t dev) 2832f345d8eSLuigi Rizzo { 2842f345d8eSLuigi Rizzo POCE_SOFTC sc; 2852f345d8eSLuigi Rizzo int rc = 0; 2862f345d8eSLuigi Rizzo 2872f345d8eSLuigi Rizzo sc = device_get_softc(dev); 2882f345d8eSLuigi Rizzo 2892f345d8eSLuigi Rizzo rc = oce_hw_pci_alloc(sc); 2902f345d8eSLuigi Rizzo if (rc) 2912f345d8eSLuigi Rizzo return rc; 2922f345d8eSLuigi Rizzo 2932f345d8eSLuigi Rizzo sc->tx_ring_size = OCE_TX_RING_SIZE; 2942f345d8eSLuigi Rizzo sc->rx_ring_size = OCE_RX_RING_SIZE; 2952f345d8eSLuigi Rizzo sc->rq_frag_size = OCE_RQ_BUF_SIZE; 2962f345d8eSLuigi Rizzo sc->flow_control = OCE_DEFAULT_FLOW_CONTROL; 2972f345d8eSLuigi Rizzo sc->promisc = OCE_DEFAULT_PROMISCUOUS; 2982f345d8eSLuigi Rizzo 2992f345d8eSLuigi Rizzo LOCK_CREATE(&sc->bmbx_lock, "Mailbox_lock"); 3002f345d8eSLuigi Rizzo LOCK_CREATE(&sc->dev_lock, "Device_lock"); 3012f345d8eSLuigi Rizzo 3022f345d8eSLuigi Rizzo /* initialise the hardware */ 3032f345d8eSLuigi Rizzo rc = oce_hw_init(sc); 3042f345d8eSLuigi Rizzo if (rc) 3052f345d8eSLuigi Rizzo goto pci_res_free; 3062f345d8eSLuigi Rizzo 307291a1934SXin LI oce_get_config(sc); 308291a1934SXin LI 3092f345d8eSLuigi Rizzo setup_max_queues_want(sc); 3102f345d8eSLuigi Rizzo 3112f345d8eSLuigi Rizzo rc = oce_setup_intr(sc); 3122f345d8eSLuigi Rizzo if (rc) 3132f345d8eSLuigi Rizzo goto mbox_free; 3142f345d8eSLuigi Rizzo 3152f345d8eSLuigi Rizzo rc = oce_queue_init_all(sc); 3162f345d8eSLuigi Rizzo if (rc) 3172f345d8eSLuigi Rizzo goto intr_free; 3182f345d8eSLuigi Rizzo 3192f345d8eSLuigi Rizzo rc = oce_attach_ifp(sc); 3202f345d8eSLuigi Rizzo if (rc) 3212f345d8eSLuigi Rizzo goto queues_free; 3222f345d8eSLuigi Rizzo 323ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 3242f345d8eSLuigi Rizzo rc = oce_init_lro(sc); 3252f345d8eSLuigi Rizzo if (rc) 3262f345d8eSLuigi Rizzo goto ifp_free; 327ad512958SBjoern A. Zeeb #endif 3282f345d8eSLuigi Rizzo 3292f345d8eSLuigi Rizzo rc = oce_hw_start(sc); 3302f345d8eSLuigi Rizzo if (rc) 331db702c59SEitan Adler goto lro_free; 3322f345d8eSLuigi Rizzo 3332f345d8eSLuigi Rizzo sc->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, 3342f345d8eSLuigi Rizzo oce_add_vlan, sc, EVENTHANDLER_PRI_FIRST); 3352f345d8eSLuigi Rizzo sc->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, 3362f345d8eSLuigi Rizzo oce_del_vlan, sc, EVENTHANDLER_PRI_FIRST); 3372f345d8eSLuigi Rizzo 3382f345d8eSLuigi Rizzo rc = oce_stats_init(sc); 3392f345d8eSLuigi Rizzo if (rc) 3402f345d8eSLuigi Rizzo goto vlan_free; 3412f345d8eSLuigi Rizzo 3422f345d8eSLuigi Rizzo oce_add_sysctls(sc); 3432f345d8eSLuigi Rizzo 3442f345d8eSLuigi Rizzo callout_init(&sc->timer, CALLOUT_MPSAFE); 3452f345d8eSLuigi Rizzo rc = callout_reset(&sc->timer, 2 * hz, oce_local_timer, sc); 3462f345d8eSLuigi Rizzo if (rc) 3472f345d8eSLuigi Rizzo goto stats_free; 3482f345d8eSLuigi Rizzo 3492f345d8eSLuigi Rizzo return 0; 3502f345d8eSLuigi Rizzo 3512f345d8eSLuigi Rizzo stats_free: 3522f345d8eSLuigi Rizzo callout_drain(&sc->timer); 3532f345d8eSLuigi Rizzo oce_stats_free(sc); 3542f345d8eSLuigi Rizzo vlan_free: 3552f345d8eSLuigi Rizzo if (sc->vlan_attach) 3562f345d8eSLuigi Rizzo EVENTHANDLER_DEREGISTER(vlan_config, sc->vlan_attach); 3572f345d8eSLuigi Rizzo if (sc->vlan_detach) 3582f345d8eSLuigi Rizzo EVENTHANDLER_DEREGISTER(vlan_unconfig, sc->vlan_detach); 3592f345d8eSLuigi Rizzo oce_hw_intr_disable(sc); 3602f345d8eSLuigi Rizzo lro_free: 361ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 3622f345d8eSLuigi Rizzo oce_free_lro(sc); 3632f345d8eSLuigi Rizzo ifp_free: 364ad512958SBjoern A. Zeeb #endif 3652f345d8eSLuigi Rizzo ether_ifdetach(sc->ifp); 3662f345d8eSLuigi Rizzo if_free(sc->ifp); 3672f345d8eSLuigi Rizzo queues_free: 3682f345d8eSLuigi Rizzo oce_queue_release_all(sc); 3692f345d8eSLuigi Rizzo intr_free: 3702f345d8eSLuigi Rizzo oce_intr_free(sc); 3712f345d8eSLuigi Rizzo mbox_free: 3722f345d8eSLuigi Rizzo oce_dma_free(sc, &sc->bsmbx); 3732f345d8eSLuigi Rizzo pci_res_free: 3742f345d8eSLuigi Rizzo oce_hw_pci_free(sc); 3752f345d8eSLuigi Rizzo LOCK_DESTROY(&sc->dev_lock); 3762f345d8eSLuigi Rizzo LOCK_DESTROY(&sc->bmbx_lock); 3772f345d8eSLuigi Rizzo return rc; 3782f345d8eSLuigi Rizzo 3792f345d8eSLuigi Rizzo } 3802f345d8eSLuigi Rizzo 3812f345d8eSLuigi Rizzo 3822f345d8eSLuigi Rizzo static int 3832f345d8eSLuigi Rizzo oce_detach(device_t dev) 3842f345d8eSLuigi Rizzo { 3852f345d8eSLuigi Rizzo POCE_SOFTC sc = device_get_softc(dev); 3862f345d8eSLuigi Rizzo 3872f345d8eSLuigi Rizzo LOCK(&sc->dev_lock); 3882f345d8eSLuigi Rizzo oce_if_deactivate(sc); 3892f345d8eSLuigi Rizzo UNLOCK(&sc->dev_lock); 3902f345d8eSLuigi Rizzo 3912f345d8eSLuigi Rizzo callout_drain(&sc->timer); 3922f345d8eSLuigi Rizzo 3932f345d8eSLuigi Rizzo if (sc->vlan_attach != NULL) 3942f345d8eSLuigi Rizzo EVENTHANDLER_DEREGISTER(vlan_config, sc->vlan_attach); 3952f345d8eSLuigi Rizzo if (sc->vlan_detach != NULL) 3962f345d8eSLuigi Rizzo EVENTHANDLER_DEREGISTER(vlan_unconfig, sc->vlan_detach); 3972f345d8eSLuigi Rizzo 3982f345d8eSLuigi Rizzo ether_ifdetach(sc->ifp); 3992f345d8eSLuigi Rizzo 4002f345d8eSLuigi Rizzo if_free(sc->ifp); 4012f345d8eSLuigi Rizzo 4022f345d8eSLuigi Rizzo oce_hw_shutdown(sc); 4032f345d8eSLuigi Rizzo 4042f345d8eSLuigi Rizzo bus_generic_detach(dev); 4052f345d8eSLuigi Rizzo 4062f345d8eSLuigi Rizzo return 0; 4072f345d8eSLuigi Rizzo } 4082f345d8eSLuigi Rizzo 4092f345d8eSLuigi Rizzo 4102f345d8eSLuigi Rizzo static int 4112f345d8eSLuigi Rizzo oce_shutdown(device_t dev) 4122f345d8eSLuigi Rizzo { 4132f345d8eSLuigi Rizzo int rc; 4142f345d8eSLuigi Rizzo 4152f345d8eSLuigi Rizzo rc = oce_detach(dev); 4162f345d8eSLuigi Rizzo 4172f345d8eSLuigi Rizzo return rc; 4182f345d8eSLuigi Rizzo } 4192f345d8eSLuigi Rizzo 4202f345d8eSLuigi Rizzo 4212f345d8eSLuigi Rizzo static int 4222f345d8eSLuigi Rizzo oce_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 4232f345d8eSLuigi Rizzo { 4242f345d8eSLuigi Rizzo struct ifreq *ifr = (struct ifreq *)data; 4252f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 4262f345d8eSLuigi Rizzo int rc = 0; 4272f345d8eSLuigi Rizzo uint32_t u; 4282f345d8eSLuigi Rizzo 4292f345d8eSLuigi Rizzo switch (command) { 4302f345d8eSLuigi Rizzo 4312f345d8eSLuigi Rizzo case SIOCGIFMEDIA: 4322f345d8eSLuigi Rizzo rc = ifmedia_ioctl(ifp, ifr, &sc->media, command); 4332f345d8eSLuigi Rizzo break; 4342f345d8eSLuigi Rizzo 4352f345d8eSLuigi Rizzo case SIOCSIFMTU: 4362f345d8eSLuigi Rizzo if (ifr->ifr_mtu > OCE_MAX_MTU) 4372f345d8eSLuigi Rizzo rc = EINVAL; 4382f345d8eSLuigi Rizzo else 4392f345d8eSLuigi Rizzo ifp->if_mtu = ifr->ifr_mtu; 4402f345d8eSLuigi Rizzo break; 4412f345d8eSLuigi Rizzo 4422f345d8eSLuigi Rizzo case SIOCSIFFLAGS: 4432f345d8eSLuigi Rizzo if (ifp->if_flags & IFF_UP) { 4442f345d8eSLuigi Rizzo if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 4452f345d8eSLuigi Rizzo sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 4462f345d8eSLuigi Rizzo oce_init(sc); 4472f345d8eSLuigi Rizzo } 4482f345d8eSLuigi Rizzo device_printf(sc->dev, "Interface Up\n"); 4492f345d8eSLuigi Rizzo } else { 4502f345d8eSLuigi Rizzo LOCK(&sc->dev_lock); 4512f345d8eSLuigi Rizzo 4522f345d8eSLuigi Rizzo sc->ifp->if_drv_flags &= 4532f345d8eSLuigi Rizzo ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 4542f345d8eSLuigi Rizzo oce_if_deactivate(sc); 4552f345d8eSLuigi Rizzo 4562f345d8eSLuigi Rizzo UNLOCK(&sc->dev_lock); 4572f345d8eSLuigi Rizzo 4582f345d8eSLuigi Rizzo device_printf(sc->dev, "Interface Down\n"); 4592f345d8eSLuigi Rizzo } 4602f345d8eSLuigi Rizzo 4612f345d8eSLuigi Rizzo if ((ifp->if_flags & IFF_PROMISC) && !sc->promisc) { 462*5fbb6830SXin LI if (!oce_rxf_set_promiscuous(sc, (1 | (1 << 1)))) 4632f345d8eSLuigi Rizzo sc->promisc = TRUE; 4642f345d8eSLuigi Rizzo } else if (!(ifp->if_flags & IFF_PROMISC) && sc->promisc) { 465*5fbb6830SXin LI if (!oce_rxf_set_promiscuous(sc, 0)) 4662f345d8eSLuigi Rizzo sc->promisc = FALSE; 4672f345d8eSLuigi Rizzo } 4682f345d8eSLuigi Rizzo 4692f345d8eSLuigi Rizzo break; 4702f345d8eSLuigi Rizzo 4712f345d8eSLuigi Rizzo case SIOCADDMULTI: 4722f345d8eSLuigi Rizzo case SIOCDELMULTI: 4732f345d8eSLuigi Rizzo rc = oce_hw_update_multicast(sc); 4742f345d8eSLuigi Rizzo if (rc) 4752f345d8eSLuigi Rizzo device_printf(sc->dev, 4762f345d8eSLuigi Rizzo "Update multicast address failed\n"); 4772f345d8eSLuigi Rizzo break; 4782f345d8eSLuigi Rizzo 4792f345d8eSLuigi Rizzo case SIOCSIFCAP: 4802f345d8eSLuigi Rizzo u = ifr->ifr_reqcap ^ ifp->if_capenable; 4812f345d8eSLuigi Rizzo 4822f345d8eSLuigi Rizzo if (u & IFCAP_TXCSUM) { 4832f345d8eSLuigi Rizzo ifp->if_capenable ^= IFCAP_TXCSUM; 4842f345d8eSLuigi Rizzo ifp->if_hwassist ^= (CSUM_TCP | CSUM_UDP | CSUM_IP); 4852f345d8eSLuigi Rizzo 4862f345d8eSLuigi Rizzo if (IFCAP_TSO & ifp->if_capenable && 4872f345d8eSLuigi Rizzo !(IFCAP_TXCSUM & ifp->if_capenable)) { 4882f345d8eSLuigi Rizzo ifp->if_capenable &= ~IFCAP_TSO; 4892f345d8eSLuigi Rizzo ifp->if_hwassist &= ~CSUM_TSO; 4902f345d8eSLuigi Rizzo if_printf(ifp, 4912f345d8eSLuigi Rizzo "TSO disabled due to -txcsum.\n"); 4922f345d8eSLuigi Rizzo } 4932f345d8eSLuigi Rizzo } 4942f345d8eSLuigi Rizzo 4952f345d8eSLuigi Rizzo if (u & IFCAP_RXCSUM) 4962f345d8eSLuigi Rizzo ifp->if_capenable ^= IFCAP_RXCSUM; 4972f345d8eSLuigi Rizzo 4982f345d8eSLuigi Rizzo if (u & IFCAP_TSO4) { 4992f345d8eSLuigi Rizzo ifp->if_capenable ^= IFCAP_TSO4; 5002f345d8eSLuigi Rizzo 5012f345d8eSLuigi Rizzo if (IFCAP_TSO & ifp->if_capenable) { 5022f345d8eSLuigi Rizzo if (IFCAP_TXCSUM & ifp->if_capenable) 5032f345d8eSLuigi Rizzo ifp->if_hwassist |= CSUM_TSO; 5042f345d8eSLuigi Rizzo else { 5052f345d8eSLuigi Rizzo ifp->if_capenable &= ~IFCAP_TSO; 5062f345d8eSLuigi Rizzo ifp->if_hwassist &= ~CSUM_TSO; 5072f345d8eSLuigi Rizzo if_printf(ifp, 5082f345d8eSLuigi Rizzo "Enable txcsum first.\n"); 5092f345d8eSLuigi Rizzo rc = EAGAIN; 5102f345d8eSLuigi Rizzo } 5112f345d8eSLuigi Rizzo } else 5122f345d8eSLuigi Rizzo ifp->if_hwassist &= ~CSUM_TSO; 5132f345d8eSLuigi Rizzo } 5142f345d8eSLuigi Rizzo 5152f345d8eSLuigi Rizzo if (u & IFCAP_VLAN_HWTAGGING) 5162f345d8eSLuigi Rizzo ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; 5172f345d8eSLuigi Rizzo 5182f345d8eSLuigi Rizzo if (u & IFCAP_VLAN_HWFILTER) { 5192f345d8eSLuigi Rizzo ifp->if_capenable ^= IFCAP_VLAN_HWFILTER; 5202f345d8eSLuigi Rizzo oce_vid_config(sc); 5212f345d8eSLuigi Rizzo } 522ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 5232f345d8eSLuigi Rizzo if (u & IFCAP_LRO) 5242f345d8eSLuigi Rizzo ifp->if_capenable ^= IFCAP_LRO; 525ad512958SBjoern A. Zeeb #endif 5262f345d8eSLuigi Rizzo 5272f345d8eSLuigi Rizzo break; 5282f345d8eSLuigi Rizzo 5292f345d8eSLuigi Rizzo case SIOCGPRIVATE_0: 5302f345d8eSLuigi Rizzo rc = oce_handle_passthrough(ifp, data); 5312f345d8eSLuigi Rizzo break; 5322f345d8eSLuigi Rizzo default: 5332f345d8eSLuigi Rizzo rc = ether_ioctl(ifp, command, data); 5342f345d8eSLuigi Rizzo break; 5352f345d8eSLuigi Rizzo } 5362f345d8eSLuigi Rizzo 5372f345d8eSLuigi Rizzo return rc; 5382f345d8eSLuigi Rizzo } 5392f345d8eSLuigi Rizzo 5402f345d8eSLuigi Rizzo 5412f345d8eSLuigi Rizzo static void 5422f345d8eSLuigi Rizzo oce_init(void *arg) 5432f345d8eSLuigi Rizzo { 5442f345d8eSLuigi Rizzo POCE_SOFTC sc = arg; 5452f345d8eSLuigi Rizzo 5462f345d8eSLuigi Rizzo LOCK(&sc->dev_lock); 5472f345d8eSLuigi Rizzo 5482f345d8eSLuigi Rizzo if (sc->ifp->if_flags & IFF_UP) { 5492f345d8eSLuigi Rizzo oce_if_deactivate(sc); 5502f345d8eSLuigi Rizzo oce_if_activate(sc); 5512f345d8eSLuigi Rizzo } 5522f345d8eSLuigi Rizzo 5532f345d8eSLuigi Rizzo UNLOCK(&sc->dev_lock); 5542f345d8eSLuigi Rizzo 5552f345d8eSLuigi Rizzo } 5562f345d8eSLuigi Rizzo 5572f345d8eSLuigi Rizzo 5582f345d8eSLuigi Rizzo static int 5592f345d8eSLuigi Rizzo oce_multiq_start(struct ifnet *ifp, struct mbuf *m) 5602f345d8eSLuigi Rizzo { 5612f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 5622f345d8eSLuigi Rizzo struct oce_wq *wq = NULL; 5632f345d8eSLuigi Rizzo int queue_index = 0; 5642f345d8eSLuigi Rizzo int status = 0; 5652f345d8eSLuigi Rizzo 566291a1934SXin LI if (!sc->link_status) 567291a1934SXin LI return ENXIO; 568291a1934SXin LI 5692f345d8eSLuigi Rizzo if ((m->m_flags & M_FLOWID) != 0) 5702f345d8eSLuigi Rizzo queue_index = m->m_pkthdr.flowid % sc->nwqs; 5712f345d8eSLuigi Rizzo 5722f345d8eSLuigi Rizzo wq = sc->wq[queue_index]; 5732f345d8eSLuigi Rizzo 574291a1934SXin LI LOCK(&wq->tx_lock); 5752f345d8eSLuigi Rizzo status = oce_multiq_transmit(ifp, m, wq); 5762f345d8eSLuigi Rizzo UNLOCK(&wq->tx_lock); 577291a1934SXin LI 5782f345d8eSLuigi Rizzo return status; 5792f345d8eSLuigi Rizzo 5802f345d8eSLuigi Rizzo } 5812f345d8eSLuigi Rizzo 5822f345d8eSLuigi Rizzo 5832f345d8eSLuigi Rizzo static void 5842f345d8eSLuigi Rizzo oce_multiq_flush(struct ifnet *ifp) 5852f345d8eSLuigi Rizzo { 5862f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 5872f345d8eSLuigi Rizzo struct mbuf *m; 5882f345d8eSLuigi Rizzo int i = 0; 5892f345d8eSLuigi Rizzo 5902f345d8eSLuigi Rizzo for (i = 0; i < sc->nwqs; i++) { 5912f345d8eSLuigi Rizzo while ((m = buf_ring_dequeue_sc(sc->wq[i]->br)) != NULL) 5922f345d8eSLuigi Rizzo m_freem(m); 5932f345d8eSLuigi Rizzo } 5942f345d8eSLuigi Rizzo if_qflush(ifp); 5952f345d8eSLuigi Rizzo } 5962f345d8eSLuigi Rizzo 5972f345d8eSLuigi Rizzo 5982f345d8eSLuigi Rizzo 5992f345d8eSLuigi Rizzo /***************************************************************************** 6002f345d8eSLuigi Rizzo * Driver interrupt routines functions * 6012f345d8eSLuigi Rizzo *****************************************************************************/ 6022f345d8eSLuigi Rizzo 6032f345d8eSLuigi Rizzo static void 6042f345d8eSLuigi Rizzo oce_intr(void *arg, int pending) 6052f345d8eSLuigi Rizzo { 6062f345d8eSLuigi Rizzo 6072f345d8eSLuigi Rizzo POCE_INTR_INFO ii = (POCE_INTR_INFO) arg; 6082f345d8eSLuigi Rizzo POCE_SOFTC sc = ii->sc; 6092f345d8eSLuigi Rizzo struct oce_eq *eq = ii->eq; 6102f345d8eSLuigi Rizzo struct oce_eqe *eqe; 6112f345d8eSLuigi Rizzo struct oce_cq *cq = NULL; 6122f345d8eSLuigi Rizzo int i, num_eqes = 0; 6132f345d8eSLuigi Rizzo 6142f345d8eSLuigi Rizzo 6152f345d8eSLuigi Rizzo bus_dmamap_sync(eq->ring->dma.tag, eq->ring->dma.map, 6162f345d8eSLuigi Rizzo BUS_DMASYNC_POSTWRITE); 6172f345d8eSLuigi Rizzo do { 6182f345d8eSLuigi Rizzo eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe); 6192f345d8eSLuigi Rizzo if (eqe->evnt == 0) 6202f345d8eSLuigi Rizzo break; 6212f345d8eSLuigi Rizzo eqe->evnt = 0; 6222f345d8eSLuigi Rizzo bus_dmamap_sync(eq->ring->dma.tag, eq->ring->dma.map, 6232f345d8eSLuigi Rizzo BUS_DMASYNC_POSTWRITE); 6242f345d8eSLuigi Rizzo RING_GET(eq->ring, 1); 6252f345d8eSLuigi Rizzo num_eqes++; 6262f345d8eSLuigi Rizzo 6272f345d8eSLuigi Rizzo } while (TRUE); 6282f345d8eSLuigi Rizzo 6292f345d8eSLuigi Rizzo if (!num_eqes) 6302f345d8eSLuigi Rizzo goto eq_arm; /* Spurious */ 6312f345d8eSLuigi Rizzo 6322f345d8eSLuigi Rizzo /* Clear EQ entries, but dont arm */ 6332f345d8eSLuigi Rizzo oce_arm_eq(sc, eq->eq_id, num_eqes, FALSE, FALSE); 6342f345d8eSLuigi Rizzo 6352f345d8eSLuigi Rizzo /* Process TX, RX and MCC. But dont arm CQ*/ 6362f345d8eSLuigi Rizzo for (i = 0; i < eq->cq_valid; i++) { 6372f345d8eSLuigi Rizzo cq = eq->cq[i]; 6382f345d8eSLuigi Rizzo (*cq->cq_handler)(cq->cb_arg); 6392f345d8eSLuigi Rizzo } 6402f345d8eSLuigi Rizzo 6412f345d8eSLuigi Rizzo /* Arm all cqs connected to this EQ */ 6422f345d8eSLuigi Rizzo for (i = 0; i < eq->cq_valid; i++) { 6432f345d8eSLuigi Rizzo cq = eq->cq[i]; 6442f345d8eSLuigi Rizzo oce_arm_cq(sc, cq->cq_id, 0, TRUE); 6452f345d8eSLuigi Rizzo } 6462f345d8eSLuigi Rizzo 6472f345d8eSLuigi Rizzo eq_arm: 6482f345d8eSLuigi Rizzo oce_arm_eq(sc, eq->eq_id, 0, TRUE, FALSE); 649cdaba892SXin LI 6502f345d8eSLuigi Rizzo return; 6512f345d8eSLuigi Rizzo } 6522f345d8eSLuigi Rizzo 6532f345d8eSLuigi Rizzo 6542f345d8eSLuigi Rizzo static int 6552f345d8eSLuigi Rizzo oce_setup_intr(POCE_SOFTC sc) 6562f345d8eSLuigi Rizzo { 6572f345d8eSLuigi Rizzo int rc = 0, use_intx = 0; 6582f345d8eSLuigi Rizzo int vector = 0, req_vectors = 0; 6592f345d8eSLuigi Rizzo 660291a1934SXin LI if (is_rss_enabled(sc)) 6612f345d8eSLuigi Rizzo req_vectors = MAX((sc->nrqs - 1), sc->nwqs); 6622f345d8eSLuigi Rizzo else 6632f345d8eSLuigi Rizzo req_vectors = 1; 6642f345d8eSLuigi Rizzo 6652f345d8eSLuigi Rizzo if (sc->flags & OCE_FLAGS_MSIX_CAPABLE) { 6662f345d8eSLuigi Rizzo sc->intr_count = req_vectors; 6672f345d8eSLuigi Rizzo rc = pci_alloc_msix(sc->dev, &sc->intr_count); 6682f345d8eSLuigi Rizzo if (rc != 0) { 6692f345d8eSLuigi Rizzo use_intx = 1; 6702f345d8eSLuigi Rizzo pci_release_msi(sc->dev); 6712f345d8eSLuigi Rizzo } else 6722f345d8eSLuigi Rizzo sc->flags |= OCE_FLAGS_USING_MSIX; 6732f345d8eSLuigi Rizzo } else 6742f345d8eSLuigi Rizzo use_intx = 1; 6752f345d8eSLuigi Rizzo 6762f345d8eSLuigi Rizzo if (use_intx) 6772f345d8eSLuigi Rizzo sc->intr_count = 1; 6782f345d8eSLuigi Rizzo 6792f345d8eSLuigi Rizzo /* Scale number of queues based on intr we got */ 6802f345d8eSLuigi Rizzo update_queues_got(sc); 6812f345d8eSLuigi Rizzo 6822f345d8eSLuigi Rizzo if (use_intx) { 6832f345d8eSLuigi Rizzo device_printf(sc->dev, "Using legacy interrupt\n"); 6842f345d8eSLuigi Rizzo rc = oce_alloc_intr(sc, vector, oce_intr); 6852f345d8eSLuigi Rizzo if (rc) 6862f345d8eSLuigi Rizzo goto error; 6872f345d8eSLuigi Rizzo } else { 6882f345d8eSLuigi Rizzo for (; vector < sc->intr_count; vector++) { 6892f345d8eSLuigi Rizzo rc = oce_alloc_intr(sc, vector, oce_intr); 6902f345d8eSLuigi Rizzo if (rc) 6912f345d8eSLuigi Rizzo goto error; 6922f345d8eSLuigi Rizzo } 6932f345d8eSLuigi Rizzo } 6942f345d8eSLuigi Rizzo 6952f345d8eSLuigi Rizzo return 0; 6962f345d8eSLuigi Rizzo error: 6972f345d8eSLuigi Rizzo oce_intr_free(sc); 6982f345d8eSLuigi Rizzo return rc; 6992f345d8eSLuigi Rizzo } 7002f345d8eSLuigi Rizzo 7012f345d8eSLuigi Rizzo 7022f345d8eSLuigi Rizzo static int 7032f345d8eSLuigi Rizzo oce_fast_isr(void *arg) 7042f345d8eSLuigi Rizzo { 7052f345d8eSLuigi Rizzo POCE_INTR_INFO ii = (POCE_INTR_INFO) arg; 7062f345d8eSLuigi Rizzo POCE_SOFTC sc = ii->sc; 7072f345d8eSLuigi Rizzo 7082f345d8eSLuigi Rizzo if (ii->eq == NULL) 7092f345d8eSLuigi Rizzo return FILTER_STRAY; 7102f345d8eSLuigi Rizzo 7112f345d8eSLuigi Rizzo oce_arm_eq(sc, ii->eq->eq_id, 0, FALSE, TRUE); 7122f345d8eSLuigi Rizzo 7132f345d8eSLuigi Rizzo taskqueue_enqueue_fast(ii->tq, &ii->task); 7142f345d8eSLuigi Rizzo 715cdaba892SXin LI ii->eq->intr++; 716cdaba892SXin LI 7172f345d8eSLuigi Rizzo return FILTER_HANDLED; 7182f345d8eSLuigi Rizzo } 7192f345d8eSLuigi Rizzo 7202f345d8eSLuigi Rizzo 7212f345d8eSLuigi Rizzo static int 7222f345d8eSLuigi Rizzo oce_alloc_intr(POCE_SOFTC sc, int vector, void (*isr) (void *arg, int pending)) 7232f345d8eSLuigi Rizzo { 7242f345d8eSLuigi Rizzo POCE_INTR_INFO ii = &sc->intrs[vector]; 7252f345d8eSLuigi Rizzo int rc = 0, rr; 7262f345d8eSLuigi Rizzo 7272f345d8eSLuigi Rizzo if (vector >= OCE_MAX_EQ) 7282f345d8eSLuigi Rizzo return (EINVAL); 7292f345d8eSLuigi Rizzo 7302f345d8eSLuigi Rizzo /* Set the resource id for the interrupt. 7312f345d8eSLuigi Rizzo * MSIx is vector + 1 for the resource id, 7322f345d8eSLuigi Rizzo * INTx is 0 for the resource id. 7332f345d8eSLuigi Rizzo */ 7342f345d8eSLuigi Rizzo if (sc->flags & OCE_FLAGS_USING_MSIX) 7352f345d8eSLuigi Rizzo rr = vector + 1; 7362f345d8eSLuigi Rizzo else 7372f345d8eSLuigi Rizzo rr = 0; 7382f345d8eSLuigi Rizzo ii->intr_res = bus_alloc_resource_any(sc->dev, 7392f345d8eSLuigi Rizzo SYS_RES_IRQ, 7402f345d8eSLuigi Rizzo &rr, RF_ACTIVE|RF_SHAREABLE); 7412f345d8eSLuigi Rizzo ii->irq_rr = rr; 7422f345d8eSLuigi Rizzo if (ii->intr_res == NULL) { 7432f345d8eSLuigi Rizzo device_printf(sc->dev, 7442f345d8eSLuigi Rizzo "Could not allocate interrupt\n"); 7452f345d8eSLuigi Rizzo rc = ENXIO; 7462f345d8eSLuigi Rizzo return rc; 7472f345d8eSLuigi Rizzo } 7482f345d8eSLuigi Rizzo 7492f345d8eSLuigi Rizzo TASK_INIT(&ii->task, 0, isr, ii); 7502f345d8eSLuigi Rizzo ii->vector = vector; 7512f345d8eSLuigi Rizzo sprintf(ii->task_name, "oce_task[%d]", ii->vector); 7522f345d8eSLuigi Rizzo ii->tq = taskqueue_create_fast(ii->task_name, 7532f345d8eSLuigi Rizzo M_NOWAIT, 7542f345d8eSLuigi Rizzo taskqueue_thread_enqueue, 7552f345d8eSLuigi Rizzo &ii->tq); 7562f345d8eSLuigi Rizzo taskqueue_start_threads(&ii->tq, 1, PI_NET, "%s taskq", 7572f345d8eSLuigi Rizzo device_get_nameunit(sc->dev)); 7582f345d8eSLuigi Rizzo 7592f345d8eSLuigi Rizzo ii->sc = sc; 7602f345d8eSLuigi Rizzo rc = bus_setup_intr(sc->dev, 7612f345d8eSLuigi Rizzo ii->intr_res, 7622f345d8eSLuigi Rizzo INTR_TYPE_NET, 7632f345d8eSLuigi Rizzo oce_fast_isr, NULL, ii, &ii->tag); 7642f345d8eSLuigi Rizzo return rc; 7652f345d8eSLuigi Rizzo 7662f345d8eSLuigi Rizzo } 7672f345d8eSLuigi Rizzo 7682f345d8eSLuigi Rizzo 7692f345d8eSLuigi Rizzo void 7702f345d8eSLuigi Rizzo oce_intr_free(POCE_SOFTC sc) 7712f345d8eSLuigi Rizzo { 7722f345d8eSLuigi Rizzo int i = 0; 7732f345d8eSLuigi Rizzo 7742f345d8eSLuigi Rizzo for (i = 0; i < sc->intr_count; i++) { 7752f345d8eSLuigi Rizzo 7762f345d8eSLuigi Rizzo if (sc->intrs[i].tag != NULL) 7772f345d8eSLuigi Rizzo bus_teardown_intr(sc->dev, sc->intrs[i].intr_res, 7782f345d8eSLuigi Rizzo sc->intrs[i].tag); 7792f345d8eSLuigi Rizzo if (sc->intrs[i].tq != NULL) 7802f345d8eSLuigi Rizzo taskqueue_free(sc->intrs[i].tq); 7812f345d8eSLuigi Rizzo 7822f345d8eSLuigi Rizzo if (sc->intrs[i].intr_res != NULL) 7832f345d8eSLuigi Rizzo bus_release_resource(sc->dev, SYS_RES_IRQ, 7842f345d8eSLuigi Rizzo sc->intrs[i].irq_rr, 7852f345d8eSLuigi Rizzo sc->intrs[i].intr_res); 7862f345d8eSLuigi Rizzo sc->intrs[i].tag = NULL; 7872f345d8eSLuigi Rizzo sc->intrs[i].intr_res = NULL; 7882f345d8eSLuigi Rizzo } 7892f345d8eSLuigi Rizzo 7902f345d8eSLuigi Rizzo if (sc->flags & OCE_FLAGS_USING_MSIX) 7912f345d8eSLuigi Rizzo pci_release_msi(sc->dev); 7922f345d8eSLuigi Rizzo 7932f345d8eSLuigi Rizzo } 7942f345d8eSLuigi Rizzo 7952f345d8eSLuigi Rizzo 7962f345d8eSLuigi Rizzo 7972f345d8eSLuigi Rizzo /****************************************************************************** 7982f345d8eSLuigi Rizzo * Media callbacks functions * 7992f345d8eSLuigi Rizzo ******************************************************************************/ 8002f345d8eSLuigi Rizzo 8012f345d8eSLuigi Rizzo static void 8022f345d8eSLuigi Rizzo oce_media_status(struct ifnet *ifp, struct ifmediareq *req) 8032f345d8eSLuigi Rizzo { 8042f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) ifp->if_softc; 8052f345d8eSLuigi Rizzo 8062f345d8eSLuigi Rizzo 8072f345d8eSLuigi Rizzo req->ifm_status = IFM_AVALID; 8082f345d8eSLuigi Rizzo req->ifm_active = IFM_ETHER; 8092f345d8eSLuigi Rizzo 8102f345d8eSLuigi Rizzo if (sc->link_status == 1) 8112f345d8eSLuigi Rizzo req->ifm_status |= IFM_ACTIVE; 8122f345d8eSLuigi Rizzo else 8132f345d8eSLuigi Rizzo return; 8142f345d8eSLuigi Rizzo 8152f345d8eSLuigi Rizzo switch (sc->link_speed) { 8162f345d8eSLuigi Rizzo case 1: /* 10 Mbps */ 8172f345d8eSLuigi Rizzo req->ifm_active |= IFM_10_T | IFM_FDX; 8182f345d8eSLuigi Rizzo sc->speed = 10; 8192f345d8eSLuigi Rizzo break; 8202f345d8eSLuigi Rizzo case 2: /* 100 Mbps */ 8212f345d8eSLuigi Rizzo req->ifm_active |= IFM_100_TX | IFM_FDX; 8222f345d8eSLuigi Rizzo sc->speed = 100; 8232f345d8eSLuigi Rizzo break; 8242f345d8eSLuigi Rizzo case 3: /* 1 Gbps */ 8252f345d8eSLuigi Rizzo req->ifm_active |= IFM_1000_T | IFM_FDX; 8262f345d8eSLuigi Rizzo sc->speed = 1000; 8272f345d8eSLuigi Rizzo break; 8282f345d8eSLuigi Rizzo case 4: /* 10 Gbps */ 8292f345d8eSLuigi Rizzo req->ifm_active |= IFM_10G_SR | IFM_FDX; 8302f345d8eSLuigi Rizzo sc->speed = 10000; 8312f345d8eSLuigi Rizzo break; 8322f345d8eSLuigi Rizzo } 8332f345d8eSLuigi Rizzo 8342f345d8eSLuigi Rizzo return; 8352f345d8eSLuigi Rizzo } 8362f345d8eSLuigi Rizzo 8372f345d8eSLuigi Rizzo 8382f345d8eSLuigi Rizzo int 8392f345d8eSLuigi Rizzo oce_media_change(struct ifnet *ifp) 8402f345d8eSLuigi Rizzo { 8412f345d8eSLuigi Rizzo return 0; 8422f345d8eSLuigi Rizzo } 8432f345d8eSLuigi Rizzo 8442f345d8eSLuigi Rizzo 8452f345d8eSLuigi Rizzo 8462f345d8eSLuigi Rizzo 8472f345d8eSLuigi Rizzo /***************************************************************************** 8482f345d8eSLuigi Rizzo * Transmit routines functions * 8492f345d8eSLuigi Rizzo *****************************************************************************/ 8502f345d8eSLuigi Rizzo 8512f345d8eSLuigi Rizzo static int 8522f345d8eSLuigi Rizzo oce_tx(POCE_SOFTC sc, struct mbuf **mpp, int wq_index) 8532f345d8eSLuigi Rizzo { 8542f345d8eSLuigi Rizzo int rc = 0, i, retry_cnt = 0; 8552f345d8eSLuigi Rizzo bus_dma_segment_t segs[OCE_MAX_TX_ELEMENTS]; 8562f345d8eSLuigi Rizzo struct mbuf *m, *m_temp; 8572f345d8eSLuigi Rizzo struct oce_wq *wq = sc->wq[wq_index]; 8582f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 8592f345d8eSLuigi Rizzo struct oce_nic_hdr_wqe *nichdr; 8602f345d8eSLuigi Rizzo struct oce_nic_frag_wqe *nicfrag; 8612f345d8eSLuigi Rizzo int num_wqes; 8622f345d8eSLuigi Rizzo uint32_t reg_value; 863cdaba892SXin LI boolean_t complete = TRUE; 8642f345d8eSLuigi Rizzo 8652f345d8eSLuigi Rizzo m = *mpp; 8662f345d8eSLuigi Rizzo if (!m) 8672f345d8eSLuigi Rizzo return EINVAL; 8682f345d8eSLuigi Rizzo 8692f345d8eSLuigi Rizzo if (!(m->m_flags & M_PKTHDR)) { 8702f345d8eSLuigi Rizzo rc = ENXIO; 8712f345d8eSLuigi Rizzo goto free_ret; 8722f345d8eSLuigi Rizzo } 8732f345d8eSLuigi Rizzo 874cdaba892SXin LI if(oce_tx_asic_stall_verify(sc, m)) { 875cdaba892SXin LI m = oce_insert_vlan_tag(sc, m, &complete); 876cdaba892SXin LI if(!m) { 877cdaba892SXin LI device_printf(sc->dev, "Insertion unsuccessful\n"); 878cdaba892SXin LI return 0; 879cdaba892SXin LI } 880cdaba892SXin LI 881cdaba892SXin LI } 882cdaba892SXin LI 8832f345d8eSLuigi Rizzo if (m->m_pkthdr.csum_flags & CSUM_TSO) { 8842f345d8eSLuigi Rizzo /* consolidate packet buffers for TSO/LSO segment offload */ 8859bd3250aSLuigi Rizzo #if defined(INET6) || defined(INET) 8869bd3250aSLuigi Rizzo m = oce_tso_setup(sc, mpp); 887ad512958SBjoern A. Zeeb #else 888ad512958SBjoern A. Zeeb m = NULL; 889ad512958SBjoern A. Zeeb #endif 8902f345d8eSLuigi Rizzo if (m == NULL) { 8912f345d8eSLuigi Rizzo rc = ENXIO; 8922f345d8eSLuigi Rizzo goto free_ret; 8932f345d8eSLuigi Rizzo } 8942f345d8eSLuigi Rizzo } 8952f345d8eSLuigi Rizzo 896291a1934SXin LI pd = &wq->pckts[wq->pkt_desc_head]; 8972f345d8eSLuigi Rizzo retry: 8982f345d8eSLuigi Rizzo rc = bus_dmamap_load_mbuf_sg(wq->tag, 8992f345d8eSLuigi Rizzo pd->map, 9002f345d8eSLuigi Rizzo m, segs, &pd->nsegs, BUS_DMA_NOWAIT); 9012f345d8eSLuigi Rizzo if (rc == 0) { 9022f345d8eSLuigi Rizzo num_wqes = pd->nsegs + 1; 903291a1934SXin LI if (IS_BE(sc) || IS_SH(sc)) { 9042f345d8eSLuigi Rizzo /*Dummy required only for BE3.*/ 9052f345d8eSLuigi Rizzo if (num_wqes & 1) 9062f345d8eSLuigi Rizzo num_wqes++; 9072f345d8eSLuigi Rizzo } 9082f345d8eSLuigi Rizzo if (num_wqes >= RING_NUM_FREE(wq->ring)) { 9092f345d8eSLuigi Rizzo bus_dmamap_unload(wq->tag, pd->map); 9102f345d8eSLuigi Rizzo return EBUSY; 9112f345d8eSLuigi Rizzo } 912291a1934SXin LI atomic_store_rel_int(&wq->pkt_desc_head, 913291a1934SXin LI (wq->pkt_desc_head + 1) % \ 914291a1934SXin LI OCE_WQ_PACKET_ARRAY_SIZE); 9152f345d8eSLuigi Rizzo bus_dmamap_sync(wq->tag, pd->map, BUS_DMASYNC_PREWRITE); 9162f345d8eSLuigi Rizzo pd->mbuf = m; 9172f345d8eSLuigi Rizzo 9182f345d8eSLuigi Rizzo nichdr = 9192f345d8eSLuigi Rizzo RING_GET_PRODUCER_ITEM_VA(wq->ring, struct oce_nic_hdr_wqe); 9202f345d8eSLuigi Rizzo nichdr->u0.dw[0] = 0; 9212f345d8eSLuigi Rizzo nichdr->u0.dw[1] = 0; 9222f345d8eSLuigi Rizzo nichdr->u0.dw[2] = 0; 9232f345d8eSLuigi Rizzo nichdr->u0.dw[3] = 0; 9242f345d8eSLuigi Rizzo 925cdaba892SXin LI nichdr->u0.s.complete = complete; 9262f345d8eSLuigi Rizzo nichdr->u0.s.event = 1; 9272f345d8eSLuigi Rizzo nichdr->u0.s.crc = 1; 9282f345d8eSLuigi Rizzo nichdr->u0.s.forward = 0; 9292f345d8eSLuigi Rizzo nichdr->u0.s.ipcs = (m->m_pkthdr.csum_flags & CSUM_IP) ? 1 : 0; 9302f345d8eSLuigi Rizzo nichdr->u0.s.udpcs = 9312f345d8eSLuigi Rizzo (m->m_pkthdr.csum_flags & CSUM_UDP) ? 1 : 0; 9322f345d8eSLuigi Rizzo nichdr->u0.s.tcpcs = 9332f345d8eSLuigi Rizzo (m->m_pkthdr.csum_flags & CSUM_TCP) ? 1 : 0; 9342f345d8eSLuigi Rizzo nichdr->u0.s.num_wqe = num_wqes; 9352f345d8eSLuigi Rizzo nichdr->u0.s.total_length = m->m_pkthdr.len; 936*5fbb6830SXin LI 9372f345d8eSLuigi Rizzo if (m->m_flags & M_VLANTAG) { 9382f345d8eSLuigi Rizzo nichdr->u0.s.vlan = 1; /*Vlan present*/ 9392f345d8eSLuigi Rizzo nichdr->u0.s.vlan_tag = m->m_pkthdr.ether_vtag; 9402f345d8eSLuigi Rizzo } 941*5fbb6830SXin LI 9422f345d8eSLuigi Rizzo if (m->m_pkthdr.csum_flags & CSUM_TSO) { 9432f345d8eSLuigi Rizzo if (m->m_pkthdr.tso_segsz) { 9442f345d8eSLuigi Rizzo nichdr->u0.s.lso = 1; 9452f345d8eSLuigi Rizzo nichdr->u0.s.lso_mss = m->m_pkthdr.tso_segsz; 9462f345d8eSLuigi Rizzo } 947291a1934SXin LI if (!IS_BE(sc) || !IS_SH(sc)) 9482f345d8eSLuigi Rizzo nichdr->u0.s.ipcs = 1; 9492f345d8eSLuigi Rizzo } 9502f345d8eSLuigi Rizzo 9512f345d8eSLuigi Rizzo RING_PUT(wq->ring, 1); 952291a1934SXin LI atomic_add_int(&wq->ring->num_used, 1); 9532f345d8eSLuigi Rizzo 9542f345d8eSLuigi Rizzo for (i = 0; i < pd->nsegs; i++) { 9552f345d8eSLuigi Rizzo nicfrag = 9562f345d8eSLuigi Rizzo RING_GET_PRODUCER_ITEM_VA(wq->ring, 9572f345d8eSLuigi Rizzo struct oce_nic_frag_wqe); 9582f345d8eSLuigi Rizzo nicfrag->u0.s.rsvd0 = 0; 9592f345d8eSLuigi Rizzo nicfrag->u0.s.frag_pa_hi = ADDR_HI(segs[i].ds_addr); 9602f345d8eSLuigi Rizzo nicfrag->u0.s.frag_pa_lo = ADDR_LO(segs[i].ds_addr); 9612f345d8eSLuigi Rizzo nicfrag->u0.s.frag_len = segs[i].ds_len; 9622f345d8eSLuigi Rizzo pd->wqe_idx = wq->ring->pidx; 9632f345d8eSLuigi Rizzo RING_PUT(wq->ring, 1); 964291a1934SXin LI atomic_add_int(&wq->ring->num_used, 1); 9652f345d8eSLuigi Rizzo } 9662f345d8eSLuigi Rizzo if (num_wqes > (pd->nsegs + 1)) { 9672f345d8eSLuigi Rizzo nicfrag = 9682f345d8eSLuigi Rizzo RING_GET_PRODUCER_ITEM_VA(wq->ring, 9692f345d8eSLuigi Rizzo struct oce_nic_frag_wqe); 9702f345d8eSLuigi Rizzo nicfrag->u0.dw[0] = 0; 9712f345d8eSLuigi Rizzo nicfrag->u0.dw[1] = 0; 9722f345d8eSLuigi Rizzo nicfrag->u0.dw[2] = 0; 9732f345d8eSLuigi Rizzo nicfrag->u0.dw[3] = 0; 9742f345d8eSLuigi Rizzo pd->wqe_idx = wq->ring->pidx; 9752f345d8eSLuigi Rizzo RING_PUT(wq->ring, 1); 976291a1934SXin LI atomic_add_int(&wq->ring->num_used, 1); 9772f345d8eSLuigi Rizzo pd->nsegs++; 9782f345d8eSLuigi Rizzo } 9792f345d8eSLuigi Rizzo 9802f345d8eSLuigi Rizzo sc->ifp->if_opackets++; 9812f345d8eSLuigi Rizzo wq->tx_stats.tx_reqs++; 9822f345d8eSLuigi Rizzo wq->tx_stats.tx_wrbs += num_wqes; 9832f345d8eSLuigi Rizzo wq->tx_stats.tx_bytes += m->m_pkthdr.len; 9842f345d8eSLuigi Rizzo wq->tx_stats.tx_pkts++; 9852f345d8eSLuigi Rizzo 9862f345d8eSLuigi Rizzo bus_dmamap_sync(wq->ring->dma.tag, wq->ring->dma.map, 9872f345d8eSLuigi Rizzo BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 9882f345d8eSLuigi Rizzo reg_value = (num_wqes << 16) | wq->wq_id; 989291a1934SXin LI OCE_WRITE_REG32(sc, db, wq->db_offset, reg_value); 9902f345d8eSLuigi Rizzo 9912f345d8eSLuigi Rizzo } else if (rc == EFBIG) { 9922f345d8eSLuigi Rizzo if (retry_cnt == 0) { 993c6499eccSGleb Smirnoff m_temp = m_defrag(m, M_NOWAIT); 9942f345d8eSLuigi Rizzo if (m_temp == NULL) 9952f345d8eSLuigi Rizzo goto free_ret; 9962f345d8eSLuigi Rizzo m = m_temp; 9972f345d8eSLuigi Rizzo *mpp = m_temp; 9982f345d8eSLuigi Rizzo retry_cnt = retry_cnt + 1; 9992f345d8eSLuigi Rizzo goto retry; 10002f345d8eSLuigi Rizzo } else 10012f345d8eSLuigi Rizzo goto free_ret; 10022f345d8eSLuigi Rizzo } else if (rc == ENOMEM) 10032f345d8eSLuigi Rizzo return rc; 10042f345d8eSLuigi Rizzo else 10052f345d8eSLuigi Rizzo goto free_ret; 10062f345d8eSLuigi Rizzo 10072f345d8eSLuigi Rizzo return 0; 10082f345d8eSLuigi Rizzo 10092f345d8eSLuigi Rizzo free_ret: 10102f345d8eSLuigi Rizzo m_freem(*mpp); 10112f345d8eSLuigi Rizzo *mpp = NULL; 10122f345d8eSLuigi Rizzo return rc; 10132f345d8eSLuigi Rizzo } 10142f345d8eSLuigi Rizzo 10152f345d8eSLuigi Rizzo 10162f345d8eSLuigi Rizzo static void 10172f345d8eSLuigi Rizzo oce_tx_complete(struct oce_wq *wq, uint32_t wqe_idx, uint32_t status) 10182f345d8eSLuigi Rizzo { 10192f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 10202f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) wq->parent; 10212f345d8eSLuigi Rizzo struct mbuf *m; 10222f345d8eSLuigi Rizzo 1023291a1934SXin LI pd = &wq->pckts[wq->pkt_desc_tail]; 1024291a1934SXin LI atomic_store_rel_int(&wq->pkt_desc_tail, 1025291a1934SXin LI (wq->pkt_desc_tail + 1) % OCE_WQ_PACKET_ARRAY_SIZE); 1026291a1934SXin LI atomic_subtract_int(&wq->ring->num_used, pd->nsegs + 1); 10272f345d8eSLuigi Rizzo bus_dmamap_sync(wq->tag, pd->map, BUS_DMASYNC_POSTWRITE); 10282f345d8eSLuigi Rizzo bus_dmamap_unload(wq->tag, pd->map); 10292f345d8eSLuigi Rizzo 10302f345d8eSLuigi Rizzo m = pd->mbuf; 10312f345d8eSLuigi Rizzo m_freem(m); 10322f345d8eSLuigi Rizzo pd->mbuf = NULL; 10332f345d8eSLuigi Rizzo 1034291a1934SXin LI 10352f345d8eSLuigi Rizzo if (sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) { 10362f345d8eSLuigi Rizzo if (wq->ring->num_used < (wq->ring->num_items / 2)) { 10372f345d8eSLuigi Rizzo sc->ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE); 10382f345d8eSLuigi Rizzo oce_tx_restart(sc, wq); 10392f345d8eSLuigi Rizzo } 10402f345d8eSLuigi Rizzo } 10412f345d8eSLuigi Rizzo } 10422f345d8eSLuigi Rizzo 10432f345d8eSLuigi Rizzo 10442f345d8eSLuigi Rizzo static void 10452f345d8eSLuigi Rizzo oce_tx_restart(POCE_SOFTC sc, struct oce_wq *wq) 10462f345d8eSLuigi Rizzo { 10472f345d8eSLuigi Rizzo 10482f345d8eSLuigi Rizzo if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) 10492f345d8eSLuigi Rizzo return; 10502f345d8eSLuigi Rizzo 10512f345d8eSLuigi Rizzo #if __FreeBSD_version >= 800000 10522f345d8eSLuigi Rizzo if (!drbr_empty(sc->ifp, wq->br)) 10532f345d8eSLuigi Rizzo #else 10542f345d8eSLuigi Rizzo if (!IFQ_DRV_IS_EMPTY(&sc->ifp->if_snd)) 10552f345d8eSLuigi Rizzo #endif 10562f345d8eSLuigi Rizzo taskqueue_enqueue_fast(taskqueue_swi, &wq->txtask); 10572f345d8eSLuigi Rizzo 10582f345d8eSLuigi Rizzo } 10592f345d8eSLuigi Rizzo 10609bd3250aSLuigi Rizzo 1061ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 10622f345d8eSLuigi Rizzo static struct mbuf * 10639bd3250aSLuigi Rizzo oce_tso_setup(POCE_SOFTC sc, struct mbuf **mpp) 10642f345d8eSLuigi Rizzo { 10652f345d8eSLuigi Rizzo struct mbuf *m; 1066ad512958SBjoern A. Zeeb #ifdef INET 10672f345d8eSLuigi Rizzo struct ip *ip; 1068ad512958SBjoern A. Zeeb #endif 1069ad512958SBjoern A. Zeeb #ifdef INET6 10702f345d8eSLuigi Rizzo struct ip6_hdr *ip6; 1071ad512958SBjoern A. Zeeb #endif 10722f345d8eSLuigi Rizzo struct ether_vlan_header *eh; 10732f345d8eSLuigi Rizzo struct tcphdr *th; 10742f345d8eSLuigi Rizzo uint16_t etype; 10759bd3250aSLuigi Rizzo int total_len = 0, ehdrlen = 0; 10762f345d8eSLuigi Rizzo 10772f345d8eSLuigi Rizzo m = *mpp; 10782f345d8eSLuigi Rizzo 10792f345d8eSLuigi Rizzo if (M_WRITABLE(m) == 0) { 1080c6499eccSGleb Smirnoff m = m_dup(*mpp, M_NOWAIT); 10812f345d8eSLuigi Rizzo if (!m) 10822f345d8eSLuigi Rizzo return NULL; 10832f345d8eSLuigi Rizzo m_freem(*mpp); 10842f345d8eSLuigi Rizzo *mpp = m; 10852f345d8eSLuigi Rizzo } 10862f345d8eSLuigi Rizzo 10872f345d8eSLuigi Rizzo eh = mtod(m, struct ether_vlan_header *); 10882f345d8eSLuigi Rizzo if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { 10892f345d8eSLuigi Rizzo etype = ntohs(eh->evl_proto); 10902f345d8eSLuigi Rizzo ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 10912f345d8eSLuigi Rizzo } else { 10922f345d8eSLuigi Rizzo etype = ntohs(eh->evl_encap_proto); 10932f345d8eSLuigi Rizzo ehdrlen = ETHER_HDR_LEN; 10942f345d8eSLuigi Rizzo } 10952f345d8eSLuigi Rizzo 10962f345d8eSLuigi Rizzo switch (etype) { 1097ad512958SBjoern A. Zeeb #ifdef INET 10982f345d8eSLuigi Rizzo case ETHERTYPE_IP: 10992f345d8eSLuigi Rizzo ip = (struct ip *)(m->m_data + ehdrlen); 11002f345d8eSLuigi Rizzo if (ip->ip_p != IPPROTO_TCP) 11012f345d8eSLuigi Rizzo return NULL; 11022f345d8eSLuigi Rizzo th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); 11032f345d8eSLuigi Rizzo 11042f345d8eSLuigi Rizzo total_len = ehdrlen + (ip->ip_hl << 2) + (th->th_off << 2); 11052f345d8eSLuigi Rizzo break; 1106ad512958SBjoern A. Zeeb #endif 1107ad512958SBjoern A. Zeeb #ifdef INET6 11082f345d8eSLuigi Rizzo case ETHERTYPE_IPV6: 11092f345d8eSLuigi Rizzo ip6 = (struct ip6_hdr *)(m->m_data + ehdrlen); 11102f345d8eSLuigi Rizzo if (ip6->ip6_nxt != IPPROTO_TCP) 11112f345d8eSLuigi Rizzo return NULL; 11122f345d8eSLuigi Rizzo th = (struct tcphdr *)((caddr_t)ip6 + sizeof(struct ip6_hdr)); 11132f345d8eSLuigi Rizzo 11142f345d8eSLuigi Rizzo total_len = ehdrlen + sizeof(struct ip6_hdr) + (th->th_off << 2); 11152f345d8eSLuigi Rizzo break; 1116ad512958SBjoern A. Zeeb #endif 11172f345d8eSLuigi Rizzo default: 11182f345d8eSLuigi Rizzo return NULL; 11192f345d8eSLuigi Rizzo } 11202f345d8eSLuigi Rizzo 11212f345d8eSLuigi Rizzo m = m_pullup(m, total_len); 11222f345d8eSLuigi Rizzo if (!m) 11232f345d8eSLuigi Rizzo return NULL; 11242f345d8eSLuigi Rizzo *mpp = m; 11252f345d8eSLuigi Rizzo return m; 11262f345d8eSLuigi Rizzo 11272f345d8eSLuigi Rizzo } 1128ad512958SBjoern A. Zeeb #endif /* INET6 || INET */ 11292f345d8eSLuigi Rizzo 11302f345d8eSLuigi Rizzo void 11312f345d8eSLuigi Rizzo oce_tx_task(void *arg, int npending) 11322f345d8eSLuigi Rizzo { 11332f345d8eSLuigi Rizzo struct oce_wq *wq = arg; 11342f345d8eSLuigi Rizzo POCE_SOFTC sc = wq->parent; 11352f345d8eSLuigi Rizzo struct ifnet *ifp = sc->ifp; 11362f345d8eSLuigi Rizzo int rc = 0; 11372f345d8eSLuigi Rizzo 11382f345d8eSLuigi Rizzo #if __FreeBSD_version >= 800000 1139291a1934SXin LI LOCK(&wq->tx_lock); 11402f345d8eSLuigi Rizzo rc = oce_multiq_transmit(ifp, NULL, wq); 11412f345d8eSLuigi Rizzo if (rc) { 11422f345d8eSLuigi Rizzo device_printf(sc->dev, 11432f345d8eSLuigi Rizzo "TX[%d] restart failed\n", wq->queue_index); 11442f345d8eSLuigi Rizzo } 11452f345d8eSLuigi Rizzo UNLOCK(&wq->tx_lock); 11462f345d8eSLuigi Rizzo #else 11472f345d8eSLuigi Rizzo oce_start(ifp); 11482f345d8eSLuigi Rizzo #endif 11492f345d8eSLuigi Rizzo 11502f345d8eSLuigi Rizzo } 11512f345d8eSLuigi Rizzo 11522f345d8eSLuigi Rizzo 11532f345d8eSLuigi Rizzo void 11542f345d8eSLuigi Rizzo oce_start(struct ifnet *ifp) 11552f345d8eSLuigi Rizzo { 11562f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 11572f345d8eSLuigi Rizzo struct mbuf *m; 11582f345d8eSLuigi Rizzo int rc = 0; 11599bd3250aSLuigi Rizzo int def_q = 0; /* Defualt tx queue is 0*/ 11602f345d8eSLuigi Rizzo 11612f345d8eSLuigi Rizzo if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 11622f345d8eSLuigi Rizzo IFF_DRV_RUNNING) 11632f345d8eSLuigi Rizzo return; 11642f345d8eSLuigi Rizzo 1165cdaba892SXin LI if (!sc->link_status) 1166cdaba892SXin LI return; 1167cdaba892SXin LI 11682f345d8eSLuigi Rizzo do { 11692f345d8eSLuigi Rizzo IF_DEQUEUE(&sc->ifp->if_snd, m); 11702f345d8eSLuigi Rizzo if (m == NULL) 11712f345d8eSLuigi Rizzo break; 11729bd3250aSLuigi Rizzo 11739bd3250aSLuigi Rizzo LOCK(&sc->wq[def_q]->tx_lock); 11749bd3250aSLuigi Rizzo rc = oce_tx(sc, &m, def_q); 11759bd3250aSLuigi Rizzo UNLOCK(&sc->wq[def_q]->tx_lock); 11762f345d8eSLuigi Rizzo if (rc) { 11772f345d8eSLuigi Rizzo if (m != NULL) { 11789bd3250aSLuigi Rizzo sc->wq[def_q]->tx_stats.tx_stops ++; 11792f345d8eSLuigi Rizzo ifp->if_drv_flags |= IFF_DRV_OACTIVE; 11802f345d8eSLuigi Rizzo IFQ_DRV_PREPEND(&ifp->if_snd, m); 11812f345d8eSLuigi Rizzo m = NULL; 11822f345d8eSLuigi Rizzo } 11832f345d8eSLuigi Rizzo break; 11842f345d8eSLuigi Rizzo } 11852f345d8eSLuigi Rizzo if (m != NULL) 11862f345d8eSLuigi Rizzo ETHER_BPF_MTAP(ifp, m); 11872f345d8eSLuigi Rizzo 11889bd3250aSLuigi Rizzo } while (TRUE); 11892f345d8eSLuigi Rizzo 11902f345d8eSLuigi Rizzo return; 11912f345d8eSLuigi Rizzo } 11922f345d8eSLuigi Rizzo 11932f345d8eSLuigi Rizzo 11942f345d8eSLuigi Rizzo /* Handle the Completion Queue for transmit */ 11952f345d8eSLuigi Rizzo uint16_t 11962f345d8eSLuigi Rizzo oce_wq_handler(void *arg) 11972f345d8eSLuigi Rizzo { 11982f345d8eSLuigi Rizzo struct oce_wq *wq = (struct oce_wq *)arg; 11992f345d8eSLuigi Rizzo POCE_SOFTC sc = wq->parent; 12002f345d8eSLuigi Rizzo struct oce_cq *cq = wq->cq; 12012f345d8eSLuigi Rizzo struct oce_nic_tx_cqe *cqe; 12022f345d8eSLuigi Rizzo int num_cqes = 0; 12032f345d8eSLuigi Rizzo 12042f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 12052f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 12062f345d8eSLuigi Rizzo cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_tx_cqe); 12072f345d8eSLuigi Rizzo while (cqe->u0.dw[3]) { 12082f345d8eSLuigi Rizzo DW_SWAP((uint32_t *) cqe, sizeof(oce_wq_cqe)); 12092f345d8eSLuigi Rizzo 12102f345d8eSLuigi Rizzo wq->ring->cidx = cqe->u0.s.wqe_index + 1; 12112f345d8eSLuigi Rizzo if (wq->ring->cidx >= wq->ring->num_items) 12122f345d8eSLuigi Rizzo wq->ring->cidx -= wq->ring->num_items; 12132f345d8eSLuigi Rizzo 12142f345d8eSLuigi Rizzo oce_tx_complete(wq, cqe->u0.s.wqe_index, cqe->u0.s.status); 12152f345d8eSLuigi Rizzo wq->tx_stats.tx_compl++; 12162f345d8eSLuigi Rizzo cqe->u0.dw[3] = 0; 12172f345d8eSLuigi Rizzo RING_GET(cq->ring, 1); 12182f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 12192f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 12202f345d8eSLuigi Rizzo cqe = 12212f345d8eSLuigi Rizzo RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_tx_cqe); 12222f345d8eSLuigi Rizzo num_cqes++; 12232f345d8eSLuigi Rizzo } 12242f345d8eSLuigi Rizzo 12252f345d8eSLuigi Rizzo if (num_cqes) 12262f345d8eSLuigi Rizzo oce_arm_cq(sc, cq->cq_id, num_cqes, FALSE); 12272f345d8eSLuigi Rizzo 12282f345d8eSLuigi Rizzo return 0; 12292f345d8eSLuigi Rizzo } 12302f345d8eSLuigi Rizzo 12312f345d8eSLuigi Rizzo 1232*5fbb6830SXin LI #if __FreeBSD_version >= 1000000 1233*5fbb6830SXin LI static __inline void 1234*5fbb6830SXin LI drbr_stats_update(struct ifnet *ifp, int len, int mflags) 1235*5fbb6830SXin LI { 1236*5fbb6830SXin LI #ifndef NO_SLOW_STATS 1237*5fbb6830SXin LI ifp->if_obytes += len; 1238*5fbb6830SXin LI if (mflags & M_MCAST) 1239*5fbb6830SXin LI ifp->if_omcasts++; 1240*5fbb6830SXin LI #endif 1241*5fbb6830SXin LI } 1242*5fbb6830SXin LI #endif 1243*5fbb6830SXin LI 12442f345d8eSLuigi Rizzo static int 12452f345d8eSLuigi Rizzo oce_multiq_transmit(struct ifnet *ifp, struct mbuf *m, struct oce_wq *wq) 12462f345d8eSLuigi Rizzo { 12472f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 12482f345d8eSLuigi Rizzo int status = 0, queue_index = 0; 12492f345d8eSLuigi Rizzo struct mbuf *next = NULL; 12502f345d8eSLuigi Rizzo struct buf_ring *br = NULL; 12512f345d8eSLuigi Rizzo 12522f345d8eSLuigi Rizzo br = wq->br; 12532f345d8eSLuigi Rizzo queue_index = wq->queue_index; 12542f345d8eSLuigi Rizzo 12552f345d8eSLuigi Rizzo if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 12562f345d8eSLuigi Rizzo IFF_DRV_RUNNING) { 12572f345d8eSLuigi Rizzo if (m != NULL) 12582f345d8eSLuigi Rizzo status = drbr_enqueue(ifp, br, m); 12592f345d8eSLuigi Rizzo return status; 12602f345d8eSLuigi Rizzo } 12612f345d8eSLuigi Rizzo 1262ded5ea6aSRandall Stewart if (m != NULL) { 12632f345d8eSLuigi Rizzo if ((status = drbr_enqueue(ifp, br, m)) != 0) 12642f345d8eSLuigi Rizzo return status; 1265ded5ea6aSRandall Stewart } 1266ded5ea6aSRandall Stewart while ((next = drbr_peek(ifp, br)) != NULL) { 12672f345d8eSLuigi Rizzo if (oce_tx(sc, &next, queue_index)) { 1268ded5ea6aSRandall Stewart if (next == NULL) { 1269ded5ea6aSRandall Stewart drbr_advance(ifp, br); 1270ded5ea6aSRandall Stewart } else { 1271ded5ea6aSRandall Stewart drbr_putback(ifp, br, next); 12722f345d8eSLuigi Rizzo wq->tx_stats.tx_stops ++; 12732f345d8eSLuigi Rizzo ifp->if_drv_flags |= IFF_DRV_OACTIVE; 12742f345d8eSLuigi Rizzo status = drbr_enqueue(ifp, br, next); 12752f345d8eSLuigi Rizzo } 12762f345d8eSLuigi Rizzo break; 12772f345d8eSLuigi Rizzo } 1278ded5ea6aSRandall Stewart drbr_advance(ifp, br); 1279063efed2SGleb Smirnoff ifp->if_obytes += next->m_pkthdr.len; 1280063efed2SGleb Smirnoff if (next->m_flags & M_MCAST) 1281063efed2SGleb Smirnoff ifp->if_omcasts++; 12822f345d8eSLuigi Rizzo ETHER_BPF_MTAP(ifp, next); 12832f345d8eSLuigi Rizzo } 12842f345d8eSLuigi Rizzo 12852f345d8eSLuigi Rizzo return status; 12862f345d8eSLuigi Rizzo } 12872f345d8eSLuigi Rizzo 12882f345d8eSLuigi Rizzo 12892f345d8eSLuigi Rizzo 12902f345d8eSLuigi Rizzo 12912f345d8eSLuigi Rizzo /***************************************************************************** 12922f345d8eSLuigi Rizzo * Receive routines functions * 12932f345d8eSLuigi Rizzo *****************************************************************************/ 12942f345d8eSLuigi Rizzo 12952f345d8eSLuigi Rizzo static void 12962f345d8eSLuigi Rizzo oce_rx(struct oce_rq *rq, uint32_t rqe_idx, struct oce_nic_rx_cqe *cqe) 12972f345d8eSLuigi Rizzo { 12982f345d8eSLuigi Rizzo uint32_t out; 12992f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 13002f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) rq->parent; 13012f345d8eSLuigi Rizzo int i, len, frag_len; 13022f345d8eSLuigi Rizzo struct mbuf *m = NULL, *tail = NULL; 13032f345d8eSLuigi Rizzo uint16_t vtag; 13042f345d8eSLuigi Rizzo 13052f345d8eSLuigi Rizzo len = cqe->u0.s.pkt_size; 13062f345d8eSLuigi Rizzo if (!len) { 13072f345d8eSLuigi Rizzo /*partial DMA workaround for Lancer*/ 13082f345d8eSLuigi Rizzo oce_discard_rx_comp(rq, cqe); 13092f345d8eSLuigi Rizzo goto exit; 13102f345d8eSLuigi Rizzo } 13112f345d8eSLuigi Rizzo 13129bd3250aSLuigi Rizzo /* Get vlan_tag value */ 1313291a1934SXin LI if(IS_BE(sc) || IS_SH(sc)) 13149bd3250aSLuigi Rizzo vtag = BSWAP_16(cqe->u0.s.vlan_tag); 13159bd3250aSLuigi Rizzo else 13169bd3250aSLuigi Rizzo vtag = cqe->u0.s.vlan_tag; 13179bd3250aSLuigi Rizzo 13189bd3250aSLuigi Rizzo 13192f345d8eSLuigi Rizzo for (i = 0; i < cqe->u0.s.num_fragments; i++) { 13202f345d8eSLuigi Rizzo 13212f345d8eSLuigi Rizzo if (rq->packets_out == rq->packets_in) { 13222f345d8eSLuigi Rizzo device_printf(sc->dev, 13232f345d8eSLuigi Rizzo "RQ transmit descriptor missing\n"); 13242f345d8eSLuigi Rizzo } 13252f345d8eSLuigi Rizzo out = rq->packets_out + 1; 13262f345d8eSLuigi Rizzo if (out == OCE_RQ_PACKET_ARRAY_SIZE) 13272f345d8eSLuigi Rizzo out = 0; 13282f345d8eSLuigi Rizzo pd = &rq->pckts[rq->packets_out]; 13292f345d8eSLuigi Rizzo rq->packets_out = out; 13302f345d8eSLuigi Rizzo 13312f345d8eSLuigi Rizzo bus_dmamap_sync(rq->tag, pd->map, BUS_DMASYNC_POSTWRITE); 13322f345d8eSLuigi Rizzo bus_dmamap_unload(rq->tag, pd->map); 13332f345d8eSLuigi Rizzo rq->pending--; 13342f345d8eSLuigi Rizzo 13352f345d8eSLuigi Rizzo frag_len = (len > rq->cfg.frag_size) ? rq->cfg.frag_size : len; 13362f345d8eSLuigi Rizzo pd->mbuf->m_len = frag_len; 13372f345d8eSLuigi Rizzo 13382f345d8eSLuigi Rizzo if (tail != NULL) { 13392f345d8eSLuigi Rizzo /* additional fragments */ 13402f345d8eSLuigi Rizzo pd->mbuf->m_flags &= ~M_PKTHDR; 13412f345d8eSLuigi Rizzo tail->m_next = pd->mbuf; 13422f345d8eSLuigi Rizzo tail = pd->mbuf; 13432f345d8eSLuigi Rizzo } else { 13442f345d8eSLuigi Rizzo /* first fragment, fill out much of the packet header */ 13452f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.len = len; 13462f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.csum_flags = 0; 13472f345d8eSLuigi Rizzo if (IF_CSUM_ENABLED(sc)) { 13482f345d8eSLuigi Rizzo if (cqe->u0.s.l4_cksum_pass) { 13492f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.csum_flags |= 13502f345d8eSLuigi Rizzo (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); 13512f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.csum_data = 0xffff; 13522f345d8eSLuigi Rizzo } 13532f345d8eSLuigi Rizzo if (cqe->u0.s.ip_cksum_pass) { 13549bd3250aSLuigi Rizzo if (!cqe->u0.s.ip_ver) { /* IPV4 */ 13552f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.csum_flags |= 13562f345d8eSLuigi Rizzo (CSUM_IP_CHECKED|CSUM_IP_VALID); 13572f345d8eSLuigi Rizzo } 13582f345d8eSLuigi Rizzo } 13592f345d8eSLuigi Rizzo } 13602f345d8eSLuigi Rizzo m = tail = pd->mbuf; 13612f345d8eSLuigi Rizzo } 13622f345d8eSLuigi Rizzo pd->mbuf = NULL; 13632f345d8eSLuigi Rizzo len -= frag_len; 13642f345d8eSLuigi Rizzo } 13652f345d8eSLuigi Rizzo 13662f345d8eSLuigi Rizzo if (m) { 13672f345d8eSLuigi Rizzo if (!oce_cqe_portid_valid(sc, cqe)) { 13682f345d8eSLuigi Rizzo m_freem(m); 13692f345d8eSLuigi Rizzo goto exit; 13702f345d8eSLuigi Rizzo } 13712f345d8eSLuigi Rizzo 13722f345d8eSLuigi Rizzo m->m_pkthdr.rcvif = sc->ifp; 13732f345d8eSLuigi Rizzo #if __FreeBSD_version >= 800000 1374291a1934SXin LI if (rq->queue_index) 1375291a1934SXin LI m->m_pkthdr.flowid = (rq->queue_index - 1); 1376291a1934SXin LI else 13772f345d8eSLuigi Rizzo m->m_pkthdr.flowid = rq->queue_index; 13782f345d8eSLuigi Rizzo m->m_flags |= M_FLOWID; 13792f345d8eSLuigi Rizzo #endif 13809bd3250aSLuigi Rizzo /* This deternies if vlan tag is Valid */ 13812f345d8eSLuigi Rizzo if (oce_cqe_vtp_valid(sc, cqe)) { 13822f345d8eSLuigi Rizzo if (sc->function_mode & FNM_FLEX10_MODE) { 13839bd3250aSLuigi Rizzo /* FLEX10. If QnQ is not set, neglect VLAN */ 13842f345d8eSLuigi Rizzo if (cqe->u0.s.qnq) { 13852f345d8eSLuigi Rizzo m->m_pkthdr.ether_vtag = vtag; 13862f345d8eSLuigi Rizzo m->m_flags |= M_VLANTAG; 13872f345d8eSLuigi Rizzo } 13889bd3250aSLuigi Rizzo } else if (sc->pvid != (vtag & VLAN_VID_MASK)) { 13899bd3250aSLuigi Rizzo /* In UMC mode generally pvid will be striped by 13909bd3250aSLuigi Rizzo hw. But in some cases we have seen it comes 13919bd3250aSLuigi Rizzo with pvid. So if pvid == vlan, neglect vlan. 13929bd3250aSLuigi Rizzo */ 13932f345d8eSLuigi Rizzo m->m_pkthdr.ether_vtag = vtag; 13942f345d8eSLuigi Rizzo m->m_flags |= M_VLANTAG; 13952f345d8eSLuigi Rizzo } 13962f345d8eSLuigi Rizzo } 13972f345d8eSLuigi Rizzo 13982f345d8eSLuigi Rizzo sc->ifp->if_ipackets++; 1399ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 14002f345d8eSLuigi Rizzo /* Try to queue to LRO */ 14012f345d8eSLuigi Rizzo if (IF_LRO_ENABLED(sc) && 14022f345d8eSLuigi Rizzo (cqe->u0.s.ip_cksum_pass) && 14032f345d8eSLuigi Rizzo (cqe->u0.s.l4_cksum_pass) && 14042f345d8eSLuigi Rizzo (!cqe->u0.s.ip_ver) && 14052f345d8eSLuigi Rizzo (rq->lro.lro_cnt != 0)) { 14062f345d8eSLuigi Rizzo 14072f345d8eSLuigi Rizzo if (tcp_lro_rx(&rq->lro, m, 0) == 0) { 14082f345d8eSLuigi Rizzo rq->lro_pkts_queued ++; 14092f345d8eSLuigi Rizzo goto post_done; 14102f345d8eSLuigi Rizzo } 14112f345d8eSLuigi Rizzo /* If LRO posting fails then try to post to STACK */ 14122f345d8eSLuigi Rizzo } 1413ad512958SBjoern A. Zeeb #endif 14142f345d8eSLuigi Rizzo 14152f345d8eSLuigi Rizzo (*sc->ifp->if_input) (sc->ifp, m); 1416ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 14172f345d8eSLuigi Rizzo post_done: 1418ad512958SBjoern A. Zeeb #endif 14192f345d8eSLuigi Rizzo /* Update rx stats per queue */ 14202f345d8eSLuigi Rizzo rq->rx_stats.rx_pkts++; 14212f345d8eSLuigi Rizzo rq->rx_stats.rx_bytes += cqe->u0.s.pkt_size; 14222f345d8eSLuigi Rizzo rq->rx_stats.rx_frags += cqe->u0.s.num_fragments; 14232f345d8eSLuigi Rizzo if (cqe->u0.s.pkt_type == OCE_MULTICAST_PACKET) 14242f345d8eSLuigi Rizzo rq->rx_stats.rx_mcast_pkts++; 14252f345d8eSLuigi Rizzo if (cqe->u0.s.pkt_type == OCE_UNICAST_PACKET) 14262f345d8eSLuigi Rizzo rq->rx_stats.rx_ucast_pkts++; 14272f345d8eSLuigi Rizzo } 14282f345d8eSLuigi Rizzo exit: 14292f345d8eSLuigi Rizzo return; 14302f345d8eSLuigi Rizzo } 14312f345d8eSLuigi Rizzo 14322f345d8eSLuigi Rizzo 14332f345d8eSLuigi Rizzo static void 14342f345d8eSLuigi Rizzo oce_discard_rx_comp(struct oce_rq *rq, struct oce_nic_rx_cqe *cqe) 14352f345d8eSLuigi Rizzo { 14362f345d8eSLuigi Rizzo uint32_t out, i = 0; 14372f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 14382f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) rq->parent; 14392f345d8eSLuigi Rizzo int num_frags = cqe->u0.s.num_fragments; 14402f345d8eSLuigi Rizzo 14412f345d8eSLuigi Rizzo for (i = 0; i < num_frags; i++) { 14422f345d8eSLuigi Rizzo if (rq->packets_out == rq->packets_in) { 14432f345d8eSLuigi Rizzo device_printf(sc->dev, 14442f345d8eSLuigi Rizzo "RQ transmit descriptor missing\n"); 14452f345d8eSLuigi Rizzo } 14462f345d8eSLuigi Rizzo out = rq->packets_out + 1; 14472f345d8eSLuigi Rizzo if (out == OCE_RQ_PACKET_ARRAY_SIZE) 14482f345d8eSLuigi Rizzo out = 0; 14492f345d8eSLuigi Rizzo pd = &rq->pckts[rq->packets_out]; 14502f345d8eSLuigi Rizzo rq->packets_out = out; 14512f345d8eSLuigi Rizzo 14522f345d8eSLuigi Rizzo bus_dmamap_sync(rq->tag, pd->map, BUS_DMASYNC_POSTWRITE); 14532f345d8eSLuigi Rizzo bus_dmamap_unload(rq->tag, pd->map); 14542f345d8eSLuigi Rizzo rq->pending--; 14552f345d8eSLuigi Rizzo m_freem(pd->mbuf); 14562f345d8eSLuigi Rizzo } 14572f345d8eSLuigi Rizzo 14582f345d8eSLuigi Rizzo } 14592f345d8eSLuigi Rizzo 14602f345d8eSLuigi Rizzo 14612f345d8eSLuigi Rizzo static int 14622f345d8eSLuigi Rizzo oce_cqe_vtp_valid(POCE_SOFTC sc, struct oce_nic_rx_cqe *cqe) 14632f345d8eSLuigi Rizzo { 14642f345d8eSLuigi Rizzo struct oce_nic_rx_cqe_v1 *cqe_v1; 14652f345d8eSLuigi Rizzo int vtp = 0; 14662f345d8eSLuigi Rizzo 14672f345d8eSLuigi Rizzo if (sc->be3_native) { 14682f345d8eSLuigi Rizzo cqe_v1 = (struct oce_nic_rx_cqe_v1 *)cqe; 14692f345d8eSLuigi Rizzo vtp = cqe_v1->u0.s.vlan_tag_present; 14709bd3250aSLuigi Rizzo } else 14712f345d8eSLuigi Rizzo vtp = cqe->u0.s.vlan_tag_present; 14722f345d8eSLuigi Rizzo 14732f345d8eSLuigi Rizzo return vtp; 14742f345d8eSLuigi Rizzo 14752f345d8eSLuigi Rizzo } 14762f345d8eSLuigi Rizzo 14772f345d8eSLuigi Rizzo 14782f345d8eSLuigi Rizzo static int 14792f345d8eSLuigi Rizzo oce_cqe_portid_valid(POCE_SOFTC sc, struct oce_nic_rx_cqe *cqe) 14802f345d8eSLuigi Rizzo { 14812f345d8eSLuigi Rizzo struct oce_nic_rx_cqe_v1 *cqe_v1; 14822f345d8eSLuigi Rizzo int port_id = 0; 14832f345d8eSLuigi Rizzo 1484291a1934SXin LI if (sc->be3_native && (IS_BE(sc) || IS_SH(sc))) { 14852f345d8eSLuigi Rizzo cqe_v1 = (struct oce_nic_rx_cqe_v1 *)cqe; 14862f345d8eSLuigi Rizzo port_id = cqe_v1->u0.s.port; 14872f345d8eSLuigi Rizzo if (sc->port_id != port_id) 14882f345d8eSLuigi Rizzo return 0; 14892f345d8eSLuigi Rizzo } else 14902f345d8eSLuigi Rizzo ;/* For BE3 legacy and Lancer this is dummy */ 14912f345d8eSLuigi Rizzo 14922f345d8eSLuigi Rizzo return 1; 14932f345d8eSLuigi Rizzo 14942f345d8eSLuigi Rizzo } 14952f345d8eSLuigi Rizzo 1496ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 14972f345d8eSLuigi Rizzo static void 14982f345d8eSLuigi Rizzo oce_rx_flush_lro(struct oce_rq *rq) 14992f345d8eSLuigi Rizzo { 15002f345d8eSLuigi Rizzo struct lro_ctrl *lro = &rq->lro; 15012f345d8eSLuigi Rizzo struct lro_entry *queued; 15022f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) rq->parent; 15032f345d8eSLuigi Rizzo 15042f345d8eSLuigi Rizzo if (!IF_LRO_ENABLED(sc)) 15052f345d8eSLuigi Rizzo return; 15062f345d8eSLuigi Rizzo 15072f345d8eSLuigi Rizzo while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { 15082f345d8eSLuigi Rizzo SLIST_REMOVE_HEAD(&lro->lro_active, next); 15092f345d8eSLuigi Rizzo tcp_lro_flush(lro, queued); 15102f345d8eSLuigi Rizzo } 15112f345d8eSLuigi Rizzo rq->lro_pkts_queued = 0; 15122f345d8eSLuigi Rizzo 15132f345d8eSLuigi Rizzo return; 15142f345d8eSLuigi Rizzo } 15152f345d8eSLuigi Rizzo 15162f345d8eSLuigi Rizzo 15172f345d8eSLuigi Rizzo static int 15182f345d8eSLuigi Rizzo oce_init_lro(POCE_SOFTC sc) 15192f345d8eSLuigi Rizzo { 15202f345d8eSLuigi Rizzo struct lro_ctrl *lro = NULL; 15212f345d8eSLuigi Rizzo int i = 0, rc = 0; 15222f345d8eSLuigi Rizzo 15232f345d8eSLuigi Rizzo for (i = 0; i < sc->nrqs; i++) { 15242f345d8eSLuigi Rizzo lro = &sc->rq[i]->lro; 15252f345d8eSLuigi Rizzo rc = tcp_lro_init(lro); 15262f345d8eSLuigi Rizzo if (rc != 0) { 15272f345d8eSLuigi Rizzo device_printf(sc->dev, "LRO init failed\n"); 15282f345d8eSLuigi Rizzo return rc; 15292f345d8eSLuigi Rizzo } 15302f345d8eSLuigi Rizzo lro->ifp = sc->ifp; 15312f345d8eSLuigi Rizzo } 15322f345d8eSLuigi Rizzo 15332f345d8eSLuigi Rizzo return rc; 15342f345d8eSLuigi Rizzo } 15359bd3250aSLuigi Rizzo 15362f345d8eSLuigi Rizzo 15372f345d8eSLuigi Rizzo void 15382f345d8eSLuigi Rizzo oce_free_lro(POCE_SOFTC sc) 15392f345d8eSLuigi Rizzo { 15402f345d8eSLuigi Rizzo struct lro_ctrl *lro = NULL; 15412f345d8eSLuigi Rizzo int i = 0; 15422f345d8eSLuigi Rizzo 15432f345d8eSLuigi Rizzo for (i = 0; i < sc->nrqs; i++) { 15442f345d8eSLuigi Rizzo lro = &sc->rq[i]->lro; 15452f345d8eSLuigi Rizzo if (lro) 15462f345d8eSLuigi Rizzo tcp_lro_free(lro); 15472f345d8eSLuigi Rizzo } 15482f345d8eSLuigi Rizzo } 1549cdaba892SXin LI #endif 15502f345d8eSLuigi Rizzo 15512f345d8eSLuigi Rizzo int 15522f345d8eSLuigi Rizzo oce_alloc_rx_bufs(struct oce_rq *rq, int count) 15532f345d8eSLuigi Rizzo { 15542f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) rq->parent; 15552f345d8eSLuigi Rizzo int i, in, rc; 15562f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 15572f345d8eSLuigi Rizzo bus_dma_segment_t segs[6]; 15582f345d8eSLuigi Rizzo int nsegs, added = 0; 15592f345d8eSLuigi Rizzo struct oce_nic_rqe *rqe; 15602f345d8eSLuigi Rizzo pd_rxulp_db_t rxdb_reg; 15612f345d8eSLuigi Rizzo 1562cdaba892SXin LI bzero(&rxdb_reg, sizeof(pd_rxulp_db_t)); 15632f345d8eSLuigi Rizzo for (i = 0; i < count; i++) { 15642f345d8eSLuigi Rizzo in = rq->packets_in + 1; 15652f345d8eSLuigi Rizzo if (in == OCE_RQ_PACKET_ARRAY_SIZE) 15662f345d8eSLuigi Rizzo in = 0; 15672f345d8eSLuigi Rizzo if (in == rq->packets_out) 15682f345d8eSLuigi Rizzo break; /* no more room */ 15692f345d8eSLuigi Rizzo 15702f345d8eSLuigi Rizzo pd = &rq->pckts[rq->packets_in]; 1571c6499eccSGleb Smirnoff pd->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 15722f345d8eSLuigi Rizzo if (pd->mbuf == NULL) 15732f345d8eSLuigi Rizzo break; 15742f345d8eSLuigi Rizzo 15752f345d8eSLuigi Rizzo pd->mbuf->m_len = pd->mbuf->m_pkthdr.len = MCLBYTES; 15762f345d8eSLuigi Rizzo rc = bus_dmamap_load_mbuf_sg(rq->tag, 15772f345d8eSLuigi Rizzo pd->map, 15782f345d8eSLuigi Rizzo pd->mbuf, 15792f345d8eSLuigi Rizzo segs, &nsegs, BUS_DMA_NOWAIT); 15802f345d8eSLuigi Rizzo if (rc) { 15812f345d8eSLuigi Rizzo m_free(pd->mbuf); 15822f345d8eSLuigi Rizzo break; 15832f345d8eSLuigi Rizzo } 15842f345d8eSLuigi Rizzo 15852f345d8eSLuigi Rizzo if (nsegs != 1) { 15862f345d8eSLuigi Rizzo i--; 15872f345d8eSLuigi Rizzo continue; 15882f345d8eSLuigi Rizzo } 15892f345d8eSLuigi Rizzo 15902f345d8eSLuigi Rizzo rq->packets_in = in; 15912f345d8eSLuigi Rizzo bus_dmamap_sync(rq->tag, pd->map, BUS_DMASYNC_PREREAD); 15922f345d8eSLuigi Rizzo 15932f345d8eSLuigi Rizzo rqe = RING_GET_PRODUCER_ITEM_VA(rq->ring, struct oce_nic_rqe); 15942f345d8eSLuigi Rizzo rqe->u0.s.frag_pa_hi = ADDR_HI(segs[0].ds_addr); 15952f345d8eSLuigi Rizzo rqe->u0.s.frag_pa_lo = ADDR_LO(segs[0].ds_addr); 15962f345d8eSLuigi Rizzo DW_SWAP(u32ptr(rqe), sizeof(struct oce_nic_rqe)); 15972f345d8eSLuigi Rizzo RING_PUT(rq->ring, 1); 15982f345d8eSLuigi Rizzo added++; 15992f345d8eSLuigi Rizzo rq->pending++; 16002f345d8eSLuigi Rizzo } 16012f345d8eSLuigi Rizzo if (added != 0) { 16022f345d8eSLuigi Rizzo for (i = added / OCE_MAX_RQ_POSTS; i > 0; i--) { 16032f345d8eSLuigi Rizzo rxdb_reg.bits.num_posted = OCE_MAX_RQ_POSTS; 16042f345d8eSLuigi Rizzo rxdb_reg.bits.qid = rq->rq_id; 16052f345d8eSLuigi Rizzo OCE_WRITE_REG32(sc, db, PD_RXULP_DB, rxdb_reg.dw0); 16062f345d8eSLuigi Rizzo added -= OCE_MAX_RQ_POSTS; 16072f345d8eSLuigi Rizzo } 16082f345d8eSLuigi Rizzo if (added > 0) { 16092f345d8eSLuigi Rizzo rxdb_reg.bits.qid = rq->rq_id; 16102f345d8eSLuigi Rizzo rxdb_reg.bits.num_posted = added; 16112f345d8eSLuigi Rizzo OCE_WRITE_REG32(sc, db, PD_RXULP_DB, rxdb_reg.dw0); 16122f345d8eSLuigi Rizzo } 16132f345d8eSLuigi Rizzo } 16142f345d8eSLuigi Rizzo 16152f345d8eSLuigi Rizzo return 0; 16162f345d8eSLuigi Rizzo } 16172f345d8eSLuigi Rizzo 16182f345d8eSLuigi Rizzo 16192f345d8eSLuigi Rizzo /* Handle the Completion Queue for receive */ 16202f345d8eSLuigi Rizzo uint16_t 16212f345d8eSLuigi Rizzo oce_rq_handler(void *arg) 16222f345d8eSLuigi Rizzo { 16232f345d8eSLuigi Rizzo struct oce_rq *rq = (struct oce_rq *)arg; 16242f345d8eSLuigi Rizzo struct oce_cq *cq = rq->cq; 16252f345d8eSLuigi Rizzo POCE_SOFTC sc = rq->parent; 16262f345d8eSLuigi Rizzo struct oce_nic_rx_cqe *cqe; 16272f345d8eSLuigi Rizzo int num_cqes = 0, rq_buffers_used = 0; 16282f345d8eSLuigi Rizzo 16292f345d8eSLuigi Rizzo 16302f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 16312f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 16322f345d8eSLuigi Rizzo cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_rx_cqe); 16332f345d8eSLuigi Rizzo while (cqe->u0.dw[2]) { 16342f345d8eSLuigi Rizzo DW_SWAP((uint32_t *) cqe, sizeof(oce_rq_cqe)); 16352f345d8eSLuigi Rizzo 16362f345d8eSLuigi Rizzo RING_GET(rq->ring, 1); 16372f345d8eSLuigi Rizzo if (cqe->u0.s.error == 0) { 16382f345d8eSLuigi Rizzo oce_rx(rq, cqe->u0.s.frag_index, cqe); 16392f345d8eSLuigi Rizzo } else { 16402f345d8eSLuigi Rizzo rq->rx_stats.rxcp_err++; 16412f345d8eSLuigi Rizzo sc->ifp->if_ierrors++; 16422f345d8eSLuigi Rizzo /* Post L3/L4 errors to stack.*/ 16432f345d8eSLuigi Rizzo oce_rx(rq, cqe->u0.s.frag_index, cqe); 16442f345d8eSLuigi Rizzo } 16452f345d8eSLuigi Rizzo rq->rx_stats.rx_compl++; 16462f345d8eSLuigi Rizzo cqe->u0.dw[2] = 0; 16472f345d8eSLuigi Rizzo 1648ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 16492f345d8eSLuigi Rizzo if (IF_LRO_ENABLED(sc) && rq->lro_pkts_queued >= 16) { 16502f345d8eSLuigi Rizzo oce_rx_flush_lro(rq); 16512f345d8eSLuigi Rizzo } 1652ad512958SBjoern A. Zeeb #endif 16532f345d8eSLuigi Rizzo 16542f345d8eSLuigi Rizzo RING_GET(cq->ring, 1); 16552f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 16562f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 16572f345d8eSLuigi Rizzo cqe = 16582f345d8eSLuigi Rizzo RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_rx_cqe); 16592f345d8eSLuigi Rizzo num_cqes++; 16602f345d8eSLuigi Rizzo if (num_cqes >= (IS_XE201(sc) ? 8 : oce_max_rsp_handled)) 16612f345d8eSLuigi Rizzo break; 16622f345d8eSLuigi Rizzo } 16639bd3250aSLuigi Rizzo 1664ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 16652f345d8eSLuigi Rizzo if (IF_LRO_ENABLED(sc)) 16662f345d8eSLuigi Rizzo oce_rx_flush_lro(rq); 1667ad512958SBjoern A. Zeeb #endif 16682f345d8eSLuigi Rizzo 16692f345d8eSLuigi Rizzo if (num_cqes) { 16702f345d8eSLuigi Rizzo oce_arm_cq(sc, cq->cq_id, num_cqes, FALSE); 16712f345d8eSLuigi Rizzo rq_buffers_used = OCE_RQ_PACKET_ARRAY_SIZE - rq->pending; 16722f345d8eSLuigi Rizzo if (rq_buffers_used > 1) 16732f345d8eSLuigi Rizzo oce_alloc_rx_bufs(rq, (rq_buffers_used - 1)); 16742f345d8eSLuigi Rizzo } 16752f345d8eSLuigi Rizzo 16762f345d8eSLuigi Rizzo return 0; 16772f345d8eSLuigi Rizzo 16782f345d8eSLuigi Rizzo } 16792f345d8eSLuigi Rizzo 16802f345d8eSLuigi Rizzo 16812f345d8eSLuigi Rizzo 16822f345d8eSLuigi Rizzo 16832f345d8eSLuigi Rizzo /***************************************************************************** 16842f345d8eSLuigi Rizzo * Helper function prototypes in this file * 16852f345d8eSLuigi Rizzo *****************************************************************************/ 16862f345d8eSLuigi Rizzo 16872f345d8eSLuigi Rizzo static int 16882f345d8eSLuigi Rizzo oce_attach_ifp(POCE_SOFTC sc) 16892f345d8eSLuigi Rizzo { 16902f345d8eSLuigi Rizzo 16912f345d8eSLuigi Rizzo sc->ifp = if_alloc(IFT_ETHER); 16922f345d8eSLuigi Rizzo if (!sc->ifp) 16932f345d8eSLuigi Rizzo return ENOMEM; 16942f345d8eSLuigi Rizzo 16952f345d8eSLuigi Rizzo ifmedia_init(&sc->media, IFM_IMASK, oce_media_change, oce_media_status); 16962f345d8eSLuigi Rizzo ifmedia_add(&sc->media, IFM_ETHER | IFM_AUTO, 0, NULL); 16972f345d8eSLuigi Rizzo ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO); 16982f345d8eSLuigi Rizzo 16992f345d8eSLuigi Rizzo sc->ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST; 17002f345d8eSLuigi Rizzo sc->ifp->if_ioctl = oce_ioctl; 17012f345d8eSLuigi Rizzo sc->ifp->if_start = oce_start; 17022f345d8eSLuigi Rizzo sc->ifp->if_init = oce_init; 17032f345d8eSLuigi Rizzo sc->ifp->if_mtu = ETHERMTU; 17042f345d8eSLuigi Rizzo sc->ifp->if_softc = sc; 17052f345d8eSLuigi Rizzo #if __FreeBSD_version >= 800000 17062f345d8eSLuigi Rizzo sc->ifp->if_transmit = oce_multiq_start; 17072f345d8eSLuigi Rizzo sc->ifp->if_qflush = oce_multiq_flush; 17082f345d8eSLuigi Rizzo #endif 17092f345d8eSLuigi Rizzo 17102f345d8eSLuigi Rizzo if_initname(sc->ifp, 17112f345d8eSLuigi Rizzo device_get_name(sc->dev), device_get_unit(sc->dev)); 17122f345d8eSLuigi Rizzo 17132f345d8eSLuigi Rizzo sc->ifp->if_snd.ifq_drv_maxlen = OCE_MAX_TX_DESC - 1; 17142f345d8eSLuigi Rizzo IFQ_SET_MAXLEN(&sc->ifp->if_snd, sc->ifp->if_snd.ifq_drv_maxlen); 17152f345d8eSLuigi Rizzo IFQ_SET_READY(&sc->ifp->if_snd); 17162f345d8eSLuigi Rizzo 17172f345d8eSLuigi Rizzo sc->ifp->if_hwassist = OCE_IF_HWASSIST; 17182f345d8eSLuigi Rizzo sc->ifp->if_hwassist |= CSUM_TSO; 17192f345d8eSLuigi Rizzo sc->ifp->if_hwassist |= (CSUM_IP | CSUM_TCP | CSUM_UDP); 17202f345d8eSLuigi Rizzo 17212f345d8eSLuigi Rizzo sc->ifp->if_capabilities = OCE_IF_CAPABILITIES; 17222f345d8eSLuigi Rizzo sc->ifp->if_capabilities |= IFCAP_HWCSUM; 17232f345d8eSLuigi Rizzo sc->ifp->if_capabilities |= IFCAP_VLAN_HWFILTER; 17249bd3250aSLuigi Rizzo 1725ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 1726ad512958SBjoern A. Zeeb sc->ifp->if_capabilities |= IFCAP_TSO; 17272f345d8eSLuigi Rizzo sc->ifp->if_capabilities |= IFCAP_LRO; 17289bd3250aSLuigi Rizzo sc->ifp->if_capabilities |= IFCAP_VLAN_HWTSO; 1729ad512958SBjoern A. Zeeb #endif 17302f345d8eSLuigi Rizzo 17312f345d8eSLuigi Rizzo sc->ifp->if_capenable = sc->ifp->if_capabilities; 17326d9190b4SJohn Baldwin if_initbaudrate(sc->ifp, IF_Gbps(10)); 17332f345d8eSLuigi Rizzo 1734*5fbb6830SXin LI #if __FreeBSD_version >= 1000000 1735*5fbb6830SXin LI sc->ifp->if_hw_tsomax = OCE_MAX_TSO_SIZE; 1736*5fbb6830SXin LI #endif 1737*5fbb6830SXin LI 17382f345d8eSLuigi Rizzo ether_ifattach(sc->ifp, sc->macaddr.mac_addr); 17392f345d8eSLuigi Rizzo 17402f345d8eSLuigi Rizzo return 0; 17412f345d8eSLuigi Rizzo } 17422f345d8eSLuigi Rizzo 17432f345d8eSLuigi Rizzo 17442f345d8eSLuigi Rizzo static void 17452f345d8eSLuigi Rizzo oce_add_vlan(void *arg, struct ifnet *ifp, uint16_t vtag) 17462f345d8eSLuigi Rizzo { 17472f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 17482f345d8eSLuigi Rizzo 17492f345d8eSLuigi Rizzo if (ifp->if_softc != arg) 17502f345d8eSLuigi Rizzo return; 17512f345d8eSLuigi Rizzo if ((vtag == 0) || (vtag > 4095)) 17522f345d8eSLuigi Rizzo return; 17532f345d8eSLuigi Rizzo 17542f345d8eSLuigi Rizzo sc->vlan_tag[vtag] = 1; 17552f345d8eSLuigi Rizzo sc->vlans_added++; 1756*5fbb6830SXin LI if (sc->vlans_added <= (sc->max_vlans + 1)) 17572f345d8eSLuigi Rizzo oce_vid_config(sc); 17582f345d8eSLuigi Rizzo } 17592f345d8eSLuigi Rizzo 17602f345d8eSLuigi Rizzo 17612f345d8eSLuigi Rizzo static void 17622f345d8eSLuigi Rizzo oce_del_vlan(void *arg, struct ifnet *ifp, uint16_t vtag) 17632f345d8eSLuigi Rizzo { 17642f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 17652f345d8eSLuigi Rizzo 17662f345d8eSLuigi Rizzo if (ifp->if_softc != arg) 17672f345d8eSLuigi Rizzo return; 17682f345d8eSLuigi Rizzo if ((vtag == 0) || (vtag > 4095)) 17692f345d8eSLuigi Rizzo return; 17702f345d8eSLuigi Rizzo 17712f345d8eSLuigi Rizzo sc->vlan_tag[vtag] = 0; 17722f345d8eSLuigi Rizzo sc->vlans_added--; 17732f345d8eSLuigi Rizzo oce_vid_config(sc); 17742f345d8eSLuigi Rizzo } 17752f345d8eSLuigi Rizzo 17762f345d8eSLuigi Rizzo 17772f345d8eSLuigi Rizzo /* 17782f345d8eSLuigi Rizzo * A max of 64 vlans can be configured in BE. If the user configures 17792f345d8eSLuigi Rizzo * more, place the card in vlan promiscuous mode. 17802f345d8eSLuigi Rizzo */ 17812f345d8eSLuigi Rizzo static int 17822f345d8eSLuigi Rizzo oce_vid_config(POCE_SOFTC sc) 17832f345d8eSLuigi Rizzo { 17842f345d8eSLuigi Rizzo struct normal_vlan vtags[MAX_VLANFILTER_SIZE]; 17852f345d8eSLuigi Rizzo uint16_t ntags = 0, i; 17862f345d8eSLuigi Rizzo int status = 0; 17872f345d8eSLuigi Rizzo 17882f345d8eSLuigi Rizzo if ((sc->vlans_added <= MAX_VLANFILTER_SIZE) && 17892f345d8eSLuigi Rizzo (sc->ifp->if_capenable & IFCAP_VLAN_HWFILTER)) { 17902f345d8eSLuigi Rizzo for (i = 0; i < MAX_VLANS; i++) { 17912f345d8eSLuigi Rizzo if (sc->vlan_tag[i]) { 17922f345d8eSLuigi Rizzo vtags[ntags].vtag = i; 17932f345d8eSLuigi Rizzo ntags++; 17942f345d8eSLuigi Rizzo } 17952f345d8eSLuigi Rizzo } 17962f345d8eSLuigi Rizzo if (ntags) 17972f345d8eSLuigi Rizzo status = oce_config_vlan(sc, (uint8_t) sc->if_id, 17982f345d8eSLuigi Rizzo vtags, ntags, 1, 0); 17992f345d8eSLuigi Rizzo } else 18002f345d8eSLuigi Rizzo status = oce_config_vlan(sc, (uint8_t) sc->if_id, 18012f345d8eSLuigi Rizzo NULL, 0, 1, 1); 18022f345d8eSLuigi Rizzo return status; 18032f345d8eSLuigi Rizzo } 18042f345d8eSLuigi Rizzo 18052f345d8eSLuigi Rizzo 18062f345d8eSLuigi Rizzo static void 18072f345d8eSLuigi Rizzo oce_mac_addr_set(POCE_SOFTC sc) 18082f345d8eSLuigi Rizzo { 18092f345d8eSLuigi Rizzo uint32_t old_pmac_id = sc->pmac_id; 18102f345d8eSLuigi Rizzo int status = 0; 18112f345d8eSLuigi Rizzo 18122f345d8eSLuigi Rizzo 18132f345d8eSLuigi Rizzo status = bcmp((IF_LLADDR(sc->ifp)), sc->macaddr.mac_addr, 18142f345d8eSLuigi Rizzo sc->macaddr.size_of_struct); 18152f345d8eSLuigi Rizzo if (!status) 18162f345d8eSLuigi Rizzo return; 18172f345d8eSLuigi Rizzo 18182f345d8eSLuigi Rizzo status = oce_mbox_macaddr_add(sc, (uint8_t *)(IF_LLADDR(sc->ifp)), 18192f345d8eSLuigi Rizzo sc->if_id, &sc->pmac_id); 18202f345d8eSLuigi Rizzo if (!status) { 18212f345d8eSLuigi Rizzo status = oce_mbox_macaddr_del(sc, sc->if_id, old_pmac_id); 18222f345d8eSLuigi Rizzo bcopy((IF_LLADDR(sc->ifp)), sc->macaddr.mac_addr, 18232f345d8eSLuigi Rizzo sc->macaddr.size_of_struct); 18242f345d8eSLuigi Rizzo } 18252f345d8eSLuigi Rizzo if (status) 18262f345d8eSLuigi Rizzo device_printf(sc->dev, "Failed update macaddress\n"); 18272f345d8eSLuigi Rizzo 18282f345d8eSLuigi Rizzo } 18292f345d8eSLuigi Rizzo 18302f345d8eSLuigi Rizzo 18312f345d8eSLuigi Rizzo static int 18322f345d8eSLuigi Rizzo oce_handle_passthrough(struct ifnet *ifp, caddr_t data) 18332f345d8eSLuigi Rizzo { 18342f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 18352f345d8eSLuigi Rizzo struct ifreq *ifr = (struct ifreq *)data; 18362f345d8eSLuigi Rizzo int rc = ENXIO; 18372f345d8eSLuigi Rizzo char cookie[32] = {0}; 18382f345d8eSLuigi Rizzo void *priv_data = (void *)ifr->ifr_data; 18392f345d8eSLuigi Rizzo void *ioctl_ptr; 18402f345d8eSLuigi Rizzo uint32_t req_size; 18412f345d8eSLuigi Rizzo struct mbx_hdr req; 18422f345d8eSLuigi Rizzo OCE_DMA_MEM dma_mem; 1843cdaba892SXin LI struct mbx_common_get_cntl_attr *fw_cmd; 18442f345d8eSLuigi Rizzo 18452f345d8eSLuigi Rizzo if (copyin(priv_data, cookie, strlen(IOCTL_COOKIE))) 18462f345d8eSLuigi Rizzo return EFAULT; 18472f345d8eSLuigi Rizzo 18482f345d8eSLuigi Rizzo if (memcmp(cookie, IOCTL_COOKIE, strlen(IOCTL_COOKIE))) 18492f345d8eSLuigi Rizzo return EINVAL; 18502f345d8eSLuigi Rizzo 18512f345d8eSLuigi Rizzo ioctl_ptr = (char *)priv_data + strlen(IOCTL_COOKIE); 18522f345d8eSLuigi Rizzo if (copyin(ioctl_ptr, &req, sizeof(struct mbx_hdr))) 18532f345d8eSLuigi Rizzo return EFAULT; 18542f345d8eSLuigi Rizzo 18552f345d8eSLuigi Rizzo req_size = le32toh(req.u0.req.request_length); 18562f345d8eSLuigi Rizzo if (req_size > 65536) 18572f345d8eSLuigi Rizzo return EINVAL; 18582f345d8eSLuigi Rizzo 18592f345d8eSLuigi Rizzo req_size += sizeof(struct mbx_hdr); 18602f345d8eSLuigi Rizzo rc = oce_dma_alloc(sc, req_size, &dma_mem, 0); 18612f345d8eSLuigi Rizzo if (rc) 18622f345d8eSLuigi Rizzo return ENOMEM; 18632f345d8eSLuigi Rizzo 18642f345d8eSLuigi Rizzo if (copyin(ioctl_ptr, OCE_DMAPTR(&dma_mem,char), req_size)) { 18652f345d8eSLuigi Rizzo rc = EFAULT; 18662f345d8eSLuigi Rizzo goto dma_free; 18672f345d8eSLuigi Rizzo } 18682f345d8eSLuigi Rizzo 18692f345d8eSLuigi Rizzo rc = oce_pass_through_mbox(sc, &dma_mem, req_size); 18702f345d8eSLuigi Rizzo if (rc) { 18712f345d8eSLuigi Rizzo rc = EIO; 18722f345d8eSLuigi Rizzo goto dma_free; 18732f345d8eSLuigi Rizzo } 18742f345d8eSLuigi Rizzo 18752f345d8eSLuigi Rizzo if (copyout(OCE_DMAPTR(&dma_mem,char), ioctl_ptr, req_size)) 18762f345d8eSLuigi Rizzo rc = EFAULT; 18772f345d8eSLuigi Rizzo 1878cdaba892SXin LI /* 1879cdaba892SXin LI firmware is filling all the attributes for this ioctl except 1880cdaba892SXin LI the driver version..so fill it 1881cdaba892SXin LI */ 1882cdaba892SXin LI if(req.u0.rsp.opcode == OPCODE_COMMON_GET_CNTL_ATTRIBUTES) { 1883cdaba892SXin LI fw_cmd = (struct mbx_common_get_cntl_attr *) ioctl_ptr; 1884cdaba892SXin LI strncpy(fw_cmd->params.rsp.cntl_attr_info.hba_attr.drv_ver_str, 1885cdaba892SXin LI COMPONENT_REVISION, strlen(COMPONENT_REVISION)); 1886cdaba892SXin LI } 1887cdaba892SXin LI 18882f345d8eSLuigi Rizzo dma_free: 18892f345d8eSLuigi Rizzo oce_dma_free(sc, &dma_mem); 18902f345d8eSLuigi Rizzo return rc; 18912f345d8eSLuigi Rizzo 18922f345d8eSLuigi Rizzo } 18932f345d8eSLuigi Rizzo 1894cdaba892SXin LI static void 1895cdaba892SXin LI oce_eqd_set_periodic(POCE_SOFTC sc) 1896cdaba892SXin LI { 1897cdaba892SXin LI struct oce_set_eqd set_eqd[OCE_MAX_EQ]; 1898cdaba892SXin LI struct oce_aic_obj *aic; 1899cdaba892SXin LI struct oce_eq *eqo; 1900cdaba892SXin LI uint64_t now = 0, delta; 1901cdaba892SXin LI int eqd, i, num = 0; 1902cdaba892SXin LI uint32_t ips = 0; 1903cdaba892SXin LI int tps; 1904cdaba892SXin LI 1905cdaba892SXin LI for (i = 0 ; i < sc->neqs; i++) { 1906cdaba892SXin LI eqo = sc->eq[i]; 1907cdaba892SXin LI aic = &sc->aic_obj[i]; 1908cdaba892SXin LI /* When setting the static eq delay from the user space */ 1909cdaba892SXin LI if (!aic->enable) { 1910cdaba892SXin LI eqd = aic->et_eqd; 1911cdaba892SXin LI goto modify_eqd; 1912cdaba892SXin LI } 1913cdaba892SXin LI 1914cdaba892SXin LI now = ticks; 1915cdaba892SXin LI 1916cdaba892SXin LI /* Over flow check */ 1917cdaba892SXin LI if ((now < aic->ticks) || (eqo->intr < aic->intr_prev)) 1918cdaba892SXin LI goto done; 1919cdaba892SXin LI 1920cdaba892SXin LI delta = now - aic->ticks; 1921cdaba892SXin LI tps = delta/hz; 1922cdaba892SXin LI 1923cdaba892SXin LI /* Interrupt rate based on elapsed ticks */ 1924cdaba892SXin LI if(tps) 1925cdaba892SXin LI ips = (uint32_t)(eqo->intr - aic->intr_prev) / tps; 1926cdaba892SXin LI 1927cdaba892SXin LI if (ips > INTR_RATE_HWM) 1928cdaba892SXin LI eqd = aic->cur_eqd + 20; 1929cdaba892SXin LI else if (ips < INTR_RATE_LWM) 1930cdaba892SXin LI eqd = aic->cur_eqd / 2; 1931cdaba892SXin LI else 1932cdaba892SXin LI goto done; 1933cdaba892SXin LI 1934cdaba892SXin LI if (eqd < 10) 1935cdaba892SXin LI eqd = 0; 1936cdaba892SXin LI 1937cdaba892SXin LI /* Make sure that the eq delay is in the known range */ 1938cdaba892SXin LI eqd = min(eqd, aic->max_eqd); 1939cdaba892SXin LI eqd = max(eqd, aic->min_eqd); 1940cdaba892SXin LI 1941cdaba892SXin LI modify_eqd: 1942cdaba892SXin LI if (eqd != aic->cur_eqd) { 1943cdaba892SXin LI set_eqd[num].delay_multiplier = (eqd * 65)/100; 1944cdaba892SXin LI set_eqd[num].eq_id = eqo->eq_id; 1945cdaba892SXin LI aic->cur_eqd = eqd; 1946cdaba892SXin LI num++; 1947cdaba892SXin LI } 1948cdaba892SXin LI done: 1949cdaba892SXin LI aic->intr_prev = eqo->intr; 1950cdaba892SXin LI aic->ticks = now; 1951cdaba892SXin LI } 1952cdaba892SXin LI 1953cdaba892SXin LI /* Is there atleast one eq that needs to be modified? */ 1954cdaba892SXin LI if(num) 1955cdaba892SXin LI oce_mbox_eqd_modify_periodic(sc, set_eqd, num); 1956cdaba892SXin LI 1957cdaba892SXin LI } 19582f345d8eSLuigi Rizzo 1959*5fbb6830SXin LI static void oce_detect_hw_error(POCE_SOFTC sc) 1960*5fbb6830SXin LI { 1961*5fbb6830SXin LI 1962*5fbb6830SXin LI uint32_t ue_low = 0, ue_high = 0, ue_low_mask = 0, ue_high_mask = 0; 1963*5fbb6830SXin LI uint32_t sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0; 1964*5fbb6830SXin LI uint32_t i; 1965*5fbb6830SXin LI 1966*5fbb6830SXin LI if (sc->hw_error) 1967*5fbb6830SXin LI return; 1968*5fbb6830SXin LI 1969*5fbb6830SXin LI if (IS_XE201(sc)) { 1970*5fbb6830SXin LI sliport_status = OCE_READ_REG32(sc, db, SLIPORT_STATUS_OFFSET); 1971*5fbb6830SXin LI if (sliport_status & SLIPORT_STATUS_ERR_MASK) { 1972*5fbb6830SXin LI sliport_err1 = OCE_READ_REG32(sc, db, SLIPORT_ERROR1_OFFSET); 1973*5fbb6830SXin LI sliport_err2 = OCE_READ_REG32(sc, db, SLIPORT_ERROR2_OFFSET); 1974*5fbb6830SXin LI } 1975*5fbb6830SXin LI } else { 1976*5fbb6830SXin LI ue_low = OCE_READ_REG32(sc, devcfg, PCICFG_UE_STATUS_LOW); 1977*5fbb6830SXin LI ue_high = OCE_READ_REG32(sc, devcfg, PCICFG_UE_STATUS_HIGH); 1978*5fbb6830SXin LI ue_low_mask = OCE_READ_REG32(sc, devcfg, PCICFG_UE_STATUS_LOW_MASK); 1979*5fbb6830SXin LI ue_high_mask = OCE_READ_REG32(sc, devcfg, PCICFG_UE_STATUS_HI_MASK); 1980*5fbb6830SXin LI 1981*5fbb6830SXin LI ue_low = (ue_low & ~ue_low_mask); 1982*5fbb6830SXin LI ue_high = (ue_high & ~ue_high_mask); 1983*5fbb6830SXin LI } 1984*5fbb6830SXin LI 1985*5fbb6830SXin LI /* On certain platforms BE hardware can indicate spurious UEs. 1986*5fbb6830SXin LI * Allow the h/w to stop working completely in case of a real UE. 1987*5fbb6830SXin LI * Hence not setting the hw_error for UE detection. 1988*5fbb6830SXin LI */ 1989*5fbb6830SXin LI if (sliport_status & SLIPORT_STATUS_ERR_MASK) { 1990*5fbb6830SXin LI sc->hw_error = TRUE; 1991*5fbb6830SXin LI device_printf(sc->dev, "Error detected in the card\n"); 1992*5fbb6830SXin LI } 1993*5fbb6830SXin LI 1994*5fbb6830SXin LI if (sliport_status & SLIPORT_STATUS_ERR_MASK) { 1995*5fbb6830SXin LI device_printf(sc->dev, 1996*5fbb6830SXin LI "ERR: sliport status 0x%x\n", sliport_status); 1997*5fbb6830SXin LI device_printf(sc->dev, 1998*5fbb6830SXin LI "ERR: sliport error1 0x%x\n", sliport_err1); 1999*5fbb6830SXin LI device_printf(sc->dev, 2000*5fbb6830SXin LI "ERR: sliport error2 0x%x\n", sliport_err2); 2001*5fbb6830SXin LI } 2002*5fbb6830SXin LI 2003*5fbb6830SXin LI if (ue_low) { 2004*5fbb6830SXin LI for (i = 0; ue_low; ue_low >>= 1, i++) { 2005*5fbb6830SXin LI if (ue_low & 1) 2006*5fbb6830SXin LI device_printf(sc->dev, "UE: %s bit set\n", 2007*5fbb6830SXin LI ue_status_low_desc[i]); 2008*5fbb6830SXin LI } 2009*5fbb6830SXin LI } 2010*5fbb6830SXin LI 2011*5fbb6830SXin LI if (ue_high) { 2012*5fbb6830SXin LI for (i = 0; ue_high; ue_high >>= 1, i++) { 2013*5fbb6830SXin LI if (ue_high & 1) 2014*5fbb6830SXin LI device_printf(sc->dev, "UE: %s bit set\n", 2015*5fbb6830SXin LI ue_status_hi_desc[i]); 2016*5fbb6830SXin LI } 2017*5fbb6830SXin LI } 2018*5fbb6830SXin LI 2019*5fbb6830SXin LI } 2020*5fbb6830SXin LI 2021*5fbb6830SXin LI 20222f345d8eSLuigi Rizzo static void 20232f345d8eSLuigi Rizzo oce_local_timer(void *arg) 20242f345d8eSLuigi Rizzo { 20252f345d8eSLuigi Rizzo POCE_SOFTC sc = arg; 20262f345d8eSLuigi Rizzo int i = 0; 20272f345d8eSLuigi Rizzo 2028*5fbb6830SXin LI oce_detect_hw_error(sc); 20292f345d8eSLuigi Rizzo oce_refresh_nic_stats(sc); 20302f345d8eSLuigi Rizzo oce_refresh_queue_stats(sc); 20312f345d8eSLuigi Rizzo oce_mac_addr_set(sc); 20322f345d8eSLuigi Rizzo 20332f345d8eSLuigi Rizzo /* TX Watch Dog*/ 20342f345d8eSLuigi Rizzo for (i = 0; i < sc->nwqs; i++) 20352f345d8eSLuigi Rizzo oce_tx_restart(sc, sc->wq[i]); 20362f345d8eSLuigi Rizzo 2037cdaba892SXin LI /* calculate and set the eq delay for optimal interrupt rate */ 2038291a1934SXin LI if (IS_BE(sc) || IS_SH(sc)) 2039cdaba892SXin LI oce_eqd_set_periodic(sc); 2040cdaba892SXin LI 20412f345d8eSLuigi Rizzo callout_reset(&sc->timer, hz, oce_local_timer, sc); 20422f345d8eSLuigi Rizzo } 20432f345d8eSLuigi Rizzo 20442f345d8eSLuigi Rizzo 2045beb0f7e7SJosh Paetzel /* NOTE : This should only be called holding 2046beb0f7e7SJosh Paetzel * DEVICE_LOCK. 2047beb0f7e7SJosh Paetzel */ 20482f345d8eSLuigi Rizzo static void 20492f345d8eSLuigi Rizzo oce_if_deactivate(POCE_SOFTC sc) 20502f345d8eSLuigi Rizzo { 20512f345d8eSLuigi Rizzo int i, mtime = 0; 20522f345d8eSLuigi Rizzo int wait_req = 0; 20532f345d8eSLuigi Rizzo struct oce_rq *rq; 20542f345d8eSLuigi Rizzo struct oce_wq *wq; 20552f345d8eSLuigi Rizzo struct oce_eq *eq; 20562f345d8eSLuigi Rizzo 20572f345d8eSLuigi Rizzo sc->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 20582f345d8eSLuigi Rizzo 20592f345d8eSLuigi Rizzo /*Wait for max of 400ms for TX completions to be done */ 20602f345d8eSLuigi Rizzo while (mtime < 400) { 20612f345d8eSLuigi Rizzo wait_req = 0; 20622f345d8eSLuigi Rizzo for_all_wq_queues(sc, wq, i) { 20632f345d8eSLuigi Rizzo if (wq->ring->num_used) { 20642f345d8eSLuigi Rizzo wait_req = 1; 20652f345d8eSLuigi Rizzo DELAY(1); 20662f345d8eSLuigi Rizzo break; 20672f345d8eSLuigi Rizzo } 20682f345d8eSLuigi Rizzo } 20692f345d8eSLuigi Rizzo mtime += 1; 20702f345d8eSLuigi Rizzo if (!wait_req) 20712f345d8eSLuigi Rizzo break; 20722f345d8eSLuigi Rizzo } 20732f345d8eSLuigi Rizzo 20742f345d8eSLuigi Rizzo /* Stop intrs and finish any bottom halves pending */ 20752f345d8eSLuigi Rizzo oce_hw_intr_disable(sc); 20762f345d8eSLuigi Rizzo 2077cdaba892SXin LI /* Since taskqueue_drain takes a Gaint Lock, We should not acquire 2078beb0f7e7SJosh Paetzel any other lock. So unlock device lock and require after 2079beb0f7e7SJosh Paetzel completing taskqueue_drain. 2080beb0f7e7SJosh Paetzel */ 2081beb0f7e7SJosh Paetzel UNLOCK(&sc->dev_lock); 20822f345d8eSLuigi Rizzo for (i = 0; i < sc->intr_count; i++) { 20832f345d8eSLuigi Rizzo if (sc->intrs[i].tq != NULL) { 20842f345d8eSLuigi Rizzo taskqueue_drain(sc->intrs[i].tq, &sc->intrs[i].task); 20852f345d8eSLuigi Rizzo } 20862f345d8eSLuigi Rizzo } 2087beb0f7e7SJosh Paetzel LOCK(&sc->dev_lock); 20882f345d8eSLuigi Rizzo 20892f345d8eSLuigi Rizzo /* Delete RX queue in card with flush param */ 20902f345d8eSLuigi Rizzo oce_stop_rx(sc); 20912f345d8eSLuigi Rizzo 20922f345d8eSLuigi Rizzo /* Invalidate any pending cq and eq entries*/ 20932f345d8eSLuigi Rizzo for_all_evnt_queues(sc, eq, i) 20942f345d8eSLuigi Rizzo oce_drain_eq(eq); 20952f345d8eSLuigi Rizzo for_all_rq_queues(sc, rq, i) 20962f345d8eSLuigi Rizzo oce_drain_rq_cq(rq); 20972f345d8eSLuigi Rizzo for_all_wq_queues(sc, wq, i) 20982f345d8eSLuigi Rizzo oce_drain_wq_cq(wq); 20992f345d8eSLuigi Rizzo 21002f345d8eSLuigi Rizzo /* But still we need to get MCC aync events. 21012f345d8eSLuigi Rizzo So enable intrs and also arm first EQ 21022f345d8eSLuigi Rizzo */ 21032f345d8eSLuigi Rizzo oce_hw_intr_enable(sc); 21042f345d8eSLuigi Rizzo oce_arm_eq(sc, sc->eq[0]->eq_id, 0, TRUE, FALSE); 21052f345d8eSLuigi Rizzo 21062f345d8eSLuigi Rizzo DELAY(10); 21072f345d8eSLuigi Rizzo } 21082f345d8eSLuigi Rizzo 21092f345d8eSLuigi Rizzo 21102f345d8eSLuigi Rizzo static void 21112f345d8eSLuigi Rizzo oce_if_activate(POCE_SOFTC sc) 21122f345d8eSLuigi Rizzo { 21132f345d8eSLuigi Rizzo struct oce_eq *eq; 21142f345d8eSLuigi Rizzo struct oce_rq *rq; 21152f345d8eSLuigi Rizzo struct oce_wq *wq; 21162f345d8eSLuigi Rizzo int i, rc = 0; 21172f345d8eSLuigi Rizzo 21182f345d8eSLuigi Rizzo sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 21192f345d8eSLuigi Rizzo 21202f345d8eSLuigi Rizzo oce_hw_intr_disable(sc); 21212f345d8eSLuigi Rizzo 21222f345d8eSLuigi Rizzo oce_start_rx(sc); 21232f345d8eSLuigi Rizzo 21242f345d8eSLuigi Rizzo for_all_rq_queues(sc, rq, i) { 21252f345d8eSLuigi Rizzo rc = oce_start_rq(rq); 21262f345d8eSLuigi Rizzo if (rc) 21272f345d8eSLuigi Rizzo device_printf(sc->dev, "Unable to start RX\n"); 21282f345d8eSLuigi Rizzo } 21292f345d8eSLuigi Rizzo 21302f345d8eSLuigi Rizzo for_all_wq_queues(sc, wq, i) { 21312f345d8eSLuigi Rizzo rc = oce_start_wq(wq); 21322f345d8eSLuigi Rizzo if (rc) 21332f345d8eSLuigi Rizzo device_printf(sc->dev, "Unable to start TX\n"); 21342f345d8eSLuigi Rizzo } 21352f345d8eSLuigi Rizzo 21362f345d8eSLuigi Rizzo 21372f345d8eSLuigi Rizzo for_all_evnt_queues(sc, eq, i) 21382f345d8eSLuigi Rizzo oce_arm_eq(sc, eq->eq_id, 0, TRUE, FALSE); 21392f345d8eSLuigi Rizzo 21402f345d8eSLuigi Rizzo oce_hw_intr_enable(sc); 21412f345d8eSLuigi Rizzo 21422f345d8eSLuigi Rizzo } 21432f345d8eSLuigi Rizzo 21449bd3250aSLuigi Rizzo static void 21459bd3250aSLuigi Rizzo process_link_state(POCE_SOFTC sc, struct oce_async_cqe_link_state *acqe) 21462f345d8eSLuigi Rizzo { 21479bd3250aSLuigi Rizzo /* Update Link status */ 21482f345d8eSLuigi Rizzo if ((acqe->u0.s.link_status & ~ASYNC_EVENT_LOGICAL) == 21492f345d8eSLuigi Rizzo ASYNC_EVENT_LINK_UP) { 21502f345d8eSLuigi Rizzo sc->link_status = ASYNC_EVENT_LINK_UP; 21512f345d8eSLuigi Rizzo if_link_state_change(sc->ifp, LINK_STATE_UP); 21522f345d8eSLuigi Rizzo } else { 21532f345d8eSLuigi Rizzo sc->link_status = ASYNC_EVENT_LINK_DOWN; 21542f345d8eSLuigi Rizzo if_link_state_change(sc->ifp, LINK_STATE_DOWN); 21552f345d8eSLuigi Rizzo } 21562f345d8eSLuigi Rizzo 21579bd3250aSLuigi Rizzo /* Update speed */ 21582f345d8eSLuigi Rizzo sc->link_speed = acqe->u0.s.speed; 21599bd3250aSLuigi Rizzo sc->qos_link_speed = (uint32_t) acqe->u0.s.qos_link_speed * 10; 21609bd3250aSLuigi Rizzo 21619bd3250aSLuigi Rizzo } 21629bd3250aSLuigi Rizzo 21639bd3250aSLuigi Rizzo 21649bd3250aSLuigi Rizzo /* Handle the Completion Queue for the Mailbox/Async notifications */ 21659bd3250aSLuigi Rizzo uint16_t 21669bd3250aSLuigi Rizzo oce_mq_handler(void *arg) 21679bd3250aSLuigi Rizzo { 21689bd3250aSLuigi Rizzo struct oce_mq *mq = (struct oce_mq *)arg; 21699bd3250aSLuigi Rizzo POCE_SOFTC sc = mq->parent; 21709bd3250aSLuigi Rizzo struct oce_cq *cq = mq->cq; 21719bd3250aSLuigi Rizzo int num_cqes = 0, evt_type = 0, optype = 0; 21729bd3250aSLuigi Rizzo struct oce_mq_cqe *cqe; 21739bd3250aSLuigi Rizzo struct oce_async_cqe_link_state *acqe; 21749bd3250aSLuigi Rizzo struct oce_async_event_grp5_pvid_state *gcqe; 2175cdaba892SXin LI struct oce_async_event_qnq *dbgcqe; 21769bd3250aSLuigi Rizzo 21779bd3250aSLuigi Rizzo 21789bd3250aSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 21799bd3250aSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 21809bd3250aSLuigi Rizzo cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_mq_cqe); 21819bd3250aSLuigi Rizzo 21829bd3250aSLuigi Rizzo while (cqe->u0.dw[3]) { 21839bd3250aSLuigi Rizzo DW_SWAP((uint32_t *) cqe, sizeof(oce_mq_cqe)); 21849bd3250aSLuigi Rizzo if (cqe->u0.s.async_event) { 21859bd3250aSLuigi Rizzo evt_type = cqe->u0.s.event_type; 21869bd3250aSLuigi Rizzo optype = cqe->u0.s.async_type; 21879bd3250aSLuigi Rizzo if (evt_type == ASYNC_EVENT_CODE_LINK_STATE) { 21889bd3250aSLuigi Rizzo /* Link status evt */ 21899bd3250aSLuigi Rizzo acqe = (struct oce_async_cqe_link_state *)cqe; 21909bd3250aSLuigi Rizzo process_link_state(sc, acqe); 21919bd3250aSLuigi Rizzo } else if ((evt_type == ASYNC_EVENT_GRP5) && 21929bd3250aSLuigi Rizzo (optype == ASYNC_EVENT_PVID_STATE)) { 21939bd3250aSLuigi Rizzo /* GRP5 PVID */ 21949bd3250aSLuigi Rizzo gcqe = 21959bd3250aSLuigi Rizzo (struct oce_async_event_grp5_pvid_state *)cqe; 21969bd3250aSLuigi Rizzo if (gcqe->enabled) 21979bd3250aSLuigi Rizzo sc->pvid = gcqe->tag & VLAN_VID_MASK; 21989bd3250aSLuigi Rizzo else 21999bd3250aSLuigi Rizzo sc->pvid = 0; 22009bd3250aSLuigi Rizzo 22012f345d8eSLuigi Rizzo } 2202cdaba892SXin LI else if(evt_type == ASYNC_EVENT_CODE_DEBUG && 2203cdaba892SXin LI optype == ASYNC_EVENT_DEBUG_QNQ) { 2204cdaba892SXin LI dbgcqe = 2205cdaba892SXin LI (struct oce_async_event_qnq *)cqe; 2206cdaba892SXin LI if(dbgcqe->valid) 2207cdaba892SXin LI sc->qnqid = dbgcqe->vlan_tag; 2208cdaba892SXin LI sc->qnq_debug_event = TRUE; 2209cdaba892SXin LI } 22102f345d8eSLuigi Rizzo } 22112f345d8eSLuigi Rizzo cqe->u0.dw[3] = 0; 22122f345d8eSLuigi Rizzo RING_GET(cq->ring, 1); 22132f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 22142f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 22152f345d8eSLuigi Rizzo cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_mq_cqe); 22162f345d8eSLuigi Rizzo num_cqes++; 22172f345d8eSLuigi Rizzo } 22182f345d8eSLuigi Rizzo 22192f345d8eSLuigi Rizzo if (num_cqes) 22202f345d8eSLuigi Rizzo oce_arm_cq(sc, cq->cq_id, num_cqes, FALSE); 22212f345d8eSLuigi Rizzo 22222f345d8eSLuigi Rizzo return 0; 22232f345d8eSLuigi Rizzo } 22242f345d8eSLuigi Rizzo 22252f345d8eSLuigi Rizzo 22262f345d8eSLuigi Rizzo static void 22272f345d8eSLuigi Rizzo setup_max_queues_want(POCE_SOFTC sc) 22282f345d8eSLuigi Rizzo { 22292f345d8eSLuigi Rizzo /* Check if it is FLEX machine. Is so dont use RSS */ 22302f345d8eSLuigi Rizzo if ((sc->function_mode & FNM_FLEX10_MODE) || 22319bd3250aSLuigi Rizzo (sc->function_mode & FNM_UMC_MODE) || 22329bd3250aSLuigi Rizzo (sc->function_mode & FNM_VNIC_MODE) || 2233291a1934SXin LI (!is_rss_enabled(sc)) || 22342f345d8eSLuigi Rizzo (sc->flags & OCE_FLAGS_BE2)) { 22352f345d8eSLuigi Rizzo sc->nrqs = 1; 22362f345d8eSLuigi Rizzo sc->nwqs = 1; 2237*5fbb6830SXin LI } else { 2238*5fbb6830SXin LI sc->nrqs = MIN(OCE_NCPUS, sc->nrssqs) + 1; 2239*5fbb6830SXin LI sc->nwqs = MIN(OCE_NCPUS, sc->nrssqs); 22402f345d8eSLuigi Rizzo } 22412f345d8eSLuigi Rizzo } 22422f345d8eSLuigi Rizzo 22432f345d8eSLuigi Rizzo 22442f345d8eSLuigi Rizzo static void 22452f345d8eSLuigi Rizzo update_queues_got(POCE_SOFTC sc) 22462f345d8eSLuigi Rizzo { 2247291a1934SXin LI if (is_rss_enabled(sc)) { 22482f345d8eSLuigi Rizzo sc->nrqs = sc->intr_count + 1; 22492f345d8eSLuigi Rizzo sc->nwqs = sc->intr_count; 22502f345d8eSLuigi Rizzo } else { 22512f345d8eSLuigi Rizzo sc->nrqs = 1; 22522f345d8eSLuigi Rizzo sc->nwqs = 1; 22532f345d8eSLuigi Rizzo } 22542f345d8eSLuigi Rizzo } 22552f345d8eSLuigi Rizzo 2256cdaba892SXin LI static int 2257cdaba892SXin LI oce_check_ipv6_ext_hdr(struct mbuf *m) 2258cdaba892SXin LI { 2259cdaba892SXin LI struct ether_header *eh = mtod(m, struct ether_header *); 2260cdaba892SXin LI caddr_t m_datatemp = m->m_data; 2261cdaba892SXin LI 2262cdaba892SXin LI if (eh->ether_type == htons(ETHERTYPE_IPV6)) { 2263cdaba892SXin LI m->m_data += sizeof(struct ether_header); 2264cdaba892SXin LI struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 2265cdaba892SXin LI 2266cdaba892SXin LI if((ip6->ip6_nxt != IPPROTO_TCP) && \ 2267cdaba892SXin LI (ip6->ip6_nxt != IPPROTO_UDP)){ 2268cdaba892SXin LI struct ip6_ext *ip6e = NULL; 2269cdaba892SXin LI m->m_data += sizeof(struct ip6_hdr); 2270cdaba892SXin LI 2271cdaba892SXin LI ip6e = (struct ip6_ext *) mtod(m, struct ip6_ext *); 2272cdaba892SXin LI if(ip6e->ip6e_len == 0xff) { 2273cdaba892SXin LI m->m_data = m_datatemp; 2274cdaba892SXin LI return TRUE; 2275cdaba892SXin LI } 2276cdaba892SXin LI } 2277cdaba892SXin LI m->m_data = m_datatemp; 2278cdaba892SXin LI } 2279cdaba892SXin LI return FALSE; 2280cdaba892SXin LI } 2281cdaba892SXin LI 2282cdaba892SXin LI static int 2283cdaba892SXin LI is_be3_a1(POCE_SOFTC sc) 2284cdaba892SXin LI { 2285cdaba892SXin LI if((sc->flags & OCE_FLAGS_BE3) && ((sc->asic_revision & 0xFF) < 2)) { 2286cdaba892SXin LI return TRUE; 2287cdaba892SXin LI } 2288cdaba892SXin LI return FALSE; 2289cdaba892SXin LI } 2290cdaba892SXin LI 2291cdaba892SXin LI static struct mbuf * 2292cdaba892SXin LI oce_insert_vlan_tag(POCE_SOFTC sc, struct mbuf *m, boolean_t *complete) 2293cdaba892SXin LI { 2294cdaba892SXin LI uint16_t vlan_tag = 0; 2295cdaba892SXin LI 2296cdaba892SXin LI if(!M_WRITABLE(m)) 2297cdaba892SXin LI return NULL; 2298cdaba892SXin LI 2299cdaba892SXin LI /* Embed vlan tag in the packet if it is not part of it */ 2300cdaba892SXin LI if(m->m_flags & M_VLANTAG) { 2301cdaba892SXin LI vlan_tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); 2302cdaba892SXin LI m->m_flags &= ~M_VLANTAG; 2303cdaba892SXin LI } 2304cdaba892SXin LI 2305cdaba892SXin LI /* if UMC, ignore vlan tag insertion and instead insert pvid */ 2306cdaba892SXin LI if(sc->pvid) { 2307cdaba892SXin LI if(!vlan_tag) 2308cdaba892SXin LI vlan_tag = sc->pvid; 2309cdaba892SXin LI *complete = FALSE; 2310cdaba892SXin LI } 2311cdaba892SXin LI 2312cdaba892SXin LI if(vlan_tag) { 2313cdaba892SXin LI m = ether_vlanencap(m, vlan_tag); 2314cdaba892SXin LI } 2315cdaba892SXin LI 2316cdaba892SXin LI if(sc->qnqid) { 2317cdaba892SXin LI m = ether_vlanencap(m, sc->qnqid); 2318cdaba892SXin LI *complete = FALSE; 2319cdaba892SXin LI } 2320cdaba892SXin LI return m; 2321cdaba892SXin LI } 2322cdaba892SXin LI 2323cdaba892SXin LI static int 2324cdaba892SXin LI oce_tx_asic_stall_verify(POCE_SOFTC sc, struct mbuf *m) 2325cdaba892SXin LI { 2326cdaba892SXin LI if(is_be3_a1(sc) && IS_QNQ_OR_UMC(sc) && \ 2327cdaba892SXin LI oce_check_ipv6_ext_hdr(m)) { 2328cdaba892SXin LI return TRUE; 2329cdaba892SXin LI } 2330cdaba892SXin LI return FALSE; 2331cdaba892SXin LI } 2332291a1934SXin LI 2333291a1934SXin LI static void 2334291a1934SXin LI oce_get_config(POCE_SOFTC sc) 2335291a1934SXin LI { 2336291a1934SXin LI int rc = 0; 2337291a1934SXin LI uint32_t max_rss = 0; 2338291a1934SXin LI 2339291a1934SXin LI if ((IS_BE(sc) || IS_SH(sc)) && (!sc->be3_native)) 2340291a1934SXin LI max_rss = OCE_LEGACY_MODE_RSS; 2341291a1934SXin LI else 2342291a1934SXin LI max_rss = OCE_MAX_RSS; 2343291a1934SXin LI 2344291a1934SXin LI if (!IS_BE(sc)) { 2345291a1934SXin LI rc = oce_get_func_config(sc); 2346291a1934SXin LI if (rc) { 2347291a1934SXin LI sc->nwqs = OCE_MAX_WQ; 2348291a1934SXin LI sc->nrssqs = max_rss; 2349291a1934SXin LI sc->nrqs = sc->nrssqs + 1; 2350291a1934SXin LI } 2351291a1934SXin LI } 2352291a1934SXin LI else { 2353291a1934SXin LI rc = oce_get_profile_config(sc); 2354291a1934SXin LI sc->nrssqs = max_rss; 2355291a1934SXin LI sc->nrqs = sc->nrssqs + 1; 2356291a1934SXin LI if (rc) 2357291a1934SXin LI sc->nwqs = OCE_MAX_WQ; 2358291a1934SXin LI } 2359291a1934SXin LI } 2360