1227064Sbz/* 2227064Sbz * Copyright (c) 2010-2011 Qlogic Corporation 3227064Sbz * All rights reserved. 4227064Sbz * 5227064Sbz * Redistribution and use in source and binary forms, with or without 6227064Sbz * modification, are permitted provided that the following conditions 7227064Sbz * are met: 8227064Sbz * 9227064Sbz * 1. Redistributions of source code must retain the above copyright 10227064Sbz * notice, this list of conditions and the following disclaimer. 11227064Sbz * 2. Redistributions in binary form must reproduce the above copyright 12227064Sbz * notice, this list of conditions and the following disclaimer in the 13227064Sbz * documentation and/or other materials provided with the distribution. 14227064Sbz * 15227064Sbz * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16227064Sbz * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17227064Sbz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18227064Sbz * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19227064Sbz * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20227064Sbz * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21227064Sbz * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22227064Sbz * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23227064Sbz * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24227064Sbz * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25227064Sbz * POSSIBILITY OF SUCH DAMAGE. 26227064Sbz */ 27227064Sbz 28227064Sbz/* 29227064Sbz * File: qla_hw.c 30227064Sbz * Author : David C Somayajulu, Qlogic Corporation, Aliso Viejo, CA 92656. 31227064Sbz * Content: Contains Hardware dependant functions 32227064Sbz */ 33227064Sbz 34227064Sbz#include <sys/cdefs.h> 35227064Sbz__FBSDID("$FreeBSD$"); 36227064Sbz 37227064Sbz#include "qla_os.h" 38227064Sbz#include "qla_reg.h" 39227064Sbz#include "qla_hw.h" 40227064Sbz#include "qla_def.h" 41227064Sbz#include "qla_inline.h" 42227064Sbz#include "qla_ver.h" 43227064Sbz#include "qla_glbl.h" 44227064Sbz#include "qla_dbg.h" 45227064Sbz 46227064Sbzstatic uint32_t sysctl_num_rds_rings = 2; 47227064Sbzstatic uint32_t sysctl_num_sds_rings = 4; 48227064Sbz 49227064Sbz/* 50227064Sbz * Static Functions 51227064Sbz */ 52227064Sbz 53227064Sbzstatic void qla_init_cntxt_regions(qla_host_t *ha); 54227064Sbzstatic int qla_issue_cmd(qla_host_t *ha, qla_cdrp_t *cdrp); 55227064Sbzstatic int qla_fw_cmd(qla_host_t *ha, void *fw_cmd, uint32_t size); 56227064Sbzstatic int qla_config_mac_addr(qla_host_t *ha, uint8_t *mac_addr, 57227064Sbz uint16_t cntxt_id, uint32_t add_multi); 58227064Sbzstatic void qla_del_rcv_cntxt(qla_host_t *ha); 59227064Sbzstatic int qla_init_rcv_cntxt(qla_host_t *ha); 60227064Sbzstatic void qla_del_xmt_cntxt(qla_host_t *ha); 61227064Sbzstatic int qla_init_xmt_cntxt(qla_host_t *ha); 62227064Sbzstatic int qla_get_max_rds(qla_host_t *ha); 63227064Sbzstatic int qla_get_max_sds(qla_host_t *ha); 64227064Sbzstatic int qla_get_max_rules(qla_host_t *ha); 65227064Sbzstatic int qla_get_max_rcv_cntxts(qla_host_t *ha); 66227064Sbzstatic int qla_get_max_tx_cntxts(qla_host_t *ha); 67227064Sbzstatic int qla_get_max_mtu(qla_host_t *ha); 68227064Sbzstatic int qla_get_max_lro(qla_host_t *ha); 69227064Sbzstatic int qla_get_flow_control(qla_host_t *ha); 70227064Sbzstatic void qla_hw_tx_done_locked(qla_host_t *ha); 71227064Sbz 72227064Sbzint 73227064Sbzqla_get_msix_count(qla_host_t *ha) 74227064Sbz{ 75227064Sbz return (sysctl_num_sds_rings); 76227064Sbz} 77227064Sbz 78227064Sbz/* 79227064Sbz * Name: qla_hw_add_sysctls 80227064Sbz * Function: Add P3Plus specific sysctls 81227064Sbz */ 82227064Sbzvoid 83227064Sbzqla_hw_add_sysctls(qla_host_t *ha) 84227064Sbz{ 85227064Sbz device_t dev; 86227064Sbz 87227064Sbz dev = ha->pci_dev; 88227064Sbz 89227064Sbz SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 90227064Sbz SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 91227064Sbz OID_AUTO, "num_rds_rings", CTLFLAG_RD, &sysctl_num_rds_rings, 92227064Sbz sysctl_num_rds_rings, "Number of Rcv Descriptor Rings"); 93227064Sbz 94227064Sbz SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 95227064Sbz SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 96227064Sbz OID_AUTO, "num_sds_rings", CTLFLAG_RD, &sysctl_num_sds_rings, 97227064Sbz sysctl_num_sds_rings, "Number of Status Descriptor Rings"); 98227064Sbz} 99227064Sbz 100227064Sbz/* 101227064Sbz * Name: qla_free_dma 102227064Sbz * Function: Frees the DMA'able memory allocated in qla_alloc_dma() 103227064Sbz */ 104227064Sbzvoid 105227064Sbzqla_free_dma(qla_host_t *ha) 106227064Sbz{ 107227064Sbz uint32_t i; 108227064Sbz 109227064Sbz if (ha->hw.dma_buf.flags.context) { 110227064Sbz qla_free_dmabuf(ha, &ha->hw.dma_buf.context); 111227064Sbz ha->hw.dma_buf.flags.context = 0; 112227064Sbz } 113227064Sbz 114227064Sbz if (ha->hw.dma_buf.flags.sds_ring) { 115227064Sbz for (i = 0; i < ha->hw.num_sds_rings; i++) 116227064Sbz qla_free_dmabuf(ha, &ha->hw.dma_buf.sds_ring[i]); 117227064Sbz ha->hw.dma_buf.flags.sds_ring = 0; 118227064Sbz } 119227064Sbz 120227064Sbz if (ha->hw.dma_buf.flags.rds_ring) { 121227064Sbz for (i = 0; i < ha->hw.num_rds_rings; i++) 122227064Sbz qla_free_dmabuf(ha, &ha->hw.dma_buf.rds_ring[i]); 123227064Sbz ha->hw.dma_buf.flags.rds_ring = 0; 124227064Sbz } 125227064Sbz 126227064Sbz if (ha->hw.dma_buf.flags.tx_ring) { 127227064Sbz qla_free_dmabuf(ha, &ha->hw.dma_buf.tx_ring); 128227064Sbz ha->hw.dma_buf.flags.tx_ring = 0; 129227064Sbz } 130227064Sbz} 131227064Sbz 132227064Sbz/* 133227064Sbz * Name: qla_alloc_dma 134227064Sbz * Function: Allocates DMA'able memory for Tx/Rx Rings, Tx/Rx Contexts. 135227064Sbz */ 136227064Sbzint 137227064Sbzqla_alloc_dma(qla_host_t *ha) 138227064Sbz{ 139227064Sbz device_t dev; 140227064Sbz uint32_t i, j, size; 141227064Sbz 142227064Sbz dev = ha->pci_dev; 143227064Sbz 144227064Sbz QL_DPRINT2((dev, "%s: enter\n", __func__)); 145227064Sbz 146227064Sbz ha->hw.num_rds_rings = (uint16_t)sysctl_num_rds_rings; 147227064Sbz ha->hw.num_sds_rings = (uint16_t)sysctl_num_sds_rings; 148227064Sbz 149227064Sbz /* 150227064Sbz * Allocate Transmit Ring 151227064Sbz */ 152227064Sbz 153227064Sbz ha->hw.dma_buf.tx_ring.alignment = 8; 154227064Sbz ha->hw.dma_buf.tx_ring.size = 155227064Sbz (sizeof(q80_tx_cmd_t)) * NUM_TX_DESCRIPTORS; 156227064Sbz 157227064Sbz if (qla_alloc_dmabuf(ha, &ha->hw.dma_buf.tx_ring)) { 158227064Sbz device_printf(dev, "%s: tx ring alloc failed\n", __func__); 159227064Sbz goto qla_alloc_dma_exit; 160227064Sbz } 161227064Sbz ha->hw.dma_buf.flags.tx_ring = 1; 162227064Sbz 163227064Sbz QL_DPRINT2((dev, "%s: tx_ring phys %p virt %p\n", 164227064Sbz __func__, (void *)(ha->hw.dma_buf.tx_ring.dma_addr), 165227064Sbz ha->hw.dma_buf.tx_ring.dma_b)); 166227064Sbz /* 167227064Sbz * Allocate Receive Descriptor Rings 168227064Sbz */ 169227064Sbz 170227064Sbz for (i = 0; i < ha->hw.num_rds_rings; i++) { 171227064Sbz ha->hw.dma_buf.rds_ring[i].alignment = 8; 172227064Sbz 173227064Sbz if (i == RDS_RING_INDEX_NORMAL) { 174227064Sbz ha->hw.dma_buf.rds_ring[i].size = 175227064Sbz (sizeof(q80_recv_desc_t)) * NUM_RX_DESCRIPTORS; 176227064Sbz } else if (i == RDS_RING_INDEX_JUMBO) { 177227064Sbz ha->hw.dma_buf.rds_ring[i].size = 178227064Sbz (sizeof(q80_recv_desc_t)) * 179227064Sbz NUM_RX_JUMBO_DESCRIPTORS; 180227064Sbz } else 181227064Sbz break; 182227064Sbz 183227064Sbz if (qla_alloc_dmabuf(ha, &ha->hw.dma_buf.rds_ring[i])) { 184227064Sbz QL_DPRINT4((dev, "%s: rds ring alloc failed\n", 185227064Sbz __func__)); 186227064Sbz 187227064Sbz for (j = 0; j < i; j++) 188227064Sbz qla_free_dmabuf(ha, 189227064Sbz &ha->hw.dma_buf.rds_ring[j]); 190227064Sbz 191227064Sbz goto qla_alloc_dma_exit; 192227064Sbz } 193227064Sbz QL_DPRINT4((dev, "%s: rx_ring[%d] phys %p virt %p\n", 194227064Sbz __func__, i, 195227064Sbz (void *)(ha->hw.dma_buf.rds_ring[i].dma_addr), 196227064Sbz ha->hw.dma_buf.rds_ring[i].dma_b)); 197227064Sbz } 198227064Sbz ha->hw.dma_buf.flags.rds_ring = 1; 199227064Sbz 200227064Sbz /* 201227064Sbz * Allocate Status Descriptor Rings 202227064Sbz */ 203227064Sbz 204227064Sbz for (i = 0; i < ha->hw.num_sds_rings; i++) { 205227064Sbz ha->hw.dma_buf.sds_ring[i].alignment = 8; 206227064Sbz ha->hw.dma_buf.sds_ring[i].size = 207227064Sbz (sizeof(q80_stat_desc_t)) * NUM_STATUS_DESCRIPTORS; 208227064Sbz 209227064Sbz if (qla_alloc_dmabuf(ha, &ha->hw.dma_buf.sds_ring[i])) { 210227064Sbz device_printf(dev, "%s: sds ring alloc failed\n", 211227064Sbz __func__); 212227064Sbz 213227064Sbz for (j = 0; j < i; j++) 214227064Sbz qla_free_dmabuf(ha, 215227064Sbz &ha->hw.dma_buf.sds_ring[j]); 216227064Sbz 217227064Sbz goto qla_alloc_dma_exit; 218227064Sbz } 219227064Sbz QL_DPRINT4((dev, "%s: sds_ring[%d] phys %p virt %p\n", 220227064Sbz __func__, i, 221227064Sbz (void *)(ha->hw.dma_buf.sds_ring[i].dma_addr), 222227064Sbz ha->hw.dma_buf.sds_ring[i].dma_b)); 223227064Sbz } 224227064Sbz ha->hw.dma_buf.flags.sds_ring = 1; 225227064Sbz 226227064Sbz /* 227227064Sbz * Allocate Context Area 228227064Sbz */ 229227064Sbz size = QL_ALIGN((sizeof (q80_tx_cntxt_req_t)), QL_BUFFER_ALIGN); 230227064Sbz 231227064Sbz size += QL_ALIGN((sizeof (q80_tx_cntxt_rsp_t)), QL_BUFFER_ALIGN); 232227064Sbz 233227064Sbz size += QL_ALIGN((sizeof (q80_rcv_cntxt_req_t)), QL_BUFFER_ALIGN); 234227064Sbz 235227064Sbz size += QL_ALIGN((sizeof (q80_rcv_cntxt_rsp_t)), QL_BUFFER_ALIGN); 236227064Sbz 237227064Sbz size += sizeof (uint32_t); /* for tx consumer index */ 238227064Sbz 239227064Sbz size = QL_ALIGN(size, PAGE_SIZE); 240227064Sbz 241227064Sbz ha->hw.dma_buf.context.alignment = 8; 242227064Sbz ha->hw.dma_buf.context.size = size; 243227064Sbz 244227064Sbz if (qla_alloc_dmabuf(ha, &ha->hw.dma_buf.context)) { 245227064Sbz device_printf(dev, "%s: context alloc failed\n", __func__); 246227064Sbz goto qla_alloc_dma_exit; 247227064Sbz } 248227064Sbz ha->hw.dma_buf.flags.context = 1; 249227064Sbz QL_DPRINT2((dev, "%s: context phys %p virt %p\n", 250227064Sbz __func__, (void *)(ha->hw.dma_buf.context.dma_addr), 251227064Sbz ha->hw.dma_buf.context.dma_b)); 252227064Sbz 253227064Sbz qla_init_cntxt_regions(ha); 254227064Sbz 255227064Sbz return 0; 256227064Sbz 257227064Sbzqla_alloc_dma_exit: 258227064Sbz qla_free_dma(ha); 259227064Sbz return -1; 260227064Sbz} 261227064Sbz 262227064Sbz/* 263227064Sbz * Name: qla_init_cntxt_regions 264227064Sbz * Function: Initializes Tx/Rx Contexts. 265227064Sbz */ 266227064Sbzstatic void 267227064Sbzqla_init_cntxt_regions(qla_host_t *ha) 268227064Sbz{ 269227064Sbz qla_hw_t *hw; 270227064Sbz q80_tx_cntxt_req_t *tx_cntxt_req; 271227064Sbz q80_rcv_cntxt_req_t *rx_cntxt_req; 272227064Sbz bus_addr_t phys_addr; 273227064Sbz uint32_t i; 274227064Sbz device_t dev; 275227064Sbz uint32_t size; 276227064Sbz 277227064Sbz dev = ha->pci_dev; 278227064Sbz 279227064Sbz hw = &ha->hw; 280227064Sbz 281227064Sbz hw->tx_ring_base = hw->dma_buf.tx_ring.dma_b; 282227064Sbz 283227064Sbz for (i = 0; i < ha->hw.num_sds_rings; i++) 284227064Sbz hw->sds[i].sds_ring_base = 285227064Sbz (q80_stat_desc_t *)hw->dma_buf.sds_ring[i].dma_b; 286227064Sbz 287227064Sbz 288227064Sbz phys_addr = hw->dma_buf.context.dma_addr; 289227064Sbz 290227064Sbz memset((void *)hw->dma_buf.context.dma_b, 0, 291227064Sbz ha->hw.dma_buf.context.size); 292227064Sbz 293227064Sbz hw->tx_cntxt_req = 294227064Sbz (q80_tx_cntxt_req_t *)hw->dma_buf.context.dma_b; 295227064Sbz hw->tx_cntxt_req_paddr = phys_addr; 296227064Sbz 297227064Sbz size = QL_ALIGN((sizeof (q80_tx_cntxt_req_t)), QL_BUFFER_ALIGN); 298227064Sbz 299227064Sbz hw->tx_cntxt_rsp = 300227064Sbz (q80_tx_cntxt_rsp_t *)((uint8_t *)hw->tx_cntxt_req + size); 301227064Sbz hw->tx_cntxt_rsp_paddr = hw->tx_cntxt_req_paddr + size; 302227064Sbz 303227064Sbz size = QL_ALIGN((sizeof (q80_tx_cntxt_rsp_t)), QL_BUFFER_ALIGN); 304227064Sbz 305227064Sbz hw->rx_cntxt_req = 306227064Sbz (q80_rcv_cntxt_req_t *)((uint8_t *)hw->tx_cntxt_rsp + size); 307227064Sbz hw->rx_cntxt_req_paddr = hw->tx_cntxt_rsp_paddr + size; 308227064Sbz 309227064Sbz size = QL_ALIGN((sizeof (q80_rcv_cntxt_req_t)), QL_BUFFER_ALIGN); 310227064Sbz 311227064Sbz hw->rx_cntxt_rsp = 312227064Sbz (q80_rcv_cntxt_rsp_t *)((uint8_t *)hw->rx_cntxt_req + size); 313227064Sbz hw->rx_cntxt_rsp_paddr = hw->rx_cntxt_req_paddr + size; 314227064Sbz 315227064Sbz size = QL_ALIGN((sizeof (q80_rcv_cntxt_rsp_t)), QL_BUFFER_ALIGN); 316227064Sbz 317227064Sbz hw->tx_cons = (uint32_t *)((uint8_t *)hw->rx_cntxt_rsp + size); 318227064Sbz hw->tx_cons_paddr = hw->rx_cntxt_rsp_paddr + size; 319227064Sbz 320227064Sbz /* 321227064Sbz * Initialize the Transmit Context Request so that we don't need to 322227064Sbz * do it everytime we need to create a context 323227064Sbz */ 324227064Sbz tx_cntxt_req = hw->tx_cntxt_req; 325227064Sbz 326227064Sbz tx_cntxt_req->rsp_dma_addr = qla_host_to_le64(hw->tx_cntxt_rsp_paddr); 327227064Sbz 328227064Sbz tx_cntxt_req->cmd_cons_dma_addr = qla_host_to_le64(hw->tx_cons_paddr); 329227064Sbz 330227064Sbz tx_cntxt_req->caps[0] = qla_host_to_le32((CNTXT_CAP0_BASEFW | 331227064Sbz CNTXT_CAP0_LEGACY_MN | CNTXT_CAP0_LSO)); 332227064Sbz 333227064Sbz tx_cntxt_req->intr_mode = qla_host_to_le32(CNTXT_INTR_MODE_SHARED); 334227064Sbz 335227064Sbz tx_cntxt_req->phys_addr = 336227064Sbz qla_host_to_le64(hw->dma_buf.tx_ring.dma_addr); 337227064Sbz 338227064Sbz tx_cntxt_req->num_entries = qla_host_to_le32(NUM_TX_DESCRIPTORS); 339227064Sbz 340227064Sbz /* 341227064Sbz * Initialize the Receive Context Request 342227064Sbz */ 343227064Sbz 344227064Sbz rx_cntxt_req = hw->rx_cntxt_req; 345227064Sbz 346227064Sbz rx_cntxt_req->rx_req.rsp_dma_addr = 347227064Sbz qla_host_to_le64(hw->rx_cntxt_rsp_paddr); 348227064Sbz 349227064Sbz rx_cntxt_req->rx_req.caps[0] = qla_host_to_le32(CNTXT_CAP0_BASEFW | 350227064Sbz CNTXT_CAP0_LEGACY_MN | 351227064Sbz CNTXT_CAP0_JUMBO | 352227064Sbz CNTXT_CAP0_LRO| 353227064Sbz CNTXT_CAP0_HW_LRO); 354227064Sbz 355227064Sbz rx_cntxt_req->rx_req.intr_mode = 356227064Sbz qla_host_to_le32(CNTXT_INTR_MODE_SHARED); 357227064Sbz 358227064Sbz rx_cntxt_req->rx_req.rds_intr_mode = 359227064Sbz qla_host_to_le32(CNTXT_INTR_MODE_UNIQUE); 360227064Sbz 361227064Sbz rx_cntxt_req->rx_req.rds_ring_offset = 0; 362227064Sbz rx_cntxt_req->rx_req.sds_ring_offset = qla_host_to_le32( 363227064Sbz (hw->num_rds_rings * sizeof(q80_rq_rds_ring_t))); 364227064Sbz rx_cntxt_req->rx_req.num_rds_rings = 365227064Sbz qla_host_to_le16(hw->num_rds_rings); 366227064Sbz rx_cntxt_req->rx_req.num_sds_rings = 367227064Sbz qla_host_to_le16(hw->num_sds_rings); 368227064Sbz 369227064Sbz for (i = 0; i < hw->num_rds_rings; i++) { 370227064Sbz rx_cntxt_req->rds_req[i].phys_addr = 371227064Sbz qla_host_to_le64(hw->dma_buf.rds_ring[i].dma_addr); 372227064Sbz 373227064Sbz if (i == RDS_RING_INDEX_NORMAL) { 374227064Sbz rx_cntxt_req->rds_req[i].buf_size = 375227064Sbz qla_host_to_le64(MCLBYTES); 376227064Sbz rx_cntxt_req->rds_req[i].size = 377227064Sbz qla_host_to_le32(NUM_RX_DESCRIPTORS); 378227064Sbz } else { 379227064Sbz rx_cntxt_req->rds_req[i].buf_size = 380227064Sbz qla_host_to_le64(MJUM9BYTES); 381227064Sbz rx_cntxt_req->rds_req[i].size = 382227064Sbz qla_host_to_le32(NUM_RX_JUMBO_DESCRIPTORS); 383227064Sbz } 384227064Sbz } 385227064Sbz 386227064Sbz for (i = 0; i < hw->num_sds_rings; i++) { 387227064Sbz rx_cntxt_req->sds_req[i].phys_addr = 388227064Sbz qla_host_to_le64(hw->dma_buf.sds_ring[i].dma_addr); 389227064Sbz rx_cntxt_req->sds_req[i].size = 390227064Sbz qla_host_to_le32(NUM_STATUS_DESCRIPTORS); 391227064Sbz rx_cntxt_req->sds_req[i].msi_index = qla_host_to_le16(i); 392227064Sbz } 393227064Sbz 394227064Sbz QL_DPRINT2((ha->pci_dev, "%s: tx_cntxt_req = %p paddr %p\n", 395227064Sbz __func__, hw->tx_cntxt_req, (void *)hw->tx_cntxt_req_paddr)); 396227064Sbz QL_DPRINT2((ha->pci_dev, "%s: tx_cntxt_rsp = %p paddr %p\n", 397227064Sbz __func__, hw->tx_cntxt_rsp, (void *)hw->tx_cntxt_rsp_paddr)); 398227064Sbz QL_DPRINT2((ha->pci_dev, "%s: rx_cntxt_req = %p paddr %p\n", 399227064Sbz __func__, hw->rx_cntxt_req, (void *)hw->rx_cntxt_req_paddr)); 400227064Sbz QL_DPRINT2((ha->pci_dev, "%s: rx_cntxt_rsp = %p paddr %p\n", 401227064Sbz __func__, hw->rx_cntxt_rsp, (void *)hw->rx_cntxt_rsp_paddr)); 402227064Sbz QL_DPRINT2((ha->pci_dev, "%s: tx_cons = %p paddr %p\n", 403227064Sbz __func__, hw->tx_cons, (void *)hw->tx_cons_paddr)); 404227064Sbz} 405227064Sbz 406227064Sbz/* 407227064Sbz * Name: qla_issue_cmd 408227064Sbz * Function: Issues commands on the CDRP interface and returns responses. 409227064Sbz */ 410227064Sbzstatic int 411227064Sbzqla_issue_cmd(qla_host_t *ha, qla_cdrp_t *cdrp) 412227064Sbz{ 413227064Sbz int ret = 0; 414227064Sbz uint32_t signature; 415227064Sbz uint32_t count = 400; /* 4 seconds or 400 10ms intervals */ 416227064Sbz uint32_t data; 417227064Sbz device_t dev; 418227064Sbz 419227064Sbz dev = ha->pci_dev; 420227064Sbz 421227064Sbz signature = 0xcafe0000 | 0x0100 | ha->pci_func; 422227064Sbz 423227064Sbz ret = qla_sem_lock(ha, Q8_SEM5_LOCK, 0, (uint32_t)ha->pci_func); 424227064Sbz 425227064Sbz if (ret) { 426227064Sbz device_printf(dev, "%s: SEM5_LOCK lock failed\n", __func__); 427227064Sbz return (ret); 428227064Sbz } 429227064Sbz 430227064Sbz WRITE_OFFSET32(ha, Q8_NX_CDRP_SIGNATURE, signature); 431227064Sbz 432227064Sbz WRITE_OFFSET32(ha, Q8_NX_CDRP_ARG1, (cdrp->cmd_arg1)); 433227064Sbz WRITE_OFFSET32(ha, Q8_NX_CDRP_ARG2, (cdrp->cmd_arg2)); 434227064Sbz WRITE_OFFSET32(ha, Q8_NX_CDRP_ARG3, (cdrp->cmd_arg3)); 435227064Sbz 436227064Sbz WRITE_OFFSET32(ha, Q8_NX_CDRP_CMD_RSP, cdrp->cmd); 437227064Sbz 438227064Sbz while (count) { 439227064Sbz qla_mdelay(__func__, 10); 440227064Sbz 441227064Sbz data = READ_REG32(ha, Q8_NX_CDRP_CMD_RSP); 442227064Sbz 443227064Sbz if ((!(data & 0x80000000))) 444227064Sbz break; 445227064Sbz count--; 446227064Sbz } 447227064Sbz if ((!count) || (data != 1)) 448227064Sbz ret = -1; 449227064Sbz 450227064Sbz cdrp->rsp = READ_REG32(ha, Q8_NX_CDRP_CMD_RSP); 451227064Sbz cdrp->rsp_arg1 = READ_REG32(ha, Q8_NX_CDRP_ARG1); 452227064Sbz cdrp->rsp_arg2 = READ_REG32(ha, Q8_NX_CDRP_ARG2); 453227064Sbz cdrp->rsp_arg3 = READ_REG32(ha, Q8_NX_CDRP_ARG3); 454227064Sbz 455227064Sbz qla_sem_unlock(ha, Q8_SEM5_UNLOCK); 456227064Sbz 457227064Sbz if (ret) { 458227064Sbz device_printf(dev, "%s: " 459227064Sbz "cmd[0x%08x] = 0x%08x\n" 460227064Sbz "\tsig[0x%08x] = 0x%08x\n" 461227064Sbz "\targ1[0x%08x] = 0x%08x\n" 462227064Sbz "\targ2[0x%08x] = 0x%08x\n" 463227064Sbz "\targ3[0x%08x] = 0x%08x\n", 464227064Sbz __func__, Q8_NX_CDRP_CMD_RSP, cdrp->cmd, 465227064Sbz Q8_NX_CDRP_SIGNATURE, signature, 466227064Sbz Q8_NX_CDRP_ARG1, cdrp->cmd_arg1, 467227064Sbz Q8_NX_CDRP_ARG2, cdrp->cmd_arg2, 468227064Sbz Q8_NX_CDRP_ARG3, cdrp->cmd_arg3); 469227064Sbz 470227064Sbz device_printf(dev, "%s: exit (ret = 0x%x)\n" 471227064Sbz "\t\t rsp = 0x%08x\n" 472227064Sbz "\t\t arg1 = 0x%08x\n" 473227064Sbz "\t\t arg2 = 0x%08x\n" 474227064Sbz "\t\t arg3 = 0x%08x\n", 475227064Sbz __func__, ret, cdrp->rsp, 476227064Sbz cdrp->rsp_arg1, cdrp->rsp_arg2, cdrp->rsp_arg3); 477227064Sbz } 478227064Sbz 479227064Sbz return (ret); 480227064Sbz} 481227064Sbz 482227064Sbz#define QLA_TX_MIN_FREE 2 483227064Sbz 484227064Sbz/* 485227064Sbz * Name: qla_fw_cmd 486227064Sbz * Function: Issues firmware control commands on the Tx Ring. 487227064Sbz */ 488227064Sbzstatic int 489227064Sbzqla_fw_cmd(qla_host_t *ha, void *fw_cmd, uint32_t size) 490227064Sbz{ 491227064Sbz device_t dev; 492227064Sbz q80_tx_cmd_t *tx_cmd; 493227064Sbz qla_hw_t *hw = &ha->hw; 494227064Sbz int count = 100; 495227064Sbz 496227064Sbz dev = ha->pci_dev; 497227064Sbz 498227064Sbz QLA_TX_LOCK(ha); 499227064Sbz 500227064Sbz if (hw->txr_free <= QLA_TX_MIN_FREE) { 501227064Sbz while (count--) { 502227064Sbz qla_hw_tx_done_locked(ha); 503227064Sbz if (hw->txr_free > QLA_TX_MIN_FREE) 504227064Sbz break; 505227064Sbz 506227064Sbz QLA_TX_UNLOCK(ha); 507227064Sbz qla_mdelay(__func__, 10); 508227064Sbz QLA_TX_LOCK(ha); 509227064Sbz } 510227064Sbz if (hw->txr_free <= QLA_TX_MIN_FREE) { 511227064Sbz QLA_TX_UNLOCK(ha); 512227064Sbz device_printf(dev, "%s: xmit queue full\n", __func__); 513227064Sbz return (-1); 514227064Sbz } 515227064Sbz } 516227064Sbz tx_cmd = &hw->tx_ring_base[hw->txr_next]; 517227064Sbz 518227064Sbz bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t)); 519227064Sbz 520227064Sbz bcopy(fw_cmd, tx_cmd, size); 521227064Sbz 522227064Sbz hw->txr_next = (hw->txr_next + 1) & (NUM_TX_DESCRIPTORS - 1); 523227064Sbz hw->txr_free--; 524227064Sbz 525227064Sbz QL_UPDATE_TX_PRODUCER_INDEX(ha, hw->txr_next); 526227064Sbz 527227064Sbz QLA_TX_UNLOCK(ha); 528227064Sbz 529227064Sbz return (0); 530227064Sbz} 531227064Sbz 532227064Sbz/* 533227064Sbz * Name: qla_config_rss 534227064Sbz * Function: Configure RSS for the context/interface. 535227064Sbz */ 536227064Sbzconst uint64_t rss_key[] = { 0xbeac01fa6a42b73bULL, 0x8030f20c77cb2da3ULL, 537227064Sbz 0xae7b30b4d0ca2bcbULL, 0x43a38fb04167253dULL, 538227064Sbz 0x255b0ec26d5a56daULL }; 539227064Sbz 540227064Sbzstatic int 541227064Sbzqla_config_rss(qla_host_t *ha, uint16_t cntxt_id) 542227064Sbz{ 543227064Sbz qla_fw_cds_config_rss_t rss_config; 544227064Sbz int ret, i; 545227064Sbz 546227064Sbz bzero(&rss_config, sizeof(qla_fw_cds_config_rss_t)); 547227064Sbz 548227064Sbz rss_config.hdr.cmd = Q8_FWCD_CNTRL_REQ; 549227064Sbz rss_config.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_RSS; 550227064Sbz rss_config.hdr.cntxt_id = cntxt_id; 551227064Sbz 552227064Sbz rss_config.hash_type = (Q8_FWCD_RSS_HASH_TYPE_IPV4_TCP_IP | 553227064Sbz Q8_FWCD_RSS_HASH_TYPE_IPV6_TCP_IP); 554227064Sbz rss_config.flags = Q8_FWCD_RSS_FLAGS_ENABLE_RSS; 555227064Sbz 556227064Sbz rss_config.ind_tbl_mask = 0x7; 557227064Sbz 558227064Sbz for (i = 0; i < 5; i++) 559227064Sbz rss_config.rss_key[i] = rss_key[i]; 560227064Sbz 561227064Sbz ret = qla_fw_cmd(ha, &rss_config, sizeof(qla_fw_cds_config_rss_t)); 562227064Sbz 563227064Sbz return ret; 564227064Sbz} 565227064Sbz 566227064Sbz/* 567227064Sbz * Name: qla_config_intr_coalesce 568227064Sbz * Function: Configure Interrupt Coalescing. 569227064Sbz */ 570227064Sbzstatic int 571227064Sbzqla_config_intr_coalesce(qla_host_t *ha, uint16_t cntxt_id, int tenable) 572227064Sbz{ 573227064Sbz qla_fw_cds_config_intr_coalesc_t intr_coalesce; 574227064Sbz int ret; 575227064Sbz 576227064Sbz bzero(&intr_coalesce, sizeof(qla_fw_cds_config_intr_coalesc_t)); 577227064Sbz 578227064Sbz intr_coalesce.hdr.cmd = Q8_FWCD_CNTRL_REQ; 579227064Sbz intr_coalesce.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_INTR_COALESCING; 580227064Sbz intr_coalesce.hdr.cntxt_id = cntxt_id; 581227064Sbz 582227064Sbz intr_coalesce.flags = 0x04; 583227064Sbz intr_coalesce.max_rcv_pkts = 256; 584227064Sbz intr_coalesce.max_rcv_usecs = 3; 585227064Sbz intr_coalesce.max_snd_pkts = 64; 586227064Sbz intr_coalesce.max_snd_usecs = 4; 587227064Sbz 588227064Sbz if (tenable) { 589227064Sbz intr_coalesce.usecs_to = 1000; /* 1 millisecond */ 590227064Sbz intr_coalesce.timer_type = Q8_FWCMD_INTR_COALESC_TIMER_PERIODIC; 591227064Sbz intr_coalesce.sds_ring_bitmask = 592227064Sbz Q8_FWCMD_INTR_COALESC_SDS_RING_0; 593227064Sbz } 594227064Sbz 595227064Sbz ret = qla_fw_cmd(ha, &intr_coalesce, 596227064Sbz sizeof(qla_fw_cds_config_intr_coalesc_t)); 597227064Sbz 598227064Sbz return ret; 599227064Sbz} 600227064Sbz 601227064Sbz 602227064Sbz/* 603227064Sbz * Name: qla_config_mac_addr 604227064Sbz * Function: binds a MAC address to the context/interface. 605227064Sbz * Can be unicast, multicast or broadcast. 606227064Sbz */ 607227064Sbzstatic int 608227064Sbzqla_config_mac_addr(qla_host_t *ha, uint8_t *mac_addr, uint16_t cntxt_id, 609227064Sbz uint32_t add_multi) 610227064Sbz{ 611227064Sbz qla_fw_cds_config_mac_addr_t mac_config; 612227064Sbz int ret; 613227064Sbz 614227064Sbz// device_printf(ha->pci_dev, 615227064Sbz// "%s: mac_addr %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, 616227064Sbz// mac_addr[0], mac_addr[1], mac_addr[2], 617227064Sbz// mac_addr[3], mac_addr[4], mac_addr[5]); 618227064Sbz 619227064Sbz bzero(&mac_config, sizeof(qla_fw_cds_config_mac_addr_t)); 620227064Sbz 621227064Sbz mac_config.hdr.cmd = Q8_FWCD_CNTRL_REQ; 622227064Sbz mac_config.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_MAC_ADDR; 623227064Sbz mac_config.hdr.cntxt_id = cntxt_id; 624227064Sbz 625227064Sbz if (add_multi) 626227064Sbz mac_config.cmd = Q8_FWCD_ADD_MAC_ADDR; 627227064Sbz else 628227064Sbz mac_config.cmd = Q8_FWCD_DEL_MAC_ADDR; 629227064Sbz bcopy(mac_addr, mac_config.mac_addr,6); 630227064Sbz 631227064Sbz ret = qla_fw_cmd(ha, &mac_config, sizeof(qla_fw_cds_config_mac_addr_t)); 632227064Sbz 633227064Sbz return ret; 634227064Sbz} 635227064Sbz 636227064Sbz 637227064Sbz/* 638227064Sbz * Name: qla_set_mac_rcv_mode 639227064Sbz * Function: Enable/Disable AllMulticast and Promiscous Modes. 640227064Sbz */ 641227064Sbzstatic int 642227064Sbzqla_set_mac_rcv_mode(qla_host_t *ha, uint16_t cntxt_id, uint32_t mode) 643227064Sbz{ 644227064Sbz qla_set_mac_rcv_mode_t rcv_mode; 645227064Sbz int ret; 646227064Sbz 647227064Sbz bzero(&rcv_mode, sizeof(qla_set_mac_rcv_mode_t)); 648227064Sbz 649227064Sbz rcv_mode.hdr.cmd = Q8_FWCD_CNTRL_REQ; 650227064Sbz rcv_mode.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_MAC_RCV_MODE; 651227064Sbz rcv_mode.hdr.cntxt_id = cntxt_id; 652227064Sbz 653227064Sbz rcv_mode.mode = mode; 654227064Sbz 655227064Sbz ret = qla_fw_cmd(ha, &rcv_mode, sizeof(qla_set_mac_rcv_mode_t)); 656227064Sbz 657227064Sbz return ret; 658227064Sbz} 659227064Sbz 660227064Sbzvoid 661227064Sbzqla_set_promisc(qla_host_t *ha) 662227064Sbz{ 663227064Sbz (void)qla_set_mac_rcv_mode(ha, 664227064Sbz (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id, 665227064Sbz Q8_MAC_RCV_ENABLE_PROMISCUOUS); 666227064Sbz} 667227064Sbz 668227064Sbzvoid 669227064Sbzqla_set_allmulti(qla_host_t *ha) 670227064Sbz{ 671227064Sbz (void)qla_set_mac_rcv_mode(ha, 672227064Sbz (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id, 673227064Sbz Q8_MAC_RCV_ENABLE_ALLMULTI); 674227064Sbz} 675227064Sbz 676227064Sbzvoid 677227064Sbzqla_reset_promisc_allmulti(qla_host_t *ha) 678227064Sbz{ 679227064Sbz (void)qla_set_mac_rcv_mode(ha, 680227064Sbz (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id, 681227064Sbz Q8_MAC_RCV_RESET_PROMISC_ALLMULTI); 682227064Sbz} 683227064Sbz 684227064Sbz/* 685227064Sbz * Name: qla_config_ipv4_addr 686227064Sbz * Function: Configures the Destination IP Addr for LRO. 687227064Sbz */ 688227064Sbzvoid 689227064Sbzqla_config_ipv4_addr(qla_host_t *ha, uint32_t ipv4_addr) 690227064Sbz{ 691227064Sbz qla_config_ipv4_t ip_conf; 692227064Sbz 693227064Sbz bzero(&ip_conf, sizeof(qla_config_ipv4_t)); 694227064Sbz 695227064Sbz ip_conf.hdr.cmd = Q8_FWCD_CNTRL_REQ; 696227064Sbz ip_conf.hdr.opcode = Q8_FWCD_OPCODE_CONFIG_IPADDR; 697227064Sbz ip_conf.hdr.cntxt_id = (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id; 698227064Sbz 699227064Sbz ip_conf.cmd = (uint64_t)Q8_CONFIG_CMD_IP_ENABLE; 700227064Sbz ip_conf.ipv4_addr = (uint64_t)ipv4_addr; 701227064Sbz 702227064Sbz (void)qla_fw_cmd(ha, &ip_conf, sizeof(qla_config_ipv4_t)); 703227064Sbz 704227064Sbz return; 705227064Sbz} 706227064Sbz 707227064Sbz/* 708227064Sbz * Name: qla_tx_tso 709227064Sbz * Function: Checks if the packet to be transmitted is a candidate for 710227064Sbz * Large TCP Segment Offload. If yes, the appropriate fields in the Tx 711227064Sbz * Ring Structure are plugged in. 712227064Sbz */ 713227064Sbzstatic int 714227064Sbzqla_tx_tso(qla_host_t *ha, struct mbuf *mp, q80_tx_cmd_t *tx_cmd) 715227064Sbz{ 716227064Sbz struct ether_vlan_header *eh; 717227064Sbz struct ip *ip = NULL; 718227064Sbz struct tcphdr *th = NULL; 719227064Sbz uint32_t ehdrlen, hdrlen, ip_hlen, tcp_hlen; 720227064Sbz uint16_t etype, opcode, offload = 1; 721227064Sbz device_t dev; 722227064Sbz 723227064Sbz dev = ha->pci_dev; 724227064Sbz 725227064Sbz if (mp->m_pkthdr.len <= ha->max_frame_size) 726227064Sbz return (-1); 727227064Sbz 728227064Sbz eh = mtod(mp, struct ether_vlan_header *); 729227064Sbz 730227064Sbz if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { 731227064Sbz ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 732227064Sbz etype = ntohs(eh->evl_proto); 733227064Sbz } else { 734227064Sbz ehdrlen = ETHER_HDR_LEN; 735227064Sbz etype = ntohs(eh->evl_encap_proto); 736227064Sbz } 737227064Sbz 738227064Sbz switch (etype) { 739227064Sbz case ETHERTYPE_IP: 740227064Sbz ip = (struct ip *)(mp->m_data + ehdrlen); 741227064Sbz ip_hlen = ip->ip_hl << 2; 742227064Sbz opcode = Q8_TX_CMD_OP_XMT_TCP_LSO; 743227064Sbz 744227064Sbz if (ip->ip_p != IPPROTO_TCP) { 745227064Sbz offload = 0; 746227064Sbz } else 747227064Sbz th = (struct tcphdr *)((caddr_t)ip + ip_hlen); 748227064Sbz break; 749227064Sbz 750227064Sbz default: 751227064Sbz QL_DPRINT8((dev, "%s: type!=ip\n", __func__)); 752227064Sbz offload = 0; 753227064Sbz break; 754227064Sbz } 755227064Sbz 756227064Sbz if (!offload) 757227064Sbz return (-1); 758227064Sbz 759227064Sbz tcp_hlen = th->th_off << 2; 760227064Sbz 761227064Sbz hdrlen = ehdrlen + ip_hlen + tcp_hlen; 762227064Sbz 763227064Sbz if (mp->m_len < hdrlen) { 764227064Sbz device_printf(dev, "%s: (mp->m_len < hdrlen)\n", __func__); 765227064Sbz return (-1); 766227064Sbz } 767227064Sbz 768227064Sbz tx_cmd->flags_opcode = opcode ; 769227064Sbz tx_cmd->tcp_hdr_off = ip_hlen + ehdrlen; 770227064Sbz tx_cmd->ip_hdr_off = ehdrlen; 771227064Sbz tx_cmd->mss = mp->m_pkthdr.tso_segsz; 772227064Sbz tx_cmd->total_hdr_len = hdrlen; 773227064Sbz 774227064Sbz /* Check for Multicast least significant bit of MSB == 1 */ 775227064Sbz if (eh->evl_dhost[0] & 0x01) { 776227064Sbz tx_cmd->flags_opcode = Q8_TX_CMD_FLAGS_MULTICAST; 777227064Sbz } 778227064Sbz 779227064Sbz return (0); 780227064Sbz} 781227064Sbz 782227064Sbz/* 783227064Sbz * Name: qla_tx_chksum 784227064Sbz * Function: Checks if the packet to be transmitted is a candidate for 785227064Sbz * TCP/UDP Checksum offload. If yes, the appropriate fields in the Tx 786227064Sbz * Ring Structure are plugged in. 787227064Sbz */ 788227064Sbzstatic int 789227064Sbzqla_tx_chksum(qla_host_t *ha, struct mbuf *mp, q80_tx_cmd_t *tx_cmd) 790227064Sbz{ 791227064Sbz struct ether_vlan_header *eh; 792227064Sbz struct ip *ip; 793227064Sbz struct ip6_hdr *ip6; 794227064Sbz uint32_t ehdrlen, ip_hlen; 795227064Sbz uint16_t etype, opcode, offload = 1; 796227064Sbz device_t dev; 797227064Sbz 798227064Sbz dev = ha->pci_dev; 799227064Sbz 800227064Sbz if ((mp->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) == 0) 801227064Sbz return (-1); 802227064Sbz 803227064Sbz eh = mtod(mp, struct ether_vlan_header *); 804227064Sbz 805227064Sbz if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { 806227064Sbz ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 807227064Sbz etype = ntohs(eh->evl_proto); 808227064Sbz } else { 809227064Sbz ehdrlen = ETHER_HDR_LEN; 810227064Sbz etype = ntohs(eh->evl_encap_proto); 811227064Sbz } 812227064Sbz 813227064Sbz 814227064Sbz switch (etype) { 815227064Sbz case ETHERTYPE_IP: 816227064Sbz ip = (struct ip *)(mp->m_data + ehdrlen); 817227064Sbz 818227064Sbz ip_hlen = ip->ip_hl << 2; 819227064Sbz 820227064Sbz if (mp->m_len < (ehdrlen + ip_hlen)) { 821227064Sbz device_printf(dev, "%s: ipv4 mlen\n", __func__); 822227064Sbz offload = 0; 823227064Sbz break; 824227064Sbz } 825227064Sbz 826227064Sbz if (ip->ip_p == IPPROTO_TCP) 827227064Sbz opcode = Q8_TX_CMD_OP_XMT_TCP_CHKSUM; 828227064Sbz else if (ip->ip_p == IPPROTO_UDP) 829227064Sbz opcode = Q8_TX_CMD_OP_XMT_UDP_CHKSUM; 830227064Sbz else { 831227064Sbz device_printf(dev, "%s: ipv4\n", __func__); 832227064Sbz offload = 0; 833227064Sbz } 834227064Sbz break; 835227064Sbz 836227064Sbz case ETHERTYPE_IPV6: 837227064Sbz ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); 838227064Sbz 839227064Sbz ip_hlen = sizeof(struct ip6_hdr); 840227064Sbz 841227064Sbz if (mp->m_len < (ehdrlen + ip_hlen)) { 842227064Sbz device_printf(dev, "%s: ipv6 mlen\n", __func__); 843227064Sbz offload = 0; 844227064Sbz break; 845227064Sbz } 846227064Sbz 847227064Sbz if (ip6->ip6_nxt == IPPROTO_TCP) 848227064Sbz opcode = Q8_TX_CMD_OP_XMT_TCP_CHKSUM_IPV6; 849227064Sbz else if (ip6->ip6_nxt == IPPROTO_UDP) 850227064Sbz opcode = Q8_TX_CMD_OP_XMT_UDP_CHKSUM_IPV6; 851227064Sbz else { 852227064Sbz device_printf(dev, "%s: ipv6\n", __func__); 853227064Sbz offload = 0; 854227064Sbz } 855227064Sbz break; 856227064Sbz 857227064Sbz default: 858227064Sbz offload = 0; 859227064Sbz break; 860227064Sbz } 861227064Sbz if (!offload) 862227064Sbz return (-1); 863227064Sbz 864227064Sbz tx_cmd->flags_opcode = opcode; 865227064Sbz 866227064Sbz tx_cmd->tcp_hdr_off = ip_hlen + ehdrlen; 867227064Sbz 868227064Sbz return (0); 869227064Sbz} 870227064Sbz 871227064Sbz/* 872227064Sbz * Name: qla_hw_send 873227064Sbz * Function: Transmits a packet. It first checks if the packet is a 874227064Sbz * candidate for Large TCP Segment Offload and then for UDP/TCP checksum 875227064Sbz * offload. If either of these creteria are not met, it is transmitted 876227064Sbz * as a regular ethernet frame. 877227064Sbz */ 878227064Sbzint 879227064Sbzqla_hw_send(qla_host_t *ha, bus_dma_segment_t *segs, int nsegs, 880227064Sbz uint32_t *tx_idx, struct mbuf *mp) 881227064Sbz{ 882227064Sbz struct ether_vlan_header *eh; 883227064Sbz qla_hw_t *hw = &ha->hw; 884227064Sbz q80_tx_cmd_t *tx_cmd, tso_cmd; 885227064Sbz bus_dma_segment_t *c_seg; 886227064Sbz uint32_t num_tx_cmds, hdr_len = 0; 887227064Sbz uint32_t total_length = 0, bytes, tx_cmd_count = 0; 888227064Sbz device_t dev; 889227064Sbz int i; 890227064Sbz 891227064Sbz dev = ha->pci_dev; 892227064Sbz 893227064Sbz /* 894227064Sbz * Always make sure there is atleast one empty slot in the tx_ring 895227064Sbz * tx_ring is considered full when there only one entry available 896227064Sbz */ 897227064Sbz num_tx_cmds = (nsegs + (Q8_TX_CMD_MAX_SEGMENTS - 1)) >> 2; 898227064Sbz 899227064Sbz total_length = mp->m_pkthdr.len; 900227064Sbz if (total_length > QLA_MAX_TSO_FRAME_SIZE) { 901227064Sbz device_printf(dev, "%s: total length exceeds maxlen(%d)\n", 902227064Sbz __func__, total_length); 903227064Sbz return (-1); 904227064Sbz } 905227064Sbz 906227064Sbz bzero((void *)&tso_cmd, sizeof(q80_tx_cmd_t)); 907227064Sbz 908227064Sbz if (qla_tx_tso(ha, mp, &tso_cmd) == 0) { 909227064Sbz /* find the additional tx_cmd descriptors required */ 910227064Sbz 911227064Sbz hdr_len = tso_cmd.total_hdr_len; 912227064Sbz 913227064Sbz bytes = sizeof(q80_tx_cmd_t) - Q8_TX_CMD_TSO_ALIGN; 914227064Sbz bytes = QL_MIN(bytes, hdr_len); 915227064Sbz 916227064Sbz num_tx_cmds++; 917227064Sbz hdr_len -= bytes; 918227064Sbz 919227064Sbz while (hdr_len) { 920227064Sbz bytes = QL_MIN((sizeof(q80_tx_cmd_t)), hdr_len); 921227064Sbz hdr_len -= bytes; 922227064Sbz num_tx_cmds++; 923227064Sbz } 924227064Sbz hdr_len = tso_cmd.total_hdr_len; 925227064Sbz } 926227064Sbz 927227064Sbz if (hw->txr_free <= (num_tx_cmds + QLA_TX_MIN_FREE)) { 928227064Sbz qla_hw_tx_done_locked(ha); 929227064Sbz if (hw->txr_free <= (num_tx_cmds + QLA_TX_MIN_FREE)) { 930227064Sbz QL_DPRINT8((dev, "%s: (hw->txr_free <= " 931227064Sbz "(num_tx_cmds + QLA_TX_MIN_FREE))\n", 932227064Sbz __func__)); 933227064Sbz return (-1); 934227064Sbz } 935227064Sbz } 936227064Sbz 937227064Sbz *tx_idx = hw->txr_next; 938227064Sbz 939227064Sbz tx_cmd = &hw->tx_ring_base[hw->txr_next]; 940227064Sbz 941227064Sbz if (hdr_len == 0) { 942227064Sbz if ((nsegs > Q8_TX_MAX_SEGMENTS) || 943227064Sbz (mp->m_pkthdr.len > ha->max_frame_size)){ 944227064Sbz device_printf(dev, 945227064Sbz "%s: (nsegs[%d, %d, 0x%x] > Q8_TX_MAX_SEGMENTS)\n", 946227064Sbz __func__, nsegs, mp->m_pkthdr.len, 947227064Sbz mp->m_pkthdr.csum_flags); 948227064Sbz qla_dump_buf8(ha, "qla_hw_send: wrong pkt", 949227064Sbz mtod(mp, char *), mp->m_len); 950227064Sbz return (EINVAL); 951227064Sbz } 952227064Sbz bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t)); 953227064Sbz if (qla_tx_chksum(ha, mp, tx_cmd) != 0) 954227064Sbz tx_cmd->flags_opcode = Q8_TX_CMD_OP_XMT_ETHER; 955227064Sbz } else { 956227064Sbz bcopy(&tso_cmd, tx_cmd, sizeof(q80_tx_cmd_t)); 957227064Sbz } 958227064Sbz 959227064Sbz eh = mtod(mp, struct ether_vlan_header *); 960227064Sbz if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) 961227064Sbz tx_cmd->flags_opcode |= Q8_TX_CMD_FLAGS_VLAN_TAGGED; 962227064Sbz else if (mp->m_flags & M_VLANTAG) { 963227064Sbz tx_cmd->flags_opcode |= (Q8_TX_CMD_FLAGS_VLAN_TAGGED | 964227064Sbz Q8_TX_CMD_FLAGS_HW_VLAN_ID); 965227064Sbz tx_cmd->vlan_tci = mp->m_pkthdr.ether_vtag; 966227064Sbz } 967227064Sbz 968227064Sbz 969227064Sbz tx_cmd->n_bufs = (uint8_t)nsegs; 970227064Sbz tx_cmd->data_len_lo = (uint8_t)(total_length & 0xFF); 971227064Sbz tx_cmd->data_len_hi = qla_host_to_le16(((uint16_t)(total_length >> 8))); 972227064Sbz tx_cmd->port_cntxtid = Q8_TX_CMD_PORT_CNXTID(ha->pci_func); 973227064Sbz 974227064Sbz c_seg = segs; 975227064Sbz 976227064Sbz while (1) { 977227064Sbz for (i = 0; ((i < Q8_TX_CMD_MAX_SEGMENTS) && nsegs); i++) { 978227064Sbz 979227064Sbz switch (i) { 980227064Sbz case 0: 981227064Sbz tx_cmd->buf1_addr = c_seg->ds_addr; 982227064Sbz tx_cmd->buf1_len = c_seg->ds_len; 983227064Sbz break; 984227064Sbz 985227064Sbz case 1: 986227064Sbz tx_cmd->buf2_addr = c_seg->ds_addr; 987227064Sbz tx_cmd->buf2_len = c_seg->ds_len; 988227064Sbz break; 989227064Sbz 990227064Sbz case 2: 991227064Sbz tx_cmd->buf3_addr = c_seg->ds_addr; 992227064Sbz tx_cmd->buf3_len = c_seg->ds_len; 993227064Sbz break; 994227064Sbz 995227064Sbz case 3: 996227064Sbz tx_cmd->buf4_addr = c_seg->ds_addr; 997227064Sbz tx_cmd->buf4_len = c_seg->ds_len; 998227064Sbz break; 999227064Sbz } 1000227064Sbz 1001227064Sbz c_seg++; 1002227064Sbz nsegs--; 1003227064Sbz } 1004227064Sbz 1005227064Sbz hw->txr_next = (hw->txr_next + 1) & (NUM_TX_DESCRIPTORS - 1); 1006227064Sbz tx_cmd_count++; 1007227064Sbz 1008227064Sbz if (!nsegs) 1009227064Sbz break; 1010227064Sbz 1011227064Sbz tx_cmd = &hw->tx_ring_base[hw->txr_next]; 1012227064Sbz bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t)); 1013227064Sbz } 1014227064Sbz 1015227064Sbz if (hdr_len) { 1016227064Sbz /* TSO : Copy the header in the following tx cmd descriptors */ 1017227064Sbz uint8_t *src, *dst; 1018227064Sbz 1019227064Sbz src = (uint8_t *)eh; 1020227064Sbz 1021227064Sbz tx_cmd = &hw->tx_ring_base[hw->txr_next]; 1022227064Sbz bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t)); 1023227064Sbz 1024227064Sbz bytes = sizeof(q80_tx_cmd_t) - Q8_TX_CMD_TSO_ALIGN; 1025227064Sbz bytes = QL_MIN(bytes, hdr_len); 1026227064Sbz 1027227064Sbz dst = (uint8_t *)tx_cmd + Q8_TX_CMD_TSO_ALIGN; 1028227064Sbz 1029227064Sbz if (mp->m_flags & M_VLANTAG) { 1030227064Sbz /* first copy the src/dst MAC addresses */ 1031227064Sbz bcopy(src, dst, (ETHER_ADDR_LEN * 2)); 1032227064Sbz dst += (ETHER_ADDR_LEN * 2); 1033227064Sbz src += (ETHER_ADDR_LEN * 2); 1034227064Sbz 1035227064Sbz hdr_len -= (ETHER_ADDR_LEN * 2); 1036227064Sbz 1037227064Sbz *((uint16_t *)dst) = htons(ETHERTYPE_VLAN); 1038227064Sbz dst += 2; 1039227064Sbz *((uint16_t *)dst) = mp->m_pkthdr.ether_vtag; 1040227064Sbz dst += 2; 1041227064Sbz 1042227064Sbz bytes -= ((ETHER_ADDR_LEN * 2) + 4); 1043227064Sbz 1044227064Sbz bcopy(src, dst, bytes); 1045227064Sbz src += bytes; 1046227064Sbz hdr_len -= bytes; 1047227064Sbz } else { 1048227064Sbz bcopy(src, dst, bytes); 1049227064Sbz src += bytes; 1050227064Sbz hdr_len -= bytes; 1051227064Sbz } 1052227064Sbz 1053227064Sbz hw->txr_next = (hw->txr_next + 1) & (NUM_TX_DESCRIPTORS - 1); 1054227064Sbz tx_cmd_count++; 1055227064Sbz 1056227064Sbz while (hdr_len) { 1057227064Sbz tx_cmd = &hw->tx_ring_base[hw->txr_next]; 1058227064Sbz bzero((void *)tx_cmd, sizeof(q80_tx_cmd_t)); 1059227064Sbz 1060227064Sbz bytes = QL_MIN((sizeof(q80_tx_cmd_t)), hdr_len); 1061227064Sbz 1062227064Sbz bcopy(src, tx_cmd, bytes); 1063227064Sbz src += bytes; 1064227064Sbz hdr_len -= bytes; 1065227064Sbz hw->txr_next = 1066227064Sbz (hw->txr_next + 1) & (NUM_TX_DESCRIPTORS - 1); 1067227064Sbz tx_cmd_count++; 1068227064Sbz } 1069227064Sbz } 1070227064Sbz 1071227064Sbz hw->txr_free = hw->txr_free - tx_cmd_count; 1072227064Sbz 1073227064Sbz QL_UPDATE_TX_PRODUCER_INDEX(ha, hw->txr_next); 1074227064Sbz QL_DPRINT8((dev, "%s: return\n", __func__)); 1075227064Sbz return (0); 1076227064Sbz} 1077227064Sbz 1078227064Sbz/* 1079227064Sbz * Name: qla_del_hw_if 1080227064Sbz * Function: Destroys the hardware specific entities corresponding to an 1081227064Sbz * Ethernet Interface 1082227064Sbz */ 1083227064Sbzvoid 1084227064Sbzqla_del_hw_if(qla_host_t *ha) 1085227064Sbz{ 1086227064Sbz int i; 1087227064Sbz 1088227064Sbz for (i = 0; i < ha->hw.num_sds_rings; i++) 1089227064Sbz QL_DISABLE_INTERRUPTS(ha, i); 1090227064Sbz 1091227064Sbz qla_del_rcv_cntxt(ha); 1092227064Sbz qla_del_xmt_cntxt(ha); 1093227064Sbz 1094227064Sbz ha->hw.flags.lro = 0; 1095227064Sbz} 1096227064Sbz 1097227064Sbz/* 1098227064Sbz * Name: qla_init_hw_if 1099227064Sbz * Function: Creates the hardware specific entities corresponding to an 1100227064Sbz * Ethernet Interface - Transmit and Receive Contexts. Sets the MAC Address 1101227064Sbz * corresponding to the interface. Enables LRO if allowed. 1102227064Sbz */ 1103227064Sbzint 1104227064Sbzqla_init_hw_if(qla_host_t *ha) 1105227064Sbz{ 1106227064Sbz device_t dev; 1107227064Sbz int i; 1108227064Sbz uint8_t bcast_mac[6]; 1109227064Sbz 1110227064Sbz qla_get_hw_caps(ha); 1111227064Sbz 1112227064Sbz dev = ha->pci_dev; 1113227064Sbz 1114227064Sbz for (i = 0; i < ha->hw.num_sds_rings; i++) { 1115227064Sbz bzero(ha->hw.dma_buf.sds_ring[i].dma_b, 1116227064Sbz ha->hw.dma_buf.sds_ring[i].size); 1117227064Sbz } 1118227064Sbz /* 1119227064Sbz * Create Receive Context 1120227064Sbz */ 1121227064Sbz if (qla_init_rcv_cntxt(ha)) { 1122227064Sbz return (-1); 1123227064Sbz } 1124227064Sbz 1125227064Sbz ha->hw.rx_next = NUM_RX_DESCRIPTORS - 2; 1126227064Sbz ha->hw.rxj_next = NUM_RX_JUMBO_DESCRIPTORS - 2; 1127227064Sbz ha->hw.rx_in = ha->hw.rxj_in = 0; 1128227064Sbz 1129227064Sbz /* Update the RDS Producer Indices */ 1130227064Sbz QL_UPDATE_RDS_PRODUCER_INDEX(ha, 0, ha->hw.rx_next); 1131227064Sbz QL_UPDATE_RDS_PRODUCER_INDEX(ha, 1, ha->hw.rxj_next); 1132227064Sbz 1133227064Sbz /* 1134227064Sbz * Create Transmit Context 1135227064Sbz */ 1136227064Sbz if (qla_init_xmt_cntxt(ha)) { 1137227064Sbz qla_del_rcv_cntxt(ha); 1138227064Sbz return (-1); 1139227064Sbz } 1140227064Sbz 1141227064Sbz qla_config_mac_addr(ha, ha->hw.mac_addr, 1142227064Sbz (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id, 1); 1143227064Sbz 1144227064Sbz bcast_mac[0] = 0xFF; bcast_mac[1] = 0xFF; bcast_mac[2] = 0xFF; 1145227064Sbz bcast_mac[3] = 0xFF; bcast_mac[4] = 0xFF; bcast_mac[5] = 0xFF; 1146227064Sbz qla_config_mac_addr(ha, bcast_mac, 1147227064Sbz (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id, 1); 1148227064Sbz 1149227064Sbz qla_config_rss(ha, (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id); 1150227064Sbz 1151227064Sbz qla_config_intr_coalesce(ha, (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id, 0); 1152227064Sbz 1153227064Sbz for (i = 0; i < ha->hw.num_sds_rings; i++) 1154227064Sbz QL_ENABLE_INTERRUPTS(ha, i); 1155227064Sbz 1156227064Sbz return (0); 1157227064Sbz} 1158227064Sbz 1159227064Sbz/* 1160227064Sbz * Name: qla_init_rcv_cntxt 1161227064Sbz * Function: Creates the Receive Context. 1162227064Sbz */ 1163227064Sbzstatic int 1164227064Sbzqla_init_rcv_cntxt(qla_host_t *ha) 1165227064Sbz{ 1166227064Sbz device_t dev; 1167227064Sbz qla_cdrp_t cdrp; 1168227064Sbz q80_rcv_cntxt_rsp_t *rsp; 1169227064Sbz q80_stat_desc_t *sdesc; 1170227064Sbz bus_addr_t phys_addr; 1171227064Sbz int i, j; 1172227064Sbz qla_hw_t *hw = &ha->hw; 1173227064Sbz 1174227064Sbz dev = ha->pci_dev; 1175227064Sbz 1176227064Sbz /* 1177227064Sbz * Create Receive Context 1178227064Sbz */ 1179227064Sbz 1180227064Sbz for (i = 0; i < hw->num_sds_rings; i++) { 1181227064Sbz sdesc = (q80_stat_desc_t *)&hw->sds[i].sds_ring_base[0]; 1182227064Sbz for (j = 0; j < NUM_STATUS_DESCRIPTORS; j++) { 1183227064Sbz sdesc->data[0] = 1184227064Sbz Q8_STAT_DESC_SET_OWNER(Q8_STAT_DESC_OWNER_FW); 1185227064Sbz } 1186227064Sbz } 1187227064Sbz 1188227064Sbz phys_addr = ha->hw.rx_cntxt_req_paddr; 1189227064Sbz 1190227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1191227064Sbz 1192227064Sbz cdrp.cmd = Q8_CMD_CREATE_RX_CNTXT; 1193227064Sbz cdrp.cmd_arg1 = (uint32_t)(phys_addr >> 32); 1194227064Sbz cdrp.cmd_arg2 = (uint32_t)(phys_addr); 1195227064Sbz cdrp.cmd_arg3 = (uint32_t)(sizeof (q80_rcv_cntxt_req_t)); 1196227064Sbz 1197227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1198227064Sbz device_printf(dev, "%s: Q8_CMD_CREATE_RX_CNTXT failed\n", 1199227064Sbz __func__); 1200227064Sbz return (-1); 1201227064Sbz } else { 1202227064Sbz rsp = ha->hw.rx_cntxt_rsp; 1203227064Sbz 1204227064Sbz QL_DPRINT2((dev, "%s: rcv cntxt successful" 1205227064Sbz " rds_ring_offset = 0x%08x" 1206227064Sbz " sds_ring_offset = 0x%08x" 1207227064Sbz " cntxt_state = 0x%08x" 1208227064Sbz " funcs_per_port = 0x%08x" 1209227064Sbz " num_rds_rings = 0x%04x" 1210227064Sbz " num_sds_rings = 0x%04x" 1211227064Sbz " cntxt_id = 0x%04x" 1212227064Sbz " phys_port = 0x%02x" 1213227064Sbz " virt_port = 0x%02x\n", 1214227064Sbz __func__, 1215227064Sbz rsp->rx_rsp.rds_ring_offset, 1216227064Sbz rsp->rx_rsp.sds_ring_offset, 1217227064Sbz rsp->rx_rsp.cntxt_state, 1218227064Sbz rsp->rx_rsp.funcs_per_port, 1219227064Sbz rsp->rx_rsp.num_rds_rings, 1220227064Sbz rsp->rx_rsp.num_sds_rings, 1221227064Sbz rsp->rx_rsp.cntxt_id, 1222227064Sbz rsp->rx_rsp.phys_port, 1223227064Sbz rsp->rx_rsp.virt_port)); 1224227064Sbz 1225227064Sbz for (i = 0; i < ha->hw.num_rds_rings; i++) { 1226227064Sbz QL_DPRINT2((dev, 1227227064Sbz "%s: rcv cntxt rds[%i].producer_reg = 0x%08x\n", 1228227064Sbz __func__, i, rsp->rds_rsp[i].producer_reg)); 1229227064Sbz } 1230227064Sbz for (i = 0; i < ha->hw.num_sds_rings; i++) { 1231227064Sbz QL_DPRINT2((dev, 1232227064Sbz "%s: rcv cntxt sds[%i].consumer_reg = 0x%08x" 1233227064Sbz " sds[%i].intr_mask_reg = 0x%08x\n", 1234227064Sbz __func__, i, rsp->sds_rsp[i].consumer_reg, 1235227064Sbz i, rsp->sds_rsp[i].intr_mask_reg)); 1236227064Sbz } 1237227064Sbz } 1238227064Sbz ha->hw.flags.init_rx_cnxt = 1; 1239227064Sbz return (0); 1240227064Sbz} 1241227064Sbz 1242227064Sbz/* 1243227064Sbz * Name: qla_del_rcv_cntxt 1244227064Sbz * Function: Destroys the Receive Context. 1245227064Sbz */ 1246227064Sbzvoid 1247227064Sbzqla_del_rcv_cntxt(qla_host_t *ha) 1248227064Sbz{ 1249227064Sbz qla_cdrp_t cdrp; 1250227064Sbz device_t dev = ha->pci_dev; 1251227064Sbz 1252227064Sbz if (!ha->hw.flags.init_rx_cnxt) 1253227064Sbz return; 1254227064Sbz 1255227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1256227064Sbz 1257227064Sbz cdrp.cmd = Q8_CMD_DESTROY_RX_CNTXT; 1258227064Sbz cdrp.cmd_arg1 = (uint32_t) (ha->hw.rx_cntxt_rsp)->rx_rsp.cntxt_id; 1259227064Sbz 1260227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1261227064Sbz device_printf(dev, "%s: Q8_CMD_DESTROY_RX_CNTXT failed\n", 1262227064Sbz __func__); 1263227064Sbz } 1264227064Sbz ha->hw.flags.init_rx_cnxt = 0; 1265227064Sbz} 1266227064Sbz 1267227064Sbz/* 1268227064Sbz * Name: qla_init_xmt_cntxt 1269227064Sbz * Function: Creates the Transmit Context. 1270227064Sbz */ 1271227064Sbzstatic int 1272227064Sbzqla_init_xmt_cntxt(qla_host_t *ha) 1273227064Sbz{ 1274227064Sbz bus_addr_t phys_addr; 1275227064Sbz device_t dev; 1276227064Sbz q80_tx_cntxt_rsp_t *tx_rsp; 1277227064Sbz qla_cdrp_t cdrp; 1278227064Sbz qla_hw_t *hw = &ha->hw; 1279227064Sbz 1280227064Sbz dev = ha->pci_dev; 1281227064Sbz 1282227064Sbz /* 1283227064Sbz * Create Transmit Context 1284227064Sbz */ 1285227064Sbz phys_addr = ha->hw.tx_cntxt_req_paddr; 1286227064Sbz tx_rsp = ha->hw.tx_cntxt_rsp; 1287227064Sbz 1288227064Sbz hw->txr_comp = hw->txr_next = 0; 1289227064Sbz *(hw->tx_cons) = 0; 1290227064Sbz 1291227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1292227064Sbz 1293227064Sbz cdrp.cmd = Q8_CMD_CREATE_TX_CNTXT; 1294227064Sbz cdrp.cmd_arg1 = (uint32_t)(phys_addr >> 32); 1295227064Sbz cdrp.cmd_arg2 = (uint32_t)(phys_addr); 1296227064Sbz cdrp.cmd_arg3 = (uint32_t)(sizeof (q80_tx_cntxt_req_t)); 1297227064Sbz 1298227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1299227064Sbz device_printf(dev, "%s: Q8_CMD_CREATE_TX_CNTXT failed\n", 1300227064Sbz __func__); 1301227064Sbz return (-1); 1302227064Sbz } else { 1303227064Sbz ha->hw.tx_prod_reg = tx_rsp->producer_reg; 1304227064Sbz 1305227064Sbz QL_DPRINT2((dev, "%s: tx cntxt successful" 1306227064Sbz " cntxt_state = 0x%08x " 1307227064Sbz " cntxt_id = 0x%04x " 1308227064Sbz " phys_port_id = 0x%02x " 1309227064Sbz " virt_port_id = 0x%02x " 1310227064Sbz " producer_reg = 0x%08x " 1311227064Sbz " intr_mask_reg = 0x%08x\n", 1312227064Sbz __func__, tx_rsp->cntxt_state, tx_rsp->cntxt_id, 1313227064Sbz tx_rsp->phys_port_id, tx_rsp->virt_port_id, 1314227064Sbz tx_rsp->producer_reg, tx_rsp->intr_mask_reg)); 1315227064Sbz } 1316227064Sbz ha->hw.txr_free = NUM_TX_DESCRIPTORS; 1317227064Sbz 1318227064Sbz ha->hw.flags.init_tx_cnxt = 1; 1319227064Sbz return (0); 1320227064Sbz} 1321227064Sbz 1322227064Sbz/* 1323227064Sbz * Name: qla_del_xmt_cntxt 1324227064Sbz * Function: Destroys the Transmit Context. 1325227064Sbz */ 1326227064Sbzstatic void 1327227064Sbzqla_del_xmt_cntxt(qla_host_t *ha) 1328227064Sbz{ 1329227064Sbz qla_cdrp_t cdrp; 1330227064Sbz device_t dev = ha->pci_dev; 1331227064Sbz 1332227064Sbz if (!ha->hw.flags.init_tx_cnxt) 1333227064Sbz return; 1334227064Sbz 1335227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1336227064Sbz 1337227064Sbz cdrp.cmd = Q8_CMD_DESTROY_TX_CNTXT; 1338227064Sbz cdrp.cmd_arg1 = (uint32_t) (ha->hw.tx_cntxt_rsp)->cntxt_id; 1339227064Sbz 1340227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1341227064Sbz device_printf(dev, "%s: Q8_CMD_DESTROY_TX_CNTXT failed\n", 1342227064Sbz __func__); 1343227064Sbz } 1344227064Sbz ha->hw.flags.init_tx_cnxt = 0; 1345227064Sbz} 1346227064Sbz 1347227064Sbz/* 1348227064Sbz * Name: qla_get_max_rds 1349227064Sbz * Function: Returns the maximum number of Receive Descriptor Rings per context. 1350227064Sbz */ 1351227064Sbzstatic int 1352227064Sbzqla_get_max_rds(qla_host_t *ha) 1353227064Sbz{ 1354227064Sbz qla_cdrp_t cdrp; 1355227064Sbz device_t dev; 1356227064Sbz 1357227064Sbz dev = ha->pci_dev; 1358227064Sbz 1359227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1360227064Sbz 1361227064Sbz cdrp.cmd = Q8_CMD_RD_MAX_RDS_PER_CNTXT; 1362227064Sbz 1363227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1364227064Sbz device_printf(dev, "%s: Q8_CMD_RD_MAX_RDS_PER_CNTXT failed\n", 1365227064Sbz __func__); 1366227064Sbz return (-1); 1367227064Sbz } else { 1368227064Sbz ha->hw.max_rds_per_cntxt = cdrp.rsp_arg1; 1369227064Sbz QL_DPRINT2((dev, "%s: max_rds_per_context 0x%08x\n", 1370227064Sbz __func__, ha->hw.max_rds_per_cntxt)); 1371227064Sbz } 1372227064Sbz return 0; 1373227064Sbz} 1374227064Sbz 1375227064Sbz/* 1376227064Sbz * Name: qla_get_max_sds 1377227064Sbz * Function: Returns the maximum number of Status Descriptor Rings per context. 1378227064Sbz */ 1379227064Sbzstatic int 1380227064Sbzqla_get_max_sds(qla_host_t *ha) 1381227064Sbz{ 1382227064Sbz qla_cdrp_t cdrp; 1383227064Sbz device_t dev; 1384227064Sbz 1385227064Sbz dev = ha->pci_dev; 1386227064Sbz 1387227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1388227064Sbz 1389227064Sbz cdrp.cmd = Q8_CMD_RD_MAX_SDS_PER_CNTXT; 1390227064Sbz 1391227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1392227064Sbz device_printf(dev, "%s: Q8_CMD_RD_MAX_RDS_PER_CNTXT failed\n", 1393227064Sbz __func__); 1394227064Sbz return (-1); 1395227064Sbz } else { 1396227064Sbz ha->hw.max_sds_per_cntxt = cdrp.rsp_arg1; 1397227064Sbz QL_DPRINT2((dev, "%s: max_sds_per_context 0x%08x\n", 1398227064Sbz __func__, ha->hw.max_sds_per_cntxt)); 1399227064Sbz } 1400227064Sbz return 0; 1401227064Sbz} 1402227064Sbz 1403227064Sbz/* 1404227064Sbz * Name: qla_get_max_rules 1405227064Sbz * Function: Returns the maximum number of Rules per context. 1406227064Sbz */ 1407227064Sbzstatic int 1408227064Sbzqla_get_max_rules(qla_host_t *ha) 1409227064Sbz{ 1410227064Sbz qla_cdrp_t cdrp; 1411227064Sbz device_t dev; 1412227064Sbz 1413227064Sbz dev = ha->pci_dev; 1414227064Sbz 1415227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1416227064Sbz 1417227064Sbz cdrp.cmd = Q8_CMD_RD_MAX_RULES_PER_CNTXT; 1418227064Sbz 1419227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1420227064Sbz device_printf(dev, "%s: Q8_CMD_RD_MAX_RULES_PER_CNTXT failed\n", 1421227064Sbz __func__); 1422227064Sbz return (-1); 1423227064Sbz } else { 1424227064Sbz ha->hw.max_rules_per_cntxt = cdrp.rsp_arg1; 1425227064Sbz QL_DPRINT2((dev, "%s: max_rules_per_cntxt 0x%08x\n", 1426227064Sbz __func__, ha->hw.max_rules_per_cntxt)); 1427227064Sbz } 1428227064Sbz return 0; 1429227064Sbz} 1430227064Sbz 1431227064Sbz/* 1432227064Sbz * Name: qla_get_max_rcv_cntxts 1433227064Sbz * Function: Returns the maximum number of Receive Contexts supported. 1434227064Sbz */ 1435227064Sbzstatic int 1436227064Sbzqla_get_max_rcv_cntxts(qla_host_t *ha) 1437227064Sbz{ 1438227064Sbz qla_cdrp_t cdrp; 1439227064Sbz device_t dev; 1440227064Sbz 1441227064Sbz dev = ha->pci_dev; 1442227064Sbz 1443227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1444227064Sbz 1445227064Sbz cdrp.cmd = Q8_CMD_RD_MAX_RX_CNTXT; 1446227064Sbz 1447227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1448227064Sbz device_printf(dev, "%s: Q8_CMD_RD_MAX_RX_CNTXT failed\n", 1449227064Sbz __func__); 1450227064Sbz return (-1); 1451227064Sbz } else { 1452227064Sbz ha->hw.max_rcv_cntxts = cdrp.rsp_arg1; 1453227064Sbz QL_DPRINT2((dev, "%s: max_rcv_cntxts 0x%08x\n", 1454227064Sbz __func__, ha->hw.max_rcv_cntxts)); 1455227064Sbz } 1456227064Sbz return 0; 1457227064Sbz} 1458227064Sbz 1459227064Sbz/* 1460227064Sbz * Name: qla_get_max_tx_cntxts 1461227064Sbz * Function: Returns the maximum number of Transmit Contexts supported. 1462227064Sbz */ 1463227064Sbzstatic int 1464227064Sbzqla_get_max_tx_cntxts(qla_host_t *ha) 1465227064Sbz{ 1466227064Sbz qla_cdrp_t cdrp; 1467227064Sbz device_t dev; 1468227064Sbz 1469227064Sbz dev = ha->pci_dev; 1470227064Sbz 1471227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1472227064Sbz 1473227064Sbz cdrp.cmd = Q8_CMD_RD_MAX_TX_CNTXT; 1474227064Sbz 1475227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1476227064Sbz device_printf(dev, "%s: Q8_CMD_RD_MAX_TX_CNTXT failed\n", 1477227064Sbz __func__); 1478227064Sbz return (-1); 1479227064Sbz } else { 1480227064Sbz ha->hw.max_xmt_cntxts = cdrp.rsp_arg1; 1481227064Sbz QL_DPRINT2((dev, "%s: max_xmt_cntxts 0x%08x\n", 1482227064Sbz __func__, ha->hw.max_xmt_cntxts)); 1483227064Sbz } 1484227064Sbz return 0; 1485227064Sbz} 1486227064Sbz 1487227064Sbz/* 1488227064Sbz * Name: qla_get_max_mtu 1489227064Sbz * Function: Returns the MTU supported for a context. 1490227064Sbz */ 1491227064Sbzstatic int 1492227064Sbzqla_get_max_mtu(qla_host_t *ha) 1493227064Sbz{ 1494227064Sbz qla_cdrp_t cdrp; 1495227064Sbz device_t dev; 1496227064Sbz 1497227064Sbz dev = ha->pci_dev; 1498227064Sbz 1499227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1500227064Sbz 1501227064Sbz cdrp.cmd = Q8_CMD_RD_MAX_MTU; 1502227064Sbz 1503227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1504227064Sbz device_printf(dev, "%s: Q8_CMD_RD_MAX_MTU failed\n", __func__); 1505227064Sbz return (-1); 1506227064Sbz } else { 1507227064Sbz ha->hw.max_mtu = cdrp.rsp_arg1; 1508227064Sbz QL_DPRINT2((dev, "%s: max_mtu 0x%08x\n", __func__, 1509227064Sbz ha->hw.max_mtu)); 1510227064Sbz } 1511227064Sbz return 0; 1512227064Sbz} 1513227064Sbz 1514227064Sbz/* 1515227064Sbz * Name: qla_set_max_mtu 1516227064Sbz * Function: 1517227064Sbz * Sets the maximum transfer unit size for the specified rcv context. 1518227064Sbz */ 1519227064Sbzint 1520227064Sbzqla_set_max_mtu(qla_host_t *ha, uint32_t mtu, uint16_t cntxt_id) 1521227064Sbz{ 1522227064Sbz qla_cdrp_t cdrp; 1523227064Sbz device_t dev; 1524227064Sbz 1525227064Sbz dev = ha->pci_dev; 1526227064Sbz 1527227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1528227064Sbz 1529227064Sbz cdrp.cmd = Q8_CMD_SET_MTU; 1530227064Sbz cdrp.cmd_arg1 = (uint32_t)cntxt_id; 1531227064Sbz cdrp.cmd_arg2 = mtu; 1532227064Sbz 1533227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1534227064Sbz device_printf(dev, "%s: Q8_CMD_RD_MAX_MTU failed\n", __func__); 1535227064Sbz return (-1); 1536227064Sbz } else { 1537227064Sbz ha->hw.max_mtu = cdrp.rsp_arg1; 1538227064Sbz } 1539227064Sbz return 0; 1540227064Sbz} 1541227064Sbz 1542227064Sbz/* 1543227064Sbz * Name: qla_get_max_lro 1544227064Sbz * Function: Returns the maximum number of TCP Connection which can be supported 1545227064Sbz * with LRO. 1546227064Sbz */ 1547227064Sbzstatic int 1548227064Sbzqla_get_max_lro(qla_host_t *ha) 1549227064Sbz{ 1550227064Sbz qla_cdrp_t cdrp; 1551227064Sbz device_t dev; 1552227064Sbz 1553227064Sbz dev = ha->pci_dev; 1554227064Sbz 1555227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1556227064Sbz 1557227064Sbz cdrp.cmd = Q8_CMD_RD_MAX_LRO; 1558227064Sbz 1559227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1560227064Sbz device_printf(dev, "%s: Q8_CMD_RD_MAX_LRO failed\n", __func__); 1561227064Sbz return (-1); 1562227064Sbz } else { 1563227064Sbz ha->hw.max_lro = cdrp.rsp_arg1; 1564227064Sbz QL_DPRINT2((dev, "%s: max_lro 0x%08x\n", __func__, 1565227064Sbz ha->hw.max_lro)); 1566227064Sbz } 1567227064Sbz return 0; 1568227064Sbz} 1569227064Sbz 1570227064Sbz/* 1571227064Sbz * Name: qla_get_flow_control 1572227064Sbz * Function: Returns the Receive/Transmit Flow Control (PAUSE) settings for 1573227064Sbz * PCI function. 1574227064Sbz */ 1575227064Sbzstatic int 1576227064Sbzqla_get_flow_control(qla_host_t *ha) 1577227064Sbz{ 1578227064Sbz qla_cdrp_t cdrp; 1579227064Sbz device_t dev; 1580227064Sbz 1581227064Sbz dev = ha->pci_dev; 1582227064Sbz 1583227064Sbz bzero(&cdrp, sizeof(qla_cdrp_t)); 1584227064Sbz 1585227064Sbz cdrp.cmd = Q8_CMD_GET_FLOW_CNTRL; 1586227064Sbz 1587227064Sbz if (qla_issue_cmd(ha, &cdrp)) { 1588227064Sbz device_printf(dev, "%s: Q8_CMD_GET_FLOW_CNTRL failed\n", 1589227064Sbz __func__); 1590227064Sbz return (-1); 1591227064Sbz } else { 1592227064Sbz QL_DPRINT2((dev, "%s: flow control 0x%08x\n", __func__, 1593227064Sbz cdrp.rsp_arg1)); 1594227064Sbz } 1595227064Sbz return 0; 1596227064Sbz} 1597227064Sbz 1598227064Sbz/* 1599227064Sbz * Name: qla_get_flow_control 1600227064Sbz * Function: Retrieves hardware capabilities 1601227064Sbz */ 1602227064Sbzvoid 1603227064Sbzqla_get_hw_caps(qla_host_t *ha) 1604227064Sbz{ 1605227064Sbz //qla_read_mac_addr(ha); 1606227064Sbz qla_get_max_rds(ha); 1607227064Sbz qla_get_max_sds(ha); 1608227064Sbz qla_get_max_rules(ha); 1609227064Sbz qla_get_max_rcv_cntxts(ha); 1610227064Sbz qla_get_max_tx_cntxts(ha); 1611227064Sbz qla_get_max_mtu(ha); 1612227064Sbz qla_get_max_lro(ha); 1613227064Sbz qla_get_flow_control(ha); 1614227064Sbz return; 1615227064Sbz} 1616227064Sbz 1617227064Sbz/* 1618227064Sbz * Name: qla_hw_set_multi 1619227064Sbz * Function: Sets the Multicast Addresses provided the host O.S into the 1620227064Sbz * hardware (for the given interface) 1621227064Sbz */ 1622227064Sbzvoid 1623227064Sbzqla_hw_set_multi(qla_host_t *ha, uint8_t *mta, uint32_t mcnt, 1624227064Sbz uint32_t add_multi) 1625227064Sbz{ 1626227064Sbz q80_rcv_cntxt_rsp_t *rsp; 1627227064Sbz int i; 1628227064Sbz 1629227064Sbz rsp = ha->hw.rx_cntxt_rsp; 1630227064Sbz for (i = 0; i < mcnt; i++) { 1631227064Sbz qla_config_mac_addr(ha, mta, rsp->rx_rsp.cntxt_id, add_multi); 1632227064Sbz mta += Q8_MAC_ADDR_LEN; 1633227064Sbz } 1634227064Sbz return; 1635227064Sbz} 1636227064Sbz 1637227064Sbz/* 1638227064Sbz * Name: qla_hw_tx_done_locked 1639227064Sbz * Function: Handle Transmit Completions 1640227064Sbz */ 1641227064Sbzstatic void 1642227064Sbzqla_hw_tx_done_locked(qla_host_t *ha) 1643227064Sbz{ 1644227064Sbz qla_tx_buf_t *txb; 1645227064Sbz qla_hw_t *hw = &ha->hw; 1646227064Sbz uint32_t comp_idx, comp_count = 0; 1647227064Sbz 1648227064Sbz /* retrieve index of last entry in tx ring completed */ 1649227064Sbz comp_idx = qla_le32_to_host(*(hw->tx_cons)); 1650227064Sbz 1651227064Sbz while (comp_idx != hw->txr_comp) { 1652227064Sbz 1653227064Sbz txb = &ha->tx_buf[hw->txr_comp]; 1654227064Sbz 1655227064Sbz hw->txr_comp++; 1656227064Sbz if (hw->txr_comp == NUM_TX_DESCRIPTORS) 1657227064Sbz hw->txr_comp = 0; 1658227064Sbz 1659227064Sbz comp_count++; 1660227064Sbz 1661227064Sbz if (txb->m_head) { 1662227064Sbz bus_dmamap_sync(ha->tx_tag, txb->map, 1663227064Sbz BUS_DMASYNC_POSTWRITE); 1664227064Sbz bus_dmamap_unload(ha->tx_tag, txb->map); 1665227064Sbz bus_dmamap_destroy(ha->tx_tag, txb->map); 1666227064Sbz m_freem(txb->m_head); 1667227064Sbz 1668227064Sbz txb->map = (bus_dmamap_t)0; 1669227064Sbz txb->m_head = NULL; 1670227064Sbz } 1671227064Sbz } 1672227064Sbz 1673227064Sbz hw->txr_free += comp_count; 1674227064Sbz 1675227064Sbz QL_DPRINT8((ha->pci_dev, "%s: return [c,f, p, pn][%d, %d, %d, %d]\n", __func__, 1676227064Sbz hw->txr_comp, hw->txr_free, hw->txr_next, READ_REG32(ha, (ha->hw.tx_prod_reg + 0x1b2000)))); 1677227064Sbz 1678227064Sbz return; 1679227064Sbz} 1680227064Sbz 1681227064Sbz/* 1682227064Sbz * Name: qla_hw_tx_done 1683227064Sbz * Function: Handle Transmit Completions 1684227064Sbz */ 1685227064Sbzvoid 1686227064Sbzqla_hw_tx_done(qla_host_t *ha) 1687227064Sbz{ 1688227064Sbz if (!mtx_trylock(&ha->tx_lock)) { 1689227064Sbz QL_DPRINT8((ha->pci_dev, 1690227064Sbz "%s: !mtx_trylock(&ha->tx_lock)\n", __func__)); 1691227064Sbz return; 1692227064Sbz } 1693227064Sbz qla_hw_tx_done_locked(ha); 1694227064Sbz 1695227064Sbz if (ha->hw.txr_free > free_pkt_thres) 1696227064Sbz ha->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1697227064Sbz 1698227064Sbz mtx_unlock(&ha->tx_lock); 1699227064Sbz return; 1700227064Sbz} 1701227064Sbz 1702227064Sbzvoid 1703227064Sbzqla_update_link_state(qla_host_t *ha) 1704227064Sbz{ 1705227064Sbz uint32_t link_state; 1706227064Sbz 1707227064Sbz if (!(ha->ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1708227064Sbz ha->hw.flags.link_up = 0; 1709227064Sbz return; 1710227064Sbz } 1711227064Sbz link_state = READ_REG32(ha, Q8_LINK_STATE); 1712227064Sbz 1713227064Sbz if (ha->pci_func == 0) 1714227064Sbz ha->hw.flags.link_up = (((link_state & 0xF) == 1)? 1 : 0); 1715227064Sbz else 1716227064Sbz ha->hw.flags.link_up = ((((link_state >> 4)& 0xF) == 1)? 1 : 0); 1717227064Sbz} 1718227064Sbz 1719227064Sbzint 1720227064Sbzqla_config_lro(qla_host_t *ha) 1721227064Sbz{ 1722227064Sbz int i; 1723227064Sbz qla_hw_t *hw = &ha->hw; 1724227064Sbz struct lro_ctrl *lro; 1725227064Sbz 1726227064Sbz for (i = 0; i < hw->num_sds_rings; i++) { 1727227064Sbz lro = &hw->sds[i].lro; 1728227064Sbz if (tcp_lro_init(lro)) { 1729227064Sbz device_printf(ha->pci_dev, "%s: tcp_lro_init failed\n", 1730227064Sbz __func__); 1731227064Sbz return (-1); 1732227064Sbz } 1733227064Sbz lro->ifp = ha->ifp; 1734227064Sbz } 1735227064Sbz ha->flags.lro_init = 1; 1736227064Sbz 1737227064Sbz QL_DPRINT2((ha->pci_dev, "%s: LRO initialized\n", __func__)); 1738227064Sbz return (0); 1739227064Sbz} 1740227064Sbz 1741227064Sbzvoid 1742227064Sbzqla_free_lro(qla_host_t *ha) 1743227064Sbz{ 1744227064Sbz int i; 1745227064Sbz qla_hw_t *hw = &ha->hw; 1746227064Sbz struct lro_ctrl *lro; 1747227064Sbz 1748227064Sbz if (!ha->flags.lro_init) 1749227064Sbz return; 1750227064Sbz 1751227064Sbz for (i = 0; i < hw->num_sds_rings; i++) { 1752227064Sbz lro = &hw->sds[i].lro; 1753227064Sbz tcp_lro_free(lro); 1754227064Sbz } 1755227064Sbz ha->flags.lro_init = 0; 1756227064Sbz} 1757227064Sbz 1758227064Sbzvoid 1759227064Sbzqla_hw_stop_rcv(qla_host_t *ha) 1760227064Sbz{ 1761227064Sbz int i, done, count = 100; 1762227064Sbz 1763227064Sbz while (count--) { 1764227064Sbz done = 1; 1765227064Sbz for (i = 0; i < ha->hw.num_sds_rings; i++) { 1766227064Sbz if (ha->hw.sds[i].rcv_active) 1767227064Sbz done = 0; 1768227064Sbz } 1769227064Sbz if (done) 1770227064Sbz break; 1771227064Sbz else 1772227064Sbz qla_mdelay(__func__, 10); 1773227064Sbz } 1774227064Sbz} 1775227064Sbz 1776