004b26b8e1
All files in this driver directory contain the following notice: See LICENSE.qlcnic for copyright and licensing details. LICENSE.qlacnic can be found in Documentation/networking/device_drivers/qlogic/. The file contains: - A copyright notice This copyright notice is redundant as all files contain the same copyright notice already - A license notice You may modify and redistribute the device driver code under the GNU General Public License (a copy of which is attached hereto as Exhibit A) published by the Free Software Foundation (version 2). This can be replaced with the corresponding SPDX license identifier (GPL-2.0-only) in the source files which reference this license file. - The full GPLv2 license text A redundant copy of LICENSES/preferred/GPL-2.0 Remove the notices and add the SPDX license identifier GPL-2.0-only to the source files. Finally remove the now redundant LICENSE.qlcnic file. Reviewed-by: Richard Fontana <rfontana@redhat.com> Reviewed-by: Jilayne Lovejoy <opensource@jilayne.com> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Acked-by: Igor Russkikh <irusskikh@marvell.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Lukas Bulwahn <lukas.bulwahn@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1430 lines
39 KiB
C
1430 lines
39 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* QLogic qlcnic NIC Driver
|
|
* Copyright (c) 2009-2013 QLogic Corporation
|
|
*/
|
|
|
|
#include "qlcnic.h"
|
|
|
|
static const struct qlcnic_mailbox_metadata qlcnic_mbx_tbl[] = {
|
|
{QLCNIC_CMD_CREATE_RX_CTX, 4, 1},
|
|
{QLCNIC_CMD_DESTROY_RX_CTX, 2, 1},
|
|
{QLCNIC_CMD_CREATE_TX_CTX, 4, 1},
|
|
{QLCNIC_CMD_DESTROY_TX_CTX, 3, 1},
|
|
{QLCNIC_CMD_INTRPT_TEST, 4, 1},
|
|
{QLCNIC_CMD_SET_MTU, 4, 1},
|
|
{QLCNIC_CMD_READ_PHY, 4, 2},
|
|
{QLCNIC_CMD_WRITE_PHY, 5, 1},
|
|
{QLCNIC_CMD_READ_HW_REG, 4, 1},
|
|
{QLCNIC_CMD_GET_FLOW_CTL, 4, 2},
|
|
{QLCNIC_CMD_SET_FLOW_CTL, 4, 1},
|
|
{QLCNIC_CMD_READ_MAX_MTU, 4, 2},
|
|
{QLCNIC_CMD_READ_MAX_LRO, 4, 2},
|
|
{QLCNIC_CMD_MAC_ADDRESS, 4, 3},
|
|
{QLCNIC_CMD_GET_PCI_INFO, 4, 1},
|
|
{QLCNIC_CMD_GET_NIC_INFO, 4, 1},
|
|
{QLCNIC_CMD_SET_NIC_INFO, 4, 1},
|
|
{QLCNIC_CMD_GET_ESWITCH_CAPABILITY, 4, 3},
|
|
{QLCNIC_CMD_TOGGLE_ESWITCH, 4, 1},
|
|
{QLCNIC_CMD_GET_ESWITCH_STATUS, 4, 3},
|
|
{QLCNIC_CMD_SET_PORTMIRRORING, 4, 1},
|
|
{QLCNIC_CMD_CONFIGURE_ESWITCH, 4, 1},
|
|
{QLCNIC_CMD_GET_MAC_STATS, 4, 1},
|
|
{QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG, 4, 3},
|
|
{QLCNIC_CMD_GET_ESWITCH_STATS, 4, 1},
|
|
{QLCNIC_CMD_CONFIG_PORT, 4, 1},
|
|
{QLCNIC_CMD_TEMP_SIZE, 4, 4},
|
|
{QLCNIC_CMD_GET_TEMP_HDR, 4, 1},
|
|
{QLCNIC_CMD_82XX_SET_DRV_VER, 4, 1},
|
|
{QLCNIC_CMD_GET_LED_STATUS, 4, 2},
|
|
{QLCNIC_CMD_MQ_TX_CONFIG_INTR, 2, 3},
|
|
{QLCNIC_CMD_DCB_QUERY_CAP, 1, 2},
|
|
{QLCNIC_CMD_DCB_QUERY_PARAM, 4, 1},
|
|
};
|
|
|
|
static inline u32 qlcnic_get_cmd_signature(struct qlcnic_hardware_context *ahw)
|
|
{
|
|
return (ahw->pci_func & 0xff) | ((ahw->fw_hal_version & 0xff) << 8) |
|
|
(0xcafe << 16);
|
|
}
|
|
|
|
/* Allocate mailbox registers */
|
|
int qlcnic_82xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx,
|
|
struct qlcnic_adapter *adapter, u32 type)
|
|
{
|
|
int i, size;
|
|
const struct qlcnic_mailbox_metadata *mbx_tbl;
|
|
|
|
mbx_tbl = qlcnic_mbx_tbl;
|
|
size = ARRAY_SIZE(qlcnic_mbx_tbl);
|
|
for (i = 0; i < size; i++) {
|
|
if (type == mbx_tbl[i].cmd) {
|
|
mbx->req.num = mbx_tbl[i].in_args;
|
|
mbx->rsp.num = mbx_tbl[i].out_args;
|
|
mbx->req.arg = kcalloc(mbx->req.num,
|
|
sizeof(u32), GFP_ATOMIC);
|
|
if (!mbx->req.arg)
|
|
return -ENOMEM;
|
|
mbx->rsp.arg = kcalloc(mbx->rsp.num,
|
|
sizeof(u32), GFP_ATOMIC);
|
|
if (!mbx->rsp.arg) {
|
|
kfree(mbx->req.arg);
|
|
mbx->req.arg = NULL;
|
|
return -ENOMEM;
|
|
}
|
|
mbx->req.arg[0] = type;
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Free up mailbox registers */
|
|
void qlcnic_free_mbx_args(struct qlcnic_cmd_args *cmd)
|
|
{
|
|
kfree(cmd->req.arg);
|
|
cmd->req.arg = NULL;
|
|
kfree(cmd->rsp.arg);
|
|
cmd->rsp.arg = NULL;
|
|
}
|
|
|
|
static u32
|
|
qlcnic_poll_rsp(struct qlcnic_adapter *adapter)
|
|
{
|
|
u32 rsp;
|
|
int timeout = 0, err = 0;
|
|
|
|
do {
|
|
/* give atleast 1ms for firmware to respond */
|
|
mdelay(1);
|
|
|
|
if (++timeout > QLCNIC_OS_CRB_RETRY_COUNT)
|
|
return QLCNIC_CDRP_RSP_TIMEOUT;
|
|
|
|
rsp = QLCRD32(adapter, QLCNIC_CDRP_CRB_OFFSET, &err);
|
|
} while (!QLCNIC_CDRP_IS_RSP(rsp));
|
|
|
|
return rsp;
|
|
}
|
|
|
|
int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_cmd_args *cmd)
|
|
{
|
|
int i, err = 0;
|
|
u32 rsp;
|
|
u32 signature;
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
struct qlcnic_hardware_context *ahw = adapter->ahw;
|
|
const char *fmt;
|
|
|
|
signature = qlcnic_get_cmd_signature(ahw);
|
|
|
|
/* Acquire semaphore before accessing CRB */
|
|
if (qlcnic_api_lock(adapter)) {
|
|
cmd->rsp.arg[0] = QLCNIC_RCODE_TIMEOUT;
|
|
return cmd->rsp.arg[0];
|
|
}
|
|
|
|
QLCWR32(adapter, QLCNIC_SIGN_CRB_OFFSET, signature);
|
|
for (i = 1; i < cmd->req.num; i++)
|
|
QLCWR32(adapter, QLCNIC_CDRP_ARG(i), cmd->req.arg[i]);
|
|
QLCWR32(adapter, QLCNIC_CDRP_CRB_OFFSET,
|
|
QLCNIC_CDRP_FORM_CMD(cmd->req.arg[0]));
|
|
rsp = qlcnic_poll_rsp(adapter);
|
|
|
|
if (rsp == QLCNIC_CDRP_RSP_TIMEOUT) {
|
|
dev_err(&pdev->dev, "command timeout, response = 0x%x\n", rsp);
|
|
cmd->rsp.arg[0] = QLCNIC_RCODE_TIMEOUT;
|
|
} else if (rsp == QLCNIC_CDRP_RSP_FAIL) {
|
|
cmd->rsp.arg[0] = QLCRD32(adapter, QLCNIC_CDRP_ARG(1), &err);
|
|
switch (cmd->rsp.arg[0]) {
|
|
case QLCNIC_RCODE_INVALID_ARGS:
|
|
fmt = "CDRP invalid args: [%d]\n";
|
|
break;
|
|
case QLCNIC_RCODE_NOT_SUPPORTED:
|
|
case QLCNIC_RCODE_NOT_IMPL:
|
|
fmt = "CDRP command not supported: [%d]\n";
|
|
break;
|
|
case QLCNIC_RCODE_NOT_PERMITTED:
|
|
fmt = "CDRP requested action not permitted: [%d]\n";
|
|
break;
|
|
case QLCNIC_RCODE_INVALID:
|
|
fmt = "CDRP invalid or unknown cmd received: [%d]\n";
|
|
break;
|
|
case QLCNIC_RCODE_TIMEOUT:
|
|
fmt = "CDRP command timeout: [%d]\n";
|
|
break;
|
|
default:
|
|
fmt = "CDRP command failed: [%d]\n";
|
|
break;
|
|
}
|
|
dev_err(&pdev->dev, fmt, cmd->rsp.arg[0]);
|
|
qlcnic_dump_mbx(adapter, cmd);
|
|
} else if (rsp == QLCNIC_CDRP_RSP_OK)
|
|
cmd->rsp.arg[0] = QLCNIC_RCODE_SUCCESS;
|
|
|
|
for (i = 1; i < cmd->rsp.num; i++)
|
|
cmd->rsp.arg[i] = QLCRD32(adapter, QLCNIC_CDRP_ARG(i), &err);
|
|
|
|
/* Release semaphore */
|
|
qlcnic_api_unlock(adapter);
|
|
return cmd->rsp.arg[0];
|
|
}
|
|
|
|
int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter, u32 fw_cmd)
|
|
{
|
|
struct qlcnic_cmd_args cmd;
|
|
u32 arg1, arg2, arg3;
|
|
char drv_string[12];
|
|
int err = 0;
|
|
|
|
memset(drv_string, 0, sizeof(drv_string));
|
|
snprintf(drv_string, sizeof(drv_string), "%d"".""%d"".""%d",
|
|
_QLCNIC_LINUX_MAJOR, _QLCNIC_LINUX_MINOR,
|
|
_QLCNIC_LINUX_SUBVERSION);
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, fw_cmd);
|
|
if (err)
|
|
return err;
|
|
|
|
memcpy(&arg1, drv_string, sizeof(u32));
|
|
memcpy(&arg2, drv_string + 4, sizeof(u32));
|
|
memcpy(&arg3, drv_string + 8, sizeof(u32));
|
|
|
|
cmd.req.arg[1] = arg1;
|
|
cmd.req.arg[2] = arg2;
|
|
cmd.req.arg[3] = arg3;
|
|
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
if (err) {
|
|
dev_info(&adapter->pdev->dev,
|
|
"Failed to set driver version in firmware\n");
|
|
err = -EIO;
|
|
}
|
|
qlcnic_free_mbx_args(&cmd);
|
|
return err;
|
|
}
|
|
|
|
int
|
|
qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu)
|
|
{
|
|
int err = 0;
|
|
struct qlcnic_cmd_args cmd;
|
|
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
|
|
|
|
if (recv_ctx->state != QLCNIC_HOST_CTX_STATE_ACTIVE)
|
|
return err;
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_MTU);
|
|
if (err)
|
|
return err;
|
|
|
|
cmd.req.arg[1] = recv_ctx->context_id;
|
|
cmd.req.arg[2] = mtu;
|
|
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
if (err) {
|
|
dev_err(&adapter->pdev->dev, "Failed to set mtu\n");
|
|
err = -EIO;
|
|
}
|
|
qlcnic_free_mbx_args(&cmd);
|
|
return err;
|
|
}
|
|
|
|
int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
|
|
{
|
|
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
|
|
struct qlcnic_hardware_context *ahw = adapter->ahw;
|
|
dma_addr_t hostrq_phys_addr, cardrsp_phys_addr;
|
|
struct net_device *netdev = adapter->netdev;
|
|
u32 temp_intr_crb_mode, temp_rds_crb_mode;
|
|
struct qlcnic_cardrsp_rds_ring *prsp_rds;
|
|
struct qlcnic_cardrsp_sds_ring *prsp_sds;
|
|
struct qlcnic_hostrq_rds_ring *prq_rds;
|
|
struct qlcnic_hostrq_sds_ring *prq_sds;
|
|
struct qlcnic_host_rds_ring *rds_ring;
|
|
struct qlcnic_host_sds_ring *sds_ring;
|
|
struct qlcnic_cardrsp_rx_ctx *prsp;
|
|
struct qlcnic_hostrq_rx_ctx *prq;
|
|
u8 i, nrds_rings, nsds_rings;
|
|
struct qlcnic_cmd_args cmd;
|
|
size_t rq_size, rsp_size;
|
|
u32 cap, reg, val, reg2;
|
|
u64 phys_addr;
|
|
u16 temp_u16;
|
|
void *addr;
|
|
int err;
|
|
|
|
nrds_rings = adapter->max_rds_rings;
|
|
nsds_rings = adapter->drv_sds_rings;
|
|
|
|
rq_size = SIZEOF_HOSTRQ_RX(struct qlcnic_hostrq_rx_ctx, nrds_rings,
|
|
nsds_rings);
|
|
rsp_size = SIZEOF_CARDRSP_RX(struct qlcnic_cardrsp_rx_ctx, nrds_rings,
|
|
nsds_rings);
|
|
|
|
addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size,
|
|
&hostrq_phys_addr, GFP_KERNEL);
|
|
if (addr == NULL)
|
|
return -ENOMEM;
|
|
prq = addr;
|
|
|
|
addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size,
|
|
&cardrsp_phys_addr, GFP_KERNEL);
|
|
if (addr == NULL) {
|
|
err = -ENOMEM;
|
|
goto out_free_rq;
|
|
}
|
|
prsp = addr;
|
|
|
|
prq->host_rsp_dma_addr = cpu_to_le64(cardrsp_phys_addr);
|
|
|
|
cap = (QLCNIC_CAP0_LEGACY_CONTEXT | QLCNIC_CAP0_LEGACY_MN
|
|
| QLCNIC_CAP0_VALIDOFF);
|
|
cap |= (QLCNIC_CAP0_JUMBO_CONTIGUOUS | QLCNIC_CAP0_LRO_CONTIGUOUS);
|
|
|
|
if (qlcnic_check_multi_tx(adapter) &&
|
|
!adapter->ahw->diag_test) {
|
|
cap |= QLCNIC_CAP0_TX_MULTI;
|
|
} else {
|
|
temp_u16 = offsetof(struct qlcnic_hostrq_rx_ctx, msix_handler);
|
|
prq->valid_field_offset = cpu_to_le16(temp_u16);
|
|
prq->txrx_sds_binding = nsds_rings - 1;
|
|
temp_intr_crb_mode = QLCNIC_HOST_INT_CRB_MODE_SHARED;
|
|
prq->host_int_crb_mode = cpu_to_le32(temp_intr_crb_mode);
|
|
temp_rds_crb_mode = QLCNIC_HOST_RDS_CRB_MODE_UNIQUE;
|
|
prq->host_rds_crb_mode = cpu_to_le32(temp_rds_crb_mode);
|
|
}
|
|
|
|
prq->capabilities[0] = cpu_to_le32(cap);
|
|
|
|
prq->num_rds_rings = cpu_to_le16(nrds_rings);
|
|
prq->num_sds_rings = cpu_to_le16(nsds_rings);
|
|
prq->rds_ring_offset = 0;
|
|
|
|
val = le32_to_cpu(prq->rds_ring_offset) +
|
|
(sizeof(struct qlcnic_hostrq_rds_ring) * nrds_rings);
|
|
prq->sds_ring_offset = cpu_to_le32(val);
|
|
|
|
prq_rds = (struct qlcnic_hostrq_rds_ring *)(prq->data +
|
|
le32_to_cpu(prq->rds_ring_offset));
|
|
|
|
for (i = 0; i < nrds_rings; i++) {
|
|
rds_ring = &recv_ctx->rds_rings[i];
|
|
rds_ring->producer = 0;
|
|
prq_rds[i].host_phys_addr = cpu_to_le64(rds_ring->phys_addr);
|
|
prq_rds[i].ring_size = cpu_to_le32(rds_ring->num_desc);
|
|
prq_rds[i].ring_kind = cpu_to_le32(i);
|
|
prq_rds[i].buff_size = cpu_to_le64(rds_ring->dma_size);
|
|
}
|
|
|
|
prq_sds = (struct qlcnic_hostrq_sds_ring *)(prq->data +
|
|
le32_to_cpu(prq->sds_ring_offset));
|
|
|
|
for (i = 0; i < nsds_rings; i++) {
|
|
sds_ring = &recv_ctx->sds_rings[i];
|
|
sds_ring->consumer = 0;
|
|
memset(sds_ring->desc_head, 0, STATUS_DESC_RINGSIZE(sds_ring));
|
|
prq_sds[i].host_phys_addr = cpu_to_le64(sds_ring->phys_addr);
|
|
prq_sds[i].ring_size = cpu_to_le32(sds_ring->num_desc);
|
|
if (qlcnic_check_multi_tx(adapter) &&
|
|
!adapter->ahw->diag_test)
|
|
prq_sds[i].msi_index = cpu_to_le16(ahw->intr_tbl[i].id);
|
|
else
|
|
prq_sds[i].msi_index = cpu_to_le16(i);
|
|
}
|
|
|
|
phys_addr = hostrq_phys_addr;
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_RX_CTX);
|
|
if (err)
|
|
goto out_free_rsp;
|
|
|
|
cmd.req.arg[1] = MSD(phys_addr);
|
|
cmd.req.arg[2] = LSD(phys_addr);
|
|
cmd.req.arg[3] = rq_size;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
if (err) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to create rx ctx in firmware%d\n", err);
|
|
goto out_free_rsp;
|
|
}
|
|
|
|
prsp_rds = ((struct qlcnic_cardrsp_rds_ring *)
|
|
&prsp->data[le32_to_cpu(prsp->rds_ring_offset)]);
|
|
|
|
for (i = 0; i < le16_to_cpu(prsp->num_rds_rings); i++) {
|
|
rds_ring = &recv_ctx->rds_rings[i];
|
|
reg = le32_to_cpu(prsp_rds[i].host_producer_crb);
|
|
rds_ring->crb_rcv_producer = ahw->pci_base0 + reg;
|
|
}
|
|
|
|
prsp_sds = ((struct qlcnic_cardrsp_sds_ring *)
|
|
&prsp->data[le32_to_cpu(prsp->sds_ring_offset)]);
|
|
|
|
for (i = 0; i < le16_to_cpu(prsp->num_sds_rings); i++) {
|
|
sds_ring = &recv_ctx->sds_rings[i];
|
|
reg = le32_to_cpu(prsp_sds[i].host_consumer_crb);
|
|
if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test)
|
|
reg2 = ahw->intr_tbl[i].src;
|
|
else
|
|
reg2 = le32_to_cpu(prsp_sds[i].interrupt_crb);
|
|
|
|
sds_ring->crb_intr_mask = ahw->pci_base0 + reg2;
|
|
sds_ring->crb_sts_consumer = ahw->pci_base0 + reg;
|
|
}
|
|
|
|
recv_ctx->state = le32_to_cpu(prsp->host_ctx_state);
|
|
recv_ctx->context_id = le16_to_cpu(prsp->context_id);
|
|
recv_ctx->virt_port = prsp->virt_port;
|
|
|
|
netdev_info(netdev, "Rx Context[%d] Created, state 0x%x\n",
|
|
recv_ctx->context_id, recv_ctx->state);
|
|
qlcnic_free_mbx_args(&cmd);
|
|
|
|
out_free_rsp:
|
|
dma_free_coherent(&adapter->pdev->dev, rsp_size, prsp,
|
|
cardrsp_phys_addr);
|
|
out_free_rq:
|
|
dma_free_coherent(&adapter->pdev->dev, rq_size, prq, hostrq_phys_addr);
|
|
|
|
return err;
|
|
}
|
|
|
|
void qlcnic_82xx_fw_cmd_del_rx_ctx(struct qlcnic_adapter *adapter)
|
|
{
|
|
int err;
|
|
struct qlcnic_cmd_args cmd;
|
|
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX);
|
|
if (err)
|
|
return;
|
|
|
|
cmd.req.arg[1] = recv_ctx->context_id;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
if (err)
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to destroy rx ctx in firmware\n");
|
|
|
|
recv_ctx->state = QLCNIC_HOST_CTX_STATE_FREED;
|
|
qlcnic_free_mbx_args(&cmd);
|
|
}
|
|
|
|
int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_host_tx_ring *tx_ring,
|
|
int ring)
|
|
{
|
|
struct qlcnic_hardware_context *ahw = adapter->ahw;
|
|
struct net_device *netdev = adapter->netdev;
|
|
struct qlcnic_hostrq_tx_ctx *prq;
|
|
struct qlcnic_hostrq_cds_ring *prq_cds;
|
|
struct qlcnic_cardrsp_tx_ctx *prsp;
|
|
struct qlcnic_cmd_args cmd;
|
|
u32 temp, intr_mask, temp_int_crb_mode;
|
|
dma_addr_t rq_phys_addr, rsp_phys_addr;
|
|
int temp_nsds_rings, index, err;
|
|
void *rq_addr, *rsp_addr;
|
|
size_t rq_size, rsp_size;
|
|
u64 phys_addr;
|
|
u16 msix_id;
|
|
|
|
/* reset host resources */
|
|
tx_ring->producer = 0;
|
|
tx_ring->sw_consumer = 0;
|
|
*(tx_ring->hw_consumer) = 0;
|
|
|
|
rq_size = SIZEOF_HOSTRQ_TX(struct qlcnic_hostrq_tx_ctx);
|
|
rq_addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size,
|
|
&rq_phys_addr, GFP_KERNEL);
|
|
if (!rq_addr)
|
|
return -ENOMEM;
|
|
|
|
rsp_size = SIZEOF_CARDRSP_TX(struct qlcnic_cardrsp_tx_ctx);
|
|
rsp_addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size,
|
|
&rsp_phys_addr, GFP_KERNEL);
|
|
if (!rsp_addr) {
|
|
err = -ENOMEM;
|
|
goto out_free_rq;
|
|
}
|
|
|
|
prq = rq_addr;
|
|
prsp = rsp_addr;
|
|
|
|
prq->host_rsp_dma_addr = cpu_to_le64(rsp_phys_addr);
|
|
|
|
temp = (QLCNIC_CAP0_LEGACY_CONTEXT | QLCNIC_CAP0_LEGACY_MN |
|
|
QLCNIC_CAP0_LSO);
|
|
if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test)
|
|
temp |= QLCNIC_CAP0_TX_MULTI;
|
|
|
|
prq->capabilities[0] = cpu_to_le32(temp);
|
|
|
|
if (qlcnic_check_multi_tx(adapter) &&
|
|
!adapter->ahw->diag_test) {
|
|
temp_nsds_rings = adapter->drv_sds_rings;
|
|
index = temp_nsds_rings + ring;
|
|
msix_id = ahw->intr_tbl[index].id;
|
|
prq->msi_index = cpu_to_le16(msix_id);
|
|
} else {
|
|
temp_int_crb_mode = QLCNIC_HOST_INT_CRB_MODE_SHARED;
|
|
prq->host_int_crb_mode = cpu_to_le32(temp_int_crb_mode);
|
|
prq->msi_index = 0;
|
|
}
|
|
|
|
prq->interrupt_ctl = 0;
|
|
prq->cmd_cons_dma_addr = cpu_to_le64(tx_ring->hw_cons_phys_addr);
|
|
|
|
prq_cds = &prq->cds_ring;
|
|
|
|
prq_cds->host_phys_addr = cpu_to_le64(tx_ring->phys_addr);
|
|
prq_cds->ring_size = cpu_to_le32(tx_ring->num_desc);
|
|
|
|
phys_addr = rq_phys_addr;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
|
|
if (err)
|
|
goto out_free_rsp;
|
|
|
|
cmd.req.arg[1] = MSD(phys_addr);
|
|
cmd.req.arg[2] = LSD(phys_addr);
|
|
cmd.req.arg[3] = rq_size;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
|
|
if (err == QLCNIC_RCODE_SUCCESS) {
|
|
tx_ring->state = le32_to_cpu(prsp->host_ctx_state);
|
|
temp = le32_to_cpu(prsp->cds_ring.host_producer_crb);
|
|
tx_ring->crb_cmd_producer = adapter->ahw->pci_base0 + temp;
|
|
tx_ring->ctx_id = le16_to_cpu(prsp->context_id);
|
|
if (qlcnic_check_multi_tx(adapter) &&
|
|
!adapter->ahw->diag_test &&
|
|
(adapter->flags & QLCNIC_MSIX_ENABLED)) {
|
|
index = adapter->drv_sds_rings + ring;
|
|
intr_mask = ahw->intr_tbl[index].src;
|
|
tx_ring->crb_intr_mask = ahw->pci_base0 + intr_mask;
|
|
}
|
|
|
|
netdev_info(netdev, "Tx Context[0x%x] Created, state 0x%x\n",
|
|
tx_ring->ctx_id, tx_ring->state);
|
|
} else {
|
|
netdev_err(netdev, "Failed to create tx ctx in firmware%d\n",
|
|
err);
|
|
err = -EIO;
|
|
}
|
|
qlcnic_free_mbx_args(&cmd);
|
|
|
|
out_free_rsp:
|
|
dma_free_coherent(&adapter->pdev->dev, rsp_size, rsp_addr,
|
|
rsp_phys_addr);
|
|
out_free_rq:
|
|
dma_free_coherent(&adapter->pdev->dev, rq_size, rq_addr, rq_phys_addr);
|
|
|
|
return err;
|
|
}
|
|
|
|
void qlcnic_82xx_fw_cmd_del_tx_ctx(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_host_tx_ring *tx_ring)
|
|
{
|
|
struct qlcnic_cmd_args cmd;
|
|
int ret;
|
|
|
|
ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX);
|
|
if (ret)
|
|
return;
|
|
|
|
cmd.req.arg[1] = tx_ring->ctx_id;
|
|
if (qlcnic_issue_cmd(adapter, &cmd))
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to destroy tx ctx in firmware\n");
|
|
qlcnic_free_mbx_args(&cmd);
|
|
}
|
|
|
|
int
|
|
qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config)
|
|
{
|
|
int err;
|
|
struct qlcnic_cmd_args cmd;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_PORT);
|
|
if (err)
|
|
return err;
|
|
|
|
cmd.req.arg[1] = config;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
qlcnic_free_mbx_args(&cmd);
|
|
return err;
|
|
}
|
|
|
|
int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
|
|
{
|
|
void *addr;
|
|
int err, ring;
|
|
struct qlcnic_recv_context *recv_ctx;
|
|
struct qlcnic_host_rds_ring *rds_ring;
|
|
struct qlcnic_host_sds_ring *sds_ring;
|
|
struct qlcnic_host_tx_ring *tx_ring;
|
|
__le32 *ptr;
|
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
|
|
recv_ctx = adapter->recv_ctx;
|
|
|
|
for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
|
|
tx_ring = &adapter->tx_ring[ring];
|
|
ptr = (__le32 *)dma_alloc_coherent(&pdev->dev, sizeof(u32),
|
|
&tx_ring->hw_cons_phys_addr,
|
|
GFP_KERNEL);
|
|
if (ptr == NULL) {
|
|
err = -ENOMEM;
|
|
goto err_out_free;
|
|
}
|
|
|
|
tx_ring->hw_consumer = ptr;
|
|
/* cmd desc ring */
|
|
addr = dma_alloc_coherent(&pdev->dev, TX_DESC_RINGSIZE(tx_ring),
|
|
&tx_ring->phys_addr,
|
|
GFP_KERNEL);
|
|
if (addr == NULL) {
|
|
err = -ENOMEM;
|
|
goto err_out_free;
|
|
}
|
|
|
|
tx_ring->desc_head = addr;
|
|
}
|
|
|
|
for (ring = 0; ring < adapter->max_rds_rings; ring++) {
|
|
rds_ring = &recv_ctx->rds_rings[ring];
|
|
addr = dma_alloc_coherent(&adapter->pdev->dev,
|
|
RCV_DESC_RINGSIZE(rds_ring),
|
|
&rds_ring->phys_addr, GFP_KERNEL);
|
|
if (addr == NULL) {
|
|
err = -ENOMEM;
|
|
goto err_out_free;
|
|
}
|
|
rds_ring->desc_head = addr;
|
|
|
|
}
|
|
|
|
for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
|
|
sds_ring = &recv_ctx->sds_rings[ring];
|
|
|
|
addr = dma_alloc_coherent(&adapter->pdev->dev,
|
|
STATUS_DESC_RINGSIZE(sds_ring),
|
|
&sds_ring->phys_addr, GFP_KERNEL);
|
|
if (addr == NULL) {
|
|
err = -ENOMEM;
|
|
goto err_out_free;
|
|
}
|
|
sds_ring->desc_head = addr;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_out_free:
|
|
qlcnic_free_hw_resources(adapter);
|
|
return err;
|
|
}
|
|
|
|
int qlcnic_fw_create_ctx(struct qlcnic_adapter *dev)
|
|
{
|
|
int i, err, ring;
|
|
|
|
if (dev->flags & QLCNIC_NEED_FLR) {
|
|
pci_reset_function(dev->pdev);
|
|
dev->flags &= ~QLCNIC_NEED_FLR;
|
|
}
|
|
|
|
if (qlcnic_83xx_check(dev) && (dev->flags & QLCNIC_MSIX_ENABLED)) {
|
|
if (dev->ahw->diag_test != QLCNIC_LOOPBACK_TEST) {
|
|
err = qlcnic_83xx_config_intrpt(dev, 1);
|
|
if (err)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (qlcnic_82xx_check(dev) && (dev->flags & QLCNIC_MSIX_ENABLED) &&
|
|
qlcnic_check_multi_tx(dev) && !dev->ahw->diag_test) {
|
|
err = qlcnic_82xx_mq_intrpt(dev, 1);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
err = qlcnic_fw_cmd_create_rx_ctx(dev);
|
|
if (err)
|
|
goto err_out;
|
|
|
|
for (ring = 0; ring < dev->drv_tx_rings; ring++) {
|
|
err = qlcnic_fw_cmd_create_tx_ctx(dev,
|
|
&dev->tx_ring[ring],
|
|
ring);
|
|
if (err) {
|
|
qlcnic_fw_cmd_del_rx_ctx(dev);
|
|
if (ring == 0)
|
|
goto err_out;
|
|
|
|
for (i = 0; i < ring; i++)
|
|
qlcnic_fw_cmd_del_tx_ctx(dev, &dev->tx_ring[i]);
|
|
|
|
goto err_out;
|
|
}
|
|
}
|
|
|
|
set_bit(__QLCNIC_FW_ATTACHED, &dev->state);
|
|
|
|
return 0;
|
|
|
|
err_out:
|
|
if (qlcnic_82xx_check(dev) && (dev->flags & QLCNIC_MSIX_ENABLED) &&
|
|
qlcnic_check_multi_tx(dev) && !dev->ahw->diag_test)
|
|
qlcnic_82xx_config_intrpt(dev, 0);
|
|
|
|
if (qlcnic_83xx_check(dev) && (dev->flags & QLCNIC_MSIX_ENABLED)) {
|
|
if (dev->ahw->diag_test != QLCNIC_LOOPBACK_TEST)
|
|
qlcnic_83xx_config_intrpt(dev, 0);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
|
|
{
|
|
int ring;
|
|
|
|
if (test_and_clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) {
|
|
qlcnic_fw_cmd_del_rx_ctx(adapter);
|
|
for (ring = 0; ring < adapter->drv_tx_rings; ring++)
|
|
qlcnic_fw_cmd_del_tx_ctx(adapter,
|
|
&adapter->tx_ring[ring]);
|
|
|
|
if (qlcnic_82xx_check(adapter) &&
|
|
(adapter->flags & QLCNIC_MSIX_ENABLED) &&
|
|
qlcnic_check_multi_tx(adapter) &&
|
|
!adapter->ahw->diag_test)
|
|
qlcnic_82xx_config_intrpt(adapter, 0);
|
|
|
|
if (qlcnic_83xx_check(adapter) &&
|
|
(adapter->flags & QLCNIC_MSIX_ENABLED)) {
|
|
if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST)
|
|
qlcnic_83xx_config_intrpt(adapter, 0);
|
|
}
|
|
/* Allow dma queues to drain after context reset */
|
|
mdelay(20);
|
|
}
|
|
}
|
|
|
|
void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter)
|
|
{
|
|
struct qlcnic_recv_context *recv_ctx;
|
|
struct qlcnic_host_rds_ring *rds_ring;
|
|
struct qlcnic_host_sds_ring *sds_ring;
|
|
struct qlcnic_host_tx_ring *tx_ring;
|
|
int ring;
|
|
|
|
recv_ctx = adapter->recv_ctx;
|
|
|
|
for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
|
|
tx_ring = &adapter->tx_ring[ring];
|
|
if (tx_ring->hw_consumer != NULL) {
|
|
dma_free_coherent(&adapter->pdev->dev, sizeof(u32),
|
|
tx_ring->hw_consumer,
|
|
tx_ring->hw_cons_phys_addr);
|
|
|
|
tx_ring->hw_consumer = NULL;
|
|
}
|
|
|
|
if (tx_ring->desc_head != NULL) {
|
|
dma_free_coherent(&adapter->pdev->dev,
|
|
TX_DESC_RINGSIZE(tx_ring),
|
|
tx_ring->desc_head,
|
|
tx_ring->phys_addr);
|
|
tx_ring->desc_head = NULL;
|
|
}
|
|
}
|
|
|
|
for (ring = 0; ring < adapter->max_rds_rings; ring++) {
|
|
rds_ring = &recv_ctx->rds_rings[ring];
|
|
|
|
if (rds_ring->desc_head != NULL) {
|
|
dma_free_coherent(&adapter->pdev->dev,
|
|
RCV_DESC_RINGSIZE(rds_ring),
|
|
rds_ring->desc_head,
|
|
rds_ring->phys_addr);
|
|
rds_ring->desc_head = NULL;
|
|
}
|
|
}
|
|
|
|
for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
|
|
sds_ring = &recv_ctx->sds_rings[ring];
|
|
|
|
if (sds_ring->desc_head != NULL) {
|
|
dma_free_coherent(&adapter->pdev->dev,
|
|
STATUS_DESC_RINGSIZE(sds_ring),
|
|
sds_ring->desc_head,
|
|
sds_ring->phys_addr);
|
|
sds_ring->desc_head = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int qlcnic_82xx_config_intrpt(struct qlcnic_adapter *adapter, u8 op_type)
|
|
{
|
|
struct qlcnic_hardware_context *ahw = adapter->ahw;
|
|
struct net_device *netdev = adapter->netdev;
|
|
struct qlcnic_cmd_args cmd;
|
|
u32 type, val;
|
|
int i, err = 0;
|
|
|
|
for (i = 0; i < ahw->num_msix; i++) {
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter,
|
|
QLCNIC_CMD_MQ_TX_CONFIG_INTR);
|
|
if (err)
|
|
return err;
|
|
type = op_type ? QLCNIC_INTRPT_ADD : QLCNIC_INTRPT_DEL;
|
|
val = type | (ahw->intr_tbl[i].type << 4);
|
|
if (ahw->intr_tbl[i].type == QLCNIC_INTRPT_MSIX)
|
|
val |= (ahw->intr_tbl[i].id << 16);
|
|
cmd.req.arg[1] = val;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
if (err) {
|
|
netdev_err(netdev, "Failed to %s interrupts %d\n",
|
|
op_type == QLCNIC_INTRPT_ADD ? "Add" :
|
|
"Delete", err);
|
|
qlcnic_free_mbx_args(&cmd);
|
|
return err;
|
|
}
|
|
val = cmd.rsp.arg[1];
|
|
if (LSB(val)) {
|
|
netdev_info(netdev,
|
|
"failed to configure interrupt for %d\n",
|
|
ahw->intr_tbl[i].id);
|
|
continue;
|
|
}
|
|
if (op_type) {
|
|
ahw->intr_tbl[i].id = MSW(val);
|
|
ahw->intr_tbl[i].enabled = 1;
|
|
ahw->intr_tbl[i].src = cmd.rsp.arg[2];
|
|
} else {
|
|
ahw->intr_tbl[i].id = i;
|
|
ahw->intr_tbl[i].enabled = 0;
|
|
ahw->intr_tbl[i].src = 0;
|
|
}
|
|
qlcnic_free_mbx_args(&cmd);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int qlcnic_82xx_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac,
|
|
u8 function)
|
|
{
|
|
int err, i;
|
|
struct qlcnic_cmd_args cmd;
|
|
u32 mac_low, mac_high;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
|
|
if (err)
|
|
return err;
|
|
|
|
cmd.req.arg[1] = function | BIT_8;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
|
|
if (err == QLCNIC_RCODE_SUCCESS) {
|
|
mac_low = cmd.rsp.arg[1];
|
|
mac_high = cmd.rsp.arg[2];
|
|
|
|
for (i = 0; i < 2; i++)
|
|
mac[i] = (u8) (mac_high >> ((1 - i) * 8));
|
|
for (i = 2; i < 6; i++)
|
|
mac[i] = (u8) (mac_low >> ((5 - i) * 8));
|
|
} else {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to get mac address%d\n", err);
|
|
err = -EIO;
|
|
}
|
|
qlcnic_free_mbx_args(&cmd);
|
|
return err;
|
|
}
|
|
|
|
/* Get info of a NIC partition */
|
|
int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_info *npar_info, u8 func_id)
|
|
{
|
|
int err;
|
|
dma_addr_t nic_dma_t;
|
|
const struct qlcnic_info_le *nic_info;
|
|
void *nic_info_addr;
|
|
struct qlcnic_cmd_args cmd;
|
|
size_t nic_size = sizeof(struct qlcnic_info_le);
|
|
|
|
nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size,
|
|
&nic_dma_t, GFP_KERNEL);
|
|
if (!nic_info_addr)
|
|
return -ENOMEM;
|
|
|
|
nic_info = nic_info_addr;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
|
|
if (err)
|
|
goto out_free_dma;
|
|
|
|
cmd.req.arg[1] = MSD(nic_dma_t);
|
|
cmd.req.arg[2] = LSD(nic_dma_t);
|
|
cmd.req.arg[3] = (func_id << 16 | nic_size);
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
if (err != QLCNIC_RCODE_SUCCESS) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to get nic info%d\n", err);
|
|
err = -EIO;
|
|
} else {
|
|
npar_info->pci_func = le16_to_cpu(nic_info->pci_func);
|
|
npar_info->op_mode = le16_to_cpu(nic_info->op_mode);
|
|
npar_info->min_tx_bw = le16_to_cpu(nic_info->min_tx_bw);
|
|
npar_info->max_tx_bw = le16_to_cpu(nic_info->max_tx_bw);
|
|
npar_info->phys_port = le16_to_cpu(nic_info->phys_port);
|
|
npar_info->switch_mode = le16_to_cpu(nic_info->switch_mode);
|
|
npar_info->max_tx_ques = le16_to_cpu(nic_info->max_tx_ques);
|
|
npar_info->max_rx_ques = le16_to_cpu(nic_info->max_rx_ques);
|
|
npar_info->capabilities = le32_to_cpu(nic_info->capabilities);
|
|
npar_info->max_mtu = le16_to_cpu(nic_info->max_mtu);
|
|
}
|
|
|
|
qlcnic_free_mbx_args(&cmd);
|
|
out_free_dma:
|
|
dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
|
|
nic_dma_t);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Configure a NIC partition */
|
|
int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_info *nic)
|
|
{
|
|
int err = -EIO;
|
|
dma_addr_t nic_dma_t;
|
|
void *nic_info_addr;
|
|
struct qlcnic_cmd_args cmd;
|
|
struct qlcnic_info_le *nic_info;
|
|
size_t nic_size = sizeof(struct qlcnic_info_le);
|
|
|
|
if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC)
|
|
return err;
|
|
|
|
nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size,
|
|
&nic_dma_t, GFP_KERNEL);
|
|
if (!nic_info_addr)
|
|
return -ENOMEM;
|
|
|
|
nic_info = nic_info_addr;
|
|
|
|
nic_info->pci_func = cpu_to_le16(nic->pci_func);
|
|
nic_info->op_mode = cpu_to_le16(nic->op_mode);
|
|
nic_info->phys_port = cpu_to_le16(nic->phys_port);
|
|
nic_info->switch_mode = cpu_to_le16(nic->switch_mode);
|
|
nic_info->capabilities = cpu_to_le32(nic->capabilities);
|
|
nic_info->max_mac_filters = nic->max_mac_filters;
|
|
nic_info->max_tx_ques = cpu_to_le16(nic->max_tx_ques);
|
|
nic_info->max_rx_ques = cpu_to_le16(nic->max_rx_ques);
|
|
nic_info->min_tx_bw = cpu_to_le16(nic->min_tx_bw);
|
|
nic_info->max_tx_bw = cpu_to_le16(nic->max_tx_bw);
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
|
|
if (err)
|
|
goto out_free_dma;
|
|
|
|
cmd.req.arg[1] = MSD(nic_dma_t);
|
|
cmd.req.arg[2] = LSD(nic_dma_t);
|
|
cmd.req.arg[3] = ((nic->pci_func << 16) | nic_size);
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
|
|
if (err != QLCNIC_RCODE_SUCCESS) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to set nic info%d\n", err);
|
|
err = -EIO;
|
|
}
|
|
|
|
qlcnic_free_mbx_args(&cmd);
|
|
out_free_dma:
|
|
dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
|
|
nic_dma_t);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Get PCI Info of a partition */
|
|
int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_pci_info *pci_info)
|
|
{
|
|
struct qlcnic_hardware_context *ahw = adapter->ahw;
|
|
size_t npar_size = sizeof(struct qlcnic_pci_info_le);
|
|
size_t pci_size = npar_size * ahw->max_vnic_func;
|
|
u16 nic = 0, fcoe = 0, iscsi = 0;
|
|
struct qlcnic_pci_info_le *npar;
|
|
struct qlcnic_cmd_args cmd;
|
|
dma_addr_t pci_info_dma_t;
|
|
void *pci_info_addr;
|
|
int err = 0, i;
|
|
|
|
pci_info_addr = dma_alloc_coherent(&adapter->pdev->dev, pci_size,
|
|
&pci_info_dma_t, GFP_KERNEL);
|
|
if (!pci_info_addr)
|
|
return -ENOMEM;
|
|
|
|
npar = pci_info_addr;
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
|
|
if (err)
|
|
goto out_free_dma;
|
|
|
|
cmd.req.arg[1] = MSD(pci_info_dma_t);
|
|
cmd.req.arg[2] = LSD(pci_info_dma_t);
|
|
cmd.req.arg[3] = pci_size;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
|
|
ahw->total_nic_func = 0;
|
|
if (err == QLCNIC_RCODE_SUCCESS) {
|
|
for (i = 0; i < ahw->max_vnic_func; i++, npar++, pci_info++) {
|
|
pci_info->id = le16_to_cpu(npar->id);
|
|
pci_info->active = le16_to_cpu(npar->active);
|
|
if (!pci_info->active)
|
|
continue;
|
|
pci_info->type = le16_to_cpu(npar->type);
|
|
err = qlcnic_get_pci_func_type(adapter, pci_info->type,
|
|
&nic, &fcoe, &iscsi);
|
|
pci_info->default_port =
|
|
le16_to_cpu(npar->default_port);
|
|
pci_info->tx_min_bw =
|
|
le16_to_cpu(npar->tx_min_bw);
|
|
pci_info->tx_max_bw =
|
|
le16_to_cpu(npar->tx_max_bw);
|
|
memcpy(pci_info->mac, npar->mac, ETH_ALEN);
|
|
}
|
|
} else {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Failed to get PCI Info%d\n", err);
|
|
err = -EIO;
|
|
}
|
|
|
|
ahw->total_nic_func = nic;
|
|
ahw->total_pci_func = nic + fcoe + iscsi;
|
|
if (ahw->total_nic_func == 0 || ahw->total_pci_func == 0) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"%s: Invalid function count: total nic func[%x], total pci func[%x]\n",
|
|
__func__, ahw->total_nic_func, ahw->total_pci_func);
|
|
err = -EIO;
|
|
}
|
|
qlcnic_free_mbx_args(&cmd);
|
|
out_free_dma:
|
|
dma_free_coherent(&adapter->pdev->dev, pci_size, pci_info_addr,
|
|
pci_info_dma_t);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Configure eSwitch for port mirroring */
|
|
int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
|
|
u8 enable_mirroring, u8 pci_func)
|
|
{
|
|
struct device *dev = &adapter->pdev->dev;
|
|
struct qlcnic_cmd_args cmd;
|
|
int err = -EIO;
|
|
u32 arg1;
|
|
|
|
if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC ||
|
|
!(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE)) {
|
|
dev_err(&adapter->pdev->dev, "%s: Not a management function\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
|
|
arg1 = id | (enable_mirroring ? BIT_4 : 0);
|
|
arg1 |= pci_func << 8;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter,
|
|
QLCNIC_CMD_SET_PORTMIRRORING);
|
|
if (err)
|
|
return err;
|
|
|
|
cmd.req.arg[1] = arg1;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
|
|
if (err != QLCNIC_RCODE_SUCCESS)
|
|
dev_err(dev, "Failed to configure port mirroring for vNIC function %d on eSwitch %d\n",
|
|
pci_func, id);
|
|
else
|
|
dev_info(dev, "Configured port mirroring for vNIC function %d on eSwitch %d\n",
|
|
pci_func, id);
|
|
qlcnic_free_mbx_args(&cmd);
|
|
|
|
return err;
|
|
}
|
|
|
|
int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
|
|
const u8 rx_tx, struct __qlcnic_esw_statistics *esw_stats) {
|
|
|
|
size_t stats_size = sizeof(struct qlcnic_esw_stats_le);
|
|
struct qlcnic_esw_stats_le *stats;
|
|
dma_addr_t stats_dma_t;
|
|
void *stats_addr;
|
|
u32 arg1;
|
|
struct qlcnic_cmd_args cmd;
|
|
int err;
|
|
|
|
if (esw_stats == NULL)
|
|
return -ENOMEM;
|
|
|
|
if ((adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) &&
|
|
(func != adapter->ahw->pci_func)) {
|
|
dev_err(&adapter->pdev->dev,
|
|
"Not privilege to query stats for func=%d", func);
|
|
return -EIO;
|
|
}
|
|
|
|
stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size,
|
|
&stats_dma_t, GFP_KERNEL);
|
|
if (!stats_addr)
|
|
return -ENOMEM;
|
|
|
|
arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12;
|
|
arg1 |= rx_tx << 15 | stats_size << 16;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter,
|
|
QLCNIC_CMD_GET_ESWITCH_STATS);
|
|
if (err)
|
|
goto out_free_dma;
|
|
|
|
cmd.req.arg[1] = arg1;
|
|
cmd.req.arg[2] = MSD(stats_dma_t);
|
|
cmd.req.arg[3] = LSD(stats_dma_t);
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
|
|
if (!err) {
|
|
stats = stats_addr;
|
|
esw_stats->context_id = le16_to_cpu(stats->context_id);
|
|
esw_stats->version = le16_to_cpu(stats->version);
|
|
esw_stats->size = le16_to_cpu(stats->size);
|
|
esw_stats->multicast_frames =
|
|
le64_to_cpu(stats->multicast_frames);
|
|
esw_stats->broadcast_frames =
|
|
le64_to_cpu(stats->broadcast_frames);
|
|
esw_stats->unicast_frames = le64_to_cpu(stats->unicast_frames);
|
|
esw_stats->dropped_frames = le64_to_cpu(stats->dropped_frames);
|
|
esw_stats->local_frames = le64_to_cpu(stats->local_frames);
|
|
esw_stats->errors = le64_to_cpu(stats->errors);
|
|
esw_stats->numbytes = le64_to_cpu(stats->numbytes);
|
|
}
|
|
|
|
qlcnic_free_mbx_args(&cmd);
|
|
out_free_dma:
|
|
dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
|
|
stats_dma_t);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* This routine will retrieve the MAC statistics from firmware */
|
|
int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_mac_statistics *mac_stats)
|
|
{
|
|
struct qlcnic_mac_statistics_le *stats;
|
|
struct qlcnic_cmd_args cmd;
|
|
size_t stats_size = sizeof(struct qlcnic_mac_statistics_le);
|
|
dma_addr_t stats_dma_t;
|
|
void *stats_addr;
|
|
int err;
|
|
|
|
if (mac_stats == NULL)
|
|
return -ENOMEM;
|
|
|
|
stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size,
|
|
&stats_dma_t, GFP_KERNEL);
|
|
if (!stats_addr)
|
|
return -ENOMEM;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_MAC_STATS);
|
|
if (err)
|
|
goto out_free_dma;
|
|
|
|
cmd.req.arg[1] = stats_size << 16;
|
|
cmd.req.arg[2] = MSD(stats_dma_t);
|
|
cmd.req.arg[3] = LSD(stats_dma_t);
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
if (!err) {
|
|
stats = stats_addr;
|
|
mac_stats->mac_tx_frames = le64_to_cpu(stats->mac_tx_frames);
|
|
mac_stats->mac_tx_bytes = le64_to_cpu(stats->mac_tx_bytes);
|
|
mac_stats->mac_tx_mcast_pkts =
|
|
le64_to_cpu(stats->mac_tx_mcast_pkts);
|
|
mac_stats->mac_tx_bcast_pkts =
|
|
le64_to_cpu(stats->mac_tx_bcast_pkts);
|
|
mac_stats->mac_rx_frames = le64_to_cpu(stats->mac_rx_frames);
|
|
mac_stats->mac_rx_bytes = le64_to_cpu(stats->mac_rx_bytes);
|
|
mac_stats->mac_rx_mcast_pkts =
|
|
le64_to_cpu(stats->mac_rx_mcast_pkts);
|
|
mac_stats->mac_rx_length_error =
|
|
le64_to_cpu(stats->mac_rx_length_error);
|
|
mac_stats->mac_rx_length_small =
|
|
le64_to_cpu(stats->mac_rx_length_small);
|
|
mac_stats->mac_rx_length_large =
|
|
le64_to_cpu(stats->mac_rx_length_large);
|
|
mac_stats->mac_rx_jabber = le64_to_cpu(stats->mac_rx_jabber);
|
|
mac_stats->mac_rx_dropped = le64_to_cpu(stats->mac_rx_dropped);
|
|
mac_stats->mac_rx_crc_error = le64_to_cpu(stats->mac_rx_crc_error);
|
|
} else {
|
|
dev_err(&adapter->pdev->dev,
|
|
"%s: Get mac stats failed, err=%d.\n", __func__, err);
|
|
}
|
|
|
|
qlcnic_free_mbx_args(&cmd);
|
|
|
|
out_free_dma:
|
|
dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
|
|
stats_dma_t);
|
|
|
|
return err;
|
|
}
|
|
|
|
int qlcnic_get_eswitch_stats(struct qlcnic_adapter *adapter, const u8 eswitch,
|
|
const u8 rx_tx, struct __qlcnic_esw_statistics *esw_stats) {
|
|
|
|
struct __qlcnic_esw_statistics port_stats;
|
|
u8 i;
|
|
int ret = -EIO;
|
|
|
|
if (esw_stats == NULL)
|
|
return -ENOMEM;
|
|
if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC)
|
|
return -EIO;
|
|
if (adapter->npars == NULL)
|
|
return -EIO;
|
|
|
|
memset(esw_stats, 0, sizeof(u64));
|
|
esw_stats->unicast_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->multicast_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->broadcast_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->dropped_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->errors = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->local_frames = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->numbytes = QLCNIC_STATS_NOT_AVAIL;
|
|
esw_stats->context_id = eswitch;
|
|
|
|
for (i = 0; i < adapter->ahw->total_nic_func; i++) {
|
|
if (adapter->npars[i].phy_port != eswitch)
|
|
continue;
|
|
|
|
memset(&port_stats, 0, sizeof(struct __qlcnic_esw_statistics));
|
|
if (qlcnic_get_port_stats(adapter, adapter->npars[i].pci_func,
|
|
rx_tx, &port_stats))
|
|
continue;
|
|
|
|
esw_stats->size = port_stats.size;
|
|
esw_stats->version = port_stats.version;
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->unicast_frames,
|
|
port_stats.unicast_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->multicast_frames,
|
|
port_stats.multicast_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->broadcast_frames,
|
|
port_stats.broadcast_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->dropped_frames,
|
|
port_stats.dropped_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->errors,
|
|
port_stats.errors);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->local_frames,
|
|
port_stats.local_frames);
|
|
QLCNIC_ADD_ESW_STATS(esw_stats->numbytes,
|
|
port_stats.numbytes);
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw,
|
|
const u8 port, const u8 rx_tx)
|
|
{
|
|
struct qlcnic_hardware_context *ahw = adapter->ahw;
|
|
struct qlcnic_cmd_args cmd;
|
|
int err;
|
|
u32 arg1;
|
|
|
|
if (ahw->op_mode != QLCNIC_MGMT_FUNC)
|
|
return -EIO;
|
|
|
|
if (func_esw == QLCNIC_STATS_PORT) {
|
|
if (port >= ahw->max_vnic_func)
|
|
goto err_ret;
|
|
} else if (func_esw == QLCNIC_STATS_ESWITCH) {
|
|
if (port >= QLCNIC_NIU_MAX_XG_PORTS)
|
|
goto err_ret;
|
|
} else {
|
|
goto err_ret;
|
|
}
|
|
|
|
if (rx_tx > QLCNIC_QUERY_TX_COUNTER)
|
|
goto err_ret;
|
|
|
|
arg1 = port | QLCNIC_STATS_VERSION << 8 | func_esw << 12;
|
|
arg1 |= BIT_14 | rx_tx << 15;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter,
|
|
QLCNIC_CMD_GET_ESWITCH_STATS);
|
|
if (err)
|
|
return err;
|
|
|
|
cmd.req.arg[1] = arg1;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
qlcnic_free_mbx_args(&cmd);
|
|
return err;
|
|
|
|
err_ret:
|
|
dev_err(&adapter->pdev->dev,
|
|
"Invalid args func_esw %d port %d rx_ctx %d\n",
|
|
func_esw, port, rx_tx);
|
|
return -EIO;
|
|
}
|
|
|
|
static int __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
|
|
u32 *arg1, u32 *arg2)
|
|
{
|
|
struct device *dev = &adapter->pdev->dev;
|
|
struct qlcnic_cmd_args cmd;
|
|
u8 pci_func = *arg1 >> 8;
|
|
int err;
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter,
|
|
QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG);
|
|
if (err)
|
|
return err;
|
|
|
|
cmd.req.arg[1] = *arg1;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
*arg1 = cmd.rsp.arg[1];
|
|
*arg2 = cmd.rsp.arg[2];
|
|
qlcnic_free_mbx_args(&cmd);
|
|
|
|
if (err == QLCNIC_RCODE_SUCCESS)
|
|
dev_info(dev, "Get eSwitch port config for vNIC function %d\n",
|
|
pci_func);
|
|
else
|
|
dev_err(dev, "Failed to get eswitch port config for vNIC function %d\n",
|
|
pci_func);
|
|
return err;
|
|
}
|
|
/* Configure eSwitch port
|
|
op_mode = 0 for setting default port behavior
|
|
op_mode = 1 for setting vlan id
|
|
op_mode = 2 for deleting vlan id
|
|
op_type = 0 for vlan_id
|
|
op_type = 1 for port vlan_id
|
|
*/
|
|
int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_esw_func_cfg *esw_cfg)
|
|
{
|
|
struct device *dev = &adapter->pdev->dev;
|
|
struct qlcnic_cmd_args cmd;
|
|
int err = -EIO, index;
|
|
u32 arg1, arg2 = 0;
|
|
u8 pci_func;
|
|
|
|
if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) {
|
|
dev_err(&adapter->pdev->dev, "%s: Not a management function\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
|
|
pci_func = esw_cfg->pci_func;
|
|
index = qlcnic_is_valid_nic_func(adapter, pci_func);
|
|
if (index < 0)
|
|
return err;
|
|
arg1 = (adapter->npars[index].phy_port & BIT_0);
|
|
arg1 |= (pci_func << 8);
|
|
|
|
if (__qlcnic_get_eswitch_port_config(adapter, &arg1, &arg2))
|
|
return err;
|
|
arg1 &= ~(0x0ff << 8);
|
|
arg1 |= (pci_func << 8);
|
|
arg1 &= ~(BIT_2 | BIT_3);
|
|
switch (esw_cfg->op_mode) {
|
|
case QLCNIC_PORT_DEFAULTS:
|
|
arg1 |= (BIT_4 | BIT_6 | BIT_7);
|
|
arg2 |= (BIT_0 | BIT_1);
|
|
if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_TSO)
|
|
arg2 |= (BIT_2 | BIT_3);
|
|
if (!(esw_cfg->discard_tagged))
|
|
arg1 &= ~BIT_4;
|
|
if (!(esw_cfg->promisc_mode))
|
|
arg1 &= ~BIT_6;
|
|
if (!(esw_cfg->mac_override))
|
|
arg1 &= ~BIT_7;
|
|
if (!(esw_cfg->mac_anti_spoof))
|
|
arg2 &= ~BIT_0;
|
|
if (!(esw_cfg->offload_flags & BIT_0))
|
|
arg2 &= ~(BIT_1 | BIT_2 | BIT_3);
|
|
if (!(esw_cfg->offload_flags & BIT_1))
|
|
arg2 &= ~BIT_2;
|
|
if (!(esw_cfg->offload_flags & BIT_2))
|
|
arg2 &= ~BIT_3;
|
|
break;
|
|
case QLCNIC_ADD_VLAN:
|
|
arg1 &= ~(0x0ffff << 16);
|
|
arg1 |= (BIT_2 | BIT_5);
|
|
arg1 |= (esw_cfg->vlan_id << 16);
|
|
break;
|
|
case QLCNIC_DEL_VLAN:
|
|
arg1 |= (BIT_3 | BIT_5);
|
|
arg1 &= ~(0x0ffff << 16);
|
|
break;
|
|
default:
|
|
dev_err(&adapter->pdev->dev, "%s: Invalid opmode 0x%x\n",
|
|
__func__, esw_cfg->op_mode);
|
|
return err;
|
|
}
|
|
|
|
err = qlcnic_alloc_mbx_args(&cmd, adapter,
|
|
QLCNIC_CMD_CONFIGURE_ESWITCH);
|
|
if (err)
|
|
return err;
|
|
|
|
cmd.req.arg[1] = arg1;
|
|
cmd.req.arg[2] = arg2;
|
|
err = qlcnic_issue_cmd(adapter, &cmd);
|
|
qlcnic_free_mbx_args(&cmd);
|
|
|
|
if (err != QLCNIC_RCODE_SUCCESS)
|
|
dev_err(dev, "Failed to configure eswitch for vNIC function %d\n",
|
|
pci_func);
|
|
else
|
|
dev_info(dev, "Configured eSwitch for vNIC function %d\n",
|
|
pci_func);
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
|
|
struct qlcnic_esw_func_cfg *esw_cfg)
|
|
{
|
|
u32 arg1, arg2;
|
|
int index;
|
|
u8 phy_port;
|
|
|
|
if (adapter->ahw->op_mode == QLCNIC_MGMT_FUNC) {
|
|
index = qlcnic_is_valid_nic_func(adapter, esw_cfg->pci_func);
|
|
if (index < 0)
|
|
return -EIO;
|
|
phy_port = adapter->npars[index].phy_port;
|
|
} else {
|
|
phy_port = adapter->ahw->physical_port;
|
|
}
|
|
arg1 = phy_port;
|
|
arg1 |= (esw_cfg->pci_func << 8);
|
|
if (__qlcnic_get_eswitch_port_config(adapter, &arg1, &arg2))
|
|
return -EIO;
|
|
|
|
esw_cfg->discard_tagged = !!(arg1 & BIT_4);
|
|
esw_cfg->host_vlan_tag = !!(arg1 & BIT_5);
|
|
esw_cfg->promisc_mode = !!(arg1 & BIT_6);
|
|
esw_cfg->mac_override = !!(arg1 & BIT_7);
|
|
esw_cfg->vlan_id = LSW(arg1 >> 16);
|
|
esw_cfg->mac_anti_spoof = (arg2 & 0x1);
|
|
esw_cfg->offload_flags = ((arg2 >> 1) & 0x7);
|
|
|
|
return 0;
|
|
}
|