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 465fbb6830SXin LI /* UE Status Low CSR */ 475fbb6830SXin LI static char *ue_status_low_desc[] = { 485fbb6830SXin LI "CEV", 495fbb6830SXin LI "CTX", 505fbb6830SXin LI "DBUF", 515fbb6830SXin LI "ERX", 525fbb6830SXin LI "Host", 535fbb6830SXin LI "MPU", 545fbb6830SXin LI "NDMA", 555fbb6830SXin LI "PTC ", 565fbb6830SXin LI "RDMA ", 575fbb6830SXin LI "RXF ", 585fbb6830SXin LI "RXIPS ", 595fbb6830SXin LI "RXULP0 ", 605fbb6830SXin LI "RXULP1 ", 615fbb6830SXin LI "RXULP2 ", 625fbb6830SXin LI "TIM ", 635fbb6830SXin LI "TPOST ", 645fbb6830SXin LI "TPRE ", 655fbb6830SXin LI "TXIPS ", 665fbb6830SXin LI "TXULP0 ", 675fbb6830SXin LI "TXULP1 ", 685fbb6830SXin LI "UC ", 695fbb6830SXin LI "WDMA ", 705fbb6830SXin LI "TXULP2 ", 715fbb6830SXin LI "HOST1 ", 725fbb6830SXin LI "P0_OB_LINK ", 735fbb6830SXin LI "P1_OB_LINK ", 745fbb6830SXin LI "HOST_GPIO ", 755fbb6830SXin LI "MBOX ", 765fbb6830SXin LI "AXGMAC0", 775fbb6830SXin LI "AXGMAC1", 785fbb6830SXin LI "JTAG", 795fbb6830SXin LI "MPU_INTPEND" 805fbb6830SXin LI }; 815fbb6830SXin LI 825fbb6830SXin LI /* UE Status High CSR */ 835fbb6830SXin LI static char *ue_status_hi_desc[] = { 845fbb6830SXin LI "LPCMEMHOST", 855fbb6830SXin LI "MGMT_MAC", 865fbb6830SXin LI "PCS0ONLINE", 875fbb6830SXin LI "MPU_IRAM", 885fbb6830SXin LI "PCS1ONLINE", 895fbb6830SXin LI "PCTL0", 905fbb6830SXin LI "PCTL1", 915fbb6830SXin LI "PMEM", 925fbb6830SXin LI "RR", 935fbb6830SXin LI "TXPB", 945fbb6830SXin LI "RXPP", 955fbb6830SXin LI "XAUI", 965fbb6830SXin LI "TXP", 975fbb6830SXin LI "ARM", 985fbb6830SXin LI "IPC", 995fbb6830SXin LI "HOST2", 1005fbb6830SXin LI "HOST3", 1015fbb6830SXin LI "HOST4", 1025fbb6830SXin LI "HOST5", 1035fbb6830SXin LI "HOST6", 1045fbb6830SXin LI "HOST7", 1055fbb6830SXin LI "HOST8", 1065fbb6830SXin LI "HOST9", 1075fbb6830SXin LI "NETC", 1085fbb6830SXin LI "Unknown", 1095fbb6830SXin LI "Unknown", 1105fbb6830SXin LI "Unknown", 1115fbb6830SXin LI "Unknown", 1125fbb6830SXin LI "Unknown", 1135fbb6830SXin LI "Unknown", 1145fbb6830SXin LI "Unknown", 1155fbb6830SXin LI "Unknown" 1165fbb6830SXin LI }; 1175fbb6830SXin 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) { 4625fbb6830SXin 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) { 4655fbb6830SXin 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; 832*a4f734b4SXin LI case 5: /* 20 Gbps */ 833*a4f734b4SXin LI req->ifm_active |= IFM_10G_SR | IFM_FDX; 834*a4f734b4SXin LI sc->speed = 20000; 835*a4f734b4SXin LI break; 836*a4f734b4SXin LI case 6: /* 25 Gbps */ 837*a4f734b4SXin LI req->ifm_active |= IFM_10G_SR | IFM_FDX; 838*a4f734b4SXin LI sc->speed = 25000; 839*a4f734b4SXin LI break; 840b41206d8SXin LI case 7: /* 40 Gbps */ 841b41206d8SXin LI req->ifm_active |= IFM_40G_SR4 | IFM_FDX; 842b41206d8SXin LI sc->speed = 40000; 843b41206d8SXin LI break; 844*a4f734b4SXin LI default: 845*a4f734b4SXin LI sc->speed = 0; 846*a4f734b4SXin LI break; 8472f345d8eSLuigi Rizzo } 8482f345d8eSLuigi Rizzo 8492f345d8eSLuigi Rizzo return; 8502f345d8eSLuigi Rizzo } 8512f345d8eSLuigi Rizzo 8522f345d8eSLuigi Rizzo 8532f345d8eSLuigi Rizzo int 8542f345d8eSLuigi Rizzo oce_media_change(struct ifnet *ifp) 8552f345d8eSLuigi Rizzo { 8562f345d8eSLuigi Rizzo return 0; 8572f345d8eSLuigi Rizzo } 8582f345d8eSLuigi Rizzo 8592f345d8eSLuigi Rizzo 8602f345d8eSLuigi Rizzo 8612f345d8eSLuigi Rizzo 8622f345d8eSLuigi Rizzo /***************************************************************************** 8632f345d8eSLuigi Rizzo * Transmit routines functions * 8642f345d8eSLuigi Rizzo *****************************************************************************/ 8652f345d8eSLuigi Rizzo 8662f345d8eSLuigi Rizzo static int 8672f345d8eSLuigi Rizzo oce_tx(POCE_SOFTC sc, struct mbuf **mpp, int wq_index) 8682f345d8eSLuigi Rizzo { 8692f345d8eSLuigi Rizzo int rc = 0, i, retry_cnt = 0; 8702f345d8eSLuigi Rizzo bus_dma_segment_t segs[OCE_MAX_TX_ELEMENTS]; 8712f345d8eSLuigi Rizzo struct mbuf *m, *m_temp; 8722f345d8eSLuigi Rizzo struct oce_wq *wq = sc->wq[wq_index]; 8732f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 8742f345d8eSLuigi Rizzo struct oce_nic_hdr_wqe *nichdr; 8752f345d8eSLuigi Rizzo struct oce_nic_frag_wqe *nicfrag; 8762f345d8eSLuigi Rizzo int num_wqes; 8772f345d8eSLuigi Rizzo uint32_t reg_value; 878cdaba892SXin LI boolean_t complete = TRUE; 8792f345d8eSLuigi Rizzo 8802f345d8eSLuigi Rizzo m = *mpp; 8812f345d8eSLuigi Rizzo if (!m) 8822f345d8eSLuigi Rizzo return EINVAL; 8832f345d8eSLuigi Rizzo 8842f345d8eSLuigi Rizzo if (!(m->m_flags & M_PKTHDR)) { 8852f345d8eSLuigi Rizzo rc = ENXIO; 8862f345d8eSLuigi Rizzo goto free_ret; 8872f345d8eSLuigi Rizzo } 8882f345d8eSLuigi Rizzo 889cdaba892SXin LI if(oce_tx_asic_stall_verify(sc, m)) { 890cdaba892SXin LI m = oce_insert_vlan_tag(sc, m, &complete); 891cdaba892SXin LI if(!m) { 892cdaba892SXin LI device_printf(sc->dev, "Insertion unsuccessful\n"); 893cdaba892SXin LI return 0; 894cdaba892SXin LI } 895cdaba892SXin LI 896cdaba892SXin LI } 897cdaba892SXin LI 8982f345d8eSLuigi Rizzo if (m->m_pkthdr.csum_flags & CSUM_TSO) { 8992f345d8eSLuigi Rizzo /* consolidate packet buffers for TSO/LSO segment offload */ 9009bd3250aSLuigi Rizzo #if defined(INET6) || defined(INET) 9019bd3250aSLuigi Rizzo m = oce_tso_setup(sc, mpp); 902ad512958SBjoern A. Zeeb #else 903ad512958SBjoern A. Zeeb m = NULL; 904ad512958SBjoern A. Zeeb #endif 9052f345d8eSLuigi Rizzo if (m == NULL) { 9062f345d8eSLuigi Rizzo rc = ENXIO; 9072f345d8eSLuigi Rizzo goto free_ret; 9082f345d8eSLuigi Rizzo } 9092f345d8eSLuigi Rizzo } 9102f345d8eSLuigi Rizzo 911291a1934SXin LI pd = &wq->pckts[wq->pkt_desc_head]; 9122f345d8eSLuigi Rizzo retry: 9132f345d8eSLuigi Rizzo rc = bus_dmamap_load_mbuf_sg(wq->tag, 9142f345d8eSLuigi Rizzo pd->map, 9152f345d8eSLuigi Rizzo m, segs, &pd->nsegs, BUS_DMA_NOWAIT); 9162f345d8eSLuigi Rizzo if (rc == 0) { 9172f345d8eSLuigi Rizzo num_wqes = pd->nsegs + 1; 918291a1934SXin LI if (IS_BE(sc) || IS_SH(sc)) { 9192f345d8eSLuigi Rizzo /*Dummy required only for BE3.*/ 9202f345d8eSLuigi Rizzo if (num_wqes & 1) 9212f345d8eSLuigi Rizzo num_wqes++; 9222f345d8eSLuigi Rizzo } 9232f345d8eSLuigi Rizzo if (num_wqes >= RING_NUM_FREE(wq->ring)) { 9242f345d8eSLuigi Rizzo bus_dmamap_unload(wq->tag, pd->map); 9252f345d8eSLuigi Rizzo return EBUSY; 9262f345d8eSLuigi Rizzo } 927291a1934SXin LI atomic_store_rel_int(&wq->pkt_desc_head, 928291a1934SXin LI (wq->pkt_desc_head + 1) % \ 929291a1934SXin LI OCE_WQ_PACKET_ARRAY_SIZE); 9302f345d8eSLuigi Rizzo bus_dmamap_sync(wq->tag, pd->map, BUS_DMASYNC_PREWRITE); 9312f345d8eSLuigi Rizzo pd->mbuf = m; 9322f345d8eSLuigi Rizzo 9332f345d8eSLuigi Rizzo nichdr = 9342f345d8eSLuigi Rizzo RING_GET_PRODUCER_ITEM_VA(wq->ring, struct oce_nic_hdr_wqe); 9352f345d8eSLuigi Rizzo nichdr->u0.dw[0] = 0; 9362f345d8eSLuigi Rizzo nichdr->u0.dw[1] = 0; 9372f345d8eSLuigi Rizzo nichdr->u0.dw[2] = 0; 9382f345d8eSLuigi Rizzo nichdr->u0.dw[3] = 0; 9392f345d8eSLuigi Rizzo 940cdaba892SXin LI nichdr->u0.s.complete = complete; 9412f345d8eSLuigi Rizzo nichdr->u0.s.event = 1; 9422f345d8eSLuigi Rizzo nichdr->u0.s.crc = 1; 9432f345d8eSLuigi Rizzo nichdr->u0.s.forward = 0; 9442f345d8eSLuigi Rizzo nichdr->u0.s.ipcs = (m->m_pkthdr.csum_flags & CSUM_IP) ? 1 : 0; 9452f345d8eSLuigi Rizzo nichdr->u0.s.udpcs = 9462f345d8eSLuigi Rizzo (m->m_pkthdr.csum_flags & CSUM_UDP) ? 1 : 0; 9472f345d8eSLuigi Rizzo nichdr->u0.s.tcpcs = 9482f345d8eSLuigi Rizzo (m->m_pkthdr.csum_flags & CSUM_TCP) ? 1 : 0; 9492f345d8eSLuigi Rizzo nichdr->u0.s.num_wqe = num_wqes; 9502f345d8eSLuigi Rizzo nichdr->u0.s.total_length = m->m_pkthdr.len; 9515fbb6830SXin LI 9522f345d8eSLuigi Rizzo if (m->m_flags & M_VLANTAG) { 9532f345d8eSLuigi Rizzo nichdr->u0.s.vlan = 1; /*Vlan present*/ 9542f345d8eSLuigi Rizzo nichdr->u0.s.vlan_tag = m->m_pkthdr.ether_vtag; 9552f345d8eSLuigi Rizzo } 9565fbb6830SXin LI 9572f345d8eSLuigi Rizzo if (m->m_pkthdr.csum_flags & CSUM_TSO) { 9582f345d8eSLuigi Rizzo if (m->m_pkthdr.tso_segsz) { 9592f345d8eSLuigi Rizzo nichdr->u0.s.lso = 1; 9602f345d8eSLuigi Rizzo nichdr->u0.s.lso_mss = m->m_pkthdr.tso_segsz; 9612f345d8eSLuigi Rizzo } 962291a1934SXin LI if (!IS_BE(sc) || !IS_SH(sc)) 9632f345d8eSLuigi Rizzo nichdr->u0.s.ipcs = 1; 9642f345d8eSLuigi Rizzo } 9652f345d8eSLuigi Rizzo 9662f345d8eSLuigi Rizzo RING_PUT(wq->ring, 1); 967291a1934SXin LI atomic_add_int(&wq->ring->num_used, 1); 9682f345d8eSLuigi Rizzo 9692f345d8eSLuigi Rizzo for (i = 0; i < pd->nsegs; i++) { 9702f345d8eSLuigi Rizzo nicfrag = 9712f345d8eSLuigi Rizzo RING_GET_PRODUCER_ITEM_VA(wq->ring, 9722f345d8eSLuigi Rizzo struct oce_nic_frag_wqe); 9732f345d8eSLuigi Rizzo nicfrag->u0.s.rsvd0 = 0; 9742f345d8eSLuigi Rizzo nicfrag->u0.s.frag_pa_hi = ADDR_HI(segs[i].ds_addr); 9752f345d8eSLuigi Rizzo nicfrag->u0.s.frag_pa_lo = ADDR_LO(segs[i].ds_addr); 9762f345d8eSLuigi Rizzo nicfrag->u0.s.frag_len = segs[i].ds_len; 9772f345d8eSLuigi Rizzo pd->wqe_idx = wq->ring->pidx; 9782f345d8eSLuigi Rizzo RING_PUT(wq->ring, 1); 979291a1934SXin LI atomic_add_int(&wq->ring->num_used, 1); 9802f345d8eSLuigi Rizzo } 9812f345d8eSLuigi Rizzo if (num_wqes > (pd->nsegs + 1)) { 9822f345d8eSLuigi Rizzo nicfrag = 9832f345d8eSLuigi Rizzo RING_GET_PRODUCER_ITEM_VA(wq->ring, 9842f345d8eSLuigi Rizzo struct oce_nic_frag_wqe); 9852f345d8eSLuigi Rizzo nicfrag->u0.dw[0] = 0; 9862f345d8eSLuigi Rizzo nicfrag->u0.dw[1] = 0; 9872f345d8eSLuigi Rizzo nicfrag->u0.dw[2] = 0; 9882f345d8eSLuigi Rizzo nicfrag->u0.dw[3] = 0; 9892f345d8eSLuigi Rizzo pd->wqe_idx = wq->ring->pidx; 9902f345d8eSLuigi Rizzo RING_PUT(wq->ring, 1); 991291a1934SXin LI atomic_add_int(&wq->ring->num_used, 1); 9922f345d8eSLuigi Rizzo pd->nsegs++; 9932f345d8eSLuigi Rizzo } 9942f345d8eSLuigi Rizzo 9952f345d8eSLuigi Rizzo sc->ifp->if_opackets++; 9962f345d8eSLuigi Rizzo wq->tx_stats.tx_reqs++; 9972f345d8eSLuigi Rizzo wq->tx_stats.tx_wrbs += num_wqes; 9982f345d8eSLuigi Rizzo wq->tx_stats.tx_bytes += m->m_pkthdr.len; 9992f345d8eSLuigi Rizzo wq->tx_stats.tx_pkts++; 10002f345d8eSLuigi Rizzo 10012f345d8eSLuigi Rizzo bus_dmamap_sync(wq->ring->dma.tag, wq->ring->dma.map, 10022f345d8eSLuigi Rizzo BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 10032f345d8eSLuigi Rizzo reg_value = (num_wqes << 16) | wq->wq_id; 1004291a1934SXin LI OCE_WRITE_REG32(sc, db, wq->db_offset, reg_value); 10052f345d8eSLuigi Rizzo 10062f345d8eSLuigi Rizzo } else if (rc == EFBIG) { 10072f345d8eSLuigi Rizzo if (retry_cnt == 0) { 1008c6499eccSGleb Smirnoff m_temp = m_defrag(m, M_NOWAIT); 10092f345d8eSLuigi Rizzo if (m_temp == NULL) 10102f345d8eSLuigi Rizzo goto free_ret; 10112f345d8eSLuigi Rizzo m = m_temp; 10122f345d8eSLuigi Rizzo *mpp = m_temp; 10132f345d8eSLuigi Rizzo retry_cnt = retry_cnt + 1; 10142f345d8eSLuigi Rizzo goto retry; 10152f345d8eSLuigi Rizzo } else 10162f345d8eSLuigi Rizzo goto free_ret; 10172f345d8eSLuigi Rizzo } else if (rc == ENOMEM) 10182f345d8eSLuigi Rizzo return rc; 10192f345d8eSLuigi Rizzo else 10202f345d8eSLuigi Rizzo goto free_ret; 10212f345d8eSLuigi Rizzo 10222f345d8eSLuigi Rizzo return 0; 10232f345d8eSLuigi Rizzo 10242f345d8eSLuigi Rizzo free_ret: 10252f345d8eSLuigi Rizzo m_freem(*mpp); 10262f345d8eSLuigi Rizzo *mpp = NULL; 10272f345d8eSLuigi Rizzo return rc; 10282f345d8eSLuigi Rizzo } 10292f345d8eSLuigi Rizzo 10302f345d8eSLuigi Rizzo 10312f345d8eSLuigi Rizzo static void 10322f345d8eSLuigi Rizzo oce_tx_complete(struct oce_wq *wq, uint32_t wqe_idx, uint32_t status) 10332f345d8eSLuigi Rizzo { 10342f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 10352f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) wq->parent; 10362f345d8eSLuigi Rizzo struct mbuf *m; 10372f345d8eSLuigi Rizzo 1038291a1934SXin LI pd = &wq->pckts[wq->pkt_desc_tail]; 1039291a1934SXin LI atomic_store_rel_int(&wq->pkt_desc_tail, 1040291a1934SXin LI (wq->pkt_desc_tail + 1) % OCE_WQ_PACKET_ARRAY_SIZE); 1041291a1934SXin LI atomic_subtract_int(&wq->ring->num_used, pd->nsegs + 1); 10422f345d8eSLuigi Rizzo bus_dmamap_sync(wq->tag, pd->map, BUS_DMASYNC_POSTWRITE); 10432f345d8eSLuigi Rizzo bus_dmamap_unload(wq->tag, pd->map); 10442f345d8eSLuigi Rizzo 10452f345d8eSLuigi Rizzo m = pd->mbuf; 10462f345d8eSLuigi Rizzo m_freem(m); 10472f345d8eSLuigi Rizzo pd->mbuf = NULL; 10482f345d8eSLuigi Rizzo 1049291a1934SXin LI 10502f345d8eSLuigi Rizzo if (sc->ifp->if_drv_flags & IFF_DRV_OACTIVE) { 10512f345d8eSLuigi Rizzo if (wq->ring->num_used < (wq->ring->num_items / 2)) { 10522f345d8eSLuigi Rizzo sc->ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE); 10532f345d8eSLuigi Rizzo oce_tx_restart(sc, wq); 10542f345d8eSLuigi Rizzo } 10552f345d8eSLuigi Rizzo } 10562f345d8eSLuigi Rizzo } 10572f345d8eSLuigi Rizzo 10582f345d8eSLuigi Rizzo 10592f345d8eSLuigi Rizzo static void 10602f345d8eSLuigi Rizzo oce_tx_restart(POCE_SOFTC sc, struct oce_wq *wq) 10612f345d8eSLuigi Rizzo { 10622f345d8eSLuigi Rizzo 10632f345d8eSLuigi Rizzo if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) 10642f345d8eSLuigi Rizzo return; 10652f345d8eSLuigi Rizzo 10662f345d8eSLuigi Rizzo #if __FreeBSD_version >= 800000 10672f345d8eSLuigi Rizzo if (!drbr_empty(sc->ifp, wq->br)) 10682f345d8eSLuigi Rizzo #else 10692f345d8eSLuigi Rizzo if (!IFQ_DRV_IS_EMPTY(&sc->ifp->if_snd)) 10702f345d8eSLuigi Rizzo #endif 10712f345d8eSLuigi Rizzo taskqueue_enqueue_fast(taskqueue_swi, &wq->txtask); 10722f345d8eSLuigi Rizzo 10732f345d8eSLuigi Rizzo } 10742f345d8eSLuigi Rizzo 10759bd3250aSLuigi Rizzo 1076ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 10772f345d8eSLuigi Rizzo static struct mbuf * 10789bd3250aSLuigi Rizzo oce_tso_setup(POCE_SOFTC sc, struct mbuf **mpp) 10792f345d8eSLuigi Rizzo { 10802f345d8eSLuigi Rizzo struct mbuf *m; 1081ad512958SBjoern A. Zeeb #ifdef INET 10822f345d8eSLuigi Rizzo struct ip *ip; 1083ad512958SBjoern A. Zeeb #endif 1084ad512958SBjoern A. Zeeb #ifdef INET6 10852f345d8eSLuigi Rizzo struct ip6_hdr *ip6; 1086ad512958SBjoern A. Zeeb #endif 10872f345d8eSLuigi Rizzo struct ether_vlan_header *eh; 10882f345d8eSLuigi Rizzo struct tcphdr *th; 10892f345d8eSLuigi Rizzo uint16_t etype; 10909bd3250aSLuigi Rizzo int total_len = 0, ehdrlen = 0; 10912f345d8eSLuigi Rizzo 10922f345d8eSLuigi Rizzo m = *mpp; 10932f345d8eSLuigi Rizzo 10942f345d8eSLuigi Rizzo if (M_WRITABLE(m) == 0) { 1095c6499eccSGleb Smirnoff m = m_dup(*mpp, M_NOWAIT); 10962f345d8eSLuigi Rizzo if (!m) 10972f345d8eSLuigi Rizzo return NULL; 10982f345d8eSLuigi Rizzo m_freem(*mpp); 10992f345d8eSLuigi Rizzo *mpp = m; 11002f345d8eSLuigi Rizzo } 11012f345d8eSLuigi Rizzo 11022f345d8eSLuigi Rizzo eh = mtod(m, struct ether_vlan_header *); 11032f345d8eSLuigi Rizzo if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { 11042f345d8eSLuigi Rizzo etype = ntohs(eh->evl_proto); 11052f345d8eSLuigi Rizzo ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 11062f345d8eSLuigi Rizzo } else { 11072f345d8eSLuigi Rizzo etype = ntohs(eh->evl_encap_proto); 11082f345d8eSLuigi Rizzo ehdrlen = ETHER_HDR_LEN; 11092f345d8eSLuigi Rizzo } 11102f345d8eSLuigi Rizzo 11112f345d8eSLuigi Rizzo switch (etype) { 1112ad512958SBjoern A. Zeeb #ifdef INET 11132f345d8eSLuigi Rizzo case ETHERTYPE_IP: 11142f345d8eSLuigi Rizzo ip = (struct ip *)(m->m_data + ehdrlen); 11152f345d8eSLuigi Rizzo if (ip->ip_p != IPPROTO_TCP) 11162f345d8eSLuigi Rizzo return NULL; 11172f345d8eSLuigi Rizzo th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); 11182f345d8eSLuigi Rizzo 11192f345d8eSLuigi Rizzo total_len = ehdrlen + (ip->ip_hl << 2) + (th->th_off << 2); 11202f345d8eSLuigi Rizzo break; 1121ad512958SBjoern A. Zeeb #endif 1122ad512958SBjoern A. Zeeb #ifdef INET6 11232f345d8eSLuigi Rizzo case ETHERTYPE_IPV6: 11242f345d8eSLuigi Rizzo ip6 = (struct ip6_hdr *)(m->m_data + ehdrlen); 11252f345d8eSLuigi Rizzo if (ip6->ip6_nxt != IPPROTO_TCP) 11262f345d8eSLuigi Rizzo return NULL; 11272f345d8eSLuigi Rizzo th = (struct tcphdr *)((caddr_t)ip6 + sizeof(struct ip6_hdr)); 11282f345d8eSLuigi Rizzo 11292f345d8eSLuigi Rizzo total_len = ehdrlen + sizeof(struct ip6_hdr) + (th->th_off << 2); 11302f345d8eSLuigi Rizzo break; 1131ad512958SBjoern A. Zeeb #endif 11322f345d8eSLuigi Rizzo default: 11332f345d8eSLuigi Rizzo return NULL; 11342f345d8eSLuigi Rizzo } 11352f345d8eSLuigi Rizzo 11362f345d8eSLuigi Rizzo m = m_pullup(m, total_len); 11372f345d8eSLuigi Rizzo if (!m) 11382f345d8eSLuigi Rizzo return NULL; 11392f345d8eSLuigi Rizzo *mpp = m; 11402f345d8eSLuigi Rizzo return m; 11412f345d8eSLuigi Rizzo 11422f345d8eSLuigi Rizzo } 1143ad512958SBjoern A. Zeeb #endif /* INET6 || INET */ 11442f345d8eSLuigi Rizzo 11452f345d8eSLuigi Rizzo void 11462f345d8eSLuigi Rizzo oce_tx_task(void *arg, int npending) 11472f345d8eSLuigi Rizzo { 11482f345d8eSLuigi Rizzo struct oce_wq *wq = arg; 11492f345d8eSLuigi Rizzo POCE_SOFTC sc = wq->parent; 11502f345d8eSLuigi Rizzo struct ifnet *ifp = sc->ifp; 11512f345d8eSLuigi Rizzo int rc = 0; 11522f345d8eSLuigi Rizzo 11532f345d8eSLuigi Rizzo #if __FreeBSD_version >= 800000 1154291a1934SXin LI LOCK(&wq->tx_lock); 11552f345d8eSLuigi Rizzo rc = oce_multiq_transmit(ifp, NULL, wq); 11562f345d8eSLuigi Rizzo if (rc) { 11572f345d8eSLuigi Rizzo device_printf(sc->dev, 11582f345d8eSLuigi Rizzo "TX[%d] restart failed\n", wq->queue_index); 11592f345d8eSLuigi Rizzo } 11602f345d8eSLuigi Rizzo UNLOCK(&wq->tx_lock); 11612f345d8eSLuigi Rizzo #else 11622f345d8eSLuigi Rizzo oce_start(ifp); 11632f345d8eSLuigi Rizzo #endif 11642f345d8eSLuigi Rizzo 11652f345d8eSLuigi Rizzo } 11662f345d8eSLuigi Rizzo 11672f345d8eSLuigi Rizzo 11682f345d8eSLuigi Rizzo void 11692f345d8eSLuigi Rizzo oce_start(struct ifnet *ifp) 11702f345d8eSLuigi Rizzo { 11712f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 11722f345d8eSLuigi Rizzo struct mbuf *m; 11732f345d8eSLuigi Rizzo int rc = 0; 11749bd3250aSLuigi Rizzo int def_q = 0; /* Defualt tx queue is 0*/ 11752f345d8eSLuigi Rizzo 11762f345d8eSLuigi Rizzo if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 11772f345d8eSLuigi Rizzo IFF_DRV_RUNNING) 11782f345d8eSLuigi Rizzo return; 11792f345d8eSLuigi Rizzo 1180cdaba892SXin LI if (!sc->link_status) 1181cdaba892SXin LI return; 1182cdaba892SXin LI 11832f345d8eSLuigi Rizzo do { 11842f345d8eSLuigi Rizzo IF_DEQUEUE(&sc->ifp->if_snd, m); 11852f345d8eSLuigi Rizzo if (m == NULL) 11862f345d8eSLuigi Rizzo break; 11879bd3250aSLuigi Rizzo 11889bd3250aSLuigi Rizzo LOCK(&sc->wq[def_q]->tx_lock); 11899bd3250aSLuigi Rizzo rc = oce_tx(sc, &m, def_q); 11909bd3250aSLuigi Rizzo UNLOCK(&sc->wq[def_q]->tx_lock); 11912f345d8eSLuigi Rizzo if (rc) { 11922f345d8eSLuigi Rizzo if (m != NULL) { 11939bd3250aSLuigi Rizzo sc->wq[def_q]->tx_stats.tx_stops ++; 11942f345d8eSLuigi Rizzo ifp->if_drv_flags |= IFF_DRV_OACTIVE; 11952f345d8eSLuigi Rizzo IFQ_DRV_PREPEND(&ifp->if_snd, m); 11962f345d8eSLuigi Rizzo m = NULL; 11972f345d8eSLuigi Rizzo } 11982f345d8eSLuigi Rizzo break; 11992f345d8eSLuigi Rizzo } 12002f345d8eSLuigi Rizzo if (m != NULL) 12012f345d8eSLuigi Rizzo ETHER_BPF_MTAP(ifp, m); 12022f345d8eSLuigi Rizzo 12039bd3250aSLuigi Rizzo } while (TRUE); 12042f345d8eSLuigi Rizzo 12052f345d8eSLuigi Rizzo return; 12062f345d8eSLuigi Rizzo } 12072f345d8eSLuigi Rizzo 12082f345d8eSLuigi Rizzo 12092f345d8eSLuigi Rizzo /* Handle the Completion Queue for transmit */ 12102f345d8eSLuigi Rizzo uint16_t 12112f345d8eSLuigi Rizzo oce_wq_handler(void *arg) 12122f345d8eSLuigi Rizzo { 12132f345d8eSLuigi Rizzo struct oce_wq *wq = (struct oce_wq *)arg; 12142f345d8eSLuigi Rizzo POCE_SOFTC sc = wq->parent; 12152f345d8eSLuigi Rizzo struct oce_cq *cq = wq->cq; 12162f345d8eSLuigi Rizzo struct oce_nic_tx_cqe *cqe; 12172f345d8eSLuigi Rizzo int num_cqes = 0; 12182f345d8eSLuigi Rizzo 12192f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 12202f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 12212f345d8eSLuigi Rizzo cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_tx_cqe); 12222f345d8eSLuigi Rizzo while (cqe->u0.dw[3]) { 12232f345d8eSLuigi Rizzo DW_SWAP((uint32_t *) cqe, sizeof(oce_wq_cqe)); 12242f345d8eSLuigi Rizzo 12252f345d8eSLuigi Rizzo wq->ring->cidx = cqe->u0.s.wqe_index + 1; 12262f345d8eSLuigi Rizzo if (wq->ring->cidx >= wq->ring->num_items) 12272f345d8eSLuigi Rizzo wq->ring->cidx -= wq->ring->num_items; 12282f345d8eSLuigi Rizzo 12292f345d8eSLuigi Rizzo oce_tx_complete(wq, cqe->u0.s.wqe_index, cqe->u0.s.status); 12302f345d8eSLuigi Rizzo wq->tx_stats.tx_compl++; 12312f345d8eSLuigi Rizzo cqe->u0.dw[3] = 0; 12322f345d8eSLuigi Rizzo RING_GET(cq->ring, 1); 12332f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 12342f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 12352f345d8eSLuigi Rizzo cqe = 12362f345d8eSLuigi Rizzo RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_tx_cqe); 12372f345d8eSLuigi Rizzo num_cqes++; 12382f345d8eSLuigi Rizzo } 12392f345d8eSLuigi Rizzo 12402f345d8eSLuigi Rizzo if (num_cqes) 12412f345d8eSLuigi Rizzo oce_arm_cq(sc, cq->cq_id, num_cqes, FALSE); 12422f345d8eSLuigi Rizzo 12432f345d8eSLuigi Rizzo return 0; 12442f345d8eSLuigi Rizzo } 12452f345d8eSLuigi Rizzo 12462f345d8eSLuigi Rizzo 12472f345d8eSLuigi Rizzo static int 12482f345d8eSLuigi Rizzo oce_multiq_transmit(struct ifnet *ifp, struct mbuf *m, struct oce_wq *wq) 12492f345d8eSLuigi Rizzo { 12502f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 12512f345d8eSLuigi Rizzo int status = 0, queue_index = 0; 12522f345d8eSLuigi Rizzo struct mbuf *next = NULL; 12532f345d8eSLuigi Rizzo struct buf_ring *br = NULL; 12542f345d8eSLuigi Rizzo 12552f345d8eSLuigi Rizzo br = wq->br; 12562f345d8eSLuigi Rizzo queue_index = wq->queue_index; 12572f345d8eSLuigi Rizzo 12582f345d8eSLuigi Rizzo if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 12592f345d8eSLuigi Rizzo IFF_DRV_RUNNING) { 12602f345d8eSLuigi Rizzo if (m != NULL) 12612f345d8eSLuigi Rizzo status = drbr_enqueue(ifp, br, m); 12622f345d8eSLuigi Rizzo return status; 12632f345d8eSLuigi Rizzo } 12642f345d8eSLuigi Rizzo 1265ded5ea6aSRandall Stewart if (m != NULL) { 12662f345d8eSLuigi Rizzo if ((status = drbr_enqueue(ifp, br, m)) != 0) 12672f345d8eSLuigi Rizzo return status; 1268ded5ea6aSRandall Stewart } 1269ded5ea6aSRandall Stewart while ((next = drbr_peek(ifp, br)) != NULL) { 12702f345d8eSLuigi Rizzo if (oce_tx(sc, &next, queue_index)) { 1271ded5ea6aSRandall Stewart if (next == NULL) { 1272ded5ea6aSRandall Stewart drbr_advance(ifp, br); 1273ded5ea6aSRandall Stewart } else { 1274ded5ea6aSRandall Stewart drbr_putback(ifp, br, next); 12752f345d8eSLuigi Rizzo wq->tx_stats.tx_stops ++; 12762f345d8eSLuigi Rizzo ifp->if_drv_flags |= IFF_DRV_OACTIVE; 12772f345d8eSLuigi Rizzo status = drbr_enqueue(ifp, br, next); 12782f345d8eSLuigi Rizzo } 12792f345d8eSLuigi Rizzo break; 12802f345d8eSLuigi Rizzo } 1281ded5ea6aSRandall Stewart drbr_advance(ifp, br); 1282063efed2SGleb Smirnoff ifp->if_obytes += next->m_pkthdr.len; 1283063efed2SGleb Smirnoff if (next->m_flags & M_MCAST) 1284063efed2SGleb Smirnoff ifp->if_omcasts++; 12852f345d8eSLuigi Rizzo ETHER_BPF_MTAP(ifp, next); 12862f345d8eSLuigi Rizzo } 12872f345d8eSLuigi Rizzo 12882f345d8eSLuigi Rizzo return status; 12892f345d8eSLuigi Rizzo } 12902f345d8eSLuigi Rizzo 12912f345d8eSLuigi Rizzo 12922f345d8eSLuigi Rizzo 12932f345d8eSLuigi Rizzo 12942f345d8eSLuigi Rizzo /***************************************************************************** 12952f345d8eSLuigi Rizzo * Receive routines functions * 12962f345d8eSLuigi Rizzo *****************************************************************************/ 12972f345d8eSLuigi Rizzo 12982f345d8eSLuigi Rizzo static void 12992f345d8eSLuigi Rizzo oce_rx(struct oce_rq *rq, uint32_t rqe_idx, struct oce_nic_rx_cqe *cqe) 13002f345d8eSLuigi Rizzo { 13012f345d8eSLuigi Rizzo uint32_t out; 13022f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 13032f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) rq->parent; 13042f345d8eSLuigi Rizzo int i, len, frag_len; 13052f345d8eSLuigi Rizzo struct mbuf *m = NULL, *tail = NULL; 13062f345d8eSLuigi Rizzo uint16_t vtag; 13072f345d8eSLuigi Rizzo 13082f345d8eSLuigi Rizzo len = cqe->u0.s.pkt_size; 13092f345d8eSLuigi Rizzo if (!len) { 13102f345d8eSLuigi Rizzo /*partial DMA workaround for Lancer*/ 13112f345d8eSLuigi Rizzo oce_discard_rx_comp(rq, cqe); 13122f345d8eSLuigi Rizzo goto exit; 13132f345d8eSLuigi Rizzo } 13142f345d8eSLuigi Rizzo 13159bd3250aSLuigi Rizzo /* Get vlan_tag value */ 1316291a1934SXin LI if(IS_BE(sc) || IS_SH(sc)) 13179bd3250aSLuigi Rizzo vtag = BSWAP_16(cqe->u0.s.vlan_tag); 13189bd3250aSLuigi Rizzo else 13199bd3250aSLuigi Rizzo vtag = cqe->u0.s.vlan_tag; 13209bd3250aSLuigi Rizzo 13219bd3250aSLuigi Rizzo 13222f345d8eSLuigi Rizzo for (i = 0; i < cqe->u0.s.num_fragments; i++) { 13232f345d8eSLuigi Rizzo 13242f345d8eSLuigi Rizzo if (rq->packets_out == rq->packets_in) { 13252f345d8eSLuigi Rizzo device_printf(sc->dev, 13262f345d8eSLuigi Rizzo "RQ transmit descriptor missing\n"); 13272f345d8eSLuigi Rizzo } 13282f345d8eSLuigi Rizzo out = rq->packets_out + 1; 13292f345d8eSLuigi Rizzo if (out == OCE_RQ_PACKET_ARRAY_SIZE) 13302f345d8eSLuigi Rizzo out = 0; 13312f345d8eSLuigi Rizzo pd = &rq->pckts[rq->packets_out]; 13322f345d8eSLuigi Rizzo rq->packets_out = out; 13332f345d8eSLuigi Rizzo 13342f345d8eSLuigi Rizzo bus_dmamap_sync(rq->tag, pd->map, BUS_DMASYNC_POSTWRITE); 13352f345d8eSLuigi Rizzo bus_dmamap_unload(rq->tag, pd->map); 13362f345d8eSLuigi Rizzo rq->pending--; 13372f345d8eSLuigi Rizzo 13382f345d8eSLuigi Rizzo frag_len = (len > rq->cfg.frag_size) ? rq->cfg.frag_size : len; 13392f345d8eSLuigi Rizzo pd->mbuf->m_len = frag_len; 13402f345d8eSLuigi Rizzo 13412f345d8eSLuigi Rizzo if (tail != NULL) { 13422f345d8eSLuigi Rizzo /* additional fragments */ 13432f345d8eSLuigi Rizzo pd->mbuf->m_flags &= ~M_PKTHDR; 13442f345d8eSLuigi Rizzo tail->m_next = pd->mbuf; 13452f345d8eSLuigi Rizzo tail = pd->mbuf; 13462f345d8eSLuigi Rizzo } else { 13472f345d8eSLuigi Rizzo /* first fragment, fill out much of the packet header */ 13482f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.len = len; 13492f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.csum_flags = 0; 13502f345d8eSLuigi Rizzo if (IF_CSUM_ENABLED(sc)) { 13512f345d8eSLuigi Rizzo if (cqe->u0.s.l4_cksum_pass) { 13522f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.csum_flags |= 13532f345d8eSLuigi Rizzo (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); 13542f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.csum_data = 0xffff; 13552f345d8eSLuigi Rizzo } 13562f345d8eSLuigi Rizzo if (cqe->u0.s.ip_cksum_pass) { 13579bd3250aSLuigi Rizzo if (!cqe->u0.s.ip_ver) { /* IPV4 */ 13582f345d8eSLuigi Rizzo pd->mbuf->m_pkthdr.csum_flags |= 13592f345d8eSLuigi Rizzo (CSUM_IP_CHECKED|CSUM_IP_VALID); 13602f345d8eSLuigi Rizzo } 13612f345d8eSLuigi Rizzo } 13622f345d8eSLuigi Rizzo } 13632f345d8eSLuigi Rizzo m = tail = pd->mbuf; 13642f345d8eSLuigi Rizzo } 13652f345d8eSLuigi Rizzo pd->mbuf = NULL; 13662f345d8eSLuigi Rizzo len -= frag_len; 13672f345d8eSLuigi Rizzo } 13682f345d8eSLuigi Rizzo 13692f345d8eSLuigi Rizzo if (m) { 13702f345d8eSLuigi Rizzo if (!oce_cqe_portid_valid(sc, cqe)) { 13712f345d8eSLuigi Rizzo m_freem(m); 13722f345d8eSLuigi Rizzo goto exit; 13732f345d8eSLuigi Rizzo } 13742f345d8eSLuigi Rizzo 13752f345d8eSLuigi Rizzo m->m_pkthdr.rcvif = sc->ifp; 13762f345d8eSLuigi Rizzo #if __FreeBSD_version >= 800000 1377291a1934SXin LI if (rq->queue_index) 1378291a1934SXin LI m->m_pkthdr.flowid = (rq->queue_index - 1); 1379291a1934SXin LI else 13802f345d8eSLuigi Rizzo m->m_pkthdr.flowid = rq->queue_index; 13812f345d8eSLuigi Rizzo m->m_flags |= M_FLOWID; 13822f345d8eSLuigi Rizzo #endif 13839bd3250aSLuigi Rizzo /* This deternies if vlan tag is Valid */ 13842f345d8eSLuigi Rizzo if (oce_cqe_vtp_valid(sc, cqe)) { 13852f345d8eSLuigi Rizzo if (sc->function_mode & FNM_FLEX10_MODE) { 13869bd3250aSLuigi Rizzo /* FLEX10. If QnQ is not set, neglect VLAN */ 13872f345d8eSLuigi Rizzo if (cqe->u0.s.qnq) { 13882f345d8eSLuigi Rizzo m->m_pkthdr.ether_vtag = vtag; 13892f345d8eSLuigi Rizzo m->m_flags |= M_VLANTAG; 13902f345d8eSLuigi Rizzo } 13919bd3250aSLuigi Rizzo } else if (sc->pvid != (vtag & VLAN_VID_MASK)) { 13929bd3250aSLuigi Rizzo /* In UMC mode generally pvid will be striped by 13939bd3250aSLuigi Rizzo hw. But in some cases we have seen it comes 13949bd3250aSLuigi Rizzo with pvid. So if pvid == vlan, neglect vlan. 13959bd3250aSLuigi Rizzo */ 13962f345d8eSLuigi Rizzo m->m_pkthdr.ether_vtag = vtag; 13972f345d8eSLuigi Rizzo m->m_flags |= M_VLANTAG; 13982f345d8eSLuigi Rizzo } 13992f345d8eSLuigi Rizzo } 14002f345d8eSLuigi Rizzo 14012f345d8eSLuigi Rizzo sc->ifp->if_ipackets++; 1402ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 14032f345d8eSLuigi Rizzo /* Try to queue to LRO */ 14042f345d8eSLuigi Rizzo if (IF_LRO_ENABLED(sc) && 14052f345d8eSLuigi Rizzo (cqe->u0.s.ip_cksum_pass) && 14062f345d8eSLuigi Rizzo (cqe->u0.s.l4_cksum_pass) && 14072f345d8eSLuigi Rizzo (!cqe->u0.s.ip_ver) && 14082f345d8eSLuigi Rizzo (rq->lro.lro_cnt != 0)) { 14092f345d8eSLuigi Rizzo 14102f345d8eSLuigi Rizzo if (tcp_lro_rx(&rq->lro, m, 0) == 0) { 14112f345d8eSLuigi Rizzo rq->lro_pkts_queued ++; 14122f345d8eSLuigi Rizzo goto post_done; 14132f345d8eSLuigi Rizzo } 14142f345d8eSLuigi Rizzo /* If LRO posting fails then try to post to STACK */ 14152f345d8eSLuigi Rizzo } 1416ad512958SBjoern A. Zeeb #endif 14172f345d8eSLuigi Rizzo 14182f345d8eSLuigi Rizzo (*sc->ifp->if_input) (sc->ifp, m); 1419ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 14202f345d8eSLuigi Rizzo post_done: 1421ad512958SBjoern A. Zeeb #endif 14222f345d8eSLuigi Rizzo /* Update rx stats per queue */ 14232f345d8eSLuigi Rizzo rq->rx_stats.rx_pkts++; 14242f345d8eSLuigi Rizzo rq->rx_stats.rx_bytes += cqe->u0.s.pkt_size; 14252f345d8eSLuigi Rizzo rq->rx_stats.rx_frags += cqe->u0.s.num_fragments; 14262f345d8eSLuigi Rizzo if (cqe->u0.s.pkt_type == OCE_MULTICAST_PACKET) 14272f345d8eSLuigi Rizzo rq->rx_stats.rx_mcast_pkts++; 14282f345d8eSLuigi Rizzo if (cqe->u0.s.pkt_type == OCE_UNICAST_PACKET) 14292f345d8eSLuigi Rizzo rq->rx_stats.rx_ucast_pkts++; 14302f345d8eSLuigi Rizzo } 14312f345d8eSLuigi Rizzo exit: 14322f345d8eSLuigi Rizzo return; 14332f345d8eSLuigi Rizzo } 14342f345d8eSLuigi Rizzo 14352f345d8eSLuigi Rizzo 14362f345d8eSLuigi Rizzo static void 14372f345d8eSLuigi Rizzo oce_discard_rx_comp(struct oce_rq *rq, struct oce_nic_rx_cqe *cqe) 14382f345d8eSLuigi Rizzo { 14392f345d8eSLuigi Rizzo uint32_t out, i = 0; 14402f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 14412f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) rq->parent; 14422f345d8eSLuigi Rizzo int num_frags = cqe->u0.s.num_fragments; 14432f345d8eSLuigi Rizzo 14442f345d8eSLuigi Rizzo for (i = 0; i < num_frags; i++) { 14452f345d8eSLuigi Rizzo if (rq->packets_out == rq->packets_in) { 14462f345d8eSLuigi Rizzo device_printf(sc->dev, 14472f345d8eSLuigi Rizzo "RQ transmit descriptor missing\n"); 14482f345d8eSLuigi Rizzo } 14492f345d8eSLuigi Rizzo out = rq->packets_out + 1; 14502f345d8eSLuigi Rizzo if (out == OCE_RQ_PACKET_ARRAY_SIZE) 14512f345d8eSLuigi Rizzo out = 0; 14522f345d8eSLuigi Rizzo pd = &rq->pckts[rq->packets_out]; 14532f345d8eSLuigi Rizzo rq->packets_out = out; 14542f345d8eSLuigi Rizzo 14552f345d8eSLuigi Rizzo bus_dmamap_sync(rq->tag, pd->map, BUS_DMASYNC_POSTWRITE); 14562f345d8eSLuigi Rizzo bus_dmamap_unload(rq->tag, pd->map); 14572f345d8eSLuigi Rizzo rq->pending--; 14582f345d8eSLuigi Rizzo m_freem(pd->mbuf); 14592f345d8eSLuigi Rizzo } 14602f345d8eSLuigi Rizzo 14612f345d8eSLuigi Rizzo } 14622f345d8eSLuigi Rizzo 14632f345d8eSLuigi Rizzo 14642f345d8eSLuigi Rizzo static int 14652f345d8eSLuigi Rizzo oce_cqe_vtp_valid(POCE_SOFTC sc, struct oce_nic_rx_cqe *cqe) 14662f345d8eSLuigi Rizzo { 14672f345d8eSLuigi Rizzo struct oce_nic_rx_cqe_v1 *cqe_v1; 14682f345d8eSLuigi Rizzo int vtp = 0; 14692f345d8eSLuigi Rizzo 14702f345d8eSLuigi Rizzo if (sc->be3_native) { 14712f345d8eSLuigi Rizzo cqe_v1 = (struct oce_nic_rx_cqe_v1 *)cqe; 14722f345d8eSLuigi Rizzo vtp = cqe_v1->u0.s.vlan_tag_present; 14739bd3250aSLuigi Rizzo } else 14742f345d8eSLuigi Rizzo vtp = cqe->u0.s.vlan_tag_present; 14752f345d8eSLuigi Rizzo 14762f345d8eSLuigi Rizzo return vtp; 14772f345d8eSLuigi Rizzo 14782f345d8eSLuigi Rizzo } 14792f345d8eSLuigi Rizzo 14802f345d8eSLuigi Rizzo 14812f345d8eSLuigi Rizzo static int 14822f345d8eSLuigi Rizzo oce_cqe_portid_valid(POCE_SOFTC sc, struct oce_nic_rx_cqe *cqe) 14832f345d8eSLuigi Rizzo { 14842f345d8eSLuigi Rizzo struct oce_nic_rx_cqe_v1 *cqe_v1; 14852f345d8eSLuigi Rizzo int port_id = 0; 14862f345d8eSLuigi Rizzo 1487291a1934SXin LI if (sc->be3_native && (IS_BE(sc) || IS_SH(sc))) { 14882f345d8eSLuigi Rizzo cqe_v1 = (struct oce_nic_rx_cqe_v1 *)cqe; 14892f345d8eSLuigi Rizzo port_id = cqe_v1->u0.s.port; 14902f345d8eSLuigi Rizzo if (sc->port_id != port_id) 14912f345d8eSLuigi Rizzo return 0; 14922f345d8eSLuigi Rizzo } else 14932f345d8eSLuigi Rizzo ;/* For BE3 legacy and Lancer this is dummy */ 14942f345d8eSLuigi Rizzo 14952f345d8eSLuigi Rizzo return 1; 14962f345d8eSLuigi Rizzo 14972f345d8eSLuigi Rizzo } 14982f345d8eSLuigi Rizzo 1499ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 15002f345d8eSLuigi Rizzo static void 15012f345d8eSLuigi Rizzo oce_rx_flush_lro(struct oce_rq *rq) 15022f345d8eSLuigi Rizzo { 15032f345d8eSLuigi Rizzo struct lro_ctrl *lro = &rq->lro; 15042f345d8eSLuigi Rizzo struct lro_entry *queued; 15052f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) rq->parent; 15062f345d8eSLuigi Rizzo 15072f345d8eSLuigi Rizzo if (!IF_LRO_ENABLED(sc)) 15082f345d8eSLuigi Rizzo return; 15092f345d8eSLuigi Rizzo 15102f345d8eSLuigi Rizzo while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { 15112f345d8eSLuigi Rizzo SLIST_REMOVE_HEAD(&lro->lro_active, next); 15122f345d8eSLuigi Rizzo tcp_lro_flush(lro, queued); 15132f345d8eSLuigi Rizzo } 15142f345d8eSLuigi Rizzo rq->lro_pkts_queued = 0; 15152f345d8eSLuigi Rizzo 15162f345d8eSLuigi Rizzo return; 15172f345d8eSLuigi Rizzo } 15182f345d8eSLuigi Rizzo 15192f345d8eSLuigi Rizzo 15202f345d8eSLuigi Rizzo static int 15212f345d8eSLuigi Rizzo oce_init_lro(POCE_SOFTC sc) 15222f345d8eSLuigi Rizzo { 15232f345d8eSLuigi Rizzo struct lro_ctrl *lro = NULL; 15242f345d8eSLuigi Rizzo int i = 0, rc = 0; 15252f345d8eSLuigi Rizzo 15262f345d8eSLuigi Rizzo for (i = 0; i < sc->nrqs; i++) { 15272f345d8eSLuigi Rizzo lro = &sc->rq[i]->lro; 15282f345d8eSLuigi Rizzo rc = tcp_lro_init(lro); 15292f345d8eSLuigi Rizzo if (rc != 0) { 15302f345d8eSLuigi Rizzo device_printf(sc->dev, "LRO init failed\n"); 15312f345d8eSLuigi Rizzo return rc; 15322f345d8eSLuigi Rizzo } 15332f345d8eSLuigi Rizzo lro->ifp = sc->ifp; 15342f345d8eSLuigi Rizzo } 15352f345d8eSLuigi Rizzo 15362f345d8eSLuigi Rizzo return rc; 15372f345d8eSLuigi Rizzo } 15389bd3250aSLuigi Rizzo 15392f345d8eSLuigi Rizzo 15402f345d8eSLuigi Rizzo void 15412f345d8eSLuigi Rizzo oce_free_lro(POCE_SOFTC sc) 15422f345d8eSLuigi Rizzo { 15432f345d8eSLuigi Rizzo struct lro_ctrl *lro = NULL; 15442f345d8eSLuigi Rizzo int i = 0; 15452f345d8eSLuigi Rizzo 15462f345d8eSLuigi Rizzo for (i = 0; i < sc->nrqs; i++) { 15472f345d8eSLuigi Rizzo lro = &sc->rq[i]->lro; 15482f345d8eSLuigi Rizzo if (lro) 15492f345d8eSLuigi Rizzo tcp_lro_free(lro); 15502f345d8eSLuigi Rizzo } 15512f345d8eSLuigi Rizzo } 1552cdaba892SXin LI #endif 15532f345d8eSLuigi Rizzo 15542f345d8eSLuigi Rizzo int 15552f345d8eSLuigi Rizzo oce_alloc_rx_bufs(struct oce_rq *rq, int count) 15562f345d8eSLuigi Rizzo { 15572f345d8eSLuigi Rizzo POCE_SOFTC sc = (POCE_SOFTC) rq->parent; 15582f345d8eSLuigi Rizzo int i, in, rc; 15592f345d8eSLuigi Rizzo struct oce_packet_desc *pd; 15602f345d8eSLuigi Rizzo bus_dma_segment_t segs[6]; 15612f345d8eSLuigi Rizzo int nsegs, added = 0; 15622f345d8eSLuigi Rizzo struct oce_nic_rqe *rqe; 15632f345d8eSLuigi Rizzo pd_rxulp_db_t rxdb_reg; 15642f345d8eSLuigi Rizzo 1565cdaba892SXin LI bzero(&rxdb_reg, sizeof(pd_rxulp_db_t)); 15662f345d8eSLuigi Rizzo for (i = 0; i < count; i++) { 15672f345d8eSLuigi Rizzo in = rq->packets_in + 1; 15682f345d8eSLuigi Rizzo if (in == OCE_RQ_PACKET_ARRAY_SIZE) 15692f345d8eSLuigi Rizzo in = 0; 15702f345d8eSLuigi Rizzo if (in == rq->packets_out) 15712f345d8eSLuigi Rizzo break; /* no more room */ 15722f345d8eSLuigi Rizzo 15732f345d8eSLuigi Rizzo pd = &rq->pckts[rq->packets_in]; 1574c6499eccSGleb Smirnoff pd->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 15752f345d8eSLuigi Rizzo if (pd->mbuf == NULL) 15762f345d8eSLuigi Rizzo break; 15772f345d8eSLuigi Rizzo 15782f345d8eSLuigi Rizzo pd->mbuf->m_len = pd->mbuf->m_pkthdr.len = MCLBYTES; 15792f345d8eSLuigi Rizzo rc = bus_dmamap_load_mbuf_sg(rq->tag, 15802f345d8eSLuigi Rizzo pd->map, 15812f345d8eSLuigi Rizzo pd->mbuf, 15822f345d8eSLuigi Rizzo segs, &nsegs, BUS_DMA_NOWAIT); 15832f345d8eSLuigi Rizzo if (rc) { 15842f345d8eSLuigi Rizzo m_free(pd->mbuf); 15852f345d8eSLuigi Rizzo break; 15862f345d8eSLuigi Rizzo } 15872f345d8eSLuigi Rizzo 15882f345d8eSLuigi Rizzo if (nsegs != 1) { 15892f345d8eSLuigi Rizzo i--; 15902f345d8eSLuigi Rizzo continue; 15912f345d8eSLuigi Rizzo } 15922f345d8eSLuigi Rizzo 15932f345d8eSLuigi Rizzo rq->packets_in = in; 15942f345d8eSLuigi Rizzo bus_dmamap_sync(rq->tag, pd->map, BUS_DMASYNC_PREREAD); 15952f345d8eSLuigi Rizzo 15962f345d8eSLuigi Rizzo rqe = RING_GET_PRODUCER_ITEM_VA(rq->ring, struct oce_nic_rqe); 15972f345d8eSLuigi Rizzo rqe->u0.s.frag_pa_hi = ADDR_HI(segs[0].ds_addr); 15982f345d8eSLuigi Rizzo rqe->u0.s.frag_pa_lo = ADDR_LO(segs[0].ds_addr); 15992f345d8eSLuigi Rizzo DW_SWAP(u32ptr(rqe), sizeof(struct oce_nic_rqe)); 16002f345d8eSLuigi Rizzo RING_PUT(rq->ring, 1); 16012f345d8eSLuigi Rizzo added++; 16022f345d8eSLuigi Rizzo rq->pending++; 16032f345d8eSLuigi Rizzo } 16042f345d8eSLuigi Rizzo if (added != 0) { 16052f345d8eSLuigi Rizzo for (i = added / OCE_MAX_RQ_POSTS; i > 0; i--) { 16062f345d8eSLuigi Rizzo rxdb_reg.bits.num_posted = OCE_MAX_RQ_POSTS; 16072f345d8eSLuigi Rizzo rxdb_reg.bits.qid = rq->rq_id; 16082f345d8eSLuigi Rizzo OCE_WRITE_REG32(sc, db, PD_RXULP_DB, rxdb_reg.dw0); 16092f345d8eSLuigi Rizzo added -= OCE_MAX_RQ_POSTS; 16102f345d8eSLuigi Rizzo } 16112f345d8eSLuigi Rizzo if (added > 0) { 16122f345d8eSLuigi Rizzo rxdb_reg.bits.qid = rq->rq_id; 16132f345d8eSLuigi Rizzo rxdb_reg.bits.num_posted = added; 16142f345d8eSLuigi Rizzo OCE_WRITE_REG32(sc, db, PD_RXULP_DB, rxdb_reg.dw0); 16152f345d8eSLuigi Rizzo } 16162f345d8eSLuigi Rizzo } 16172f345d8eSLuigi Rizzo 16182f345d8eSLuigi Rizzo return 0; 16192f345d8eSLuigi Rizzo } 16202f345d8eSLuigi Rizzo 16212f345d8eSLuigi Rizzo 16222f345d8eSLuigi Rizzo /* Handle the Completion Queue for receive */ 16232f345d8eSLuigi Rizzo uint16_t 16242f345d8eSLuigi Rizzo oce_rq_handler(void *arg) 16252f345d8eSLuigi Rizzo { 16262f345d8eSLuigi Rizzo struct oce_rq *rq = (struct oce_rq *)arg; 16272f345d8eSLuigi Rizzo struct oce_cq *cq = rq->cq; 16282f345d8eSLuigi Rizzo POCE_SOFTC sc = rq->parent; 16292f345d8eSLuigi Rizzo struct oce_nic_rx_cqe *cqe; 16302f345d8eSLuigi Rizzo int num_cqes = 0, rq_buffers_used = 0; 16312f345d8eSLuigi Rizzo 16322f345d8eSLuigi Rizzo 16332f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 16342f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 16352f345d8eSLuigi Rizzo cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_rx_cqe); 16362f345d8eSLuigi Rizzo while (cqe->u0.dw[2]) { 16372f345d8eSLuigi Rizzo DW_SWAP((uint32_t *) cqe, sizeof(oce_rq_cqe)); 16382f345d8eSLuigi Rizzo 16392f345d8eSLuigi Rizzo RING_GET(rq->ring, 1); 16402f345d8eSLuigi Rizzo if (cqe->u0.s.error == 0) { 16412f345d8eSLuigi Rizzo oce_rx(rq, cqe->u0.s.frag_index, cqe); 16422f345d8eSLuigi Rizzo } else { 16432f345d8eSLuigi Rizzo rq->rx_stats.rxcp_err++; 16442f345d8eSLuigi Rizzo sc->ifp->if_ierrors++; 16452f345d8eSLuigi Rizzo /* Post L3/L4 errors to stack.*/ 16462f345d8eSLuigi Rizzo oce_rx(rq, cqe->u0.s.frag_index, cqe); 16472f345d8eSLuigi Rizzo } 16482f345d8eSLuigi Rizzo rq->rx_stats.rx_compl++; 16492f345d8eSLuigi Rizzo cqe->u0.dw[2] = 0; 16502f345d8eSLuigi Rizzo 1651ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 16522f345d8eSLuigi Rizzo if (IF_LRO_ENABLED(sc) && rq->lro_pkts_queued >= 16) { 16532f345d8eSLuigi Rizzo oce_rx_flush_lro(rq); 16542f345d8eSLuigi Rizzo } 1655ad512958SBjoern A. Zeeb #endif 16562f345d8eSLuigi Rizzo 16572f345d8eSLuigi Rizzo RING_GET(cq->ring, 1); 16582f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 16592f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 16602f345d8eSLuigi Rizzo cqe = 16612f345d8eSLuigi Rizzo RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_nic_rx_cqe); 16622f345d8eSLuigi Rizzo num_cqes++; 16632f345d8eSLuigi Rizzo if (num_cqes >= (IS_XE201(sc) ? 8 : oce_max_rsp_handled)) 16642f345d8eSLuigi Rizzo break; 16652f345d8eSLuigi Rizzo } 16669bd3250aSLuigi Rizzo 1667ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 16682f345d8eSLuigi Rizzo if (IF_LRO_ENABLED(sc)) 16692f345d8eSLuigi Rizzo oce_rx_flush_lro(rq); 1670ad512958SBjoern A. Zeeb #endif 16712f345d8eSLuigi Rizzo 16722f345d8eSLuigi Rizzo if (num_cqes) { 16732f345d8eSLuigi Rizzo oce_arm_cq(sc, cq->cq_id, num_cqes, FALSE); 16742f345d8eSLuigi Rizzo rq_buffers_used = OCE_RQ_PACKET_ARRAY_SIZE - rq->pending; 16752f345d8eSLuigi Rizzo if (rq_buffers_used > 1) 16762f345d8eSLuigi Rizzo oce_alloc_rx_bufs(rq, (rq_buffers_used - 1)); 16772f345d8eSLuigi Rizzo } 16782f345d8eSLuigi Rizzo 16792f345d8eSLuigi Rizzo return 0; 16802f345d8eSLuigi Rizzo 16812f345d8eSLuigi Rizzo } 16822f345d8eSLuigi Rizzo 16832f345d8eSLuigi Rizzo 16842f345d8eSLuigi Rizzo 16852f345d8eSLuigi Rizzo 16862f345d8eSLuigi Rizzo /***************************************************************************** 16872f345d8eSLuigi Rizzo * Helper function prototypes in this file * 16882f345d8eSLuigi Rizzo *****************************************************************************/ 16892f345d8eSLuigi Rizzo 16902f345d8eSLuigi Rizzo static int 16912f345d8eSLuigi Rizzo oce_attach_ifp(POCE_SOFTC sc) 16922f345d8eSLuigi Rizzo { 16932f345d8eSLuigi Rizzo 16942f345d8eSLuigi Rizzo sc->ifp = if_alloc(IFT_ETHER); 16952f345d8eSLuigi Rizzo if (!sc->ifp) 16962f345d8eSLuigi Rizzo return ENOMEM; 16972f345d8eSLuigi Rizzo 16982f345d8eSLuigi Rizzo ifmedia_init(&sc->media, IFM_IMASK, oce_media_change, oce_media_status); 16992f345d8eSLuigi Rizzo ifmedia_add(&sc->media, IFM_ETHER | IFM_AUTO, 0, NULL); 17002f345d8eSLuigi Rizzo ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO); 17012f345d8eSLuigi Rizzo 17022f345d8eSLuigi Rizzo sc->ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST; 17032f345d8eSLuigi Rizzo sc->ifp->if_ioctl = oce_ioctl; 17042f345d8eSLuigi Rizzo sc->ifp->if_start = oce_start; 17052f345d8eSLuigi Rizzo sc->ifp->if_init = oce_init; 17062f345d8eSLuigi Rizzo sc->ifp->if_mtu = ETHERMTU; 17072f345d8eSLuigi Rizzo sc->ifp->if_softc = sc; 17082f345d8eSLuigi Rizzo #if __FreeBSD_version >= 800000 17092f345d8eSLuigi Rizzo sc->ifp->if_transmit = oce_multiq_start; 17102f345d8eSLuigi Rizzo sc->ifp->if_qflush = oce_multiq_flush; 17112f345d8eSLuigi Rizzo #endif 17122f345d8eSLuigi Rizzo 17132f345d8eSLuigi Rizzo if_initname(sc->ifp, 17142f345d8eSLuigi Rizzo device_get_name(sc->dev), device_get_unit(sc->dev)); 17152f345d8eSLuigi Rizzo 17162f345d8eSLuigi Rizzo sc->ifp->if_snd.ifq_drv_maxlen = OCE_MAX_TX_DESC - 1; 17172f345d8eSLuigi Rizzo IFQ_SET_MAXLEN(&sc->ifp->if_snd, sc->ifp->if_snd.ifq_drv_maxlen); 17182f345d8eSLuigi Rizzo IFQ_SET_READY(&sc->ifp->if_snd); 17192f345d8eSLuigi Rizzo 17202f345d8eSLuigi Rizzo sc->ifp->if_hwassist = OCE_IF_HWASSIST; 17212f345d8eSLuigi Rizzo sc->ifp->if_hwassist |= CSUM_TSO; 17222f345d8eSLuigi Rizzo sc->ifp->if_hwassist |= (CSUM_IP | CSUM_TCP | CSUM_UDP); 17232f345d8eSLuigi Rizzo 17242f345d8eSLuigi Rizzo sc->ifp->if_capabilities = OCE_IF_CAPABILITIES; 17252f345d8eSLuigi Rizzo sc->ifp->if_capabilities |= IFCAP_HWCSUM; 17262f345d8eSLuigi Rizzo sc->ifp->if_capabilities |= IFCAP_VLAN_HWFILTER; 17279bd3250aSLuigi Rizzo 1728ad512958SBjoern A. Zeeb #if defined(INET6) || defined(INET) 1729ad512958SBjoern A. Zeeb sc->ifp->if_capabilities |= IFCAP_TSO; 17302f345d8eSLuigi Rizzo sc->ifp->if_capabilities |= IFCAP_LRO; 17319bd3250aSLuigi Rizzo sc->ifp->if_capabilities |= IFCAP_VLAN_HWTSO; 1732ad512958SBjoern A. Zeeb #endif 17332f345d8eSLuigi Rizzo 17342f345d8eSLuigi Rizzo sc->ifp->if_capenable = sc->ifp->if_capabilities; 1735b245f96cSGleb Smirnoff sc->ifp->if_baudrate = IF_Gbps(10); 17362f345d8eSLuigi Rizzo 17375fbb6830SXin LI #if __FreeBSD_version >= 1000000 17385fbb6830SXin LI sc->ifp->if_hw_tsomax = OCE_MAX_TSO_SIZE; 17395fbb6830SXin LI #endif 17405fbb6830SXin LI 17412f345d8eSLuigi Rizzo ether_ifattach(sc->ifp, sc->macaddr.mac_addr); 17422f345d8eSLuigi Rizzo 17432f345d8eSLuigi Rizzo return 0; 17442f345d8eSLuigi Rizzo } 17452f345d8eSLuigi Rizzo 17462f345d8eSLuigi Rizzo 17472f345d8eSLuigi Rizzo static void 17482f345d8eSLuigi Rizzo oce_add_vlan(void *arg, struct ifnet *ifp, uint16_t vtag) 17492f345d8eSLuigi Rizzo { 17502f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 17512f345d8eSLuigi Rizzo 17522f345d8eSLuigi Rizzo if (ifp->if_softc != arg) 17532f345d8eSLuigi Rizzo return; 17542f345d8eSLuigi Rizzo if ((vtag == 0) || (vtag > 4095)) 17552f345d8eSLuigi Rizzo return; 17562f345d8eSLuigi Rizzo 17572f345d8eSLuigi Rizzo sc->vlan_tag[vtag] = 1; 17582f345d8eSLuigi Rizzo sc->vlans_added++; 17595fbb6830SXin LI if (sc->vlans_added <= (sc->max_vlans + 1)) 17602f345d8eSLuigi Rizzo oce_vid_config(sc); 17612f345d8eSLuigi Rizzo } 17622f345d8eSLuigi Rizzo 17632f345d8eSLuigi Rizzo 17642f345d8eSLuigi Rizzo static void 17652f345d8eSLuigi Rizzo oce_del_vlan(void *arg, struct ifnet *ifp, uint16_t vtag) 17662f345d8eSLuigi Rizzo { 17672f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 17682f345d8eSLuigi Rizzo 17692f345d8eSLuigi Rizzo if (ifp->if_softc != arg) 17702f345d8eSLuigi Rizzo return; 17712f345d8eSLuigi Rizzo if ((vtag == 0) || (vtag > 4095)) 17722f345d8eSLuigi Rizzo return; 17732f345d8eSLuigi Rizzo 17742f345d8eSLuigi Rizzo sc->vlan_tag[vtag] = 0; 17752f345d8eSLuigi Rizzo sc->vlans_added--; 17762f345d8eSLuigi Rizzo oce_vid_config(sc); 17772f345d8eSLuigi Rizzo } 17782f345d8eSLuigi Rizzo 17792f345d8eSLuigi Rizzo 17802f345d8eSLuigi Rizzo /* 17812f345d8eSLuigi Rizzo * A max of 64 vlans can be configured in BE. If the user configures 17822f345d8eSLuigi Rizzo * more, place the card in vlan promiscuous mode. 17832f345d8eSLuigi Rizzo */ 17842f345d8eSLuigi Rizzo static int 17852f345d8eSLuigi Rizzo oce_vid_config(POCE_SOFTC sc) 17862f345d8eSLuigi Rizzo { 17872f345d8eSLuigi Rizzo struct normal_vlan vtags[MAX_VLANFILTER_SIZE]; 17882f345d8eSLuigi Rizzo uint16_t ntags = 0, i; 17892f345d8eSLuigi Rizzo int status = 0; 17902f345d8eSLuigi Rizzo 17912f345d8eSLuigi Rizzo if ((sc->vlans_added <= MAX_VLANFILTER_SIZE) && 17922f345d8eSLuigi Rizzo (sc->ifp->if_capenable & IFCAP_VLAN_HWFILTER)) { 17932f345d8eSLuigi Rizzo for (i = 0; i < MAX_VLANS; i++) { 17942f345d8eSLuigi Rizzo if (sc->vlan_tag[i]) { 17952f345d8eSLuigi Rizzo vtags[ntags].vtag = i; 17962f345d8eSLuigi Rizzo ntags++; 17972f345d8eSLuigi Rizzo } 17982f345d8eSLuigi Rizzo } 17992f345d8eSLuigi Rizzo if (ntags) 18002f345d8eSLuigi Rizzo status = oce_config_vlan(sc, (uint8_t) sc->if_id, 18012f345d8eSLuigi Rizzo vtags, ntags, 1, 0); 18022f345d8eSLuigi Rizzo } else 18032f345d8eSLuigi Rizzo status = oce_config_vlan(sc, (uint8_t) sc->if_id, 18042f345d8eSLuigi Rizzo NULL, 0, 1, 1); 18052f345d8eSLuigi Rizzo return status; 18062f345d8eSLuigi Rizzo } 18072f345d8eSLuigi Rizzo 18082f345d8eSLuigi Rizzo 18092f345d8eSLuigi Rizzo static void 18102f345d8eSLuigi Rizzo oce_mac_addr_set(POCE_SOFTC sc) 18112f345d8eSLuigi Rizzo { 18122f345d8eSLuigi Rizzo uint32_t old_pmac_id = sc->pmac_id; 18132f345d8eSLuigi Rizzo int status = 0; 18142f345d8eSLuigi Rizzo 18152f345d8eSLuigi Rizzo 18162f345d8eSLuigi Rizzo status = bcmp((IF_LLADDR(sc->ifp)), sc->macaddr.mac_addr, 18172f345d8eSLuigi Rizzo sc->macaddr.size_of_struct); 18182f345d8eSLuigi Rizzo if (!status) 18192f345d8eSLuigi Rizzo return; 18202f345d8eSLuigi Rizzo 18212f345d8eSLuigi Rizzo status = oce_mbox_macaddr_add(sc, (uint8_t *)(IF_LLADDR(sc->ifp)), 18222f345d8eSLuigi Rizzo sc->if_id, &sc->pmac_id); 18232f345d8eSLuigi Rizzo if (!status) { 18242f345d8eSLuigi Rizzo status = oce_mbox_macaddr_del(sc, sc->if_id, old_pmac_id); 18252f345d8eSLuigi Rizzo bcopy((IF_LLADDR(sc->ifp)), sc->macaddr.mac_addr, 18262f345d8eSLuigi Rizzo sc->macaddr.size_of_struct); 18272f345d8eSLuigi Rizzo } 18282f345d8eSLuigi Rizzo if (status) 18292f345d8eSLuigi Rizzo device_printf(sc->dev, "Failed update macaddress\n"); 18302f345d8eSLuigi Rizzo 18312f345d8eSLuigi Rizzo } 18322f345d8eSLuigi Rizzo 18332f345d8eSLuigi Rizzo 18342f345d8eSLuigi Rizzo static int 18352f345d8eSLuigi Rizzo oce_handle_passthrough(struct ifnet *ifp, caddr_t data) 18362f345d8eSLuigi Rizzo { 18372f345d8eSLuigi Rizzo POCE_SOFTC sc = ifp->if_softc; 18382f345d8eSLuigi Rizzo struct ifreq *ifr = (struct ifreq *)data; 18392f345d8eSLuigi Rizzo int rc = ENXIO; 18402f345d8eSLuigi Rizzo char cookie[32] = {0}; 18412f345d8eSLuigi Rizzo void *priv_data = (void *)ifr->ifr_data; 18422f345d8eSLuigi Rizzo void *ioctl_ptr; 18432f345d8eSLuigi Rizzo uint32_t req_size; 18442f345d8eSLuigi Rizzo struct mbx_hdr req; 18452f345d8eSLuigi Rizzo OCE_DMA_MEM dma_mem; 1846cdaba892SXin LI struct mbx_common_get_cntl_attr *fw_cmd; 18472f345d8eSLuigi Rizzo 18482f345d8eSLuigi Rizzo if (copyin(priv_data, cookie, strlen(IOCTL_COOKIE))) 18492f345d8eSLuigi Rizzo return EFAULT; 18502f345d8eSLuigi Rizzo 18512f345d8eSLuigi Rizzo if (memcmp(cookie, IOCTL_COOKIE, strlen(IOCTL_COOKIE))) 18522f345d8eSLuigi Rizzo return EINVAL; 18532f345d8eSLuigi Rizzo 18542f345d8eSLuigi Rizzo ioctl_ptr = (char *)priv_data + strlen(IOCTL_COOKIE); 18552f345d8eSLuigi Rizzo if (copyin(ioctl_ptr, &req, sizeof(struct mbx_hdr))) 18562f345d8eSLuigi Rizzo return EFAULT; 18572f345d8eSLuigi Rizzo 18582f345d8eSLuigi Rizzo req_size = le32toh(req.u0.req.request_length); 18592f345d8eSLuigi Rizzo if (req_size > 65536) 18602f345d8eSLuigi Rizzo return EINVAL; 18612f345d8eSLuigi Rizzo 18622f345d8eSLuigi Rizzo req_size += sizeof(struct mbx_hdr); 18632f345d8eSLuigi Rizzo rc = oce_dma_alloc(sc, req_size, &dma_mem, 0); 18642f345d8eSLuigi Rizzo if (rc) 18652f345d8eSLuigi Rizzo return ENOMEM; 18662f345d8eSLuigi Rizzo 18672f345d8eSLuigi Rizzo if (copyin(ioctl_ptr, OCE_DMAPTR(&dma_mem,char), req_size)) { 18682f345d8eSLuigi Rizzo rc = EFAULT; 18692f345d8eSLuigi Rizzo goto dma_free; 18702f345d8eSLuigi Rizzo } 18712f345d8eSLuigi Rizzo 18722f345d8eSLuigi Rizzo rc = oce_pass_through_mbox(sc, &dma_mem, req_size); 18732f345d8eSLuigi Rizzo if (rc) { 18742f345d8eSLuigi Rizzo rc = EIO; 18752f345d8eSLuigi Rizzo goto dma_free; 18762f345d8eSLuigi Rizzo } 18772f345d8eSLuigi Rizzo 18782f345d8eSLuigi Rizzo if (copyout(OCE_DMAPTR(&dma_mem,char), ioctl_ptr, req_size)) 18792f345d8eSLuigi Rizzo rc = EFAULT; 18802f345d8eSLuigi Rizzo 1881cdaba892SXin LI /* 1882cdaba892SXin LI firmware is filling all the attributes for this ioctl except 1883cdaba892SXin LI the driver version..so fill it 1884cdaba892SXin LI */ 1885cdaba892SXin LI if(req.u0.rsp.opcode == OPCODE_COMMON_GET_CNTL_ATTRIBUTES) { 1886cdaba892SXin LI fw_cmd = (struct mbx_common_get_cntl_attr *) ioctl_ptr; 1887cdaba892SXin LI strncpy(fw_cmd->params.rsp.cntl_attr_info.hba_attr.drv_ver_str, 1888cdaba892SXin LI COMPONENT_REVISION, strlen(COMPONENT_REVISION)); 1889cdaba892SXin LI } 1890cdaba892SXin LI 18912f345d8eSLuigi Rizzo dma_free: 18922f345d8eSLuigi Rizzo oce_dma_free(sc, &dma_mem); 18932f345d8eSLuigi Rizzo return rc; 18942f345d8eSLuigi Rizzo 18952f345d8eSLuigi Rizzo } 18962f345d8eSLuigi Rizzo 1897cdaba892SXin LI static void 1898cdaba892SXin LI oce_eqd_set_periodic(POCE_SOFTC sc) 1899cdaba892SXin LI { 1900cdaba892SXin LI struct oce_set_eqd set_eqd[OCE_MAX_EQ]; 1901cdaba892SXin LI struct oce_aic_obj *aic; 1902cdaba892SXin LI struct oce_eq *eqo; 1903cdaba892SXin LI uint64_t now = 0, delta; 1904cdaba892SXin LI int eqd, i, num = 0; 1905cdaba892SXin LI uint32_t ips = 0; 1906cdaba892SXin LI int tps; 1907cdaba892SXin LI 1908cdaba892SXin LI for (i = 0 ; i < sc->neqs; i++) { 1909cdaba892SXin LI eqo = sc->eq[i]; 1910cdaba892SXin LI aic = &sc->aic_obj[i]; 1911cdaba892SXin LI /* When setting the static eq delay from the user space */ 1912cdaba892SXin LI if (!aic->enable) { 1913cdaba892SXin LI eqd = aic->et_eqd; 1914cdaba892SXin LI goto modify_eqd; 1915cdaba892SXin LI } 1916cdaba892SXin LI 1917cdaba892SXin LI now = ticks; 1918cdaba892SXin LI 1919cdaba892SXin LI /* Over flow check */ 1920cdaba892SXin LI if ((now < aic->ticks) || (eqo->intr < aic->intr_prev)) 1921cdaba892SXin LI goto done; 1922cdaba892SXin LI 1923cdaba892SXin LI delta = now - aic->ticks; 1924cdaba892SXin LI tps = delta/hz; 1925cdaba892SXin LI 1926cdaba892SXin LI /* Interrupt rate based on elapsed ticks */ 1927cdaba892SXin LI if(tps) 1928cdaba892SXin LI ips = (uint32_t)(eqo->intr - aic->intr_prev) / tps; 1929cdaba892SXin LI 1930cdaba892SXin LI if (ips > INTR_RATE_HWM) 1931cdaba892SXin LI eqd = aic->cur_eqd + 20; 1932cdaba892SXin LI else if (ips < INTR_RATE_LWM) 1933cdaba892SXin LI eqd = aic->cur_eqd / 2; 1934cdaba892SXin LI else 1935cdaba892SXin LI goto done; 1936cdaba892SXin LI 1937cdaba892SXin LI if (eqd < 10) 1938cdaba892SXin LI eqd = 0; 1939cdaba892SXin LI 1940cdaba892SXin LI /* Make sure that the eq delay is in the known range */ 1941cdaba892SXin LI eqd = min(eqd, aic->max_eqd); 1942cdaba892SXin LI eqd = max(eqd, aic->min_eqd); 1943cdaba892SXin LI 1944cdaba892SXin LI modify_eqd: 1945cdaba892SXin LI if (eqd != aic->cur_eqd) { 1946cdaba892SXin LI set_eqd[num].delay_multiplier = (eqd * 65)/100; 1947cdaba892SXin LI set_eqd[num].eq_id = eqo->eq_id; 1948cdaba892SXin LI aic->cur_eqd = eqd; 1949cdaba892SXin LI num++; 1950cdaba892SXin LI } 1951cdaba892SXin LI done: 1952cdaba892SXin LI aic->intr_prev = eqo->intr; 1953cdaba892SXin LI aic->ticks = now; 1954cdaba892SXin LI } 1955cdaba892SXin LI 1956cdaba892SXin LI /* Is there atleast one eq that needs to be modified? */ 1957cdaba892SXin LI if(num) 1958cdaba892SXin LI oce_mbox_eqd_modify_periodic(sc, set_eqd, num); 1959cdaba892SXin LI } 19602f345d8eSLuigi Rizzo 19615fbb6830SXin LI static void oce_detect_hw_error(POCE_SOFTC sc) 19625fbb6830SXin LI { 19635fbb6830SXin LI 19645fbb6830SXin LI uint32_t ue_low = 0, ue_high = 0, ue_low_mask = 0, ue_high_mask = 0; 19655fbb6830SXin LI uint32_t sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0; 19665fbb6830SXin LI uint32_t i; 19675fbb6830SXin LI 19685fbb6830SXin LI if (sc->hw_error) 19695fbb6830SXin LI return; 19705fbb6830SXin LI 19715fbb6830SXin LI if (IS_XE201(sc)) { 19725fbb6830SXin LI sliport_status = OCE_READ_REG32(sc, db, SLIPORT_STATUS_OFFSET); 19735fbb6830SXin LI if (sliport_status & SLIPORT_STATUS_ERR_MASK) { 19745fbb6830SXin LI sliport_err1 = OCE_READ_REG32(sc, db, SLIPORT_ERROR1_OFFSET); 19755fbb6830SXin LI sliport_err2 = OCE_READ_REG32(sc, db, SLIPORT_ERROR2_OFFSET); 19765fbb6830SXin LI } 19775fbb6830SXin LI } else { 19785fbb6830SXin LI ue_low = OCE_READ_REG32(sc, devcfg, PCICFG_UE_STATUS_LOW); 19795fbb6830SXin LI ue_high = OCE_READ_REG32(sc, devcfg, PCICFG_UE_STATUS_HIGH); 19805fbb6830SXin LI ue_low_mask = OCE_READ_REG32(sc, devcfg, PCICFG_UE_STATUS_LOW_MASK); 19815fbb6830SXin LI ue_high_mask = OCE_READ_REG32(sc, devcfg, PCICFG_UE_STATUS_HI_MASK); 19825fbb6830SXin LI 19835fbb6830SXin LI ue_low = (ue_low & ~ue_low_mask); 19845fbb6830SXin LI ue_high = (ue_high & ~ue_high_mask); 19855fbb6830SXin LI } 19865fbb6830SXin LI 19875fbb6830SXin LI /* On certain platforms BE hardware can indicate spurious UEs. 19885fbb6830SXin LI * Allow the h/w to stop working completely in case of a real UE. 19895fbb6830SXin LI * Hence not setting the hw_error for UE detection. 19905fbb6830SXin LI */ 19915fbb6830SXin LI if (sliport_status & SLIPORT_STATUS_ERR_MASK) { 19925fbb6830SXin LI sc->hw_error = TRUE; 19935fbb6830SXin LI device_printf(sc->dev, "Error detected in the card\n"); 19945fbb6830SXin LI } 19955fbb6830SXin LI 19965fbb6830SXin LI if (sliport_status & SLIPORT_STATUS_ERR_MASK) { 19975fbb6830SXin LI device_printf(sc->dev, 19985fbb6830SXin LI "ERR: sliport status 0x%x\n", sliport_status); 19995fbb6830SXin LI device_printf(sc->dev, 20005fbb6830SXin LI "ERR: sliport error1 0x%x\n", sliport_err1); 20015fbb6830SXin LI device_printf(sc->dev, 20025fbb6830SXin LI "ERR: sliport error2 0x%x\n", sliport_err2); 20035fbb6830SXin LI } 20045fbb6830SXin LI 20055fbb6830SXin LI if (ue_low) { 20065fbb6830SXin LI for (i = 0; ue_low; ue_low >>= 1, i++) { 20075fbb6830SXin LI if (ue_low & 1) 20085fbb6830SXin LI device_printf(sc->dev, "UE: %s bit set\n", 20095fbb6830SXin LI ue_status_low_desc[i]); 20105fbb6830SXin LI } 20115fbb6830SXin LI } 20125fbb6830SXin LI 20135fbb6830SXin LI if (ue_high) { 20145fbb6830SXin LI for (i = 0; ue_high; ue_high >>= 1, i++) { 20155fbb6830SXin LI if (ue_high & 1) 20165fbb6830SXin LI device_printf(sc->dev, "UE: %s bit set\n", 20175fbb6830SXin LI ue_status_hi_desc[i]); 20185fbb6830SXin LI } 20195fbb6830SXin LI } 20205fbb6830SXin LI 20215fbb6830SXin LI } 20225fbb6830SXin LI 20235fbb6830SXin LI 20242f345d8eSLuigi Rizzo static void 20252f345d8eSLuigi Rizzo oce_local_timer(void *arg) 20262f345d8eSLuigi Rizzo { 20272f345d8eSLuigi Rizzo POCE_SOFTC sc = arg; 20282f345d8eSLuigi Rizzo int i = 0; 20292f345d8eSLuigi Rizzo 20305fbb6830SXin LI oce_detect_hw_error(sc); 20312f345d8eSLuigi Rizzo oce_refresh_nic_stats(sc); 20322f345d8eSLuigi Rizzo oce_refresh_queue_stats(sc); 20332f345d8eSLuigi Rizzo oce_mac_addr_set(sc); 20342f345d8eSLuigi Rizzo 20352f345d8eSLuigi Rizzo /* TX Watch Dog*/ 20362f345d8eSLuigi Rizzo for (i = 0; i < sc->nwqs; i++) 20372f345d8eSLuigi Rizzo oce_tx_restart(sc, sc->wq[i]); 20382f345d8eSLuigi Rizzo 2039cdaba892SXin LI /* calculate and set the eq delay for optimal interrupt rate */ 2040291a1934SXin LI if (IS_BE(sc) || IS_SH(sc)) 2041cdaba892SXin LI oce_eqd_set_periodic(sc); 2042cdaba892SXin LI 20432f345d8eSLuigi Rizzo callout_reset(&sc->timer, hz, oce_local_timer, sc); 20442f345d8eSLuigi Rizzo } 20452f345d8eSLuigi Rizzo 20462f345d8eSLuigi Rizzo 2047beb0f7e7SJosh Paetzel /* NOTE : This should only be called holding 2048beb0f7e7SJosh Paetzel * DEVICE_LOCK. 2049beb0f7e7SJosh Paetzel */ 20502f345d8eSLuigi Rizzo static void 20512f345d8eSLuigi Rizzo oce_if_deactivate(POCE_SOFTC sc) 20522f345d8eSLuigi Rizzo { 20532f345d8eSLuigi Rizzo int i, mtime = 0; 20542f345d8eSLuigi Rizzo int wait_req = 0; 20552f345d8eSLuigi Rizzo struct oce_rq *rq; 20562f345d8eSLuigi Rizzo struct oce_wq *wq; 20572f345d8eSLuigi Rizzo struct oce_eq *eq; 20582f345d8eSLuigi Rizzo 20592f345d8eSLuigi Rizzo sc->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 20602f345d8eSLuigi Rizzo 20612f345d8eSLuigi Rizzo /*Wait for max of 400ms for TX completions to be done */ 20622f345d8eSLuigi Rizzo while (mtime < 400) { 20632f345d8eSLuigi Rizzo wait_req = 0; 20642f345d8eSLuigi Rizzo for_all_wq_queues(sc, wq, i) { 20652f345d8eSLuigi Rizzo if (wq->ring->num_used) { 20662f345d8eSLuigi Rizzo wait_req = 1; 20672f345d8eSLuigi Rizzo DELAY(1); 20682f345d8eSLuigi Rizzo break; 20692f345d8eSLuigi Rizzo } 20702f345d8eSLuigi Rizzo } 20712f345d8eSLuigi Rizzo mtime += 1; 20722f345d8eSLuigi Rizzo if (!wait_req) 20732f345d8eSLuigi Rizzo break; 20742f345d8eSLuigi Rizzo } 20752f345d8eSLuigi Rizzo 20762f345d8eSLuigi Rizzo /* Stop intrs and finish any bottom halves pending */ 20772f345d8eSLuigi Rizzo oce_hw_intr_disable(sc); 20782f345d8eSLuigi Rizzo 2079cdaba892SXin LI /* Since taskqueue_drain takes a Gaint Lock, We should not acquire 2080beb0f7e7SJosh Paetzel any other lock. So unlock device lock and require after 2081beb0f7e7SJosh Paetzel completing taskqueue_drain. 2082beb0f7e7SJosh Paetzel */ 2083beb0f7e7SJosh Paetzel UNLOCK(&sc->dev_lock); 20842f345d8eSLuigi Rizzo for (i = 0; i < sc->intr_count; i++) { 20852f345d8eSLuigi Rizzo if (sc->intrs[i].tq != NULL) { 20862f345d8eSLuigi Rizzo taskqueue_drain(sc->intrs[i].tq, &sc->intrs[i].task); 20872f345d8eSLuigi Rizzo } 20882f345d8eSLuigi Rizzo } 2089beb0f7e7SJosh Paetzel LOCK(&sc->dev_lock); 20902f345d8eSLuigi Rizzo 20912f345d8eSLuigi Rizzo /* Delete RX queue in card with flush param */ 20922f345d8eSLuigi Rizzo oce_stop_rx(sc); 20932f345d8eSLuigi Rizzo 20942f345d8eSLuigi Rizzo /* Invalidate any pending cq and eq entries*/ 20952f345d8eSLuigi Rizzo for_all_evnt_queues(sc, eq, i) 20962f345d8eSLuigi Rizzo oce_drain_eq(eq); 20972f345d8eSLuigi Rizzo for_all_rq_queues(sc, rq, i) 20982f345d8eSLuigi Rizzo oce_drain_rq_cq(rq); 20992f345d8eSLuigi Rizzo for_all_wq_queues(sc, wq, i) 21002f345d8eSLuigi Rizzo oce_drain_wq_cq(wq); 21012f345d8eSLuigi Rizzo 21022f345d8eSLuigi Rizzo /* But still we need to get MCC aync events. 21032f345d8eSLuigi Rizzo So enable intrs and also arm first EQ 21042f345d8eSLuigi Rizzo */ 21052f345d8eSLuigi Rizzo oce_hw_intr_enable(sc); 21062f345d8eSLuigi Rizzo oce_arm_eq(sc, sc->eq[0]->eq_id, 0, TRUE, FALSE); 21072f345d8eSLuigi Rizzo 21082f345d8eSLuigi Rizzo DELAY(10); 21092f345d8eSLuigi Rizzo } 21102f345d8eSLuigi Rizzo 21112f345d8eSLuigi Rizzo 21122f345d8eSLuigi Rizzo static void 21132f345d8eSLuigi Rizzo oce_if_activate(POCE_SOFTC sc) 21142f345d8eSLuigi Rizzo { 21152f345d8eSLuigi Rizzo struct oce_eq *eq; 21162f345d8eSLuigi Rizzo struct oce_rq *rq; 21172f345d8eSLuigi Rizzo struct oce_wq *wq; 21182f345d8eSLuigi Rizzo int i, rc = 0; 21192f345d8eSLuigi Rizzo 21202f345d8eSLuigi Rizzo sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 21212f345d8eSLuigi Rizzo 21222f345d8eSLuigi Rizzo oce_hw_intr_disable(sc); 21232f345d8eSLuigi Rizzo 21242f345d8eSLuigi Rizzo oce_start_rx(sc); 21252f345d8eSLuigi Rizzo 21262f345d8eSLuigi Rizzo for_all_rq_queues(sc, rq, i) { 21272f345d8eSLuigi Rizzo rc = oce_start_rq(rq); 21282f345d8eSLuigi Rizzo if (rc) 21292f345d8eSLuigi Rizzo device_printf(sc->dev, "Unable to start RX\n"); 21302f345d8eSLuigi Rizzo } 21312f345d8eSLuigi Rizzo 21322f345d8eSLuigi Rizzo for_all_wq_queues(sc, wq, i) { 21332f345d8eSLuigi Rizzo rc = oce_start_wq(wq); 21342f345d8eSLuigi Rizzo if (rc) 21352f345d8eSLuigi Rizzo device_printf(sc->dev, "Unable to start TX\n"); 21362f345d8eSLuigi Rizzo } 21372f345d8eSLuigi Rizzo 21382f345d8eSLuigi Rizzo 21392f345d8eSLuigi Rizzo for_all_evnt_queues(sc, eq, i) 21402f345d8eSLuigi Rizzo oce_arm_eq(sc, eq->eq_id, 0, TRUE, FALSE); 21412f345d8eSLuigi Rizzo 21422f345d8eSLuigi Rizzo oce_hw_intr_enable(sc); 21432f345d8eSLuigi Rizzo 21442f345d8eSLuigi Rizzo } 21452f345d8eSLuigi Rizzo 21469bd3250aSLuigi Rizzo static void 21479bd3250aSLuigi Rizzo process_link_state(POCE_SOFTC sc, struct oce_async_cqe_link_state *acqe) 21482f345d8eSLuigi Rizzo { 21499bd3250aSLuigi Rizzo /* Update Link status */ 21502f345d8eSLuigi Rizzo if ((acqe->u0.s.link_status & ~ASYNC_EVENT_LOGICAL) == 21512f345d8eSLuigi Rizzo ASYNC_EVENT_LINK_UP) { 21522f345d8eSLuigi Rizzo sc->link_status = ASYNC_EVENT_LINK_UP; 21532f345d8eSLuigi Rizzo if_link_state_change(sc->ifp, LINK_STATE_UP); 21542f345d8eSLuigi Rizzo } else { 21552f345d8eSLuigi Rizzo sc->link_status = ASYNC_EVENT_LINK_DOWN; 21562f345d8eSLuigi Rizzo if_link_state_change(sc->ifp, LINK_STATE_DOWN); 21572f345d8eSLuigi Rizzo } 21589bd3250aSLuigi Rizzo } 21599bd3250aSLuigi Rizzo 21609bd3250aSLuigi Rizzo 21619bd3250aSLuigi Rizzo /* Handle the Completion Queue for the Mailbox/Async notifications */ 21629bd3250aSLuigi Rizzo uint16_t 21639bd3250aSLuigi Rizzo oce_mq_handler(void *arg) 21649bd3250aSLuigi Rizzo { 21659bd3250aSLuigi Rizzo struct oce_mq *mq = (struct oce_mq *)arg; 21669bd3250aSLuigi Rizzo POCE_SOFTC sc = mq->parent; 21679bd3250aSLuigi Rizzo struct oce_cq *cq = mq->cq; 21689bd3250aSLuigi Rizzo int num_cqes = 0, evt_type = 0, optype = 0; 21699bd3250aSLuigi Rizzo struct oce_mq_cqe *cqe; 21709bd3250aSLuigi Rizzo struct oce_async_cqe_link_state *acqe; 21719bd3250aSLuigi Rizzo struct oce_async_event_grp5_pvid_state *gcqe; 2172cdaba892SXin LI struct oce_async_event_qnq *dbgcqe; 21739bd3250aSLuigi Rizzo 21749bd3250aSLuigi Rizzo 21759bd3250aSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 21769bd3250aSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 21779bd3250aSLuigi Rizzo cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_mq_cqe); 21789bd3250aSLuigi Rizzo 21799bd3250aSLuigi Rizzo while (cqe->u0.dw[3]) { 21809bd3250aSLuigi Rizzo DW_SWAP((uint32_t *) cqe, sizeof(oce_mq_cqe)); 21819bd3250aSLuigi Rizzo if (cqe->u0.s.async_event) { 21829bd3250aSLuigi Rizzo evt_type = cqe->u0.s.event_type; 21839bd3250aSLuigi Rizzo optype = cqe->u0.s.async_type; 21849bd3250aSLuigi Rizzo if (evt_type == ASYNC_EVENT_CODE_LINK_STATE) { 21859bd3250aSLuigi Rizzo /* Link status evt */ 21869bd3250aSLuigi Rizzo acqe = (struct oce_async_cqe_link_state *)cqe; 21879bd3250aSLuigi Rizzo process_link_state(sc, acqe); 21889bd3250aSLuigi Rizzo } else if ((evt_type == ASYNC_EVENT_GRP5) && 21899bd3250aSLuigi Rizzo (optype == ASYNC_EVENT_PVID_STATE)) { 21909bd3250aSLuigi Rizzo /* GRP5 PVID */ 21919bd3250aSLuigi Rizzo gcqe = 21929bd3250aSLuigi Rizzo (struct oce_async_event_grp5_pvid_state *)cqe; 21939bd3250aSLuigi Rizzo if (gcqe->enabled) 21949bd3250aSLuigi Rizzo sc->pvid = gcqe->tag & VLAN_VID_MASK; 21959bd3250aSLuigi Rizzo else 21969bd3250aSLuigi Rizzo sc->pvid = 0; 21979bd3250aSLuigi Rizzo 21982f345d8eSLuigi Rizzo } 2199cdaba892SXin LI else if(evt_type == ASYNC_EVENT_CODE_DEBUG && 2200cdaba892SXin LI optype == ASYNC_EVENT_DEBUG_QNQ) { 2201cdaba892SXin LI dbgcqe = 2202cdaba892SXin LI (struct oce_async_event_qnq *)cqe; 2203cdaba892SXin LI if(dbgcqe->valid) 2204cdaba892SXin LI sc->qnqid = dbgcqe->vlan_tag; 2205cdaba892SXin LI sc->qnq_debug_event = TRUE; 2206cdaba892SXin LI } 22072f345d8eSLuigi Rizzo } 22082f345d8eSLuigi Rizzo cqe->u0.dw[3] = 0; 22092f345d8eSLuigi Rizzo RING_GET(cq->ring, 1); 22102f345d8eSLuigi Rizzo bus_dmamap_sync(cq->ring->dma.tag, 22112f345d8eSLuigi Rizzo cq->ring->dma.map, BUS_DMASYNC_POSTWRITE); 22122f345d8eSLuigi Rizzo cqe = RING_GET_CONSUMER_ITEM_VA(cq->ring, struct oce_mq_cqe); 22132f345d8eSLuigi Rizzo num_cqes++; 22142f345d8eSLuigi Rizzo } 22152f345d8eSLuigi Rizzo 22162f345d8eSLuigi Rizzo if (num_cqes) 22172f345d8eSLuigi Rizzo oce_arm_cq(sc, cq->cq_id, num_cqes, FALSE); 22182f345d8eSLuigi Rizzo 22192f345d8eSLuigi Rizzo return 0; 22202f345d8eSLuigi Rizzo } 22212f345d8eSLuigi Rizzo 22222f345d8eSLuigi Rizzo 22232f345d8eSLuigi Rizzo static void 22242f345d8eSLuigi Rizzo setup_max_queues_want(POCE_SOFTC sc) 22252f345d8eSLuigi Rizzo { 22262f345d8eSLuigi Rizzo /* Check if it is FLEX machine. Is so dont use RSS */ 22272f345d8eSLuigi Rizzo if ((sc->function_mode & FNM_FLEX10_MODE) || 22289bd3250aSLuigi Rizzo (sc->function_mode & FNM_UMC_MODE) || 22299bd3250aSLuigi Rizzo (sc->function_mode & FNM_VNIC_MODE) || 2230291a1934SXin LI (!is_rss_enabled(sc)) || 2231*a4f734b4SXin LI IS_BE2(sc)) { 22322f345d8eSLuigi Rizzo sc->nrqs = 1; 22332f345d8eSLuigi Rizzo sc->nwqs = 1; 22345fbb6830SXin LI } else { 22355fbb6830SXin LI sc->nrqs = MIN(OCE_NCPUS, sc->nrssqs) + 1; 22365fbb6830SXin LI sc->nwqs = MIN(OCE_NCPUS, sc->nrssqs); 22372f345d8eSLuigi Rizzo } 2238*a4f734b4SXin LI 2239*a4f734b4SXin LI if (IS_BE2(sc) && is_rss_enabled(sc)) 2240*a4f734b4SXin LI sc->nrqs = MIN(OCE_NCPUS, sc->nrssqs) + 1; 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 } 2254*a4f734b4SXin LI 2255*a4f734b4SXin LI if (IS_BE2(sc)) 2256*a4f734b4SXin LI sc->nwqs = 1; 22572f345d8eSLuigi Rizzo } 22582f345d8eSLuigi Rizzo 2259cdaba892SXin LI static int 2260cdaba892SXin LI oce_check_ipv6_ext_hdr(struct mbuf *m) 2261cdaba892SXin LI { 2262cdaba892SXin LI struct ether_header *eh = mtod(m, struct ether_header *); 2263cdaba892SXin LI caddr_t m_datatemp = m->m_data; 2264cdaba892SXin LI 2265cdaba892SXin LI if (eh->ether_type == htons(ETHERTYPE_IPV6)) { 2266cdaba892SXin LI m->m_data += sizeof(struct ether_header); 2267cdaba892SXin LI struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 2268cdaba892SXin LI 2269cdaba892SXin LI if((ip6->ip6_nxt != IPPROTO_TCP) && \ 2270cdaba892SXin LI (ip6->ip6_nxt != IPPROTO_UDP)){ 2271cdaba892SXin LI struct ip6_ext *ip6e = NULL; 2272cdaba892SXin LI m->m_data += sizeof(struct ip6_hdr); 2273cdaba892SXin LI 2274cdaba892SXin LI ip6e = (struct ip6_ext *) mtod(m, struct ip6_ext *); 2275cdaba892SXin LI if(ip6e->ip6e_len == 0xff) { 2276cdaba892SXin LI m->m_data = m_datatemp; 2277cdaba892SXin LI return TRUE; 2278cdaba892SXin LI } 2279cdaba892SXin LI } 2280cdaba892SXin LI m->m_data = m_datatemp; 2281cdaba892SXin LI } 2282cdaba892SXin LI return FALSE; 2283cdaba892SXin LI } 2284cdaba892SXin LI 2285cdaba892SXin LI static int 2286cdaba892SXin LI is_be3_a1(POCE_SOFTC sc) 2287cdaba892SXin LI { 2288cdaba892SXin LI if((sc->flags & OCE_FLAGS_BE3) && ((sc->asic_revision & 0xFF) < 2)) { 2289cdaba892SXin LI return TRUE; 2290cdaba892SXin LI } 2291cdaba892SXin LI return FALSE; 2292cdaba892SXin LI } 2293cdaba892SXin LI 2294cdaba892SXin LI static struct mbuf * 2295cdaba892SXin LI oce_insert_vlan_tag(POCE_SOFTC sc, struct mbuf *m, boolean_t *complete) 2296cdaba892SXin LI { 2297cdaba892SXin LI uint16_t vlan_tag = 0; 2298cdaba892SXin LI 2299cdaba892SXin LI if(!M_WRITABLE(m)) 2300cdaba892SXin LI return NULL; 2301cdaba892SXin LI 2302cdaba892SXin LI /* Embed vlan tag in the packet if it is not part of it */ 2303cdaba892SXin LI if(m->m_flags & M_VLANTAG) { 2304cdaba892SXin LI vlan_tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); 2305cdaba892SXin LI m->m_flags &= ~M_VLANTAG; 2306cdaba892SXin LI } 2307cdaba892SXin LI 2308cdaba892SXin LI /* if UMC, ignore vlan tag insertion and instead insert pvid */ 2309cdaba892SXin LI if(sc->pvid) { 2310cdaba892SXin LI if(!vlan_tag) 2311cdaba892SXin LI vlan_tag = sc->pvid; 2312cdaba892SXin LI *complete = FALSE; 2313cdaba892SXin LI } 2314cdaba892SXin LI 2315cdaba892SXin LI if(vlan_tag) { 2316cdaba892SXin LI m = ether_vlanencap(m, vlan_tag); 2317cdaba892SXin LI } 2318cdaba892SXin LI 2319cdaba892SXin LI if(sc->qnqid) { 2320cdaba892SXin LI m = ether_vlanencap(m, sc->qnqid); 2321cdaba892SXin LI *complete = FALSE; 2322cdaba892SXin LI } 2323cdaba892SXin LI return m; 2324cdaba892SXin LI } 2325cdaba892SXin LI 2326cdaba892SXin LI static int 2327cdaba892SXin LI oce_tx_asic_stall_verify(POCE_SOFTC sc, struct mbuf *m) 2328cdaba892SXin LI { 2329cdaba892SXin LI if(is_be3_a1(sc) && IS_QNQ_OR_UMC(sc) && \ 2330cdaba892SXin LI oce_check_ipv6_ext_hdr(m)) { 2331cdaba892SXin LI return TRUE; 2332cdaba892SXin LI } 2333cdaba892SXin LI return FALSE; 2334cdaba892SXin LI } 2335291a1934SXin LI 2336291a1934SXin LI static void 2337291a1934SXin LI oce_get_config(POCE_SOFTC sc) 2338291a1934SXin LI { 2339291a1934SXin LI int rc = 0; 2340291a1934SXin LI uint32_t max_rss = 0; 2341291a1934SXin LI 2342291a1934SXin LI if ((IS_BE(sc) || IS_SH(sc)) && (!sc->be3_native)) 2343291a1934SXin LI max_rss = OCE_LEGACY_MODE_RSS; 2344291a1934SXin LI else 2345291a1934SXin LI max_rss = OCE_MAX_RSS; 2346291a1934SXin LI 2347291a1934SXin LI if (!IS_BE(sc)) { 2348b41206d8SXin LI rc = oce_get_profile_config(sc, max_rss); 2349291a1934SXin LI if (rc) { 2350291a1934SXin LI sc->nwqs = OCE_MAX_WQ; 2351291a1934SXin LI sc->nrssqs = max_rss; 2352291a1934SXin LI sc->nrqs = sc->nrssqs + 1; 2353291a1934SXin LI } 2354291a1934SXin LI } 2355b41206d8SXin LI else { /* For BE3 don't rely on fw for determining the resources */ 2356291a1934SXin LI sc->nrssqs = max_rss; 2357291a1934SXin LI sc->nrqs = sc->nrssqs + 1; 2358291a1934SXin LI sc->nwqs = OCE_MAX_WQ; 2359b41206d8SXin LI sc->max_vlans = MAX_VLANFILTER_SIZE; 2360291a1934SXin LI } 2361291a1934SXin LI } 2362