1252206Sdavidcs/* 2252206Sdavidcs * Copyright (c) 2013-2014 Qlogic Corporation 3252206Sdavidcs * All rights reserved. 4252206Sdavidcs * 5252206Sdavidcs * Redistribution and use in source and binary forms, with or without 6252206Sdavidcs * modification, are permitted provided that the following conditions 7252206Sdavidcs * are met: 8252206Sdavidcs * 9252206Sdavidcs * 1. Redistributions of source code must retain the above copyright 10252206Sdavidcs * notice, this list of conditions and the following disclaimer. 11252206Sdavidcs * 2. Redistributions in binary form must reproduce the above copyright 12252206Sdavidcs * notice, this list of conditions and the following disclaimer in the 13252206Sdavidcs * documentation and/or other materials provided with the distribution. 14252206Sdavidcs * 15252206Sdavidcs * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16252206Sdavidcs * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17252206Sdavidcs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18252206Sdavidcs * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19252206Sdavidcs * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20252206Sdavidcs * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21252206Sdavidcs * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22252206Sdavidcs * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23252206Sdavidcs * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24252206Sdavidcs * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25252206Sdavidcs * POSSIBILITY OF SUCH DAMAGE. 26252206Sdavidcs */ 27252206Sdavidcs 28252206Sdavidcs/* 29252206Sdavidcs * File: qls_hw.c 30252206Sdavidcs * Author : David C Somayajulu, Qlogic Corporation, Aliso Viejo, CA 92656. 31252206Sdavidcs * Content: Contains Hardware dependant functions 32252206Sdavidcs */ 33252206Sdavidcs#include <sys/cdefs.h> 34252206Sdavidcs__FBSDID("$FreeBSD$"); 35252206Sdavidcs 36252206Sdavidcs 37252206Sdavidcs 38252206Sdavidcs#include "qls_os.h" 39252206Sdavidcs#include "qls_hw.h" 40252206Sdavidcs#include "qls_def.h" 41252206Sdavidcs#include "qls_inline.h" 42252206Sdavidcs#include "qls_ver.h" 43252206Sdavidcs#include "qls_glbl.h" 44252206Sdavidcs#include "qls_dbg.h" 45252206Sdavidcs 46252206Sdavidcs/* 47252206Sdavidcs * Static Functions 48252206Sdavidcs */ 49252206Sdavidcsstatic int qls_wait_for_mac_proto_idx_ready(qla_host_t *ha, uint32_t op); 50252206Sdavidcsstatic int qls_config_unicast_mac_addr(qla_host_t *ha, uint32_t add_mac); 51252206Sdavidcsstatic int qls_config_mcast_mac_addr(qla_host_t *ha, uint8_t *mac_addr, 52252206Sdavidcs uint32_t add_mac, uint32_t index); 53252206Sdavidcs 54252206Sdavidcsstatic int qls_init_rss(qla_host_t *ha); 55252206Sdavidcsstatic int qls_init_comp_queue(qla_host_t *ha, int cid); 56252206Sdavidcsstatic int qls_init_work_queue(qla_host_t *ha, int wid); 57252206Sdavidcsstatic int qls_init_fw_routing_table(qla_host_t *ha); 58252206Sdavidcsstatic int qls_hw_add_all_mcast(qla_host_t *ha); 59252206Sdavidcsstatic int qls_hw_add_mcast(qla_host_t *ha, uint8_t *mta); 60252206Sdavidcsstatic int qls_hw_del_mcast(qla_host_t *ha, uint8_t *mta); 61252206Sdavidcsstatic int qls_wait_for_flash_ready(qla_host_t *ha); 62252206Sdavidcs 63252206Sdavidcsstatic int qls_sem_lock(qla_host_t *ha, uint32_t mask, uint32_t value); 64252206Sdavidcsstatic void qls_sem_unlock(qla_host_t *ha, uint32_t mask); 65252206Sdavidcs 66252206Sdavidcsstatic void qls_free_tx_dma(qla_host_t *ha); 67252206Sdavidcsstatic int qls_alloc_tx_dma(qla_host_t *ha); 68252206Sdavidcsstatic void qls_free_rx_dma(qla_host_t *ha); 69252206Sdavidcsstatic int qls_alloc_rx_dma(qla_host_t *ha); 70252206Sdavidcsstatic void qls_free_mpi_dma(qla_host_t *ha); 71252206Sdavidcsstatic int qls_alloc_mpi_dma(qla_host_t *ha); 72252206Sdavidcsstatic void qls_free_rss_dma(qla_host_t *ha); 73252206Sdavidcsstatic int qls_alloc_rss_dma(qla_host_t *ha); 74252206Sdavidcs 75252206Sdavidcsstatic int qls_flash_validate(qla_host_t *ha, const char *signature); 76252206Sdavidcs 77252206Sdavidcs 78252206Sdavidcsstatic int qls_wait_for_proc_addr_ready(qla_host_t *ha); 79252206Sdavidcsstatic int qls_proc_addr_rd_reg(qla_host_t *ha, uint32_t addr_module, 80252206Sdavidcs uint32_t reg, uint32_t *data); 81252206Sdavidcsstatic int qls_proc_addr_wr_reg(qla_host_t *ha, uint32_t addr_module, 82252206Sdavidcs uint32_t reg, uint32_t data); 83252206Sdavidcs 84252206Sdavidcsstatic int qls_hw_reset(qla_host_t *ha); 85252206Sdavidcs 86252206Sdavidcs/* 87252206Sdavidcs * MPI Related Functions 88252206Sdavidcs */ 89252206Sdavidcsstatic int qls_mbx_cmd(qla_host_t *ha, uint32_t *in_mbx, uint32_t i_count, 90252206Sdavidcs uint32_t *out_mbx, uint32_t o_count); 91252206Sdavidcsstatic int qls_mbx_set_mgmt_ctrl(qla_host_t *ha, uint32_t t_ctrl); 92252206Sdavidcsstatic int qls_mbx_get_mgmt_ctrl(qla_host_t *ha, uint32_t *t_status); 93252206Sdavidcsstatic void qls_mbx_get_link_status(qla_host_t *ha); 94252206Sdavidcsstatic void qls_mbx_about_fw(qla_host_t *ha); 95252206Sdavidcs 96252206Sdavidcsint 97252206Sdavidcsqls_get_msix_count(qla_host_t *ha) 98252206Sdavidcs{ 99252206Sdavidcs return (ha->num_rx_rings); 100252206Sdavidcs} 101252206Sdavidcs 102252206Sdavidcsstatic int 103252206Sdavidcsqls_syctl_mpi_dump(SYSCTL_HANDLER_ARGS) 104252206Sdavidcs{ 105252206Sdavidcs int err = 0, ret; 106252206Sdavidcs qla_host_t *ha; 107252206Sdavidcs 108252206Sdavidcs err = sysctl_handle_int(oidp, &ret, 0, req); 109252206Sdavidcs 110252206Sdavidcs if (err || !req->newptr) 111252206Sdavidcs return (err); 112252206Sdavidcs 113252206Sdavidcs 114252206Sdavidcs if (ret == 1) { 115252206Sdavidcs ha = (qla_host_t *)arg1; 116252206Sdavidcs qls_mpi_core_dump(ha); 117252206Sdavidcs } 118252206Sdavidcs return (err); 119252206Sdavidcs} 120252206Sdavidcs 121252206Sdavidcsstatic int 122252206Sdavidcsqls_syctl_link_status(SYSCTL_HANDLER_ARGS) 123252206Sdavidcs{ 124252206Sdavidcs int err = 0, ret; 125252206Sdavidcs qla_host_t *ha; 126252206Sdavidcs 127252206Sdavidcs err = sysctl_handle_int(oidp, &ret, 0, req); 128252206Sdavidcs 129252206Sdavidcs if (err || !req->newptr) 130252206Sdavidcs return (err); 131252206Sdavidcs 132252206Sdavidcs 133252206Sdavidcs if (ret == 1) { 134252206Sdavidcs ha = (qla_host_t *)arg1; 135252206Sdavidcs qls_mbx_get_link_status(ha); 136252206Sdavidcs qls_mbx_about_fw(ha); 137252206Sdavidcs } 138252206Sdavidcs return (err); 139252206Sdavidcs} 140252206Sdavidcs 141252206Sdavidcsvoid 142252206Sdavidcsqls_hw_add_sysctls(qla_host_t *ha) 143252206Sdavidcs{ 144252206Sdavidcs device_t dev; 145252206Sdavidcs 146252206Sdavidcs dev = ha->pci_dev; 147252206Sdavidcs 148252206Sdavidcs ha->num_rx_rings = MAX_RX_RINGS; ha->num_tx_rings = MAX_TX_RINGS; 149252206Sdavidcs 150252206Sdavidcs SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 151252206Sdavidcs SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 152252206Sdavidcs OID_AUTO, "num_rx_rings", CTLFLAG_RD, &ha->num_rx_rings, 153252206Sdavidcs ha->num_rx_rings, "Number of Completion Queues"); 154252206Sdavidcs 155252206Sdavidcs SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 156252206Sdavidcs SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 157252206Sdavidcs OID_AUTO, "num_tx_rings", CTLFLAG_RD, &ha->num_tx_rings, 158252206Sdavidcs ha->num_tx_rings, "Number of Transmit Rings"); 159252206Sdavidcs 160252206Sdavidcs SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 161252206Sdavidcs SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 162252206Sdavidcs OID_AUTO, "mpi_dump", CTLTYPE_INT | CTLFLAG_RW, 163252206Sdavidcs (void *)ha, 0, 164252206Sdavidcs qls_syctl_mpi_dump, "I", "MPI Dump"); 165252206Sdavidcs 166252206Sdavidcs SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 167252206Sdavidcs SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 168252206Sdavidcs OID_AUTO, "link_status", CTLTYPE_INT | CTLFLAG_RW, 169252206Sdavidcs (void *)ha, 0, 170252206Sdavidcs qls_syctl_link_status, "I", "Link Status"); 171252206Sdavidcs} 172252206Sdavidcs 173252206Sdavidcs/* 174252206Sdavidcs * Name: qls_free_dma 175252206Sdavidcs * Function: Frees the DMA'able memory allocated in qls_alloc_dma() 176252206Sdavidcs */ 177252206Sdavidcsvoid 178252206Sdavidcsqls_free_dma(qla_host_t *ha) 179252206Sdavidcs{ 180252206Sdavidcs qls_free_rss_dma(ha); 181252206Sdavidcs qls_free_mpi_dma(ha); 182252206Sdavidcs qls_free_tx_dma(ha); 183252206Sdavidcs qls_free_rx_dma(ha); 184252206Sdavidcs return; 185252206Sdavidcs} 186252206Sdavidcs 187252206Sdavidcs/* 188252206Sdavidcs * Name: qls_alloc_dma 189252206Sdavidcs * Function: Allocates DMA'able memory for Tx/Rx Rings, Tx/Rx Contexts. 190252206Sdavidcs */ 191252206Sdavidcsint 192252206Sdavidcsqls_alloc_dma(qla_host_t *ha) 193252206Sdavidcs{ 194252206Sdavidcs if (qls_alloc_rx_dma(ha)) 195252206Sdavidcs return (-1); 196252206Sdavidcs 197252206Sdavidcs if (qls_alloc_tx_dma(ha)) { 198252206Sdavidcs qls_free_rx_dma(ha); 199252206Sdavidcs return (-1); 200252206Sdavidcs } 201252206Sdavidcs 202252206Sdavidcs if (qls_alloc_mpi_dma(ha)) { 203252206Sdavidcs qls_free_tx_dma(ha); 204252206Sdavidcs qls_free_rx_dma(ha); 205252206Sdavidcs return (-1); 206252206Sdavidcs } 207252206Sdavidcs 208252206Sdavidcs if (qls_alloc_rss_dma(ha)) { 209252206Sdavidcs qls_free_mpi_dma(ha); 210252206Sdavidcs qls_free_tx_dma(ha); 211252206Sdavidcs qls_free_rx_dma(ha); 212252206Sdavidcs return (-1); 213252206Sdavidcs } 214252206Sdavidcs 215252206Sdavidcs return (0); 216252206Sdavidcs} 217252206Sdavidcs 218252206Sdavidcs 219252206Sdavidcsstatic int 220252206Sdavidcsqls_wait_for_mac_proto_idx_ready(qla_host_t *ha, uint32_t op) 221252206Sdavidcs{ 222252206Sdavidcs uint32_t data32; 223252206Sdavidcs uint32_t count = 3; 224252206Sdavidcs 225252206Sdavidcs while (count--) { 226252206Sdavidcs data32 = READ_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX); 227252206Sdavidcs 228252206Sdavidcs if (data32 & op) 229252206Sdavidcs return (0); 230252206Sdavidcs 231252206Sdavidcs QLA_USEC_DELAY(100); 232252206Sdavidcs } 233252206Sdavidcs ha->qla_initiate_recovery = 1; 234252206Sdavidcs return (-1); 235252206Sdavidcs} 236252206Sdavidcs 237252206Sdavidcs/* 238252206Sdavidcs * Name: qls_config_unicast_mac_addr 239252206Sdavidcs * Function: binds/unbinds a unicast MAC address to the interface. 240252206Sdavidcs */ 241252206Sdavidcsstatic int 242252206Sdavidcsqls_config_unicast_mac_addr(qla_host_t *ha, uint32_t add_mac) 243252206Sdavidcs{ 244252206Sdavidcs int ret = 0; 245252206Sdavidcs uint32_t mac_upper = 0; 246252206Sdavidcs uint32_t mac_lower = 0; 247252206Sdavidcs uint32_t value = 0, index; 248252206Sdavidcs 249252206Sdavidcs if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_MAC_SERDES, 250252206Sdavidcs Q81_CTL_SEM_SET_MAC_SERDES)) { 251252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__)); 252252206Sdavidcs return(-1); 253252206Sdavidcs } 254252206Sdavidcs 255252206Sdavidcs if (add_mac) { 256252206Sdavidcs mac_upper = (ha->mac_addr[0] << 8) | ha->mac_addr[1]; 257252206Sdavidcs mac_lower = (ha->mac_addr[2] << 24) | (ha->mac_addr[3] << 16) | 258252206Sdavidcs (ha->mac_addr[4] << 8) | ha->mac_addr[5]; 259252206Sdavidcs } 260252206Sdavidcs ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW); 261252206Sdavidcs if (ret) 262252206Sdavidcs goto qls_config_unicast_mac_addr_exit; 263252206Sdavidcs 264252206Sdavidcs index = 128 * (ha->pci_func & 0x1); /* index */ 265252206Sdavidcs 266252206Sdavidcs value = (index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) | 267252206Sdavidcs Q81_CTL_MAC_PROTO_AI_TYPE_CAM_MAC; 268252206Sdavidcs 269252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value); 270252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, mac_lower); 271252206Sdavidcs 272252206Sdavidcs ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW); 273252206Sdavidcs if (ret) 274252206Sdavidcs goto qls_config_unicast_mac_addr_exit; 275252206Sdavidcs 276252206Sdavidcs value = (index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) | 277252206Sdavidcs Q81_CTL_MAC_PROTO_AI_TYPE_CAM_MAC | 0x1; 278252206Sdavidcs 279252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value); 280252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, mac_upper); 281252206Sdavidcs 282252206Sdavidcs ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW); 283252206Sdavidcs if (ret) 284252206Sdavidcs goto qls_config_unicast_mac_addr_exit; 285252206Sdavidcs 286252206Sdavidcs value = (index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) | 287252206Sdavidcs Q81_CTL_MAC_PROTO_AI_TYPE_CAM_MAC | 0x2; 288252206Sdavidcs 289252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value); 290252206Sdavidcs 291252206Sdavidcs value = Q81_CAM_MAC_OFF2_ROUTE_NIC | 292252206Sdavidcs ((ha->pci_func & 0x1) << Q81_CAM_MAC_OFF2_FUNC_SHIFT) | 293252206Sdavidcs (0 << Q81_CAM_MAC_OFF2_CQID_SHIFT); 294252206Sdavidcs 295252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, value); 296252206Sdavidcs 297252206Sdavidcsqls_config_unicast_mac_addr_exit: 298252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_MAC_SERDES); 299252206Sdavidcs return (ret); 300252206Sdavidcs} 301252206Sdavidcs 302252206Sdavidcs/* 303252206Sdavidcs * Name: qls_config_mcast_mac_addr 304252206Sdavidcs * Function: binds/unbinds a multicast MAC address to the interface. 305252206Sdavidcs */ 306252206Sdavidcsstatic int 307252206Sdavidcsqls_config_mcast_mac_addr(qla_host_t *ha, uint8_t *mac_addr, uint32_t add_mac, 308252206Sdavidcs uint32_t index) 309252206Sdavidcs{ 310252206Sdavidcs int ret = 0; 311252206Sdavidcs uint32_t mac_upper = 0; 312252206Sdavidcs uint32_t mac_lower = 0; 313252206Sdavidcs uint32_t value = 0; 314252206Sdavidcs 315252206Sdavidcs if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_MAC_SERDES, 316252206Sdavidcs Q81_CTL_SEM_SET_MAC_SERDES)) { 317252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__)); 318252206Sdavidcs return(-1); 319252206Sdavidcs } 320252206Sdavidcs 321252206Sdavidcs if (add_mac) { 322252206Sdavidcs mac_upper = (mac_addr[0] << 8) | mac_addr[1]; 323252206Sdavidcs mac_lower = (mac_addr[2] << 24) | (mac_addr[3] << 16) | 324252206Sdavidcs (mac_addr[4] << 8) | mac_addr[5]; 325252206Sdavidcs } 326252206Sdavidcs ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW); 327252206Sdavidcs if (ret) 328252206Sdavidcs goto qls_config_mcast_mac_addr_exit; 329252206Sdavidcs 330252206Sdavidcs value = Q81_CTL_MAC_PROTO_AI_E | 331252206Sdavidcs (index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) | 332252206Sdavidcs Q81_CTL_MAC_PROTO_AI_TYPE_MCAST ; 333252206Sdavidcs 334252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value); 335252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, mac_lower); 336252206Sdavidcs 337252206Sdavidcs ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW); 338252206Sdavidcs if (ret) 339252206Sdavidcs goto qls_config_mcast_mac_addr_exit; 340252206Sdavidcs 341252206Sdavidcs value = Q81_CTL_MAC_PROTO_AI_E | 342252206Sdavidcs (index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) | 343252206Sdavidcs Q81_CTL_MAC_PROTO_AI_TYPE_MCAST | 0x1; 344252206Sdavidcs 345252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value); 346252206Sdavidcs WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, mac_upper); 347252206Sdavidcs 348252206Sdavidcsqls_config_mcast_mac_addr_exit: 349252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_MAC_SERDES); 350252206Sdavidcs 351252206Sdavidcs return (ret); 352252206Sdavidcs} 353252206Sdavidcs 354252206Sdavidcs/* 355252206Sdavidcs * Name: qls_set_mac_rcv_mode 356252206Sdavidcs * Function: Enable/Disable AllMulticast and Promiscous Modes. 357252206Sdavidcs */ 358252206Sdavidcsstatic int 359252206Sdavidcsqls_wait_for_route_idx_ready(qla_host_t *ha, uint32_t op) 360252206Sdavidcs{ 361252206Sdavidcs uint32_t data32; 362252206Sdavidcs uint32_t count = 3; 363252206Sdavidcs 364252206Sdavidcs while (count--) { 365252206Sdavidcs data32 = READ_REG32(ha, Q81_CTL_ROUTING_INDEX); 366252206Sdavidcs 367252206Sdavidcs if (data32 & op) 368252206Sdavidcs return (0); 369252206Sdavidcs 370252206Sdavidcs QLA_USEC_DELAY(100); 371252206Sdavidcs } 372252206Sdavidcs ha->qla_initiate_recovery = 1; 373252206Sdavidcs return (-1); 374252206Sdavidcs} 375252206Sdavidcs 376252206Sdavidcsstatic int 377252206Sdavidcsqls_load_route_idx_reg(qla_host_t *ha, uint32_t index, uint32_t data) 378252206Sdavidcs{ 379252206Sdavidcs int ret = 0; 380252206Sdavidcs 381252206Sdavidcs ret = qls_wait_for_route_idx_ready(ha, Q81_CTL_RI_MW); 382252206Sdavidcs 383252206Sdavidcs if (ret) { 384252206Sdavidcs device_printf(ha->pci_dev, "%s: [0x%08x, 0x%08x] failed\n", 385252206Sdavidcs __func__, index, data); 386252206Sdavidcs goto qls_load_route_idx_reg_exit; 387252206Sdavidcs } 388252206Sdavidcs 389252206Sdavidcs 390252206Sdavidcs WRITE_REG32(ha, Q81_CTL_ROUTING_INDEX, index); 391252206Sdavidcs WRITE_REG32(ha, Q81_CTL_ROUTING_DATA, data); 392252206Sdavidcs 393252206Sdavidcsqls_load_route_idx_reg_exit: 394252206Sdavidcs return (ret); 395252206Sdavidcs} 396252206Sdavidcs 397252206Sdavidcsstatic int 398252206Sdavidcsqls_load_route_idx_reg_locked(qla_host_t *ha, uint32_t index, uint32_t data) 399252206Sdavidcs{ 400252206Sdavidcs int ret = 0; 401252206Sdavidcs 402252206Sdavidcs if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG, 403252206Sdavidcs Q81_CTL_SEM_SET_RIDX_DATAREG)) { 404252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__)); 405252206Sdavidcs return(-1); 406252206Sdavidcs } 407252206Sdavidcs 408252206Sdavidcs ret = qls_load_route_idx_reg(ha, index, data); 409252206Sdavidcs 410252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG); 411252206Sdavidcs 412252206Sdavidcs return (ret); 413252206Sdavidcs} 414252206Sdavidcs 415252206Sdavidcsstatic int 416252206Sdavidcsqls_clear_routing_table(qla_host_t *ha) 417252206Sdavidcs{ 418252206Sdavidcs int i, ret = 0; 419252206Sdavidcs 420252206Sdavidcs if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG, 421252206Sdavidcs Q81_CTL_SEM_SET_RIDX_DATAREG)) { 422252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__)); 423252206Sdavidcs return(-1); 424252206Sdavidcs } 425252206Sdavidcs 426252206Sdavidcs for (i = 0; i < 16; i++) { 427252206Sdavidcs ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_TYPE_NICQMASK| 428252206Sdavidcs (i << 8) | Q81_CTL_RI_DST_DFLTQ), 0); 429252206Sdavidcs if (ret) 430252206Sdavidcs break; 431252206Sdavidcs } 432252206Sdavidcs 433252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG); 434252206Sdavidcs 435252206Sdavidcs return (ret); 436252206Sdavidcs} 437252206Sdavidcs 438252206Sdavidcsint 439252206Sdavidcsqls_set_promisc(qla_host_t *ha) 440252206Sdavidcs{ 441252206Sdavidcs int ret; 442252206Sdavidcs 443252206Sdavidcs ret = qls_load_route_idx_reg_locked(ha, 444252206Sdavidcs (Q81_CTL_RI_E | Q81_CTL_RI_TYPE_NICQMASK | 445252206Sdavidcs Q81_CTL_RI_IDX_PROMISCUOUS | Q81_CTL_RI_DST_DFLTQ), 446252206Sdavidcs Q81_CTL_RD_VALID_PKT); 447252206Sdavidcs return (ret); 448252206Sdavidcs} 449252206Sdavidcs 450252206Sdavidcsvoid 451252206Sdavidcsqls_reset_promisc(qla_host_t *ha) 452252206Sdavidcs{ 453252206Sdavidcs int ret; 454252206Sdavidcs 455252206Sdavidcs ret = qls_load_route_idx_reg_locked(ha, (Q81_CTL_RI_TYPE_NICQMASK | 456252206Sdavidcs Q81_CTL_RI_IDX_PROMISCUOUS | Q81_CTL_RI_DST_DFLTQ), 0); 457252206Sdavidcs return; 458252206Sdavidcs} 459252206Sdavidcs 460252206Sdavidcsint 461252206Sdavidcsqls_set_allmulti(qla_host_t *ha) 462252206Sdavidcs{ 463252206Sdavidcs int ret; 464252206Sdavidcs 465252206Sdavidcs ret = qls_load_route_idx_reg_locked(ha, 466252206Sdavidcs (Q81_CTL_RI_E | Q81_CTL_RI_TYPE_NICQMASK | 467252206Sdavidcs Q81_CTL_RI_IDX_ALLMULTI | Q81_CTL_RI_DST_DFLTQ), 468252206Sdavidcs Q81_CTL_RD_MCAST); 469252206Sdavidcs return (ret); 470252206Sdavidcs} 471252206Sdavidcs 472252206Sdavidcsvoid 473252206Sdavidcsqls_reset_allmulti(qla_host_t *ha) 474252206Sdavidcs{ 475252206Sdavidcs int ret; 476252206Sdavidcs 477252206Sdavidcs ret = qls_load_route_idx_reg_locked(ha, (Q81_CTL_RI_TYPE_NICQMASK | 478252206Sdavidcs Q81_CTL_RI_IDX_ALLMULTI | Q81_CTL_RI_DST_DFLTQ), 0); 479252206Sdavidcs return; 480252206Sdavidcs} 481252206Sdavidcs 482252206Sdavidcs 483252206Sdavidcsstatic int 484252206Sdavidcsqls_init_fw_routing_table(qla_host_t *ha) 485252206Sdavidcs{ 486252206Sdavidcs int ret = 0; 487252206Sdavidcs 488252206Sdavidcs ret = qls_clear_routing_table(ha); 489252206Sdavidcs if (ret) 490252206Sdavidcs return (-1); 491252206Sdavidcs 492252206Sdavidcs if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG, 493252206Sdavidcs Q81_CTL_SEM_SET_RIDX_DATAREG)) { 494252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__)); 495252206Sdavidcs return(-1); 496252206Sdavidcs } 497252206Sdavidcs 498252206Sdavidcs ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_E | Q81_CTL_RI_DST_DROP | 499252206Sdavidcs Q81_CTL_RI_TYPE_NICQMASK | Q81_CTL_RI_IDX_ALL_ERROR), 500252206Sdavidcs Q81_CTL_RD_ERROR_PKT); 501252206Sdavidcs if (ret) 502252206Sdavidcs goto qls_init_fw_routing_table_exit; 503252206Sdavidcs 504252206Sdavidcs ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_E | Q81_CTL_RI_DST_DFLTQ | 505252206Sdavidcs Q81_CTL_RI_TYPE_NICQMASK | Q81_CTL_RI_IDX_BCAST), 506252206Sdavidcs Q81_CTL_RD_BCAST); 507252206Sdavidcs if (ret) 508252206Sdavidcs goto qls_init_fw_routing_table_exit; 509252206Sdavidcs 510252206Sdavidcs if (ha->num_rx_rings > 1 ) { 511252206Sdavidcs ret = qls_load_route_idx_reg(ha, 512252206Sdavidcs (Q81_CTL_RI_E | Q81_CTL_RI_DST_RSS | 513252206Sdavidcs Q81_CTL_RI_TYPE_NICQMASK | 514252206Sdavidcs Q81_CTL_RI_IDX_RSS_MATCH), 515252206Sdavidcs Q81_CTL_RD_RSS_MATCH); 516252206Sdavidcs if (ret) 517252206Sdavidcs goto qls_init_fw_routing_table_exit; 518252206Sdavidcs } 519252206Sdavidcs 520252206Sdavidcs ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_E | Q81_CTL_RI_DST_DFLTQ | 521252206Sdavidcs Q81_CTL_RI_TYPE_NICQMASK | Q81_CTL_RI_IDX_MCAST_MATCH), 522252206Sdavidcs Q81_CTL_RD_MCAST_REG_MATCH); 523252206Sdavidcs if (ret) 524252206Sdavidcs goto qls_init_fw_routing_table_exit; 525252206Sdavidcs 526252206Sdavidcs ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_E | Q81_CTL_RI_DST_DFLTQ | 527252206Sdavidcs Q81_CTL_RI_TYPE_NICQMASK | Q81_CTL_RI_IDX_CAM_HIT), 528252206Sdavidcs Q81_CTL_RD_CAM_HIT); 529252206Sdavidcs if (ret) 530252206Sdavidcs goto qls_init_fw_routing_table_exit; 531252206Sdavidcs 532252206Sdavidcsqls_init_fw_routing_table_exit: 533252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG); 534252206Sdavidcs return (ret); 535252206Sdavidcs} 536252206Sdavidcs 537252206Sdavidcsstatic int 538252206Sdavidcsqls_tx_tso_chksum(qla_host_t *ha, struct mbuf *mp, q81_tx_tso_t *tx_mac) 539252206Sdavidcs{ 540252206Sdavidcs struct ether_vlan_header *eh; 541252206Sdavidcs struct ip *ip; 542252206Sdavidcs struct ip6_hdr *ip6; 543252206Sdavidcs struct tcphdr *th; 544252206Sdavidcs uint32_t ehdrlen, ip_hlen; 545252206Sdavidcs int ret = 0; 546252206Sdavidcs uint16_t etype; 547252206Sdavidcs device_t dev; 548252206Sdavidcs uint8_t buf[sizeof(struct ip6_hdr)]; 549252206Sdavidcs 550252206Sdavidcs dev = ha->pci_dev; 551252206Sdavidcs 552252206Sdavidcs eh = mtod(mp, struct ether_vlan_header *); 553252206Sdavidcs 554252206Sdavidcs if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { 555252206Sdavidcs ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; 556252206Sdavidcs etype = ntohs(eh->evl_proto); 557252206Sdavidcs } else { 558252206Sdavidcs ehdrlen = ETHER_HDR_LEN; 559252206Sdavidcs etype = ntohs(eh->evl_encap_proto); 560252206Sdavidcs } 561252206Sdavidcs 562252206Sdavidcs switch (etype) { 563252206Sdavidcs case ETHERTYPE_IP: 564252206Sdavidcs ip = (struct ip *)(mp->m_data + ehdrlen); 565252206Sdavidcs 566252206Sdavidcs ip_hlen = sizeof (struct ip); 567252206Sdavidcs 568252206Sdavidcs if (mp->m_len < (ehdrlen + ip_hlen)) { 569252206Sdavidcs m_copydata(mp, ehdrlen, sizeof(struct ip), buf); 570252206Sdavidcs ip = (struct ip *)buf; 571252206Sdavidcs } 572252206Sdavidcs tx_mac->opcode = Q81_IOCB_TX_TSO; 573252206Sdavidcs tx_mac->flags |= Q81_TX_TSO_FLAGS_IPV4 ; 574252206Sdavidcs 575252206Sdavidcs tx_mac->phdr_offsets = ehdrlen; 576252206Sdavidcs 577252206Sdavidcs tx_mac->phdr_offsets |= ((ehdrlen + ip_hlen) << 578252206Sdavidcs Q81_TX_TSO_PHDR_SHIFT); 579252206Sdavidcs 580252206Sdavidcs ip->ip_sum = 0; 581252206Sdavidcs 582252206Sdavidcs if (mp->m_pkthdr.csum_flags & CSUM_TSO) { 583252206Sdavidcs tx_mac->flags |= Q81_TX_TSO_FLAGS_LSO; 584252206Sdavidcs 585252206Sdavidcs th = (struct tcphdr *)(ip + 1); 586252206Sdavidcs 587252206Sdavidcs th->th_sum = in_pseudo(ip->ip_src.s_addr, 588252206Sdavidcs ip->ip_dst.s_addr, 589252206Sdavidcs htons(IPPROTO_TCP)); 590252206Sdavidcs tx_mac->mss = mp->m_pkthdr.tso_segsz; 591252206Sdavidcs tx_mac->phdr_length = ip_hlen + ehdrlen + 592252206Sdavidcs (th->th_off << 2); 593252206Sdavidcs break; 594252206Sdavidcs } 595252206Sdavidcs tx_mac->vlan_off |= Q81_TX_TSO_VLAN_OFF_IC ; 596252206Sdavidcs 597252206Sdavidcs 598252206Sdavidcs if (ip->ip_p == IPPROTO_TCP) { 599252206Sdavidcs tx_mac->flags |= Q81_TX_TSO_FLAGS_TC; 600252206Sdavidcs } else if (ip->ip_p == IPPROTO_UDP) { 601252206Sdavidcs tx_mac->flags |= Q81_TX_TSO_FLAGS_UC; 602252206Sdavidcs } 603252206Sdavidcs break; 604252206Sdavidcs 605252206Sdavidcs case ETHERTYPE_IPV6: 606252206Sdavidcs ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); 607252206Sdavidcs 608252206Sdavidcs ip_hlen = sizeof(struct ip6_hdr); 609252206Sdavidcs 610252206Sdavidcs if (mp->m_len < (ehdrlen + ip_hlen)) { 611252206Sdavidcs m_copydata(mp, ehdrlen, sizeof (struct ip6_hdr), 612252206Sdavidcs buf); 613252206Sdavidcs ip6 = (struct ip6_hdr *)buf; 614252206Sdavidcs } 615252206Sdavidcs 616252206Sdavidcs tx_mac->opcode = Q81_IOCB_TX_TSO; 617252206Sdavidcs tx_mac->flags |= Q81_TX_TSO_FLAGS_IPV6 ; 618252206Sdavidcs tx_mac->vlan_off |= Q81_TX_TSO_VLAN_OFF_IC ; 619252206Sdavidcs 620252206Sdavidcs tx_mac->phdr_offsets = ehdrlen; 621252206Sdavidcs tx_mac->phdr_offsets |= ((ehdrlen + ip_hlen) << 622252206Sdavidcs Q81_TX_TSO_PHDR_SHIFT); 623252206Sdavidcs 624252206Sdavidcs if (ip6->ip6_nxt == IPPROTO_TCP) { 625252206Sdavidcs tx_mac->flags |= Q81_TX_TSO_FLAGS_TC; 626252206Sdavidcs } else if (ip6->ip6_nxt == IPPROTO_UDP) { 627252206Sdavidcs tx_mac->flags |= Q81_TX_TSO_FLAGS_UC; 628252206Sdavidcs } 629252206Sdavidcs break; 630252206Sdavidcs 631252206Sdavidcs default: 632252206Sdavidcs ret = -1; 633252206Sdavidcs break; 634252206Sdavidcs } 635252206Sdavidcs 636252206Sdavidcs return (ret); 637252206Sdavidcs} 638252206Sdavidcs 639252206Sdavidcs#define QLA_TX_MIN_FREE 2 640252206Sdavidcsint 641252206Sdavidcsqls_hw_tx_done(qla_host_t *ha, uint32_t txr_idx) 642252206Sdavidcs{ 643252206Sdavidcs uint32_t txr_done, txr_next; 644252206Sdavidcs 645252206Sdavidcs txr_done = ha->tx_ring[txr_idx].txr_done; 646252206Sdavidcs txr_next = ha->tx_ring[txr_idx].txr_next; 647252206Sdavidcs 648252206Sdavidcs if (txr_done == txr_next) { 649252206Sdavidcs ha->tx_ring[txr_idx].txr_free = NUM_TX_DESCRIPTORS; 650252206Sdavidcs } else if (txr_done > txr_next) { 651252206Sdavidcs ha->tx_ring[txr_idx].txr_free = txr_done - txr_next; 652252206Sdavidcs } else { 653252206Sdavidcs ha->tx_ring[txr_idx].txr_free = NUM_TX_DESCRIPTORS + 654252206Sdavidcs txr_done - txr_next; 655252206Sdavidcs } 656252206Sdavidcs 657252206Sdavidcs if (ha->tx_ring[txr_idx].txr_free <= QLA_TX_MIN_FREE) 658252206Sdavidcs return (-1); 659252206Sdavidcs 660252206Sdavidcs return (0); 661252206Sdavidcs} 662252206Sdavidcs 663252206Sdavidcs/* 664252206Sdavidcs * Name: qls_hw_send 665252206Sdavidcs * Function: Transmits a packet. It first checks if the packet is a 666252206Sdavidcs * candidate for Large TCP Segment Offload and then for UDP/TCP checksum 667252206Sdavidcs * offload. If either of these creteria are not met, it is transmitted 668252206Sdavidcs * as a regular ethernet frame. 669252206Sdavidcs */ 670252206Sdavidcsint 671252206Sdavidcsqls_hw_send(qla_host_t *ha, bus_dma_segment_t *segs, int nsegs, 672252206Sdavidcs uint32_t txr_next, struct mbuf *mp, uint32_t txr_idx) 673252206Sdavidcs{ 674252206Sdavidcs q81_tx_mac_t *tx_mac; 675252206Sdavidcs q81_txb_desc_t *tx_desc; 676252206Sdavidcs uint32_t total_length = 0; 677252206Sdavidcs uint32_t i; 678252206Sdavidcs device_t dev; 679252206Sdavidcs int ret = 0; 680252206Sdavidcs 681252206Sdavidcs dev = ha->pci_dev; 682252206Sdavidcs 683252206Sdavidcs total_length = mp->m_pkthdr.len; 684252206Sdavidcs 685252206Sdavidcs if (total_length > QLA_MAX_TSO_FRAME_SIZE) { 686252206Sdavidcs device_printf(dev, "%s: total length exceeds maxlen(%d)\n", 687252206Sdavidcs __func__, total_length); 688252206Sdavidcs return (-1); 689252206Sdavidcs } 690252206Sdavidcs 691252206Sdavidcs if (ha->tx_ring[txr_idx].txr_free <= (NUM_TX_DESCRIPTORS >> 2)) { 692252206Sdavidcs if (qls_hw_tx_done(ha, txr_idx)) { 693252206Sdavidcs device_printf(dev, "%s: tx_free[%d] = %d\n", 694252206Sdavidcs __func__, txr_idx, 695252206Sdavidcs ha->tx_ring[txr_idx].txr_free); 696252206Sdavidcs return (-1); 697252206Sdavidcs } 698252206Sdavidcs } 699252206Sdavidcs 700252206Sdavidcs tx_mac = (q81_tx_mac_t *)&ha->tx_ring[txr_idx].wq_vaddr[txr_next]; 701252206Sdavidcs 702252206Sdavidcs bzero(tx_mac, sizeof(q81_tx_mac_t)); 703252206Sdavidcs 704252206Sdavidcs if ((mp->m_pkthdr.csum_flags & 705252206Sdavidcs (CSUM_TCP | CSUM_UDP | CSUM_IP | CSUM_TSO)) != 0) { 706252206Sdavidcs 707252206Sdavidcs ret = qls_tx_tso_chksum(ha, mp, (q81_tx_tso_t *)tx_mac); 708252206Sdavidcs if (ret) 709252206Sdavidcs return (EINVAL); 710252206Sdavidcs 711252206Sdavidcs if (mp->m_pkthdr.csum_flags & CSUM_TSO) 712252206Sdavidcs ha->tx_ring[txr_idx].tx_tso_frames++; 713252206Sdavidcs else 714252206Sdavidcs ha->tx_ring[txr_idx].tx_frames++; 715252206Sdavidcs 716252206Sdavidcs } else { 717252206Sdavidcs tx_mac->opcode = Q81_IOCB_TX_MAC; 718252206Sdavidcs } 719252206Sdavidcs 720252206Sdavidcs if (mp->m_flags & M_VLANTAG) { 721252206Sdavidcs 722252206Sdavidcs tx_mac->vlan_tci = mp->m_pkthdr.ether_vtag; 723252206Sdavidcs tx_mac->vlan_off |= Q81_TX_MAC_VLAN_OFF_V; 724252206Sdavidcs 725252206Sdavidcs ha->tx_ring[txr_idx].tx_vlan_frames++; 726252206Sdavidcs } 727252206Sdavidcs 728252206Sdavidcs tx_mac->frame_length = total_length; 729252206Sdavidcs 730252206Sdavidcs tx_mac->tid_lo = txr_next; 731252206Sdavidcs 732252206Sdavidcs if (nsegs <= MAX_TX_MAC_DESC) { 733252206Sdavidcs 734252206Sdavidcs QL_DPRINT2((dev, "%s: 1 [%d, %d]\n", __func__, total_length, 735252206Sdavidcs tx_mac->tid_lo)); 736252206Sdavidcs 737252206Sdavidcs for (i = 0; i < nsegs; i++) { 738252206Sdavidcs tx_mac->txd[i].baddr = segs->ds_addr; 739252206Sdavidcs tx_mac->txd[i].length = segs->ds_len; 740252206Sdavidcs segs++; 741252206Sdavidcs } 742252206Sdavidcs tx_mac->txd[(nsegs - 1)].flags = Q81_RXB_DESC_FLAGS_E; 743252206Sdavidcs 744252206Sdavidcs } else { 745252206Sdavidcs QL_DPRINT2((dev, "%s: 2 [%d, %d]\n", __func__, total_length, 746252206Sdavidcs tx_mac->tid_lo)); 747252206Sdavidcs 748252206Sdavidcs tx_mac->txd[0].baddr = 749252206Sdavidcs ha->tx_ring[txr_idx].tx_buf[txr_next].oal_paddr; 750252206Sdavidcs tx_mac->txd[0].length = 751252206Sdavidcs nsegs * (sizeof(q81_txb_desc_t)); 752252206Sdavidcs tx_mac->txd[0].flags = Q81_RXB_DESC_FLAGS_C; 753252206Sdavidcs 754252206Sdavidcs tx_desc = ha->tx_ring[txr_idx].tx_buf[txr_next].oal_vaddr; 755252206Sdavidcs 756252206Sdavidcs for (i = 0; i < nsegs; i++) { 757252206Sdavidcs tx_desc->baddr = segs->ds_addr; 758252206Sdavidcs tx_desc->length = segs->ds_len; 759252206Sdavidcs 760252206Sdavidcs if (i == (nsegs -1)) 761252206Sdavidcs tx_desc->flags = Q81_RXB_DESC_FLAGS_E; 762252206Sdavidcs else 763252206Sdavidcs tx_desc->flags = 0; 764252206Sdavidcs 765252206Sdavidcs segs++; 766252206Sdavidcs tx_desc++; 767252206Sdavidcs } 768252206Sdavidcs } 769252206Sdavidcs txr_next = (txr_next + 1) & (NUM_TX_DESCRIPTORS - 1); 770252206Sdavidcs ha->tx_ring[txr_idx].txr_next = txr_next; 771252206Sdavidcs 772252206Sdavidcs ha->tx_ring[txr_idx].txr_free--; 773252206Sdavidcs 774252206Sdavidcs Q81_WR_WQ_PROD_IDX(txr_idx, txr_next); 775252206Sdavidcs 776252206Sdavidcs return (0); 777252206Sdavidcs} 778252206Sdavidcs 779252206Sdavidcs/* 780252206Sdavidcs * Name: qls_del_hw_if 781252206Sdavidcs * Function: Destroys the hardware specific entities corresponding to an 782252206Sdavidcs * Ethernet Interface 783252206Sdavidcs */ 784252206Sdavidcsvoid 785252206Sdavidcsqls_del_hw_if(qla_host_t *ha) 786252206Sdavidcs{ 787252206Sdavidcs uint32_t value; 788252206Sdavidcs int i; 789252206Sdavidcs //int count; 790252206Sdavidcs 791252206Sdavidcs if (ha->hw_init == 0) { 792252206Sdavidcs qls_hw_reset(ha); 793252206Sdavidcs return; 794252206Sdavidcs } 795252206Sdavidcs 796252206Sdavidcs for (i = 0; i < ha->num_tx_rings; i++) { 797252206Sdavidcs Q81_SET_WQ_INVALID(i); 798252206Sdavidcs } 799252206Sdavidcs for (i = 0; i < ha->num_rx_rings; i++) { 800252206Sdavidcs Q81_SET_CQ_INVALID(i); 801252206Sdavidcs } 802252206Sdavidcs 803252206Sdavidcs for (i = 0; i < ha->num_rx_rings; i++) { 804252206Sdavidcs Q81_DISABLE_INTR(ha, i); /* MSI-x i */ 805252206Sdavidcs } 806252206Sdavidcs 807252206Sdavidcs value = (Q81_CTL_INTRE_IHD << Q81_CTL_INTRE_MASK_SHIFT); 808252206Sdavidcs WRITE_REG32(ha, Q81_CTL_INTR_ENABLE, value); 809252206Sdavidcs 810252206Sdavidcs value = (Q81_CTL_INTRE_EI << Q81_CTL_INTRE_MASK_SHIFT); 811252206Sdavidcs WRITE_REG32(ha, Q81_CTL_INTR_ENABLE, value); 812252206Sdavidcs ha->flags.intr_enable = 0; 813252206Sdavidcs 814252206Sdavidcs qls_hw_reset(ha); 815252206Sdavidcs 816252206Sdavidcs return; 817252206Sdavidcs} 818252206Sdavidcs 819252206Sdavidcs/* 820252206Sdavidcs * Name: qls_init_hw_if 821252206Sdavidcs * Function: Creates the hardware specific entities corresponding to an 822252206Sdavidcs * Ethernet Interface - Transmit and Receive Contexts. Sets the MAC Address 823252206Sdavidcs * corresponding to the interface. Enables LRO if allowed. 824252206Sdavidcs */ 825252206Sdavidcsint 826252206Sdavidcsqls_init_hw_if(qla_host_t *ha) 827252206Sdavidcs{ 828252206Sdavidcs device_t dev; 829252206Sdavidcs uint32_t value; 830252206Sdavidcs int ret = 0; 831252206Sdavidcs int i; 832252206Sdavidcs 833252206Sdavidcs 834252206Sdavidcs QL_DPRINT2((ha->pci_dev, "%s:enter\n", __func__)); 835252206Sdavidcs 836252206Sdavidcs dev = ha->pci_dev; 837252206Sdavidcs 838252206Sdavidcs ret = qls_hw_reset(ha); 839252206Sdavidcs if (ret) 840252206Sdavidcs goto qls_init_hw_if_exit; 841252206Sdavidcs 842252206Sdavidcs ha->vm_pgsize = 4096; 843252206Sdavidcs 844252206Sdavidcs /* Enable FAE and EFE bits in System Register */ 845252206Sdavidcs value = Q81_CTL_SYSTEM_ENABLE_FAE | Q81_CTL_SYSTEM_ENABLE_EFE; 846252206Sdavidcs value = (value << Q81_CTL_SYSTEM_MASK_SHIFT) | value; 847252206Sdavidcs 848252206Sdavidcs WRITE_REG32(ha, Q81_CTL_SYSTEM, value); 849252206Sdavidcs 850252206Sdavidcs /* Set Default Completion Queue_ID in NIC Rcv Configuration Register */ 851252206Sdavidcs value = (Q81_CTL_NIC_RCVC_DCQ_MASK << Q81_CTL_NIC_RCVC_MASK_SHIFT); 852252206Sdavidcs WRITE_REG32(ha, Q81_CTL_NIC_RCV_CONFIG, value); 853252206Sdavidcs 854252206Sdavidcs /* Function Specific Control Register - Set Page Size and Enable NIC */ 855252206Sdavidcs value = Q81_CTL_FUNC_SPECIFIC_FE | 856252206Sdavidcs Q81_CTL_FUNC_SPECIFIC_VM_PGSIZE_MASK | 857252206Sdavidcs Q81_CTL_FUNC_SPECIFIC_EPC_O | 858252206Sdavidcs Q81_CTL_FUNC_SPECIFIC_EPC_I | 859252206Sdavidcs Q81_CTL_FUNC_SPECIFIC_EC; 860252206Sdavidcs value = (value << Q81_CTL_FUNC_SPECIFIC_MASK_SHIFT) | 861252206Sdavidcs Q81_CTL_FUNC_SPECIFIC_FE | 862252206Sdavidcs Q81_CTL_FUNC_SPECIFIC_VM_PGSIZE_4K | 863252206Sdavidcs Q81_CTL_FUNC_SPECIFIC_EPC_O | 864252206Sdavidcs Q81_CTL_FUNC_SPECIFIC_EPC_I | 865252206Sdavidcs Q81_CTL_FUNC_SPECIFIC_EC; 866252206Sdavidcs 867252206Sdavidcs WRITE_REG32(ha, Q81_CTL_FUNC_SPECIFIC, value); 868252206Sdavidcs 869252206Sdavidcs /* Interrupt Mask Register */ 870252206Sdavidcs value = Q81_CTL_INTRM_PI; 871252206Sdavidcs value = (value << Q81_CTL_INTRM_MASK_SHIFT) | value; 872252206Sdavidcs 873252206Sdavidcs WRITE_REG32(ha, Q81_CTL_INTR_MASK, value); 874252206Sdavidcs 875252206Sdavidcs /* Initialiatize Completion Queue */ 876252206Sdavidcs for (i = 0; i < ha->num_rx_rings; i++) { 877252206Sdavidcs ret = qls_init_comp_queue(ha, i); 878252206Sdavidcs if (ret) 879252206Sdavidcs goto qls_init_hw_if_exit; 880252206Sdavidcs } 881252206Sdavidcs 882252206Sdavidcs if (ha->num_rx_rings > 1 ) { 883252206Sdavidcs ret = qls_init_rss(ha); 884252206Sdavidcs if (ret) 885252206Sdavidcs goto qls_init_hw_if_exit; 886252206Sdavidcs } 887252206Sdavidcs 888252206Sdavidcs /* Initialize Work Queue */ 889252206Sdavidcs 890252206Sdavidcs for (i = 0; i < ha->num_tx_rings; i++) { 891252206Sdavidcs ret = qls_init_work_queue(ha, i); 892252206Sdavidcs if (ret) 893252206Sdavidcs goto qls_init_hw_if_exit; 894252206Sdavidcs } 895252206Sdavidcs 896252206Sdavidcs if (ret) 897252206Sdavidcs goto qls_init_hw_if_exit; 898252206Sdavidcs 899252206Sdavidcs /* Set up CAM RAM with MAC Address */ 900252206Sdavidcs ret = qls_config_unicast_mac_addr(ha, 1); 901252206Sdavidcs if (ret) 902252206Sdavidcs goto qls_init_hw_if_exit; 903252206Sdavidcs 904252206Sdavidcs ret = qls_hw_add_all_mcast(ha); 905252206Sdavidcs if (ret) 906252206Sdavidcs goto qls_init_hw_if_exit; 907252206Sdavidcs 908252206Sdavidcs /* Initialize Firmware Routing Table */ 909252206Sdavidcs ret = qls_init_fw_routing_table(ha); 910252206Sdavidcs if (ret) 911252206Sdavidcs goto qls_init_hw_if_exit; 912252206Sdavidcs 913252206Sdavidcs /* Get Chip Revision ID */ 914252206Sdavidcs ha->rev_id = READ_REG32(ha, Q81_CTL_REV_ID); 915252206Sdavidcs 916252206Sdavidcs /* Enable Global Interrupt */ 917252206Sdavidcs value = Q81_CTL_INTRE_EI; 918252206Sdavidcs value = (value << Q81_CTL_INTRE_MASK_SHIFT) | value; 919252206Sdavidcs 920252206Sdavidcs WRITE_REG32(ha, Q81_CTL_INTR_ENABLE, value); 921252206Sdavidcs 922252206Sdavidcs /* Enable Interrupt Handshake Disable */ 923252206Sdavidcs value = Q81_CTL_INTRE_IHD; 924252206Sdavidcs value = (value << Q81_CTL_INTRE_MASK_SHIFT) | value; 925252206Sdavidcs 926252206Sdavidcs WRITE_REG32(ha, Q81_CTL_INTR_ENABLE, value); 927252206Sdavidcs 928252206Sdavidcs /* Enable Completion Interrupt */ 929252206Sdavidcs 930252206Sdavidcs ha->flags.intr_enable = 1; 931252206Sdavidcs 932252206Sdavidcs for (i = 0; i < ha->num_rx_rings; i++) { 933252206Sdavidcs Q81_ENABLE_INTR(ha, i); /* MSI-x i */ 934252206Sdavidcs } 935252206Sdavidcs 936252206Sdavidcs ha->hw_init = 1; 937252206Sdavidcs 938252206Sdavidcs qls_mbx_get_link_status(ha); 939252206Sdavidcs 940252206Sdavidcs QL_DPRINT2((ha->pci_dev, "%s:rxr [0x%08x]\n", __func__, 941252206Sdavidcs ha->rx_ring[0].cq_db_offset)); 942252206Sdavidcs QL_DPRINT2((ha->pci_dev, "%s:txr [0x%08x]\n", __func__, 943252206Sdavidcs ha->tx_ring[0].wq_db_offset)); 944252206Sdavidcs 945252206Sdavidcs for (i = 0; i < ha->num_rx_rings; i++) { 946252206Sdavidcs 947252206Sdavidcs Q81_WR_CQ_CONS_IDX(i, 0); 948252206Sdavidcs Q81_WR_LBQ_PROD_IDX(i, ha->rx_ring[i].lbq_in); 949252206Sdavidcs Q81_WR_SBQ_PROD_IDX(i, ha->rx_ring[i].sbq_in); 950252206Sdavidcs 951252206Sdavidcs QL_DPRINT2((dev, "%s: [wq_idx, cq_idx, lbq_idx, sbq_idx]" 952252206Sdavidcs "[0x%08x, 0x%08x, 0x%08x, 0x%08x]\n", __func__, 953252206Sdavidcs Q81_RD_WQ_IDX(i), Q81_RD_CQ_IDX(i), Q81_RD_LBQ_IDX(i), 954252206Sdavidcs Q81_RD_SBQ_IDX(i))); 955252206Sdavidcs } 956252206Sdavidcs 957252206Sdavidcs for (i = 0; i < ha->num_rx_rings; i++) { 958252206Sdavidcs Q81_SET_CQ_VALID(i); 959252206Sdavidcs } 960252206Sdavidcs 961252206Sdavidcsqls_init_hw_if_exit: 962252206Sdavidcs QL_DPRINT2((ha->pci_dev, "%s:exit\n", __func__)); 963252206Sdavidcs return (ret); 964252206Sdavidcs} 965252206Sdavidcs 966252206Sdavidcsstatic int 967252206Sdavidcsqls_wait_for_config_reg_bits(qla_host_t *ha, uint32_t bits, uint32_t value) 968252206Sdavidcs{ 969252206Sdavidcs uint32_t data32; 970252206Sdavidcs uint32_t count = 3; 971252206Sdavidcs 972252206Sdavidcs while (count--) { 973252206Sdavidcs 974252206Sdavidcs data32 = READ_REG32(ha, Q81_CTL_CONFIG); 975252206Sdavidcs 976252206Sdavidcs if ((data32 & bits) == value) 977252206Sdavidcs return (0); 978252206Sdavidcs 979252206Sdavidcs QLA_USEC_DELAY(100); 980252206Sdavidcs } 981252206Sdavidcs ha->qla_initiate_recovery = 1; 982252206Sdavidcs device_printf(ha->pci_dev, "%s: failed\n", __func__); 983252206Sdavidcs return (-1); 984252206Sdavidcs} 985252206Sdavidcs 986252206Sdavidcsstatic uint8_t q81_hash_key[] = { 987252206Sdavidcs 0xda, 0x56, 0x5a, 0x6d, 988252206Sdavidcs 0xc2, 0x0e, 0x5b, 0x25, 989252206Sdavidcs 0x3d, 0x25, 0x67, 0x41, 990252206Sdavidcs 0xb0, 0x8f, 0xa3, 0x43, 991252206Sdavidcs 0xcb, 0x2b, 0xca, 0xd0, 992252206Sdavidcs 0xb4, 0x30, 0x7b, 0xae, 993252206Sdavidcs 0xa3, 0x2d, 0xcb, 0x77, 994252206Sdavidcs 0x0c, 0xf2, 0x30, 0x80, 995252206Sdavidcs 0x3b, 0xb7, 0x42, 0x6a, 996252206Sdavidcs 0xfa, 0x01, 0xac, 0xbe }; 997252206Sdavidcs 998252206Sdavidcsstatic int 999252206Sdavidcsqls_init_rss(qla_host_t *ha) 1000252206Sdavidcs{ 1001252206Sdavidcs q81_rss_icb_t *rss_icb; 1002252206Sdavidcs int ret = 0; 1003252206Sdavidcs int i; 1004252206Sdavidcs uint32_t value; 1005252206Sdavidcs 1006252206Sdavidcs rss_icb = ha->rss_dma.dma_b; 1007252206Sdavidcs 1008252206Sdavidcs bzero(rss_icb, sizeof (q81_rss_icb_t)); 1009252206Sdavidcs 1010252206Sdavidcs rss_icb->flags_base_cq_num = Q81_RSS_ICB_FLAGS_L4K | 1011252206Sdavidcs Q81_RSS_ICB_FLAGS_L6K | Q81_RSS_ICB_FLAGS_LI | 1012252206Sdavidcs Q81_RSS_ICB_FLAGS_LB | Q81_RSS_ICB_FLAGS_LM | 1013252206Sdavidcs Q81_RSS_ICB_FLAGS_RT4 | Q81_RSS_ICB_FLAGS_RT6; 1014252206Sdavidcs 1015252206Sdavidcs rss_icb->mask = 0x3FF; 1016252206Sdavidcs 1017252206Sdavidcs for (i = 0; i < Q81_RSS_ICB_NUM_INDTBL_ENTRIES; i++) { 1018252206Sdavidcs rss_icb->cq_id[i] = (i & (ha->num_rx_rings - 1)); 1019252206Sdavidcs } 1020252206Sdavidcs 1021252206Sdavidcs memcpy(rss_icb->ipv6_rss_hash_key, q81_hash_key, 40); 1022252206Sdavidcs memcpy(rss_icb->ipv4_rss_hash_key, q81_hash_key, 16); 1023252206Sdavidcs 1024252206Sdavidcs ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LR, 0); 1025252206Sdavidcs 1026252206Sdavidcs if (ret) 1027252206Sdavidcs goto qls_init_rss_exit; 1028252206Sdavidcs 1029252206Sdavidcs ret = qls_sem_lock(ha, Q81_CTL_SEM_MASK_ICB, Q81_CTL_SEM_SET_ICB); 1030252206Sdavidcs 1031252206Sdavidcs if (ret) { 1032252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__)); 1033252206Sdavidcs goto qls_init_rss_exit; 1034252206Sdavidcs } 1035252206Sdavidcs 1036252206Sdavidcs value = (uint32_t)ha->rss_dma.dma_addr; 1037252206Sdavidcs WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_LO, value); 1038252206Sdavidcs 1039252206Sdavidcs value = (uint32_t)(ha->rss_dma.dma_addr >> 32); 1040252206Sdavidcs WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_HI, value); 1041252206Sdavidcs 1042252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_ICB); 1043252206Sdavidcs 1044252206Sdavidcs value = (Q81_CTL_CONFIG_LR << Q81_CTL_CONFIG_MASK_SHIFT) | 1045252206Sdavidcs Q81_CTL_CONFIG_LR; 1046252206Sdavidcs 1047252206Sdavidcs WRITE_REG32(ha, Q81_CTL_CONFIG, value); 1048252206Sdavidcs 1049252206Sdavidcs ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LR, 0); 1050252206Sdavidcs 1051252206Sdavidcsqls_init_rss_exit: 1052252206Sdavidcs return (ret); 1053252206Sdavidcs} 1054252206Sdavidcs 1055252206Sdavidcsstatic int 1056252206Sdavidcsqls_init_comp_queue(qla_host_t *ha, int cid) 1057252206Sdavidcs{ 1058252206Sdavidcs q81_cq_icb_t *cq_icb; 1059252206Sdavidcs qla_rx_ring_t *rxr; 1060252206Sdavidcs int ret = 0; 1061252206Sdavidcs uint32_t value; 1062252206Sdavidcs 1063252206Sdavidcs rxr = &ha->rx_ring[cid]; 1064252206Sdavidcs 1065252206Sdavidcs rxr->cq_db_offset = ha->vm_pgsize * (128 + cid); 1066252206Sdavidcs 1067252206Sdavidcs cq_icb = rxr->cq_icb_vaddr; 1068252206Sdavidcs 1069252206Sdavidcs bzero(cq_icb, sizeof (q81_cq_icb_t)); 1070252206Sdavidcs 1071252206Sdavidcs cq_icb->msix_vector = cid; 1072252206Sdavidcs cq_icb->flags = Q81_CQ_ICB_FLAGS_LC | 1073252206Sdavidcs Q81_CQ_ICB_FLAGS_LI | 1074252206Sdavidcs Q81_CQ_ICB_FLAGS_LL | 1075252206Sdavidcs Q81_CQ_ICB_FLAGS_LS | 1076252206Sdavidcs Q81_CQ_ICB_FLAGS_LV; 1077252206Sdavidcs 1078252206Sdavidcs cq_icb->length_v = NUM_CQ_ENTRIES; 1079252206Sdavidcs 1080252206Sdavidcs cq_icb->cq_baddr_lo = (rxr->cq_base_paddr & 0xFFFFFFFF); 1081252206Sdavidcs cq_icb->cq_baddr_hi = (rxr->cq_base_paddr >> 32) & 0xFFFFFFFF; 1082252206Sdavidcs 1083252206Sdavidcs cq_icb->cqi_addr_lo = (rxr->cqi_paddr & 0xFFFFFFFF); 1084252206Sdavidcs cq_icb->cqi_addr_hi = (rxr->cqi_paddr >> 32) & 0xFFFFFFFF; 1085252206Sdavidcs 1086252206Sdavidcs cq_icb->pkt_idelay = 10; 1087252206Sdavidcs cq_icb->idelay = 100; 1088252206Sdavidcs 1089252206Sdavidcs cq_icb->lbq_baddr_lo = (rxr->lbq_addr_tbl_paddr & 0xFFFFFFFF); 1090252206Sdavidcs cq_icb->lbq_baddr_hi = (rxr->lbq_addr_tbl_paddr >> 32) & 0xFFFFFFFF; 1091252206Sdavidcs 1092252206Sdavidcs cq_icb->lbq_bsize = QLA_LGB_SIZE; 1093252206Sdavidcs cq_icb->lbq_length = QLA_NUM_LGB_ENTRIES; 1094252206Sdavidcs 1095252206Sdavidcs cq_icb->sbq_baddr_lo = (rxr->sbq_addr_tbl_paddr & 0xFFFFFFFF); 1096252206Sdavidcs cq_icb->sbq_baddr_hi = (rxr->sbq_addr_tbl_paddr >> 32) & 0xFFFFFFFF; 1097252206Sdavidcs 1098252206Sdavidcs cq_icb->sbq_bsize = (uint16_t)ha->msize; 1099252206Sdavidcs cq_icb->sbq_length = QLA_NUM_SMB_ENTRIES; 1100252206Sdavidcs 1101252206Sdavidcs QL_DUMP_CQ(ha); 1102252206Sdavidcs 1103252206Sdavidcs ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LCQ, 0); 1104252206Sdavidcs 1105252206Sdavidcs if (ret) 1106252206Sdavidcs goto qls_init_comp_queue_exit; 1107252206Sdavidcs 1108252206Sdavidcs ret = qls_sem_lock(ha, Q81_CTL_SEM_MASK_ICB, Q81_CTL_SEM_SET_ICB); 1109252206Sdavidcs 1110252206Sdavidcs if (ret) { 1111252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__)); 1112252206Sdavidcs goto qls_init_comp_queue_exit; 1113252206Sdavidcs } 1114252206Sdavidcs 1115252206Sdavidcs value = (uint32_t)rxr->cq_icb_paddr; 1116252206Sdavidcs WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_LO, value); 1117252206Sdavidcs 1118252206Sdavidcs value = (uint32_t)(rxr->cq_icb_paddr >> 32); 1119252206Sdavidcs WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_HI, value); 1120252206Sdavidcs 1121252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_ICB); 1122252206Sdavidcs 1123252206Sdavidcs value = Q81_CTL_CONFIG_LCQ | Q81_CTL_CONFIG_Q_NUM_MASK; 1124252206Sdavidcs value = (value << Q81_CTL_CONFIG_MASK_SHIFT) | Q81_CTL_CONFIG_LCQ; 1125252206Sdavidcs value |= (cid << Q81_CTL_CONFIG_Q_NUM_SHIFT); 1126252206Sdavidcs WRITE_REG32(ha, Q81_CTL_CONFIG, value); 1127252206Sdavidcs 1128252206Sdavidcs ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LCQ, 0); 1129252206Sdavidcs 1130252206Sdavidcs rxr->cq_next = 0; 1131252206Sdavidcs rxr->lbq_next = rxr->lbq_free = 0; 1132252206Sdavidcs rxr->sbq_next = rxr->sbq_free = 0; 1133252206Sdavidcs rxr->rx_free = rxr->rx_next = 0; 1134252206Sdavidcs rxr->lbq_in = (QLA_NUM_LGB_ENTRIES - 1) & ~0xF; 1135252206Sdavidcs rxr->sbq_in = (QLA_NUM_SMB_ENTRIES - 1) & ~0xF; 1136252206Sdavidcs 1137252206Sdavidcsqls_init_comp_queue_exit: 1138252206Sdavidcs return (ret); 1139252206Sdavidcs} 1140252206Sdavidcs 1141252206Sdavidcsstatic int 1142252206Sdavidcsqls_init_work_queue(qla_host_t *ha, int wid) 1143252206Sdavidcs{ 1144252206Sdavidcs q81_wq_icb_t *wq_icb; 1145252206Sdavidcs qla_tx_ring_t *txr; 1146252206Sdavidcs int ret = 0; 1147252206Sdavidcs uint32_t value; 1148252206Sdavidcs 1149252206Sdavidcs txr = &ha->tx_ring[wid]; 1150252206Sdavidcs 1151252206Sdavidcs txr->wq_db_addr = (struct resource *)((uint8_t *)ha->pci_reg1 1152252206Sdavidcs + (ha->vm_pgsize * wid)); 1153252206Sdavidcs 1154252206Sdavidcs txr->wq_db_offset = (ha->vm_pgsize * wid); 1155252206Sdavidcs 1156252206Sdavidcs wq_icb = txr->wq_icb_vaddr; 1157252206Sdavidcs bzero(wq_icb, sizeof (q81_wq_icb_t)); 1158252206Sdavidcs 1159252206Sdavidcs wq_icb->length_v = NUM_TX_DESCRIPTORS | 1160252206Sdavidcs Q81_WQ_ICB_VALID; 1161252206Sdavidcs 1162252206Sdavidcs wq_icb->flags = Q81_WQ_ICB_FLAGS_LO | Q81_WQ_ICB_FLAGS_LI | 1163252206Sdavidcs Q81_WQ_ICB_FLAGS_LB | Q81_WQ_ICB_FLAGS_LC; 1164252206Sdavidcs 1165252206Sdavidcs wq_icb->wqcqid_rss = wid; 1166252206Sdavidcs 1167252206Sdavidcs wq_icb->baddr_lo = txr->wq_paddr & 0xFFFFFFFF; 1168252206Sdavidcs wq_icb->baddr_hi = (txr->wq_paddr >> 32)& 0xFFFFFFFF; 1169252206Sdavidcs 1170252206Sdavidcs wq_icb->ci_addr_lo = txr->txr_cons_paddr & 0xFFFFFFFF; 1171252206Sdavidcs wq_icb->ci_addr_hi = (txr->txr_cons_paddr >> 32)& 0xFFFFFFFF; 1172252206Sdavidcs 1173252206Sdavidcs ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LRQ, 0); 1174252206Sdavidcs 1175252206Sdavidcs if (ret) 1176252206Sdavidcs goto qls_init_wq_exit; 1177252206Sdavidcs 1178252206Sdavidcs ret = qls_sem_lock(ha, Q81_CTL_SEM_MASK_ICB, Q81_CTL_SEM_SET_ICB); 1179252206Sdavidcs 1180252206Sdavidcs if (ret) { 1181252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__)); 1182252206Sdavidcs goto qls_init_wq_exit; 1183252206Sdavidcs } 1184252206Sdavidcs 1185252206Sdavidcs value = (uint32_t)txr->wq_icb_paddr; 1186252206Sdavidcs WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_LO, value); 1187252206Sdavidcs 1188252206Sdavidcs value = (uint32_t)(txr->wq_icb_paddr >> 32); 1189252206Sdavidcs WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_HI, value); 1190252206Sdavidcs 1191252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_ICB); 1192252206Sdavidcs 1193252206Sdavidcs value = Q81_CTL_CONFIG_LRQ | Q81_CTL_CONFIG_Q_NUM_MASK; 1194252206Sdavidcs value = (value << Q81_CTL_CONFIG_MASK_SHIFT) | Q81_CTL_CONFIG_LRQ; 1195252206Sdavidcs value |= (wid << Q81_CTL_CONFIG_Q_NUM_SHIFT); 1196252206Sdavidcs WRITE_REG32(ha, Q81_CTL_CONFIG, value); 1197252206Sdavidcs 1198252206Sdavidcs ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LRQ, 0); 1199252206Sdavidcs 1200252206Sdavidcs txr->txr_free = NUM_TX_DESCRIPTORS; 1201252206Sdavidcs txr->txr_next = 0; 1202252206Sdavidcs txr->txr_done = 0; 1203252206Sdavidcs 1204252206Sdavidcsqls_init_wq_exit: 1205252206Sdavidcs return (ret); 1206252206Sdavidcs} 1207252206Sdavidcs 1208252206Sdavidcsstatic int 1209252206Sdavidcsqls_hw_add_all_mcast(qla_host_t *ha) 1210252206Sdavidcs{ 1211252206Sdavidcs int i, nmcast; 1212252206Sdavidcs 1213252206Sdavidcs nmcast = ha->nmcast; 1214252206Sdavidcs 1215252206Sdavidcs for (i = 0 ; ((i < Q8_MAX_NUM_MULTICAST_ADDRS) && nmcast); i++) { 1216252206Sdavidcs if ((ha->mcast[i].addr[0] != 0) || 1217252206Sdavidcs (ha->mcast[i].addr[1] != 0) || 1218252206Sdavidcs (ha->mcast[i].addr[2] != 0) || 1219252206Sdavidcs (ha->mcast[i].addr[3] != 0) || 1220252206Sdavidcs (ha->mcast[i].addr[4] != 0) || 1221252206Sdavidcs (ha->mcast[i].addr[5] != 0)) { 1222252206Sdavidcs 1223252206Sdavidcs if (qls_config_mcast_mac_addr(ha, ha->mcast[i].addr, 1224252206Sdavidcs 1, i)) { 1225252206Sdavidcs device_printf(ha->pci_dev, "%s: failed\n", 1226252206Sdavidcs __func__); 1227252206Sdavidcs return (-1); 1228252206Sdavidcs } 1229252206Sdavidcs 1230252206Sdavidcs nmcast--; 1231252206Sdavidcs } 1232252206Sdavidcs } 1233252206Sdavidcs return 0; 1234252206Sdavidcs} 1235252206Sdavidcs 1236252206Sdavidcsstatic int 1237252206Sdavidcsqls_hw_add_mcast(qla_host_t *ha, uint8_t *mta) 1238252206Sdavidcs{ 1239252206Sdavidcs int i; 1240252206Sdavidcs 1241252206Sdavidcs for (i = 0; i < Q8_MAX_NUM_MULTICAST_ADDRS; i++) { 1242252206Sdavidcs 1243252206Sdavidcs if (QL_MAC_CMP(ha->mcast[i].addr, mta) == 0) 1244252206Sdavidcs return 0; /* its been already added */ 1245252206Sdavidcs } 1246252206Sdavidcs 1247252206Sdavidcs for (i = 0; i < Q8_MAX_NUM_MULTICAST_ADDRS; i++) { 1248252206Sdavidcs 1249252206Sdavidcs if ((ha->mcast[i].addr[0] == 0) && 1250252206Sdavidcs (ha->mcast[i].addr[1] == 0) && 1251252206Sdavidcs (ha->mcast[i].addr[2] == 0) && 1252252206Sdavidcs (ha->mcast[i].addr[3] == 0) && 1253252206Sdavidcs (ha->mcast[i].addr[4] == 0) && 1254252206Sdavidcs (ha->mcast[i].addr[5] == 0)) { 1255252206Sdavidcs 1256252206Sdavidcs if (qls_config_mcast_mac_addr(ha, mta, 1, i)) 1257252206Sdavidcs return (-1); 1258252206Sdavidcs 1259252206Sdavidcs bcopy(mta, ha->mcast[i].addr, Q8_MAC_ADDR_LEN); 1260252206Sdavidcs ha->nmcast++; 1261252206Sdavidcs 1262252206Sdavidcs return 0; 1263252206Sdavidcs } 1264252206Sdavidcs } 1265252206Sdavidcs return 0; 1266252206Sdavidcs} 1267252206Sdavidcs 1268252206Sdavidcsstatic int 1269252206Sdavidcsqls_hw_del_mcast(qla_host_t *ha, uint8_t *mta) 1270252206Sdavidcs{ 1271252206Sdavidcs int i; 1272252206Sdavidcs 1273252206Sdavidcs for (i = 0; i < Q8_MAX_NUM_MULTICAST_ADDRS; i++) { 1274252206Sdavidcs if (QL_MAC_CMP(ha->mcast[i].addr, mta) == 0) { 1275252206Sdavidcs 1276252206Sdavidcs if (qls_config_mcast_mac_addr(ha, mta, 0, i)) 1277252206Sdavidcs return (-1); 1278252206Sdavidcs 1279252206Sdavidcs ha->mcast[i].addr[0] = 0; 1280252206Sdavidcs ha->mcast[i].addr[1] = 0; 1281252206Sdavidcs ha->mcast[i].addr[2] = 0; 1282252206Sdavidcs ha->mcast[i].addr[3] = 0; 1283252206Sdavidcs ha->mcast[i].addr[4] = 0; 1284252206Sdavidcs ha->mcast[i].addr[5] = 0; 1285252206Sdavidcs 1286252206Sdavidcs ha->nmcast--; 1287252206Sdavidcs 1288252206Sdavidcs return 0; 1289252206Sdavidcs } 1290252206Sdavidcs } 1291252206Sdavidcs return 0; 1292252206Sdavidcs} 1293252206Sdavidcs 1294252206Sdavidcs/* 1295252206Sdavidcs * Name: qls_hw_set_multi 1296252206Sdavidcs * Function: Sets the Multicast Addresses provided the host O.S into the 1297252206Sdavidcs * hardware (for the given interface) 1298252206Sdavidcs */ 1299252206Sdavidcsvoid 1300252206Sdavidcsqls_hw_set_multi(qla_host_t *ha, uint8_t *mta, uint32_t mcnt, 1301252206Sdavidcs uint32_t add_mac) 1302252206Sdavidcs{ 1303252206Sdavidcs int i; 1304252206Sdavidcs 1305252206Sdavidcs for (i = 0; i < mcnt; i++) { 1306252206Sdavidcs if (add_mac) { 1307252206Sdavidcs if (qls_hw_add_mcast(ha, mta)) 1308252206Sdavidcs break; 1309252206Sdavidcs } else { 1310252206Sdavidcs if (qls_hw_del_mcast(ha, mta)) 1311252206Sdavidcs break; 1312252206Sdavidcs } 1313252206Sdavidcs 1314252206Sdavidcs mta += Q8_MAC_ADDR_LEN; 1315252206Sdavidcs } 1316252206Sdavidcs return; 1317252206Sdavidcs} 1318252206Sdavidcs 1319252206Sdavidcsvoid 1320252206Sdavidcsqls_update_link_state(qla_host_t *ha) 1321252206Sdavidcs{ 1322252206Sdavidcs uint32_t link_state; 1323252206Sdavidcs uint32_t prev_link_state; 1324252206Sdavidcs 1325252206Sdavidcs if (!(ha->ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1326252206Sdavidcs ha->link_up = 0; 1327252206Sdavidcs return; 1328252206Sdavidcs } 1329252206Sdavidcs link_state = READ_REG32(ha, Q81_CTL_STATUS); 1330252206Sdavidcs 1331252206Sdavidcs prev_link_state = ha->link_up; 1332252206Sdavidcs 1333252206Sdavidcs if ((ha->pci_func & 0x1) == 0) 1334252206Sdavidcs ha->link_up = ((link_state & Q81_CTL_STATUS_PL0)? 1 : 0); 1335252206Sdavidcs else 1336252206Sdavidcs ha->link_up = ((link_state & Q81_CTL_STATUS_PL1)? 1 : 0); 1337252206Sdavidcs 1338252206Sdavidcs if (prev_link_state != ha->link_up) { 1339252206Sdavidcs 1340252206Sdavidcs 1341252206Sdavidcs if (ha->link_up) { 1342252206Sdavidcs if_link_state_change(ha->ifp, LINK_STATE_UP); 1343252206Sdavidcs } else { 1344252206Sdavidcs if_link_state_change(ha->ifp, LINK_STATE_DOWN); 1345252206Sdavidcs } 1346252206Sdavidcs } 1347252206Sdavidcs return; 1348252206Sdavidcs} 1349252206Sdavidcs 1350252206Sdavidcsstatic void 1351252206Sdavidcsqls_free_tx_ring_dma(qla_host_t *ha, int r_idx) 1352252206Sdavidcs{ 1353252206Sdavidcs if (ha->tx_ring[r_idx].flags.wq_dma) { 1354252206Sdavidcs qls_free_dmabuf(ha, &ha->tx_ring[r_idx].wq_dma); 1355252206Sdavidcs ha->tx_ring[r_idx].flags.wq_dma = 0; 1356252206Sdavidcs } 1357252206Sdavidcs 1358252206Sdavidcs if (ha->tx_ring[r_idx].flags.privb_dma) { 1359252206Sdavidcs qls_free_dmabuf(ha, &ha->tx_ring[r_idx].privb_dma); 1360252206Sdavidcs ha->tx_ring[r_idx].flags.privb_dma = 0; 1361252206Sdavidcs } 1362252206Sdavidcs return; 1363252206Sdavidcs} 1364252206Sdavidcs 1365252206Sdavidcsstatic void 1366252206Sdavidcsqls_free_tx_dma(qla_host_t *ha) 1367252206Sdavidcs{ 1368252206Sdavidcs int i, j; 1369252206Sdavidcs qla_tx_buf_t *txb; 1370252206Sdavidcs 1371252206Sdavidcs for (i = 0; i < ha->num_tx_rings; i++) { 1372252206Sdavidcs 1373252206Sdavidcs qls_free_tx_ring_dma(ha, i); 1374252206Sdavidcs 1375252206Sdavidcs for (j = 0; j < NUM_TX_DESCRIPTORS; j++) { 1376252206Sdavidcs 1377252206Sdavidcs txb = &ha->tx_ring[i].tx_buf[j]; 1378252206Sdavidcs 1379252206Sdavidcs if (txb->map) { 1380252206Sdavidcs bus_dmamap_destroy(ha->tx_tag, txb->map); 1381252206Sdavidcs } 1382252206Sdavidcs } 1383252206Sdavidcs } 1384252206Sdavidcs 1385252206Sdavidcs if (ha->tx_tag != NULL) { 1386252206Sdavidcs bus_dma_tag_destroy(ha->tx_tag); 1387252206Sdavidcs ha->tx_tag = NULL; 1388252206Sdavidcs } 1389252206Sdavidcs 1390252206Sdavidcs return; 1391252206Sdavidcs} 1392252206Sdavidcs 1393252206Sdavidcsstatic int 1394252206Sdavidcsqls_alloc_tx_ring_dma(qla_host_t *ha, int ridx) 1395252206Sdavidcs{ 1396252206Sdavidcs int ret = 0, i; 1397252206Sdavidcs uint8_t *v_addr; 1398252206Sdavidcs bus_addr_t p_addr; 1399252206Sdavidcs qla_tx_buf_t *txb; 1400252206Sdavidcs device_t dev = ha->pci_dev; 1401252206Sdavidcs 1402252206Sdavidcs ha->tx_ring[ridx].wq_dma.alignment = 8; 1403252206Sdavidcs ha->tx_ring[ridx].wq_dma.size = 1404252206Sdavidcs NUM_TX_DESCRIPTORS * (sizeof (q81_tx_cmd_t)); 1405252206Sdavidcs 1406252206Sdavidcs ret = qls_alloc_dmabuf(ha, &ha->tx_ring[ridx].wq_dma); 1407252206Sdavidcs 1408252206Sdavidcs if (ret) { 1409252206Sdavidcs device_printf(dev, "%s: [%d] txr failed\n", __func__, ridx); 1410252206Sdavidcs goto qls_alloc_tx_ring_dma_exit; 1411252206Sdavidcs } 1412252206Sdavidcs ha->tx_ring[ridx].flags.wq_dma = 1; 1413252206Sdavidcs 1414252206Sdavidcs ha->tx_ring[ridx].privb_dma.alignment = 8; 1415252206Sdavidcs ha->tx_ring[ridx].privb_dma.size = QLA_TX_PRIVATE_BSIZE; 1416252206Sdavidcs 1417252206Sdavidcs ret = qls_alloc_dmabuf(ha, &ha->tx_ring[ridx].privb_dma); 1418252206Sdavidcs 1419252206Sdavidcs if (ret) { 1420252206Sdavidcs device_printf(dev, "%s: [%d] oalb failed\n", __func__, ridx); 1421252206Sdavidcs goto qls_alloc_tx_ring_dma_exit; 1422252206Sdavidcs } 1423252206Sdavidcs 1424252206Sdavidcs ha->tx_ring[ridx].flags.privb_dma = 1; 1425252206Sdavidcs 1426252206Sdavidcs ha->tx_ring[ridx].wq_vaddr = ha->tx_ring[ridx].wq_dma.dma_b; 1427252206Sdavidcs ha->tx_ring[ridx].wq_paddr = ha->tx_ring[ridx].wq_dma.dma_addr; 1428252206Sdavidcs 1429252206Sdavidcs v_addr = ha->tx_ring[ridx].privb_dma.dma_b; 1430252206Sdavidcs p_addr = ha->tx_ring[ridx].privb_dma.dma_addr; 1431252206Sdavidcs 1432252206Sdavidcs ha->tx_ring[ridx].wq_icb_vaddr = v_addr; 1433252206Sdavidcs ha->tx_ring[ridx].wq_icb_paddr = p_addr; 1434252206Sdavidcs 1435252206Sdavidcs ha->tx_ring[ridx].txr_cons_vaddr = 1436252206Sdavidcs (uint32_t *)(v_addr + (PAGE_SIZE >> 1)); 1437252206Sdavidcs ha->tx_ring[ridx].txr_cons_paddr = p_addr + (PAGE_SIZE >> 1); 1438252206Sdavidcs 1439252206Sdavidcs v_addr = v_addr + (PAGE_SIZE >> 1); 1440252206Sdavidcs p_addr = p_addr + (PAGE_SIZE >> 1); 1441252206Sdavidcs 1442252206Sdavidcs txb = ha->tx_ring[ridx].tx_buf; 1443252206Sdavidcs 1444252206Sdavidcs for (i = 0; i < NUM_TX_DESCRIPTORS; i++) { 1445252206Sdavidcs 1446252206Sdavidcs txb[i].oal_vaddr = v_addr; 1447252206Sdavidcs txb[i].oal_paddr = p_addr; 1448252206Sdavidcs 1449252206Sdavidcs v_addr = v_addr + QLA_OAL_BLK_SIZE; 1450252206Sdavidcs p_addr = p_addr + QLA_OAL_BLK_SIZE; 1451252206Sdavidcs } 1452252206Sdavidcs 1453252206Sdavidcsqls_alloc_tx_ring_dma_exit: 1454252206Sdavidcs return (ret); 1455252206Sdavidcs} 1456252206Sdavidcs 1457252206Sdavidcsstatic int 1458252206Sdavidcsqls_alloc_tx_dma(qla_host_t *ha) 1459252206Sdavidcs{ 1460252206Sdavidcs int i, j; 1461252206Sdavidcs int ret = 0; 1462252206Sdavidcs qla_tx_buf_t *txb; 1463252206Sdavidcs 1464252206Sdavidcs if (bus_dma_tag_create(NULL, /* parent */ 1465252206Sdavidcs 1, 0, /* alignment, bounds */ 1466252206Sdavidcs BUS_SPACE_MAXADDR, /* lowaddr */ 1467252206Sdavidcs BUS_SPACE_MAXADDR, /* highaddr */ 1468252206Sdavidcs NULL, NULL, /* filter, filterarg */ 1469252206Sdavidcs QLA_MAX_TSO_FRAME_SIZE, /* maxsize */ 1470252206Sdavidcs QLA_MAX_SEGMENTS, /* nsegments */ 1471252206Sdavidcs PAGE_SIZE, /* maxsegsize */ 1472252206Sdavidcs BUS_DMA_ALLOCNOW, /* flags */ 1473252206Sdavidcs NULL, /* lockfunc */ 1474252206Sdavidcs NULL, /* lockfuncarg */ 1475252206Sdavidcs &ha->tx_tag)) { 1476252206Sdavidcs device_printf(ha->pci_dev, "%s: tx_tag alloc failed\n", 1477252206Sdavidcs __func__); 1478252206Sdavidcs return (ENOMEM); 1479252206Sdavidcs } 1480252206Sdavidcs 1481252206Sdavidcs for (i = 0; i < ha->num_tx_rings; i++) { 1482252206Sdavidcs 1483252206Sdavidcs ret = qls_alloc_tx_ring_dma(ha, i); 1484252206Sdavidcs 1485252206Sdavidcs if (ret) { 1486252206Sdavidcs qls_free_tx_dma(ha); 1487252206Sdavidcs break; 1488252206Sdavidcs } 1489252206Sdavidcs 1490252206Sdavidcs for (j = 0; j < NUM_TX_DESCRIPTORS; j++) { 1491252206Sdavidcs 1492252206Sdavidcs txb = &ha->tx_ring[i].tx_buf[j]; 1493252206Sdavidcs 1494252206Sdavidcs ret = bus_dmamap_create(ha->tx_tag, 1495252206Sdavidcs BUS_DMA_NOWAIT, &txb->map); 1496252206Sdavidcs if (ret) { 1497252206Sdavidcs ha->err_tx_dmamap_create++; 1498252206Sdavidcs device_printf(ha->pci_dev, 1499252206Sdavidcs "%s: bus_dmamap_create failed[%d, %d, %d]\n", 1500252206Sdavidcs __func__, ret, i, j); 1501252206Sdavidcs 1502252206Sdavidcs qls_free_tx_dma(ha); 1503252206Sdavidcs 1504252206Sdavidcs return (ret); 1505252206Sdavidcs } 1506252206Sdavidcs } 1507252206Sdavidcs } 1508252206Sdavidcs 1509252206Sdavidcs return (ret); 1510252206Sdavidcs} 1511252206Sdavidcs 1512252206Sdavidcsstatic void 1513252206Sdavidcsqls_free_rss_dma(qla_host_t *ha) 1514252206Sdavidcs{ 1515252206Sdavidcs qls_free_dmabuf(ha, &ha->rss_dma); 1516252206Sdavidcs ha->flags.rss_dma = 0; 1517252206Sdavidcs} 1518252206Sdavidcs 1519252206Sdavidcsstatic int 1520252206Sdavidcsqls_alloc_rss_dma(qla_host_t *ha) 1521252206Sdavidcs{ 1522252206Sdavidcs int ret = 0; 1523252206Sdavidcs 1524252206Sdavidcs ha->rss_dma.alignment = 4; 1525252206Sdavidcs ha->rss_dma.size = PAGE_SIZE; 1526252206Sdavidcs 1527252206Sdavidcs ret = qls_alloc_dmabuf(ha, &ha->rss_dma); 1528252206Sdavidcs 1529252206Sdavidcs if (ret) 1530252206Sdavidcs device_printf(ha->pci_dev, "%s: failed\n", __func__); 1531252206Sdavidcs else 1532252206Sdavidcs ha->flags.rss_dma = 1; 1533252206Sdavidcs 1534252206Sdavidcs return (ret); 1535252206Sdavidcs} 1536252206Sdavidcs 1537252206Sdavidcsstatic void 1538252206Sdavidcsqls_free_mpi_dma(qla_host_t *ha) 1539252206Sdavidcs{ 1540252206Sdavidcs qls_free_dmabuf(ha, &ha->mpi_dma); 1541252206Sdavidcs ha->flags.mpi_dma = 0; 1542252206Sdavidcs} 1543252206Sdavidcs 1544252206Sdavidcsstatic int 1545252206Sdavidcsqls_alloc_mpi_dma(qla_host_t *ha) 1546252206Sdavidcs{ 1547252206Sdavidcs int ret = 0; 1548252206Sdavidcs 1549252206Sdavidcs ha->mpi_dma.alignment = 4; 1550252206Sdavidcs ha->mpi_dma.size = (0x4000 * 4); 1551252206Sdavidcs 1552252206Sdavidcs ret = qls_alloc_dmabuf(ha, &ha->mpi_dma); 1553252206Sdavidcs if (ret) 1554252206Sdavidcs device_printf(ha->pci_dev, "%s: failed\n", __func__); 1555252206Sdavidcs else 1556252206Sdavidcs ha->flags.mpi_dma = 1; 1557252206Sdavidcs 1558252206Sdavidcs return (ret); 1559252206Sdavidcs} 1560252206Sdavidcs 1561252206Sdavidcsstatic void 1562252206Sdavidcsqls_free_rx_ring_dma(qla_host_t *ha, int ridx) 1563252206Sdavidcs{ 1564252206Sdavidcs if (ha->rx_ring[ridx].flags.cq_dma) { 1565252206Sdavidcs qls_free_dmabuf(ha, &ha->rx_ring[ridx].cq_dma); 1566252206Sdavidcs ha->rx_ring[ridx].flags.cq_dma = 0; 1567252206Sdavidcs } 1568252206Sdavidcs 1569252206Sdavidcs if (ha->rx_ring[ridx].flags.lbq_dma) { 1570252206Sdavidcs qls_free_dmabuf(ha, &ha->rx_ring[ridx].lbq_dma); 1571252206Sdavidcs ha->rx_ring[ridx].flags.lbq_dma = 0; 1572252206Sdavidcs } 1573252206Sdavidcs 1574252206Sdavidcs if (ha->rx_ring[ridx].flags.sbq_dma) { 1575252206Sdavidcs qls_free_dmabuf(ha, &ha->rx_ring[ridx].sbq_dma); 1576252206Sdavidcs ha->rx_ring[ridx].flags.sbq_dma = 0; 1577252206Sdavidcs } 1578252206Sdavidcs 1579252206Sdavidcs if (ha->rx_ring[ridx].flags.lb_dma) { 1580252206Sdavidcs qls_free_dmabuf(ha, &ha->rx_ring[ridx].lb_dma); 1581252206Sdavidcs ha->rx_ring[ridx].flags.lb_dma = 0; 1582252206Sdavidcs } 1583252206Sdavidcs return; 1584252206Sdavidcs} 1585252206Sdavidcs 1586252206Sdavidcsstatic void 1587252206Sdavidcsqls_free_rx_dma(qla_host_t *ha) 1588252206Sdavidcs{ 1589252206Sdavidcs int i; 1590252206Sdavidcs 1591252206Sdavidcs for (i = 0; i < ha->num_rx_rings; i++) { 1592252206Sdavidcs qls_free_rx_ring_dma(ha, i); 1593252206Sdavidcs } 1594252206Sdavidcs 1595252206Sdavidcs if (ha->rx_tag != NULL) { 1596252206Sdavidcs bus_dma_tag_destroy(ha->rx_tag); 1597252206Sdavidcs ha->rx_tag = NULL; 1598252206Sdavidcs } 1599252206Sdavidcs 1600252206Sdavidcs return; 1601252206Sdavidcs} 1602252206Sdavidcs 1603252206Sdavidcsstatic int 1604252206Sdavidcsqls_alloc_rx_ring_dma(qla_host_t *ha, int ridx) 1605252206Sdavidcs{ 1606252206Sdavidcs int i, ret = 0; 1607252206Sdavidcs uint8_t *v_addr; 1608252206Sdavidcs bus_addr_t p_addr; 1609252206Sdavidcs volatile q81_bq_addr_e_t *bq_e; 1610252206Sdavidcs device_t dev = ha->pci_dev; 1611252206Sdavidcs 1612252206Sdavidcs ha->rx_ring[ridx].cq_dma.alignment = 128; 1613252206Sdavidcs ha->rx_ring[ridx].cq_dma.size = 1614252206Sdavidcs (NUM_CQ_ENTRIES * (sizeof (q81_cq_e_t))) + PAGE_SIZE; 1615252206Sdavidcs 1616252206Sdavidcs ret = qls_alloc_dmabuf(ha, &ha->rx_ring[ridx].cq_dma); 1617252206Sdavidcs 1618252206Sdavidcs if (ret) { 1619252206Sdavidcs device_printf(dev, "%s: [%d] cq failed\n", __func__, ridx); 1620252206Sdavidcs goto qls_alloc_rx_ring_dma_exit; 1621252206Sdavidcs } 1622252206Sdavidcs ha->rx_ring[ridx].flags.cq_dma = 1; 1623252206Sdavidcs 1624252206Sdavidcs ha->rx_ring[ridx].lbq_dma.alignment = 8; 1625252206Sdavidcs ha->rx_ring[ridx].lbq_dma.size = QLA_LGBQ_AND_TABLE_SIZE; 1626252206Sdavidcs 1627252206Sdavidcs ret = qls_alloc_dmabuf(ha, &ha->rx_ring[ridx].lbq_dma); 1628252206Sdavidcs 1629252206Sdavidcs if (ret) { 1630252206Sdavidcs device_printf(dev, "%s: [%d] lbq failed\n", __func__, ridx); 1631252206Sdavidcs goto qls_alloc_rx_ring_dma_exit; 1632252206Sdavidcs } 1633252206Sdavidcs ha->rx_ring[ridx].flags.lbq_dma = 1; 1634252206Sdavidcs 1635252206Sdavidcs ha->rx_ring[ridx].sbq_dma.alignment = 8; 1636252206Sdavidcs ha->rx_ring[ridx].sbq_dma.size = QLA_SMBQ_AND_TABLE_SIZE; 1637252206Sdavidcs 1638252206Sdavidcs ret = qls_alloc_dmabuf(ha, &ha->rx_ring[ridx].sbq_dma); 1639252206Sdavidcs 1640252206Sdavidcs if (ret) { 1641252206Sdavidcs device_printf(dev, "%s: [%d] sbq failed\n", __func__, ridx); 1642252206Sdavidcs goto qls_alloc_rx_ring_dma_exit; 1643252206Sdavidcs } 1644252206Sdavidcs ha->rx_ring[ridx].flags.sbq_dma = 1; 1645252206Sdavidcs 1646252206Sdavidcs ha->rx_ring[ridx].lb_dma.alignment = 8; 1647252206Sdavidcs ha->rx_ring[ridx].lb_dma.size = (QLA_LGB_SIZE * QLA_NUM_LGB_ENTRIES); 1648252206Sdavidcs 1649252206Sdavidcs ret = qls_alloc_dmabuf(ha, &ha->rx_ring[ridx].lb_dma); 1650252206Sdavidcs if (ret) { 1651252206Sdavidcs device_printf(dev, "%s: [%d] lb failed\n", __func__, ridx); 1652252206Sdavidcs goto qls_alloc_rx_ring_dma_exit; 1653252206Sdavidcs } 1654252206Sdavidcs ha->rx_ring[ridx].flags.lb_dma = 1; 1655252206Sdavidcs 1656252206Sdavidcs bzero(ha->rx_ring[ridx].cq_dma.dma_b, ha->rx_ring[ridx].cq_dma.size); 1657252206Sdavidcs bzero(ha->rx_ring[ridx].lbq_dma.dma_b, ha->rx_ring[ridx].lbq_dma.size); 1658252206Sdavidcs bzero(ha->rx_ring[ridx].sbq_dma.dma_b, ha->rx_ring[ridx].sbq_dma.size); 1659252206Sdavidcs bzero(ha->rx_ring[ridx].lb_dma.dma_b, ha->rx_ring[ridx].lb_dma.size); 1660252206Sdavidcs 1661252206Sdavidcs /* completion queue */ 1662252206Sdavidcs ha->rx_ring[ridx].cq_base_vaddr = ha->rx_ring[ridx].cq_dma.dma_b; 1663252206Sdavidcs ha->rx_ring[ridx].cq_base_paddr = ha->rx_ring[ridx].cq_dma.dma_addr; 1664252206Sdavidcs 1665252206Sdavidcs v_addr = ha->rx_ring[ridx].cq_dma.dma_b; 1666252206Sdavidcs p_addr = ha->rx_ring[ridx].cq_dma.dma_addr; 1667252206Sdavidcs 1668252206Sdavidcs v_addr = v_addr + (NUM_CQ_ENTRIES * (sizeof (q81_cq_e_t))); 1669252206Sdavidcs p_addr = p_addr + (NUM_CQ_ENTRIES * (sizeof (q81_cq_e_t))); 1670252206Sdavidcs 1671252206Sdavidcs /* completion queue icb */ 1672252206Sdavidcs ha->rx_ring[ridx].cq_icb_vaddr = v_addr; 1673252206Sdavidcs ha->rx_ring[ridx].cq_icb_paddr = p_addr; 1674252206Sdavidcs 1675252206Sdavidcs v_addr = v_addr + (PAGE_SIZE >> 2); 1676252206Sdavidcs p_addr = p_addr + (PAGE_SIZE >> 2); 1677252206Sdavidcs 1678252206Sdavidcs /* completion queue index register */ 1679252206Sdavidcs ha->rx_ring[ridx].cqi_vaddr = (uint32_t *)v_addr; 1680252206Sdavidcs ha->rx_ring[ridx].cqi_paddr = p_addr; 1681252206Sdavidcs 1682252206Sdavidcs v_addr = ha->rx_ring[ridx].lbq_dma.dma_b; 1683252206Sdavidcs p_addr = ha->rx_ring[ridx].lbq_dma.dma_addr; 1684252206Sdavidcs 1685252206Sdavidcs /* large buffer queue address table */ 1686252206Sdavidcs ha->rx_ring[ridx].lbq_addr_tbl_vaddr = v_addr; 1687252206Sdavidcs ha->rx_ring[ridx].lbq_addr_tbl_paddr = p_addr; 1688252206Sdavidcs 1689252206Sdavidcs /* large buffer queue */ 1690252206Sdavidcs ha->rx_ring[ridx].lbq_vaddr = v_addr + PAGE_SIZE; 1691252206Sdavidcs ha->rx_ring[ridx].lbq_paddr = p_addr + PAGE_SIZE; 1692252206Sdavidcs 1693252206Sdavidcs v_addr = ha->rx_ring[ridx].sbq_dma.dma_b; 1694252206Sdavidcs p_addr = ha->rx_ring[ridx].sbq_dma.dma_addr; 1695252206Sdavidcs 1696252206Sdavidcs /* small buffer queue address table */ 1697252206Sdavidcs ha->rx_ring[ridx].sbq_addr_tbl_vaddr = v_addr; 1698252206Sdavidcs ha->rx_ring[ridx].sbq_addr_tbl_paddr = p_addr; 1699252206Sdavidcs 1700252206Sdavidcs /* small buffer queue */ 1701252206Sdavidcs ha->rx_ring[ridx].sbq_vaddr = v_addr + PAGE_SIZE; 1702252206Sdavidcs ha->rx_ring[ridx].sbq_paddr = p_addr + PAGE_SIZE; 1703252206Sdavidcs 1704252206Sdavidcs ha->rx_ring[ridx].lb_vaddr = ha->rx_ring[ridx].lb_dma.dma_b; 1705252206Sdavidcs ha->rx_ring[ridx].lb_paddr = ha->rx_ring[ridx].lb_dma.dma_addr; 1706252206Sdavidcs 1707252206Sdavidcs /* Initialize Large Buffer Queue Table */ 1708252206Sdavidcs 1709252206Sdavidcs p_addr = ha->rx_ring[ridx].lbq_paddr; 1710252206Sdavidcs bq_e = ha->rx_ring[ridx].lbq_addr_tbl_vaddr; 1711252206Sdavidcs 1712252206Sdavidcs bq_e->addr_lo = p_addr & 0xFFFFFFFF; 1713252206Sdavidcs bq_e->addr_hi = (p_addr >> 32) & 0xFFFFFFFF; 1714252206Sdavidcs 1715252206Sdavidcs p_addr = ha->rx_ring[ridx].lb_paddr; 1716252206Sdavidcs bq_e = ha->rx_ring[ridx].lbq_vaddr; 1717252206Sdavidcs 1718252206Sdavidcs for (i = 0; i < QLA_NUM_LGB_ENTRIES; i++) { 1719252206Sdavidcs bq_e->addr_lo = p_addr & 0xFFFFFFFF; 1720252206Sdavidcs bq_e->addr_hi = (p_addr >> 32) & 0xFFFFFFFF; 1721252206Sdavidcs 1722252206Sdavidcs p_addr = p_addr + QLA_LGB_SIZE; 1723252206Sdavidcs bq_e++; 1724252206Sdavidcs } 1725252206Sdavidcs 1726252206Sdavidcs /* Initialize Small Buffer Queue Table */ 1727252206Sdavidcs 1728252206Sdavidcs p_addr = ha->rx_ring[ridx].sbq_paddr; 1729252206Sdavidcs bq_e = ha->rx_ring[ridx].sbq_addr_tbl_vaddr; 1730252206Sdavidcs 1731252206Sdavidcs for (i =0; i < (QLA_SBQ_SIZE/QLA_PAGE_SIZE); i++) { 1732252206Sdavidcs bq_e->addr_lo = p_addr & 0xFFFFFFFF; 1733252206Sdavidcs bq_e->addr_hi = (p_addr >> 32) & 0xFFFFFFFF; 1734252206Sdavidcs 1735252206Sdavidcs p_addr = p_addr + QLA_PAGE_SIZE; 1736252206Sdavidcs bq_e++; 1737252206Sdavidcs } 1738252206Sdavidcs 1739252206Sdavidcsqls_alloc_rx_ring_dma_exit: 1740252206Sdavidcs return (ret); 1741252206Sdavidcs} 1742252206Sdavidcs 1743252206Sdavidcsstatic int 1744252206Sdavidcsqls_alloc_rx_dma(qla_host_t *ha) 1745252206Sdavidcs{ 1746252206Sdavidcs int i; 1747252206Sdavidcs int ret = 0; 1748252206Sdavidcs 1749252206Sdavidcs if (bus_dma_tag_create(NULL, /* parent */ 1750252206Sdavidcs 1, 0, /* alignment, bounds */ 1751252206Sdavidcs BUS_SPACE_MAXADDR, /* lowaddr */ 1752252206Sdavidcs BUS_SPACE_MAXADDR, /* highaddr */ 1753252206Sdavidcs NULL, NULL, /* filter, filterarg */ 1754252206Sdavidcs MJUM9BYTES, /* maxsize */ 1755252206Sdavidcs 1, /* nsegments */ 1756252206Sdavidcs MJUM9BYTES, /* maxsegsize */ 1757252206Sdavidcs BUS_DMA_ALLOCNOW, /* flags */ 1758252206Sdavidcs NULL, /* lockfunc */ 1759252206Sdavidcs NULL, /* lockfuncarg */ 1760252206Sdavidcs &ha->rx_tag)) { 1761252206Sdavidcs 1762252206Sdavidcs device_printf(ha->pci_dev, "%s: rx_tag alloc failed\n", 1763252206Sdavidcs __func__); 1764252206Sdavidcs 1765252206Sdavidcs return (ENOMEM); 1766252206Sdavidcs } 1767252206Sdavidcs 1768252206Sdavidcs for (i = 0; i < ha->num_rx_rings; i++) { 1769252206Sdavidcs ret = qls_alloc_rx_ring_dma(ha, i); 1770252206Sdavidcs 1771252206Sdavidcs if (ret) { 1772252206Sdavidcs qls_free_rx_dma(ha); 1773252206Sdavidcs break; 1774252206Sdavidcs } 1775252206Sdavidcs } 1776252206Sdavidcs 1777252206Sdavidcs return (ret); 1778252206Sdavidcs} 1779252206Sdavidcs 1780252206Sdavidcsstatic int 1781252206Sdavidcsqls_wait_for_flash_ready(qla_host_t *ha) 1782252206Sdavidcs{ 1783252206Sdavidcs uint32_t data32; 1784252206Sdavidcs uint32_t count = 3; 1785252206Sdavidcs 1786252206Sdavidcs while (count--) { 1787252206Sdavidcs 1788252206Sdavidcs data32 = READ_REG32(ha, Q81_CTL_FLASH_ADDR); 1789252206Sdavidcs 1790252206Sdavidcs if (data32 & Q81_CTL_FLASH_ADDR_ERR) 1791252206Sdavidcs goto qls_wait_for_flash_ready_exit; 1792252206Sdavidcs 1793252206Sdavidcs if (data32 & Q81_CTL_FLASH_ADDR_RDY) 1794252206Sdavidcs return (0); 1795252206Sdavidcs 1796252206Sdavidcs QLA_USEC_DELAY(100); 1797252206Sdavidcs } 1798252206Sdavidcs 1799252206Sdavidcsqls_wait_for_flash_ready_exit: 1800252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: failed\n", __func__)); 1801252206Sdavidcs 1802252206Sdavidcs return (-1); 1803252206Sdavidcs} 1804252206Sdavidcs 1805252206Sdavidcs/* 1806252206Sdavidcs * Name: qls_rd_flash32 1807252206Sdavidcs * Function: Read Flash Memory 1808252206Sdavidcs */ 1809252206Sdavidcsint 1810252206Sdavidcsqls_rd_flash32(qla_host_t *ha, uint32_t addr, uint32_t *data) 1811252206Sdavidcs{ 1812252206Sdavidcs int ret; 1813252206Sdavidcs 1814252206Sdavidcs ret = qls_wait_for_flash_ready(ha); 1815252206Sdavidcs 1816252206Sdavidcs if (ret) 1817252206Sdavidcs return (ret); 1818252206Sdavidcs 1819252206Sdavidcs WRITE_REG32(ha, Q81_CTL_FLASH_ADDR, (addr | Q81_CTL_FLASH_ADDR_R)); 1820252206Sdavidcs 1821252206Sdavidcs ret = qls_wait_for_flash_ready(ha); 1822252206Sdavidcs 1823252206Sdavidcs if (ret) 1824252206Sdavidcs return (ret); 1825252206Sdavidcs 1826252206Sdavidcs *data = READ_REG32(ha, Q81_CTL_FLASH_DATA); 1827252206Sdavidcs 1828252206Sdavidcs return 0; 1829252206Sdavidcs} 1830252206Sdavidcs 1831252206Sdavidcsstatic int 1832252206Sdavidcsqls_flash_validate(qla_host_t *ha, const char *signature) 1833252206Sdavidcs{ 1834252206Sdavidcs uint16_t csum16 = 0; 1835252206Sdavidcs uint16_t *data16; 1836252206Sdavidcs int i; 1837252206Sdavidcs 1838252206Sdavidcs if (bcmp(ha->flash.id, signature, 4)) { 1839252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: invalid signature " 1840252206Sdavidcs "%x:%x:%x:%x %s\n", __func__, ha->flash.id[0], 1841252206Sdavidcs ha->flash.id[1], ha->flash.id[2], ha->flash.id[3], 1842252206Sdavidcs signature)); 1843252206Sdavidcs return(-1); 1844252206Sdavidcs } 1845252206Sdavidcs 1846252206Sdavidcs data16 = (uint16_t *)&ha->flash; 1847252206Sdavidcs 1848252206Sdavidcs for (i = 0; i < (sizeof (q81_flash_t) >> 1); i++) { 1849252206Sdavidcs csum16 += *data16++; 1850252206Sdavidcs } 1851252206Sdavidcs 1852252206Sdavidcs if (csum16) { 1853252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: invalid checksum\n", __func__)); 1854252206Sdavidcs return(-1); 1855252206Sdavidcs } 1856252206Sdavidcs return(0); 1857252206Sdavidcs} 1858252206Sdavidcs 1859252206Sdavidcsint 1860252206Sdavidcsqls_rd_nic_params(qla_host_t *ha) 1861252206Sdavidcs{ 1862252206Sdavidcs int i, ret = 0; 1863252206Sdavidcs uint32_t faddr; 1864252206Sdavidcs uint32_t *qflash; 1865252206Sdavidcs 1866252206Sdavidcs if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_FLASH, Q81_CTL_SEM_SET_FLASH)) { 1867252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__)); 1868252206Sdavidcs return(-1); 1869252206Sdavidcs } 1870252206Sdavidcs 1871252206Sdavidcs if ((ha->pci_func & 0x1) == 0) 1872252206Sdavidcs faddr = Q81_F0_FLASH_OFFSET >> 2; 1873252206Sdavidcs else 1874252206Sdavidcs faddr = Q81_F1_FLASH_OFFSET >> 2; 1875252206Sdavidcs 1876252206Sdavidcs qflash = (uint32_t *)&ha->flash; 1877252206Sdavidcs 1878252206Sdavidcs for (i = 0; i < (sizeof(q81_flash_t) >> 2) ; i++) { 1879252206Sdavidcs 1880252206Sdavidcs ret = qls_rd_flash32(ha, faddr, qflash); 1881252206Sdavidcs 1882252206Sdavidcs if (ret) 1883252206Sdavidcs goto qls_rd_flash_data_exit; 1884252206Sdavidcs 1885252206Sdavidcs faddr++; 1886252206Sdavidcs qflash++; 1887252206Sdavidcs } 1888252206Sdavidcs 1889252206Sdavidcs QL_DUMP_BUFFER8(ha, __func__, (&ha->flash), (sizeof (q81_flash_t))); 1890252206Sdavidcs 1891252206Sdavidcs ret = qls_flash_validate(ha, Q81_FLASH_ID); 1892252206Sdavidcs 1893252206Sdavidcs if (ret) 1894252206Sdavidcs goto qls_rd_flash_data_exit; 1895252206Sdavidcs 1896252206Sdavidcs bcopy(ha->flash.mac_addr0, ha->mac_addr, ETHER_ADDR_LEN); 1897252206Sdavidcs 1898252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: mac %02x:%02x:%02x:%02x:%02x:%02x\n", 1899252206Sdavidcs __func__, ha->mac_addr[0], ha->mac_addr[1], ha->mac_addr[2], 1900252206Sdavidcs ha->mac_addr[3], ha->mac_addr[4], ha->mac_addr[5])); 1901252206Sdavidcs 1902252206Sdavidcsqls_rd_flash_data_exit: 1903252206Sdavidcs 1904252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_FLASH); 1905252206Sdavidcs 1906252206Sdavidcs return(ret); 1907252206Sdavidcs} 1908252206Sdavidcs 1909252206Sdavidcsstatic int 1910252206Sdavidcsqls_sem_lock(qla_host_t *ha, uint32_t mask, uint32_t value) 1911252206Sdavidcs{ 1912252206Sdavidcs uint32_t count = 30; 1913252206Sdavidcs uint32_t data; 1914252206Sdavidcs 1915252206Sdavidcs while (count--) { 1916252206Sdavidcs WRITE_REG32(ha, Q81_CTL_SEMAPHORE, (mask|value)); 1917252206Sdavidcs 1918252206Sdavidcs data = READ_REG32(ha, Q81_CTL_SEMAPHORE); 1919252206Sdavidcs 1920252206Sdavidcs if (data & value) { 1921252206Sdavidcs return (0); 1922252206Sdavidcs } else { 1923252206Sdavidcs QLA_USEC_DELAY(100); 1924252206Sdavidcs } 1925252206Sdavidcs } 1926252206Sdavidcs ha->qla_initiate_recovery = 1; 1927252206Sdavidcs return (-1); 1928252206Sdavidcs} 1929252206Sdavidcs 1930252206Sdavidcsstatic void 1931252206Sdavidcsqls_sem_unlock(qla_host_t *ha, uint32_t mask) 1932252206Sdavidcs{ 1933252206Sdavidcs WRITE_REG32(ha, Q81_CTL_SEMAPHORE, mask); 1934252206Sdavidcs} 1935252206Sdavidcs 1936252206Sdavidcsstatic int 1937252206Sdavidcsqls_wait_for_proc_addr_ready(qla_host_t *ha) 1938252206Sdavidcs{ 1939252206Sdavidcs uint32_t data32; 1940252206Sdavidcs uint32_t count = 3; 1941252206Sdavidcs 1942252206Sdavidcs while (count--) { 1943252206Sdavidcs 1944252206Sdavidcs data32 = READ_REG32(ha, Q81_CTL_PROC_ADDR); 1945252206Sdavidcs 1946252206Sdavidcs if (data32 & Q81_CTL_PROC_ADDR_ERR) 1947252206Sdavidcs goto qls_wait_for_proc_addr_ready_exit; 1948252206Sdavidcs 1949252206Sdavidcs if (data32 & Q81_CTL_PROC_ADDR_RDY) 1950252206Sdavidcs return (0); 1951252206Sdavidcs 1952252206Sdavidcs QLA_USEC_DELAY(100); 1953252206Sdavidcs } 1954252206Sdavidcs 1955252206Sdavidcsqls_wait_for_proc_addr_ready_exit: 1956252206Sdavidcs QL_DPRINT1((ha->pci_dev, "%s: failed\n", __func__)); 1957252206Sdavidcs 1958252206Sdavidcs ha->qla_initiate_recovery = 1; 1959252206Sdavidcs return (-1); 1960252206Sdavidcs} 1961252206Sdavidcs 1962252206Sdavidcsstatic int 1963252206Sdavidcsqls_proc_addr_rd_reg(qla_host_t *ha, uint32_t addr_module, uint32_t reg, 1964252206Sdavidcs uint32_t *data) 1965252206Sdavidcs{ 1966252206Sdavidcs int ret; 1967252206Sdavidcs uint32_t value; 1968252206Sdavidcs 1969252206Sdavidcs ret = qls_wait_for_proc_addr_ready(ha); 1970252206Sdavidcs 1971252206Sdavidcs if (ret) 1972252206Sdavidcs goto qls_proc_addr_rd_reg_exit; 1973252206Sdavidcs 1974252206Sdavidcs value = addr_module | reg | Q81_CTL_PROC_ADDR_READ; 1975252206Sdavidcs 1976252206Sdavidcs WRITE_REG32(ha, Q81_CTL_PROC_ADDR, value); 1977252206Sdavidcs 1978252206Sdavidcs ret = qls_wait_for_proc_addr_ready(ha); 1979252206Sdavidcs 1980252206Sdavidcs if (ret) 1981252206Sdavidcs goto qls_proc_addr_rd_reg_exit; 1982252206Sdavidcs 1983252206Sdavidcs *data = READ_REG32(ha, Q81_CTL_PROC_DATA); 1984252206Sdavidcs 1985252206Sdavidcsqls_proc_addr_rd_reg_exit: 1986252206Sdavidcs return (ret); 1987252206Sdavidcs} 1988252206Sdavidcs 1989252206Sdavidcsstatic int 1990252206Sdavidcsqls_proc_addr_wr_reg(qla_host_t *ha, uint32_t addr_module, uint32_t reg, 1991252206Sdavidcs uint32_t data) 1992252206Sdavidcs{ 1993252206Sdavidcs int ret; 1994252206Sdavidcs uint32_t value; 1995252206Sdavidcs 1996252206Sdavidcs ret = qls_wait_for_proc_addr_ready(ha); 1997252206Sdavidcs 1998252206Sdavidcs if (ret) 1999252206Sdavidcs goto qls_proc_addr_wr_reg_exit; 2000252206Sdavidcs 2001252206Sdavidcs WRITE_REG32(ha, Q81_CTL_PROC_DATA, data); 2002252206Sdavidcs 2003252206Sdavidcs value = addr_module | reg; 2004252206Sdavidcs 2005252206Sdavidcs WRITE_REG32(ha, Q81_CTL_PROC_ADDR, value); 2006252206Sdavidcs 2007252206Sdavidcs ret = qls_wait_for_proc_addr_ready(ha); 2008252206Sdavidcs 2009252206Sdavidcsqls_proc_addr_wr_reg_exit: 2010252206Sdavidcs return (ret); 2011252206Sdavidcs} 2012252206Sdavidcs 2013252206Sdavidcsstatic int 2014252206Sdavidcsqls_hw_nic_reset(qla_host_t *ha) 2015252206Sdavidcs{ 2016252206Sdavidcs int count; 2017252206Sdavidcs uint32_t data; 2018252206Sdavidcs device_t dev = ha->pci_dev; 2019252206Sdavidcs 2020252206Sdavidcs ha->hw_init = 0; 2021252206Sdavidcs 2022252206Sdavidcs data = (Q81_CTL_RESET_FUNC << Q81_CTL_RESET_MASK_SHIFT) | 2023252206Sdavidcs Q81_CTL_RESET_FUNC; 2024252206Sdavidcs WRITE_REG32(ha, Q81_CTL_RESET, data); 2025252206Sdavidcs 2026252206Sdavidcs count = 10; 2027252206Sdavidcs while (count--) { 2028252206Sdavidcs data = READ_REG32(ha, Q81_CTL_RESET); 2029252206Sdavidcs if ((data & Q81_CTL_RESET_FUNC) == 0) 2030252206Sdavidcs break; 2031252206Sdavidcs QLA_USEC_DELAY(10); 2032252206Sdavidcs } 2033252206Sdavidcs if (count == 0) { 2034252206Sdavidcs device_printf(dev, "%s: Bit 15 not cleared after Reset\n", 2035252206Sdavidcs __func__); 2036252206Sdavidcs return (-1); 2037252206Sdavidcs } 2038252206Sdavidcs return (0); 2039252206Sdavidcs} 2040252206Sdavidcs 2041252206Sdavidcsstatic int 2042252206Sdavidcsqls_hw_reset(qla_host_t *ha) 2043252206Sdavidcs{ 2044252206Sdavidcs device_t dev = ha->pci_dev; 2045252206Sdavidcs int ret; 2046252206Sdavidcs int count; 2047252206Sdavidcs uint32_t data; 2048252206Sdavidcs 2049252206Sdavidcs QL_DPRINT2((ha->pci_dev, "%s:enter[%d]\n", __func__, ha->hw_init)); 2050252206Sdavidcs 2051252206Sdavidcs if (ha->hw_init == 0) { 2052252206Sdavidcs ret = qls_hw_nic_reset(ha); 2053252206Sdavidcs goto qls_hw_reset_exit; 2054252206Sdavidcs } 2055252206Sdavidcs 2056252206Sdavidcs ret = qls_clear_routing_table(ha); 2057252206Sdavidcs if (ret) 2058252206Sdavidcs goto qls_hw_reset_exit; 2059252206Sdavidcs 2060252206Sdavidcs ret = qls_mbx_set_mgmt_ctrl(ha, Q81_MBX_SET_MGMT_CTL_STOP); 2061252206Sdavidcs if (ret) 2062252206Sdavidcs goto qls_hw_reset_exit; 2063252206Sdavidcs 2064252206Sdavidcs /* 2065252206Sdavidcs * Wait for FIFO to empty 2066252206Sdavidcs */ 2067252206Sdavidcs count = 5; 2068252206Sdavidcs while (count--) { 2069252206Sdavidcs data = READ_REG32(ha, Q81_CTL_STATUS); 2070252206Sdavidcs if (data & Q81_CTL_STATUS_NFE) 2071252206Sdavidcs break; 2072252206Sdavidcs qls_mdelay(__func__, 100); 2073252206Sdavidcs } 2074252206Sdavidcs if (count == 0) { 2075252206Sdavidcs device_printf(dev, "%s: NFE bit not set\n", __func__); 2076252206Sdavidcs goto qls_hw_reset_exit; 2077252206Sdavidcs } 2078252206Sdavidcs 2079252206Sdavidcs count = 5; 2080252206Sdavidcs while (count--) { 2081252206Sdavidcs (void)qls_mbx_get_mgmt_ctrl(ha, &data); 2082252206Sdavidcs 2083252206Sdavidcs if ((data & Q81_MBX_GET_MGMT_CTL_FIFO_EMPTY) && 2084252206Sdavidcs (data & Q81_MBX_GET_MGMT_CTL_SET_MGMT)) 2085252206Sdavidcs break; 2086252206Sdavidcs qls_mdelay(__func__, 100); 2087252206Sdavidcs } 2088252206Sdavidcs if (count == 0) 2089252206Sdavidcs goto qls_hw_reset_exit; 2090252206Sdavidcs 2091252206Sdavidcs /* 2092252206Sdavidcs * Reset the NIC function 2093252206Sdavidcs */ 2094252206Sdavidcs ret = qls_hw_nic_reset(ha); 2095252206Sdavidcs if (ret) 2096252206Sdavidcs goto qls_hw_reset_exit; 2097252206Sdavidcs 2098252206Sdavidcs ret = qls_mbx_set_mgmt_ctrl(ha, Q81_MBX_SET_MGMT_CTL_RESUME); 2099252206Sdavidcs 2100252206Sdavidcsqls_hw_reset_exit: 2101252206Sdavidcs if (ret) 2102252206Sdavidcs device_printf(dev, "%s: failed\n", __func__); 2103252206Sdavidcs 2104252206Sdavidcs return (ret); 2105252206Sdavidcs} 2106252206Sdavidcs 2107252206Sdavidcs/* 2108252206Sdavidcs * MPI Related Functions 2109252206Sdavidcs */ 2110252206Sdavidcsint 2111252206Sdavidcsqls_mpi_risc_rd_reg(qla_host_t *ha, uint32_t reg, uint32_t *data) 2112252206Sdavidcs{ 2113252206Sdavidcs int ret; 2114252206Sdavidcs 2115252206Sdavidcs ret = qls_proc_addr_rd_reg(ha, Q81_CTL_PROC_ADDR_MPI_RISC, 2116252206Sdavidcs reg, data); 2117252206Sdavidcs return (ret); 2118252206Sdavidcs} 2119252206Sdavidcs 2120252206Sdavidcsint 2121252206Sdavidcsqls_mpi_risc_wr_reg(qla_host_t *ha, uint32_t reg, uint32_t data) 2122252206Sdavidcs{ 2123252206Sdavidcs int ret; 2124252206Sdavidcs 2125252206Sdavidcs ret = qls_proc_addr_wr_reg(ha, Q81_CTL_PROC_ADDR_MPI_RISC, 2126252206Sdavidcs reg, data); 2127252206Sdavidcs return (ret); 2128252206Sdavidcs} 2129252206Sdavidcs 2130252206Sdavidcsint 2131252206Sdavidcsqls_mbx_rd_reg(qla_host_t *ha, uint32_t reg, uint32_t *data) 2132252206Sdavidcs{ 2133252206Sdavidcs int ret; 2134252206Sdavidcs 2135252206Sdavidcs if ((ha->pci_func & 0x1) == 0) 2136252206Sdavidcs reg += Q81_FUNC0_MBX_OUT_REG0; 2137252206Sdavidcs else 2138252206Sdavidcs reg += Q81_FUNC1_MBX_OUT_REG0; 2139252206Sdavidcs 2140252206Sdavidcs ret = qls_mpi_risc_rd_reg(ha, reg, data); 2141252206Sdavidcs 2142252206Sdavidcs return (ret); 2143252206Sdavidcs} 2144252206Sdavidcs 2145252206Sdavidcsint 2146252206Sdavidcsqls_mbx_wr_reg(qla_host_t *ha, uint32_t reg, uint32_t data) 2147252206Sdavidcs{ 2148252206Sdavidcs int ret; 2149252206Sdavidcs 2150252206Sdavidcs if ((ha->pci_func & 0x1) == 0) 2151252206Sdavidcs reg += Q81_FUNC0_MBX_IN_REG0; 2152252206Sdavidcs else 2153252206Sdavidcs reg += Q81_FUNC1_MBX_IN_REG0; 2154252206Sdavidcs 2155252206Sdavidcs ret = qls_mpi_risc_wr_reg(ha, reg, data); 2156252206Sdavidcs 2157252206Sdavidcs return (ret); 2158252206Sdavidcs} 2159252206Sdavidcs 2160252206Sdavidcs 2161252206Sdavidcsstatic int 2162252206Sdavidcsqls_mbx_cmd(qla_host_t *ha, uint32_t *in_mbx, uint32_t i_count, 2163252206Sdavidcs uint32_t *out_mbx, uint32_t o_count) 2164252206Sdavidcs{ 2165252206Sdavidcs int i, ret = -1; 2166252206Sdavidcs uint32_t data32, mbx_cmd = 0; 2167252206Sdavidcs uint32_t count = 50; 2168252206Sdavidcs 2169252206Sdavidcs QL_DPRINT2((ha->pci_dev, "%s: enter[0x%08x 0x%08x 0x%08x]\n", 2170252206Sdavidcs __func__, *in_mbx, *(in_mbx + 1), *(in_mbx + 2))); 2171252206Sdavidcs 2172252206Sdavidcs data32 = READ_REG32(ha, Q81_CTL_HOST_CMD_STATUS); 2173252206Sdavidcs 2174252206Sdavidcs if (data32 & Q81_CTL_HCS_HTR_INTR) { 2175252206Sdavidcs device_printf(ha->pci_dev, "%s: cmd_status[0x%08x]\n", 2176252206Sdavidcs __func__, data32); 2177252206Sdavidcs goto qls_mbx_cmd_exit; 2178252206Sdavidcs } 2179252206Sdavidcs 2180252206Sdavidcs if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_PROC_ADDR_NIC_RCV, 2181252206Sdavidcs Q81_CTL_SEM_SET_PROC_ADDR_NIC_RCV)) { 2182252206Sdavidcs device_printf(ha->pci_dev, "%s: semlock failed\n", __func__); 2183252206Sdavidcs goto qls_mbx_cmd_exit; 2184252206Sdavidcs } 2185252206Sdavidcs 2186252206Sdavidcs ha->mbx_done = 0; 2187252206Sdavidcs 2188252206Sdavidcs mbx_cmd = *in_mbx; 2189252206Sdavidcs 2190252206Sdavidcs for (i = 0; i < i_count; i++) { 2191252206Sdavidcs 2192252206Sdavidcs ret = qls_mbx_wr_reg(ha, i, *in_mbx); 2193252206Sdavidcs 2194252206Sdavidcs if (ret) { 2195252206Sdavidcs device_printf(ha->pci_dev, 2196252206Sdavidcs "%s: mbx_wr[%d, 0x%08x] failed\n", __func__, 2197252206Sdavidcs i, *in_mbx); 2198252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_PROC_ADDR_NIC_RCV); 2199252206Sdavidcs goto qls_mbx_cmd_exit; 2200252206Sdavidcs } 2201252206Sdavidcs 2202252206Sdavidcs in_mbx++; 2203252206Sdavidcs } 2204252206Sdavidcs WRITE_REG32(ha, Q81_CTL_HOST_CMD_STATUS, Q81_CTL_HCS_CMD_SET_HTR_INTR); 2205252206Sdavidcs 2206252206Sdavidcs qls_sem_unlock(ha, Q81_CTL_SEM_MASK_PROC_ADDR_NIC_RCV); 2207252206Sdavidcs 2208252206Sdavidcs ret = -1; 2209252206Sdavidcs ha->mbx_done = 0; 2210252206Sdavidcs 2211252206Sdavidcs while (count--) { 2212252206Sdavidcs 2213252206Sdavidcs if (ha->flags.intr_enable == 0) { 2214252206Sdavidcs data32 = READ_REG32(ha, Q81_CTL_STATUS); 2215252206Sdavidcs 2216252206Sdavidcs if (!(data32 & Q81_CTL_STATUS_PI)) { 2217252206Sdavidcs qls_mdelay(__func__, 100); 2218252206Sdavidcs continue; 2219252206Sdavidcs } 2220252206Sdavidcs 2221252206Sdavidcs ret = qls_mbx_rd_reg(ha, 0, &data32); 2222252206Sdavidcs 2223252206Sdavidcs if (ret == 0 ) { 2224252206Sdavidcs if ((data32 & 0xF000) == 0x4000) { 2225252206Sdavidcs 2226252206Sdavidcs out_mbx[0] = data32; 2227252206Sdavidcs 2228252206Sdavidcs for (i = 1; i < o_count; i++) { 2229252206Sdavidcs ret = qls_mbx_rd_reg(ha, i, 2230252206Sdavidcs &data32); 2231252206Sdavidcs if (ret) { 2232252206Sdavidcs device_printf( 2233252206Sdavidcs ha->pci_dev, 2234252206Sdavidcs "%s: mbx_rd[%d]" 2235252206Sdavidcs " failed\n", 2236252206Sdavidcs __func__, i); 2237252206Sdavidcs break; 2238252206Sdavidcs } 2239252206Sdavidcs out_mbx[i] = data32; 2240252206Sdavidcs } 2241252206Sdavidcs break; 2242252206Sdavidcs } else if ((data32 & 0xF000) == 0x8000) { 2243252206Sdavidcs count = 50; 2244252206Sdavidcs WRITE_REG32(ha,\ 2245252206Sdavidcs Q81_CTL_HOST_CMD_STATUS,\ 2246252206Sdavidcs Q81_CTL_HCS_CMD_CLR_RTH_INTR); 2247252206Sdavidcs } 2248252206Sdavidcs } 2249252206Sdavidcs } else { 2250252206Sdavidcs if (ha->mbx_done) { 2251252206Sdavidcs for (i = 1; i < o_count; i++) { 2252252206Sdavidcs out_mbx[i] = ha->mbox[i]; 2253252206Sdavidcs } 2254252206Sdavidcs ret = 0; 2255252206Sdavidcs break; 2256252206Sdavidcs } 2257252206Sdavidcs } 2258252206Sdavidcs qls_mdelay(__func__, 1000); 2259252206Sdavidcs } 2260252206Sdavidcs 2261252206Sdavidcsqls_mbx_cmd_exit: 2262252206Sdavidcs 2263252206Sdavidcs if (ha->flags.intr_enable == 0) { 2264252206Sdavidcs WRITE_REG32(ha, Q81_CTL_HOST_CMD_STATUS,\ 2265252206Sdavidcs Q81_CTL_HCS_CMD_CLR_RTH_INTR); 2266252206Sdavidcs } 2267252206Sdavidcs 2268252206Sdavidcs if (ret) { 2269252206Sdavidcs ha->qla_initiate_recovery = 1; 2270252206Sdavidcs } 2271252206Sdavidcs 2272252206Sdavidcs QL_DPRINT2((ha->pci_dev, "%s: exit[%d]\n", __func__, ret)); 2273252206Sdavidcs return (ret); 2274252206Sdavidcs} 2275252206Sdavidcs 2276252206Sdavidcsstatic int 2277252206Sdavidcsqls_mbx_set_mgmt_ctrl(qla_host_t *ha, uint32_t t_ctrl) 2278252206Sdavidcs{ 2279252206Sdavidcs uint32_t *mbox; 2280252206Sdavidcs device_t dev = ha->pci_dev; 2281252206Sdavidcs 2282252206Sdavidcs mbox = ha->mbox; 2283252206Sdavidcs bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS)); 2284252206Sdavidcs 2285252206Sdavidcs mbox[0] = Q81_MBX_SET_MGMT_CTL; 2286252206Sdavidcs mbox[1] = t_ctrl; 2287252206Sdavidcs 2288252206Sdavidcs if (qls_mbx_cmd(ha, mbox, 2, mbox, 1)) { 2289252206Sdavidcs device_printf(dev, "%s failed\n", __func__); 2290252206Sdavidcs return (-1); 2291252206Sdavidcs } 2292252206Sdavidcs 2293252206Sdavidcs if ((mbox[0] == Q81_MBX_CMD_COMPLETE) || 2294252206Sdavidcs ((t_ctrl == Q81_MBX_SET_MGMT_CTL_STOP) && 2295252206Sdavidcs (mbox[0] == Q81_MBX_CMD_ERROR))){ 2296252206Sdavidcs return (0); 2297252206Sdavidcs } 2298252206Sdavidcs device_printf(dev, "%s failed [0x%08x]\n", __func__, mbox[0]); 2299252206Sdavidcs return (-1); 2300252206Sdavidcs 2301252206Sdavidcs} 2302252206Sdavidcs 2303252206Sdavidcsstatic int 2304252206Sdavidcsqls_mbx_get_mgmt_ctrl(qla_host_t *ha, uint32_t *t_status) 2305252206Sdavidcs{ 2306252206Sdavidcs uint32_t *mbox; 2307252206Sdavidcs device_t dev = ha->pci_dev; 2308252206Sdavidcs 2309252206Sdavidcs *t_status = 0; 2310252206Sdavidcs 2311252206Sdavidcs mbox = ha->mbox; 2312252206Sdavidcs bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS)); 2313252206Sdavidcs 2314252206Sdavidcs mbox[0] = Q81_MBX_GET_MGMT_CTL; 2315252206Sdavidcs 2316252206Sdavidcs if (qls_mbx_cmd(ha, mbox, 1, mbox, 2)) { 2317252206Sdavidcs device_printf(dev, "%s failed\n", __func__); 2318252206Sdavidcs return (-1); 2319252206Sdavidcs } 2320252206Sdavidcs 2321252206Sdavidcs *t_status = mbox[1]; 2322252206Sdavidcs 2323252206Sdavidcs return (0); 2324252206Sdavidcs} 2325252206Sdavidcs 2326252206Sdavidcsstatic void 2327252206Sdavidcsqls_mbx_get_link_status(qla_host_t *ha) 2328252206Sdavidcs{ 2329252206Sdavidcs uint32_t *mbox; 2330252206Sdavidcs device_t dev = ha->pci_dev; 2331252206Sdavidcs 2332252206Sdavidcs mbox = ha->mbox; 2333252206Sdavidcs bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS)); 2334252206Sdavidcs 2335252206Sdavidcs mbox[0] = Q81_MBX_GET_LNK_STATUS; 2336252206Sdavidcs 2337252206Sdavidcs if (qls_mbx_cmd(ha, mbox, 1, mbox, 6)) { 2338252206Sdavidcs device_printf(dev, "%s failed\n", __func__); 2339252206Sdavidcs return; 2340252206Sdavidcs } 2341252206Sdavidcs 2342252206Sdavidcs ha->link_status = mbox[1]; 2343252206Sdavidcs ha->link_down_info = mbox[2]; 2344252206Sdavidcs ha->link_hw_info = mbox[3]; 2345252206Sdavidcs ha->link_dcbx_counters = mbox[4]; 2346252206Sdavidcs ha->link_change_counters = mbox[5]; 2347252206Sdavidcs 2348252206Sdavidcs device_printf(dev, "%s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", 2349252206Sdavidcs __func__, mbox[0],mbox[1],mbox[2],mbox[3],mbox[4],mbox[5]); 2350252206Sdavidcs 2351252206Sdavidcs return; 2352252206Sdavidcs} 2353252206Sdavidcs 2354252206Sdavidcsstatic void 2355252206Sdavidcsqls_mbx_about_fw(qla_host_t *ha) 2356252206Sdavidcs{ 2357252206Sdavidcs uint32_t *mbox; 2358252206Sdavidcs device_t dev = ha->pci_dev; 2359252206Sdavidcs 2360252206Sdavidcs mbox = ha->mbox; 2361252206Sdavidcs bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS)); 2362252206Sdavidcs 2363252206Sdavidcs mbox[0] = Q81_MBX_ABOUT_FW; 2364252206Sdavidcs 2365252206Sdavidcs if (qls_mbx_cmd(ha, mbox, 1, mbox, 6)) { 2366252206Sdavidcs device_printf(dev, "%s failed\n", __func__); 2367252206Sdavidcs return; 2368252206Sdavidcs } 2369252206Sdavidcs 2370252206Sdavidcs device_printf(dev, "%s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", 2371252206Sdavidcs __func__, mbox[0],mbox[1],mbox[2],mbox[3],mbox[4],mbox[5]); 2372252206Sdavidcs} 2373252206Sdavidcs 2374252206Sdavidcsint 2375252206Sdavidcsqls_mbx_dump_risc_ram(qla_host_t *ha, void *buf, uint32_t r_addr, 2376252206Sdavidcs uint32_t r_size) 2377252206Sdavidcs{ 2378252206Sdavidcs bus_addr_t b_paddr; 2379252206Sdavidcs uint32_t *mbox; 2380252206Sdavidcs device_t dev = ha->pci_dev; 2381252206Sdavidcs 2382252206Sdavidcs mbox = ha->mbox; 2383252206Sdavidcs bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS)); 2384252206Sdavidcs 2385252206Sdavidcs bzero(ha->mpi_dma.dma_b,(r_size << 2)); 2386252206Sdavidcs b_paddr = ha->mpi_dma.dma_addr; 2387252206Sdavidcs 2388252206Sdavidcs mbox[0] = Q81_MBX_DUMP_RISC_RAM; 2389252206Sdavidcs mbox[1] = r_addr & 0xFFFF; 2390252206Sdavidcs mbox[2] = ((uint32_t)(b_paddr >> 16)) & 0xFFFF; 2391252206Sdavidcs mbox[3] = ((uint32_t)b_paddr) & 0xFFFF; 2392252206Sdavidcs mbox[4] = (r_size >> 16) & 0xFFFF; 2393252206Sdavidcs mbox[5] = r_size & 0xFFFF; 2394252206Sdavidcs mbox[6] = ((uint32_t)(b_paddr >> 48)) & 0xFFFF; 2395252206Sdavidcs mbox[7] = ((uint32_t)(b_paddr >> 32)) & 0xFFFF; 2396252206Sdavidcs mbox[8] = (r_addr >> 16) & 0xFFFF; 2397252206Sdavidcs 2398252206Sdavidcs bus_dmamap_sync(ha->mpi_dma.dma_tag, ha->mpi_dma.dma_map, 2399252206Sdavidcs BUS_DMASYNC_PREREAD); 2400252206Sdavidcs 2401252206Sdavidcs if (qls_mbx_cmd(ha, mbox, 9, mbox, 1)) { 2402252206Sdavidcs device_printf(dev, "%s failed\n", __func__); 2403252206Sdavidcs return (-1); 2404252206Sdavidcs } 2405252206Sdavidcs if (mbox[0] != 0x4000) { 2406252206Sdavidcs device_printf(ha->pci_dev, "%s: failed!\n", __func__); 2407252206Sdavidcs return (-1); 2408252206Sdavidcs } else { 2409252206Sdavidcs bus_dmamap_sync(ha->mpi_dma.dma_tag, ha->mpi_dma.dma_map, 2410252206Sdavidcs BUS_DMASYNC_POSTREAD); 2411252206Sdavidcs bcopy(ha->mpi_dma.dma_b, buf, (r_size << 2)); 2412252206Sdavidcs } 2413252206Sdavidcs 2414252206Sdavidcs return (0); 2415252206Sdavidcs} 2416252206Sdavidcs 2417252206Sdavidcsint 2418252206Sdavidcsqls_mpi_reset(qla_host_t *ha) 2419252206Sdavidcs{ 2420252206Sdavidcs int count; 2421252206Sdavidcs uint32_t data; 2422252206Sdavidcs device_t dev = ha->pci_dev; 2423252206Sdavidcs 2424252206Sdavidcs WRITE_REG32(ha, Q81_CTL_HOST_CMD_STATUS,\ 2425252206Sdavidcs Q81_CTL_HCS_CMD_SET_RISC_RESET); 2426252206Sdavidcs 2427252206Sdavidcs count = 10; 2428252206Sdavidcs while (count--) { 2429252206Sdavidcs data = READ_REG32(ha, Q81_CTL_HOST_CMD_STATUS); 2430252206Sdavidcs if (data & Q81_CTL_HCS_RISC_RESET) { 2431252206Sdavidcs WRITE_REG32(ha, Q81_CTL_HOST_CMD_STATUS,\ 2432252206Sdavidcs Q81_CTL_HCS_CMD_CLR_RISC_RESET); 2433252206Sdavidcs break; 2434252206Sdavidcs } 2435252206Sdavidcs qls_mdelay(__func__, 10); 2436252206Sdavidcs } 2437252206Sdavidcs if (count == 0) { 2438252206Sdavidcs device_printf(dev, "%s: failed\n", __func__); 2439252206Sdavidcs return (-1); 2440252206Sdavidcs } 2441252206Sdavidcs return (0); 2442252206Sdavidcs} 2443252206Sdavidcs 2444