718cf2ccb9
Mainly focus on files that use BSD 2-Clause license, however the tool I was using misidentified many licenses so this was mostly a manual - error prone - task. The Software Package Data Exchange (SPDX) group provides a specification to make it easier for automated tools to detect and summarize well known opensource licenses. We are gradually adopting the specification, noting that the tags are considered only advisory and do not, in any way, superceed or replace the license texts.
2446 lines
56 KiB
C
2446 lines
56 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2013-2014 Qlogic Corporation
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* File: qls_hw.c
|
|
* Author : David C Somayajulu, Qlogic Corporation, Aliso Viejo, CA 92656.
|
|
* Content: Contains Hardware dependent functions
|
|
*/
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
#include "qls_os.h"
|
|
#include "qls_hw.h"
|
|
#include "qls_def.h"
|
|
#include "qls_inline.h"
|
|
#include "qls_ver.h"
|
|
#include "qls_glbl.h"
|
|
#include "qls_dbg.h"
|
|
|
|
/*
|
|
* Static Functions
|
|
*/
|
|
static int qls_wait_for_mac_proto_idx_ready(qla_host_t *ha, uint32_t op);
|
|
static int qls_config_unicast_mac_addr(qla_host_t *ha, uint32_t add_mac);
|
|
static int qls_config_mcast_mac_addr(qla_host_t *ha, uint8_t *mac_addr,
|
|
uint32_t add_mac, uint32_t index);
|
|
|
|
static int qls_init_rss(qla_host_t *ha);
|
|
static int qls_init_comp_queue(qla_host_t *ha, int cid);
|
|
static int qls_init_work_queue(qla_host_t *ha, int wid);
|
|
static int qls_init_fw_routing_table(qla_host_t *ha);
|
|
static int qls_hw_add_all_mcast(qla_host_t *ha);
|
|
static int qls_hw_add_mcast(qla_host_t *ha, uint8_t *mta);
|
|
static int qls_hw_del_mcast(qla_host_t *ha, uint8_t *mta);
|
|
static int qls_wait_for_flash_ready(qla_host_t *ha);
|
|
|
|
static int qls_sem_lock(qla_host_t *ha, uint32_t mask, uint32_t value);
|
|
static void qls_sem_unlock(qla_host_t *ha, uint32_t mask);
|
|
|
|
static void qls_free_tx_dma(qla_host_t *ha);
|
|
static int qls_alloc_tx_dma(qla_host_t *ha);
|
|
static void qls_free_rx_dma(qla_host_t *ha);
|
|
static int qls_alloc_rx_dma(qla_host_t *ha);
|
|
static void qls_free_mpi_dma(qla_host_t *ha);
|
|
static int qls_alloc_mpi_dma(qla_host_t *ha);
|
|
static void qls_free_rss_dma(qla_host_t *ha);
|
|
static int qls_alloc_rss_dma(qla_host_t *ha);
|
|
|
|
static int qls_flash_validate(qla_host_t *ha, const char *signature);
|
|
|
|
|
|
static int qls_wait_for_proc_addr_ready(qla_host_t *ha);
|
|
static int qls_proc_addr_rd_reg(qla_host_t *ha, uint32_t addr_module,
|
|
uint32_t reg, uint32_t *data);
|
|
static int qls_proc_addr_wr_reg(qla_host_t *ha, uint32_t addr_module,
|
|
uint32_t reg, uint32_t data);
|
|
|
|
static int qls_hw_reset(qla_host_t *ha);
|
|
|
|
/*
|
|
* MPI Related Functions
|
|
*/
|
|
static int qls_mbx_cmd(qla_host_t *ha, uint32_t *in_mbx, uint32_t i_count,
|
|
uint32_t *out_mbx, uint32_t o_count);
|
|
static int qls_mbx_set_mgmt_ctrl(qla_host_t *ha, uint32_t t_ctrl);
|
|
static int qls_mbx_get_mgmt_ctrl(qla_host_t *ha, uint32_t *t_status);
|
|
static void qls_mbx_get_link_status(qla_host_t *ha);
|
|
static void qls_mbx_about_fw(qla_host_t *ha);
|
|
|
|
int
|
|
qls_get_msix_count(qla_host_t *ha)
|
|
{
|
|
return (ha->num_rx_rings);
|
|
}
|
|
|
|
static int
|
|
qls_syctl_mpi_dump(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
int err = 0, ret;
|
|
qla_host_t *ha;
|
|
|
|
err = sysctl_handle_int(oidp, &ret, 0, req);
|
|
|
|
if (err || !req->newptr)
|
|
return (err);
|
|
|
|
|
|
if (ret == 1) {
|
|
ha = (qla_host_t *)arg1;
|
|
qls_mpi_core_dump(ha);
|
|
}
|
|
return (err);
|
|
}
|
|
|
|
static int
|
|
qls_syctl_link_status(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
int err = 0, ret;
|
|
qla_host_t *ha;
|
|
|
|
err = sysctl_handle_int(oidp, &ret, 0, req);
|
|
|
|
if (err || !req->newptr)
|
|
return (err);
|
|
|
|
|
|
if (ret == 1) {
|
|
ha = (qla_host_t *)arg1;
|
|
qls_mbx_get_link_status(ha);
|
|
qls_mbx_about_fw(ha);
|
|
}
|
|
return (err);
|
|
}
|
|
|
|
void
|
|
qls_hw_add_sysctls(qla_host_t *ha)
|
|
{
|
|
device_t dev;
|
|
|
|
dev = ha->pci_dev;
|
|
|
|
ha->num_rx_rings = MAX_RX_RINGS; ha->num_tx_rings = MAX_TX_RINGS;
|
|
|
|
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
|
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
|
OID_AUTO, "num_rx_rings", CTLFLAG_RD, &ha->num_rx_rings,
|
|
ha->num_rx_rings, "Number of Completion Queues");
|
|
|
|
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
|
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
|
OID_AUTO, "num_tx_rings", CTLFLAG_RD, &ha->num_tx_rings,
|
|
ha->num_tx_rings, "Number of Transmit Rings");
|
|
|
|
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
|
OID_AUTO, "mpi_dump", CTLTYPE_INT | CTLFLAG_RW,
|
|
(void *)ha, 0,
|
|
qls_syctl_mpi_dump, "I", "MPI Dump");
|
|
|
|
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
|
|
OID_AUTO, "link_status", CTLTYPE_INT | CTLFLAG_RW,
|
|
(void *)ha, 0,
|
|
qls_syctl_link_status, "I", "Link Status");
|
|
}
|
|
|
|
/*
|
|
* Name: qls_free_dma
|
|
* Function: Frees the DMA'able memory allocated in qls_alloc_dma()
|
|
*/
|
|
void
|
|
qls_free_dma(qla_host_t *ha)
|
|
{
|
|
qls_free_rss_dma(ha);
|
|
qls_free_mpi_dma(ha);
|
|
qls_free_tx_dma(ha);
|
|
qls_free_rx_dma(ha);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Name: qls_alloc_dma
|
|
* Function: Allocates DMA'able memory for Tx/Rx Rings, Tx/Rx Contexts.
|
|
*/
|
|
int
|
|
qls_alloc_dma(qla_host_t *ha)
|
|
{
|
|
if (qls_alloc_rx_dma(ha))
|
|
return (-1);
|
|
|
|
if (qls_alloc_tx_dma(ha)) {
|
|
qls_free_rx_dma(ha);
|
|
return (-1);
|
|
}
|
|
|
|
if (qls_alloc_mpi_dma(ha)) {
|
|
qls_free_tx_dma(ha);
|
|
qls_free_rx_dma(ha);
|
|
return (-1);
|
|
}
|
|
|
|
if (qls_alloc_rss_dma(ha)) {
|
|
qls_free_mpi_dma(ha);
|
|
qls_free_tx_dma(ha);
|
|
qls_free_rx_dma(ha);
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
static int
|
|
qls_wait_for_mac_proto_idx_ready(qla_host_t *ha, uint32_t op)
|
|
{
|
|
uint32_t data32;
|
|
uint32_t count = 3;
|
|
|
|
while (count--) {
|
|
data32 = READ_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX);
|
|
|
|
if (data32 & op)
|
|
return (0);
|
|
|
|
QLA_USEC_DELAY(100);
|
|
}
|
|
ha->qla_initiate_recovery = 1;
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Name: qls_config_unicast_mac_addr
|
|
* Function: binds/unbinds a unicast MAC address to the interface.
|
|
*/
|
|
static int
|
|
qls_config_unicast_mac_addr(qla_host_t *ha, uint32_t add_mac)
|
|
{
|
|
int ret = 0;
|
|
uint32_t mac_upper = 0;
|
|
uint32_t mac_lower = 0;
|
|
uint32_t value = 0, index;
|
|
|
|
if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_MAC_SERDES,
|
|
Q81_CTL_SEM_SET_MAC_SERDES)) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__));
|
|
return(-1);
|
|
}
|
|
|
|
if (add_mac) {
|
|
mac_upper = (ha->mac_addr[0] << 8) | ha->mac_addr[1];
|
|
mac_lower = (ha->mac_addr[2] << 24) | (ha->mac_addr[3] << 16) |
|
|
(ha->mac_addr[4] << 8) | ha->mac_addr[5];
|
|
}
|
|
ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW);
|
|
if (ret)
|
|
goto qls_config_unicast_mac_addr_exit;
|
|
|
|
index = 128 * (ha->pci_func & 0x1); /* index */
|
|
|
|
value = (index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) |
|
|
Q81_CTL_MAC_PROTO_AI_TYPE_CAM_MAC;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value);
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, mac_lower);
|
|
|
|
ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW);
|
|
if (ret)
|
|
goto qls_config_unicast_mac_addr_exit;
|
|
|
|
value = (index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) |
|
|
Q81_CTL_MAC_PROTO_AI_TYPE_CAM_MAC | 0x1;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value);
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, mac_upper);
|
|
|
|
ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW);
|
|
if (ret)
|
|
goto qls_config_unicast_mac_addr_exit;
|
|
|
|
value = (index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) |
|
|
Q81_CTL_MAC_PROTO_AI_TYPE_CAM_MAC | 0x2;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value);
|
|
|
|
value = Q81_CAM_MAC_OFF2_ROUTE_NIC |
|
|
((ha->pci_func & 0x1) << Q81_CAM_MAC_OFF2_FUNC_SHIFT) |
|
|
(0 << Q81_CAM_MAC_OFF2_CQID_SHIFT);
|
|
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, value);
|
|
|
|
qls_config_unicast_mac_addr_exit:
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_MAC_SERDES);
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Name: qls_config_mcast_mac_addr
|
|
* Function: binds/unbinds a multicast MAC address to the interface.
|
|
*/
|
|
static int
|
|
qls_config_mcast_mac_addr(qla_host_t *ha, uint8_t *mac_addr, uint32_t add_mac,
|
|
uint32_t index)
|
|
{
|
|
int ret = 0;
|
|
uint32_t mac_upper = 0;
|
|
uint32_t mac_lower = 0;
|
|
uint32_t value = 0;
|
|
|
|
if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_MAC_SERDES,
|
|
Q81_CTL_SEM_SET_MAC_SERDES)) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__));
|
|
return(-1);
|
|
}
|
|
|
|
if (add_mac) {
|
|
mac_upper = (mac_addr[0] << 8) | mac_addr[1];
|
|
mac_lower = (mac_addr[2] << 24) | (mac_addr[3] << 16) |
|
|
(mac_addr[4] << 8) | mac_addr[5];
|
|
}
|
|
ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW);
|
|
if (ret)
|
|
goto qls_config_mcast_mac_addr_exit;
|
|
|
|
value = Q81_CTL_MAC_PROTO_AI_E |
|
|
(index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) |
|
|
Q81_CTL_MAC_PROTO_AI_TYPE_MCAST ;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value);
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, mac_lower);
|
|
|
|
ret = qls_wait_for_mac_proto_idx_ready(ha, Q81_CTL_MAC_PROTO_AI_MW);
|
|
if (ret)
|
|
goto qls_config_mcast_mac_addr_exit;
|
|
|
|
value = Q81_CTL_MAC_PROTO_AI_E |
|
|
(index << Q81_CTL_MAC_PROTO_AI_IDX_SHIFT) |
|
|
Q81_CTL_MAC_PROTO_AI_TYPE_MCAST | 0x1;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_INDEX, value);
|
|
WRITE_REG32(ha, Q81_CTL_MAC_PROTO_ADDR_DATA, mac_upper);
|
|
|
|
qls_config_mcast_mac_addr_exit:
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_MAC_SERDES);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Name: qls_set_mac_rcv_mode
|
|
* Function: Enable/Disable AllMulticast and Promiscuous Modes.
|
|
*/
|
|
static int
|
|
qls_wait_for_route_idx_ready(qla_host_t *ha, uint32_t op)
|
|
{
|
|
uint32_t data32;
|
|
uint32_t count = 3;
|
|
|
|
while (count--) {
|
|
data32 = READ_REG32(ha, Q81_CTL_ROUTING_INDEX);
|
|
|
|
if (data32 & op)
|
|
return (0);
|
|
|
|
QLA_USEC_DELAY(100);
|
|
}
|
|
ha->qla_initiate_recovery = 1;
|
|
return (-1);
|
|
}
|
|
|
|
static int
|
|
qls_load_route_idx_reg(qla_host_t *ha, uint32_t index, uint32_t data)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = qls_wait_for_route_idx_ready(ha, Q81_CTL_RI_MW);
|
|
|
|
if (ret) {
|
|
device_printf(ha->pci_dev, "%s: [0x%08x, 0x%08x] failed\n",
|
|
__func__, index, data);
|
|
goto qls_load_route_idx_reg_exit;
|
|
}
|
|
|
|
|
|
WRITE_REG32(ha, Q81_CTL_ROUTING_INDEX, index);
|
|
WRITE_REG32(ha, Q81_CTL_ROUTING_DATA, data);
|
|
|
|
qls_load_route_idx_reg_exit:
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_load_route_idx_reg_locked(qla_host_t *ha, uint32_t index, uint32_t data)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG,
|
|
Q81_CTL_SEM_SET_RIDX_DATAREG)) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__));
|
|
return(-1);
|
|
}
|
|
|
|
ret = qls_load_route_idx_reg(ha, index, data);
|
|
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_clear_routing_table(qla_host_t *ha)
|
|
{
|
|
int i, ret = 0;
|
|
|
|
if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG,
|
|
Q81_CTL_SEM_SET_RIDX_DATAREG)) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__));
|
|
return(-1);
|
|
}
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_TYPE_NICQMASK|
|
|
(i << 8) | Q81_CTL_RI_DST_DFLTQ), 0);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
qls_set_promisc(qla_host_t *ha)
|
|
{
|
|
int ret;
|
|
|
|
ret = qls_load_route_idx_reg_locked(ha,
|
|
(Q81_CTL_RI_E | Q81_CTL_RI_TYPE_NICQMASK |
|
|
Q81_CTL_RI_IDX_PROMISCUOUS | Q81_CTL_RI_DST_DFLTQ),
|
|
Q81_CTL_RD_VALID_PKT);
|
|
return (ret);
|
|
}
|
|
|
|
void
|
|
qls_reset_promisc(qla_host_t *ha)
|
|
{
|
|
int ret;
|
|
|
|
ret = qls_load_route_idx_reg_locked(ha, (Q81_CTL_RI_TYPE_NICQMASK |
|
|
Q81_CTL_RI_IDX_PROMISCUOUS | Q81_CTL_RI_DST_DFLTQ), 0);
|
|
return;
|
|
}
|
|
|
|
int
|
|
qls_set_allmulti(qla_host_t *ha)
|
|
{
|
|
int ret;
|
|
|
|
ret = qls_load_route_idx_reg_locked(ha,
|
|
(Q81_CTL_RI_E | Q81_CTL_RI_TYPE_NICQMASK |
|
|
Q81_CTL_RI_IDX_ALLMULTI | Q81_CTL_RI_DST_DFLTQ),
|
|
Q81_CTL_RD_MCAST);
|
|
return (ret);
|
|
}
|
|
|
|
void
|
|
qls_reset_allmulti(qla_host_t *ha)
|
|
{
|
|
int ret;
|
|
|
|
ret = qls_load_route_idx_reg_locked(ha, (Q81_CTL_RI_TYPE_NICQMASK |
|
|
Q81_CTL_RI_IDX_ALLMULTI | Q81_CTL_RI_DST_DFLTQ), 0);
|
|
return;
|
|
}
|
|
|
|
|
|
static int
|
|
qls_init_fw_routing_table(qla_host_t *ha)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = qls_clear_routing_table(ha);
|
|
if (ret)
|
|
return (-1);
|
|
|
|
if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG,
|
|
Q81_CTL_SEM_SET_RIDX_DATAREG)) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__));
|
|
return(-1);
|
|
}
|
|
|
|
ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_E | Q81_CTL_RI_DST_DROP |
|
|
Q81_CTL_RI_TYPE_NICQMASK | Q81_CTL_RI_IDX_ALL_ERROR),
|
|
Q81_CTL_RD_ERROR_PKT);
|
|
if (ret)
|
|
goto qls_init_fw_routing_table_exit;
|
|
|
|
ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_E | Q81_CTL_RI_DST_DFLTQ |
|
|
Q81_CTL_RI_TYPE_NICQMASK | Q81_CTL_RI_IDX_BCAST),
|
|
Q81_CTL_RD_BCAST);
|
|
if (ret)
|
|
goto qls_init_fw_routing_table_exit;
|
|
|
|
if (ha->num_rx_rings > 1 ) {
|
|
ret = qls_load_route_idx_reg(ha,
|
|
(Q81_CTL_RI_E | Q81_CTL_RI_DST_RSS |
|
|
Q81_CTL_RI_TYPE_NICQMASK |
|
|
Q81_CTL_RI_IDX_RSS_MATCH),
|
|
Q81_CTL_RD_RSS_MATCH);
|
|
if (ret)
|
|
goto qls_init_fw_routing_table_exit;
|
|
}
|
|
|
|
ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_E | Q81_CTL_RI_DST_DFLTQ |
|
|
Q81_CTL_RI_TYPE_NICQMASK | Q81_CTL_RI_IDX_MCAST_MATCH),
|
|
Q81_CTL_RD_MCAST_REG_MATCH);
|
|
if (ret)
|
|
goto qls_init_fw_routing_table_exit;
|
|
|
|
ret = qls_load_route_idx_reg(ha, (Q81_CTL_RI_E | Q81_CTL_RI_DST_DFLTQ |
|
|
Q81_CTL_RI_TYPE_NICQMASK | Q81_CTL_RI_IDX_CAM_HIT),
|
|
Q81_CTL_RD_CAM_HIT);
|
|
if (ret)
|
|
goto qls_init_fw_routing_table_exit;
|
|
|
|
qls_init_fw_routing_table_exit:
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_RIDX_DATAREG);
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_tx_tso_chksum(qla_host_t *ha, struct mbuf *mp, q81_tx_tso_t *tx_mac)
|
|
{
|
|
struct ether_vlan_header *eh;
|
|
struct ip *ip;
|
|
struct ip6_hdr *ip6;
|
|
struct tcphdr *th;
|
|
uint32_t ehdrlen, ip_hlen;
|
|
int ret = 0;
|
|
uint16_t etype;
|
|
device_t dev;
|
|
uint8_t buf[sizeof(struct ip6_hdr)];
|
|
|
|
dev = ha->pci_dev;
|
|
|
|
eh = mtod(mp, struct ether_vlan_header *);
|
|
|
|
if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
|
|
ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
|
|
etype = ntohs(eh->evl_proto);
|
|
} else {
|
|
ehdrlen = ETHER_HDR_LEN;
|
|
etype = ntohs(eh->evl_encap_proto);
|
|
}
|
|
|
|
switch (etype) {
|
|
case ETHERTYPE_IP:
|
|
ip = (struct ip *)(mp->m_data + ehdrlen);
|
|
|
|
ip_hlen = sizeof (struct ip);
|
|
|
|
if (mp->m_len < (ehdrlen + ip_hlen)) {
|
|
m_copydata(mp, ehdrlen, sizeof(struct ip), buf);
|
|
ip = (struct ip *)buf;
|
|
}
|
|
tx_mac->opcode = Q81_IOCB_TX_TSO;
|
|
tx_mac->flags |= Q81_TX_TSO_FLAGS_IPV4 ;
|
|
|
|
tx_mac->phdr_offsets = ehdrlen;
|
|
|
|
tx_mac->phdr_offsets |= ((ehdrlen + ip_hlen) <<
|
|
Q81_TX_TSO_PHDR_SHIFT);
|
|
|
|
ip->ip_sum = 0;
|
|
|
|
if (mp->m_pkthdr.csum_flags & CSUM_TSO) {
|
|
tx_mac->flags |= Q81_TX_TSO_FLAGS_LSO;
|
|
|
|
th = (struct tcphdr *)(ip + 1);
|
|
|
|
th->th_sum = in_pseudo(ip->ip_src.s_addr,
|
|
ip->ip_dst.s_addr,
|
|
htons(IPPROTO_TCP));
|
|
tx_mac->mss = mp->m_pkthdr.tso_segsz;
|
|
tx_mac->phdr_length = ip_hlen + ehdrlen +
|
|
(th->th_off << 2);
|
|
break;
|
|
}
|
|
tx_mac->vlan_off |= Q81_TX_TSO_VLAN_OFF_IC ;
|
|
|
|
|
|
if (ip->ip_p == IPPROTO_TCP) {
|
|
tx_mac->flags |= Q81_TX_TSO_FLAGS_TC;
|
|
} else if (ip->ip_p == IPPROTO_UDP) {
|
|
tx_mac->flags |= Q81_TX_TSO_FLAGS_UC;
|
|
}
|
|
break;
|
|
|
|
case ETHERTYPE_IPV6:
|
|
ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen);
|
|
|
|
ip_hlen = sizeof(struct ip6_hdr);
|
|
|
|
if (mp->m_len < (ehdrlen + ip_hlen)) {
|
|
m_copydata(mp, ehdrlen, sizeof (struct ip6_hdr),
|
|
buf);
|
|
ip6 = (struct ip6_hdr *)buf;
|
|
}
|
|
|
|
tx_mac->opcode = Q81_IOCB_TX_TSO;
|
|
tx_mac->flags |= Q81_TX_TSO_FLAGS_IPV6 ;
|
|
tx_mac->vlan_off |= Q81_TX_TSO_VLAN_OFF_IC ;
|
|
|
|
tx_mac->phdr_offsets = ehdrlen;
|
|
tx_mac->phdr_offsets |= ((ehdrlen + ip_hlen) <<
|
|
Q81_TX_TSO_PHDR_SHIFT);
|
|
|
|
if (ip6->ip6_nxt == IPPROTO_TCP) {
|
|
tx_mac->flags |= Q81_TX_TSO_FLAGS_TC;
|
|
} else if (ip6->ip6_nxt == IPPROTO_UDP) {
|
|
tx_mac->flags |= Q81_TX_TSO_FLAGS_UC;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
|
|
#define QLA_TX_MIN_FREE 2
|
|
int
|
|
qls_hw_tx_done(qla_host_t *ha, uint32_t txr_idx)
|
|
{
|
|
uint32_t txr_done, txr_next;
|
|
|
|
txr_done = ha->tx_ring[txr_idx].txr_done;
|
|
txr_next = ha->tx_ring[txr_idx].txr_next;
|
|
|
|
if (txr_done == txr_next) {
|
|
ha->tx_ring[txr_idx].txr_free = NUM_TX_DESCRIPTORS;
|
|
} else if (txr_done > txr_next) {
|
|
ha->tx_ring[txr_idx].txr_free = txr_done - txr_next;
|
|
} else {
|
|
ha->tx_ring[txr_idx].txr_free = NUM_TX_DESCRIPTORS +
|
|
txr_done - txr_next;
|
|
}
|
|
|
|
if (ha->tx_ring[txr_idx].txr_free <= QLA_TX_MIN_FREE)
|
|
return (-1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Name: qls_hw_send
|
|
* Function: Transmits a packet. It first checks if the packet is a
|
|
* candidate for Large TCP Segment Offload and then for UDP/TCP checksum
|
|
* offload. If either of these creteria are not met, it is transmitted
|
|
* as a regular ethernet frame.
|
|
*/
|
|
int
|
|
qls_hw_send(qla_host_t *ha, bus_dma_segment_t *segs, int nsegs,
|
|
uint32_t txr_next, struct mbuf *mp, uint32_t txr_idx)
|
|
{
|
|
q81_tx_mac_t *tx_mac;
|
|
q81_txb_desc_t *tx_desc;
|
|
uint32_t total_length = 0;
|
|
uint32_t i;
|
|
device_t dev;
|
|
int ret = 0;
|
|
|
|
dev = ha->pci_dev;
|
|
|
|
total_length = mp->m_pkthdr.len;
|
|
|
|
if (total_length > QLA_MAX_TSO_FRAME_SIZE) {
|
|
device_printf(dev, "%s: total length exceeds maxlen(%d)\n",
|
|
__func__, total_length);
|
|
return (-1);
|
|
}
|
|
|
|
if (ha->tx_ring[txr_idx].txr_free <= (NUM_TX_DESCRIPTORS >> 2)) {
|
|
if (qls_hw_tx_done(ha, txr_idx)) {
|
|
device_printf(dev, "%s: tx_free[%d] = %d\n",
|
|
__func__, txr_idx,
|
|
ha->tx_ring[txr_idx].txr_free);
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
tx_mac = (q81_tx_mac_t *)&ha->tx_ring[txr_idx].wq_vaddr[txr_next];
|
|
|
|
bzero(tx_mac, sizeof(q81_tx_mac_t));
|
|
|
|
if ((mp->m_pkthdr.csum_flags &
|
|
(CSUM_TCP | CSUM_UDP | CSUM_IP | CSUM_TSO)) != 0) {
|
|
|
|
ret = qls_tx_tso_chksum(ha, mp, (q81_tx_tso_t *)tx_mac);
|
|
if (ret)
|
|
return (EINVAL);
|
|
|
|
if (mp->m_pkthdr.csum_flags & CSUM_TSO)
|
|
ha->tx_ring[txr_idx].tx_tso_frames++;
|
|
else
|
|
ha->tx_ring[txr_idx].tx_frames++;
|
|
|
|
} else {
|
|
tx_mac->opcode = Q81_IOCB_TX_MAC;
|
|
}
|
|
|
|
if (mp->m_flags & M_VLANTAG) {
|
|
|
|
tx_mac->vlan_tci = mp->m_pkthdr.ether_vtag;
|
|
tx_mac->vlan_off |= Q81_TX_MAC_VLAN_OFF_V;
|
|
|
|
ha->tx_ring[txr_idx].tx_vlan_frames++;
|
|
}
|
|
|
|
tx_mac->frame_length = total_length;
|
|
|
|
tx_mac->tid_lo = txr_next;
|
|
|
|
if (nsegs <= MAX_TX_MAC_DESC) {
|
|
|
|
QL_DPRINT2((dev, "%s: 1 [%d, %d]\n", __func__, total_length,
|
|
tx_mac->tid_lo));
|
|
|
|
for (i = 0; i < nsegs; i++) {
|
|
tx_mac->txd[i].baddr = segs->ds_addr;
|
|
tx_mac->txd[i].length = segs->ds_len;
|
|
segs++;
|
|
}
|
|
tx_mac->txd[(nsegs - 1)].flags = Q81_RXB_DESC_FLAGS_E;
|
|
|
|
} else {
|
|
QL_DPRINT2((dev, "%s: 2 [%d, %d]\n", __func__, total_length,
|
|
tx_mac->tid_lo));
|
|
|
|
tx_mac->txd[0].baddr =
|
|
ha->tx_ring[txr_idx].tx_buf[txr_next].oal_paddr;
|
|
tx_mac->txd[0].length =
|
|
nsegs * (sizeof(q81_txb_desc_t));
|
|
tx_mac->txd[0].flags = Q81_RXB_DESC_FLAGS_C;
|
|
|
|
tx_desc = ha->tx_ring[txr_idx].tx_buf[txr_next].oal_vaddr;
|
|
|
|
for (i = 0; i < nsegs; i++) {
|
|
tx_desc->baddr = segs->ds_addr;
|
|
tx_desc->length = segs->ds_len;
|
|
|
|
if (i == (nsegs -1))
|
|
tx_desc->flags = Q81_RXB_DESC_FLAGS_E;
|
|
else
|
|
tx_desc->flags = 0;
|
|
|
|
segs++;
|
|
tx_desc++;
|
|
}
|
|
}
|
|
txr_next = (txr_next + 1) & (NUM_TX_DESCRIPTORS - 1);
|
|
ha->tx_ring[txr_idx].txr_next = txr_next;
|
|
|
|
ha->tx_ring[txr_idx].txr_free--;
|
|
|
|
Q81_WR_WQ_PROD_IDX(txr_idx, txr_next);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Name: qls_del_hw_if
|
|
* Function: Destroys the hardware specific entities corresponding to an
|
|
* Ethernet Interface
|
|
*/
|
|
void
|
|
qls_del_hw_if(qla_host_t *ha)
|
|
{
|
|
uint32_t value;
|
|
int i;
|
|
//int count;
|
|
|
|
if (ha->hw_init == 0) {
|
|
qls_hw_reset(ha);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < ha->num_tx_rings; i++) {
|
|
Q81_SET_WQ_INVALID(i);
|
|
}
|
|
for (i = 0; i < ha->num_rx_rings; i++) {
|
|
Q81_SET_CQ_INVALID(i);
|
|
}
|
|
|
|
for (i = 0; i < ha->num_rx_rings; i++) {
|
|
Q81_DISABLE_INTR(ha, i); /* MSI-x i */
|
|
}
|
|
|
|
value = (Q81_CTL_INTRE_IHD << Q81_CTL_INTRE_MASK_SHIFT);
|
|
WRITE_REG32(ha, Q81_CTL_INTR_ENABLE, value);
|
|
|
|
value = (Q81_CTL_INTRE_EI << Q81_CTL_INTRE_MASK_SHIFT);
|
|
WRITE_REG32(ha, Q81_CTL_INTR_ENABLE, value);
|
|
ha->flags.intr_enable = 0;
|
|
|
|
qls_hw_reset(ha);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Name: qls_init_hw_if
|
|
* Function: Creates the hardware specific entities corresponding to an
|
|
* Ethernet Interface - Transmit and Receive Contexts. Sets the MAC Address
|
|
* corresponding to the interface. Enables LRO if allowed.
|
|
*/
|
|
int
|
|
qls_init_hw_if(qla_host_t *ha)
|
|
{
|
|
device_t dev;
|
|
uint32_t value;
|
|
int ret = 0;
|
|
int i;
|
|
|
|
|
|
QL_DPRINT2((ha->pci_dev, "%s:enter\n", __func__));
|
|
|
|
dev = ha->pci_dev;
|
|
|
|
ret = qls_hw_reset(ha);
|
|
if (ret)
|
|
goto qls_init_hw_if_exit;
|
|
|
|
ha->vm_pgsize = 4096;
|
|
|
|
/* Enable FAE and EFE bits in System Register */
|
|
value = Q81_CTL_SYSTEM_ENABLE_FAE | Q81_CTL_SYSTEM_ENABLE_EFE;
|
|
value = (value << Q81_CTL_SYSTEM_MASK_SHIFT) | value;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_SYSTEM, value);
|
|
|
|
/* Set Default Completion Queue_ID in NIC Rcv Configuration Register */
|
|
value = (Q81_CTL_NIC_RCVC_DCQ_MASK << Q81_CTL_NIC_RCVC_MASK_SHIFT);
|
|
WRITE_REG32(ha, Q81_CTL_NIC_RCV_CONFIG, value);
|
|
|
|
/* Function Specific Control Register - Set Page Size and Enable NIC */
|
|
value = Q81_CTL_FUNC_SPECIFIC_FE |
|
|
Q81_CTL_FUNC_SPECIFIC_VM_PGSIZE_MASK |
|
|
Q81_CTL_FUNC_SPECIFIC_EPC_O |
|
|
Q81_CTL_FUNC_SPECIFIC_EPC_I |
|
|
Q81_CTL_FUNC_SPECIFIC_EC;
|
|
value = (value << Q81_CTL_FUNC_SPECIFIC_MASK_SHIFT) |
|
|
Q81_CTL_FUNC_SPECIFIC_FE |
|
|
Q81_CTL_FUNC_SPECIFIC_VM_PGSIZE_4K |
|
|
Q81_CTL_FUNC_SPECIFIC_EPC_O |
|
|
Q81_CTL_FUNC_SPECIFIC_EPC_I |
|
|
Q81_CTL_FUNC_SPECIFIC_EC;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_FUNC_SPECIFIC, value);
|
|
|
|
/* Interrupt Mask Register */
|
|
value = Q81_CTL_INTRM_PI;
|
|
value = (value << Q81_CTL_INTRM_MASK_SHIFT) | value;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_INTR_MASK, value);
|
|
|
|
/* Initialiatize Completion Queue */
|
|
for (i = 0; i < ha->num_rx_rings; i++) {
|
|
ret = qls_init_comp_queue(ha, i);
|
|
if (ret)
|
|
goto qls_init_hw_if_exit;
|
|
}
|
|
|
|
if (ha->num_rx_rings > 1 ) {
|
|
ret = qls_init_rss(ha);
|
|
if (ret)
|
|
goto qls_init_hw_if_exit;
|
|
}
|
|
|
|
/* Initialize Work Queue */
|
|
|
|
for (i = 0; i < ha->num_tx_rings; i++) {
|
|
ret = qls_init_work_queue(ha, i);
|
|
if (ret)
|
|
goto qls_init_hw_if_exit;
|
|
}
|
|
|
|
if (ret)
|
|
goto qls_init_hw_if_exit;
|
|
|
|
/* Set up CAM RAM with MAC Address */
|
|
ret = qls_config_unicast_mac_addr(ha, 1);
|
|
if (ret)
|
|
goto qls_init_hw_if_exit;
|
|
|
|
ret = qls_hw_add_all_mcast(ha);
|
|
if (ret)
|
|
goto qls_init_hw_if_exit;
|
|
|
|
/* Initialize Firmware Routing Table */
|
|
ret = qls_init_fw_routing_table(ha);
|
|
if (ret)
|
|
goto qls_init_hw_if_exit;
|
|
|
|
/* Get Chip Revision ID */
|
|
ha->rev_id = READ_REG32(ha, Q81_CTL_REV_ID);
|
|
|
|
/* Enable Global Interrupt */
|
|
value = Q81_CTL_INTRE_EI;
|
|
value = (value << Q81_CTL_INTRE_MASK_SHIFT) | value;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_INTR_ENABLE, value);
|
|
|
|
/* Enable Interrupt Handshake Disable */
|
|
value = Q81_CTL_INTRE_IHD;
|
|
value = (value << Q81_CTL_INTRE_MASK_SHIFT) | value;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_INTR_ENABLE, value);
|
|
|
|
/* Enable Completion Interrupt */
|
|
|
|
ha->flags.intr_enable = 1;
|
|
|
|
for (i = 0; i < ha->num_rx_rings; i++) {
|
|
Q81_ENABLE_INTR(ha, i); /* MSI-x i */
|
|
}
|
|
|
|
ha->hw_init = 1;
|
|
|
|
qls_mbx_get_link_status(ha);
|
|
|
|
QL_DPRINT2((ha->pci_dev, "%s:rxr [0x%08x]\n", __func__,
|
|
ha->rx_ring[0].cq_db_offset));
|
|
QL_DPRINT2((ha->pci_dev, "%s:txr [0x%08x]\n", __func__,
|
|
ha->tx_ring[0].wq_db_offset));
|
|
|
|
for (i = 0; i < ha->num_rx_rings; i++) {
|
|
|
|
Q81_WR_CQ_CONS_IDX(i, 0);
|
|
Q81_WR_LBQ_PROD_IDX(i, ha->rx_ring[i].lbq_in);
|
|
Q81_WR_SBQ_PROD_IDX(i, ha->rx_ring[i].sbq_in);
|
|
|
|
QL_DPRINT2((dev, "%s: [wq_idx, cq_idx, lbq_idx, sbq_idx]"
|
|
"[0x%08x, 0x%08x, 0x%08x, 0x%08x]\n", __func__,
|
|
Q81_RD_WQ_IDX(i), Q81_RD_CQ_IDX(i), Q81_RD_LBQ_IDX(i),
|
|
Q81_RD_SBQ_IDX(i)));
|
|
}
|
|
|
|
for (i = 0; i < ha->num_rx_rings; i++) {
|
|
Q81_SET_CQ_VALID(i);
|
|
}
|
|
|
|
qls_init_hw_if_exit:
|
|
QL_DPRINT2((ha->pci_dev, "%s:exit\n", __func__));
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_wait_for_config_reg_bits(qla_host_t *ha, uint32_t bits, uint32_t value)
|
|
{
|
|
uint32_t data32;
|
|
uint32_t count = 3;
|
|
|
|
while (count--) {
|
|
|
|
data32 = READ_REG32(ha, Q81_CTL_CONFIG);
|
|
|
|
if ((data32 & bits) == value)
|
|
return (0);
|
|
|
|
QLA_USEC_DELAY(100);
|
|
}
|
|
ha->qla_initiate_recovery = 1;
|
|
device_printf(ha->pci_dev, "%s: failed\n", __func__);
|
|
return (-1);
|
|
}
|
|
|
|
static uint8_t q81_hash_key[] = {
|
|
0xda, 0x56, 0x5a, 0x6d,
|
|
0xc2, 0x0e, 0x5b, 0x25,
|
|
0x3d, 0x25, 0x67, 0x41,
|
|
0xb0, 0x8f, 0xa3, 0x43,
|
|
0xcb, 0x2b, 0xca, 0xd0,
|
|
0xb4, 0x30, 0x7b, 0xae,
|
|
0xa3, 0x2d, 0xcb, 0x77,
|
|
0x0c, 0xf2, 0x30, 0x80,
|
|
0x3b, 0xb7, 0x42, 0x6a,
|
|
0xfa, 0x01, 0xac, 0xbe };
|
|
|
|
static int
|
|
qls_init_rss(qla_host_t *ha)
|
|
{
|
|
q81_rss_icb_t *rss_icb;
|
|
int ret = 0;
|
|
int i;
|
|
uint32_t value;
|
|
|
|
rss_icb = ha->rss_dma.dma_b;
|
|
|
|
bzero(rss_icb, sizeof (q81_rss_icb_t));
|
|
|
|
rss_icb->flags_base_cq_num = Q81_RSS_ICB_FLAGS_L4K |
|
|
Q81_RSS_ICB_FLAGS_L6K | Q81_RSS_ICB_FLAGS_LI |
|
|
Q81_RSS_ICB_FLAGS_LB | Q81_RSS_ICB_FLAGS_LM |
|
|
Q81_RSS_ICB_FLAGS_RT4 | Q81_RSS_ICB_FLAGS_RT6;
|
|
|
|
rss_icb->mask = 0x3FF;
|
|
|
|
for (i = 0; i < Q81_RSS_ICB_NUM_INDTBL_ENTRIES; i++) {
|
|
rss_icb->cq_id[i] = (i & (ha->num_rx_rings - 1));
|
|
}
|
|
|
|
memcpy(rss_icb->ipv6_rss_hash_key, q81_hash_key, 40);
|
|
memcpy(rss_icb->ipv4_rss_hash_key, q81_hash_key, 16);
|
|
|
|
ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LR, 0);
|
|
|
|
if (ret)
|
|
goto qls_init_rss_exit;
|
|
|
|
ret = qls_sem_lock(ha, Q81_CTL_SEM_MASK_ICB, Q81_CTL_SEM_SET_ICB);
|
|
|
|
if (ret) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__));
|
|
goto qls_init_rss_exit;
|
|
}
|
|
|
|
value = (uint32_t)ha->rss_dma.dma_addr;
|
|
WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_LO, value);
|
|
|
|
value = (uint32_t)(ha->rss_dma.dma_addr >> 32);
|
|
WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_HI, value);
|
|
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_ICB);
|
|
|
|
value = (Q81_CTL_CONFIG_LR << Q81_CTL_CONFIG_MASK_SHIFT) |
|
|
Q81_CTL_CONFIG_LR;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_CONFIG, value);
|
|
|
|
ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LR, 0);
|
|
|
|
qls_init_rss_exit:
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_init_comp_queue(qla_host_t *ha, int cid)
|
|
{
|
|
q81_cq_icb_t *cq_icb;
|
|
qla_rx_ring_t *rxr;
|
|
int ret = 0;
|
|
uint32_t value;
|
|
|
|
rxr = &ha->rx_ring[cid];
|
|
|
|
rxr->cq_db_offset = ha->vm_pgsize * (128 + cid);
|
|
|
|
cq_icb = rxr->cq_icb_vaddr;
|
|
|
|
bzero(cq_icb, sizeof (q81_cq_icb_t));
|
|
|
|
cq_icb->msix_vector = cid;
|
|
cq_icb->flags = Q81_CQ_ICB_FLAGS_LC |
|
|
Q81_CQ_ICB_FLAGS_LI |
|
|
Q81_CQ_ICB_FLAGS_LL |
|
|
Q81_CQ_ICB_FLAGS_LS |
|
|
Q81_CQ_ICB_FLAGS_LV;
|
|
|
|
cq_icb->length_v = NUM_CQ_ENTRIES;
|
|
|
|
cq_icb->cq_baddr_lo = (rxr->cq_base_paddr & 0xFFFFFFFF);
|
|
cq_icb->cq_baddr_hi = (rxr->cq_base_paddr >> 32) & 0xFFFFFFFF;
|
|
|
|
cq_icb->cqi_addr_lo = (rxr->cqi_paddr & 0xFFFFFFFF);
|
|
cq_icb->cqi_addr_hi = (rxr->cqi_paddr >> 32) & 0xFFFFFFFF;
|
|
|
|
cq_icb->pkt_idelay = 10;
|
|
cq_icb->idelay = 100;
|
|
|
|
cq_icb->lbq_baddr_lo = (rxr->lbq_addr_tbl_paddr & 0xFFFFFFFF);
|
|
cq_icb->lbq_baddr_hi = (rxr->lbq_addr_tbl_paddr >> 32) & 0xFFFFFFFF;
|
|
|
|
cq_icb->lbq_bsize = QLA_LGB_SIZE;
|
|
cq_icb->lbq_length = QLA_NUM_LGB_ENTRIES;
|
|
|
|
cq_icb->sbq_baddr_lo = (rxr->sbq_addr_tbl_paddr & 0xFFFFFFFF);
|
|
cq_icb->sbq_baddr_hi = (rxr->sbq_addr_tbl_paddr >> 32) & 0xFFFFFFFF;
|
|
|
|
cq_icb->sbq_bsize = (uint16_t)ha->msize;
|
|
cq_icb->sbq_length = QLA_NUM_SMB_ENTRIES;
|
|
|
|
QL_DUMP_CQ(ha);
|
|
|
|
ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LCQ, 0);
|
|
|
|
if (ret)
|
|
goto qls_init_comp_queue_exit;
|
|
|
|
ret = qls_sem_lock(ha, Q81_CTL_SEM_MASK_ICB, Q81_CTL_SEM_SET_ICB);
|
|
|
|
if (ret) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__));
|
|
goto qls_init_comp_queue_exit;
|
|
}
|
|
|
|
value = (uint32_t)rxr->cq_icb_paddr;
|
|
WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_LO, value);
|
|
|
|
value = (uint32_t)(rxr->cq_icb_paddr >> 32);
|
|
WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_HI, value);
|
|
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_ICB);
|
|
|
|
value = Q81_CTL_CONFIG_LCQ | Q81_CTL_CONFIG_Q_NUM_MASK;
|
|
value = (value << Q81_CTL_CONFIG_MASK_SHIFT) | Q81_CTL_CONFIG_LCQ;
|
|
value |= (cid << Q81_CTL_CONFIG_Q_NUM_SHIFT);
|
|
WRITE_REG32(ha, Q81_CTL_CONFIG, value);
|
|
|
|
ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LCQ, 0);
|
|
|
|
rxr->cq_next = 0;
|
|
rxr->lbq_next = rxr->lbq_free = 0;
|
|
rxr->sbq_next = rxr->sbq_free = 0;
|
|
rxr->rx_free = rxr->rx_next = 0;
|
|
rxr->lbq_in = (QLA_NUM_LGB_ENTRIES - 1) & ~0xF;
|
|
rxr->sbq_in = (QLA_NUM_SMB_ENTRIES - 1) & ~0xF;
|
|
|
|
qls_init_comp_queue_exit:
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_init_work_queue(qla_host_t *ha, int wid)
|
|
{
|
|
q81_wq_icb_t *wq_icb;
|
|
qla_tx_ring_t *txr;
|
|
int ret = 0;
|
|
uint32_t value;
|
|
|
|
txr = &ha->tx_ring[wid];
|
|
|
|
txr->wq_db_addr = (struct resource *)((uint8_t *)ha->pci_reg1
|
|
+ (ha->vm_pgsize * wid));
|
|
|
|
txr->wq_db_offset = (ha->vm_pgsize * wid);
|
|
|
|
wq_icb = txr->wq_icb_vaddr;
|
|
bzero(wq_icb, sizeof (q81_wq_icb_t));
|
|
|
|
wq_icb->length_v = NUM_TX_DESCRIPTORS |
|
|
Q81_WQ_ICB_VALID;
|
|
|
|
wq_icb->flags = Q81_WQ_ICB_FLAGS_LO | Q81_WQ_ICB_FLAGS_LI |
|
|
Q81_WQ_ICB_FLAGS_LB | Q81_WQ_ICB_FLAGS_LC;
|
|
|
|
wq_icb->wqcqid_rss = wid;
|
|
|
|
wq_icb->baddr_lo = txr->wq_paddr & 0xFFFFFFFF;
|
|
wq_icb->baddr_hi = (txr->wq_paddr >> 32)& 0xFFFFFFFF;
|
|
|
|
wq_icb->ci_addr_lo = txr->txr_cons_paddr & 0xFFFFFFFF;
|
|
wq_icb->ci_addr_hi = (txr->txr_cons_paddr >> 32)& 0xFFFFFFFF;
|
|
|
|
ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LRQ, 0);
|
|
|
|
if (ret)
|
|
goto qls_init_wq_exit;
|
|
|
|
ret = qls_sem_lock(ha, Q81_CTL_SEM_MASK_ICB, Q81_CTL_SEM_SET_ICB);
|
|
|
|
if (ret) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__));
|
|
goto qls_init_wq_exit;
|
|
}
|
|
|
|
value = (uint32_t)txr->wq_icb_paddr;
|
|
WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_LO, value);
|
|
|
|
value = (uint32_t)(txr->wq_icb_paddr >> 32);
|
|
WRITE_REG32(ha, Q81_CTL_ICB_ACCESS_ADDR_HI, value);
|
|
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_ICB);
|
|
|
|
value = Q81_CTL_CONFIG_LRQ | Q81_CTL_CONFIG_Q_NUM_MASK;
|
|
value = (value << Q81_CTL_CONFIG_MASK_SHIFT) | Q81_CTL_CONFIG_LRQ;
|
|
value |= (wid << Q81_CTL_CONFIG_Q_NUM_SHIFT);
|
|
WRITE_REG32(ha, Q81_CTL_CONFIG, value);
|
|
|
|
ret = qls_wait_for_config_reg_bits(ha, Q81_CTL_CONFIG_LRQ, 0);
|
|
|
|
txr->txr_free = NUM_TX_DESCRIPTORS;
|
|
txr->txr_next = 0;
|
|
txr->txr_done = 0;
|
|
|
|
qls_init_wq_exit:
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_hw_add_all_mcast(qla_host_t *ha)
|
|
{
|
|
int i, nmcast;
|
|
|
|
nmcast = ha->nmcast;
|
|
|
|
for (i = 0 ; ((i < Q8_MAX_NUM_MULTICAST_ADDRS) && nmcast); i++) {
|
|
if ((ha->mcast[i].addr[0] != 0) ||
|
|
(ha->mcast[i].addr[1] != 0) ||
|
|
(ha->mcast[i].addr[2] != 0) ||
|
|
(ha->mcast[i].addr[3] != 0) ||
|
|
(ha->mcast[i].addr[4] != 0) ||
|
|
(ha->mcast[i].addr[5] != 0)) {
|
|
|
|
if (qls_config_mcast_mac_addr(ha, ha->mcast[i].addr,
|
|
1, i)) {
|
|
device_printf(ha->pci_dev, "%s: failed\n",
|
|
__func__);
|
|
return (-1);
|
|
}
|
|
|
|
nmcast--;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
qls_hw_add_mcast(qla_host_t *ha, uint8_t *mta)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < Q8_MAX_NUM_MULTICAST_ADDRS; i++) {
|
|
|
|
if (QL_MAC_CMP(ha->mcast[i].addr, mta) == 0)
|
|
return 0; /* its been already added */
|
|
}
|
|
|
|
for (i = 0; i < Q8_MAX_NUM_MULTICAST_ADDRS; i++) {
|
|
|
|
if ((ha->mcast[i].addr[0] == 0) &&
|
|
(ha->mcast[i].addr[1] == 0) &&
|
|
(ha->mcast[i].addr[2] == 0) &&
|
|
(ha->mcast[i].addr[3] == 0) &&
|
|
(ha->mcast[i].addr[4] == 0) &&
|
|
(ha->mcast[i].addr[5] == 0)) {
|
|
|
|
if (qls_config_mcast_mac_addr(ha, mta, 1, i))
|
|
return (-1);
|
|
|
|
bcopy(mta, ha->mcast[i].addr, Q8_MAC_ADDR_LEN);
|
|
ha->nmcast++;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
qls_hw_del_mcast(qla_host_t *ha, uint8_t *mta)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < Q8_MAX_NUM_MULTICAST_ADDRS; i++) {
|
|
if (QL_MAC_CMP(ha->mcast[i].addr, mta) == 0) {
|
|
|
|
if (qls_config_mcast_mac_addr(ha, mta, 0, i))
|
|
return (-1);
|
|
|
|
ha->mcast[i].addr[0] = 0;
|
|
ha->mcast[i].addr[1] = 0;
|
|
ha->mcast[i].addr[2] = 0;
|
|
ha->mcast[i].addr[3] = 0;
|
|
ha->mcast[i].addr[4] = 0;
|
|
ha->mcast[i].addr[5] = 0;
|
|
|
|
ha->nmcast--;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Name: qls_hw_set_multi
|
|
* Function: Sets the Multicast Addresses provided the host O.S into the
|
|
* hardware (for the given interface)
|
|
*/
|
|
void
|
|
qls_hw_set_multi(qla_host_t *ha, uint8_t *mta, uint32_t mcnt,
|
|
uint32_t add_mac)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < mcnt; i++) {
|
|
if (add_mac) {
|
|
if (qls_hw_add_mcast(ha, mta))
|
|
break;
|
|
} else {
|
|
if (qls_hw_del_mcast(ha, mta))
|
|
break;
|
|
}
|
|
|
|
mta += Q8_MAC_ADDR_LEN;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void
|
|
qls_update_link_state(qla_host_t *ha)
|
|
{
|
|
uint32_t link_state;
|
|
uint32_t prev_link_state;
|
|
|
|
if (!(ha->ifp->if_drv_flags & IFF_DRV_RUNNING)) {
|
|
ha->link_up = 0;
|
|
return;
|
|
}
|
|
link_state = READ_REG32(ha, Q81_CTL_STATUS);
|
|
|
|
prev_link_state = ha->link_up;
|
|
|
|
if ((ha->pci_func & 0x1) == 0)
|
|
ha->link_up = ((link_state & Q81_CTL_STATUS_PL0)? 1 : 0);
|
|
else
|
|
ha->link_up = ((link_state & Q81_CTL_STATUS_PL1)? 1 : 0);
|
|
|
|
if (prev_link_state != ha->link_up) {
|
|
|
|
|
|
if (ha->link_up) {
|
|
if_link_state_change(ha->ifp, LINK_STATE_UP);
|
|
} else {
|
|
if_link_state_change(ha->ifp, LINK_STATE_DOWN);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
qls_free_tx_ring_dma(qla_host_t *ha, int r_idx)
|
|
{
|
|
if (ha->tx_ring[r_idx].flags.wq_dma) {
|
|
qls_free_dmabuf(ha, &ha->tx_ring[r_idx].wq_dma);
|
|
ha->tx_ring[r_idx].flags.wq_dma = 0;
|
|
}
|
|
|
|
if (ha->tx_ring[r_idx].flags.privb_dma) {
|
|
qls_free_dmabuf(ha, &ha->tx_ring[r_idx].privb_dma);
|
|
ha->tx_ring[r_idx].flags.privb_dma = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
qls_free_tx_dma(qla_host_t *ha)
|
|
{
|
|
int i, j;
|
|
qla_tx_buf_t *txb;
|
|
|
|
for (i = 0; i < ha->num_tx_rings; i++) {
|
|
|
|
qls_free_tx_ring_dma(ha, i);
|
|
|
|
for (j = 0; j < NUM_TX_DESCRIPTORS; j++) {
|
|
|
|
txb = &ha->tx_ring[i].tx_buf[j];
|
|
|
|
if (txb->map) {
|
|
bus_dmamap_destroy(ha->tx_tag, txb->map);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ha->tx_tag != NULL) {
|
|
bus_dma_tag_destroy(ha->tx_tag);
|
|
ha->tx_tag = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int
|
|
qls_alloc_tx_ring_dma(qla_host_t *ha, int ridx)
|
|
{
|
|
int ret = 0, i;
|
|
uint8_t *v_addr;
|
|
bus_addr_t p_addr;
|
|
qla_tx_buf_t *txb;
|
|
device_t dev = ha->pci_dev;
|
|
|
|
ha->tx_ring[ridx].wq_dma.alignment = 8;
|
|
ha->tx_ring[ridx].wq_dma.size =
|
|
NUM_TX_DESCRIPTORS * (sizeof (q81_tx_cmd_t));
|
|
|
|
ret = qls_alloc_dmabuf(ha, &ha->tx_ring[ridx].wq_dma);
|
|
|
|
if (ret) {
|
|
device_printf(dev, "%s: [%d] txr failed\n", __func__, ridx);
|
|
goto qls_alloc_tx_ring_dma_exit;
|
|
}
|
|
ha->tx_ring[ridx].flags.wq_dma = 1;
|
|
|
|
ha->tx_ring[ridx].privb_dma.alignment = 8;
|
|
ha->tx_ring[ridx].privb_dma.size = QLA_TX_PRIVATE_BSIZE;
|
|
|
|
ret = qls_alloc_dmabuf(ha, &ha->tx_ring[ridx].privb_dma);
|
|
|
|
if (ret) {
|
|
device_printf(dev, "%s: [%d] oalb failed\n", __func__, ridx);
|
|
goto qls_alloc_tx_ring_dma_exit;
|
|
}
|
|
|
|
ha->tx_ring[ridx].flags.privb_dma = 1;
|
|
|
|
ha->tx_ring[ridx].wq_vaddr = ha->tx_ring[ridx].wq_dma.dma_b;
|
|
ha->tx_ring[ridx].wq_paddr = ha->tx_ring[ridx].wq_dma.dma_addr;
|
|
|
|
v_addr = ha->tx_ring[ridx].privb_dma.dma_b;
|
|
p_addr = ha->tx_ring[ridx].privb_dma.dma_addr;
|
|
|
|
ha->tx_ring[ridx].wq_icb_vaddr = v_addr;
|
|
ha->tx_ring[ridx].wq_icb_paddr = p_addr;
|
|
|
|
ha->tx_ring[ridx].txr_cons_vaddr =
|
|
(uint32_t *)(v_addr + (PAGE_SIZE >> 1));
|
|
ha->tx_ring[ridx].txr_cons_paddr = p_addr + (PAGE_SIZE >> 1);
|
|
|
|
v_addr = v_addr + (PAGE_SIZE >> 1);
|
|
p_addr = p_addr + (PAGE_SIZE >> 1);
|
|
|
|
txb = ha->tx_ring[ridx].tx_buf;
|
|
|
|
for (i = 0; i < NUM_TX_DESCRIPTORS; i++) {
|
|
|
|
txb[i].oal_vaddr = v_addr;
|
|
txb[i].oal_paddr = p_addr;
|
|
|
|
v_addr = v_addr + QLA_OAL_BLK_SIZE;
|
|
p_addr = p_addr + QLA_OAL_BLK_SIZE;
|
|
}
|
|
|
|
qls_alloc_tx_ring_dma_exit:
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_alloc_tx_dma(qla_host_t *ha)
|
|
{
|
|
int i, j;
|
|
int ret = 0;
|
|
qla_tx_buf_t *txb;
|
|
|
|
if (bus_dma_tag_create(NULL, /* parent */
|
|
1, 0, /* alignment, bounds */
|
|
BUS_SPACE_MAXADDR, /* lowaddr */
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
NULL, NULL, /* filter, filterarg */
|
|
QLA_MAX_TSO_FRAME_SIZE, /* maxsize */
|
|
QLA_MAX_SEGMENTS, /* nsegments */
|
|
PAGE_SIZE, /* maxsegsize */
|
|
BUS_DMA_ALLOCNOW, /* flags */
|
|
NULL, /* lockfunc */
|
|
NULL, /* lockfuncarg */
|
|
&ha->tx_tag)) {
|
|
device_printf(ha->pci_dev, "%s: tx_tag alloc failed\n",
|
|
__func__);
|
|
return (ENOMEM);
|
|
}
|
|
|
|
for (i = 0; i < ha->num_tx_rings; i++) {
|
|
|
|
ret = qls_alloc_tx_ring_dma(ha, i);
|
|
|
|
if (ret) {
|
|
qls_free_tx_dma(ha);
|
|
break;
|
|
}
|
|
|
|
for (j = 0; j < NUM_TX_DESCRIPTORS; j++) {
|
|
|
|
txb = &ha->tx_ring[i].tx_buf[j];
|
|
|
|
ret = bus_dmamap_create(ha->tx_tag,
|
|
BUS_DMA_NOWAIT, &txb->map);
|
|
if (ret) {
|
|
ha->err_tx_dmamap_create++;
|
|
device_printf(ha->pci_dev,
|
|
"%s: bus_dmamap_create failed[%d, %d, %d]\n",
|
|
__func__, ret, i, j);
|
|
|
|
qls_free_tx_dma(ha);
|
|
|
|
return (ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static void
|
|
qls_free_rss_dma(qla_host_t *ha)
|
|
{
|
|
qls_free_dmabuf(ha, &ha->rss_dma);
|
|
ha->flags.rss_dma = 0;
|
|
}
|
|
|
|
static int
|
|
qls_alloc_rss_dma(qla_host_t *ha)
|
|
{
|
|
int ret = 0;
|
|
|
|
ha->rss_dma.alignment = 4;
|
|
ha->rss_dma.size = PAGE_SIZE;
|
|
|
|
ret = qls_alloc_dmabuf(ha, &ha->rss_dma);
|
|
|
|
if (ret)
|
|
device_printf(ha->pci_dev, "%s: failed\n", __func__);
|
|
else
|
|
ha->flags.rss_dma = 1;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static void
|
|
qls_free_mpi_dma(qla_host_t *ha)
|
|
{
|
|
qls_free_dmabuf(ha, &ha->mpi_dma);
|
|
ha->flags.mpi_dma = 0;
|
|
}
|
|
|
|
static int
|
|
qls_alloc_mpi_dma(qla_host_t *ha)
|
|
{
|
|
int ret = 0;
|
|
|
|
ha->mpi_dma.alignment = 4;
|
|
ha->mpi_dma.size = (0x4000 * 4);
|
|
|
|
ret = qls_alloc_dmabuf(ha, &ha->mpi_dma);
|
|
if (ret)
|
|
device_printf(ha->pci_dev, "%s: failed\n", __func__);
|
|
else
|
|
ha->flags.mpi_dma = 1;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static void
|
|
qls_free_rx_ring_dma(qla_host_t *ha, int ridx)
|
|
{
|
|
if (ha->rx_ring[ridx].flags.cq_dma) {
|
|
qls_free_dmabuf(ha, &ha->rx_ring[ridx].cq_dma);
|
|
ha->rx_ring[ridx].flags.cq_dma = 0;
|
|
}
|
|
|
|
if (ha->rx_ring[ridx].flags.lbq_dma) {
|
|
qls_free_dmabuf(ha, &ha->rx_ring[ridx].lbq_dma);
|
|
ha->rx_ring[ridx].flags.lbq_dma = 0;
|
|
}
|
|
|
|
if (ha->rx_ring[ridx].flags.sbq_dma) {
|
|
qls_free_dmabuf(ha, &ha->rx_ring[ridx].sbq_dma);
|
|
ha->rx_ring[ridx].flags.sbq_dma = 0;
|
|
}
|
|
|
|
if (ha->rx_ring[ridx].flags.lb_dma) {
|
|
qls_free_dmabuf(ha, &ha->rx_ring[ridx].lb_dma);
|
|
ha->rx_ring[ridx].flags.lb_dma = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
qls_free_rx_dma(qla_host_t *ha)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ha->num_rx_rings; i++) {
|
|
qls_free_rx_ring_dma(ha, i);
|
|
}
|
|
|
|
if (ha->rx_tag != NULL) {
|
|
bus_dma_tag_destroy(ha->rx_tag);
|
|
ha->rx_tag = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int
|
|
qls_alloc_rx_ring_dma(qla_host_t *ha, int ridx)
|
|
{
|
|
int i, ret = 0;
|
|
uint8_t *v_addr;
|
|
bus_addr_t p_addr;
|
|
volatile q81_bq_addr_e_t *bq_e;
|
|
device_t dev = ha->pci_dev;
|
|
|
|
ha->rx_ring[ridx].cq_dma.alignment = 128;
|
|
ha->rx_ring[ridx].cq_dma.size =
|
|
(NUM_CQ_ENTRIES * (sizeof (q81_cq_e_t))) + PAGE_SIZE;
|
|
|
|
ret = qls_alloc_dmabuf(ha, &ha->rx_ring[ridx].cq_dma);
|
|
|
|
if (ret) {
|
|
device_printf(dev, "%s: [%d] cq failed\n", __func__, ridx);
|
|
goto qls_alloc_rx_ring_dma_exit;
|
|
}
|
|
ha->rx_ring[ridx].flags.cq_dma = 1;
|
|
|
|
ha->rx_ring[ridx].lbq_dma.alignment = 8;
|
|
ha->rx_ring[ridx].lbq_dma.size = QLA_LGBQ_AND_TABLE_SIZE;
|
|
|
|
ret = qls_alloc_dmabuf(ha, &ha->rx_ring[ridx].lbq_dma);
|
|
|
|
if (ret) {
|
|
device_printf(dev, "%s: [%d] lbq failed\n", __func__, ridx);
|
|
goto qls_alloc_rx_ring_dma_exit;
|
|
}
|
|
ha->rx_ring[ridx].flags.lbq_dma = 1;
|
|
|
|
ha->rx_ring[ridx].sbq_dma.alignment = 8;
|
|
ha->rx_ring[ridx].sbq_dma.size = QLA_SMBQ_AND_TABLE_SIZE;
|
|
|
|
ret = qls_alloc_dmabuf(ha, &ha->rx_ring[ridx].sbq_dma);
|
|
|
|
if (ret) {
|
|
device_printf(dev, "%s: [%d] sbq failed\n", __func__, ridx);
|
|
goto qls_alloc_rx_ring_dma_exit;
|
|
}
|
|
ha->rx_ring[ridx].flags.sbq_dma = 1;
|
|
|
|
ha->rx_ring[ridx].lb_dma.alignment = 8;
|
|
ha->rx_ring[ridx].lb_dma.size = (QLA_LGB_SIZE * QLA_NUM_LGB_ENTRIES);
|
|
|
|
ret = qls_alloc_dmabuf(ha, &ha->rx_ring[ridx].lb_dma);
|
|
if (ret) {
|
|
device_printf(dev, "%s: [%d] lb failed\n", __func__, ridx);
|
|
goto qls_alloc_rx_ring_dma_exit;
|
|
}
|
|
ha->rx_ring[ridx].flags.lb_dma = 1;
|
|
|
|
bzero(ha->rx_ring[ridx].cq_dma.dma_b, ha->rx_ring[ridx].cq_dma.size);
|
|
bzero(ha->rx_ring[ridx].lbq_dma.dma_b, ha->rx_ring[ridx].lbq_dma.size);
|
|
bzero(ha->rx_ring[ridx].sbq_dma.dma_b, ha->rx_ring[ridx].sbq_dma.size);
|
|
bzero(ha->rx_ring[ridx].lb_dma.dma_b, ha->rx_ring[ridx].lb_dma.size);
|
|
|
|
/* completion queue */
|
|
ha->rx_ring[ridx].cq_base_vaddr = ha->rx_ring[ridx].cq_dma.dma_b;
|
|
ha->rx_ring[ridx].cq_base_paddr = ha->rx_ring[ridx].cq_dma.dma_addr;
|
|
|
|
v_addr = ha->rx_ring[ridx].cq_dma.dma_b;
|
|
p_addr = ha->rx_ring[ridx].cq_dma.dma_addr;
|
|
|
|
v_addr = v_addr + (NUM_CQ_ENTRIES * (sizeof (q81_cq_e_t)));
|
|
p_addr = p_addr + (NUM_CQ_ENTRIES * (sizeof (q81_cq_e_t)));
|
|
|
|
/* completion queue icb */
|
|
ha->rx_ring[ridx].cq_icb_vaddr = v_addr;
|
|
ha->rx_ring[ridx].cq_icb_paddr = p_addr;
|
|
|
|
v_addr = v_addr + (PAGE_SIZE >> 2);
|
|
p_addr = p_addr + (PAGE_SIZE >> 2);
|
|
|
|
/* completion queue index register */
|
|
ha->rx_ring[ridx].cqi_vaddr = (uint32_t *)v_addr;
|
|
ha->rx_ring[ridx].cqi_paddr = p_addr;
|
|
|
|
v_addr = ha->rx_ring[ridx].lbq_dma.dma_b;
|
|
p_addr = ha->rx_ring[ridx].lbq_dma.dma_addr;
|
|
|
|
/* large buffer queue address table */
|
|
ha->rx_ring[ridx].lbq_addr_tbl_vaddr = v_addr;
|
|
ha->rx_ring[ridx].lbq_addr_tbl_paddr = p_addr;
|
|
|
|
/* large buffer queue */
|
|
ha->rx_ring[ridx].lbq_vaddr = v_addr + PAGE_SIZE;
|
|
ha->rx_ring[ridx].lbq_paddr = p_addr + PAGE_SIZE;
|
|
|
|
v_addr = ha->rx_ring[ridx].sbq_dma.dma_b;
|
|
p_addr = ha->rx_ring[ridx].sbq_dma.dma_addr;
|
|
|
|
/* small buffer queue address table */
|
|
ha->rx_ring[ridx].sbq_addr_tbl_vaddr = v_addr;
|
|
ha->rx_ring[ridx].sbq_addr_tbl_paddr = p_addr;
|
|
|
|
/* small buffer queue */
|
|
ha->rx_ring[ridx].sbq_vaddr = v_addr + PAGE_SIZE;
|
|
ha->rx_ring[ridx].sbq_paddr = p_addr + PAGE_SIZE;
|
|
|
|
ha->rx_ring[ridx].lb_vaddr = ha->rx_ring[ridx].lb_dma.dma_b;
|
|
ha->rx_ring[ridx].lb_paddr = ha->rx_ring[ridx].lb_dma.dma_addr;
|
|
|
|
/* Initialize Large Buffer Queue Table */
|
|
|
|
p_addr = ha->rx_ring[ridx].lbq_paddr;
|
|
bq_e = ha->rx_ring[ridx].lbq_addr_tbl_vaddr;
|
|
|
|
bq_e->addr_lo = p_addr & 0xFFFFFFFF;
|
|
bq_e->addr_hi = (p_addr >> 32) & 0xFFFFFFFF;
|
|
|
|
p_addr = ha->rx_ring[ridx].lb_paddr;
|
|
bq_e = ha->rx_ring[ridx].lbq_vaddr;
|
|
|
|
for (i = 0; i < QLA_NUM_LGB_ENTRIES; i++) {
|
|
bq_e->addr_lo = p_addr & 0xFFFFFFFF;
|
|
bq_e->addr_hi = (p_addr >> 32) & 0xFFFFFFFF;
|
|
|
|
p_addr = p_addr + QLA_LGB_SIZE;
|
|
bq_e++;
|
|
}
|
|
|
|
/* Initialize Small Buffer Queue Table */
|
|
|
|
p_addr = ha->rx_ring[ridx].sbq_paddr;
|
|
bq_e = ha->rx_ring[ridx].sbq_addr_tbl_vaddr;
|
|
|
|
for (i =0; i < (QLA_SBQ_SIZE/QLA_PAGE_SIZE); i++) {
|
|
bq_e->addr_lo = p_addr & 0xFFFFFFFF;
|
|
bq_e->addr_hi = (p_addr >> 32) & 0xFFFFFFFF;
|
|
|
|
p_addr = p_addr + QLA_PAGE_SIZE;
|
|
bq_e++;
|
|
}
|
|
|
|
qls_alloc_rx_ring_dma_exit:
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_alloc_rx_dma(qla_host_t *ha)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
|
|
if (bus_dma_tag_create(NULL, /* parent */
|
|
1, 0, /* alignment, bounds */
|
|
BUS_SPACE_MAXADDR, /* lowaddr */
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
NULL, NULL, /* filter, filterarg */
|
|
MJUM9BYTES, /* maxsize */
|
|
1, /* nsegments */
|
|
MJUM9BYTES, /* maxsegsize */
|
|
BUS_DMA_ALLOCNOW, /* flags */
|
|
NULL, /* lockfunc */
|
|
NULL, /* lockfuncarg */
|
|
&ha->rx_tag)) {
|
|
|
|
device_printf(ha->pci_dev, "%s: rx_tag alloc failed\n",
|
|
__func__);
|
|
|
|
return (ENOMEM);
|
|
}
|
|
|
|
for (i = 0; i < ha->num_rx_rings; i++) {
|
|
ret = qls_alloc_rx_ring_dma(ha, i);
|
|
|
|
if (ret) {
|
|
qls_free_rx_dma(ha);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_wait_for_flash_ready(qla_host_t *ha)
|
|
{
|
|
uint32_t data32;
|
|
uint32_t count = 3;
|
|
|
|
while (count--) {
|
|
|
|
data32 = READ_REG32(ha, Q81_CTL_FLASH_ADDR);
|
|
|
|
if (data32 & Q81_CTL_FLASH_ADDR_ERR)
|
|
goto qls_wait_for_flash_ready_exit;
|
|
|
|
if (data32 & Q81_CTL_FLASH_ADDR_RDY)
|
|
return (0);
|
|
|
|
QLA_USEC_DELAY(100);
|
|
}
|
|
|
|
qls_wait_for_flash_ready_exit:
|
|
QL_DPRINT1((ha->pci_dev, "%s: failed\n", __func__));
|
|
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Name: qls_rd_flash32
|
|
* Function: Read Flash Memory
|
|
*/
|
|
int
|
|
qls_rd_flash32(qla_host_t *ha, uint32_t addr, uint32_t *data)
|
|
{
|
|
int ret;
|
|
|
|
ret = qls_wait_for_flash_ready(ha);
|
|
|
|
if (ret)
|
|
return (ret);
|
|
|
|
WRITE_REG32(ha, Q81_CTL_FLASH_ADDR, (addr | Q81_CTL_FLASH_ADDR_R));
|
|
|
|
ret = qls_wait_for_flash_ready(ha);
|
|
|
|
if (ret)
|
|
return (ret);
|
|
|
|
*data = READ_REG32(ha, Q81_CTL_FLASH_DATA);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
qls_flash_validate(qla_host_t *ha, const char *signature)
|
|
{
|
|
uint16_t csum16 = 0;
|
|
uint16_t *data16;
|
|
int i;
|
|
|
|
if (bcmp(ha->flash.id, signature, 4)) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: invalid signature "
|
|
"%x:%x:%x:%x %s\n", __func__, ha->flash.id[0],
|
|
ha->flash.id[1], ha->flash.id[2], ha->flash.id[3],
|
|
signature));
|
|
return(-1);
|
|
}
|
|
|
|
data16 = (uint16_t *)&ha->flash;
|
|
|
|
for (i = 0; i < (sizeof (q81_flash_t) >> 1); i++) {
|
|
csum16 += *data16++;
|
|
}
|
|
|
|
if (csum16) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: invalid checksum\n", __func__));
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
qls_rd_nic_params(qla_host_t *ha)
|
|
{
|
|
int i, ret = 0;
|
|
uint32_t faddr;
|
|
uint32_t *qflash;
|
|
|
|
if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_FLASH, Q81_CTL_SEM_SET_FLASH)) {
|
|
QL_DPRINT1((ha->pci_dev, "%s: semlock failed\n", __func__));
|
|
return(-1);
|
|
}
|
|
|
|
if ((ha->pci_func & 0x1) == 0)
|
|
faddr = Q81_F0_FLASH_OFFSET >> 2;
|
|
else
|
|
faddr = Q81_F1_FLASH_OFFSET >> 2;
|
|
|
|
qflash = (uint32_t *)&ha->flash;
|
|
|
|
for (i = 0; i < (sizeof(q81_flash_t) >> 2) ; i++) {
|
|
|
|
ret = qls_rd_flash32(ha, faddr, qflash);
|
|
|
|
if (ret)
|
|
goto qls_rd_flash_data_exit;
|
|
|
|
faddr++;
|
|
qflash++;
|
|
}
|
|
|
|
QL_DUMP_BUFFER8(ha, __func__, (&ha->flash), (sizeof (q81_flash_t)));
|
|
|
|
ret = qls_flash_validate(ha, Q81_FLASH_ID);
|
|
|
|
if (ret)
|
|
goto qls_rd_flash_data_exit;
|
|
|
|
bcopy(ha->flash.mac_addr0, ha->mac_addr, ETHER_ADDR_LEN);
|
|
|
|
QL_DPRINT1((ha->pci_dev, "%s: mac %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
__func__, ha->mac_addr[0], ha->mac_addr[1], ha->mac_addr[2],
|
|
ha->mac_addr[3], ha->mac_addr[4], ha->mac_addr[5]));
|
|
|
|
qls_rd_flash_data_exit:
|
|
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_FLASH);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
qls_sem_lock(qla_host_t *ha, uint32_t mask, uint32_t value)
|
|
{
|
|
uint32_t count = 30;
|
|
uint32_t data;
|
|
|
|
while (count--) {
|
|
WRITE_REG32(ha, Q81_CTL_SEMAPHORE, (mask|value));
|
|
|
|
data = READ_REG32(ha, Q81_CTL_SEMAPHORE);
|
|
|
|
if (data & value) {
|
|
return (0);
|
|
} else {
|
|
QLA_USEC_DELAY(100);
|
|
}
|
|
}
|
|
ha->qla_initiate_recovery = 1;
|
|
return (-1);
|
|
}
|
|
|
|
static void
|
|
qls_sem_unlock(qla_host_t *ha, uint32_t mask)
|
|
{
|
|
WRITE_REG32(ha, Q81_CTL_SEMAPHORE, mask);
|
|
}
|
|
|
|
static int
|
|
qls_wait_for_proc_addr_ready(qla_host_t *ha)
|
|
{
|
|
uint32_t data32;
|
|
uint32_t count = 3;
|
|
|
|
while (count--) {
|
|
|
|
data32 = READ_REG32(ha, Q81_CTL_PROC_ADDR);
|
|
|
|
if (data32 & Q81_CTL_PROC_ADDR_ERR)
|
|
goto qls_wait_for_proc_addr_ready_exit;
|
|
|
|
if (data32 & Q81_CTL_PROC_ADDR_RDY)
|
|
return (0);
|
|
|
|
QLA_USEC_DELAY(100);
|
|
}
|
|
|
|
qls_wait_for_proc_addr_ready_exit:
|
|
QL_DPRINT1((ha->pci_dev, "%s: failed\n", __func__));
|
|
|
|
ha->qla_initiate_recovery = 1;
|
|
return (-1);
|
|
}
|
|
|
|
static int
|
|
qls_proc_addr_rd_reg(qla_host_t *ha, uint32_t addr_module, uint32_t reg,
|
|
uint32_t *data)
|
|
{
|
|
int ret;
|
|
uint32_t value;
|
|
|
|
ret = qls_wait_for_proc_addr_ready(ha);
|
|
|
|
if (ret)
|
|
goto qls_proc_addr_rd_reg_exit;
|
|
|
|
value = addr_module | reg | Q81_CTL_PROC_ADDR_READ;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_PROC_ADDR, value);
|
|
|
|
ret = qls_wait_for_proc_addr_ready(ha);
|
|
|
|
if (ret)
|
|
goto qls_proc_addr_rd_reg_exit;
|
|
|
|
*data = READ_REG32(ha, Q81_CTL_PROC_DATA);
|
|
|
|
qls_proc_addr_rd_reg_exit:
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_proc_addr_wr_reg(qla_host_t *ha, uint32_t addr_module, uint32_t reg,
|
|
uint32_t data)
|
|
{
|
|
int ret;
|
|
uint32_t value;
|
|
|
|
ret = qls_wait_for_proc_addr_ready(ha);
|
|
|
|
if (ret)
|
|
goto qls_proc_addr_wr_reg_exit;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_PROC_DATA, data);
|
|
|
|
value = addr_module | reg;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_PROC_ADDR, value);
|
|
|
|
ret = qls_wait_for_proc_addr_ready(ha);
|
|
|
|
qls_proc_addr_wr_reg_exit:
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_hw_nic_reset(qla_host_t *ha)
|
|
{
|
|
int count;
|
|
uint32_t data;
|
|
device_t dev = ha->pci_dev;
|
|
|
|
ha->hw_init = 0;
|
|
|
|
data = (Q81_CTL_RESET_FUNC << Q81_CTL_RESET_MASK_SHIFT) |
|
|
Q81_CTL_RESET_FUNC;
|
|
WRITE_REG32(ha, Q81_CTL_RESET, data);
|
|
|
|
count = 10;
|
|
while (count--) {
|
|
data = READ_REG32(ha, Q81_CTL_RESET);
|
|
if ((data & Q81_CTL_RESET_FUNC) == 0)
|
|
break;
|
|
QLA_USEC_DELAY(10);
|
|
}
|
|
if (count == 0) {
|
|
device_printf(dev, "%s: Bit 15 not cleared after Reset\n",
|
|
__func__);
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
qls_hw_reset(qla_host_t *ha)
|
|
{
|
|
device_t dev = ha->pci_dev;
|
|
int ret;
|
|
int count;
|
|
uint32_t data;
|
|
|
|
QL_DPRINT2((ha->pci_dev, "%s:enter[%d]\n", __func__, ha->hw_init));
|
|
|
|
if (ha->hw_init == 0) {
|
|
ret = qls_hw_nic_reset(ha);
|
|
goto qls_hw_reset_exit;
|
|
}
|
|
|
|
ret = qls_clear_routing_table(ha);
|
|
if (ret)
|
|
goto qls_hw_reset_exit;
|
|
|
|
ret = qls_mbx_set_mgmt_ctrl(ha, Q81_MBX_SET_MGMT_CTL_STOP);
|
|
if (ret)
|
|
goto qls_hw_reset_exit;
|
|
|
|
/*
|
|
* Wait for FIFO to empty
|
|
*/
|
|
count = 5;
|
|
while (count--) {
|
|
data = READ_REG32(ha, Q81_CTL_STATUS);
|
|
if (data & Q81_CTL_STATUS_NFE)
|
|
break;
|
|
qls_mdelay(__func__, 100);
|
|
}
|
|
if (count == 0) {
|
|
device_printf(dev, "%s: NFE bit not set\n", __func__);
|
|
goto qls_hw_reset_exit;
|
|
}
|
|
|
|
count = 5;
|
|
while (count--) {
|
|
(void)qls_mbx_get_mgmt_ctrl(ha, &data);
|
|
|
|
if ((data & Q81_MBX_GET_MGMT_CTL_FIFO_EMPTY) &&
|
|
(data & Q81_MBX_GET_MGMT_CTL_SET_MGMT))
|
|
break;
|
|
qls_mdelay(__func__, 100);
|
|
}
|
|
if (count == 0)
|
|
goto qls_hw_reset_exit;
|
|
|
|
/*
|
|
* Reset the NIC function
|
|
*/
|
|
ret = qls_hw_nic_reset(ha);
|
|
if (ret)
|
|
goto qls_hw_reset_exit;
|
|
|
|
ret = qls_mbx_set_mgmt_ctrl(ha, Q81_MBX_SET_MGMT_CTL_RESUME);
|
|
|
|
qls_hw_reset_exit:
|
|
if (ret)
|
|
device_printf(dev, "%s: failed\n", __func__);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* MPI Related Functions
|
|
*/
|
|
int
|
|
qls_mpi_risc_rd_reg(qla_host_t *ha, uint32_t reg, uint32_t *data)
|
|
{
|
|
int ret;
|
|
|
|
ret = qls_proc_addr_rd_reg(ha, Q81_CTL_PROC_ADDR_MPI_RISC,
|
|
reg, data);
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
qls_mpi_risc_wr_reg(qla_host_t *ha, uint32_t reg, uint32_t data)
|
|
{
|
|
int ret;
|
|
|
|
ret = qls_proc_addr_wr_reg(ha, Q81_CTL_PROC_ADDR_MPI_RISC,
|
|
reg, data);
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
qls_mbx_rd_reg(qla_host_t *ha, uint32_t reg, uint32_t *data)
|
|
{
|
|
int ret;
|
|
|
|
if ((ha->pci_func & 0x1) == 0)
|
|
reg += Q81_FUNC0_MBX_OUT_REG0;
|
|
else
|
|
reg += Q81_FUNC1_MBX_OUT_REG0;
|
|
|
|
ret = qls_mpi_risc_rd_reg(ha, reg, data);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
qls_mbx_wr_reg(qla_host_t *ha, uint32_t reg, uint32_t data)
|
|
{
|
|
int ret;
|
|
|
|
if ((ha->pci_func & 0x1) == 0)
|
|
reg += Q81_FUNC0_MBX_IN_REG0;
|
|
else
|
|
reg += Q81_FUNC1_MBX_IN_REG0;
|
|
|
|
ret = qls_mpi_risc_wr_reg(ha, reg, data);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
|
|
static int
|
|
qls_mbx_cmd(qla_host_t *ha, uint32_t *in_mbx, uint32_t i_count,
|
|
uint32_t *out_mbx, uint32_t o_count)
|
|
{
|
|
int i, ret = -1;
|
|
uint32_t data32, mbx_cmd = 0;
|
|
uint32_t count = 50;
|
|
|
|
QL_DPRINT2((ha->pci_dev, "%s: enter[0x%08x 0x%08x 0x%08x]\n",
|
|
__func__, *in_mbx, *(in_mbx + 1), *(in_mbx + 2)));
|
|
|
|
data32 = READ_REG32(ha, Q81_CTL_HOST_CMD_STATUS);
|
|
|
|
if (data32 & Q81_CTL_HCS_HTR_INTR) {
|
|
device_printf(ha->pci_dev, "%s: cmd_status[0x%08x]\n",
|
|
__func__, data32);
|
|
goto qls_mbx_cmd_exit;
|
|
}
|
|
|
|
if (qls_sem_lock(ha, Q81_CTL_SEM_MASK_PROC_ADDR_NIC_RCV,
|
|
Q81_CTL_SEM_SET_PROC_ADDR_NIC_RCV)) {
|
|
device_printf(ha->pci_dev, "%s: semlock failed\n", __func__);
|
|
goto qls_mbx_cmd_exit;
|
|
}
|
|
|
|
ha->mbx_done = 0;
|
|
|
|
mbx_cmd = *in_mbx;
|
|
|
|
for (i = 0; i < i_count; i++) {
|
|
|
|
ret = qls_mbx_wr_reg(ha, i, *in_mbx);
|
|
|
|
if (ret) {
|
|
device_printf(ha->pci_dev,
|
|
"%s: mbx_wr[%d, 0x%08x] failed\n", __func__,
|
|
i, *in_mbx);
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_PROC_ADDR_NIC_RCV);
|
|
goto qls_mbx_cmd_exit;
|
|
}
|
|
|
|
in_mbx++;
|
|
}
|
|
WRITE_REG32(ha, Q81_CTL_HOST_CMD_STATUS, Q81_CTL_HCS_CMD_SET_HTR_INTR);
|
|
|
|
qls_sem_unlock(ha, Q81_CTL_SEM_MASK_PROC_ADDR_NIC_RCV);
|
|
|
|
ret = -1;
|
|
ha->mbx_done = 0;
|
|
|
|
while (count--) {
|
|
|
|
if (ha->flags.intr_enable == 0) {
|
|
data32 = READ_REG32(ha, Q81_CTL_STATUS);
|
|
|
|
if (!(data32 & Q81_CTL_STATUS_PI)) {
|
|
qls_mdelay(__func__, 100);
|
|
continue;
|
|
}
|
|
|
|
ret = qls_mbx_rd_reg(ha, 0, &data32);
|
|
|
|
if (ret == 0 ) {
|
|
if ((data32 & 0xF000) == 0x4000) {
|
|
|
|
out_mbx[0] = data32;
|
|
|
|
for (i = 1; i < o_count; i++) {
|
|
ret = qls_mbx_rd_reg(ha, i,
|
|
&data32);
|
|
if (ret) {
|
|
device_printf(
|
|
ha->pci_dev,
|
|
"%s: mbx_rd[%d]"
|
|
" failed\n",
|
|
__func__, i);
|
|
break;
|
|
}
|
|
out_mbx[i] = data32;
|
|
}
|
|
break;
|
|
} else if ((data32 & 0xF000) == 0x8000) {
|
|
count = 50;
|
|
WRITE_REG32(ha,\
|
|
Q81_CTL_HOST_CMD_STATUS,\
|
|
Q81_CTL_HCS_CMD_CLR_RTH_INTR);
|
|
}
|
|
}
|
|
} else {
|
|
if (ha->mbx_done) {
|
|
for (i = 1; i < o_count; i++) {
|
|
out_mbx[i] = ha->mbox[i];
|
|
}
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
qls_mdelay(__func__, 1000);
|
|
}
|
|
|
|
qls_mbx_cmd_exit:
|
|
|
|
if (ha->flags.intr_enable == 0) {
|
|
WRITE_REG32(ha, Q81_CTL_HOST_CMD_STATUS,\
|
|
Q81_CTL_HCS_CMD_CLR_RTH_INTR);
|
|
}
|
|
|
|
if (ret) {
|
|
ha->qla_initiate_recovery = 1;
|
|
}
|
|
|
|
QL_DPRINT2((ha->pci_dev, "%s: exit[%d]\n", __func__, ret));
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
qls_mbx_set_mgmt_ctrl(qla_host_t *ha, uint32_t t_ctrl)
|
|
{
|
|
uint32_t *mbox;
|
|
device_t dev = ha->pci_dev;
|
|
|
|
mbox = ha->mbox;
|
|
bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS));
|
|
|
|
mbox[0] = Q81_MBX_SET_MGMT_CTL;
|
|
mbox[1] = t_ctrl;
|
|
|
|
if (qls_mbx_cmd(ha, mbox, 2, mbox, 1)) {
|
|
device_printf(dev, "%s failed\n", __func__);
|
|
return (-1);
|
|
}
|
|
|
|
if ((mbox[0] == Q81_MBX_CMD_COMPLETE) ||
|
|
((t_ctrl == Q81_MBX_SET_MGMT_CTL_STOP) &&
|
|
(mbox[0] == Q81_MBX_CMD_ERROR))){
|
|
return (0);
|
|
}
|
|
device_printf(dev, "%s failed [0x%08x]\n", __func__, mbox[0]);
|
|
return (-1);
|
|
|
|
}
|
|
|
|
static int
|
|
qls_mbx_get_mgmt_ctrl(qla_host_t *ha, uint32_t *t_status)
|
|
{
|
|
uint32_t *mbox;
|
|
device_t dev = ha->pci_dev;
|
|
|
|
*t_status = 0;
|
|
|
|
mbox = ha->mbox;
|
|
bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS));
|
|
|
|
mbox[0] = Q81_MBX_GET_MGMT_CTL;
|
|
|
|
if (qls_mbx_cmd(ha, mbox, 1, mbox, 2)) {
|
|
device_printf(dev, "%s failed\n", __func__);
|
|
return (-1);
|
|
}
|
|
|
|
*t_status = mbox[1];
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
qls_mbx_get_link_status(qla_host_t *ha)
|
|
{
|
|
uint32_t *mbox;
|
|
device_t dev = ha->pci_dev;
|
|
|
|
mbox = ha->mbox;
|
|
bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS));
|
|
|
|
mbox[0] = Q81_MBX_GET_LNK_STATUS;
|
|
|
|
if (qls_mbx_cmd(ha, mbox, 1, mbox, 6)) {
|
|
device_printf(dev, "%s failed\n", __func__);
|
|
return;
|
|
}
|
|
|
|
ha->link_status = mbox[1];
|
|
ha->link_down_info = mbox[2];
|
|
ha->link_hw_info = mbox[3];
|
|
ha->link_dcbx_counters = mbox[4];
|
|
ha->link_change_counters = mbox[5];
|
|
|
|
device_printf(dev, "%s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
|
__func__, mbox[0],mbox[1],mbox[2],mbox[3],mbox[4],mbox[5]);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
qls_mbx_about_fw(qla_host_t *ha)
|
|
{
|
|
uint32_t *mbox;
|
|
device_t dev = ha->pci_dev;
|
|
|
|
mbox = ha->mbox;
|
|
bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS));
|
|
|
|
mbox[0] = Q81_MBX_ABOUT_FW;
|
|
|
|
if (qls_mbx_cmd(ha, mbox, 1, mbox, 6)) {
|
|
device_printf(dev, "%s failed\n", __func__);
|
|
return;
|
|
}
|
|
|
|
device_printf(dev, "%s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
|
__func__, mbox[0],mbox[1],mbox[2],mbox[3],mbox[4],mbox[5]);
|
|
}
|
|
|
|
int
|
|
qls_mbx_dump_risc_ram(qla_host_t *ha, void *buf, uint32_t r_addr,
|
|
uint32_t r_size)
|
|
{
|
|
bus_addr_t b_paddr;
|
|
uint32_t *mbox;
|
|
device_t dev = ha->pci_dev;
|
|
|
|
mbox = ha->mbox;
|
|
bzero(mbox, (sizeof (uint32_t) * Q81_NUM_MBX_REGISTERS));
|
|
|
|
bzero(ha->mpi_dma.dma_b,(r_size << 2));
|
|
b_paddr = ha->mpi_dma.dma_addr;
|
|
|
|
mbox[0] = Q81_MBX_DUMP_RISC_RAM;
|
|
mbox[1] = r_addr & 0xFFFF;
|
|
mbox[2] = ((uint32_t)(b_paddr >> 16)) & 0xFFFF;
|
|
mbox[3] = ((uint32_t)b_paddr) & 0xFFFF;
|
|
mbox[4] = (r_size >> 16) & 0xFFFF;
|
|
mbox[5] = r_size & 0xFFFF;
|
|
mbox[6] = ((uint32_t)(b_paddr >> 48)) & 0xFFFF;
|
|
mbox[7] = ((uint32_t)(b_paddr >> 32)) & 0xFFFF;
|
|
mbox[8] = (r_addr >> 16) & 0xFFFF;
|
|
|
|
bus_dmamap_sync(ha->mpi_dma.dma_tag, ha->mpi_dma.dma_map,
|
|
BUS_DMASYNC_PREREAD);
|
|
|
|
if (qls_mbx_cmd(ha, mbox, 9, mbox, 1)) {
|
|
device_printf(dev, "%s failed\n", __func__);
|
|
return (-1);
|
|
}
|
|
if (mbox[0] != 0x4000) {
|
|
device_printf(ha->pci_dev, "%s: failed!\n", __func__);
|
|
return (-1);
|
|
} else {
|
|
bus_dmamap_sync(ha->mpi_dma.dma_tag, ha->mpi_dma.dma_map,
|
|
BUS_DMASYNC_POSTREAD);
|
|
bcopy(ha->mpi_dma.dma_b, buf, (r_size << 2));
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
qls_mpi_reset(qla_host_t *ha)
|
|
{
|
|
int count;
|
|
uint32_t data;
|
|
device_t dev = ha->pci_dev;
|
|
|
|
WRITE_REG32(ha, Q81_CTL_HOST_CMD_STATUS,\
|
|
Q81_CTL_HCS_CMD_SET_RISC_RESET);
|
|
|
|
count = 10;
|
|
while (count--) {
|
|
data = READ_REG32(ha, Q81_CTL_HOST_CMD_STATUS);
|
|
if (data & Q81_CTL_HCS_RISC_RESET) {
|
|
WRITE_REG32(ha, Q81_CTL_HOST_CMD_STATUS,\
|
|
Q81_CTL_HCS_CMD_CLR_RISC_RESET);
|
|
break;
|
|
}
|
|
qls_mdelay(__func__, 10);
|
|
}
|
|
if (count == 0) {
|
|
device_printf(dev, "%s: failed\n", __func__);
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|