diff options
author | davidcs <davidcs@FreeBSD.org> | 2013-06-25 17:50:22 +0000 |
---|---|---|
committer | davidcs <davidcs@FreeBSD.org> | 2013-06-25 17:50:22 +0000 |
commit | 35215c2644c33df865ab3dfbd71d550c14109a63 (patch) | |
tree | ed7e56e5bff99869e0b059a43465687ed33bb64f /sys/dev/qlxge/qls_isr.c | |
parent | b29359892ac52ab49c49c6c28c886afd659a2d4c (diff) | |
download | FreeBSD-src-35215c2644c33df865ab3dfbd71d550c14109a63.zip FreeBSD-src-35215c2644c33df865ab3dfbd71d550c14109a63.tar.gz |
Add Qlogic 10Gb Ethernet Driver for Qlogic 8100 Series CNA Adapter
Driver version (v2.0.0)
Submitted by: David C Somayajulu (davidcs@freebsd.org) QLogic Corporation
Approved by: George Neville-Neil (gnn@freebsd.org)
Diffstat (limited to 'sys/dev/qlxge/qls_isr.c')
-rw-r--r-- | sys/dev/qlxge/qls_isr.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/sys/dev/qlxge/qls_isr.c b/sys/dev/qlxge/qls_isr.c new file mode 100644 index 0000000..26e431e --- /dev/null +++ b/sys/dev/qlxge/qls_isr.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2013-2014 Qlogic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * File: qls_isr.c + * Author : David C Somayajulu, Qlogic Corporation, Aliso Viejo, CA 92656. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + + + +#include "qls_os.h" +#include "qls_hw.h" +#include "qls_def.h" +#include "qls_inline.h" +#include "qls_ver.h" +#include "qls_glbl.h" +#include "qls_dbg.h" + + +static void +qls_tx_comp(qla_host_t *ha, uint32_t txr_idx, q81_tx_mac_comp_t *tx_comp) +{ + qla_tx_buf_t *txb; + uint32_t tx_idx = tx_comp->tid_lo; + + if (tx_idx >= NUM_TX_DESCRIPTORS) { + ha->qla_initiate_recovery = 1; + return; + } + + txb = &ha->tx_ring[txr_idx].tx_buf[tx_idx]; + + if (txb->m_head) { + ha->ifp->if_opackets++; + bus_dmamap_sync(ha->tx_tag, txb->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ha->tx_tag, txb->map); + m_freem(txb->m_head); + + txb->m_head = NULL; + } + + ha->tx_ring[txr_idx].txr_done++; + + if (ha->tx_ring[txr_idx].txr_done == NUM_TX_DESCRIPTORS) + ha->tx_ring[txr_idx].txr_done = 0; +} + +static void +qls_replenish_rx(qla_host_t *ha, uint32_t r_idx) +{ + qla_rx_buf_t *rxb; + qla_rx_ring_t *rxr; + int count; + volatile q81_bq_addr_e_t *sbq_e; + + rxr = &ha->rx_ring[r_idx]; + + count = rxr->rx_free; + sbq_e = rxr->sbq_vaddr; + + while (count--) { + + rxb = &rxr->rx_buf[rxr->sbq_next]; + + if (rxb->m_head == NULL) { + if (qls_get_mbuf(ha, rxb, NULL) != 0) { + device_printf(ha->pci_dev, + "%s: qls_get_mbuf [0,%d,%d] failed\n", + __func__, rxr->sbq_next, r_idx); + rxb->m_head = NULL; + break; + } + } + + if (rxb->m_head != NULL) { + sbq_e[rxr->sbq_next].addr_lo = (uint32_t)rxb->paddr; + sbq_e[rxr->sbq_next].addr_hi = + (uint32_t)(rxb->paddr >> 32); + + rxr->sbq_next++; + if (rxr->sbq_next == NUM_RX_DESCRIPTORS) + rxr->sbq_next = 0; + + rxr->sbq_free++; + rxr->rx_free--; + } + + if (rxr->sbq_free == 16) { + + rxr->sbq_in += 16; + rxr->sbq_in = rxr->sbq_in & (NUM_RX_DESCRIPTORS - 1); + rxr->sbq_free = 0; + + Q81_WR_SBQ_PROD_IDX(r_idx, (rxr->sbq_in)); + } + } +} + +static int +qls_rx_comp(qla_host_t *ha, uint32_t rxr_idx, uint32_t cq_idx, q81_rx_t *cq_e) +{ + qla_rx_buf_t *rxb; + qla_rx_ring_t *rxr; + device_t dev = ha->pci_dev; + struct mbuf *mp = NULL; + struct ifnet *ifp = ha->ifp; + struct lro_ctrl *lro; + struct ether_vlan_header *eh; + + rxr = &ha->rx_ring[rxr_idx]; + + lro = &rxr->lro; + + rxb = &rxr->rx_buf[rxr->rx_next]; + + if (!(cq_e->flags1 & Q81_RX_FLAGS1_DS)) { + device_printf(dev, "%s: DS bit not set \n", __func__); + return -1; + } + if (rxb->paddr != cq_e->b_paddr) { + + device_printf(dev, + "%s: (rxb->paddr != cq_e->b_paddr)[%p, %p] \n", + __func__, (void *)rxb->paddr, (void *)cq_e->b_paddr); + + Q81_SET_CQ_INVALID(cq_idx); + + ha->qla_initiate_recovery = 1; + + return(-1); + } + + rxr->rx_int++; + + if ((cq_e->flags1 & Q81_RX_FLAGS1_ERR_MASK) == 0) { + + mp = rxb->m_head; + rxb->m_head = NULL; + + if (mp == NULL) { + device_printf(dev, "%s: mp == NULL\n", __func__); + } else { + mp->m_flags |= M_PKTHDR; + mp->m_pkthdr.len = cq_e->length; + mp->m_pkthdr.rcvif = ifp; + mp->m_len = cq_e->length; + + eh = mtod(mp, struct ether_vlan_header *); + + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + uint32_t *data = (uint32_t *)eh; + + mp->m_pkthdr.ether_vtag = ntohs(eh->evl_tag); + mp->m_flags |= M_VLANTAG; + + *(data + 3) = *(data + 2); + *(data + 2) = *(data + 1); + *(data + 1) = *data; + + m_adj(mp, ETHER_VLAN_ENCAP_LEN); + } + + if ((cq_e->flags1 & Q81_RX_FLAGS1_RSS_MATCH_MASK)) { + rxr->rss_int++; + mp->m_pkthdr.flowid = cq_e->rss; + mp->m_flags |= M_FLOWID; + } + if (cq_e->flags0 & (Q81_RX_FLAGS0_TE | + Q81_RX_FLAGS0_NU | Q81_RX_FLAGS0_IE)) { + mp->m_pkthdr.csum_flags = 0; + } else { + mp->m_pkthdr.csum_flags = CSUM_IP_CHECKED | + CSUM_IP_VALID | CSUM_DATA_VALID | + CSUM_PSEUDO_HDR; + mp->m_pkthdr.csum_data = 0xFFFF; + } + ifp->if_ipackets++; + + if (lro->lro_cnt && (tcp_lro_rx(lro, mp, 0) == 0)) { + /* LRO packet has been successfuly queued */ + } else { + (*ifp->if_input)(ifp, mp); + } + } + } else { + device_printf(dev, "%s: err [0%08x]\n", __func__, cq_e->flags1); + } + + rxr->rx_free++; + rxr->rx_next++; + + if (rxr->rx_next == NUM_RX_DESCRIPTORS) + rxr->rx_next = 0; + + if ((rxr->rx_free + rxr->sbq_free) >= 16) + qls_replenish_rx(ha, rxr_idx); + + return 0; +} + +static void +qls_cq_isr(qla_host_t *ha, uint32_t cq_idx) +{ + q81_cq_e_t *cq_e, *cq_b; + uint32_t i, cq_comp_idx; + int ret = 0, tx_comp_done = 0; + struct lro_ctrl *lro; + struct lro_entry *queued; + + cq_b = ha->rx_ring[cq_idx].cq_base_vaddr; + lro = &ha->rx_ring[cq_idx].lro; + + cq_comp_idx = *(ha->rx_ring[cq_idx].cqi_vaddr); + + i = ha->rx_ring[cq_idx].cq_next; + + while (i != cq_comp_idx) { + + cq_e = &cq_b[i]; + + switch (cq_e->opcode) { + + case Q81_IOCB_TX_MAC: + case Q81_IOCB_TX_TSO: + qls_tx_comp(ha, cq_idx, (q81_tx_mac_comp_t *)cq_e); + tx_comp_done++; + break; + + case Q81_IOCB_RX: + ret = qls_rx_comp(ha, cq_idx, i, (q81_rx_t *)cq_e); + + break; + + case Q81_IOCB_MPI: + case Q81_IOCB_SYS: + default: + device_printf(ha->pci_dev, "%s[%d %d 0x%x]: illegal \n", + __func__, i, (*(ha->rx_ring[cq_idx].cqi_vaddr)), + cq_e->opcode); + qls_dump_buf32(ha, __func__, cq_e, + (sizeof (q81_cq_e_t) >> 2)); + break; + } + + i++; + if (i == NUM_CQ_ENTRIES) + i = 0; + + if (ret) { + break; + } + + if (i == cq_comp_idx) { + cq_comp_idx = *(ha->rx_ring[cq_idx].cqi_vaddr); + } + + if (tx_comp_done) { + taskqueue_enqueue(ha->tx_tq, &ha->tx_task); + tx_comp_done = 0; + } + } + + while((!SLIST_EMPTY(&lro->lro_active))) { + queued = SLIST_FIRST(&lro->lro_active); + SLIST_REMOVE_HEAD(&lro->lro_active, next); + tcp_lro_flush(lro, queued); + } + + ha->rx_ring[cq_idx].cq_next = cq_comp_idx; + + if (!ret) { + Q81_WR_CQ_CONS_IDX(cq_idx, (ha->rx_ring[cq_idx].cq_next)); + } + if (tx_comp_done) + taskqueue_enqueue(ha->tx_tq, &ha->tx_task); + + return; +} + +static void +qls_mbx_isr(qla_host_t *ha) +{ + uint32_t data; + int i; + device_t dev = ha->pci_dev; + + if (qls_mbx_rd_reg(ha, 0, &data) == 0) { + + if ((data & 0xF000) == 0x4000) { + ha->mbox[0] = data; + for (i = 1; i < Q81_NUM_MBX_REGISTERS; i++) { + if (qls_mbx_rd_reg(ha, i, &data)) + break; + ha->mbox[i] = data; + } + ha->mbx_done = 1; + } else if ((data & 0xF000) == 0x8000) { + + /* we have an AEN */ + + ha->aen[0] = data; + for (i = 1; i < Q81_NUM_AEN_REGISTERS; i++) { + if (qls_mbx_rd_reg(ha, i, &data)) + break; + ha->aen[i] = data; + } + device_printf(dev,"%s: AEN " + "[0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" + " 0x%08x 0x%08x 0x%08x 0x%08x]\n", + __func__, + ha->aen[0], ha->aen[1], ha->aen[2], + ha->aen[3], ha->aen[4], ha->aen[5], + ha->aen[6], ha->aen[7], ha->aen[8]); + + switch ((ha->aen[0] & 0xFFFF)) { + + case 0x8011: + ha->link_up = 1; + break; + + case 0x8012: + ha->link_up = 0; + break; + + case 0x8130: + ha->link_hw_info = ha->aen[1]; + break; + + case 0x8131: + ha->link_hw_info = 0; + break; + + } + } + } + WRITE_REG32(ha, Q81_CTL_HOST_CMD_STATUS, Q81_CTL_HCS_CMD_CLR_RTH_INTR); + + return; +} + +void +qls_isr(void *arg) +{ + qla_ivec_t *ivec = arg; + qla_host_t *ha; + uint32_t status; + uint32_t cq_idx; + device_t dev; + + ha = ivec->ha; + cq_idx = ivec->cq_idx; + dev = ha->pci_dev; + + status = READ_REG32(ha, Q81_CTL_STATUS); + + if (status & Q81_CTL_STATUS_FE) { + device_printf(dev, "%s fatal error\n", __func__); + return; + } + + if ((cq_idx == 0) && (status & Q81_CTL_STATUS_PI)) { + qls_mbx_isr(ha); + } + + status = READ_REG32(ha, Q81_CTL_INTR_STATUS1); + + if (status & ( 0x1 << cq_idx)) + qls_cq_isr(ha, cq_idx); + + Q81_ENABLE_INTR(ha, cq_idx); + + return; +} + |