/********************************************************************** 
 | 
 * Author: Cavium, Inc. 
 | 
 * 
 | 
 * Contact: support@cavium.com 
 | 
 *          Please include "LiquidIO" in the subject. 
 | 
 * 
 | 
 * Copyright (c) 2003-2016 Cavium, Inc. 
 | 
 * 
 | 
 * This file is free software; you can redistribute it and/or modify 
 | 
 * it under the terms of the GNU General Public License, Version 2, as 
 | 
 * published by the Free Software Foundation. 
 | 
 * 
 | 
 * This file is distributed in the hope that it will be useful, but 
 | 
 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty 
 | 
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or 
 | 
 * NONINFRINGEMENT.  See the GNU General Public License for more details. 
 | 
 ***********************************************************************/ 
 | 
#include <linux/pci.h> 
 | 
#include <linux/if_vlan.h> 
 | 
#include "liquidio_common.h" 
 | 
#include "octeon_droq.h" 
 | 
#include "octeon_iq.h" 
 | 
#include "response_manager.h" 
 | 
#include "octeon_device.h" 
 | 
#include "octeon_nic.h" 
 | 
#include "octeon_main.h" 
 | 
#include "octeon_network.h" 
 | 
  
 | 
/* OOM task polling interval */ 
 | 
#define LIO_OOM_POLL_INTERVAL_MS 250 
 | 
  
 | 
#define OCTNIC_MAX_SG  MAX_SKB_FRAGS 
 | 
  
 | 
/** 
 | 
 * lio_delete_glists - Delete gather lists 
 | 
 * @lio: per-network private data 
 | 
 */ 
 | 
void lio_delete_glists(struct lio *lio) 
 | 
{ 
 | 
    struct octnic_gather *g; 
 | 
    int i; 
 | 
  
 | 
    kfree(lio->glist_lock); 
 | 
    lio->glist_lock = NULL; 
 | 
  
 | 
    if (!lio->glist) 
 | 
        return; 
 | 
  
 | 
    for (i = 0; i < lio->oct_dev->num_iqs; i++) { 
 | 
        do { 
 | 
            g = (struct octnic_gather *) 
 | 
                lio_list_delete_head(&lio->glist[i]); 
 | 
            kfree(g); 
 | 
        } while (g); 
 | 
  
 | 
        if (lio->glists_virt_base && lio->glists_virt_base[i] && 
 | 
            lio->glists_dma_base && lio->glists_dma_base[i]) { 
 | 
            lio_dma_free(lio->oct_dev, 
 | 
                     lio->glist_entry_size * lio->tx_qsize, 
 | 
                     lio->glists_virt_base[i], 
 | 
                     lio->glists_dma_base[i]); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    kfree(lio->glists_virt_base); 
 | 
    lio->glists_virt_base = NULL; 
 | 
  
 | 
    kfree(lio->glists_dma_base); 
 | 
    lio->glists_dma_base = NULL; 
 | 
  
 | 
    kfree(lio->glist); 
 | 
    lio->glist = NULL; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * lio_setup_glists - Setup gather lists 
 | 
 * @oct: octeon_device 
 | 
 * @lio: per-network private data 
 | 
 * @num_iqs: count of iqs to allocate 
 | 
 */ 
 | 
int lio_setup_glists(struct octeon_device *oct, struct lio *lio, int num_iqs) 
 | 
{ 
 | 
    struct octnic_gather *g; 
 | 
    int i, j; 
 | 
  
 | 
    lio->glist_lock = 
 | 
        kcalloc(num_iqs, sizeof(*lio->glist_lock), GFP_KERNEL); 
 | 
    if (!lio->glist_lock) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    lio->glist = 
 | 
        kcalloc(num_iqs, sizeof(*lio->glist), GFP_KERNEL); 
 | 
    if (!lio->glist) { 
 | 
        kfree(lio->glist_lock); 
 | 
        lio->glist_lock = NULL; 
 | 
        return -ENOMEM; 
 | 
    } 
 | 
  
 | 
    lio->glist_entry_size = 
 | 
        ROUNDUP8((ROUNDUP4(OCTNIC_MAX_SG) >> 2) * OCT_SG_ENTRY_SIZE); 
 | 
  
 | 
    /* allocate memory to store virtual and dma base address of 
 | 
     * per glist consistent memory 
 | 
     */ 
 | 
    lio->glists_virt_base = kcalloc(num_iqs, sizeof(*lio->glists_virt_base), 
 | 
                    GFP_KERNEL); 
 | 
    lio->glists_dma_base = kcalloc(num_iqs, sizeof(*lio->glists_dma_base), 
 | 
                       GFP_KERNEL); 
 | 
  
 | 
    if (!lio->glists_virt_base || !lio->glists_dma_base) { 
 | 
        lio_delete_glists(lio); 
 | 
        return -ENOMEM; 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < num_iqs; i++) { 
 | 
        int numa_node = dev_to_node(&oct->pci_dev->dev); 
 | 
  
 | 
        spin_lock_init(&lio->glist_lock[i]); 
 | 
  
 | 
        INIT_LIST_HEAD(&lio->glist[i]); 
 | 
  
 | 
        lio->glists_virt_base[i] = 
 | 
            lio_dma_alloc(oct, 
 | 
                      lio->glist_entry_size * lio->tx_qsize, 
 | 
                      &lio->glists_dma_base[i]); 
 | 
  
 | 
        if (!lio->glists_virt_base[i]) { 
 | 
            lio_delete_glists(lio); 
 | 
            return -ENOMEM; 
 | 
        } 
 | 
  
 | 
        for (j = 0; j < lio->tx_qsize; j++) { 
 | 
            g = kzalloc_node(sizeof(*g), GFP_KERNEL, 
 | 
                     numa_node); 
 | 
            if (!g) 
 | 
                g = kzalloc(sizeof(*g), GFP_KERNEL); 
 | 
            if (!g) 
 | 
                break; 
 | 
  
 | 
            g->sg = lio->glists_virt_base[i] + 
 | 
                (j * lio->glist_entry_size); 
 | 
  
 | 
            g->sg_dma_ptr = lio->glists_dma_base[i] + 
 | 
                    (j * lio->glist_entry_size); 
 | 
  
 | 
            list_add_tail(&g->list, &lio->glist[i]); 
 | 
        } 
 | 
  
 | 
        if (j != lio->tx_qsize) { 
 | 
            lio_delete_glists(lio); 
 | 
            return -ENOMEM; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int liquidio_set_feature(struct net_device *netdev, int cmd, u16 param1) 
 | 
{ 
 | 
    struct lio *lio = GET_LIO(netdev); 
 | 
    struct octeon_device *oct = lio->oct_dev; 
 | 
    struct octnic_ctrl_pkt nctrl; 
 | 
    int ret = 0; 
 | 
  
 | 
    memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); 
 | 
  
 | 
    nctrl.ncmd.u64 = 0; 
 | 
    nctrl.ncmd.s.cmd = cmd; 
 | 
    nctrl.ncmd.s.param1 = param1; 
 | 
    nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; 
 | 
    nctrl.netpndev = (u64)netdev; 
 | 
    nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; 
 | 
  
 | 
    ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); 
 | 
    if (ret) { 
 | 
        dev_err(&oct->pci_dev->dev, "Feature change failed in core (ret: 0x%x)\n", 
 | 
            ret); 
 | 
        if (ret > 0) 
 | 
            ret = -EIO; 
 | 
    } 
 | 
    return ret; 
 | 
} 
 | 
  
 | 
void octeon_report_tx_completion_to_bql(void *txq, unsigned int pkts_compl, 
 | 
                    unsigned int bytes_compl) 
 | 
{ 
 | 
    struct netdev_queue *netdev_queue = txq; 
 | 
  
 | 
    netdev_tx_completed_queue(netdev_queue, pkts_compl, bytes_compl); 
 | 
} 
 | 
  
 | 
void octeon_update_tx_completion_counters(void *buf, int reqtype, 
 | 
                      unsigned int *pkts_compl, 
 | 
                      unsigned int *bytes_compl) 
 | 
{ 
 | 
    struct octnet_buf_free_info *finfo; 
 | 
    struct sk_buff *skb = NULL; 
 | 
    struct octeon_soft_command *sc; 
 | 
  
 | 
    switch (reqtype) { 
 | 
    case REQTYPE_NORESP_NET: 
 | 
    case REQTYPE_NORESP_NET_SG: 
 | 
        finfo = buf; 
 | 
        skb = finfo->skb; 
 | 
        break; 
 | 
  
 | 
    case REQTYPE_RESP_NET_SG: 
 | 
    case REQTYPE_RESP_NET: 
 | 
        sc = buf; 
 | 
        skb = sc->callback_arg; 
 | 
        break; 
 | 
  
 | 
    default: 
 | 
        return; 
 | 
    } 
 | 
  
 | 
    (*pkts_compl)++; 
 | 
    *bytes_compl += skb->len; 
 | 
} 
 | 
  
 | 
int octeon_report_sent_bytes_to_bql(void *buf, int reqtype) 
 | 
{ 
 | 
    struct octnet_buf_free_info *finfo; 
 | 
    struct sk_buff *skb; 
 | 
    struct octeon_soft_command *sc; 
 | 
    struct netdev_queue *txq; 
 | 
  
 | 
    switch (reqtype) { 
 | 
    case REQTYPE_NORESP_NET: 
 | 
    case REQTYPE_NORESP_NET_SG: 
 | 
        finfo = buf; 
 | 
        skb = finfo->skb; 
 | 
        break; 
 | 
  
 | 
    case REQTYPE_RESP_NET_SG: 
 | 
    case REQTYPE_RESP_NET: 
 | 
        sc = buf; 
 | 
        skb = sc->callback_arg; 
 | 
        break; 
 | 
  
 | 
    default: 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    txq = netdev_get_tx_queue(skb->dev, skb_get_queue_mapping(skb)); 
 | 
    netdev_tx_sent_queue(txq, skb->len); 
 | 
  
 | 
    return netif_xmit_stopped(txq); 
 | 
} 
 | 
  
 | 
void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr) 
 | 
{ 
 | 
    struct octnic_ctrl_pkt *nctrl = (struct octnic_ctrl_pkt *)nctrl_ptr; 
 | 
    struct net_device *netdev = (struct net_device *)nctrl->netpndev; 
 | 
    struct lio *lio = GET_LIO(netdev); 
 | 
    struct octeon_device *oct = lio->oct_dev; 
 | 
    u8 *mac; 
 | 
  
 | 
    if (nctrl->sc_status) 
 | 
        return; 
 | 
  
 | 
    switch (nctrl->ncmd.s.cmd) { 
 | 
    case OCTNET_CMD_CHANGE_DEVFLAGS: 
 | 
    case OCTNET_CMD_SET_MULTI_LIST: 
 | 
    case OCTNET_CMD_SET_UC_LIST: 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_CHANGE_MACADDR: 
 | 
        mac = ((u8 *)&nctrl->udd[0]) + 2; 
 | 
        if (nctrl->ncmd.s.param1) { 
 | 
            /* vfidx is 0 based, but vf_num (param1) is 1 based */ 
 | 
            int vfidx = nctrl->ncmd.s.param1 - 1; 
 | 
            bool mac_is_admin_assigned = nctrl->ncmd.s.param2; 
 | 
  
 | 
            if (mac_is_admin_assigned) 
 | 
                netif_info(lio, probe, lio->netdev, 
 | 
                       "MAC Address %pM is configured for VF %d\n", 
 | 
                       mac, vfidx); 
 | 
        } else { 
 | 
            netif_info(lio, probe, lio->netdev, 
 | 
                   " MACAddr changed to %pM\n", 
 | 
                   mac); 
 | 
        } 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_GPIO_ACCESS: 
 | 
        netif_info(lio, probe, lio->netdev, "LED Flashing visual identification\n"); 
 | 
  
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_ID_ACTIVE: 
 | 
        netif_info(lio, probe, lio->netdev, "LED Flashing visual identification\n"); 
 | 
  
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_LRO_ENABLE: 
 | 
        dev_info(&oct->pci_dev->dev, "%s LRO Enabled\n", netdev->name); 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_LRO_DISABLE: 
 | 
        dev_info(&oct->pci_dev->dev, "%s LRO Disabled\n", 
 | 
             netdev->name); 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_VERBOSE_ENABLE: 
 | 
        dev_info(&oct->pci_dev->dev, "%s Firmware debug enabled\n", 
 | 
             netdev->name); 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_VERBOSE_DISABLE: 
 | 
        dev_info(&oct->pci_dev->dev, "%s Firmware debug disabled\n", 
 | 
             netdev->name); 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_VLAN_FILTER_CTL: 
 | 
        if (nctrl->ncmd.s.param1) 
 | 
            dev_info(&oct->pci_dev->dev, 
 | 
                 "%s VLAN filter enabled\n", netdev->name); 
 | 
        else 
 | 
            dev_info(&oct->pci_dev->dev, 
 | 
                 "%s VLAN filter disabled\n", netdev->name); 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_ADD_VLAN_FILTER: 
 | 
        dev_info(&oct->pci_dev->dev, "%s VLAN filter %d added\n", 
 | 
             netdev->name, nctrl->ncmd.s.param1); 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_DEL_VLAN_FILTER: 
 | 
        dev_info(&oct->pci_dev->dev, "%s VLAN filter %d removed\n", 
 | 
             netdev->name, nctrl->ncmd.s.param1); 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_SET_SETTINGS: 
 | 
        dev_info(&oct->pci_dev->dev, "%s settings changed\n", 
 | 
             netdev->name); 
 | 
  
 | 
        break; 
 | 
  
 | 
    /* Case to handle "OCTNET_CMD_TNL_RX_CSUM_CTL" 
 | 
     * Command passed by NIC driver 
 | 
     */ 
 | 
    case OCTNET_CMD_TNL_RX_CSUM_CTL: 
 | 
        if (nctrl->ncmd.s.param1 == OCTNET_CMD_RXCSUM_ENABLE) { 
 | 
            netif_info(lio, probe, lio->netdev, 
 | 
                   "RX Checksum Offload Enabled\n"); 
 | 
        } else if (nctrl->ncmd.s.param1 == 
 | 
               OCTNET_CMD_RXCSUM_DISABLE) { 
 | 
            netif_info(lio, probe, lio->netdev, 
 | 
                   "RX Checksum Offload Disabled\n"); 
 | 
        } 
 | 
        break; 
 | 
  
 | 
        /* Case to handle "OCTNET_CMD_TNL_TX_CSUM_CTL" 
 | 
         * Command passed by NIC driver 
 | 
         */ 
 | 
    case OCTNET_CMD_TNL_TX_CSUM_CTL: 
 | 
        if (nctrl->ncmd.s.param1 == OCTNET_CMD_TXCSUM_ENABLE) { 
 | 
            netif_info(lio, probe, lio->netdev, 
 | 
                   "TX Checksum Offload Enabled\n"); 
 | 
        } else if (nctrl->ncmd.s.param1 == 
 | 
               OCTNET_CMD_TXCSUM_DISABLE) { 
 | 
            netif_info(lio, probe, lio->netdev, 
 | 
                   "TX Checksum Offload Disabled\n"); 
 | 
        } 
 | 
        break; 
 | 
  
 | 
        /* Case to handle "OCTNET_CMD_VXLAN_PORT_CONFIG" 
 | 
         * Command passed by NIC driver 
 | 
         */ 
 | 
    case OCTNET_CMD_VXLAN_PORT_CONFIG: 
 | 
        if (nctrl->ncmd.s.more == OCTNET_CMD_VXLAN_PORT_ADD) { 
 | 
            netif_info(lio, probe, lio->netdev, 
 | 
                   "VxLAN Destination UDP PORT:%d ADDED\n", 
 | 
                   nctrl->ncmd.s.param1); 
 | 
        } else if (nctrl->ncmd.s.more == 
 | 
               OCTNET_CMD_VXLAN_PORT_DEL) { 
 | 
            netif_info(lio, probe, lio->netdev, 
 | 
                   "VxLAN Destination UDP PORT:%d DELETED\n", 
 | 
                   nctrl->ncmd.s.param1); 
 | 
        } 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_SET_FLOW_CTL: 
 | 
        netif_info(lio, probe, lio->netdev, "Set RX/TX flow control parameters\n"); 
 | 
        break; 
 | 
  
 | 
    case OCTNET_CMD_QUEUE_COUNT_CTL: 
 | 
        netif_info(lio, probe, lio->netdev, "Queue count updated to %d\n", 
 | 
               nctrl->ncmd.s.param1); 
 | 
        break; 
 | 
  
 | 
    default: 
 | 
        dev_err(&oct->pci_dev->dev, "%s Unknown cmd %d\n", __func__, 
 | 
            nctrl->ncmd.s.cmd); 
 | 
    } 
 | 
} 
 | 
  
 | 
void octeon_pf_changed_vf_macaddr(struct octeon_device *oct, u8 *mac) 
 | 
{ 
 | 
    bool macaddr_changed = false; 
 | 
    struct net_device *netdev; 
 | 
    struct lio *lio; 
 | 
  
 | 
    rtnl_lock(); 
 | 
  
 | 
    netdev = oct->props[0].netdev; 
 | 
    lio = GET_LIO(netdev); 
 | 
  
 | 
    lio->linfo.macaddr_is_admin_asgnd = true; 
 | 
  
 | 
    if (!ether_addr_equal(netdev->dev_addr, mac)) { 
 | 
        macaddr_changed = true; 
 | 
        ether_addr_copy(netdev->dev_addr, mac); 
 | 
        ether_addr_copy(((u8 *)&lio->linfo.hw_addr) + 2, mac); 
 | 
        call_netdevice_notifiers(NETDEV_CHANGEADDR, netdev); 
 | 
    } 
 | 
  
 | 
    rtnl_unlock(); 
 | 
  
 | 
    if (macaddr_changed) 
 | 
        dev_info(&oct->pci_dev->dev, 
 | 
             "PF changed VF's MAC address to %pM\n", mac); 
 | 
  
 | 
    /* no need to notify the firmware of the macaddr change because 
 | 
     * the PF did that already 
 | 
     */ 
 | 
} 
 | 
  
 | 
void octeon_schedule_rxq_oom_work(struct octeon_device *oct, 
 | 
                  struct octeon_droq *droq) 
 | 
{ 
 | 
    struct net_device *netdev = oct->props[0].netdev; 
 | 
    struct lio *lio = GET_LIO(netdev); 
 | 
    struct cavium_wq *wq = &lio->rxq_status_wq[droq->q_no]; 
 | 
  
 | 
    queue_delayed_work(wq->wq, &wq->wk.work, 
 | 
               msecs_to_jiffies(LIO_OOM_POLL_INTERVAL_MS)); 
 | 
} 
 | 
  
 | 
static void octnet_poll_check_rxq_oom_status(struct work_struct *work) 
 | 
{ 
 | 
    struct cavium_wk *wk = (struct cavium_wk *)work; 
 | 
    struct lio *lio = (struct lio *)wk->ctxptr; 
 | 
    struct octeon_device *oct = lio->oct_dev; 
 | 
    int q_no = wk->ctxul; 
 | 
    struct octeon_droq *droq = oct->droq[q_no]; 
 | 
  
 | 
    if (!ifstate_check(lio, LIO_IFSTATE_RUNNING) || !droq) 
 | 
        return; 
 | 
  
 | 
    if (octeon_retry_droq_refill(droq)) 
 | 
        octeon_schedule_rxq_oom_work(oct, droq); 
 | 
} 
 | 
  
 | 
int setup_rx_oom_poll_fn(struct net_device *netdev) 
 | 
{ 
 | 
    struct lio *lio = GET_LIO(netdev); 
 | 
    struct octeon_device *oct = lio->oct_dev; 
 | 
    struct cavium_wq *wq; 
 | 
    int q, q_no; 
 | 
  
 | 
    for (q = 0; q < oct->num_oqs; q++) { 
 | 
        q_no = lio->linfo.rxpciq[q].s.q_no; 
 | 
        wq = &lio->rxq_status_wq[q_no]; 
 | 
        wq->wq = alloc_workqueue("rxq-oom-status", 
 | 
                     WQ_MEM_RECLAIM, 0); 
 | 
        if (!wq->wq) { 
 | 
            dev_err(&oct->pci_dev->dev, "unable to create cavium rxq oom status wq\n"); 
 | 
            return -ENOMEM; 
 | 
        } 
 | 
  
 | 
        INIT_DELAYED_WORK(&wq->wk.work, 
 | 
                  octnet_poll_check_rxq_oom_status); 
 | 
        wq->wk.ctxptr = lio; 
 | 
        wq->wk.ctxul = q_no; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
void cleanup_rx_oom_poll_fn(struct net_device *netdev) 
 | 
{ 
 | 
    struct lio *lio = GET_LIO(netdev); 
 | 
    struct octeon_device *oct = lio->oct_dev; 
 | 
    struct cavium_wq *wq; 
 | 
    int q_no; 
 | 
  
 | 
    for (q_no = 0; q_no < oct->num_oqs; q_no++) { 
 | 
        wq = &lio->rxq_status_wq[q_no]; 
 | 
        if (wq->wq) { 
 | 
            cancel_delayed_work_sync(&wq->wk.work); 
 | 
            flush_workqueue(wq->wq); 
 | 
            destroy_workqueue(wq->wq); 
 | 
            wq->wq = NULL; 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
/* Runs in interrupt context. */ 
 | 
static void lio_update_txq_status(struct octeon_device *oct, int iq_num) 
 | 
{ 
 | 
    struct octeon_instr_queue *iq = oct->instr_queue[iq_num]; 
 | 
    struct net_device *netdev; 
 | 
    struct lio *lio; 
 | 
  
 | 
    netdev = oct->props[iq->ifidx].netdev; 
 | 
  
 | 
    /* This is needed because the first IQ does not have 
 | 
     * a netdev associated with it. 
 | 
     */ 
 | 
    if (!netdev) 
 | 
        return; 
 | 
  
 | 
    lio = GET_LIO(netdev); 
 | 
    if (__netif_subqueue_stopped(netdev, iq->q_index) && 
 | 
        lio->linfo.link.s.link_up && 
 | 
        (!octnet_iq_is_full(oct, iq_num))) { 
 | 
        netif_wake_subqueue(netdev, iq->q_index); 
 | 
        INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, 
 | 
                      tx_restart, 1); 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * octeon_setup_droq - Setup output queue 
 | 
 * @oct: octeon device 
 | 
 * @q_no: which queue 
 | 
 * @num_descs: how many descriptors 
 | 
 * @desc_size: size of each descriptor 
 | 
 * @app_ctx: application context 
 | 
 */ 
 | 
static int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, 
 | 
                 int desc_size, void *app_ctx) 
 | 
{ 
 | 
    int ret_val; 
 | 
  
 | 
    dev_dbg(&oct->pci_dev->dev, "Creating Droq: %d\n", q_no); 
 | 
    /* droq creation and local register settings. */ 
 | 
    ret_val = octeon_create_droq(oct, q_no, num_descs, desc_size, app_ctx); 
 | 
    if (ret_val < 0) 
 | 
        return ret_val; 
 | 
  
 | 
    if (ret_val == 1) { 
 | 
        dev_dbg(&oct->pci_dev->dev, "Using default droq %d\n", q_no); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    /* Enable the droq queues */ 
 | 
    octeon_set_droq_pkt_op(oct, q_no, 1); 
 | 
  
 | 
    /* Send Credit for Octeon Output queues. Credits are always 
 | 
     * sent after the output queue is enabled. 
 | 
     */ 
 | 
    writel(oct->droq[q_no]->max_count, oct->droq[q_no]->pkts_credit_reg); 
 | 
  
 | 
    return ret_val; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * liquidio_push_packet - Routine to push packets arriving on Octeon interface upto network layer. 
 | 
 * @octeon_id:octeon device id. 
 | 
 * @skbuff:   skbuff struct to be passed to network layer. 
 | 
 * @len:      size of total data received. 
 | 
 * @rh:       Control header associated with the packet 
 | 
 * @param:    additional control data with the packet 
 | 
 * @arg:      farg registered in droq_ops 
 | 
 */ 
 | 
static void 
 | 
liquidio_push_packet(u32 __maybe_unused octeon_id, 
 | 
             void *skbuff, 
 | 
             u32 len, 
 | 
             union octeon_rh *rh, 
 | 
             void *param, 
 | 
             void *arg) 
 | 
{ 
 | 
    struct net_device *netdev = (struct net_device *)arg; 
 | 
    struct octeon_droq *droq = 
 | 
        container_of(param, struct octeon_droq, napi); 
 | 
    struct sk_buff *skb = (struct sk_buff *)skbuff; 
 | 
    struct skb_shared_hwtstamps *shhwtstamps; 
 | 
    struct napi_struct *napi = param; 
 | 
    u16 vtag = 0; 
 | 
    u32 r_dh_off; 
 | 
    u64 ns; 
 | 
  
 | 
    if (netdev) { 
 | 
        struct lio *lio = GET_LIO(netdev); 
 | 
        struct octeon_device *oct = lio->oct_dev; 
 | 
  
 | 
        /* Do not proceed if the interface is not in RUNNING state. */ 
 | 
        if (!ifstate_check(lio, LIO_IFSTATE_RUNNING)) { 
 | 
            recv_buffer_free(skb); 
 | 
            droq->stats.rx_dropped++; 
 | 
            return; 
 | 
        } 
 | 
  
 | 
        skb->dev = netdev; 
 | 
  
 | 
        skb_record_rx_queue(skb, droq->q_no); 
 | 
        if (likely(len > MIN_SKB_SIZE)) { 
 | 
            struct octeon_skb_page_info *pg_info; 
 | 
            unsigned char *va; 
 | 
  
 | 
            pg_info = ((struct octeon_skb_page_info *)(skb->cb)); 
 | 
            if (pg_info->page) { 
 | 
                /* For Paged allocation use the frags */ 
 | 
                va = page_address(pg_info->page) + 
 | 
                    pg_info->page_offset; 
 | 
                memcpy(skb->data, va, MIN_SKB_SIZE); 
 | 
                skb_put(skb, MIN_SKB_SIZE); 
 | 
                skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 
 | 
                        pg_info->page, 
 | 
                        pg_info->page_offset + 
 | 
                        MIN_SKB_SIZE, 
 | 
                        len - MIN_SKB_SIZE, 
 | 
                        LIO_RXBUFFER_SZ); 
 | 
            } 
 | 
        } else { 
 | 
            struct octeon_skb_page_info *pg_info = 
 | 
                ((struct octeon_skb_page_info *)(skb->cb)); 
 | 
            skb_copy_to_linear_data(skb, page_address(pg_info->page) 
 | 
                        + pg_info->page_offset, len); 
 | 
            skb_put(skb, len); 
 | 
            put_page(pg_info->page); 
 | 
        } 
 | 
  
 | 
        r_dh_off = (rh->r_dh.len - 1) * BYTES_PER_DHLEN_UNIT; 
 | 
  
 | 
        if (oct->ptp_enable) { 
 | 
            if (rh->r_dh.has_hwtstamp) { 
 | 
                /* timestamp is included from the hardware at 
 | 
                 * the beginning of the packet. 
 | 
                 */ 
 | 
                if (ifstate_check 
 | 
                    (lio, 
 | 
                     LIO_IFSTATE_RX_TIMESTAMP_ENABLED)) { 
 | 
                    /* Nanoseconds are in the first 64-bits 
 | 
                     * of the packet. 
 | 
                     */ 
 | 
                    memcpy(&ns, (skb->data + r_dh_off), 
 | 
                           sizeof(ns)); 
 | 
                    r_dh_off -= BYTES_PER_DHLEN_UNIT; 
 | 
                    shhwtstamps = skb_hwtstamps(skb); 
 | 
                    shhwtstamps->hwtstamp = 
 | 
                        ns_to_ktime(ns + 
 | 
                                lio->ptp_adjust); 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        if (rh->r_dh.has_hash) { 
 | 
            __be32 *hash_be = (__be32 *)(skb->data + r_dh_off); 
 | 
            u32 hash = be32_to_cpu(*hash_be); 
 | 
  
 | 
            skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); 
 | 
            r_dh_off -= BYTES_PER_DHLEN_UNIT; 
 | 
        } 
 | 
  
 | 
        skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT); 
 | 
        skb->protocol = eth_type_trans(skb, skb->dev); 
 | 
  
 | 
        if ((netdev->features & NETIF_F_RXCSUM) && 
 | 
            (((rh->r_dh.encap_on) && 
 | 
              (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) || 
 | 
             (!(rh->r_dh.encap_on) && 
 | 
              ((rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED) == 
 | 
            CNNIC_CSUM_VERIFIED)))) 
 | 
            /* checksum has already been verified */ 
 | 
            skb->ip_summed = CHECKSUM_UNNECESSARY; 
 | 
        else 
 | 
            skb->ip_summed = CHECKSUM_NONE; 
 | 
  
 | 
        /* Setting Encapsulation field on basis of status received 
 | 
         * from the firmware 
 | 
         */ 
 | 
        if (rh->r_dh.encap_on) { 
 | 
            skb->encapsulation = 1; 
 | 
            skb->csum_level = 1; 
 | 
            droq->stats.rx_vxlan++; 
 | 
        } 
 | 
  
 | 
        /* inbound VLAN tag */ 
 | 
        if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && 
 | 
            rh->r_dh.vlan) { 
 | 
            u16 priority = rh->r_dh.priority; 
 | 
            u16 vid = rh->r_dh.vlan; 
 | 
  
 | 
            vtag = (priority << VLAN_PRIO_SHIFT) | vid; 
 | 
            __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag); 
 | 
        } 
 | 
  
 | 
        napi_gro_receive(napi, skb); 
 | 
  
 | 
        droq->stats.rx_bytes_received += len - 
 | 
            rh->r_dh.len * BYTES_PER_DHLEN_UNIT; 
 | 
        droq->stats.rx_pkts_received++; 
 | 
    } else { 
 | 
        recv_buffer_free(skb); 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * napi_schedule_wrapper - wrapper for calling napi_schedule 
 | 
 * @param: parameters to pass to napi_schedule 
 | 
 * 
 | 
 * Used when scheduling on different CPUs 
 | 
 */ 
 | 
static void napi_schedule_wrapper(void *param) 
 | 
{ 
 | 
    struct napi_struct *napi = param; 
 | 
  
 | 
    napi_schedule(napi); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * liquidio_napi_drv_callback - callback when receive interrupt occurs and we are in NAPI mode 
 | 
 * @arg: pointer to octeon output queue 
 | 
 */ 
 | 
static void liquidio_napi_drv_callback(void *arg) 
 | 
{ 
 | 
    struct octeon_device *oct; 
 | 
    struct octeon_droq *droq = arg; 
 | 
    int this_cpu = smp_processor_id(); 
 | 
  
 | 
    oct = droq->oct_dev; 
 | 
  
 | 
    if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct) || 
 | 
        droq->cpu_id == this_cpu) { 
 | 
        napi_schedule_irqoff(&droq->napi); 
 | 
    } else { 
 | 
        call_single_data_t *csd = &droq->csd; 
 | 
  
 | 
        csd->func = napi_schedule_wrapper; 
 | 
        csd->info = &droq->napi; 
 | 
        csd->flags = 0; 
 | 
  
 | 
        smp_call_function_single_async(droq->cpu_id, csd); 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * liquidio_napi_poll - Entry point for NAPI polling 
 | 
 * @napi: NAPI structure 
 | 
 * @budget: maximum number of items to process 
 | 
 */ 
 | 
static int liquidio_napi_poll(struct napi_struct *napi, int budget) 
 | 
{ 
 | 
    struct octeon_instr_queue *iq; 
 | 
    struct octeon_device *oct; 
 | 
    struct octeon_droq *droq; 
 | 
    int tx_done = 0, iq_no; 
 | 
    int work_done; 
 | 
  
 | 
    droq = container_of(napi, struct octeon_droq, napi); 
 | 
    oct = droq->oct_dev; 
 | 
    iq_no = droq->q_no; 
 | 
  
 | 
    /* Handle Droq descriptors */ 
 | 
    work_done = octeon_droq_process_poll_pkts(oct, droq, budget); 
 | 
  
 | 
    /* Flush the instruction queue */ 
 | 
    iq = oct->instr_queue[iq_no]; 
 | 
    if (iq) { 
 | 
        /* TODO: move this check to inside octeon_flush_iq, 
 | 
         * once check_db_timeout is removed 
 | 
         */ 
 | 
        if (atomic_read(&iq->instr_pending)) 
 | 
            /* Process iq buffers with in the budget limits */ 
 | 
            tx_done = octeon_flush_iq(oct, iq, budget); 
 | 
        else 
 | 
            tx_done = 1; 
 | 
        /* Update iq read-index rather than waiting for next interrupt. 
 | 
         * Return back if tx_done is false. 
 | 
         */ 
 | 
        /* sub-queue status update */ 
 | 
        lio_update_txq_status(oct, iq_no); 
 | 
    } else { 
 | 
        dev_err(&oct->pci_dev->dev, "%s:  iq (%d) num invalid\n", 
 | 
            __func__, iq_no); 
 | 
    } 
 | 
  
 | 
#define MAX_REG_CNT  2000000U 
 | 
    /* force enable interrupt if reg cnts are high to avoid wraparound */ 
 | 
    if ((work_done < budget && tx_done) || 
 | 
        (iq && iq->pkt_in_done >= MAX_REG_CNT) || 
 | 
        (droq->pkt_count >= MAX_REG_CNT)) { 
 | 
        napi_complete_done(napi, work_done); 
 | 
  
 | 
        octeon_enable_irq(droq->oct_dev, droq->q_no); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    return (!tx_done) ? (budget) : (work_done); 
 | 
} 
 | 
  
 | 
/** 
 | 
 * liquidio_setup_io_queues - Setup input and output queues 
 | 
 * @octeon_dev: octeon device 
 | 
 * @ifidx: Interface index 
 | 
 * @num_iqs: input io queue count 
 | 
 * @num_oqs: output io queue count 
 | 
 * 
 | 
 * Note: Queues are with respect to the octeon device. Thus 
 | 
 * an input queue is for egress packets, and output queues 
 | 
 * are for ingress packets. 
 | 
 */ 
 | 
int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx, 
 | 
                 u32 num_iqs, u32 num_oqs) 
 | 
{ 
 | 
    struct octeon_droq_ops droq_ops; 
 | 
    struct net_device *netdev; 
 | 
    struct octeon_droq *droq; 
 | 
    struct napi_struct *napi; 
 | 
    int cpu_id_modulus; 
 | 
    int num_tx_descs; 
 | 
    struct lio *lio; 
 | 
    int retval = 0; 
 | 
    int q, q_no; 
 | 
    int cpu_id; 
 | 
  
 | 
    netdev = octeon_dev->props[ifidx].netdev; 
 | 
  
 | 
    lio = GET_LIO(netdev); 
 | 
  
 | 
    memset(&droq_ops, 0, sizeof(struct octeon_droq_ops)); 
 | 
  
 | 
    droq_ops.fptr = liquidio_push_packet; 
 | 
    droq_ops.farg = netdev; 
 | 
  
 | 
    droq_ops.poll_mode = 1; 
 | 
    droq_ops.napi_fn = liquidio_napi_drv_callback; 
 | 
    cpu_id = 0; 
 | 
    cpu_id_modulus = num_present_cpus(); 
 | 
  
 | 
    /* set up DROQs. */ 
 | 
    for (q = 0; q < num_oqs; q++) { 
 | 
        q_no = lio->linfo.rxpciq[q].s.q_no; 
 | 
        dev_dbg(&octeon_dev->pci_dev->dev, 
 | 
            "%s index:%d linfo.rxpciq.s.q_no:%d\n", 
 | 
            __func__, q, q_no); 
 | 
        retval = octeon_setup_droq( 
 | 
            octeon_dev, q_no, 
 | 
            CFG_GET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(octeon_dev), 
 | 
                        lio->ifidx), 
 | 
            CFG_GET_NUM_RX_BUF_SIZE_NIC_IF(octeon_get_conf(octeon_dev), 
 | 
                           lio->ifidx), 
 | 
            NULL); 
 | 
        if (retval) { 
 | 
            dev_err(&octeon_dev->pci_dev->dev, 
 | 
                "%s : Runtime DROQ(RxQ) creation failed.\n", 
 | 
                __func__); 
 | 
            return 1; 
 | 
        } 
 | 
  
 | 
        droq = octeon_dev->droq[q_no]; 
 | 
        napi = &droq->napi; 
 | 
        dev_dbg(&octeon_dev->pci_dev->dev, "netif_napi_add netdev:%llx oct:%llx\n", 
 | 
            (u64)netdev, (u64)octeon_dev); 
 | 
        netif_napi_add(netdev, napi, liquidio_napi_poll, 64); 
 | 
  
 | 
        /* designate a CPU for this droq */ 
 | 
        droq->cpu_id = cpu_id; 
 | 
        cpu_id++; 
 | 
        if (cpu_id >= cpu_id_modulus) 
 | 
            cpu_id = 0; 
 | 
  
 | 
        octeon_register_droq_ops(octeon_dev, q_no, &droq_ops); 
 | 
    } 
 | 
  
 | 
    if (OCTEON_CN23XX_PF(octeon_dev) || OCTEON_CN23XX_VF(octeon_dev)) { 
 | 
        /* 23XX PF/VF can send/recv control messages (via the first 
 | 
         * PF/VF-owned droq) from the firmware even if the ethX 
 | 
         * interface is down, so that's why poll_mode must be off 
 | 
         * for the first droq. 
 | 
         */ 
 | 
        octeon_dev->droq[0]->ops.poll_mode = 0; 
 | 
    } 
 | 
  
 | 
    /* set up IQs. */ 
 | 
    for (q = 0; q < num_iqs; q++) { 
 | 
        num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF( 
 | 
            octeon_get_conf(octeon_dev), lio->ifidx); 
 | 
        retval = octeon_setup_iq(octeon_dev, ifidx, q, 
 | 
                     lio->linfo.txpciq[q], num_tx_descs, 
 | 
                     netdev_get_tx_queue(netdev, q)); 
 | 
        if (retval) { 
 | 
            dev_err(&octeon_dev->pci_dev->dev, 
 | 
                " %s : Runtime IQ(TxQ) creation failed.\n", 
 | 
                __func__); 
 | 
            return 1; 
 | 
        } 
 | 
  
 | 
        /* XPS */ 
 | 
        if (!OCTEON_CN23XX_VF(octeon_dev) && octeon_dev->msix_on && 
 | 
            octeon_dev->ioq_vector) { 
 | 
            struct octeon_ioq_vector    *ioq_vector; 
 | 
  
 | 
            ioq_vector = &octeon_dev->ioq_vector[q]; 
 | 
            netif_set_xps_queue(netdev, 
 | 
                        &ioq_vector->affinity_mask, 
 | 
                        ioq_vector->iq_index); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static 
 | 
int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) 
 | 
{ 
 | 
    struct octeon_device *oct = droq->oct_dev; 
 | 
    struct octeon_device_priv *oct_priv = 
 | 
        (struct octeon_device_priv *)oct->priv; 
 | 
  
 | 
    if (droq->ops.poll_mode) { 
 | 
        droq->ops.napi_fn(droq); 
 | 
    } else { 
 | 
        if (ret & MSIX_PO_INT) { 
 | 
            if (OCTEON_CN23XX_VF(oct)) 
 | 
                dev_err(&oct->pci_dev->dev, 
 | 
                    "should not come here should not get rx when poll mode = 0 for vf\n"); 
 | 
            tasklet_schedule(&oct_priv->droq_tasklet); 
 | 
            return 1; 
 | 
        } 
 | 
        /* this will be flushed periodically by check iq db */ 
 | 
        if (ret & MSIX_PI_INT) 
 | 
            return 0; 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
irqreturn_t 
 | 
liquidio_msix_intr_handler(int __maybe_unused irq, void *dev) 
 | 
{ 
 | 
    struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev; 
 | 
    struct octeon_device *oct = ioq_vector->oct_dev; 
 | 
    struct octeon_droq *droq = oct->droq[ioq_vector->droq_index]; 
 | 
    u64 ret; 
 | 
  
 | 
    ret = oct->fn_list.msix_interrupt_handler(ioq_vector); 
 | 
  
 | 
    if (ret & MSIX_PO_INT || ret & MSIX_PI_INT) 
 | 
        liquidio_schedule_msix_droq_pkt_handler(droq, ret); 
 | 
  
 | 
    return IRQ_HANDLED; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * liquidio_schedule_droq_pkt_handlers - Droq packet processor sceduler 
 | 
 * @oct: octeon device 
 | 
 */ 
 | 
static void liquidio_schedule_droq_pkt_handlers(struct octeon_device *oct) 
 | 
{ 
 | 
    struct octeon_device_priv *oct_priv = 
 | 
        (struct octeon_device_priv *)oct->priv; 
 | 
    struct octeon_droq *droq; 
 | 
    u64 oq_no; 
 | 
  
 | 
    if (oct->int_status & OCT_DEV_INTR_PKT_DATA) { 
 | 
        for (oq_no = 0; oq_no < MAX_OCTEON_OUTPUT_QUEUES(oct); 
 | 
             oq_no++) { 
 | 
            if (!(oct->droq_intr & BIT_ULL(oq_no))) 
 | 
                continue; 
 | 
  
 | 
            droq = oct->droq[oq_no]; 
 | 
  
 | 
            if (droq->ops.poll_mode) { 
 | 
                droq->ops.napi_fn(droq); 
 | 
                oct_priv->napi_mask |= BIT_ULL(oq_no); 
 | 
            } else { 
 | 
                tasklet_schedule(&oct_priv->droq_tasklet); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * liquidio_legacy_intr_handler - Interrupt handler for octeon 
 | 
 * @irq: unused 
 | 
 * @dev: octeon device 
 | 
 */ 
 | 
static 
 | 
irqreturn_t liquidio_legacy_intr_handler(int __maybe_unused irq, void *dev) 
 | 
{ 
 | 
    struct octeon_device *oct = (struct octeon_device *)dev; 
 | 
    irqreturn_t ret; 
 | 
  
 | 
    /* Disable our interrupts for the duration of ISR */ 
 | 
    oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); 
 | 
  
 | 
    ret = oct->fn_list.process_interrupt_regs(oct); 
 | 
  
 | 
    if (ret == IRQ_HANDLED) 
 | 
        liquidio_schedule_droq_pkt_handlers(oct); 
 | 
  
 | 
    /* Re-enable our interrupts  */ 
 | 
    if (!(atomic_read(&oct->status) == OCT_DEV_IN_RESET)) 
 | 
        oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * octeon_setup_interrupt - Setup interrupt for octeon device 
 | 
 * @oct: octeon device 
 | 
 * @num_ioqs: number of queues 
 | 
 * 
 | 
 *  Enable interrupt in Octeon device as given in the PCI interrupt mask. 
 | 
 */ 
 | 
int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs) 
 | 
{ 
 | 
    struct msix_entry *msix_entries; 
 | 
    char *queue_irq_names = NULL; 
 | 
    int i, num_interrupts = 0; 
 | 
    int num_alloc_ioq_vectors; 
 | 
    char *aux_irq_name = NULL; 
 | 
    int num_ioq_vectors; 
 | 
    int irqret, err; 
 | 
  
 | 
    if (oct->msix_on) { 
 | 
        oct->num_msix_irqs = num_ioqs; 
 | 
        if (OCTEON_CN23XX_PF(oct)) { 
 | 
            num_interrupts = MAX_IOQ_INTERRUPTS_PER_PF + 1; 
 | 
  
 | 
            /* one non ioq interrupt for handling 
 | 
             * sli_mac_pf_int_sum 
 | 
             */ 
 | 
            oct->num_msix_irqs += 1; 
 | 
        } else if (OCTEON_CN23XX_VF(oct)) { 
 | 
            num_interrupts = MAX_IOQ_INTERRUPTS_PER_VF; 
 | 
        } 
 | 
  
 | 
        /* allocate storage for the names assigned to each irq */ 
 | 
        oct->irq_name_storage = 
 | 
            kcalloc(num_interrupts, INTRNAMSIZ, GFP_KERNEL); 
 | 
        if (!oct->irq_name_storage) { 
 | 
            dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n"); 
 | 
            return -ENOMEM; 
 | 
        } 
 | 
  
 | 
        queue_irq_names = oct->irq_name_storage; 
 | 
  
 | 
        if (OCTEON_CN23XX_PF(oct)) 
 | 
            aux_irq_name = &queue_irq_names 
 | 
                [IRQ_NAME_OFF(MAX_IOQ_INTERRUPTS_PER_PF)]; 
 | 
  
 | 
        oct->msix_entries = kcalloc(oct->num_msix_irqs, 
 | 
                        sizeof(struct msix_entry), 
 | 
                        GFP_KERNEL); 
 | 
        if (!oct->msix_entries) { 
 | 
            dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n"); 
 | 
            kfree(oct->irq_name_storage); 
 | 
            oct->irq_name_storage = NULL; 
 | 
            return -ENOMEM; 
 | 
        } 
 | 
  
 | 
        msix_entries = (struct msix_entry *)oct->msix_entries; 
 | 
  
 | 
        /*Assumption is that pf msix vectors start from pf srn to pf to 
 | 
         * trs and not from 0. if not change this code 
 | 
         */ 
 | 
        if (OCTEON_CN23XX_PF(oct)) { 
 | 
            for (i = 0; i < oct->num_msix_irqs - 1; i++) 
 | 
                msix_entries[i].entry = 
 | 
                    oct->sriov_info.pf_srn + i; 
 | 
  
 | 
            msix_entries[oct->num_msix_irqs - 1].entry = 
 | 
                oct->sriov_info.trs; 
 | 
        } else if (OCTEON_CN23XX_VF(oct)) { 
 | 
            for (i = 0; i < oct->num_msix_irqs; i++) 
 | 
                msix_entries[i].entry = i; 
 | 
        } 
 | 
        num_alloc_ioq_vectors = pci_enable_msix_range( 
 | 
                        oct->pci_dev, msix_entries, 
 | 
                        oct->num_msix_irqs, 
 | 
                        oct->num_msix_irqs); 
 | 
        if (num_alloc_ioq_vectors < 0) { 
 | 
            dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); 
 | 
            kfree(oct->msix_entries); 
 | 
            oct->msix_entries = NULL; 
 | 
            kfree(oct->irq_name_storage); 
 | 
            oct->irq_name_storage = NULL; 
 | 
            return num_alloc_ioq_vectors; 
 | 
        } 
 | 
  
 | 
        dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); 
 | 
  
 | 
        num_ioq_vectors = oct->num_msix_irqs; 
 | 
        /* For PF, there is one non-ioq interrupt handler */ 
 | 
        if (OCTEON_CN23XX_PF(oct)) { 
 | 
            num_ioq_vectors -= 1; 
 | 
  
 | 
            snprintf(aux_irq_name, INTRNAMSIZ, 
 | 
                 "LiquidIO%u-pf%u-aux", oct->octeon_id, 
 | 
                 oct->pf_num); 
 | 
            irqret = request_irq( 
 | 
                    msix_entries[num_ioq_vectors].vector, 
 | 
                    liquidio_legacy_intr_handler, 0, 
 | 
                    aux_irq_name, oct); 
 | 
            if (irqret) { 
 | 
                dev_err(&oct->pci_dev->dev, 
 | 
                    "Request_irq failed for MSIX interrupt Error: %d\n", 
 | 
                    irqret); 
 | 
                pci_disable_msix(oct->pci_dev); 
 | 
                kfree(oct->msix_entries); 
 | 
                kfree(oct->irq_name_storage); 
 | 
                oct->irq_name_storage = NULL; 
 | 
                oct->msix_entries = NULL; 
 | 
                return irqret; 
 | 
            } 
 | 
        } 
 | 
        for (i = 0 ; i < num_ioq_vectors ; i++) { 
 | 
            if (OCTEON_CN23XX_PF(oct)) 
 | 
                snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], 
 | 
                     INTRNAMSIZ, "LiquidIO%u-pf%u-rxtx-%u", 
 | 
                     oct->octeon_id, oct->pf_num, i); 
 | 
  
 | 
            if (OCTEON_CN23XX_VF(oct)) 
 | 
                snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], 
 | 
                     INTRNAMSIZ, "LiquidIO%u-vf%u-rxtx-%u", 
 | 
                     oct->octeon_id, oct->vf_num, i); 
 | 
  
 | 
            irqret = request_irq(msix_entries[i].vector, 
 | 
                         liquidio_msix_intr_handler, 0, 
 | 
                         &queue_irq_names[IRQ_NAME_OFF(i)], 
 | 
                         &oct->ioq_vector[i]); 
 | 
  
 | 
            if (irqret) { 
 | 
                dev_err(&oct->pci_dev->dev, 
 | 
                    "Request_irq failed for MSIX interrupt Error: %d\n", 
 | 
                    irqret); 
 | 
                /* Freeing the non-ioq irq vector here . */ 
 | 
                free_irq(msix_entries[num_ioq_vectors].vector, 
 | 
                     oct); 
 | 
  
 | 
                while (i) { 
 | 
                    i--; 
 | 
                    /* clearing affinity mask. */ 
 | 
                    irq_set_affinity_hint( 
 | 
                              msix_entries[i].vector, 
 | 
                              NULL); 
 | 
                    free_irq(msix_entries[i].vector, 
 | 
                         &oct->ioq_vector[i]); 
 | 
                } 
 | 
                pci_disable_msix(oct->pci_dev); 
 | 
                kfree(oct->msix_entries); 
 | 
                kfree(oct->irq_name_storage); 
 | 
                oct->irq_name_storage = NULL; 
 | 
                oct->msix_entries = NULL; 
 | 
                return irqret; 
 | 
            } 
 | 
            oct->ioq_vector[i].vector = msix_entries[i].vector; 
 | 
            /* assign the cpu mask for this msix interrupt vector */ 
 | 
            irq_set_affinity_hint(msix_entries[i].vector, 
 | 
                          &oct->ioq_vector[i].affinity_mask 
 | 
                          ); 
 | 
        } 
 | 
        dev_dbg(&oct->pci_dev->dev, "OCTEON[%d]: MSI-X enabled\n", 
 | 
            oct->octeon_id); 
 | 
    } else { 
 | 
        err = pci_enable_msi(oct->pci_dev); 
 | 
        if (err) 
 | 
            dev_warn(&oct->pci_dev->dev, "Reverting to legacy interrupts. Error: %d\n", 
 | 
                 err); 
 | 
        else 
 | 
            oct->flags |= LIO_FLAG_MSI_ENABLED; 
 | 
  
 | 
        /* allocate storage for the names assigned to the irq */ 
 | 
        oct->irq_name_storage = kcalloc(1, INTRNAMSIZ, GFP_KERNEL); 
 | 
        if (!oct->irq_name_storage) 
 | 
            return -ENOMEM; 
 | 
  
 | 
        queue_irq_names = oct->irq_name_storage; 
 | 
  
 | 
        if (OCTEON_CN23XX_PF(oct)) 
 | 
            snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ, 
 | 
                 "LiquidIO%u-pf%u-rxtx-%u", 
 | 
                 oct->octeon_id, oct->pf_num, 0); 
 | 
  
 | 
        if (OCTEON_CN23XX_VF(oct)) 
 | 
            snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ, 
 | 
                 "LiquidIO%u-vf%u-rxtx-%u", 
 | 
                 oct->octeon_id, oct->vf_num, 0); 
 | 
  
 | 
        irqret = request_irq(oct->pci_dev->irq, 
 | 
                     liquidio_legacy_intr_handler, 
 | 
                     IRQF_SHARED, 
 | 
                     &queue_irq_names[IRQ_NAME_OFF(0)], oct); 
 | 
        if (irqret) { 
 | 
            if (oct->flags & LIO_FLAG_MSI_ENABLED) 
 | 
                pci_disable_msi(oct->pci_dev); 
 | 
            dev_err(&oct->pci_dev->dev, "Request IRQ failed with code: %d\n", 
 | 
                irqret); 
 | 
            kfree(oct->irq_name_storage); 
 | 
            oct->irq_name_storage = NULL; 
 | 
            return irqret; 
 | 
        } 
 | 
    } 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * liquidio_change_mtu - Net device change_mtu 
 | 
 * @netdev: network device 
 | 
 * @new_mtu: the new max transmit unit size 
 | 
 */ 
 | 
int liquidio_change_mtu(struct net_device *netdev, int new_mtu) 
 | 
{ 
 | 
    struct lio *lio = GET_LIO(netdev); 
 | 
    struct octeon_device *oct = lio->oct_dev; 
 | 
    struct octeon_soft_command *sc; 
 | 
    union octnet_cmd *ncmd; 
 | 
    int ret = 0; 
 | 
  
 | 
    sc = (struct octeon_soft_command *) 
 | 
        octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 16, 0); 
 | 
    if (!sc) { 
 | 
        netif_info(lio, rx_err, lio->netdev, 
 | 
               "Failed to allocate soft command\n"); 
 | 
        return -ENOMEM; 
 | 
    } 
 | 
  
 | 
    ncmd = (union octnet_cmd *)sc->virtdptr; 
 | 
  
 | 
    init_completion(&sc->complete); 
 | 
    sc->sc_status = OCTEON_REQUEST_PENDING; 
 | 
  
 | 
    ncmd->u64 = 0; 
 | 
    ncmd->s.cmd = OCTNET_CMD_CHANGE_MTU; 
 | 
    ncmd->s.param1 = new_mtu; 
 | 
  
 | 
    octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3)); 
 | 
  
 | 
    sc->iq_no = lio->linfo.txpciq[0].s.q_no; 
 | 
  
 | 
    octeon_prepare_soft_command(oct, sc, OPCODE_NIC, 
 | 
                    OPCODE_NIC_CMD, 0, 0, 0); 
 | 
  
 | 
    ret = octeon_send_soft_command(oct, sc); 
 | 
    if (ret == IQ_SEND_FAILED) { 
 | 
        netif_info(lio, rx_err, lio->netdev, "Failed to change MTU\n"); 
 | 
        octeon_free_soft_command(oct, sc); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
    /* Sleep on a wait queue till the cond flag indicates that the 
 | 
     * response arrived or timed-out. 
 | 
     */ 
 | 
    ret = wait_for_sc_completion_timeout(oct, sc, 0); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
  
 | 
    if (sc->sc_status) { 
 | 
        WRITE_ONCE(sc->caller_is_done, true); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    netdev->mtu = new_mtu; 
 | 
    lio->mtu = new_mtu; 
 | 
  
 | 
    WRITE_ONCE(sc->caller_is_done, true); 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int lio_wait_for_clean_oq(struct octeon_device *oct) 
 | 
{ 
 | 
    int retry = 100, pending_pkts = 0; 
 | 
    int idx; 
 | 
  
 | 
    do { 
 | 
        pending_pkts = 0; 
 | 
  
 | 
        for (idx = 0; idx < MAX_OCTEON_OUTPUT_QUEUES(oct); idx++) { 
 | 
            if (!(oct->io_qmask.oq & BIT_ULL(idx))) 
 | 
                continue; 
 | 
            pending_pkts += 
 | 
                atomic_read(&oct->droq[idx]->pkts_pending); 
 | 
        } 
 | 
  
 | 
        if (pending_pkts > 0) 
 | 
            schedule_timeout_uninterruptible(1); 
 | 
  
 | 
    } while (retry-- && pending_pkts); 
 | 
  
 | 
    return pending_pkts; 
 | 
} 
 | 
  
 | 
static void 
 | 
octnet_nic_stats_callback(struct octeon_device *oct_dev, 
 | 
              u32 status, void *ptr) 
 | 
{ 
 | 
    struct octeon_soft_command *sc = (struct octeon_soft_command *)ptr; 
 | 
    struct oct_nic_stats_resp *resp = 
 | 
        (struct oct_nic_stats_resp *)sc->virtrptr; 
 | 
    struct nic_rx_stats *rsp_rstats = &resp->stats.fromwire; 
 | 
    struct nic_tx_stats *rsp_tstats = &resp->stats.fromhost; 
 | 
    struct nic_rx_stats *rstats = &oct_dev->link_stats.fromwire; 
 | 
    struct nic_tx_stats *tstats = &oct_dev->link_stats.fromhost; 
 | 
  
 | 
    if (status != OCTEON_REQUEST_TIMEOUT && !resp->status) { 
 | 
        octeon_swap_8B_data((u64 *)&resp->stats, 
 | 
                    (sizeof(struct oct_link_stats)) >> 3); 
 | 
  
 | 
        /* RX link-level stats */ 
 | 
        rstats->total_rcvd = rsp_rstats->total_rcvd; 
 | 
        rstats->bytes_rcvd = rsp_rstats->bytes_rcvd; 
 | 
        rstats->total_bcst = rsp_rstats->total_bcst; 
 | 
        rstats->total_mcst = rsp_rstats->total_mcst; 
 | 
        rstats->runts      = rsp_rstats->runts; 
 | 
        rstats->ctl_rcvd   = rsp_rstats->ctl_rcvd; 
 | 
        /* Accounts for over/under-run of buffers */ 
 | 
        rstats->fifo_err  = rsp_rstats->fifo_err; 
 | 
        rstats->dmac_drop = rsp_rstats->dmac_drop; 
 | 
        rstats->fcs_err   = rsp_rstats->fcs_err; 
 | 
        rstats->jabber_err = rsp_rstats->jabber_err; 
 | 
        rstats->l2_err    = rsp_rstats->l2_err; 
 | 
        rstats->frame_err = rsp_rstats->frame_err; 
 | 
        rstats->red_drops = rsp_rstats->red_drops; 
 | 
  
 | 
        /* RX firmware stats */ 
 | 
        rstats->fw_total_rcvd = rsp_rstats->fw_total_rcvd; 
 | 
        rstats->fw_total_fwd = rsp_rstats->fw_total_fwd; 
 | 
        rstats->fw_total_mcast = rsp_rstats->fw_total_mcast; 
 | 
        rstats->fw_total_bcast = rsp_rstats->fw_total_bcast; 
 | 
        rstats->fw_err_pko = rsp_rstats->fw_err_pko; 
 | 
        rstats->fw_err_link = rsp_rstats->fw_err_link; 
 | 
        rstats->fw_err_drop = rsp_rstats->fw_err_drop; 
 | 
        rstats->fw_rx_vxlan = rsp_rstats->fw_rx_vxlan; 
 | 
        rstats->fw_rx_vxlan_err = rsp_rstats->fw_rx_vxlan_err; 
 | 
  
 | 
        /* Number of packets that are LROed      */ 
 | 
        rstats->fw_lro_pkts = rsp_rstats->fw_lro_pkts; 
 | 
        /* Number of octets that are LROed       */ 
 | 
        rstats->fw_lro_octs = rsp_rstats->fw_lro_octs; 
 | 
        /* Number of LRO packets formed          */ 
 | 
        rstats->fw_total_lro = rsp_rstats->fw_total_lro; 
 | 
        /* Number of times lRO of packet aborted */ 
 | 
        rstats->fw_lro_aborts = rsp_rstats->fw_lro_aborts; 
 | 
        rstats->fw_lro_aborts_port = rsp_rstats->fw_lro_aborts_port; 
 | 
        rstats->fw_lro_aborts_seq = rsp_rstats->fw_lro_aborts_seq; 
 | 
        rstats->fw_lro_aborts_tsval = rsp_rstats->fw_lro_aborts_tsval; 
 | 
        rstats->fw_lro_aborts_timer = rsp_rstats->fw_lro_aborts_timer; 
 | 
        /* intrmod: packet forward rate */ 
 | 
        rstats->fwd_rate = rsp_rstats->fwd_rate; 
 | 
  
 | 
        /* TX link-level stats */ 
 | 
        tstats->total_pkts_sent = rsp_tstats->total_pkts_sent; 
 | 
        tstats->total_bytes_sent = rsp_tstats->total_bytes_sent; 
 | 
        tstats->mcast_pkts_sent = rsp_tstats->mcast_pkts_sent; 
 | 
        tstats->bcast_pkts_sent = rsp_tstats->bcast_pkts_sent; 
 | 
        tstats->ctl_sent = rsp_tstats->ctl_sent; 
 | 
        /* Packets sent after one collision*/ 
 | 
        tstats->one_collision_sent = rsp_tstats->one_collision_sent; 
 | 
        /* Packets sent after multiple collision*/ 
 | 
        tstats->multi_collision_sent = rsp_tstats->multi_collision_sent; 
 | 
        /* Packets not sent due to max collisions */ 
 | 
        tstats->max_collision_fail = rsp_tstats->max_collision_fail; 
 | 
        /* Packets not sent due to max deferrals */ 
 | 
        tstats->max_deferral_fail = rsp_tstats->max_deferral_fail; 
 | 
        /* Accounts for over/under-run of buffers */ 
 | 
        tstats->fifo_err = rsp_tstats->fifo_err; 
 | 
        tstats->runts = rsp_tstats->runts; 
 | 
        /* Total number of collisions detected */ 
 | 
        tstats->total_collisions = rsp_tstats->total_collisions; 
 | 
  
 | 
        /* firmware stats */ 
 | 
        tstats->fw_total_sent = rsp_tstats->fw_total_sent; 
 | 
        tstats->fw_total_fwd = rsp_tstats->fw_total_fwd; 
 | 
        tstats->fw_total_mcast_sent = rsp_tstats->fw_total_mcast_sent; 
 | 
        tstats->fw_total_bcast_sent = rsp_tstats->fw_total_bcast_sent; 
 | 
        tstats->fw_err_pko = rsp_tstats->fw_err_pko; 
 | 
        tstats->fw_err_pki = rsp_tstats->fw_err_pki; 
 | 
        tstats->fw_err_link = rsp_tstats->fw_err_link; 
 | 
        tstats->fw_err_drop = rsp_tstats->fw_err_drop; 
 | 
        tstats->fw_tso = rsp_tstats->fw_tso; 
 | 
        tstats->fw_tso_fwd = rsp_tstats->fw_tso_fwd; 
 | 
        tstats->fw_err_tso = rsp_tstats->fw_err_tso; 
 | 
        tstats->fw_tx_vxlan = rsp_tstats->fw_tx_vxlan; 
 | 
  
 | 
        resp->status = 1; 
 | 
    } else { 
 | 
        dev_err(&oct_dev->pci_dev->dev, "sc OPCODE_NIC_PORT_STATS command failed\n"); 
 | 
        resp->status = -1; 
 | 
    } 
 | 
} 
 | 
  
 | 
static int lio_fetch_vf_stats(struct lio *lio) 
 | 
{ 
 | 
    struct octeon_device *oct_dev = lio->oct_dev; 
 | 
    struct octeon_soft_command *sc; 
 | 
    struct oct_nic_vf_stats_resp *resp; 
 | 
  
 | 
    int retval; 
 | 
  
 | 
    /* Alloc soft command */ 
 | 
    sc = (struct octeon_soft_command *) 
 | 
        octeon_alloc_soft_command(oct_dev, 
 | 
                      0, 
 | 
                      sizeof(struct oct_nic_vf_stats_resp), 
 | 
                      0); 
 | 
  
 | 
    if (!sc) { 
 | 
        dev_err(&oct_dev->pci_dev->dev, "Soft command allocation failed\n"); 
 | 
        retval = -ENOMEM; 
 | 
        goto lio_fetch_vf_stats_exit; 
 | 
    } 
 | 
  
 | 
    resp = (struct oct_nic_vf_stats_resp *)sc->virtrptr; 
 | 
    memset(resp, 0, sizeof(struct oct_nic_vf_stats_resp)); 
 | 
  
 | 
    init_completion(&sc->complete); 
 | 
    sc->sc_status = OCTEON_REQUEST_PENDING; 
 | 
  
 | 
    sc->iq_no = lio->linfo.txpciq[0].s.q_no; 
 | 
  
 | 
    octeon_prepare_soft_command(oct_dev, sc, OPCODE_NIC, 
 | 
                    OPCODE_NIC_VF_PORT_STATS, 0, 0, 0); 
 | 
  
 | 
    retval = octeon_send_soft_command(oct_dev, sc); 
 | 
    if (retval == IQ_SEND_FAILED) { 
 | 
        octeon_free_soft_command(oct_dev, sc); 
 | 
        goto lio_fetch_vf_stats_exit; 
 | 
    } 
 | 
  
 | 
    retval = 
 | 
        wait_for_sc_completion_timeout(oct_dev, sc, 
 | 
                           (2 * LIO_SC_MAX_TMO_MS)); 
 | 
    if (retval)  { 
 | 
        dev_err(&oct_dev->pci_dev->dev, 
 | 
            "sc OPCODE_NIC_VF_PORT_STATS command failed\n"); 
 | 
        goto lio_fetch_vf_stats_exit; 
 | 
    } 
 | 
  
 | 
    if (sc->sc_status != OCTEON_REQUEST_TIMEOUT && !resp->status) { 
 | 
        octeon_swap_8B_data((u64 *)&resp->spoofmac_cnt, 
 | 
                    (sizeof(u64)) >> 3); 
 | 
  
 | 
        if (resp->spoofmac_cnt != 0) { 
 | 
            dev_warn(&oct_dev->pci_dev->dev, 
 | 
                 "%llu Spoofed packets detected\n", 
 | 
                 resp->spoofmac_cnt); 
 | 
        } 
 | 
    } 
 | 
    WRITE_ONCE(sc->caller_is_done, 1); 
 | 
  
 | 
lio_fetch_vf_stats_exit: 
 | 
    return retval; 
 | 
} 
 | 
  
 | 
void lio_fetch_stats(struct work_struct *work) 
 | 
{ 
 | 
    struct cavium_wk *wk = (struct cavium_wk *)work; 
 | 
    struct lio *lio = wk->ctxptr; 
 | 
    struct octeon_device *oct_dev = lio->oct_dev; 
 | 
    struct octeon_soft_command *sc; 
 | 
    struct oct_nic_stats_resp *resp; 
 | 
    unsigned long time_in_jiffies; 
 | 
    int retval; 
 | 
  
 | 
    if (OCTEON_CN23XX_PF(oct_dev)) { 
 | 
        /* report spoofchk every 2 seconds */ 
 | 
        if (!(oct_dev->vfstats_poll % LIO_VFSTATS_POLL) && 
 | 
            (oct_dev->fw_info.app_cap_flags & LIQUIDIO_SPOOFCHK_CAP) && 
 | 
            oct_dev->sriov_info.num_vfs_alloced) { 
 | 
            lio_fetch_vf_stats(lio); 
 | 
        } 
 | 
  
 | 
        oct_dev->vfstats_poll++; 
 | 
    } 
 | 
  
 | 
    /* Alloc soft command */ 
 | 
    sc = (struct octeon_soft_command *) 
 | 
        octeon_alloc_soft_command(oct_dev, 
 | 
                      0, 
 | 
                      sizeof(struct oct_nic_stats_resp), 
 | 
                      0); 
 | 
  
 | 
    if (!sc) { 
 | 
        dev_err(&oct_dev->pci_dev->dev, "Soft command allocation failed\n"); 
 | 
        goto lio_fetch_stats_exit; 
 | 
    } 
 | 
  
 | 
    resp = (struct oct_nic_stats_resp *)sc->virtrptr; 
 | 
    memset(resp, 0, sizeof(struct oct_nic_stats_resp)); 
 | 
  
 | 
    init_completion(&sc->complete); 
 | 
    sc->sc_status = OCTEON_REQUEST_PENDING; 
 | 
  
 | 
    sc->iq_no = lio->linfo.txpciq[0].s.q_no; 
 | 
  
 | 
    octeon_prepare_soft_command(oct_dev, sc, OPCODE_NIC, 
 | 
                    OPCODE_NIC_PORT_STATS, 0, 0, 0); 
 | 
  
 | 
    retval = octeon_send_soft_command(oct_dev, sc); 
 | 
    if (retval == IQ_SEND_FAILED) { 
 | 
        octeon_free_soft_command(oct_dev, sc); 
 | 
        goto lio_fetch_stats_exit; 
 | 
    } 
 | 
  
 | 
    retval = wait_for_sc_completion_timeout(oct_dev, sc, 
 | 
                        (2 * LIO_SC_MAX_TMO_MS)); 
 | 
    if (retval)  { 
 | 
        dev_err(&oct_dev->pci_dev->dev, "sc OPCODE_NIC_PORT_STATS command failed\n"); 
 | 
        goto lio_fetch_stats_exit; 
 | 
    } 
 | 
  
 | 
    octnet_nic_stats_callback(oct_dev, sc->sc_status, sc); 
 | 
    WRITE_ONCE(sc->caller_is_done, true); 
 | 
  
 | 
lio_fetch_stats_exit: 
 | 
    time_in_jiffies = msecs_to_jiffies(LIQUIDIO_NDEV_STATS_POLL_TIME_MS); 
 | 
    if (ifstate_check(lio, LIO_IFSTATE_RUNNING)) 
 | 
        schedule_delayed_work(&lio->stats_wk.work, time_in_jiffies); 
 | 
  
 | 
    return; 
 | 
} 
 | 
  
 | 
int liquidio_set_speed(struct lio *lio, int speed) 
 | 
{ 
 | 
    struct octeon_device *oct = lio->oct_dev; 
 | 
    struct oct_nic_seapi_resp *resp; 
 | 
    struct octeon_soft_command *sc; 
 | 
    union octnet_cmd *ncmd; 
 | 
    int retval; 
 | 
    u32 var; 
 | 
  
 | 
    if (oct->speed_setting == speed) 
 | 
        return 0; 
 | 
  
 | 
    if (!OCTEON_CN23XX_PF(oct)) { 
 | 
        dev_err(&oct->pci_dev->dev, "%s: SET SPEED only for PF\n", 
 | 
            __func__); 
 | 
        return -EOPNOTSUPP; 
 | 
    } 
 | 
  
 | 
    sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 
 | 
                       sizeof(struct oct_nic_seapi_resp), 
 | 
                       0); 
 | 
    if (!sc) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    ncmd = sc->virtdptr; 
 | 
    resp = sc->virtrptr; 
 | 
    memset(resp, 0, sizeof(struct oct_nic_seapi_resp)); 
 | 
  
 | 
    init_completion(&sc->complete); 
 | 
    sc->sc_status = OCTEON_REQUEST_PENDING; 
 | 
  
 | 
    ncmd->u64 = 0; 
 | 
    ncmd->s.cmd = SEAPI_CMD_SPEED_SET; 
 | 
    ncmd->s.param1 = speed; 
 | 
  
 | 
    octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3)); 
 | 
  
 | 
    sc->iq_no = lio->linfo.txpciq[0].s.q_no; 
 | 
  
 | 
    octeon_prepare_soft_command(oct, sc, OPCODE_NIC, 
 | 
                    OPCODE_NIC_UBOOT_CTL, 0, 0, 0); 
 | 
  
 | 
    retval = octeon_send_soft_command(oct, sc); 
 | 
    if (retval == IQ_SEND_FAILED) { 
 | 
        dev_info(&oct->pci_dev->dev, "Failed to send soft command\n"); 
 | 
        octeon_free_soft_command(oct, sc); 
 | 
        retval = -EBUSY; 
 | 
    } else { 
 | 
        /* Wait for response or timeout */ 
 | 
        retval = wait_for_sc_completion_timeout(oct, sc, 0); 
 | 
        if (retval) 
 | 
            return retval; 
 | 
  
 | 
        retval = resp->status; 
 | 
  
 | 
        if (retval) { 
 | 
            dev_err(&oct->pci_dev->dev, "%s failed, retval=%d\n", 
 | 
                __func__, retval); 
 | 
            WRITE_ONCE(sc->caller_is_done, true); 
 | 
  
 | 
            return -EIO; 
 | 
        } 
 | 
  
 | 
        var = be32_to_cpu((__force __be32)resp->speed); 
 | 
        if (var != speed) { 
 | 
            dev_err(&oct->pci_dev->dev, 
 | 
                "%s: setting failed speed= %x, expect %x\n", 
 | 
                __func__, var, speed); 
 | 
        } 
 | 
  
 | 
        oct->speed_setting = var; 
 | 
        WRITE_ONCE(sc->caller_is_done, true); 
 | 
    } 
 | 
  
 | 
    return retval; 
 | 
} 
 | 
  
 | 
int liquidio_get_speed(struct lio *lio) 
 | 
{ 
 | 
    struct octeon_device *oct = lio->oct_dev; 
 | 
    struct oct_nic_seapi_resp *resp; 
 | 
    struct octeon_soft_command *sc; 
 | 
    union octnet_cmd *ncmd; 
 | 
    int retval; 
 | 
  
 | 
    sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 
 | 
                       sizeof(struct oct_nic_seapi_resp), 
 | 
                       0); 
 | 
    if (!sc) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    ncmd = sc->virtdptr; 
 | 
    resp = sc->virtrptr; 
 | 
    memset(resp, 0, sizeof(struct oct_nic_seapi_resp)); 
 | 
  
 | 
    init_completion(&sc->complete); 
 | 
    sc->sc_status = OCTEON_REQUEST_PENDING; 
 | 
  
 | 
    ncmd->u64 = 0; 
 | 
    ncmd->s.cmd = SEAPI_CMD_SPEED_GET; 
 | 
  
 | 
    octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3)); 
 | 
  
 | 
    sc->iq_no = lio->linfo.txpciq[0].s.q_no; 
 | 
  
 | 
    octeon_prepare_soft_command(oct, sc, OPCODE_NIC, 
 | 
                    OPCODE_NIC_UBOOT_CTL, 0, 0, 0); 
 | 
  
 | 
    retval = octeon_send_soft_command(oct, sc); 
 | 
    if (retval == IQ_SEND_FAILED) { 
 | 
        dev_info(&oct->pci_dev->dev, "Failed to send soft command\n"); 
 | 
        octeon_free_soft_command(oct, sc); 
 | 
        retval = -EIO; 
 | 
    } else { 
 | 
        retval = wait_for_sc_completion_timeout(oct, sc, 0); 
 | 
        if (retval) 
 | 
            return retval; 
 | 
  
 | 
        retval = resp->status; 
 | 
        if (retval) { 
 | 
            dev_err(&oct->pci_dev->dev, 
 | 
                "%s failed retval=%d\n", __func__, retval); 
 | 
            retval = -EIO; 
 | 
        } else { 
 | 
            u32 var; 
 | 
  
 | 
            var = be32_to_cpu((__force __be32)resp->speed); 
 | 
            oct->speed_setting = var; 
 | 
            if (var == 0xffff) { 
 | 
                /* unable to access boot variables 
 | 
                 * get the default value based on the NIC type 
 | 
                 */ 
 | 
                if (oct->subsystem_id == 
 | 
                        OCTEON_CN2350_25GB_SUBSYS_ID || 
 | 
                    oct->subsystem_id == 
 | 
                        OCTEON_CN2360_25GB_SUBSYS_ID) { 
 | 
                    oct->no_speed_setting = 1; 
 | 
                    oct->speed_setting = 25; 
 | 
                } else { 
 | 
                    oct->speed_setting = 10; 
 | 
                } 
 | 
            } 
 | 
  
 | 
        } 
 | 
        WRITE_ONCE(sc->caller_is_done, true); 
 | 
    } 
 | 
  
 | 
    return retval; 
 | 
} 
 | 
  
 | 
int liquidio_set_fec(struct lio *lio, int on_off) 
 | 
{ 
 | 
    struct oct_nic_seapi_resp *resp; 
 | 
    struct octeon_soft_command *sc; 
 | 
    struct octeon_device *oct; 
 | 
    union octnet_cmd *ncmd; 
 | 
    int retval; 
 | 
    u32 var; 
 | 
  
 | 
    oct = lio->oct_dev; 
 | 
  
 | 
    if (oct->props[lio->ifidx].fec == on_off) 
 | 
        return 0; 
 | 
  
 | 
    if (!OCTEON_CN23XX_PF(oct)) { 
 | 
        dev_err(&oct->pci_dev->dev, "%s: SET FEC only for PF\n", 
 | 
            __func__); 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    if (oct->speed_boot != 25)  { 
 | 
        dev_err(&oct->pci_dev->dev, 
 | 
            "Set FEC only when link speed is 25G during insmod\n"); 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 
 | 
                       sizeof(struct oct_nic_seapi_resp), 0); 
 | 
    if (!sc) { 
 | 
        dev_err(&oct->pci_dev->dev, 
 | 
            "Failed to allocate soft command\n"); 
 | 
        return -ENOMEM; 
 | 
    } 
 | 
  
 | 
    ncmd = sc->virtdptr; 
 | 
    resp = sc->virtrptr; 
 | 
    memset(resp, 0, sizeof(struct oct_nic_seapi_resp)); 
 | 
  
 | 
    init_completion(&sc->complete); 
 | 
    sc->sc_status = OCTEON_REQUEST_PENDING; 
 | 
  
 | 
    ncmd->u64 = 0; 
 | 
    ncmd->s.cmd = SEAPI_CMD_FEC_SET; 
 | 
    ncmd->s.param1 = on_off; 
 | 
    /* SEAPI_CMD_FEC_DISABLE(0) or SEAPI_CMD_FEC_RS(1) */ 
 | 
  
 | 
    octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3)); 
 | 
  
 | 
    sc->iq_no = lio->linfo.txpciq[0].s.q_no; 
 | 
  
 | 
    octeon_prepare_soft_command(oct, sc, OPCODE_NIC, 
 | 
                    OPCODE_NIC_UBOOT_CTL, 0, 0, 0); 
 | 
  
 | 
    retval = octeon_send_soft_command(oct, sc); 
 | 
    if (retval == IQ_SEND_FAILED) { 
 | 
        dev_info(&oct->pci_dev->dev, "Failed to send soft command\n"); 
 | 
        octeon_free_soft_command(oct, sc); 
 | 
        return -EIO; 
 | 
    } 
 | 
  
 | 
    retval = wait_for_sc_completion_timeout(oct, sc, 0); 
 | 
    if (retval) 
 | 
        return (-EIO); 
 | 
  
 | 
    var = be32_to_cpu(resp->fec_setting); 
 | 
    resp->fec_setting = var; 
 | 
    if (var != on_off) { 
 | 
        dev_err(&oct->pci_dev->dev, 
 | 
            "Setting failed fec= %x, expect %x\n", 
 | 
            var, on_off); 
 | 
        oct->props[lio->ifidx].fec = var; 
 | 
        if (resp->fec_setting == SEAPI_CMD_FEC_SET_RS) 
 | 
            oct->props[lio->ifidx].fec = 1; 
 | 
        else 
 | 
            oct->props[lio->ifidx].fec = 0; 
 | 
    } 
 | 
  
 | 
    WRITE_ONCE(sc->caller_is_done, true); 
 | 
  
 | 
    if (oct->props[lio->ifidx].fec != 
 | 
        oct->props[lio->ifidx].fec_boot) { 
 | 
        dev_dbg(&oct->pci_dev->dev, 
 | 
            "Reload driver to change fec to %s\n", 
 | 
            oct->props[lio->ifidx].fec ? "on" : "off"); 
 | 
    } 
 | 
  
 | 
    return retval; 
 | 
} 
 | 
  
 | 
int liquidio_get_fec(struct lio *lio) 
 | 
{ 
 | 
    struct oct_nic_seapi_resp *resp; 
 | 
    struct octeon_soft_command *sc; 
 | 
    struct octeon_device *oct; 
 | 
    union octnet_cmd *ncmd; 
 | 
    int retval; 
 | 
    u32 var; 
 | 
  
 | 
    oct = lio->oct_dev; 
 | 
  
 | 
    sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE, 
 | 
                       sizeof(struct oct_nic_seapi_resp), 0); 
 | 
    if (!sc) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    ncmd = sc->virtdptr; 
 | 
    resp = sc->virtrptr; 
 | 
    memset(resp, 0, sizeof(struct oct_nic_seapi_resp)); 
 | 
  
 | 
    init_completion(&sc->complete); 
 | 
    sc->sc_status = OCTEON_REQUEST_PENDING; 
 | 
  
 | 
    ncmd->u64 = 0; 
 | 
    ncmd->s.cmd = SEAPI_CMD_FEC_GET; 
 | 
  
 | 
    octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3)); 
 | 
  
 | 
    sc->iq_no = lio->linfo.txpciq[0].s.q_no; 
 | 
  
 | 
    octeon_prepare_soft_command(oct, sc, OPCODE_NIC, 
 | 
                    OPCODE_NIC_UBOOT_CTL, 0, 0, 0); 
 | 
  
 | 
    retval = octeon_send_soft_command(oct, sc); 
 | 
    if (retval == IQ_SEND_FAILED) { 
 | 
        dev_info(&oct->pci_dev->dev, 
 | 
             "%s: Failed to send soft command\n", __func__); 
 | 
        octeon_free_soft_command(oct, sc); 
 | 
        return -EIO; 
 | 
    } 
 | 
  
 | 
    retval = wait_for_sc_completion_timeout(oct, sc, 0); 
 | 
    if (retval) 
 | 
        return retval; 
 | 
  
 | 
    var = be32_to_cpu(resp->fec_setting); 
 | 
    resp->fec_setting = var; 
 | 
    if (resp->fec_setting == SEAPI_CMD_FEC_SET_RS) 
 | 
        oct->props[lio->ifidx].fec = 1; 
 | 
    else 
 | 
        oct->props[lio->ifidx].fec = 0; 
 | 
  
 | 
    WRITE_ONCE(sc->caller_is_done, true); 
 | 
  
 | 
    if (oct->props[lio->ifidx].fec != 
 | 
        oct->props[lio->ifidx].fec_boot) { 
 | 
        dev_dbg(&oct->pci_dev->dev, 
 | 
            "Reload driver to change fec to %s\n", 
 | 
            oct->props[lio->ifidx].fec ? "on" : "off"); 
 | 
    } 
 | 
  
 | 
    return retval; 
 | 
} 
 |