* Subject: [PATCH 3/6] bna: Brocade 10Gb Ethernet device driver
From: Rasesh Mody @ 2009-10-16 18:24 UTC (permalink / raw)
To: netdev; +Cc: amathur
From: Rasesh Mody <rmody@brocade.com>
This is patch 3/6 which contains linux driver source for
Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
We wish this patch to be considered for inclusion in 2.6.32
Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
bfa_cee.c | 463 +++++++++++
bfa_csdebug.c | 62 +
bfa_ioc.c | 2273 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bfa_sm.c | 45 +
bna_if.c | 588 +++++++++++++++
bna_iocll.h | 66 +
bna_os.h | 182 ++++
bna_priv.h | 490 ++++++++++++
bnad_compat.h | 111 ++
bnad_defs.h | 36
cna.h | 32
11 files changed, 4348 insertions(+)
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bfa_cee.c linux-2.6.32-rc4-mod/drivers/net/bna/bfa_cee.c
--- linux-2.6.32-rc4-orig/drivers/net/bna/bfa_cee.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bfa_cee.c 2009-10-16 10:30:53.404436000 -0700
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * See LICENSE.bna for copyright and licensing details.
+ */
+
+#include <defs/bfa_defs_cee.h>
+#include <cs/bfa_debug.h>
+#include <cee/bfa_cee.h>
+#include <bfi/bfi_cee.h>
+#include <bfi/bfi.h>
+#include <bfa_ioc.h>
+
+
+#define bfa_ioc_portid(__ioc) ((__ioc)->port_id)
+#define bfa_lpuid(__arg) bfa_ioc_portid(&(__arg)->ioc)
+
+static void bfa_cee_format_lldp_cfg(struct bfa_cee_lldp_cfg_s *lldp_cfg);
+static void bfa_cee_format_dcbcx_stats(struct bfa_cee_dcbx_stats_s
+ *dcbcx_stats);
+static void bfa_cee_format_lldp_stats(struct bfa_cee_lldp_stats_s
+ *lldp_stats);
+static void bfa_cee_format_cfg_stats(struct bfa_cee_cfg_stats_s *cfg_stats);
+static void bfa_cee_format_cee_cfg(void *buffer);
+static void bfa_cee_format_cee_stats(void *buffer);
+
+static void
+bfa_cee_format_cee_stats(void *buffer)
+{
+ struct bfa_cee_stats_s *cee_stats = buffer;
+ bfa_cee_format_dcbcx_stats(&cee_stats->dcbx_stats);
+ bfa_cee_format_lldp_stats(&cee_stats->lldp_stats);
+ bfa_cee_format_cfg_stats(&cee_stats->cfg_stats);
+}
+
+static void
+bfa_cee_format_cee_cfg(void *buffer)
+{
+ struct bfa_cee_attr_s *cee_cfg = buffer;
+ bfa_cee_format_lldp_cfg(&cee_cfg->lldp_remote);
+}
+
+static void
+bfa_cee_format_dcbcx_stats(struct bfa_cee_dcbx_stats_s *dcbcx_stats)
+{
+ dcbcx_stats->subtlvs_unrecognized =
+ bfa_os_ntohl(dcbcx_stats->subtlvs_unrecognized);
+ dcbcx_stats->negotiation_failed =
+ bfa_os_ntohl(dcbcx_stats->negotiation_failed);
+ dcbcx_stats->remote_cfg_changed =
+ bfa_os_ntohl(dcbcx_stats->remote_cfg_changed);
+ dcbcx_stats->tlvs_received = bfa_os_ntohl(dcbcx_stats->tlvs_received);
+ dcbcx_stats->tlvs_invalid = bfa_os_ntohl(dcbcx_stats->tlvs_invalid);
+ dcbcx_stats->seqno = bfa_os_ntohl(dcbcx_stats->seqno);
+ dcbcx_stats->ackno = bfa_os_ntohl(dcbcx_stats->ackno);
+ dcbcx_stats->recvd_seqno = bfa_os_ntohl(dcbcx_stats->recvd_seqno);
+ dcbcx_stats->recvd_ackno = bfa_os_ntohl(dcbcx_stats->recvd_ackno);
+}
+
+static void
+bfa_cee_format_lldp_stats(struct bfa_cee_lldp_stats_s *lldp_stats)
+{
+ lldp_stats->frames_transmitted =
+ bfa_os_ntohl(lldp_stats->frames_transmitted);
+ lldp_stats->frames_aged_out = bfa_os_ntohl(lldp_stats->frames_aged_out);
+ lldp_stats->frames_discarded =
+ bfa_os_ntohl(lldp_stats->frames_discarded);
+ lldp_stats->frames_in_error = bfa_os_ntohl(lldp_stats->frames_in_error);
+ lldp_stats->frames_rcvd = bfa_os_ntohl(lldp_stats->frames_rcvd);
+ lldp_stats->tlvs_discarded = bfa_os_ntohl(lldp_stats->tlvs_discarded);
+ lldp_stats->tlvs_unrecognized =
+ bfa_os_ntohl(lldp_stats->tlvs_unrecognized);
+}
+
+static void
+bfa_cee_format_cfg_stats(struct bfa_cee_cfg_stats_s *cfg_stats)
+{
+ cfg_stats->cee_status_down = bfa_os_ntohl(cfg_stats->cee_status_down);
+ cfg_stats->cee_status_up = bfa_os_ntohl(cfg_stats->cee_status_up);
+ cfg_stats->cee_hw_cfg_changed =
+ bfa_os_ntohl(cfg_stats->cee_hw_cfg_changed);
+ cfg_stats->recvd_invalid_cfg =
+ bfa_os_ntohl(cfg_stats->recvd_invalid_cfg);
+}
+
+static void
+bfa_cee_format_lldp_cfg(struct bfa_cee_lldp_cfg_s *lldp_cfg)
+{
+ lldp_cfg->time_to_interval = bfa_os_ntohs(lldp_cfg->time_to_interval);
+ lldp_cfg->enabled_system_cap =
+ bfa_os_ntohs(lldp_cfg->enabled_system_cap);
+}
+
+/**
+ * bfa_cee_attr_meminfo()
+ *
+ *
+ * @param[in] void
+ *
+ * @return Size of DMA region
+ */
+static u32
+bfa_cee_attr_meminfo(void)
+{
+ return BFA_ROUNDUP(sizeof(struct bfa_cee_attr_s), BFA_DMA_ALIGN_SZ);
+}
+
+/**
+ * bfa_cee_stats_meminfo()
+ *
+ *
+ * @param[in] void
+ *
+ * @return Size of DMA region
+ */
+static u32
+bfa_cee_stats_meminfo(void)
+{
+ return BFA_ROUNDUP(sizeof(struct bfa_cee_stats_s), BFA_DMA_ALIGN_SZ);
+}
+
+/**
+ * bfa_cee_get_attr_isr()
+ *
+ *
+ * @param[in] cee - Pointer to the CEE module
+ * status - Return status from the f/w
+ *
+ * @return void
+ */
+static void
+bfa_cee_get_attr_isr(struct bfa_cee_s *cee, bfa_status_t status)
+{
+ cee->get_attr_status = status;
+ if (status == BFA_STATUS_OK) {
+ /*
+ * The requested data has been copied to the DMA area, *process
+ * it.
+ */
+ memcpy(cee->attr, cee->attr_dma.kva,
+ sizeof(struct bfa_cee_attr_s));
+ bfa_cee_format_cee_cfg(cee->attr);
+ }
+ cee->get_attr_pending = BFA_FALSE;
+ if (cee->cbfn.get_attr_cbfn)
+ cee->cbfn.get_attr_cbfn(cee->cbfn.get_attr_cbarg, status);
+}
+
+/**
+ * bfa_cee_get_attr_isr()
+ *
+ *
+ * @param[in] cee - Pointer to the CEE module
+ * status - Return status from the f/w
+ *
+ * @return void
+ */
+static void
+bfa_cee_get_stats_isr(struct bfa_cee_s *cee, bfa_status_t status)
+{
+ cee->get_stats_status = status;
+ if (status == BFA_STATUS_OK) {
+ /*
+ * The requested data has been copied to the DMA area, process
+ * it.
+ */
+ memcpy(cee->stats, cee->stats_dma.kva,
+ sizeof(struct bfa_cee_stats_s));
+ bfa_cee_format_cee_stats(cee->stats);
+ }
+ cee->get_stats_pending = BFA_FALSE;
+ if (cee->cbfn.get_stats_cbfn)
+ cee->cbfn.get_stats_cbfn(cee->cbfn.get_stats_cbarg, status);
+}
+
+/**
+ * bfa_cee_get_attr_isr()
+ *
+ *
+ * @param[in] cee - Pointer to the CEE module
+ * status - Return status from the f/w
+ *
+ * @return void
+ */
+static void
+bfa_cee_reset_stats_isr(struct bfa_cee_s *cee, bfa_status_t status)
+{
+ cee->reset_stats_status = status;
+ cee->reset_stats_pending = BFA_FALSE;
+ if (cee->cbfn.reset_stats_cbfn)
+ cee->cbfn.reset_stats_cbfn(cee->cbfn.reset_stats_cbarg, status);
+}
+
+/**
+ * bfa_cee_meminfo()
+ *
+ *
+ * @param[in] void
+ *
+ * @return Size of DMA region
+ */
+u32
+bfa_cee_meminfo(void)
+{
+ return bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo();
+}
+
+/**
+ * bfa_cee_mem_claim()
+ *
+ *
+ * @param[in] cee CEE module pointer
+ * dma_kva Kernel Virtual Address of CEE DMA Memory
+ * dma_pa Physical Address of CEE DMA Memory
+ *
+ * @return void
+ */
+void
+bfa_cee_mem_claim(struct bfa_cee_s *cee, u8 *dma_kva, u64 dma_pa)
+{
+ cee->attr_dma.kva = dma_kva;
+ cee->attr_dma.pa = dma_pa;
+ cee->stats_dma.kva = dma_kva + bfa_cee_attr_meminfo();
+ cee->stats_dma.pa = dma_pa + bfa_cee_attr_meminfo();
+ cee->attr = (struct bfa_cee_attr_s *)dma_kva;
+ cee->stats =
+ (struct bfa_cee_stats_s *)(dma_kva + bfa_cee_attr_meminfo());
+}
+
+/**
+ * bfa_cee_get_attr()
+ *
+ * Send the request to the f/w to fetch CEE attributes.
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ *
+ * @return Status
+ */
+
+bfa_status_t
+bfa_cee_get_attr(struct bfa_cee_s *cee, struct bfa_cee_attr_s *attr,
+ bfa_cee_get_attr_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_cee_get_req_s *cmd;
+
+ bfa_assert((cee != NULL) && (cee->ioc != NULL));
+ if (!bfa_ioc_is_operational(cee->ioc))
+ return BFA_STATUS_IOC_FAILURE;
+ if (cee->get_attr_pending == BFA_TRUE)
+ return BFA_STATUS_DEVBUSY;
+ cee->get_attr_pending = BFA_TRUE;
+ cmd = (struct bfi_cee_get_req_s *)cee->get_cfg_mb.msg;
+ cee->attr = attr;
+ cee->cbfn.get_attr_cbfn = cbfn;
+ cee->cbfn.get_attr_cbarg = cbarg;
+ bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_CFG_REQ,
+ bfa_ioc_portid(cee->ioc));
+ bfa_dma_be_addr_set(cmd->dma_addr, cee->attr_dma.pa);
+ bfa_ioc_mbox_queue(cee->ioc, &cee->get_cfg_mb);
+
+ return BFA_STATUS_OK;
+}
+
+/**
+ * bfa_cee_get_stats()
+ *
+ * Send the request to the f/w to fetch CEE statistics.
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ *
+ * @return Status
+ */
+
+bfa_status_t
+bfa_cee_get_stats(struct bfa_cee_s *cee, struct bfa_cee_stats_s *stats,
+ bfa_cee_get_stats_cbfn_t cbfn, void *cbarg)
+{
+ struct bfi_cee_get_req_s *cmd;
+
+ bfa_assert((cee != NULL) && (cee->ioc != NULL));
+
+ if (!bfa_ioc_is_operational(cee->ioc))
+ return BFA_STATUS_IOC_FAILURE;
+ if (cee->get_stats_pending == BFA_TRUE)
+ return BFA_STATUS_DEVBUSY;
+ cee->get_stats_pending = BFA_TRUE;
+ cmd = (struct bfi_cee_get_req_s *)cee->get_stats_mb.msg;
+ cee->stats = stats;
+ cee->cbfn.get_stats_cbfn = cbfn;
+ cee->cbfn.get_stats_cbarg = cbarg;
+ bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_STATS_REQ,
+ bfa_ioc_portid(cee->ioc));
+ bfa_dma_be_addr_set(cmd->dma_addr, cee->stats_dma.pa);
+ bfa_ioc_mbox_queue(cee->ioc, &cee->get_stats_mb);
+
+ return BFA_STATUS_OK;
+}
+
+/**
+ * bfa_cee_reset_stats()
+ *
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ *
+ * @return Status
+ */
+
+bfa_status_t
+bfa_cee_reset_stats(struct bfa_cee_s *cee, bfa_cee_reset_stats_cbfn_t cbfn,
+ void *cbarg)
+{
+ struct bfi_cee_reset_stats_s *cmd;
+
+ bfa_assert((cee != NULL) && (cee->ioc != NULL));
+ if (!bfa_ioc_is_operational(cee->ioc))
+ return BFA_STATUS_IOC_FAILURE;
+ if (cee->reset_stats_pending == BFA_TRUE)
+ return BFA_STATUS_DEVBUSY;
+ cee->reset_stats_pending = BFA_TRUE;
+ cmd = (struct bfi_cee_reset_stats_s *)cee->reset_stats_mb.msg;
+ cee->cbfn.reset_stats_cbfn = cbfn;
+ cee->cbfn.reset_stats_cbarg = cbarg;
+ bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_RESET_STATS,
+ bfa_ioc_portid(cee->ioc));
+ bfa_ioc_mbox_queue(cee->ioc, &cee->reset_stats_mb);
+ return BFA_STATUS_OK;
+}
+
+/**
+ * bfa_cee_isrs()
+ *
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ *
+ * @return void
+ */
+
+void
+bfa_cee_isr(void *cbarg, struct bfi_mbmsg_s *m)
+{
+ union bfi_cee_i2h_msg_u *msg;
+ struct bfi_cee_get_rsp_s *get_rsp;
+ struct bfa_cee_s *cee = (struct bfa_cee_s *)cbarg;
+ msg = (union bfi_cee_i2h_msg_u *)m;
+ get_rsp = (struct bfi_cee_get_rsp_s *)m;
+ switch (msg->mh.msg_id) {
+ case BFI_CEE_I2H_GET_CFG_RSP:
+ bfa_cee_get_attr_isr(cee, get_rsp->cmd_status);
+ break;
+ case BFI_CEE_I2H_GET_STATS_RSP:
+ bfa_cee_get_stats_isr(cee, get_rsp->cmd_status);
+ break;
+ case BFI_CEE_I2H_RESET_STATS_RSP:
+ bfa_cee_reset_stats_isr(cee, get_rsp->cmd_status);
+ break;
+ default:
+ bfa_assert(0);
+ }
+}
+
+/**
+ * bfa_cee_hbfail()
+ *
+ *
+ * @param[in] Pointer to the CEE module data structure.
+ *
+ * @return void
+ */
+
+void
+bfa_cee_hbfail(void *arg)
+{
+ struct bfa_cee_s *cee;
+ cee = (struct bfa_cee_s *)arg;
+
+ if (cee->get_attr_pending == BFA_TRUE) {
+ cee->get_attr_status = BFA_STATUS_FAILED;
+ cee->get_attr_pending = BFA_FALSE;
+ if (cee->cbfn.get_attr_cbfn)
+ cee->cbfn.get_attr_cbfn(cee->cbfn.get_attr_cbarg,
+ BFA_STATUS_FAILED);
+ }
+ if (cee->get_stats_pending == BFA_TRUE) {
+ cee->get_stats_status = BFA_STATUS_FAILED;
+ cee->get_stats_pending = BFA_FALSE;
+ if (cee->cbfn.get_stats_cbfn)
+ cee->cbfn.get_stats_cbfn(cee->cbfn.get_stats_cbarg,
+ BFA_STATUS_FAILED);
+ }
+ if (cee->reset_stats_pending == BFA_TRUE) {
+ cee->reset_stats_status = BFA_STATUS_FAILED;
+ cee->reset_stats_pending = BFA_FALSE;
+ if (cee->cbfn.reset_stats_cbfn) {
+ cee->cbfn.reset_stats_cbfn(cee->cbfn.reset_stats_cbarg,
+ BFA_STATUS_FAILED);
+ }
+ }
+}
+
+/**
+ * bfa_cee_attach()
+ *
+ *
+ * @param[in] cee - Pointer to the CEE module data structure
+ * ioc - Pointer to the ioc module data structure
+ * dev - Pointer to the device driver module data structure
+ * The device driver specific mbox ISR functions have
+ * this pointer as one of the parameters.
+ * trcmod -
+ * logmod -
+ *
+ * @return void
+ */
+void
+bfa_cee_attach(struct bfa_cee_s *cee, struct bfa_ioc_s *ioc, void *dev,
+ struct bfa_trc_mod_s *trcmod, struct bfa_log_mod_s *logmod)
+{
+ bfa_assert(cee != NULL);
+ cee->dev = dev;
+ cee->trcmod = trcmod;
+ cee->logmod = logmod;
+ cee->ioc = ioc;
+
+ bfa_ioc_mbox_regisr(cee->ioc, BFI_MC_CEE, bfa_cee_isr, cee);
+ bfa_ioc_hbfail_init(&cee->hbfail, bfa_cee_hbfail, cee);
+ bfa_ioc_hbfail_register(cee->ioc, &cee->hbfail);
+}
+
+/**
+ * bfa_cee_detach()
+ *
+ *
+ * @param[in] cee - Pointer to the CEE module data structure
+ *
+ * @return void
+ */
+void
+bfa_cee_detach(struct bfa_cee_s *cee)
+{
+ /*
+ * For now, just check if there is some ioctl pending and mark that as
+ * failed?
+ */
+ /* bfa_cee_hbfail(cee); */
+}
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bfa_csdebug.c linux-2.6.32-rc4-mod/drivers/net/bna/bfa_csdebug.c
--- linux-2.6.32-rc4-orig/drivers/net/bna/bfa_csdebug.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bfa_csdebug.c 2009-10-16 10:30:53.421438000 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * See LICENSE.bna for copyright and licensing details.
+ */
+
+#include <cs/bfa_debug.h>
+#include <bfa_os_inc.h>
+#include <cs/bfa_q.h>
+
+/**
+ * cs_debug_api
+ */
+
+
+void
+bfa_panic(int line, char *file, char *panicstr)
+{
+ bfa_os_panic();
+}
+
+void
+bfa_sm_panic(struct bfa_log_mod_s *logm, int line, char *file, int event)
+{
+ bfa_os_panic();
+}
+
+int
+bfa_q_is_on_q_func(struct list_head *q, struct list_head *qe)
+{
+ struct list_head *tqe;
+
+ tqe = bfa_q_next(q);
+ while (tqe != q) {
+ if (tqe == qe)
+ return 1;
+ tqe = bfa_q_next(tqe);
+ if (tqe == NULL)
+ break;
+ }
+ return 0;
+}
+
+
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bfa_ioc.c linux-2.6.32-rc4-mod/drivers/net/bna/bfa_ioc.c
--- linux-2.6.32-rc4-orig/drivers/net/bna/bfa_ioc.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bfa_ioc.c 2009-10-16 10:30:53.439441000 -0700
@@ -0,0 +1,2273 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * See LICENSE.bna for copyright and licensing details.
+ */
+
+#include <bfa_ioc.h>
+#include <bfa_fwimg_priv.h>
+#include <cs/bfa_debug.h>
+#include <bfi/bfi_ioc.h>
+#include <bfi/bfi_ctreg.h>
+#include <defs/bfa_defs_pci.h>
+#include <cna.h>
+
+
+/**
+ * IOC local definitions
+ */
+#define BFA_IOC_TOV 2000 /* msecs */
+#define BFA_IOC_HB_TOV 1000 /* msecs */
+#define BFA_IOC_HB_FAIL_MAX 4
+#define BFA_IOC_HWINIT_MAX 2
+#define BFA_IOC_FWIMG_MINSZ (16 * 1024)
+#define BFA_IOC_TOV_RECOVER (BFA_IOC_HB_FAIL_MAX * BFA_IOC_HB_TOV \
+ + BFA_IOC_TOV)
+
+#define bfa_ioc_timer_start(__ioc) \
+ bfa_timer_begin((__ioc)->timer_mod, &(__ioc)->ioc_timer, \
+ bfa_ioc_timeout, (__ioc), BFA_IOC_TOV)
+#define bfa_ioc_timer_stop(__ioc) bfa_timer_stop(&(__ioc)->ioc_timer)
+
+#define BFA_DBG_FWTRC_ENTS (BFI_IOC_TRC_ENTS)
+#define BFA_DBG_FWTRC_LEN \
+ (BFA_DBG_FWTRC_ENTS * sizeof(struct bfa_trc_s) + \
+ (sizeof(struct bfa_trc_mod_s) - \
+ BFA_TRC_MAX * sizeof(struct bfa_trc_s)))
+#define BFA_DBG_FWTRC_OFF(_fn) (BFI_IOC_TRC_OFF + BFA_DBG_FWTRC_LEN * (_fn))
+#define bfa_ioc_stats(_ioc, _stats) ((_ioc)->stats._stats++)
+
+#define BFA_FLASH_CHUNK_NO(off) (off / BFI_FLASH_CHUNK_SZ_WORDS)
+#define BFA_FLASH_OFFSET_IN_CHUNK(off) (off % BFI_FLASH_CHUNK_SZ_WORDS)
+#define BFA_FLASH_CHUNK_ADDR(chunkno) (chunkno * BFI_FLASH_CHUNK_SZ_WORDS)
+bfa_boolean_t bfa_auto_recover = BFA_FALSE;
+
+/*
+ * forward declarations
+ */
+static void bfa_ioc_hw_sem_get(struct bfa_ioc_s *ioc);
+static void bfa_ioc_hw_sem_release(struct bfa_ioc_s *ioc);
+static void bfa_ioc_hw_sem_get_cancel(struct bfa_ioc_s *ioc);
+static void bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force);
+static void bfa_ioc_timeout(void *ioc);
+static void bfa_ioc_send_enable(struct bfa_ioc_s *ioc);
+static void bfa_ioc_send_disable(struct bfa_ioc_s *ioc);
+static void bfa_ioc_send_getattr(struct bfa_ioc_s *ioc);
+static void bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc);
+static void bfa_ioc_hb_stop(struct bfa_ioc_s *ioc);
+static void bfa_ioc_reset(struct bfa_ioc_s *ioc, bfa_boolean_t force);
+static void bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc);
+static void bfa_ioc_mbox_hbfail(struct bfa_ioc_s *ioc);
+static void bfa_ioc_recover(struct bfa_ioc_s *ioc);
+static bfa_boolean_t bfa_ioc_firmware_lock(struct bfa_ioc_s *ioc);
+static void bfa_ioc_firmware_unlock(struct bfa_ioc_s *ioc);
+static void bfa_ioc_disable_comp(struct bfa_ioc_s *ioc);
+static void bfa_ioc_lpu_stop(struct bfa_ioc_s *ioc);
+
+/**
+ * bfa_ioc_sm
+ */
+
+/**
+ * IOC state machine events
+ */
+enum ioc_event {
+ IOC_E_ENABLE = 1, /* IOC enable request */
+ IOC_E_DISABLE = 2, /* IOC disable request */
+ IOC_E_TIMEOUT = 3, /* f/w response timeout */
+ IOC_E_FWREADY = 4, /* f/w initialization done */
+ IOC_E_FWRSP_GETATTR = 5, /* IOC get attribute response */
+ IOC_E_FWRSP_ENABLE = 6, /* enable f/w response */
+ IOC_E_FWRSP_DISABLE = 7, /* disable f/w response */
+ IOC_E_HBFAIL = 8, /* heartbeat failure */
+ IOC_E_HWERROR = 9, /* hardware error interrupt */
+ IOC_E_SEMLOCKED = 10, /* h/w semaphore is locked */
+ IOC_E_DETACH = 11, /* driver detach cleanup */
+};
+
+bfa_fsm_state_decl(bfa_ioc, reset, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, fwcheck, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, mismatch, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, semwait, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, hwinit, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, enabling, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, getattr, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, op, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, initfail, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, hbfail, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, disabling, struct bfa_ioc_s, enum ioc_event);
+bfa_fsm_state_decl(bfa_ioc, disabled, struct bfa_ioc_s, enum ioc_event);
+
+static struct bfa_sm_table_s ioc_sm_table[] = {
+ {BFA_SM(bfa_ioc_sm_reset), BFA_IOC_RESET},
+ {BFA_SM(bfa_ioc_sm_fwcheck), BFA_IOC_FWMISMATCH},
+ {BFA_SM(bfa_ioc_sm_mismatch), BFA_IOC_FWMISMATCH},
+ {BFA_SM(bfa_ioc_sm_semwait), BFA_IOC_SEMWAIT},
+ {BFA_SM(bfa_ioc_sm_hwinit), BFA_IOC_HWINIT},
+ {BFA_SM(bfa_ioc_sm_enabling), BFA_IOC_HWINIT},
+ {BFA_SM(bfa_ioc_sm_getattr), BFA_IOC_GETATTR},
+ {BFA_SM(bfa_ioc_sm_op), BFA_IOC_OPERATIONAL},
+ {BFA_SM(bfa_ioc_sm_initfail), BFA_IOC_INITFAIL},
+ {BFA_SM(bfa_ioc_sm_hbfail), BFA_IOC_HBFAIL},
+ {BFA_SM(bfa_ioc_sm_disabling), BFA_IOC_DISABLING},
+ {BFA_SM(bfa_ioc_sm_disabled), BFA_IOC_DISABLED},
+};
+
+/**
+ * Reset entry actions -- initialize state machine
+ */
+static void
+bfa_ioc_sm_reset_entry(struct bfa_ioc_s *ioc)
+{
+ ioc->retry_count = 0;
+ ioc->auto_recover = bfa_auto_recover;
+}
+
+/**
+ * Beginning state. IOC is in reset state.
+ */
+static void
+bfa_ioc_sm_reset(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_ENABLE:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_fwcheck);
+ break;
+
+ case IOC_E_DISABLE:
+ bfa_ioc_disable_comp(ioc);
+ break;
+
+ case IOC_E_DETACH:
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+/**
+ * Semaphore should be acquired for version check.
+ */
+static void
+bfa_ioc_sm_fwcheck_entry(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_hw_sem_get(ioc);
+}
+
+/**
+ * Awaiting h/w semaphore to continue with version check.
+ */
+static void
+bfa_ioc_sm_fwcheck(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_SEMLOCKED:
+ if (bfa_ioc_firmware_lock(ioc)) {
+ ioc->retry_count = 0;
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hwinit);
+ } else {
+ bfa_ioc_hw_sem_release(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_mismatch);
+ }
+ break;
+
+ case IOC_E_DISABLE:
+ bfa_ioc_disable_comp(ioc);
+ /*
+ * fall through
+ */
+
+ case IOC_E_DETACH:
+ bfa_ioc_hw_sem_get_cancel(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_reset);
+ break;
+
+ case IOC_E_FWREADY:
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+/**
+ * Notify enable completion callback and generate mismatch AEN.
+ */
+static void
+bfa_ioc_sm_mismatch_entry(struct bfa_ioc_s *ioc)
+{
+ /**
+ * Provide enable completion callback and AEN notification only once.
+ */
+ if (ioc->retry_count == 0)
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ ioc->retry_count++;
+ bfa_ioc_timer_start(ioc);
+}
+
+/**
+ * Awaiting firmware version match.
+ */
+static void
+bfa_ioc_sm_mismatch(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_TIMEOUT:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_fwcheck);
+ break;
+
+ case IOC_E_DISABLE:
+ bfa_ioc_disable_comp(ioc);
+ /*
+ * fall through
+ */
+
+ case IOC_E_DETACH:
+ bfa_ioc_timer_stop(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_reset);
+ break;
+
+ case IOC_E_FWREADY:
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+/**
+ * Request for semaphore.
+ */
+static void
+bfa_ioc_sm_semwait_entry(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_hw_sem_get(ioc);
+}
+
+/**
+ * Awaiting semaphore for h/w initialzation.
+ */
+static void
+bfa_ioc_sm_semwait(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_SEMLOCKED:
+ ioc->retry_count = 0;
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hwinit);
+ break;
+
+ case IOC_E_DISABLE:
+ bfa_ioc_hw_sem_get_cancel(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled);
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+
+static void
+bfa_ioc_sm_hwinit_entry(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_timer_start(ioc);
+ bfa_ioc_reset(ioc, BFA_FALSE);
+}
+
+/**
+ * Hardware is being initialized. Interrupts are enabled.
+ * Holding hardware semaphore lock.
+ */
+static void
+bfa_ioc_sm_hwinit(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_FWREADY:
+ bfa_ioc_timer_stop(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_enabling);
+ break;
+
+ case IOC_E_HWERROR:
+ bfa_ioc_timer_stop(ioc);
+ /*
+ * fall through
+ */
+
+ case IOC_E_TIMEOUT:
+ ioc->retry_count++;
+ if (ioc->retry_count < BFA_IOC_HWINIT_MAX) {
+ bfa_ioc_timer_start(ioc);
+ bfa_ioc_reset(ioc, BFA_TRUE);
+ break;
+ }
+
+ bfa_ioc_hw_sem_release(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_initfail);
+ break;
+
+ case IOC_E_DISABLE:
+ bfa_ioc_hw_sem_release(ioc);
+ bfa_ioc_timer_stop(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled);
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+
+static void
+bfa_ioc_sm_enabling_entry(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_timer_start(ioc);
+ bfa_ioc_send_enable(ioc);
+}
+
+/**
+ * Host IOC function is being enabled, awaiting response from firmware.
+ * Semaphore is acquired.
+ */
+static void
+bfa_ioc_sm_enabling(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_FWRSP_ENABLE:
+ bfa_ioc_timer_stop(ioc);
+ bfa_ioc_hw_sem_release(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_getattr);
+ break;
+
+ case IOC_E_HWERROR:
+ bfa_ioc_timer_stop(ioc);
+ /*
+ * fall through
+ */
+
+ case IOC_E_TIMEOUT:
+ ioc->retry_count++;
+ if (ioc->retry_count < BFA_IOC_HWINIT_MAX) {
+ bfa_reg_write(ioc->ioc_regs.ioc_fwstate,
+ BFI_IOC_UNINIT);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hwinit);
+ break;
+ }
+
+ bfa_ioc_hw_sem_release(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_initfail);
+ break;
+
+ case IOC_E_DISABLE:
+ bfa_ioc_timer_stop(ioc);
+ bfa_ioc_hw_sem_release(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled);
+ break;
+
+ case IOC_E_FWREADY:
+ bfa_ioc_send_enable(ioc);
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+
+static void
+bfa_ioc_sm_getattr_entry(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_timer_start(ioc);
+ bfa_ioc_send_getattr(ioc);
+}
+
+/**
+ * IOC configuration in progress. Timer is active.
+ */
+static void
+bfa_ioc_sm_getattr(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_FWRSP_GETATTR:
+ bfa_ioc_timer_stop(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_op);
+ break;
+
+ case IOC_E_HWERROR:
+ bfa_ioc_timer_stop(ioc);
+ /*
+ * fall through
+ */
+
+ case IOC_E_TIMEOUT:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_initfail);
+ break;
+
+ case IOC_E_DISABLE:
+ bfa_ioc_timer_stop(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled);
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+
+static void
+bfa_ioc_sm_op_entry(struct bfa_ioc_s *ioc)
+{
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_OK);
+ bfa_ioc_hb_monitor(ioc);
+}
+
+static void
+bfa_ioc_sm_op(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_ENABLE:
+ break;
+
+ case IOC_E_DISABLE:
+ bfa_ioc_hb_stop(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_disabling);
+ break;
+
+ case IOC_E_HWERROR:
+ case IOC_E_FWREADY:
+ /**
+ * Hard error or IOC recovery by other function.
+ * Treat it same as heartbeat failure.
+ */
+ bfa_ioc_hb_stop(ioc);
+ /*
+ * !!! fall through !!!
+ */
+
+ case IOC_E_HBFAIL:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_hbfail);
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+
+static void
+bfa_ioc_sm_disabling_entry(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_timer_start(ioc);
+ bfa_ioc_send_disable(ioc);
+}
+
+/**
+ * IOC is being disabled
+ */
+static void
+bfa_ioc_sm_disabling(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_HWERROR:
+ case IOC_E_FWRSP_DISABLE:
+ bfa_ioc_timer_stop(ioc);
+ /*
+ * !!! fall through !!!
+ */
+
+ case IOC_E_TIMEOUT:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled);
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+/**
+ * IOC disable completion entry.
+ */
+static void
+bfa_ioc_sm_disabled_entry(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_disable_comp(ioc);
+}
+
+static void
+bfa_ioc_sm_disabled(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_ENABLE:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_semwait);
+ break;
+
+ case IOC_E_DISABLE:
+ ioc->cbfn->disable_cbfn(ioc->bfa);
+ break;
+
+ case IOC_E_FWREADY:
+ break;
+
+ case IOC_E_DETACH:
+ bfa_ioc_firmware_unlock(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_reset);
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+
+static void
+bfa_ioc_sm_initfail_entry(struct bfa_ioc_s *ioc)
+{
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ bfa_ioc_timer_start(ioc);
+}
+
+/**
+ * Hardware initialization failed.
+ */
+static void
+bfa_ioc_sm_initfail(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+ case IOC_E_DISABLE:
+ bfa_ioc_timer_stop(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled);
+ break;
+
+ case IOC_E_DETACH:
+ bfa_ioc_timer_stop(ioc);
+ bfa_ioc_firmware_unlock(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_reset);
+ break;
+
+ case IOC_E_TIMEOUT:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_semwait);
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+
+static void
+bfa_ioc_sm_hbfail_entry(struct bfa_ioc_s *ioc)
+{
+ struct list_head *qe;
+ struct bfa_ioc_hbfail_notify_s *notify;
+
+ /**
+ * Mark IOC as failed in hardware and stop firmware.
+ */
+ bfa_ioc_lpu_stop(ioc);
+ bfa_reg_write(ioc->ioc_regs.ioc_fwstate, BFI_IOC_HBFAIL);
+
+ if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) {
+ bfa_reg_write(ioc->ioc_regs.ll_halt, __FW_INIT_HALT_P);
+ /*
+ * Wait for halt to take effect
+ */
+ bfa_reg_read(ioc->ioc_regs.ll_halt);
+ }
+
+ /**
+ * Notify driver and common modules registered for notification.
+ */
+ ioc->cbfn->hbfail_cbfn(ioc->bfa);
+ list_for_each(qe, &ioc->hb_notify_q) {
+ notify = (struct bfa_ioc_hbfail_notify_s *)qe;
+ notify->cbfn(notify->cbarg);
+ }
+
+ /**
+ * Flush any queued up mailbox requests.
+ */
+ bfa_ioc_mbox_hbfail(ioc);
+
+ /**
+ * Trigger auto-recovery after a delay.
+ */
+ if (ioc->auto_recover) {
+ bfa_timer_begin(ioc->timer_mod, &ioc->ioc_timer,
+ bfa_ioc_timeout, ioc, BFA_IOC_TOV_RECOVER);
+ }
+}
+
+/**
+ * IOC heartbeat failure.
+ */
+static void
+bfa_ioc_sm_hbfail(struct bfa_ioc_s *ioc, enum ioc_event event)
+{
+
+ switch (event) {
+
+ case IOC_E_ENABLE:
+ ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
+ break;
+
+ case IOC_E_DISABLE:
+ if (ioc->auto_recover)
+ bfa_ioc_timer_stop(ioc);
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled);
+ break;
+
+ case IOC_E_TIMEOUT:
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_semwait);
+ break;
+
+ case IOC_E_FWREADY:
+ /**
+ * Recovery is already initiated by other function.
+ */
+ break;
+
+ default:
+ bfa_sm_fault(ioc, event);
+ }
+}
+
+
+
+/**
+ * bfa_ioc_pvt BFA IOC private functions
+ */
+
+static void
+bfa_ioc_disable_comp(struct bfa_ioc_s *ioc)
+{
+ struct list_head *qe;
+ struct bfa_ioc_hbfail_notify_s *notify;
+
+ ioc->cbfn->disable_cbfn(ioc->bfa);
+
+ /**
+ * Notify common modules registered for notification.
+ */
+ list_for_each(qe, &ioc->hb_notify_q) {
+ notify = (struct bfa_ioc_hbfail_notify_s *)qe;
+ notify->cbfn(notify->cbarg);
+ }
+}
+
+static void
+bfa_ioc_sem_timeout(void *ioc_arg)
+{
+ struct bfa_ioc_s *ioc = (struct bfa_ioc_s *)ioc_arg;
+
+ bfa_ioc_hw_sem_get(ioc);
+}
+
+static void
+bfa_ioc_usage_sem_get(struct bfa_ioc_s *ioc)
+{
+ u32 r32;
+ int cnt = 0;
+#define BFA_SEM_SPINCNT 1000
+
+ do {
+ r32 = bfa_reg_read(ioc->ioc_regs.ioc_usage_sem_reg);
+ cnt++;
+ if (cnt > BFA_SEM_SPINCNT)
+ break;
+ } while (r32 != 0);
+ bfa_assert(cnt < BFA_SEM_SPINCNT);
+}
+
+static void
+bfa_ioc_usage_sem_release(struct bfa_ioc_s *ioc)
+{
+ bfa_reg_write(ioc->ioc_regs.ioc_usage_sem_reg, 1);
+}
+
+static void
+bfa_ioc_hw_sem_get(struct bfa_ioc_s *ioc)
+{
+ u32 r32;
+
+ /**
+ * First read to the semaphore register will return 0, subsequent reads
+ * will return 1. Semaphore is released by writing 0 to the register
+ */
+ r32 = bfa_reg_read(ioc->ioc_regs.ioc_sem_reg);
+ if (r32 == 0) {
+ bfa_fsm_send_event(ioc, IOC_E_SEMLOCKED);
+ return;
+ }
+
+ bfa_timer_begin(ioc->timer_mod, &ioc->sem_timer, bfa_ioc_sem_timeout,
+ ioc, BFA_IOC_TOV);
+}
+
+static void
+bfa_ioc_hw_sem_release(struct bfa_ioc_s *ioc)
+{
+ bfa_reg_write(ioc->ioc_regs.ioc_sem_reg, 1);
+}
+
+static void
+bfa_ioc_hw_sem_get_cancel(struct bfa_ioc_s *ioc)
+{
+ bfa_timer_stop(&ioc->sem_timer);
+}
+
+/**
+ * Initialize LPU local memory (aka secondary memory / SRAM)
+ */
+static void
+bfa_ioc_lmem_init(struct bfa_ioc_s *ioc)
+{
+ u32 pss_ctl;
+ int i;
+#define PSS_LMEM_INIT_TIME 10000
+
+ pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg);
+ pss_ctl &= ~__PSS_LMEM_RESET;
+ pss_ctl |= __PSS_LMEM_INIT_EN;
+ pss_ctl |= __PSS_I2C_CLK_DIV(3UL); /* i2c workaround 12.5khz clock */
+ bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl);
+
+ /**
+ * wait for memory initialization to be complete
+ */
+ i = 0;
+ do {
+ pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg);
+ i++;
+ } while (!(pss_ctl & __PSS_LMEM_INIT_DONE) && (i < PSS_LMEM_INIT_TIME));
+
+ /**
+ * If memory initialization is not successful, IOC timeout will catch
+ * such failures.
+ */
+ bfa_assert(pss_ctl & __PSS_LMEM_INIT_DONE);
+
+ pss_ctl &= ~(__PSS_LMEM_INIT_DONE | __PSS_LMEM_INIT_EN);
+ bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl);
+}
+
+static void
+bfa_ioc_lpu_start(struct bfa_ioc_s *ioc)
+{
+ u32 pss_ctl;
+
+ /**
+ * Take processor out of reset.
+ */
+ pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg);
+ pss_ctl &= ~__PSS_LPU0_RESET;
+
+ bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl);
+}
+
+static void
+bfa_ioc_lpu_stop(struct bfa_ioc_s *ioc)
+{
+ u32 pss_ctl;
+
+ /**
+ * Put processors in reset.
+ */
+ pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg);
+ pss_ctl |= (__PSS_LPU0_RESET | __PSS_LPU1_RESET);
+
+ bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl);
+}
+
+/**
+ * Get driver and firmware versions.
+ */
+static void
+bfa_ioc_fwver_get(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr)
+{
+ u32 pgnum, pgoff;
+ u32 loff = 0;
+ int i;
+ u32 *fwsig = (u32 *) fwhdr;
+
+ pgnum = bfa_ioc_smem_pgnum(ioc, loff);
+ pgoff = bfa_ioc_smem_pgoff(ioc, loff);
+ bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum);
+
+ for (i = 0; i < (sizeof(struct bfi_ioc_image_hdr_s) / sizeof(u32));
+ i++) {
+ fwsig[i] = bfa_mem_read(ioc->ioc_regs.smem_page_start, loff);
+ loff += sizeof(u32);
+ }
+}
+
+static u32 *
+bfa_ioc_fwimg_get_chunk(struct bfa_ioc_s *ioc, u32 off)
+{
+ if (ioc->ctdev)
+ return bfi_image_ct_get_chunk(off);
+ return bfi_image_cb_get_chunk(off);
+}
+
+static u32
+bfa_ioc_fwimg_get_size(struct bfa_ioc_s *ioc)
+{
+return (ioc->ctdev) ? bfi_image_ct_size : bfi_image_cb_size;
+}
+
+/**
+ * Returns TRUE if same.
+ */
+static bfa_boolean_t
+bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr)
+{
+ struct bfi_ioc_image_hdr_s *drv_fwhdr;
+ int i;
+
+ drv_fwhdr =
+ (struct bfi_ioc_image_hdr_s *)bfa_ioc_fwimg_get_chunk(ioc, 0);
+
+ for (i = 0; i < BFI_IOC_MD5SUM_SZ; i++) {
+ if (fwhdr->md5sum[i] != drv_fwhdr->md5sum[i])
+ return BFA_FALSE;
+ }
+
+ return BFA_TRUE;
+}
+
+/**
+ * Return true if current running version is valid. Firmware signature and
+ * execution context (driver/bios) must match.
+ */
+static bfa_boolean_t
+bfa_ioc_fwver_valid(struct bfa_ioc_s *ioc)
+{
+ struct bfi_ioc_image_hdr_s fwhdr, *drv_fwhdr;
+
+ /**
+ * If bios/efi boot (flash based) -- return true
+ */
+ if (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ)
+ return BFA_TRUE;
+
+ bfa_ioc_fwver_get(ioc, &fwhdr);
+ drv_fwhdr =
+ (struct bfi_ioc_image_hdr_s *)bfa_ioc_fwimg_get_chunk(ioc, 0);
+
+ if (fwhdr.signature != drv_fwhdr->signature)
+ return BFA_FALSE;
+
+ if (fwhdr.exec != drv_fwhdr->exec)
+ return BFA_FALSE;
+
+ return bfa_ioc_fwver_cmp(ioc, &fwhdr);
+}
+
+/**
+ * Return true if firmware of current driver matches the running firmware.
+ */
+static bfa_boolean_t
+bfa_ioc_firmware_lock(struct bfa_ioc_s *ioc)
+{
+ enum bfi_ioc_state ioc_fwstate;
+ u32 usecnt;
+ struct bfi_ioc_image_hdr_s fwhdr;
+
+ /**
+ * Firmware match check is relevant only for CNA.
+ */
+ if (!ioc->cna)
+ return BFA_TRUE;
+
+ /**
+ * If bios boot (flash based) -- do not increment usage count
+ */
+ if (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ)
+ return BFA_TRUE;
+
+ bfa_ioc_usage_sem_get(ioc);
+ usecnt = bfa_reg_read(ioc->ioc_regs.ioc_usage_reg);
+
+ /**
+ * If usage count is 0, always return TRUE.
+ */
+ if (usecnt == 0) {
+ bfa_reg_write(ioc->ioc_regs.ioc_usage_reg, 1);
+ bfa_ioc_usage_sem_release(ioc);
+ return BFA_TRUE;
+ }
+
+ ioc_fwstate = bfa_reg_read(ioc->ioc_regs.ioc_fwstate);
+
+ /**
+ * Use count cannot be non-zero and chip in uninitialized state.
+ */
+ bfa_assert(ioc_fwstate != BFI_IOC_UNINIT);
+
+ /**
+ * Check if another driver with a different firmware is active
+ */
+ bfa_ioc_fwver_get(ioc, &fwhdr);
+ if (!bfa_ioc_fwver_cmp(ioc, &fwhdr)) {
+ bfa_ioc_usage_sem_release(ioc);
+ return BFA_FALSE;
+ }
+
+ /**
+ * Same firmware version. Increment the reference count.
+ */
+ usecnt++;
+ bfa_reg_write(ioc->ioc_regs.ioc_usage_reg, usecnt);
+ bfa_ioc_usage_sem_release(ioc);
+ return BFA_TRUE;
+}
+
+static void
+bfa_ioc_firmware_unlock(struct bfa_ioc_s *ioc)
+{
+ u32 usecnt;
+
+ /**
+ * Firmware lock is relevant only for CNA.
+ * If bios boot (flash based) -- do not decrement usage count
+ */
+ if (!ioc->cna || (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ))
+ return;
+
+ /**
+ * decrement usage count
+ */
+ bfa_ioc_usage_sem_get(ioc);
+ usecnt = bfa_reg_read(ioc->ioc_regs.ioc_usage_reg);
+ bfa_assert(usecnt > 0);
+
+ usecnt--;
+ bfa_reg_write(ioc->ioc_regs.ioc_usage_reg, usecnt);
+
+ bfa_ioc_usage_sem_release(ioc);
+}
+
+/**
+ * Conditionally flush any pending message from firmware at start.
+ */
+static void
+bfa_ioc_msgflush(struct bfa_ioc_s *ioc)
+{
+ u32 r32;
+
+ r32 = bfa_reg_read(ioc->ioc_regs.lpu_mbox_cmd);
+ if (r32)
+ bfa_reg_write(ioc->ioc_regs.lpu_mbox_cmd, 1);
+}
+
+
+static void
+bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force)
+{
+ enum bfi_ioc_state ioc_fwstate;
+ bfa_boolean_t fwvalid;
+
+ ioc_fwstate = bfa_reg_read(ioc->ioc_regs.ioc_fwstate);
+
+ if (force)
+ ioc_fwstate = BFI_IOC_UNINIT;
+
+
+ /**
+ * check if firmware is valid
+ */
+ fwvalid = (ioc_fwstate == BFI_IOC_UNINIT) ?
+ BFA_FALSE : bfa_ioc_fwver_valid(ioc);
+
+ if (!fwvalid) {
+ bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, ioc->pcidev.device_id);
+ return;
+ }
+
+ /**
+ * If hardware initialization is in progress (initialized by other IOC),
+ * just wait for an initialization completion interrupt.
+ */
+ if (ioc_fwstate == BFI_IOC_INITING) {
+ ioc->cbfn->reset_cbfn(ioc->bfa);
+ return;
+ }
+
+ /**
+ * If IOC function is disabled and firmware version is same,
+ * just re-enable IOC.
+ */
+ if (ioc_fwstate == BFI_IOC_DISABLED || ioc_fwstate == BFI_IOC_OP) {
+
+ /**
+ * When using MSI-X any pending firmware ready event should
+ * be flushed. Otherwise MSI-X interrupts are not delivered.
+ */
+ bfa_ioc_msgflush(ioc);
+ ioc->cbfn->reset_cbfn(ioc->bfa);
+ bfa_fsm_send_event(ioc, IOC_E_FWREADY);
+ return;
+ }
+
+ /**
+ * Initialize the h/w for any other states.
+ */
+ bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, ioc->pcidev.device_id);
+}
+
+static void
+bfa_ioc_timeout(void *ioc_arg)
+{
+ struct bfa_ioc_s *ioc = (struct bfa_ioc_s *)ioc_arg;
+
+ bfa_fsm_send_event(ioc, IOC_E_TIMEOUT);
+}
+
+void
+bfa_ioc_mbox_send(struct bfa_ioc_s *ioc, void *ioc_msg, int len)
+{
+ u32 *msgp = (u32 *) ioc_msg;
+ u32 i;
+
+
+ bfa_assert(len <= BFI_IOC_MSGLEN_MAX);
+
+ /*
+ * first write msg to mailbox registers
+ */
+ for (i = 0; i < len / sizeof(u32); i++)
+ bfa_reg_write(ioc->ioc_regs.hfn_mbox + i * sizeof(u32),
+ bfa_os_wtole(msgp[i]));
+
+ for (; i < BFI_IOC_MSGLEN_MAX / sizeof(u32); i++)
+ bfa_reg_write(ioc->ioc_regs.hfn_mbox + i * sizeof(u32), 0);
+
+ /*
+ * write 1 to mailbox CMD to trigger LPU event
+ */
+ bfa_reg_write(ioc->ioc_regs.hfn_mbox_cmd, 1);
+ (void)bfa_reg_read(ioc->ioc_regs.hfn_mbox_cmd);
+}
+
+static void
+bfa_ioc_send_enable(struct bfa_ioc_s *ioc)
+{
+ struct bfi_ioc_ctrl_req_s enable_req;
+
+ bfi_h2i_set(enable_req.mh, BFI_MC_IOC, BFI_IOC_H2I_ENABLE_REQ,
+ bfa_ioc_portid(ioc));
+ enable_req.ioc_class = ioc->ioc_mc;
+ bfa_ioc_mbox_send(ioc, &enable_req, sizeof(struct bfi_ioc_ctrl_req_s));
+}
+
+static void
+bfa_ioc_send_disable(struct bfa_ioc_s *ioc)
+{
+ struct bfi_ioc_ctrl_req_s disable_req;
+
+ bfi_h2i_set(disable_req.mh, BFI_MC_IOC, BFI_IOC_H2I_DISABLE_REQ,
+ bfa_ioc_portid(ioc));
+ bfa_ioc_mbox_send(ioc, &disable_req, sizeof(struct bfi_ioc_ctrl_req_s));
+}
+
+static void
+bfa_ioc_send_getattr(struct bfa_ioc_s *ioc)
+{
+ struct bfi_ioc_getattr_req_s attr_req;
+
+ bfi_h2i_set(attr_req.mh, BFI_MC_IOC, BFI_IOC_H2I_GETATTR_REQ,
+ bfa_ioc_portid(ioc));
+ bfa_dma_be_addr_set(attr_req.attr_addr, ioc->attr_dma.pa);
+ bfa_ioc_mbox_send(ioc, &attr_req, sizeof(attr_req));
+}
+
+static void
+bfa_ioc_hb_check(void *cbarg)
+{
+ struct bfa_ioc_s *ioc = cbarg;
+ u32 hb_count;
+
+ hb_count = bfa_reg_read(ioc->ioc_regs.heartbeat);
+ if (ioc->hb_count == hb_count) {
+ ioc->hb_fail++;
+ } else {
+ ioc->hb_count = hb_count;
+ ioc->hb_fail = 0;
+ }
+
+ if (ioc->hb_fail >= BFA_IOC_HB_FAIL_MAX) {
+ ioc->hb_fail = 0;
+ bfa_ioc_recover(ioc);
+ return;
+ }
+
+ bfa_ioc_mbox_poll(ioc);
+ bfa_timer_begin(ioc->timer_mod, &ioc->ioc_timer, bfa_ioc_hb_check, ioc,
+ BFA_IOC_HB_TOV);
+}
+
+static void
+bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc)
+{
+ ioc->hb_fail = 0;
+ ioc->hb_count = bfa_reg_read(ioc->ioc_regs.heartbeat);
+ bfa_timer_begin(ioc->timer_mod, &ioc->ioc_timer, bfa_ioc_hb_check, ioc,
+ BFA_IOC_HB_TOV);
+}
+
+static void
+bfa_ioc_hb_stop(struct bfa_ioc_s *ioc)
+{
+ bfa_timer_stop(&ioc->ioc_timer);
+}
+
+/**
+ * Host to LPU mailbox message addresses
+ */
+static struct {
+ u32 hfn_mbox, lpu_mbox, hfn_pgn;
+} iocreg_fnreg[] = {
+ {
+ HOSTFN0_LPU_MBOX0_0, LPU_HOSTFN0_MBOX0_0, HOST_PAGE_NUM_FN0}, {
+ HOSTFN1_LPU_MBOX0_8, LPU_HOSTFN1_MBOX0_8, HOST_PAGE_NUM_FN1}, {
+ HOSTFN2_LPU_MBOX0_0, LPU_HOSTFN2_MBOX0_0, HOST_PAGE_NUM_FN2}, {
+ HOSTFN3_LPU_MBOX0_8, LPU_HOSTFN3_MBOX0_8, HOST_PAGE_NUM_FN3}
+};
+
+/**
+ * Host <-> LPU mailbox command/status registers - port 0
+ */
+static struct {
+ u32 hfn, lpu;
+} iocreg_mbcmd_p0[] = {
+ {
+ HOSTFN0_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN0_MBOX0_CMD_STAT}, {
+ HOSTFN1_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN1_MBOX0_CMD_STAT}, {
+ HOSTFN2_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN2_MBOX0_CMD_STAT}, {
+ HOSTFN3_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN3_MBOX0_CMD_STAT}
+};
+
+/**
+ * Host <-> LPU mailbox command/status registers - port 1
+ */
+static struct {
+ u32 hfn, lpu;
+} iocreg_mbcmd_p1[] = {
+ {
+ HOSTFN0_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN0_MBOX0_CMD_STAT}, {
+ HOSTFN1_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN1_MBOX0_CMD_STAT}, {
+ HOSTFN2_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN2_MBOX0_CMD_STAT}, {
+ HOSTFN3_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN3_MBOX0_CMD_STAT}
+};
+
+/**
+ * Shared IRQ handling in INTX mode
+ */
+static struct {
+ u32 isr, msk;
+} iocreg_shirq_next[] = {
+ {
+ HOSTFN1_INT_STATUS, HOSTFN1_INT_MSK}, {
+ HOSTFN2_INT_STATUS, HOSTFN2_INT_MSK}, {
+ HOSTFN3_INT_STATUS, HOSTFN3_INT_MSK}, {
+HOSTFN0_INT_STATUS, HOSTFN0_INT_MSK},};
+
+static void
+bfa_ioc_reg_init(struct bfa_ioc_s *ioc)
+{
+ bfa_os_addr_t rb;
+ int pcifn = bfa_ioc_pcifn(ioc);
+
+ rb = bfa_ioc_bar0(ioc);
+
+ ioc->ioc_regs.hfn_mbox = rb + iocreg_fnreg[pcifn].hfn_mbox;
+ ioc->ioc_regs.lpu_mbox = rb + iocreg_fnreg[pcifn].lpu_mbox;
+ ioc->ioc_regs.host_page_num_fn = rb + iocreg_fnreg[pcifn].hfn_pgn;
+
+ if (ioc->port_id == 0) {
+ ioc->ioc_regs.heartbeat = rb + BFA_IOC0_HBEAT_REG;
+ ioc->ioc_regs.ioc_fwstate = rb + BFA_IOC0_STATE_REG;
+ ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].hfn;
+ ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].lpu;
+ ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P0;
+ } else {
+ ioc->ioc_regs.heartbeat = (rb + BFA_IOC1_HBEAT_REG);
+ ioc->ioc_regs.ioc_fwstate = (rb + BFA_IOC1_STATE_REG);
+ ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].hfn;
+ ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].lpu;
+ ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P1;
+ }
+
+ /**
+ * Shared IRQ handling in INTX mode
+ */
+ ioc->ioc_regs.shirq_isr_next = rb + iocreg_shirq_next[pcifn].isr;
+ ioc->ioc_regs.shirq_msk_next = rb + iocreg_shirq_next[pcifn].msk;
+
+ /*
+ * PSS control registers
+ */
+ ioc->ioc_regs.pss_ctl_reg = (rb + PSS_CTL_REG);
+ ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + APP_PLL_425_CTL_REG);
+ ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + APP_PLL_312_CTL_REG);
+
+ /*
+ * IOC semaphore registers and serialization
+ */
+ ioc->ioc_regs.ioc_sem_reg = (rb + HOST_SEM0_REG);
+ ioc->ioc_regs.ioc_usage_sem_reg = (rb + HOST_SEM1_REG);
+ ioc->ioc_regs.ioc_usage_reg = (rb + BFA_FW_USE_COUNT);
+
+ /**
+ * sram memory access
+ */
+ ioc->ioc_regs.smem_page_start = (rb + PSS_SMEM_PAGE_START);
+ ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CB;
+ if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT)
+ ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CT;
+}
+
+/**
+ * Initiate a full firmware download.
+ */
+static void
+bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type,
+ u32 boot_param)
+{
+ u32 *fwimg;
+ u32 pgnum, pgoff;
+ u32 loff = 0;
+ u32 chunkno = 0;
+ u32 i;
+
+ /**
+ * Initialize LMEM first before code download
+ */
+ bfa_ioc_lmem_init(ioc);
+
+ /**
+ * Flash based firmware boot
+ */
+ if (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ)
+ boot_type = BFI_BOOT_TYPE_FLASH;
+ fwimg = bfa_ioc_fwimg_get_chunk(ioc, chunkno);
+ fwimg[BFI_BOOT_TYPE_OFF / sizeof(u32)] = bfa_os_swap32(boot_type);
+ fwimg[BFI_BOOT_PARAM_OFF / sizeof(u32)] =
+ bfa_os_swap32(boot_param);
+
+ pgnum = bfa_ioc_smem_pgnum(ioc, loff);
+ pgoff = bfa_ioc_smem_pgoff(ioc, loff);
+
+ bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum);
+
+ for (i = 0; i < bfa_ioc_fwimg_get_size(ioc); i++) {
+
+ if (BFA_FLASH_CHUNK_NO(i) != chunkno) {
+ chunkno = BFA_FLASH_CHUNK_NO(i);
+ fwimg = bfa_ioc_fwimg_get_chunk(ioc,
+ BFA_FLASH_CHUNK_ADDR(chunkno));
+ }
+
+ /**
+ * write smem
+ */
+ bfa_mem_write(ioc->ioc_regs.smem_page_start, loff,
+ fwimg[BFA_FLASH_OFFSET_IN_CHUNK(i)]);
+
+ loff += sizeof(u32);
+
+ /**
+ * handle page offset wrap around
+ */
+ loff = PSS_SMEM_PGOFF(loff);
+ if (loff == 0) {
+ pgnum++;
+ bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum);
+ }
+ }
+
+ bfa_reg_write(ioc->ioc_regs.host_page_num_fn,
+ bfa_ioc_smem_pgnum(ioc, 0));
+}
+
+static void
+bfa_ioc_reset(struct bfa_ioc_s *ioc, bfa_boolean_t force)
+{
+ bfa_ioc_hwinit(ioc, force);
+}
+
+/**
+ * Update BFA configuration from firmware configuration.
+ */
+static void
+bfa_ioc_getattr_reply(struct bfa_ioc_s *ioc)
+{
+ struct bfi_ioc_attr_s *attr = ioc->attr;
+
+ attr->adapter_prop = bfa_os_ntohl(attr->adapter_prop);
+ attr->maxfrsize = bfa_os_ntohs(attr->maxfrsize);
+
+ bfa_fsm_send_event(ioc, IOC_E_FWRSP_GETATTR);
+}
+
+/**
+ * Attach time initialization of mbox logic.
+ */
+static void
+bfa_ioc_mbox_attach(struct bfa_ioc_s *ioc)
+{
+ struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod;
+ int mc;
+
+ INIT_LIST_HEAD(&mod->cmd_q);
+ for (mc = 0; mc < BFI_MC_MAX; mc++) {
+ mod->mbhdlr[mc].cbfn = NULL;
+ mod->mbhdlr[mc].cbarg = ioc->bfa;
+ }
+}
+
+/**
+ * Mbox poll timer -- restarts any pending mailbox requests.
+ */
+static void
+bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc)
+{
+ struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod;
+ struct bfa_mbox_cmd_s *cmd;
+ u32 stat;
+
+ /**
+ * If no command pending, do nothing
+ */
+ if (list_empty(&mod->cmd_q))
+ return;
+
+ /**
+ * If previous command is not yet fetched by firmware, do nothing
+ */
+ stat = bfa_reg_read(ioc->ioc_regs.hfn_mbox_cmd);
+ if (stat)
+ return;
+
+ /**
+ * Enqueue command to firmware.
+ */
+ bfa_q_deq(&mod->cmd_q, &cmd);
+ bfa_ioc_mbox_send(ioc, cmd->msg, sizeof(cmd->msg));
+}
+
+/**
+ * Cleanup any pending requests.
+ */
+static void
+bfa_ioc_mbox_hbfail(struct bfa_ioc_s *ioc)
+{
+ struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod;
+ struct bfa_mbox_cmd_s *cmd;
+
+ while (!list_empty(&mod->cmd_q))
+ bfa_q_deq(&mod->cmd_q, &cmd);
+}
+
+/**
+ * Initialize IOC to port mapping.
+ */
+
+#define FNC_PERS_FN_SHIFT(__fn) ((__fn) * 8)
+static void
+bfa_ioc_map_port(struct bfa_ioc_s *ioc)
+{
+ bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva;
+ u32 r32;
+
+ /**
+ * For crossbow, port id is same as pci function.
+ */
+ if (ioc->pcidev.device_id != BFA_PCI_DEVICE_ID_CT) {
+ ioc->port_id = bfa_ioc_pcifn(ioc);
+ return;
+ }
+
+ /**
+ * For catapult, base port id on personality register and IOC type
+ */
+ r32 = bfa_reg_read(rb + FNC_PERS_REG);
+ r32 >>= FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc));
+ ioc->port_id = (r32 & __F0_PORT_MAP_MK) >> __F0_PORT_MAP_SH;
+
+}
+
+
+
+/**
+ * bfa_ioc_public
+ */
+
+/**
+* Set interrupt mode for a function: INTX or MSIX
+ */
+void
+bfa_ioc_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix)
+{
+ bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva;
+ u32 r32, mode;
+
+ r32 = bfa_reg_read(rb + FNC_PERS_REG);
+
+ mode = (r32 >> FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc))) &
+ __F0_INTX_STATUS;
+
+ /**
+ * If already in desired mode, do not change anything
+ */
+ if (!msix && mode)
+ return;
+
+ if (msix)
+ mode = __F0_INTX_STATUS_MSIX;
+ else
+ mode = __F0_INTX_STATUS_INTA;
+
+ r32 &= ~(__F0_INTX_STATUS << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc)));
+ r32 |= (mode << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc)));
+
+ bfa_reg_write(rb + FNC_PERS_REG, r32);
+}
+
+bfa_status_t
+bfa_ioc_pll_init(struct bfa_ioc_s *ioc)
+{
+ bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva;
+ u32 pll_sclk, pll_fclk, r32;
+
+ if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) {
+ pll_sclk =
+ __APP_PLL_312_ENABLE | __APP_PLL_312_LRESETN |
+ __APP_PLL_312_RSEL200500 | __APP_PLL_312_P0_1(0U) |
+ __APP_PLL_312_JITLMT0_1(3U) |
+ __APP_PLL_312_CNTLMT0_1(1U);
+ pll_fclk =
+ __APP_PLL_425_ENABLE | __APP_PLL_425_LRESETN |
+ __APP_PLL_425_RSEL200500 | __APP_PLL_425_P0_1(0U) |
+ __APP_PLL_425_JITLMT0_1(3U) |
+ __APP_PLL_425_CNTLMT0_1(1U);
+
+ /**
+ * For catapult, choose operational mode FC/FCoE
+ */
+ if (ioc->fcmode) {
+ bfa_reg_write((rb + OP_MODE), 0);
+ bfa_reg_write((rb + ETH_MAC_SER_REG),
+ __APP_EMS_CMLCKSEL | __APP_EMS_REFCKBUFEN2
+ | __APP_EMS_CHANNEL_SEL);
+ } else {
+ ioc->pllinit = BFA_TRUE;
+ bfa_reg_write((rb + OP_MODE), __GLOBAL_FCOE_MODE);
+ bfa_reg_write((rb + ETH_MAC_SER_REG),
+ __APP_EMS_REFCKBUFEN1);
+ }
+ } else {
+ pll_sclk =
+ __APP_PLL_312_ENABLE | __APP_PLL_312_LRESETN |
+ __APP_PLL_312_P0_1(3U) | __APP_PLL_312_JITLMT0_1(3U) |
+ __APP_PLL_312_CNTLMT0_1(3U);
+ pll_fclk =
+ __APP_PLL_425_ENABLE | __APP_PLL_425_LRESETN |
+ __APP_PLL_425_RSEL200500 | __APP_PLL_425_P0_1(3U) |
+ __APP_PLL_425_JITLMT0_1(3U) |
+ __APP_PLL_425_CNTLMT0_1(3U);
+ }
+
+ bfa_reg_write((rb + BFA_IOC0_STATE_REG), BFI_IOC_UNINIT);
+ bfa_reg_write((rb + BFA_IOC1_STATE_REG), BFI_IOC_UNINIT);
+
+ bfa_reg_write((rb + HOSTFN0_INT_MSK), 0xffffffffU);
+ bfa_reg_write((rb + HOSTFN1_INT_MSK), 0xffffffffU);
+ bfa_reg_write((rb + HOSTFN0_INT_STATUS), 0xffffffffU);
+ bfa_reg_write((rb + HOSTFN1_INT_STATUS), 0xffffffffU);
+ bfa_reg_write((rb + HOSTFN0_INT_MSK), 0xffffffffU);
+ bfa_reg_write((rb + HOSTFN1_INT_MSK), 0xffffffffU);
+
+ bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg,
+ __APP_PLL_312_LOGIC_SOFT_RESET);
+ bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg,
+ __APP_PLL_312_BYPASS | __APP_PLL_312_LOGIC_SOFT_RESET);
+ bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg,
+ __APP_PLL_425_LOGIC_SOFT_RESET);
+ bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg,
+ __APP_PLL_425_BYPASS | __APP_PLL_425_LOGIC_SOFT_RESET);
+ bfa_os_udelay(2);
+ bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg,
+ __APP_PLL_312_LOGIC_SOFT_RESET);
+ bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg,
+ __APP_PLL_425_LOGIC_SOFT_RESET);
+
+ bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg,
+ pll_sclk | __APP_PLL_312_LOGIC_SOFT_RESET);
+ bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg,
+ pll_fclk | __APP_PLL_425_LOGIC_SOFT_RESET);
+
+ /**
+ * Wait for PLLs to lock.
+ */
+ bfa_os_udelay(2000);
+ bfa_reg_write((rb + HOSTFN0_INT_STATUS), 0xffffffffU);
+ bfa_reg_write((rb + HOSTFN1_INT_STATUS), 0xffffffffU);
+
+ bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, pll_sclk);
+ bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, pll_fclk);
+
+ if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) {
+ bfa_reg_write((rb + MBIST_CTL_REG), __EDRAM_BISTR_START);
+ bfa_os_udelay(1000);
+ r32 = bfa_reg_read((rb + MBIST_STAT_REG));
+ }
+
+ return BFA_STATUS_OK;
+}
+
+/**
+ * Interface used by diag module to do firmware boot with memory test
+ * as the entry vector.
+ */
+void
+bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_param)
+{
+ bfa_os_addr_t rb;
+
+ bfa_ioc_stats(ioc, ioc_boots);
+
+ if (bfa_ioc_pll_init(ioc) != BFA_STATUS_OK)
+ return;
+
+ /**
+ * Initialize IOC state of all functions on a chip reset.
+ */
+ rb = ioc->pcidev.pci_bar_kva;
+ if (boot_param == BFI_BOOT_TYPE_MEMTEST) {
+ bfa_reg_write((rb + BFA_IOC0_STATE_REG), BFI_IOC_MEMTEST);
+ bfa_reg_write((rb + BFA_IOC1_STATE_REG), BFI_IOC_MEMTEST);
+ } else {
+ bfa_reg_write((rb + BFA_IOC0_STATE_REG), BFI_IOC_INITING);
+ bfa_reg_write((rb + BFA_IOC1_STATE_REG), BFI_IOC_INITING);
+ }
+
+ bfa_ioc_download_fw(ioc, boot_type, boot_param);
+
+ /**
+ * Enable interrupts just before starting LPU
+ */
+ ioc->cbfn->reset_cbfn(ioc->bfa);
+ bfa_ioc_lpu_start(ioc);
+}
+
+/**
+ * Enable/disable IOC failure auto recovery.
+ */
+void
+bfa_ioc_auto_recover(bfa_boolean_t auto_recover)
+{
+ bfa_auto_recover = BFA_FALSE;
+}
+
+
+bfa_boolean_t
+bfa_ioc_is_operational(struct bfa_ioc_s *ioc)
+{
+ return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_op);
+}
+
+void
+bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg)
+{
+ u32 *msgp = mbmsg;
+ u32 r32;
+ int i;
+
+ /**
+ * read the MBOX msg
+ */
+ for (i = 0; i < (sizeof(union bfi_ioc_i2h_msg_u) / sizeof(u32));
+ i++) {
+ r32 = bfa_reg_read(ioc->ioc_regs.lpu_mbox +
+ i * sizeof(u32));
+ msgp[i] = bfa_os_htonl(r32);
+ }
+
+ /**
+ * turn off mailbox interrupt by clearing mailbox status
+ */
+ bfa_reg_write(ioc->ioc_regs.lpu_mbox_cmd, 1);
+ bfa_reg_read(ioc->ioc_regs.lpu_mbox_cmd);
+}
+
+void
+bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *m)
+{
+ union bfi_ioc_i2h_msg_u *msg;
+
+ msg = (union bfi_ioc_i2h_msg_u *)m;
+
+ bfa_ioc_stats(ioc, ioc_isrs);
+
+ switch (msg->mh.msg_id) {
+ case BFI_IOC_I2H_HBEAT:
+ break;
+
+ case BFI_IOC_I2H_READY_EVENT:
+ bfa_fsm_send_event(ioc, IOC_E_FWREADY);
+ break;
+
+ case BFI_IOC_I2H_ENABLE_REPLY:
+ bfa_fsm_send_event(ioc, IOC_E_FWRSP_ENABLE);
+ break;
+
+ case BFI_IOC_I2H_DISABLE_REPLY:
+ bfa_fsm_send_event(ioc, IOC_E_FWRSP_DISABLE);
+ break;
+
+ case BFI_IOC_I2H_GETATTR_REPLY:
+ bfa_ioc_getattr_reply(ioc);
+ break;
+
+ default:
+ bfa_assert(0);
+ }
+}
+
+/**
+ * IOC attach time initialization and setup.
+ *
+ * @param[in] ioc memory for IOC
+ * @param[in] bfa driver instance structure
+ * @param[in] trcmod kernel trace module
+ * @param[in] aen kernel aen event module
+ * @param[in] logm kernel logging module
+ */
+void
+bfa_ioc_attach(struct bfa_ioc_s *ioc, void *bfa, struct bfa_ioc_cbfn_s *cbfn,
+ struct bfa_timer_mod_s *timer_mod, struct bfa_trc_mod_s *trcmod,
+ struct bfa_aen_s *aen, struct bfa_log_mod_s *logm)
+{
+ ioc->bfa = bfa;
+ ioc->cbfn = cbfn;
+ ioc->timer_mod = timer_mod;
+ ioc->trcmod = trcmod;
+ ioc->aen = aen;
+ ioc->logm = logm;
+ ioc->fcmode = BFA_FALSE;
+ ioc->pllinit = BFA_FALSE;
+ ioc->dbg_fwsave_once = BFA_TRUE;
+
+ bfa_ioc_mbox_attach(ioc);
+ INIT_LIST_HEAD(&ioc->hb_notify_q);
+
+ bfa_fsm_set_state(ioc, bfa_ioc_sm_reset);
+}
+
+/**
+ * Driver detach time IOC cleanup.
+ */
+void
+bfa_ioc_detach(struct bfa_ioc_s *ioc)
+{
+ bfa_fsm_send_event(ioc, IOC_E_DETACH);
+}
+
+/**
+ * Setup IOC PCI properties.
+ *
+ * @param[in] pcidev PCI device information for this IOC
+ */
+void
+bfa_ioc_pci_init(struct bfa_ioc_s *ioc, struct bfa_pcidev_s *pcidev,
+ enum bfi_mclass mc)
+{
+ ioc->ioc_mc = mc;
+ ioc->pcidev = *pcidev;
+ ioc->ctdev = (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT);
+ ioc->cna = ioc->ctdev && !ioc->fcmode;
+
+ bfa_ioc_map_port(ioc);
+ bfa_ioc_reg_init(ioc);
+}
+
+/**
+ * Initialize IOC dma memory
+ *
+ * @param[in] dm_kva kernel virtual address of IOC dma memory
+ * @param[in] dm_pa physical address of IOC dma memory
+ */
+void
+bfa_ioc_mem_claim(struct bfa_ioc_s *ioc, u8 *dm_kva, u64 dm_pa)
+{
+ /**
+ * dma memory for firmware attribute
+ */
+ ioc->attr_dma.kva = dm_kva;
+ ioc->attr_dma.pa = dm_pa;
+ ioc->attr = (struct bfi_ioc_attr_s *)dm_kva;
+}
+
+/**
+ * Return size of dma memory required.
+ */
+u32
+bfa_ioc_meminfo(void)
+{
+ return BFA_ROUNDUP(sizeof(struct bfi_ioc_attr_s), BFA_DMA_ALIGN_SZ);
+}
+
+void
+bfa_ioc_enable(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_stats(ioc, ioc_enables);
+ ioc->dbg_fwsave_once = BFA_TRUE;
+
+ bfa_fsm_send_event(ioc, IOC_E_ENABLE);
+}
+
+void
+bfa_ioc_disable(struct bfa_ioc_s *ioc)
+{
+ bfa_ioc_stats(ioc, ioc_disables);
+ bfa_fsm_send_event(ioc, IOC_E_DISABLE);
+}
+
+/**
+ * Returns memory required for saving firmware trace in case of crash.
+ * Driver must call this interface to allocate memory required for
+ * automatic saving of firmware trace. Driver should call
+ * bfa_ioc_debug_memclaim() right after bfa_ioc_attach() to setup this
+ * trace memory.
+ */
+int
+bfa_ioc_debug_trcsz(bfa_boolean_t auto_recover)
+{
+return (auto_recover) ? BFA_DBG_FWTRC_LEN : 0;
+}
+
+/**
+ * Initialize memory for saving firmware trace. Driver must initialize
+ * trace memory before call bfa_ioc_enable().
+ */
+void
+bfa_ioc_debug_memclaim(struct bfa_ioc_s *ioc, void *dbg_fwsave)
+{
+ bfa_assert(ioc->auto_recover);
+ ioc->dbg_fwsave = dbg_fwsave;
+ ioc->dbg_fwsave_len = bfa_ioc_debug_trcsz(ioc->auto_recover);
+}
+
+u32
+bfa_ioc_smem_pgnum(struct bfa_ioc_s *ioc, u32 fmaddr)
+{
+ return PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, fmaddr);
+}
+
+u32
+bfa_ioc_smem_pgoff(struct bfa_ioc_s *ioc, u32 fmaddr)
+{
+ return PSS_SMEM_PGOFF(fmaddr);
+}
+
+/**
+ * Register mailbox message handler functions
+ *
+ * @param[in] ioc IOC instance
+ * @param[in] mcfuncs message class handler functions
+ */
+void
+bfa_ioc_mbox_register(struct bfa_ioc_s *ioc, bfa_ioc_mbox_mcfunc_t *mcfuncs)
+{
+ struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod;
+ int mc;
+
+ for (mc = 0; mc < BFI_MC_MAX; mc++)
+ mod->mbhdlr[mc].cbfn = mcfuncs[mc];
+}
+
+/**
+ * Register mailbox message handler function, to be called by common modules
+ */
+void
+bfa_ioc_mbox_regisr(struct bfa_ioc_s *ioc, enum bfi_mclass mc,
+ bfa_ioc_mbox_mcfunc_t cbfn, void *cbarg)
+{
+ struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod;
+
+ mod->mbhdlr[mc].cbfn = cbfn;
+ mod->mbhdlr[mc].cbarg = cbarg;
+}
+
+/**
+ * Queue a mailbox command request to firmware. Waits if mailbox is busy.
+ * Responsibility of caller to serialize
+ *
+ * @param[in] ioc IOC instance
+ * @param[i] cmd Mailbox command
+ */
+void
+bfa_ioc_mbox_queue(struct bfa_ioc_s *ioc, struct bfa_mbox_cmd_s *cmd)
+{
+ struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod;
+ u32 stat;
+
+ /**
+ * If a previous command is pending, queue new command
+ */
+ if (!list_empty(&mod->cmd_q)) {
+ list_add_tail(&cmd->qe, &mod->cmd_q);
+ return;
+ }
+
+ /**
+ * If mailbox is busy, queue command for poll timer
+ */
+ stat = bfa_reg_read(ioc->ioc_regs.hfn_mbox_cmd);
+ if (stat) {
+ list_add_tail(&cmd->qe, &mod->cmd_q);
+ return;
+ }
+
+ /**
+ * mailbox is free -- queue command to firmware
+ */
+ bfa_ioc_mbox_send(ioc, cmd->msg, sizeof(cmd->msg));
+}
+
+/**
+ * Handle mailbox interrupts
+ */
+void
+bfa_ioc_mbox_isr(struct bfa_ioc_s *ioc)
+{
+ struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod;
+ struct bfi_mbmsg_s m;
+ int mc;
+
+ bfa_ioc_msgget(ioc, &m);
+
+ /**
+ * Treat IOC message class as special.
+ */
+ mc = m.mh.msg_class;
+ if (mc == BFI_MC_IOC) {
+ bfa_ioc_isr(ioc, &m);
+ return;
+ }
+
+ if ((mc > BFI_MC_MAX) || (mod->mbhdlr[mc].cbfn == NULL))
+ return;
+
+ mod->mbhdlr[mc].cbfn(mod->mbhdlr[mc].cbarg, &m);
+}
+
+void
+bfa_ioc_error_isr(struct bfa_ioc_s *ioc)
+{
+ bfa_fsm_send_event(ioc, IOC_E_HWERROR);
+}
+
+#ifndef BFA_BIOS_BUILD
+
+/**
+ * return true if IOC is disabled
+ */
+bfa_boolean_t
+bfa_ioc_is_disabled(struct bfa_ioc_s *ioc)
+{
+ return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling)
+ || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled);
+}
+
+/**
+ * return true if IOC firmware is different.
+ */
+bfa_boolean_t
+bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc)
+{
+ return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset)
+ || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_fwcheck)
+ || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch);
+}
+
+#define bfa_ioc_state_disabled(__sm) \
+ (((__sm) == BFI_IOC_UNINIT) || \
+ ((__sm) == BFI_IOC_INITING) || \
+ ((__sm) == BFI_IOC_HWINIT) || \
+ ((__sm) == BFI_IOC_DISABLED) || \
+ ((__sm) == BFI_IOC_HBFAIL) || \
+ ((__sm) == BFI_IOC_CFG_DISABLED))
+
+/**
+ * Check if adapter is disabled -- both IOCs should be in a disabled
+ * state.
+ */
+bfa_boolean_t
+bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc)
+{
+ u32 ioc_state;
+ bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva;
+
+ if (!bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled))
+ return BFA_FALSE;
+
+ ioc_state = bfa_reg_read(rb + BFA_IOC0_STATE_REG);
+ if (!bfa_ioc_state_disabled(ioc_state))
+ return BFA_FALSE;
+
+ ioc_state = bfa_reg_read(rb + BFA_IOC1_STATE_REG);
+ if (!bfa_ioc_state_disabled(ioc_state))
+ return BFA_FALSE;
+
+ return BFA_TRUE;
+}
+
+/**
+ * Add to IOC heartbeat failure notification queue. To be used by common
+ * modules such as
+ */
+void
+bfa_ioc_hbfail_register(struct bfa_ioc_s *ioc,
+ struct bfa_ioc_hbfail_notify_s *notify)
+{
+ list_add_tail(¬ify->qe, &ioc->hb_notify_q);
+}
+
+#define BFA_MFG_NAME "Brocade"
+void
+bfa_ioc_get_adapter_attr(struct bfa_ioc_s *ioc,
+ struct bfa_adapter_attr_s *ad_attr)
+{
+ struct bfi_ioc_attr_s *ioc_attr;
+ char model[BFA_ADAPTER_MODEL_NAME_LEN];
+
+ ioc_attr = ioc->attr;
+ bfa_os_memcpy((void *)&ad_attr->serial_num,
+ (void *)ioc_attr->brcd_serialnum,
+ BFA_ADAPTER_SERIAL_NUM_LEN);
+
+ bfa_os_memcpy(&ad_attr->fw_ver, ioc_attr->fw_version, BFA_VERSION_LEN);
+ bfa_os_memcpy(&ad_attr->optrom_ver, ioc_attr->optrom_version,
+ BFA_VERSION_LEN);
+ bfa_os_memcpy(&ad_attr->manufacturer, BFA_MFG_NAME,
+ BFA_ADAPTER_MFG_NAME_LEN);
+ bfa_os_memcpy(&ad_attr->vpd, &ioc_attr->vpd,
+ sizeof(struct bfa_mfg_vpd_s));
+
+ ad_attr->nports = BFI_ADAPTER_GETP(NPORTS, ioc_attr->adapter_prop);
+ ad_attr->max_speed = BFI_ADAPTER_GETP(SPEED, ioc_attr->adapter_prop);
+
+ /**
+ * model name
+ */
+ if (BFI_ADAPTER_GETP(SPEED, ioc_attr->adapter_prop) == 10) {
+ strcpy(model, "BR-10?0");
+ model[5] = '0' + ad_attr->nports;
+ } else {
+ strcpy(model, "Brocade-??5");
+ model[8] =
+ '0' + BFI_ADAPTER_GETP(SPEED, ioc_attr->adapter_prop);
+ model[9] = '0' + ad_attr->nports;
+ }
+
+ if (BFI_ADAPTER_IS_SPECIAL(ioc_attr->adapter_prop))
+ ad_attr->prototype = 1;
+ else
+ ad_attr->prototype = 0;
+
+ bfa_os_memcpy(&ad_attr->model, model, BFA_ADAPTER_MODEL_NAME_LEN);
+ bfa_os_memcpy(&ad_attr->model_descr, &ad_attr->model,
+ BFA_ADAPTER_MODEL_NAME_LEN);
+
+ ad_attr->pwwn = bfa_ioc_get_pwwn(ioc);
+ ad_attr->mac = bfa_ioc_get_mac(ioc);
+
+ ad_attr->pcie_gen = ioc_attr->pcie_gen;
+ ad_attr->pcie_lanes = ioc_attr->pcie_lanes;
+ ad_attr->pcie_lanes_orig = ioc_attr->pcie_lanes_orig;
+ ad_attr->asic_rev = ioc_attr->asic_rev;
+ ad_attr->hw_ver[0] = 'R';
+ ad_attr->hw_ver[1] = 'e';
+ ad_attr->hw_ver[2] = 'v';
+ ad_attr->hw_ver[3] = '-';
+ ad_attr->hw_ver[4] = ioc_attr->asic_rev;
+ ad_attr->hw_ver[5] = '\0';
+
+ ad_attr->cna_capable = ioc->cna;
+}
+
+void
+bfa_ioc_get_attr(struct bfa_ioc_s *ioc, struct bfa_ioc_attr_s *ioc_attr)
+{
+ bfa_os_memset((void *)ioc_attr, 0, sizeof(struct bfa_ioc_attr_s));
+
+ ioc_attr->state = bfa_sm_to_state(ioc_sm_table, ioc->fsm);
+ ioc_attr->port_id = ioc->port_id;
+
+ if (!ioc->ctdev)
+ ioc_attr->ioc_type = BFA_IOC_TYPE_FC;
+ else if (ioc->ioc_mc == BFI_MC_IOCFC)
+ ioc_attr->ioc_type = BFA_IOC_TYPE_FCoE;
+ else if (ioc->ioc_mc == BFI_MC_LL)
+ ioc_attr->ioc_type = BFA_IOC_TYPE_LL;
+
+ bfa_ioc_get_adapter_attr(ioc, &ioc_attr->adapter_attr);
+
+ ioc_attr->pci_attr.device_id = ioc->pcidev.device_id;
+ ioc_attr->pci_attr.pcifn = ioc->pcidev.pci_func;
+ ioc_attr->pci_attr.chip_rev[0] = 'R';
+ ioc_attr->pci_attr.chip_rev[1] = 'e';
+ ioc_attr->pci_attr.chip_rev[2] = 'v';
+ ioc_attr->pci_attr.chip_rev[3] = '-';
+ ioc_attr->pci_attr.chip_rev[4] = ioc_attr->adapter_attr.asic_rev;
+ ioc_attr->pci_attr.chip_rev[5] = '\0';
+}
+
+/**
+ * hal_wwn_public
+ */
+wwn_t
+bfa_ioc_get_pwwn(struct bfa_ioc_s *ioc)
+{
+ union {
+ wwn_t wwn;
+ u8 byte[sizeof(wwn_t)];
+ }
+ w;
+
+ w.wwn = ioc->attr->mfg_wwn;
+
+ if (bfa_ioc_portid(ioc) == 1)
+ w.byte[7]++;
+
+ return w.wwn;
+}
+
+wwn_t
+bfa_ioc_get_nwwn(struct bfa_ioc_s *ioc)
+{
+ union {
+ wwn_t wwn;
+ u8 byte[sizeof(wwn_t)];
+ }
+ w;
+
+ w.wwn = ioc->attr->mfg_wwn;
+
+ if (bfa_ioc_portid(ioc) == 1)
+ w.byte[7]++;
+
+ w.byte[0] = 0x20;
+
+ return w.wwn;
+}
+
+wwn_t
+bfa_ioc_get_wwn_naa5(struct bfa_ioc_s *ioc, u16 inst)
+{
+ union {
+ wwn_t wwn;
+ u8 byte[sizeof(wwn_t)];
+ }
+ w , w5;
+
+
+ w.wwn = ioc->attr->mfg_wwn;
+ w5.byte[0] = 0x50 | w.byte[2] >> 4;
+ w5.byte[1] = w.byte[2] << 4 | w.byte[3] >> 4;
+ w5.byte[2] = w.byte[3] << 4 | w.byte[4] >> 4;
+ w5.byte[3] = w.byte[4] << 4 | w.byte[5] >> 4;
+ w5.byte[4] = w.byte[5] << 4 | w.byte[6] >> 4;
+ w5.byte[5] = w.byte[6] << 4 | w.byte[7] >> 4;
+ w5.byte[6] = w.byte[7] << 4 | (inst & 0x0f00) >> 8;
+ w5.byte[7] = (inst & 0xff);
+
+ return w5.wwn;
+}
+
+u64
+bfa_ioc_get_adid(struct bfa_ioc_s *ioc)
+{
+ return ioc->attr->mfg_wwn;
+}
+
+mac_t
+bfa_ioc_get_mac(struct bfa_ioc_s *ioc)
+{
+ mac_t mac;
+
+ mac = ioc->attr->mfg_mac;
+ mac.mac[MAC_ADDRLEN - 1] += bfa_ioc_pcifn(ioc);
+
+ return mac;
+}
+
+void
+bfa_ioc_set_fcmode(struct bfa_ioc_s *ioc)
+{
+ ioc->fcmode = BFA_TRUE;
+ ioc->port_id = bfa_ioc_pcifn(ioc);
+}
+
+bfa_boolean_t
+bfa_ioc_get_fcmode(struct bfa_ioc_s *ioc)
+{
+ return ioc->fcmode || (ioc->pcidev.device_id != BFA_PCI_DEVICE_ID_CT);
+}
+
+/**
+ * Return true if interrupt should be claimed.
+ */
+bfa_boolean_t
+bfa_ioc_intx_claim(struct bfa_ioc_s *ioc)
+{
+ u32 isr, msk;
+
+ /**
+ * Always claim if not catapult.
+ */
+ if (!ioc->ctdev)
+ return BFA_TRUE;
+
+ /**
+ * FALSE if next device is claiming interrupt.
+ * TRUE if next device is not interrupting or not present.
+ */
+ msk = bfa_reg_read(ioc->ioc_regs.shirq_msk_next);
+ isr = bfa_reg_read(ioc->ioc_regs.shirq_isr_next);
+ return !(isr & ~msk);
+}
+
+/**
+ * Retrieve saved firmware trace from a prior IOC failure.
+ */
+bfa_status_t
+bfa_ioc_debug_fwsave(struct bfa_ioc_s *ioc, void *trcdata, int *trclen)
+{
+ int tlen;
+
+ if (ioc->dbg_fwsave_len == 0)
+ return BFA_STATUS_ENOFSAVE;
+
+ tlen = *trclen;
+ if (tlen > ioc->dbg_fwsave_len)
+ tlen = ioc->dbg_fwsave_len;
+
+ bfa_os_memcpy(trcdata, ioc->dbg_fwsave, tlen);
+ *trclen = tlen;
+ return BFA_STATUS_OK;
+}
+
+/**
+ * Retrieve saved firmware trace from a prior IOC failure.
+ */
+bfa_status_t
+bfa_ioc_debug_fwtrc(struct bfa_ioc_s *ioc, void *trcdata, int *trclen)
+{
+ u32 pgnum;
+ u32 loff = BFA_DBG_FWTRC_OFF(bfa_ioc_portid(ioc));
+ int i, tlen;
+ u32 *tbuf = trcdata, r32;
+
+
+ pgnum = bfa_ioc_smem_pgnum(ioc, loff);
+ loff = bfa_ioc_smem_pgoff(ioc, loff);
+ bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum);
+
+ tlen = *trclen;
+ if (tlen > BFA_DBG_FWTRC_LEN)
+ tlen = BFA_DBG_FWTRC_LEN;
+ tlen /= sizeof(u32);
+
+
+ for (i = 0; i < tlen; i++) {
+ r32 = bfa_mem_read(ioc->ioc_regs.smem_page_start, loff);
+ tbuf[i] = bfa_os_ntohl(r32);
+ loff += sizeof(u32);
+
+ /**
+ * handle page offset wrap around
+ */
+ loff = PSS_SMEM_PGOFF(loff);
+ if (loff == 0) {
+ pgnum++;
+ bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum);
+ }
+ }
+ bfa_reg_write(ioc->ioc_regs.host_page_num_fn,
+ bfa_ioc_smem_pgnum(ioc, 0));
+
+ *trclen = tlen * sizeof(u32);
+ return BFA_STATUS_OK;
+}
+
+/**
+ * Save firmware trace if configured.
+ */
+static void
+bfa_ioc_debug_save(struct bfa_ioc_s *ioc)
+{
+ int tlen;
+
+ if (ioc->dbg_fwsave_len) {
+ tlen = ioc->dbg_fwsave_len;
+ bfa_ioc_debug_fwtrc(ioc, ioc->dbg_fwsave, &tlen);
+ }
+}
+
+/**
+ * Firmware failure detected. Start recovery actions.
+ */
+static void
+bfa_ioc_recover(struct bfa_ioc_s *ioc)
+{
+ if (ioc->dbg_fwsave_once) {
+ ioc->dbg_fwsave_once = BFA_FALSE;
+ bfa_ioc_debug_save(ioc);
+ }
+
+ bfa_ioc_stats(ioc, ioc_hbfails);
+ bfa_fsm_send_event(ioc, IOC_E_HBFAIL);
+}
+
+#else
+
+static void
+bfa_ioc_recover(struct bfa_ioc_s *ioc)
+{
+ bfa_assert(0);
+}
+
+#endif
+
+
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bfa_sm.c linux-2.6.32-rc4-mod/drivers/net/bna/bfa_sm.c
--- linux-2.6.32-rc4-orig/drivers/net/bna/bfa_sm.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bfa_sm.c 2009-10-16 10:30:53.456445000 -0700
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * See LICENSE.bna for copyright and licensing details.
+ */
+
+/**
+ * bfasm.c BFA State machine utility functions
+ */
+
+#include <cs/bfa_sm.h>
+
+/**
+ * cs_sm_api
+ */
+
+int
+bfa_sm_to_state(struct bfa_sm_table_s *smt, bfa_sm_t sm)
+{
+ int i = 0;
+
+ while (smt[i].sm && smt[i].sm != sm)
+ i++;
+ return smt[i].state;
+}
+
+
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bnad_compat.h linux-2.6.32-rc4-mod/drivers/net/bna/bnad_compat.h
--- linux-2.6.32-rc4-orig/drivers/net/bna/bnad_compat.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bnad_compat.h 2009-10-16 10:30:53.536442000 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _BNAD_COMPAT_H_
+#define _BNAD_COMPAT_H_
+
+#include <linux/version.h>
+
+#ifndef SET_MODULE_OWNER
+#define SET_MODULE_OWNER(netdev) do { } while (0)
+#endif
+
+#ifndef SET_NETDEV_DEV
+#define SET_NETDEV_DEV(netdev, dev) do { } while (0)
+#endif
+
+#ifndef module_param
+#define module_param(name, type, perm) MODULE_PARM(name, "i");
+#endif
+
+#ifndef NET_IP_ALIGN
+#define NET_IP_ALIGN 2
+#endif
+
+#ifndef NETDEV_TX_OK
+#define NETDEV_TX_OK 0 /* driver took care of the packet */
+#endif
+
+#ifndef NETDEV_TX_BUSY
+#define NETDEV_TX_BUSY 1 /* driver tx path was busy */
+#endif
+
+#ifndef NETDEV_TX_LOCKED
+#define NETDEV_TX_LOCKED -1 /* driver tx lock was already taken */
+#endif
+
+#ifndef ALIGN
+#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#endif
+
+#ifndef ETH_FCS_LEN
+#define ETH_FCS_LEN 4
+#endif
+
+#ifndef CHECKSUM_PARTIAL
+#define CHECKSUM_PARTIAL CHECKSUM_HW
+#endif
+
+#ifndef IRQF_SHARED
+#define IRQF_SHARED SA_SHIRQ
+#endif
+
+#ifndef SKB_DATAREF_SHIFT
+#define skb_header_cloned(_skb) 0
+#endif
+
+#ifndef VERIFY_WRITE
+#define VERIFY_WRITE 1
+#endif
+
+#ifndef VERIFY_READ
+#define VERIFY_READ 0
+#endif
+
+#ifndef dev_err
+#define dev_err(dev, format, arg...) \
+ printk(KERN_ERR "bna: " format , ## arg)
+#endif
+#ifndef dev_info
+#define dev_info(dev, format, arg...) \
+ printk(KERN_INFO "bna: " format , ## arg)
+#endif
+#ifndef dev_warn
+#define dev_warn(dev, format, arg...) \
+ printk(KERN_WARNING "bna: " format , ## arg)
+#endif
+
+#ifndef NETIF_F_GSO
+#define gso_size tso_size
+#endif
+
+/* PCI error recovery support, available in 2.6.16 and later. */
+#define HAVE_PCI_ERROR_RECOVERY
+
+#define BNAD_VLAN_FEATURES
+
+#define netif_rx_schedule_prep(_netdev, _napi) napi_schedule_prep(_napi)
+#define __netif_rx_schedule(_netdev, _napi) __napi_schedule(_napi)
+#define netif_rx_complete(_netdev, _napi) napi_complete(_napi)
+
+
+
+#endif /* _BNAD_COMPAT_H_ */
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bnad_defs.h linux-2.6.32-rc4-mod/drivers/net/bna/bnad_defs.h
--- linux-2.6.32-rc4-orig/drivers/net/bna/bnad_defs.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bnad_defs.h 2009-10-16 10:30:53.551449000 -0700
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _BNAD_DEFS_H_
+#define _BNAD_DEFS_H_
+
+#define BNAD_NAME "bna"
+#define BNAD_VERSION "2.0.0.0"
+
+#ifndef PCI_VENDOR_ID_BROCADE
+#define PCI_VENDOR_ID_BROCADE 0x1657
+#endif
+
+#ifndef PCI_DEVICE_ID_BROCADE_CATAPULT
+#define PCI_DEVICE_ID_BROCADE_CATAPULT 0x0014
+#endif
+
+#endif /* _BNAD_DEFS_H_ */
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bna_if.c linux-2.6.32-rc4-mod/drivers/net/bna/bna_if.c
--- linux-2.6.32-rc4-orig/drivers/net/bna/bna_if.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bna_if.c 2009-10-16 10:30:53.472448000 -0700
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2007-2008 Brocade Communications Systems, Inc.
+ * All rights reserved.
+ *
+ * file bna_if.c BNA Hardware and Firmware Interface
+ */
+#include <bna_os.h>
+#include "bna.h"
+#include "bna_hwreg.h"
+#include "bna_priv.h"
+#include "bna_iocll.h"
+#include "bna_intr.h"
+#include <bfi/bfi_cee.h>
+#include <protocol/types.h>
+#include <cee/bfa_cee.h>
+
+
+
+
+#define BNA_FLASH_DMA_BUF_SZ 0x010000 /* 64K */
+
+#define DO_CALLBACK(cbfn) \
+do { \
+ if (mb_cbfns->cbfn) { \
+ if (qe && qe->cbarg) { \
+ (mb_cbfns->cbfn)(qe->cbarg, status); \
+ } else { \
+ (mb_cbfns->cbfn)(dev->cbarg, status); \
+ } \
+ } \
+} while (0)
+
+#define DO_DIAG_CALLBACK(cbfn, data) \
+do { \
+ if (mb_cbfns->cbfn) { \
+ if (qe && qe->cbarg) { \
+ (mb_cbfns->cbfn)(qe->cbarg, data, status); \
+ } else { \
+ (mb_cbfns->cbfn)(dev->cbarg, data, status); \
+ } \
+ } \
+} while (0)
+
+#define bna_mbox_q_is_empty(mb_q) \
+ ((mb_q)->producer_index == \
+ (mb_q)->consumer_index)
+
+#define bna_mbox_q_first(mb_q) \
+ (&(mb_q)->mb_qe[(mb_q)->consumer_index])
+
+/* FIXME : Should come from the driver */
+static u32 bna_ioc_auto_recover;
+
+/**
+ * bna_register_callback()
+ *
+ * Function called by the driver to register a callback
+ * with the BNA
+ *
+ * @param[in] bna_dev - Opaque handle to BNA private device
+ * @param[in] cbfns - Structure for the callback functions.
+ * @param[in] cbarg - Argument to use with the callback
+ * @param[in] cmd_code - Command code exported to the drivers
+ *
+ * @return void
+ */
+void bna_register_callback(struct bna_dev_s *dev, struct bna_mbox_cbfn *cbfns,
+ void *cbarg)
+{
+ bna_os_memcpy(&dev->mb_cbfns, cbfns, sizeof(dev->mb_cbfns));
+ dev->cbarg = cbarg;
+}
+
+void
+bna_mbox_q_init(struct bna_mbox_q *q)
+{
+ BNA_ASSERT(BNA_POWER_OF_2(BNA_MAX_MBOX_CMD_QUEUE));
+
+ q->producer_index = q->consumer_index = 0;
+ q->posted = NULL;
+}
+
+static struct bna_mbox_cmd_qe *
+bna_mbox_enq(struct bna_mbox_q *mbox_q,
+ void *cmd, u32 cmd_len, void *cbarg) {
+ struct bna_mbox_cmd_qe *qe;
+
+ if (!BNA_QE_FREE_CNT(mbox_q, BNA_MAX_MBOX_CMD_QUEUE)) {
+ DPRINTK(WARNING, "No free Mbox Command Element\n");
+ return NULL;
+ }
+
+ DPRINTK(DEBUG, "Mbox PI 0x%x\n", mbox_q->producer_index);
+
+ qe = &mbox_q->mb_qe[mbox_q->producer_index];
+ BNA_QE_INDX_ADD(mbox_q->producer_index, 1,
+ BNA_MAX_MBOX_CMD_QUEUE);
+ bna_os_memcpy(&qe->cmd.msg[0], cmd, cmd_len);
+ qe->cmd_len = cmd_len;
+ qe->cbarg = cbarg;
+
+ return qe;
+}
+
+static void
+bna_mbox_deq(struct bna_mbox_q *mbox_q)
+{
+
+ DPRINTK(DEBUG, "Mbox CI 0x%x\n", mbox_q->consumer_index);
+
+ /* Free one from the head */
+ BNA_QE_INDX_ADD(mbox_q->consumer_index, 1,
+ BNA_MAX_MBOX_CMD_QUEUE);
+}
+
+static void
+bna_do_stats_update(struct bna_dev_s *dev, u8 status)
+{
+ if (status != BFI_LL_CMD_OK)
+ return;
+ bna_stats_process(dev);
+}
+
+static void
+bna_do_drv_ll_cb(struct bna_dev_s *dev, u8 cmd_code,
+ u8 status, struct bna_mbox_cmd_qe *qe)
+{
+ struct bna_mbox_cbfn *mb_cbfns = &dev->mb_cbfns;
+
+ switch (cmd_code) {
+ case BFI_LL_I2H_MAC_UCAST_SET_RSP:
+ DO_CALLBACK(ucast_set_cb);
+ break;
+ case BFI_LL_I2H_MAC_UCAST_ADD_RSP:
+ DO_CALLBACK(ucast_add_cb);
+ break;
+ case BFI_LL_I2H_MAC_UCAST_DEL_RSP:
+ DO_CALLBACK(ucast_del_cb);
+ break;
+ case BFI_LL_I2H_MAC_MCAST_ADD_RSP:
+ DO_CALLBACK(mcast_add_cb);
+ break;
+ case BFI_LL_I2H_MAC_MCAST_DEL_RSP:
+ DO_CALLBACK(mcast_del_cb);
+ break;
+ case BFI_LL_I2H_MAC_MCAST_FILTER_RSP:
+ DO_CALLBACK(mcast_filter_cb);
+ break;
+ case BFI_LL_I2H_MAC_MCAST_DEL_ALL_RSP:
+ DO_CALLBACK(mcast_del_all_cb);
+ break;
+ case BFI_LL_I2H_RXF_PROMISCUOUS_SET_RSP:
+ DO_CALLBACK(set_promisc_cb);
+ break;
+ case BFI_LL_I2H_RXF_DEFAULT_SET_RSP:
+ DO_CALLBACK(set_default_cb);
+ break;
+ case BFI_LL_I2H_TXQ_STOP_RSP:
+ DO_CALLBACK(txq_stop_cb);
+ break;
+ case BFI_LL_I2H_RXQ_STOP_RSP:
+ DO_CALLBACK(rxq_stop_cb);
+ break;
+ case BFI_LL_I2H_PORT_ADMIN_RSP:
+ DO_CALLBACK(port_admin_cb);
+ break;
+ case BFI_LL_I2H_STATS_GET_RSP:
+ bna_do_stats_update(dev, status);
+ DO_CALLBACK(stats_get_cb);
+ break;
+ case BFI_LL_I2H_STATS_CLEAR_RSP:
+ DO_CALLBACK(stats_clr_cb);
+ break;
+ case BFI_LL_I2H_LINK_DOWN_AEN:
+ DO_CALLBACK(link_down_cb);
+ break;
+ case BFI_LL_I2H_LINK_UP_AEN:
+ DO_CALLBACK(link_up_cb);
+ break;
+ case BFI_LL_I2H_DIAG_LOOPBACK_RSP:
+ DO_CALLBACK(set_diag_lb_cb);
+ break;
+ case BFI_LL_I2H_SET_PAUSE_RSP:
+ DO_CALLBACK(set_pause_cb);
+ break;
+ case BFI_LL_I2H_MTU_INFO_RSP:
+ DO_CALLBACK(mtu_info_cb);
+ break;
+ case BFI_LL_I2H_RX_RSP:
+ DO_CALLBACK(rxf_cb);
+ break;
+ case BFI_LL_I2H_CEE_DOWN_AEN:
+ break;
+ case BFI_LL_I2H_CEE_UP_AEN:
+ break;
+ default:
+ DPRINTK(WARNING, "cb(): unknown msg Id %d for LL class\n",
+ cmd_code);
+ break;
+ }
+}
+
+
+static enum bna_status_e
+bna_flush_mbox_q(struct bna_dev_s *dev, u8 wait_for_rsp)
+{
+ struct bna_mbox_q *q;
+ struct bna_mbox_cmd_qe *qe = NULL;
+ u8 msg_id = 0, msg_class = 0;
+
+ q = &dev->mbox_q;
+
+ if (bna_mbox_q_is_empty(q))
+ return BNA_OK;
+ if (q->posted != NULL && wait_for_rsp) {
+ qe = (struct bna_mbox_cmd_qe *)(q->posted);
+ /* The driver has to retry */
+ return BNA_BUSY;
+ }
+
+ while (!bna_mbox_q_is_empty(q)) {
+ qe = bna_mbox_q_first(q);
+ msg_class = ((struct bfi_mhdr_s *)(&qe->cmd.msg[0]))->msg_class;
+ msg_id = ((struct bfi_mhdr_s *)(&qe->cmd.msg[0]))->msg_id;
+
+
+ BNA_ASSERT(msg_class == BFI_MC_LL);
+
+ bna_do_drv_ll_cb(dev, BFA_I2HM(msg_id),
+ BFI_LL_CMD_NOT_EXEC, qe);
+ bna_mbox_deq(q);
+ }
+ /* Reinit the queue, i.e prod = cons = 0; */
+ bna_mbox_q_init(q);
+ return BNA_OK;
+}
+
+enum bna_status_e
+bna_cleanup(void *bna_dev)
+{
+ struct bna_dev_s *dev = (struct bna_dev_s *)bna_dev;
+
+ dev->msg_ctr = 0;
+
+ return bna_flush_mbox_q(dev, 0);
+}
+
+/**
+ * Check both command queues
+ * Write to mailbox if required
+ */
+static enum bna_status_e
+bna_chk_n_snd_q(struct bna_dev_s *dev)
+{
+ struct bna_mbox_cmd_qe *qe = NULL;
+ struct bna_mbox_q *q;
+ struct bfi_mhdr_s *mh = NULL;
+
+ q = &dev->mbox_q;
+
+ if ((bna_mbox_q_is_empty(q)) || (q->posted != NULL))
+ return BNA_OK;
+
+ qe = bna_mbox_q_first(q);
+ /* Do not post any more commands if disable pending */
+ if (dev->ioc_disable_pending == 1)
+ return BNA_OK;
+
+ mh = ((struct bfi_mhdr_s *)(&qe->cmd.msg[0]));
+ mh->mtag.i2htok = bna_os_htons(dev->msg_ctr);
+ dev->msg_ctr++;
+ bfa_ioc_mbox_queue(&dev->ioc, &qe->cmd);
+ q->posted = qe;
+
+ return BNA_OK;
+}
+
+enum bna_status_e
+bna_mbox_send(struct bna_dev_s *dev, void *cmd, u32 cmd_len,
+ void *cbarg)
+{
+ struct bna_mbox_cmd_qe *qe;
+ struct bna_mbox_q *q;
+
+ BNA_ASSERT(cmd_len <= BNA_MAX_MBOX_CMD_LEN);
+
+ if (dev->ioc_disable_pending) {
+ DPRINTK(WARNING,
+ "IOC Disable is pending :"
+ "Cannot queue Cmd class %d id %d\n",
+ mh->msg_class, mh->msg_id);
+ return BNA_FAIL;
+ }
+
+ if (!bfa_ioc_is_operational(&dev->ioc)) {
+ DPRINTK(ERR,
+ "IOC is not operational :"
+ "Cannot queue Cmd class %d id %d\n",
+ mh->msg_class, mh->msg_id);
+ return BNA_FAIL;
+ }
+
+ q = &dev->mbox_q;
+ qe = bna_mbox_enq(q, cmd, cmd_len, cbarg);
+ if (qe == NULL)
+ return BNA_FAIL;
+ return bna_chk_n_snd_q(dev);
+}
+
+
+/**
+ * Returns 1, if this is an aen, 0 otherwise
+ */
+static int
+bna_is_aen(u8 msg_id)
+{
+ return
+ (msg_id == BFI_LL_I2H_LINK_DOWN_AEN ||
+ msg_id == BFI_LL_I2H_LINK_UP_AEN ||
+ msg_id == BFI_LL_I2H_CEE_DOWN_AEN ||
+ msg_id == BFI_LL_I2H_CEE_UP_AEN);
+}
+
+static void
+bna_err_handler(struct bna_dev_s *dev, u32 intr_status)
+{
+ u32 curr_mask;
+
+ DPRINTK(DEBUG, "HW ERROR : INT statux 0x%x on port %d\n",
+ intr_status, dev->port);
+
+ bfa_ioc_error_isr(&dev->ioc);
+
+ /*
+ * Disable all the bits in interrupt mask, including
+ * the mbox & error bits.
+ * This is required so that once h/w error hits, we don't
+ * get into a loop.
+ */
+ bna_intx_disable(dev, &curr_mask);
+
+ if (dev->mb_cbfns.hw_error_cb)
+ (dev->mb_cbfns.hw_error_cb)(dev->cbarg, 0);
+}
+
+void
+bna_ll_isr(void *llarg, struct bfi_mbmsg_s *msg)
+{
+ u32 aen = 0;
+ struct bna_dev_s *dev = (struct bna_dev_s *)llarg;
+ struct bna_mbox_cmd_qe *qe = NULL;
+ struct bna_mbox_q *mbox_q = NULL;
+ struct bfi_ll_rsp *mb_rsp = NULL;
+
+ mb_rsp = (struct bfi_ll_rsp *)(msg);
+
+ BNA_ASSERT(mb_rsp->mh.msg_class == BFI_MC_LL);
+
+ aen = bna_is_aen(mb_rsp->mh.msg_id);
+ if (!aen) {
+ mbox_q = &dev->mbox_q;
+
+ BNA_ASSERT(!bna_mbox_q_is_empty(mbox_q));
+ qe = bna_mbox_q_first(mbox_q);
+ BNA_ASSERT(mbox_q->posted == qe);
+
+ if (BFA_I2HM(((struct bfi_mhdr_s *)
+ (&qe->cmd.msg[0]))->msg_id) != mb_rsp->mh.msg_id) {
+ DPRINTK(ERR,
+ "Invalid Rsp Msg %d:%d (Expected %d:%d) on %d\n",
+ mb_rsp->mh.msg_class, mb_rsp->mh.msg_id,
+ ((struct bfi_mhdr_s *)(&qe->cmd.msg[0]))->msg_class,
+ BFA_I2HM(((struct bfi_mhdr_s *)
+ (&qe->cmd.msg[0]))->msg_id),
+ dev->port);
+ BNA_ASSERT(0);
+ return;
+ }
+ bna_mbox_deq(mbox_q);
+ mbox_q->posted = NULL;
+ }
+ bna_do_drv_ll_cb(dev, mb_rsp->mh.msg_id, mb_rsp->error, qe);
+ bna_chk_n_snd_q(dev);
+}
+
+
+void
+bna_mbox_err_handler(struct bna_dev_s *dev, u32 intr_status)
+{
+ if (BNA_IS_ERR_INTR(intr_status)) {
+ bna_err_handler(dev, intr_status);
+ return;
+ }
+
+ if (BNA_IS_MBOX_INTR(intr_status))
+ bfa_ioc_mbox_isr(&dev->ioc);
+}
+
+/**
+ * bna_port_admin()
+ *
+ * Enable (up) or disable (down) the interface administratively.
+ *
+ * @param[in] dev - pointer to BNA device structure
+ * @param[in] enable - enable/disable the interface.
+ *
+ * @return BNA_OK or BNA_FAIL
+ */
+enum bna_status_e
+bna_port_admin(struct bna_dev_s *bna_dev, enum bna_enable_e enable)
+{
+ struct bna_dev_s *dev = (struct bna_dev_s *)bna_dev;
+ struct bfi_ll_port_admin_req ll_req;
+
+ ll_req.mh.msg_class = BFI_MC_LL;
+ ll_req.mh.msg_id = BFI_LL_H2I_PORT_ADMIN_REQ;
+ ll_req.mh.mtag.i2htok = 0;
+
+ ll_req.up = enable;
+
+ /* send to f/w */
+ return bna_mbox_send(dev, &ll_req, sizeof(ll_req), dev->cbarg);
+}
+/**
+ * bna_port_param_get()
+ *
+ * Get the port parameters.
+ *
+ * @param[in] dev - pointer to BNA device structure
+ * @param[out] param_ptr - pointer to where the parameters will be returned.
+ *
+ * @return void
+ */
+void
+bna_port_param_get(struct bna_dev_s *dev, struct bna_port_param *param_ptr)
+{
+ param_ptr->supported = BNA_TRUE;
+ param_ptr->advertising = BNA_TRUE;
+ param_ptr->speed = BNA_LINK_SPEED_10Gbps;
+ param_ptr->duplex = BNA_TRUE;
+ param_ptr->autoneg = BNA_FALSE;
+ param_ptr->port = dev->port;
+}
+
+/**
+ * bna_port_mac_get()
+ *
+ * Get the Burnt-in or permanent MAC address. This function does not return
+ * the MAC set thru bna_rxf_ucast_mac_set() but the one that is assigned to
+ * the port upon reset.
+ *
+ * @param[in] dev - pointer to BNA device structure
+ * @param[out] mac_ptr - Burnt-in or permanent MAC address.
+ *
+ * @return void
+ */
+void
+bna_port_mac_get(struct bna_dev_s *bna_dev, u8 *mac_ptr)
+{
+ struct bna_dev_s *dev = (struct bna_dev_s *)bna_dev;
+ mac_t mac;
+
+ mac = bfa_ioc_get_mac(&dev->ioc);
+
+ /* TODO : Use mac_t, remove memcpy */
+ bna_os_memcpy(mac_ptr, &mac, sizeof(mac_t));
+}
+
+/**
+ * IOC Integration
+ */
+
+/**
+ * bfa_iocll_cbfn
+ * Structure for callbacks to be implemented by
+ * the driver.
+ */
+static struct bfa_ioc_cbfn_s bfa_iocll_cbfn = {
+ bna_iocll_enable_cbfn,
+ bna_iocll_disable_cbfn,
+ bna_iocll_hbfail_cbfn,
+ bna_iocll_reset_cbfn
+};
+
+
+static void
+bna_iocll_memclaim(struct bna_dev_s *dev, struct bna_meminfo *mi)
+{
+
+ bfa_ioc_mem_claim(&dev->ioc, mi[BNA_DMA_MEM_T_ATTR].kva,
+ mi[BNA_DMA_MEM_T_ATTR].dma);
+
+ if (bna_ioc_auto_recover)
+ bfa_ioc_debug_memclaim(&dev->ioc,
+ mi[BNA_KVA_MEM_T_FWTRC].kva);
+}
+
+void
+bna_iocll_meminfo(struct bna_dev_s *dev, struct bna_meminfo *mi)
+{
+
+
+ mi[BNA_DMA_MEM_T_ATTR].len =
+ BNA_ALIGN(bfa_ioc_meminfo(), BNA_PAGE_SIZE);
+
+ mi[BNA_KVA_MEM_T_FWTRC].len =
+ bfa_ioc_debug_trcsz(bna_ioc_auto_recover);
+}
+
+void
+bna_iocll_attach(struct bna_dev_s *dev, void *bnad, struct bna_meminfo *meminfo,
+ struct bfa_pcidev_s *pcidev, struct bfa_trc_mod_s *trcmod,
+ struct bfa_aen_s *aen, struct bfa_log_mod_s *logm)
+{
+ bfa_ioc_attach(&dev->ioc, bnad, &bfa_iocll_cbfn, &dev->timer_mod,
+ trcmod, aen, logm);
+ bfa_ioc_pci_init(&dev->ioc, pcidev, BFI_MC_LL);
+
+ bfa_ioc_mbox_regisr(&dev->ioc, BFI_MC_LL, bna_ll_isr, dev);
+ bna_iocll_memclaim(dev, meminfo);
+
+
+ bfa_timer_init(&dev->timer_mod);
+
+}
+
+/**
+ * FIXME :
+ * Either the driver or CAL should serialize
+ * this IOC disable
+ * Currently this is happening indirectly b'cos
+ * bfa_ioc_disable() is not called if there
+ * is an outstanding cmd in the queue, which
+ * could not be flushed.
+ */
+enum bna_status_e
+bna_iocll_disable(struct bna_dev_s *dev)
+{
+ enum bna_status_e ret;
+
+ dev->ioc_disable_pending = 1;
+ ret = bna_flush_mbox_q(dev, 1);
+ if (ret != BNA_OK) {
+ DPRINTK(WARNING, "Unable to flush Mbox Queues [%d]\n", ret);
+ return ret;
+ }
+
+ bfa_ioc_disable(&dev->ioc);
+ dev->ioc_disable_pending = 0;
+
+ return BNA_OK;
+}
+
+void
+bna_iocll_getinfo(struct bna_dev_s *dev, char *serial_num, u32 size)
+{
+ struct bfa_adapter_attr_s ad_attr;
+ u32 length;
+
+ bfa_ioc_get_adapter_attr(&dev->ioc, &ad_attr);
+ length = BNA_MIN(size, sizeof(ad_attr.serial_num));
+ bna_os_memcpy(serial_num, ad_attr.serial_num, length);
+}
+
+/* Dummy */
+/* FIXME : Delete once bfa_diag.c is fixed */
+void
+bfa_pport_beacon(struct bfa_s *bfa, bfa_boolean_t beacon,
+ bfa_boolean_t link_e2e_beacon)
+{
+}
+
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bna_iocll.h linux-2.6.32-rc4-mod/drivers/net/bna/bna_iocll.h
--- linux-2.6.32-rc4-orig/drivers/net/bna/bna_iocll.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bna_iocll.h 2009-10-16 10:30:53.488437000 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * See LICENSE.bna for copyright and licensing details.
+ */
+
+#ifndef __BNA_IOCLL_H__
+#define __BNA_IOCLL_H__
+
+#include <bfa_ioc.h>
+#include <bfa_timer.h>
+#include <bfi/bfi_ll.h>
+
+#define BNA_IOC_TIMER_FREQ BFA_TIMER_FREQ
+
+/*
+ * LL specific IOC functions.
+ */
+void bna_iocll_meminfo(struct bna_dev_s *bna_dev, struct bna_meminfo *meminfo);
+void bna_iocll_attach(struct bna_dev_s *bna_dev, void *bnad,
+ struct bna_meminfo *meminfo, struct bfa_pcidev_s *pcidev,
+ struct bfa_trc_mod_s *trcmod, struct bfa_aen_s *aen,
+ struct bfa_log_mod_s *logmod);
+enum bna_status_e bna_iocll_disable(struct bna_dev_s *bna_dev);
+void bna_iocll_getinfo(struct bna_dev_s *bna_dev, char *serial_num, u32 size);
+#define bna_iocll_detach(dev) bfa_ioc_detach(&((dev)->ioc))
+#define bna_iocll_enable(dev) bfa_ioc_enable(&((dev)->ioc))
+#define bna_iocll_debug_fwsave(dev, trc_data, trc_len) \
+ bfa_ioc_debug_fwsave(&((dev)->ioc), (trc_data), (trc_len))
+#define bna_iocll_debug_fwtrc(dev, trc_data, trc_len) \
+ bfa_ioc_debug_fwtrc(&((dev)->ioc), (trc_data), (trc_len))
+#define bna_iocll_timer(dev) bfa_timer_beat(&((dev)->timer_mod))
+#define bna_iocll_getstats(dev, ioc_stats) \
+ bfa_ioc_fetch_stats(&((dev)->ioc), (ioc_stats))
+#define bna_iocll_resetstats(dev) bfa_ioc_clr_stats(&((dev)->ioc))
+#define bna_iocll_getattr(dev, ioc_attr) \
+ bfa_ioc_get_attr(&((dev)->ioc), (ioc_attr))
+
+/**
+ * Callback functions to be implemented by the driver
+ */
+void bna_iocll_enable_cbfn(void *bnad, enum bfa_status status);
+void bna_iocll_disable_cbfn(void *bnad);
+void bna_iocll_hbfail_cbfn(void *bnad);
+void bna_iocll_reset_cbfn(void *bnad);
+
+#endif /* __BNA_IOCLL_H__ */
+
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bna_os.h linux-2.6.32-rc4-mod/drivers/net/bna/bna_os.h
--- linux-2.6.32-rc4-orig/drivers/net/bna/bna_os.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bna_os.h 2009-10-16 10:30:53.504447000 -0700
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * See <license_file> for copyright and licensing details.
+ */
+
+/**
+ * Contains declarations of linux specific calls required for Brocade
+ * FCoE HBA driver implementation. Each OS has to provide this file.
+ */
+
+/**
+ * bna_os.h Linux driver OS specific declarations.
+ */
+
+#ifndef __BNA_OS_H__
+#define __BNA_OS_H__
+
+#ifdef __KERNEL__
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/if_ether.h> /* Hack for ETH_ALEN: Do we care ? */
+#include <asm/page.h>
+#include <asm/io.h>
+#include <asm/string.h>
+#else
+#include <sys/io.h>
+#endif
+
+#ifdef __KERNEL__
+
+#define BNA_PAGE_SIZE PAGE_SIZE
+#define BNA_PAGE_SHIFT PAGE_SHIFT
+
+#define BNA_ERR KERN_ERR
+#define BNA_WARNING KERN_WARNING
+#define BNA_NOTICE KERN_NOTICE
+#define BNA_INFO KERN_INFO
+#define BNA_DEBUG KERN_DEBUG
+
+
+#ifdef DEBUG
+
+#define PFX "BNA: "
+#define DPRINTK(klevel, fmt, args...) do { \
+ printk(KERN_##klevel PFX fmt, \
+ ## args); \
+} while (0)
+
+#ifdef BNA_ASSERT_PRINTK_ONLY
+#define BNA_ASSERT(p) do { \
+ if (!(p)) \
+ printk(KERN_ERR "assert(%s) failed at %s:%d\n", \
+ #p, __FILE__, __LINE__); \
+} while (0)
+#else
+#define BNA_ASSERT(p) do { \
+ if (!(p)) { \
+ printk(KERN_ERR "assert(%s) failed at %s:%d\n", \
+ #p, __FILE__, __LINE__); \
+ BUG(); \
+ } \
+} while (0)
+#endif
+
+#ifndef BNA_DEV_PRINTF
+#define BNA_DEV_PRINTF(bnad, level, fmt, arg...) \
+ dev_printk(level, &(((bnad_t *)(bnad)) \
+ ->pcidev->dev), fmt, ##arg);
+#endif
+
+#define BNA_PRINTF(level, fmt, arg...) \
+ printk(level fmt, ##arg);
+
+#else /* DEBUG */
+
+#define BNA_ASSERT(p)
+#define BNA_DEV_PRINTF(bnad, level, fmt, arg...)
+#define BNA_PRINTF(level, fmt, arg...)
+
+#define DPRINTK(klevel, fmt, args...) do { \
+ } while (0)
+
+#endif /* !DEBUG */
+
+#else /* __KERNEL__ */
+
+#define BNA_ASSERT(p)
+
+#endif /* !__KERNEL__ */
+
+#define bna_os_swap_3b(_x) \
+ ((((_x) & 0xff) << 16) | \
+ ((_x) & 0x00ff00) | \
+ (((_x) & 0xff0000) >> 16))
+
+#define bna_os_swap_8b(_x) \
+ ((((_x) & 0xff00000000000000ull) >> 56) \
+ | (((_x) & 0x00ff000000000000ull) >> 40) \
+ | (((_x) & 0x0000ff0000000000ull) >> 24) \
+ | (((_x) & 0x000000ff00000000ull) >> 8) \
+ | (((_x) & 0x00000000ff000000ull) << 8) \
+ | (((_x) & 0x0000000000ff0000ull) << 24) \
+ | (((_x) & 0x000000000000ff00ull) << 40) \
+ | (((_x) & 0x00000000000000ffull) << 56))
+
+#define bna_os_swap32(_x) \
+ ((((_x) & 0xff) << 24) | \
+ (((_x) & 0x0000ff00) << 8) | \
+ (((_x) & 0x00ff0000) >> 8) | \
+ (((_x) & 0xff000000) >> 24))
+
+
+#ifndef __BIGENDIAN
+#define bna_os_htons(_x) ((u16)((((_x) & 0xff00) >> 8) | \
+ (((_x) & 0x00ff) << 8)))
+
+#define bna_os_htonl(_x) bna_os_swap32(_x)
+#define bna_os_htonll(_x) bna_os_swap_8b(_x)
+
+#define bna_os_wtole(_x) (_x)
+#define bna_os_wtobe(_x) bna_os_swap32(_x)
+
+#define bna_os_dma_addr64(_x) bna_os_swap_8b(_x)
+
+#else
+
+#define bna_os_htons(_x) (_x)
+#define bna_os_htonl(_x) (_x)
+#define bna_os_htonll(_x) (_x)
+
+#define bna_os_wtole(_x) bna_os_swap32(_x)
+#define bna_os_wtobe(_x) (_x)
+
+#define bna_os_dma_addr64(_x) (_x)
+
+#endif
+
+#define bna_os_ntohs(_x) bna_os_htons(_x)
+#define bna_os_ntohl(_x) bna_os_htonl(_x)
+#define bna_os_ntohll(_x) bna_os_htonll(_x)
+#define bna_os_ntoh3b(_x) bna_os_hton3b(_x)
+
+#define bna_os_memset memset
+#define bna_os_memcpy memcpy
+#define bna_os_udelay udelay
+#define bna_os_vsprintf vsprintf
+
+#define bna_os_reg_read(_raddr) readl(_raddr)
+#define bna_os_reg_write(_raddr, _val) writel(_val, _raddr)
+#define bna_os_mem_read(_raddr, _off) \
+ bna_os_ntohl(((u32 *)_raddr)[(_off) >> 2])
+#define bna_os_mem_write(_raddr, _off, _val) \
+ (((u32 *)_raddr)[(_off) >> 2]) = bna_os_htonl(_val)
+
+#define bna_os_mem_readw(_raddr) \
+ bna_os_htonl(*((u32 *)(_raddr)))
+#define bna_os_mem_writew(_raddr, _val) \
+ {*((u32 *)(_raddr)) = bna_os_htonl((_val))}
+
+/* Required for DMA address manipulation in IOC */
+#define bfa_os_u32(__pa64) ((__pa64) >> 32)
+
+#endif /* __BNA_OS_H__ */
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bna_priv.h linux-2.6.32-rc4-mod/drivers/net/bna/bna_priv.h
--- linux-2.6.32-rc4-orig/drivers/net/bna/bna_priv.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bna_priv.h 2009-10-16 10:30:53.519452000 -0700
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * See <license_file> for copyright and licensing details.
+ */
+
+/**
+ * BNA register access macros.p
+ */
+
+#ifndef __BNA_PRIV_H__
+#define __BNA_PRIV_H__
+
+
+#define bna_mem_read(_raddr, _off) bna_os_mem_read(_raddr, _off)
+#define bna_mem_write(_raddr, _off, _val) \
+ bna_os_mem_write(_raddr, _off, _val)
+
+
+/**
+ Macros to declare a bit table
+ *
+ * @param[in] _table - bit table to be declared
+ * @param[in] _size - size in bits
+ */
+#define BNA_BIT_TABLE_DECLARE(_table, _size) \
+ (u32 _table[(_size) / 32])
+/**
+ Macros to set bits in a bit table
+ *
+ * @param[in] _table - bit table to be declared
+ * @param[in] _bit - bit to be set
+ */
+#define BNA_BIT_TABLE_SET(_table, _bit) \
+ (_table[(_bit) / 32] |= (1 << ((_bit) & (32 - 1))))
+/**
+ Macros to clear bits in a bit table
+ *
+ * @param[in] _table - bit table to be declared
+ * @param[in] _bit - bit to be set
+ */
+#define BNA_BIT_TABLE_CLEAR(_table, _bit) \
+ (_table[(_bit) / 32] &= ~(1 << ((_bit) & (32 - 1))))
+/**
+ Macros to set bits in a 32 bit word
+ *
+ * @param[in] _word - word in which bit is to be set
+ * @param[in] _bit - bit to be set (starting at 0)
+ */
+#define BNA_BIT_WORD_SET(_word, _bit) \
+ ((_word) |= (1 << (_bit)))
+/**
+ Macros to clear bits in a 32 bit word
+ *
+ * @param[in] _word - word in which bit is to be cleared
+ * @param[in] _bit - bit to be cleared (starting at 0)
+ */
+#define BNA_BIT_WORD_CLEAR(_word, _bit) \
+ ((_word) &= ~(1 << (_bit)))
+
+/**
+ * BNA_GET_PAGE_NUM()
+ *
+ * Macro to calculate the page number for the
+ * memory spanning multiple pages.
+ *
+ * @param[in] _base_page - Base Page Number for memory
+ * @param[in] _offset - Offset for which page number
+ * is calculated
+ */
+#define BNA_GET_PAGE_NUM(_base_page, _offset) \
+ ((_base_page) + ((_offset) >> 15))
+/**
+ * BNA_GET_PAGE_OFFSET()
+ *
+ * Macro to calculate the page offset for the
+ * from the base offset (from the top of the block)
+ * Each page 0x8000 (32KB) in size
+ *
+ * @param[in] _offset - Offset from the top of the block
+ */
+#define BNA_GET_PAGE_OFFSET(_offset) \
+ ((_offset) & 0x7fff)
+
+/**
+ * BNA_GET_WORD_OFFSET()
+ *
+ * Macro to calculate the address of a word from
+ * the base address. Needed to access H/W memory
+ * as 4 byte words. Starts from 0.
+ *
+ * @param[in] _base_offset - Starting offset of the data
+ * @param[in] _word - Word no. for which address is calculated
+ */
+#define BNA_GET_WORD_OFFSET(_base_offset, _word) \
+ ((_base_offset) + ((_word) << 2))
+/**
+ * BNA_GET_BYTE_OFFSET()
+ *
+ * Macro to calculate the address of a byte from
+ * the base address. Most of H/W memory is accessed
+ * as 4 byte words, so use this macro carefully.
+ * Starts from 0.
+ *
+ * @param[in] _base_offset - Starting offset of the data
+ * @param[in] _byte - Byte no. for which address is calculated
+ */
+#define BNA_GET_BYTE_OFFSET(_base_offset, _byte) \
+ ((_base_offset) + (_byte))
+
+/**
+ * BNA_GET_MEM_BASE_ADDR()
+ *
+ * Macro to calculate the base address of
+ * any memory block given the bar0 address
+ * and the memory base offset
+ *
+ * @param[in] _bar0 - BARO address
+ * @param[in] _base_offset - Starting offset of the memory
+ */
+#define BNA_GET_MEM_BASE_ADDR(_bar0, _base_offset) \
+ ((_bar0) + HW_BLK_HOST_MEM_ADDR \
+ + BNA_GET_PAGE_OFFSET((_base_offset)))
+
+/**
+ * Structure which maps to Rx FnDb config
+ * Size : 4 words
+ * See catapult_spec.pdf, RxA for details
+ */
+struct bna_rx_fndb_ram {
+ u32 rss_prop;
+ u32 size_routing_props;
+ u32 rit_hds_mcastq;
+ u32 control_flags;
+};
+
+/**
+ * Structure which maps to Tx FnDb config
+ * Size : 1 word
+ * See catapult_spec.pdf, TxA for details
+ */
+struct bna_tx_fndb_ram {
+ u32 vlan_n_ctrl_flags;
+};
+
+/**
+ * Structure which maps to Unicast/Multicast CAM entry
+ * Size : 2 words
+ * See catapult_spec.pdf, LUT for details
+ */
+struct bna_cam {
+ u32 cam_mac_addr_47_32; /* 31:16->res;15:0->MAC */
+ u32 cam_mac_addr_31_0;
+};
+
+/**
+ * Structure which maps to Unicast RAM entry
+ * Size : 1 word
+ * See catapult_spec.pdf, LUT for details
+ */
+struct bna_ucast_mem {
+ u32 ucast_ram_entry;
+};
+
+/**
+ * Structure which maps to VLAN RAM entry
+ * Size : 1 word ?? Need to verify
+ * See catapult_spec.pdf, LUT for details
+ */
+struct bna_vlan_mem {
+ u32 vlan_ram_entry; /* FIXME */
+};
+#define BNA_GET_VLAN_MEM_ENTRY_ADDR(_bar0, _fn_id, _vlan_id)\
+ (_bar0 + (HW_BLK_HOST_MEM_ADDR) \
+ + (BNA_GET_PAGE_OFFSET(VLAN_RAM_BASE_OFFSET)) \
+ + (((_fn_id) & 0x3f) << 9) \
+ + (((_vlan_id) & 0xfe0) >> 3))
+
+/**
+ * Structure which maps to exact/approx MVT RAM entry
+ * Size : 4 words
+ * See catapult_spec.pdf, RxA for details
+ */
+struct bna_mvt_mem {
+ u32 reserved;
+ u32 fc_bit; /* 31:1->res;0->fc_bit */
+ u32 ll_fn_63_32; /* LL fns 63 to 32 */
+ u32 ll_fn_31_0; /* LL fns 31 to 0 */
+};
+
+/**
+ * Structure which maps to RxFn Indirection Table (RIT)
+ * Size : 1 word
+ * See catapult_spec.pdf, RxA for details
+ */
+struct bna_rit_mem {
+ u32 rxq_ids; /* 31:12->res;11:0->two 6 bit RxQ Ids */
+};
+
+/**
+ * Structure which maps to RSS Table entry
+ * Size : 16 words
+ * See catapult_spec.pdf, RAD for details
+ */
+struct bna_rss_mem {
+ u32 type_n_hash; /* 31:12->res;
+ 11:8 ->protocol type
+ 7:0 ->hash index */
+ u32 hash_key[10]; /* 40 byte Toeplitz hash key */
+ u32 reserved[5];
+};
+
+/**
+ * Structure which maps to RxQ Memory entry
+ * Size : 16 words, entries are 32 words apart
+ * Alternate arrangement of RxQ & TxQ
+ * See catapult_spec.pdf, HQM for details
+ */
+struct bna_rxq_mem {
+ u32 pg_tbl_addr_lo;
+ u32 pg_tbl_addr_hi;
+ u32 cur_q_entry_lo;
+ u32 cur_q_entry_hi;
+#if 0
+ u32 nxt_pg_addr_lo;
+ u32 nxt_pg_addr_hi;
+#endif
+ u32 reserved1;
+ u32 reserved2;
+ u32 pg_cnt_n_prd_ptr;/* 31:16->total page count
+ 15:0 ->producer pointer (index?)*/
+ u32 entry_n_pg_size; /* 31:16->entry size
+ 15:0 ->page size */
+ u32 sg_n_cq_n_cns_ptr; /* 31:28->reserved; 27:24->sg count
+ 23:16->CQ; 15:0->consumer pointer(index?) */
+ u32 buf_sz_n_q_state; /* 31:16->buffer size; 15:0-> Q state */
+ u32 next_qid; /* 17:10->next QId */
+ u32 reserved3;
+ u32 reserved4[4];
+};
+
+
+/**
+ * Structure which maps to TxQ Memory entry
+ * Size : 16 words, entries are 32 words apart
+ * Alternate arrangement of RxQ & TxQ
+ * See catapult_spec.pdf, HQM for details
+ */
+struct bna_txq_mem {
+ u32 pg_tbl_addr_lo;
+ u32 pg_tbl_addr_hi;
+ u32 cur_q_entry_lo;
+ u32 cur_q_entry_hi;
+#if 0 /* obsolete, now replaced by reserved fields */
+ u32 nxt_pg_addr_lo
+ u32 nxt_pg_addr_hi;
+#endif
+ u32 reserved1;
+ u32 reserved2;
+ u32 pg_cnt_n_prd_ptr;/* 31:16->total page count
+ 15:0 ->producer pointer (index?)*/
+ u32 entry_n_pg_size; /* 31:16->entry size
+ 15:0 ->page size */
+ u32 int_blk_n_cns_ptr;/* 31:24->Int Blk Id; 23:16->Int Blk Offset
+ 15:0 ->consumer pointer(index?) */
+ u32 cns_ptr2_n_q_state;/* 31:16->cons. ptr 2; 15:0-> Q state */
+ u32 nxt_qid_n_fid_n_pri;/* 17:10->next QId;9:3->FID;2:0->Priority */
+ u32 wvc_n_cquota_n_rquota; /* 31:24->WI Vector Count;
+ 23:12->Cfg Quota;
+ 11:0 ->Run Quota */
+ u32 reserved3[4];
+};
+
+/**
+ * Structure which maps to RxQ and TxQ Memory entry
+ * Size : 32 words, entries are 32 words apart
+ * Alternate arrangement of RxQ & TxQ
+ * See catapult_spec.pdf, HQM for details
+ */
+struct bna_rxtx_q_mem {
+ struct bna_rxq_mem rxq;
+ struct bna_txq_mem txq;
+};
+
+/**
+ * Structure which maps to CQ Memory entry
+ * Size : 16 words
+ * See catapult_spec.pdf, HQM for details
+ */
+struct bna_cq_mem {
+ u32 pg_tbl_addr_lo;
+ u32 pg_tbl_addr_hi;
+ u32 cur_q_entry_lo;
+ u32 cur_q_entry_hi;
+#if 0
+ u32 nxt_pg_addr_lo;
+ u32 nxt_pg_addr_hi;
+#endif
+ u32 reserved1;
+ u32 reserved2;
+ u32 pg_cnt_n_prd_ptr;/* 31:16->total page count
+ 15:0 ->producer pointer (index?)*/
+ u32 entry_n_pg_size; /* 31:16->entry size
+ 15:0 ->page size */
+ u32 int_blk_n_cns_ptr;/* 31:24->Int Blk Id; 23:16->Int Blk Offset
+ 15:0 ->consumer pointer(index?) */
+ u32 q_state; /* 31:16->reserved; 15:0-> Q state */
+ u32 reserved3[2];
+ u32 reserved4[4];
+};
+
+/**
+ * Structure which maps to Interrupt Block Memory entry
+ * Size : 8 words (used: 5 words)
+ * See catapult_spec.pdf, HQM for details
+ */
+struct bna_ib_blk_mem {
+ u32 host_addr_lo;
+ u32 host_addr_hi;
+ u32 clsc_n_ctrl_n_msix;/* 31:24->coalescing;
+ 23:16->coalescing cfg;
+ 15:8 ->control;
+ 7:0 ->msix; */
+ u32 ipkt_n_ent_n_idxof;
+ u32 ipkt_cnt_cfg_n_unacked;
+
+ u32 reserved[3];
+};
+
+/**
+ * Structure which maps to Index Table Block Memory entry
+ * Size : 1 word
+ * See catapult_spec.pdf, HQM for details
+ */
+struct bna_idx_tbl_mem {
+ u32 idx; /* 31:16->res;15:0->idx; */
+};
+
+/**
+ * Structure which maps to Doorbell QSet Memory,
+ * Organization of Doorbell address space for a
+ * QSet (RxQ, TxQ, Rx IB, Tx IB)
+ * For Non-VM qset entries are back to back.
+ * Size : 128 bytes / 4K bytes for Non-VM / VM
+ * See catapult_spec.pdf, HQM for details
+ */
+struct bna_doorbell_qset {
+ u32 rxq[0x20 >> 2];
+ u32 txq[0x20 >> 2];
+ u32 ib0[0x20 >> 2];
+ u32 ib1[0x20 >> 2];
+};
+
+
+#define BNA_GET_DOORBELL_BASE_ADDR(_bar0) \
+ ((_bar0) + HQM_DOORBELL_BLK_BASE_ADDR)
+
+
+/**
+ * BNA_GET_DOORBELL_ENTRY_OFFSET()
+ *
+ * Macro to calculate the offset of the Doorbell QSet
+ * in the Non VM case.
+ * Entries are 128 bytes apart. Does not need a page
+ * number register for access.
+ *
+ * @param[in] _entry - Entry Number
+ */
+#define BNA_GET_DOORBELL_ENTRY_OFFSET(_entry) \
+ ((HQM_DOORBELL_BLK_BASE_ADDR) \
+ + (_entry << 7))
+/**
+ * BNA_GET_DOORBELL_VM_ENTRY_OFFSET()
+ *
+ * Macro to calculate the offset of the Doorbell QSet
+ * in the VM case.
+ * Entries are 4K (0x1000) bytes apart.
+ * Does not need a page number register for access.
+ *
+ * @param[in] _entry - Entry Number
+ */
+#define BNA_GET_DOORBELL_VM_ENTRY_OFFSET(_entry) \
+ ((HQM_DOORBELL_VM_BLK_BASE_ADDR) \
+ + (_entry << 12))
+
+/**
+ * BNA_GET_PSS_SMEM_PAGE_NUM()
+ *
+ * Macro to calculate the page number of PSS SMEM
+ * block from a linear offset
+ *
+ * @param[in] _loff - Linear offset from the top of memory
+ */
+#define BNA_GET_PSS_SMEM_PAGE_NUM(_loff) \
+ (BNA_GET_PAGE_NUM(PSS_SMEM_BLK_PG_NUM, (_loff)))
+/**
+ * BNA_GET_PSS_SMEM_PAGE_OFFSET()
+ *
+ * Macro to calculate the page offset from the top
+ * of a PSS SMEM page, for a given linear offset
+ *
+ * @param[in] _loff - Linear offset from the top of memory
+ */
+#define BNA_GET_PSS_SMEM_PAGE_OFFSET(_loff) \
+ (PSS_SMEM_BLK_MEM_ADDR \
+ + BNA_GET_PAGE_OFFSET((_loff)))
+
+/**
+ * BNA_GET_MBOX_PAGE_NUM()
+ *
+ * Macro to calculate the page number of HostFn<->LPU/
+ * LPU<->HostFn Mailbox block from a linear offset
+ *
+ * @param[in] _loff - Linear offset from the top of memory
+ */
+#define BNA_GET_MBOX_PAGE_NUM(_loff) \
+ (BNA_GET_PAGE_NUM(CPQ_BLK_PG_NUM, (_loff)))
+/**
+ * BNA_GET_HOSTFN_LPU_MBOX_PAGE_OFFSET()
+ *
+ * Macro to calculate the HostFn<->LPU
+ * Mailbox page offset from the linear offset
+ *
+ * @param[in] _loff - Linear offset in bytes from the top of memory
+ */
+#define BNA_GET_HOSTFN_LPU_MBOX_PAGE_OFFSET(_loff) \
+ (HOSTFN_LPU_MBOX + BNA_GET_PAGE_OFFSET((_loff)))
+/**
+ * BNA_GET_LPU_HOSTFN_MBOX_PAGE_OFFSET()
+ *
+ * Macro to calculate the LPU<->HostFn
+ * Mailbox page offset from the linear offset
+ *
+ * @param[in] _loff - Linear offset from the top of memory
+ */
+#define BNA_GET_LPU_HOSTFN_MBOX_PAGE_OFFSET(_loff) \
+ (LPU_HOSTFN_MBOX + BNA_GET_PAGE_OFFSET((_loff)))
+
+
+#define bna_hw_stats_to_stats bna_os_dma_addr64
+
+void bna_mbox_q_init(struct bna_mbox_q *q);
+
+#define BNA_80K_PKT_RATE 80000
+#define BNA_70K_PKT_RATE 70000
+#define BNA_60K_PKT_RATE 60000
+#define BNA_50K_PKT_RATE 50000
+#define BNA_40K_PKT_RATE 40000
+#define BNA_30K_PKT_RATE 30000
+#define BNA_20K_PKT_RATE 20000
+#define BNA_10K_PKT_RATE 10000
+
+/**
+ * Defines are in order of decreasing load
+ * i.e BNA_HIGH_LOAD_3 has the highest load
+ * and BNA_LOW_LOAD_3 has the lowest load.
+ */
+
+#define BNA_HIGH_LOAD_4 0 /* r >= 80 */
+#define BNA_HIGH_LOAD_3 1 /* 60 <= r < 80 */
+#define BNA_HIGH_LOAD_2 2 /* 50 <= r < 60 */
+#define BNA_HIGH_LOAD_1 3 /* 40 <= r < 50 */
+#define BNA_LOW_LOAD_1 4 /* 30 <= r < 40 */
+#define BNA_LOW_LOAD_2 5 /* 20 <= r < 30 */
+#define BNA_LOW_LOAD_3 6 /* 10 <= r < 20 */
+#define BNA_LOW_LOAD_4 7 /* r < 10 K */
+#define BNA_LOAD_TYPES BNA_LOW_LOAD_4
+
+#define BNA_BIAS_TYPES 2 /* small : small > large*2 */
+ /* large : if small is false */
+
+#endif /* __BNA_PRIV_H__ */
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/cna.h linux-2.6.32-rc4-mod/drivers/net/bna/cna.h
--- linux-2.6.32-rc4-orig/drivers/net/bna/cna.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/cna.h 2009-10-16 10:30:53.566453000 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+#ifndef __CNA_H__
+#define __CNA_H__
+
+#include <cs/bfa_trc.h>
+#include <defs/bfa_defs_types.h>
+
+extern char bfa_version[];
+void bfa_ioc_auto_recover(bfa_boolean_t auto_recover);
+
+#endif /* __CNA_H__ */
^ permalink raw reply
* Subject: [PATCH 6/6] bna: Brocade 10Gb Ethernet device driver
From: Rasesh Mody @ 2009-10-16 18:24 UTC (permalink / raw)
To: netdev; +Cc: amathur
From: Rasesh Mody <rmody@brocade.com>
This is patch 6/6 which contains linux driver source for
Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
We wish this patch to be considered for inclusion in 2.6.32
Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
Kconfig | 15 +++++++++++++++
Makefile | 1 +
bna | 17 +++++++++++++++++
3 files changed, 33 insertions(+)
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna linux-2.6.32-rc4-mod/drivers/net/bna
--- linux-2.6.32-rc4-orig/drivers/net/bna 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna 2009-10-16 10:36:26.005199000 -0700
@@ -0,0 +1,17 @@
+#
+# Copyright (c) 2005-2008 Brocade Communications Systems, Inc.
+# All rights reserved.
+#
+
+obj-$(CONFIG_BNA) += bna.o
+
+bna-objs := bnad.o bnad_ethtool.o bna_fn.o bna_if.o bna_queue.o \
+ bfad_fwimg.o bfa_csdebug.o bfa_sm.o \
+ bfa_ioc.o bfa_timer.o \
+ bfa_cee.o
+
+EXTRA_CFLAGS += -Idrivers/net/bna \
+ -Idrivers/net/bna/include \
+ -Idrivers/net/bna/include/cee \
+ -Idrivers/net/bna/include/cna/pstats \
+ -Idrivers/net/bna/include/cs
diff -ruP linux-2.6.32-rc4-orig/drivers/net/Kconfig linux-2.6.32-rc4-mod/drivers/net/Kconfig
--- linux-2.6.32-rc4-orig/drivers/net/Kconfig 2009-10-11 14:43:56.000000000 -0700
+++ linux-2.6.32-rc4-mod/drivers/net/Kconfig 2009-10-16 10:36:25.990206000 -0700
@@ -2757,6 +2757,21 @@
To compile this driver as a module, choose M here: the module
will be called qlge.
+config BNA
+ tristate "Brocade 1010/1020 10Gb Ethernet Driver support"
+ depends on PCI
+ ---help---
+ This driver supports Brocade 1010/1020 10Gb CEE capable Ethernet
+ cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called bna.
+
+ For general information and support, go to the Brocade support
+ website at:
+
+ <http://support.brocade.com>
+
source "drivers/net/sfc/Kconfig"
source "drivers/net/benet/Kconfig"
diff -ruP linux-2.6.32-rc4-orig/drivers/net/Makefile linux-2.6.32-rc4-mod/drivers/net/Makefile
--- linux-2.6.32-rc4-orig/drivers/net/Makefile 2009-10-11 14:43:56.000000000 -0700
+++ linux-2.6.32-rc4-mod/drivers/net/Makefile 2009-10-16 10:36:25.999196000 -0700
@@ -26,6 +26,7 @@
obj-$(CONFIG_ENIC) += enic/
obj-$(CONFIG_JME) += jme.o
obj-$(CONFIG_BE2NET) += benet/
+obj-$(CONFIG_BNA) += bna/
gianfar_driver-objs := gianfar.o \
gianfar_ethtool.o \
^ permalink raw reply
* Subject: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
From: Rasesh Mody @ 2009-10-16 18:24 UTC (permalink / raw)
To: netdev; +Cc: amathur
From: Rasesh Mody <rmody@brocade.com>
This is patch 1/6 which contains linux driver source for
Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
We wish this patch to be considered for inclusion in 2.6.32
Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
bnad.c | 3576 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bnad.h | 374 ++++++
2 files changed, 3950 insertions(+)
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bnad.c linux-2.6.32-rc4-mod/drivers/net/bna/bnad.c
--- linux-2.6.32-rc4-orig/drivers/net/bna/bnad.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bnad.c 2009-10-16 10:30:53.050461000 -0700
@@ -0,0 +1,3576 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved.
+ */
+
+/**
+ * bnad.c Brocade 10G PCIe Ethernet driver.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/delay.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_ether.h>
+#include <linux/workqueue.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/pm.h>
+#include <linux/random.h>
+
+#ifdef NETIF_F_TSO
+#include <net/checksum.h>
+#endif
+
+
+#include "bnad.h"
+#include "bna_os.h"
+#include "bna_iocll.h"
+#include "bna_intr.h"
+#include "bnad_defs.h"
+
+#ifdef BNAD_NO_IP_ALIGN
+#undef NET_IP_ALIGN
+#define NET_IP_ALIGN 0
+#endif
+
+
+
+#define BNAD_TXQ_WI_NEEDED(_vectors) (((_vectors) + 3) >> 2)
+
+#define BNAD_RESET_Q(_bnad, _q, _unmap_q) \
+do { \
+ if ((_q)->producer_index != (_q)->consumer_index) { \
+ DPRINTK(ERR, "Q producer index %u != ", (_q)->producer_index); \
+ DPRINTK(ERR, "consumer index %u\n", (_q)->consumer_index); \
+ } \
+ BNA_ASSERT((_q)->producer_index == (_q)->consumer_index); \
+ if ((_unmap_q)->producer_index != (_unmap_q)->consumer_index) { \
+ DPRINTK(ERR, "UnmapQ producer index %u != ", (_unmap_q)->producer_index); \
+ DPRINTK(ERR, "consumer index %u\n", (_unmap_q)->consumer_index); \
+ } \
+ BNA_ASSERT((_unmap_q)->producer_index == \
+ (_unmap_q)->consumer_index); \
+ (_q)->producer_index = 0; \
+ (_q)->consumer_index = 0; \
+ (_unmap_q)->producer_index = 0; \
+ (_unmap_q)->consumer_index = 0; \
+ { \
+ u32 _ui; \
+ for (_ui = 0; _ui < (_unmap_q)->q_depth; _ui++) \
+ BNA_ASSERT(!(_unmap_q)->unmap_array[_ui].skb); \
+ } \
+} while (0)
+
+static uint bnad_msix = 1;
+module_param(bnad_msix, uint, 0444);
+MODULE_PARM_DESC(bnad_msix, "Enable MSI-X");
+
+uint bnad_small_large_rxbufs = 1;
+module_param(bnad_small_large_rxbufs, uint, 0444);
+MODULE_PARM_DESC(bnad_small_large_rxbufs, "Enable small/large buffer receive");
+
+static uint bnad_rxqsets_used;
+module_param(bnad_rxqsets_used, uint, 0444);
+MODULE_PARM_DESC(bnad_rxqsets_used, "Number of RxQ sets to be used");
+
+static uint bnad_ipid_mode;
+module_param(bnad_ipid_mode, uint, 0444);
+MODULE_PARM_DESC(bnad_ipid_mode, "0 - Use IP ID 0x0000 - 0x7FFF for LSO; "
+ "1 - Use full range of IP ID for LSO");
+
+uint bnad_txq_depth = BNAD_ENTRIES_PER_TXQ;
+module_param(bnad_txq_depth, uint, 0444);
+MODULE_PARM_DESC(bnad_txq_depth, "Maximum number of entries per TxQ");
+
+uint bnad_rxq_depth = BNAD_ENTRIES_PER_RXQ;
+module_param(bnad_rxq_depth, uint, 0444);
+MODULE_PARM_DESC(bnad_rxq_depth, "Maximum number of entries per RxQ");
+
+static uint bnad_vlan_strip = 1;
+module_param(bnad_vlan_strip, uint, 0444);
+MODULE_PARM_DESC(bnad_vlan_strip, "Let the hardware strip off VLAN header");
+
+static uint bnad_log_level = LOG_WARN_LEVEL;
+module_param(bnad_log_level, uint, 0644);
+MODULE_PARM_DESC(bnad_log_level, "Log level");
+
+static uint bnad_ioc_auto_recover = 1;
+module_param(bnad_ioc_auto_recover, uint, 0644);
+MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable auto recovery");
+
+uint bnad_rxqs_per_cq;
+
+static void bnad_disable_msix(struct bnad *bnad);
+static void bnad_free_ibs(struct bnad *bnad);
+static void bnad_set_rx_mode(struct net_device *netdev);
+static void bnad_set_rx_mode_locked(struct net_device *netdev);
+static void bnad_reconfig_vlans(struct bnad *bnad);
+static void bnad_q_num_init(struct bnad *bnad, uint rxqsets);
+static int bnad_set_mac_address(struct net_device *netdev, void *addr);
+static int bnad_set_mac_address_locked(struct net_device *netdev, void *addr);
+static int bnad_change_mtu(struct net_device *netdev, int new_mtu);
+static int bnad_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
+static void
+bnad_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp);
+static void bnad_vlan_rx_add_vid(struct net_device *netdev, unsigned short vid);
+static void
+bnad_vlan_rx_kill_vid(struct net_device *netdev, unsigned short vid);
+static void bnad_netpoll(struct net_device *netdev);
+
+static const struct net_device_ops bnad_netdev_ops = {
+ .ndo_open = bnad_open,
+ .ndo_stop = bnad_stop,
+ .ndo_start_xmit = bnad_start_xmit,
+ .ndo_get_stats = bnad_get_stats,
+#ifdef HAVE_SET_RX_MODE
+ .ndo_set_rx_mode = &bnad_set_rx_mode,
+#endif
+ .ndo_set_multicast_list = bnad_set_rx_mode,
+ .ndo_set_mac_address = bnad_set_mac_address,
+ .ndo_change_mtu = bnad_change_mtu,
+ .ndo_do_ioctl = bnad_ioctl,
+
+ .ndo_vlan_rx_register = bnad_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = bnad_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = bnad_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = bnad_netpoll,
+#endif
+};
+static int bnad_check_module_params(void)
+{
+ /* bnad_msix */
+ if (bnad_msix && bnad_msix != 1)
+ printk(KERN_WARNING "bna: bnad_msix should be 0 or 1, "
+ "%u is invalid, set bnad_msix to 1\n", bnad_msix);
+
+ /* bnad_small_large_rxbufs */
+ if (bnad_small_large_rxbufs && bnad_small_large_rxbufs != 1)
+ printk(KERN_WARNING "bna: bnad_small_large_rxbufs should be "
+ "0 or 1, %u is invalid, set bnad_small_large_rxbufs to 1\n",
+ bnad_small_large_rxbufs);
+ if (bnad_small_large_rxbufs)
+ bnad_rxqs_per_cq = 2;
+ else
+ bnad_rxqs_per_cq = 1;
+
+ /* bnad_rxqsets_used */
+ if (bnad_rxqsets_used > BNAD_MAX_RXQS / bnad_rxqs_per_cq) {
+ printk(KERN_ERR "bna: the maximum value for bnad_rxqsets_used "
+ "is %u, %u is invalid\n",
+ BNAD_MAX_RXQS / bnad_rxqs_per_cq, bnad_rxqsets_used);
+ return -EINVAL;
+ }
+ if (!BNA_POWER_OF_2(bnad_rxqsets_used)) {
+ printk(KERN_ERR "bna: bnad_rxqsets_used should be power of 2, "
+ "%u is invalid\n", bnad_rxqsets_used);
+ return -EINVAL;
+ }
+ if (bnad_rxqsets_used > (uint)num_online_cpus())
+ printk(KERN_WARNING "bna: set bnad_rxqsets_used (%u) "
+ "larger than number of CPUs (%d) may not be helpful\n",
+ bnad_rxqsets_used, num_online_cpus());
+
+ /* bnad_ipid_mode */
+ if (bnad_ipid_mode && bnad_ipid_mode != 1) {
+ printk(KERN_ERR "bna: bnad_ipid_mode should be 0 or 1, "
+ "%u is invalid\n", bnad_ipid_mode);
+ return -EINVAL;
+ }
+
+ /* bnad_txq_depth */
+ if (bnad_txq_depth > BNAD_MAX_Q_DEPTH) {
+ printk(KERN_ERR "bna: bnad_txq_depth should be <= %u, "
+ "%u is invalid\n", BNAD_MAX_Q_DEPTH, bnad_txq_depth);
+ return -EINVAL;
+ }
+ if (!BNA_POWER_OF_2(bnad_txq_depth)) {
+ printk(KERN_ERR "bna: bnad_txq_depth should be power of 2, "
+ "%u is invalid\n", bnad_txq_depth);
+ return -EINVAL;
+ }
+ if (bnad_txq_depth < BNAD_MIN_Q_DEPTH) {
+ printk(KERN_ERR "bna: bnad_txq_depth should be >= %u, "
+ "%u is invalid\n", BNAD_MIN_Q_DEPTH, bnad_txq_depth);
+ return -EINVAL;
+ }
+
+ /* bnad_rxq_depth */
+ if (bnad_rxq_depth > BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq) {
+ printk(KERN_ERR "bna: bnad_rxq_depth should be <= %u, "
+ "%u is invalid\n", BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq,
+ bnad_rxq_depth);
+ return -EINVAL;
+ }
+ if (!BNA_POWER_OF_2(bnad_rxq_depth)) {
+ printk(KERN_ERR "bna: bnad_rxq_depth should be power of 2, "
+ "%u is invalid\n", bnad_rxq_depth);
+ return -EINVAL;
+ }
+ if (bnad_rxq_depth < BNAD_MIN_Q_DEPTH) {
+ printk(KERN_ERR "bna: bnad_rxq_depth should be >= %u, "
+ "%u is invalid\n", BNAD_MIN_Q_DEPTH, bnad_rxq_depth);
+ return -EINVAL;
+ }
+
+ /* bnad_vlan_strip */
+ if (bnad_vlan_strip && bnad_vlan_strip != 1)
+ printk(KERN_WARNING "bna: bnad_vlan_strip should be 0 or 1, "
+ "%u is invalid, set bnad_vlan_strip to 1\n",
+ bnad_vlan_strip);
+
+ /* bnad_ioc_auto_recover */
+ if (bnad_ioc_auto_recover && bnad_ioc_auto_recover != 1)
+ printk(KERN_WARNING
+ "bna: bnad_ioc_auto_recover should be 0 or 1, "
+ "%u is invalid, set bnad_ioc_auto_recover to 1\n",
+ bnad_ioc_auto_recover);
+
+
+ return 0;
+}
+
+u32 bnad_get_msglevel(struct net_device *netdev)
+{
+ return bnad_log_level;
+}
+
+void bnad_set_msglevel(struct net_device *netdev, u32 msglevel)
+{
+ bnad_log_level = msglevel;
+}
+
+static unsigned int bnad_free_txbufs(struct bnad_txq_info *txqinfo,
+ u16 updated_txq_cons)
+{
+ struct bnad *bnad = txqinfo->bnad;
+ unsigned int sent_packets = 0, sent_bytes = 0;
+ u16 wis, unmap_cons;
+ struct bnad_skb_unmap *unmap_array;
+ struct sk_buff *skb;
+ int i;
+
+ wis = BNAD_Q_INDEX_CHANGE(txqinfo->txq.q.consumer_index,
+ updated_txq_cons, txqinfo->txq.q.q_depth);
+ BNA_ASSERT(wis <=
+ BNA_QE_IN_USE_CNT(&txqinfo->txq.q, txqinfo->txq.q.q_depth));
+ unmap_array = txqinfo->skb_unmap_q.unmap_array;
+ unmap_cons = txqinfo->skb_unmap_q.consumer_index;
+ prefetch(&unmap_array[unmap_cons + 1]);
+ while (wis) {
+ skb = unmap_array[unmap_cons].skb;
+ BNA_ASSERT(skb);
+ unmap_array[unmap_cons].skb = NULL;
+ BNA_ASSERT(wis >=
+ BNAD_TXQ_WI_NEEDED(1 + skb_shinfo(skb)->nr_frags));
+ BNA_ASSERT(((txqinfo->skb_unmap_q.producer_index -
+ unmap_cons) & (txqinfo->skb_unmap_q.q_depth - 1)) >=
+ 1 + skb_shinfo(skb)->nr_frags);
+
+ sent_packets++;
+ sent_bytes += skb->len;
+ wis -= BNAD_TXQ_WI_NEEDED(1 + skb_shinfo(skb)->nr_frags);
+
+ pci_unmap_single(bnad->pcidev,
+ pci_unmap_addr(&unmap_array[unmap_cons], dma_addr),
+ skb_headlen(skb), PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr, 0);
+ BNA_QE_INDX_ADD(unmap_cons, 1, txqinfo->skb_unmap_q.q_depth);
+ prefetch(&unmap_array[unmap_cons + 1]);
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ pci_unmap_page(bnad->pcidev,
+ pci_unmap_addr(&unmap_array[unmap_cons], dma_addr),
+ skb_shinfo(skb)->frags[i].size, PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr,
+ 0);
+ BNA_QE_INDX_ADD(unmap_cons, 1,
+ txqinfo->skb_unmap_q.q_depth);
+ prefetch(&unmap_array[unmap_cons + 1]);
+ }
+ dev_kfree_skb_any(skb);
+ }
+
+ /* Update consumer pointers. */
+ txqinfo->txq.q.consumer_index = updated_txq_cons;
+ txqinfo->skb_unmap_q.consumer_index = unmap_cons;
+ txqinfo->tx_packets += sent_packets;
+ txqinfo->tx_bytes += sent_bytes;
+ return sent_packets;
+}
+
+static int bnad_lro_get_skb_header(struct sk_buff *skb, void **iphdr,
+ void **tcphdr, u64 *hdr_flags, void *priv)
+{
+ struct bna_cq_entry *cmpl = priv;
+ u32 flags = ntohl(cmpl->flags);
+
+ if ((flags & BNA_CQ_EF_IPV4) && (flags & BNA_CQ_EF_TCP)) {
+ skb_reset_network_header(skb);
+ skb_set_transport_header(skb, ip_hdrlen(skb));
+ *iphdr = ip_hdr(skb);
+ *tcphdr = tcp_hdr(skb);
+ *hdr_flags = LRO_IPV4 | LRO_TCP;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static inline void bnad_disable_txrx_irqs(struct bnad *bnad)
+{
+ int i;
+
+ for (i = 0; i < bnad->txq_num; i++) {
+ bna_ib_coalescing_timer_set(bnad->priv,
+ &bnad->txq_table[i].ib, 0);
+ bna_ib_ack(bnad->priv, &bnad->txq_table[i].ib, 0);
+ }
+
+ for (i = 0; i < bnad->cq_num; i++) {
+ bna_ib_coalescing_timer_set(bnad->priv,
+ &bnad->cq_table[i].ib, 0);
+ bna_ib_ack(bnad->priv, &bnad->cq_table[i].ib, 0);
+ }
+}
+
+static inline void bnad_enable_txrx_irqs(struct bnad *bnad)
+{
+ int i;
+
+ spin_lock_irq(&bnad->priv_lock);
+ for (i = 0; i < bnad->txq_num; i++) {
+ bna_ib_coalescing_timer_set(bnad->priv,
+ &bnad->txq_table[i].ib, bnad->tx_coalescing_timeo);
+ bna_ib_ack(bnad->priv, &bnad->txq_table[i].ib, 0);
+ }
+
+ for (i = 0; i < bnad->cq_num; i++) {
+ bna_ib_coalescing_timer_set(bnad->priv,
+ &bnad->cq_table[i].ib,
+ bnad->cq_table[i].rx_coalescing_timeo);
+ bna_ib_ack(bnad->priv, &bnad->cq_table[i].ib, 0);
+ }
+ spin_unlock_irq(&bnad->priv_lock);
+}
+
+static inline void
+bnad_disable_rx_irq(struct bnad *bnad, struct bnad_cq_info *cqinfo)
+{
+ bna_ib_coalescing_timer_set(bnad->priv, &cqinfo->ib, 0);
+ bna_ib_ack(bnad->priv, &cqinfo->ib, 0);
+}
+static inline void
+bnad_enable_rx_irq(struct bnad *bnad, struct bnad_cq_info *cqinfo)
+{
+ spin_lock_irq(&bnad->priv_lock);
+
+ bna_ib_coalescing_timer_set(bnad->priv, &cqinfo->ib,
+ cqinfo->rx_coalescing_timeo);
+ bna_ib_ack(bnad->priv, &cqinfo->ib, 0);
+ spin_unlock_irq(&bnad->priv_lock);
+}
+
+static unsigned int bnad_tx(struct bnad *bnad, struct bnad_txq_info *txqinfo)
+{
+ struct net_device *netdev = bnad->netdev;
+ unsigned int sent;
+
+ if (test_and_set_bit(BNAD_TXQ_FREE_SENT, &txqinfo->flags))
+ return 0;
+
+ DPRINTK(DEBUG, "%s ", netdev->name);
+ DPRINTK(DEBUG, "TxQ hw consumer index %u\n",
+ *txqinfo->hw_consumer_index);
+ sent = bnad_free_txbufs(txqinfo,
+ (u16)(*txqinfo->hw_consumer_index));
+ if (sent) {
+ if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev) &&
+ BNA_Q_FREE_COUNT(&txqinfo->txq) >=
+ BNAD_NETIF_WAKE_THRESHOLD) {
+ netif_wake_queue(netdev);
+ bnad->stats.netif_queue_wakeup++;
+ }
+ bna_ib_ack(bnad->priv, &txqinfo->ib, sent);
+ DPRINTK(DEBUG, "%s ack TxQ IB %u packets\n",
+ netdev->name, sent);
+ } else {
+ bna_ib_ack(bnad->priv, &txqinfo->ib, 0);
+ }
+
+ smp_mb__before_clear_bit();
+ clear_bit(BNAD_TXQ_FREE_SENT, &txqinfo->flags);
+
+ return sent;
+}
+
+static irqreturn_t bnad_msix_tx(int irq, void *data)
+{
+ struct bnad_txq_info *txqinfo = (struct bnad_txq_info *)data;
+ struct bnad *bnad = txqinfo->bnad;
+
+
+ bnad_tx(bnad, txqinfo);
+
+ return IRQ_HANDLED;
+}
+
+static void bnad_alloc_rxbufs(struct bnad_rxq_info *rxqinfo)
+{
+ u16 to_alloc, alloced, unmap_prod, wi_range;
+ struct bnad_skb_unmap *unmap_array;
+ struct bna_rxq_entry *rxent;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+
+ alloced = 0;
+ to_alloc = BNA_QE_FREE_CNT(&rxqinfo->skb_unmap_q,
+ rxqinfo->skb_unmap_q.q_depth);
+
+ unmap_array = rxqinfo->skb_unmap_q.unmap_array;
+ unmap_prod = rxqinfo->skb_unmap_q.producer_index;
+ BNA_RXQ_QPGE_PTR_GET(unmap_prod, &rxqinfo->rxq.q, rxent, wi_range);
+ BNA_ASSERT(wi_range && wi_range <= rxqinfo->rxq.q.q_depth);
+
+ while (to_alloc--) {
+ if (!wi_range) {
+ BNA_RXQ_QPGE_PTR_GET(unmap_prod, &rxqinfo->rxq.q,
+ rxent, wi_range);
+ BNA_ASSERT(wi_range &&
+ wi_range <= rxqinfo->rxq.q.q_depth);
+ }
+#ifdef BNAD_RXBUF_HEADROOM
+ skb = netdev_alloc_skb(rxqinfo->bnad->netdev,
+ rxqinfo->rxq_config.buffer_size + NET_IP_ALIGN);
+#else
+ skb = alloc_skb(rxqinfo->rxq_config.buffer_size + NET_IP_ALIGN,
+ GFP_ATOMIC);
+#endif
+ if (unlikely(!skb)) {
+ rxqinfo->rxbuf_alloc_failed++;
+ goto finishing;
+ }
+#ifndef BNAD_RXBUF_HEADROOM
+ skb->dev = rxqinfo->bnad->netdev;
+#endif
+ skb_reserve(skb, NET_IP_ALIGN);
+ unmap_array[unmap_prod].skb = skb;
+ dma_addr = pci_map_single(rxqinfo->bnad->pcidev, skb->data,
+ rxqinfo->rxq_config.buffer_size, PCI_DMA_FROMDEVICE);
+ pci_unmap_addr_set(&unmap_array[unmap_prod],
+ dma_addr, dma_addr);
+ BNA_SET_DMA_ADDR(dma_addr, &rxent->host_addr);
+ BNA_QE_INDX_ADD(unmap_prod, 1, rxqinfo->skb_unmap_q.q_depth);
+
+ rxent++;
+ wi_range--;
+ alloced++;
+ }
+
+finishing:
+ if (likely(alloced)) {
+ rxqinfo->skb_unmap_q.producer_index = unmap_prod;
+ rxqinfo->rxq.q.producer_index = unmap_prod;
+ smp_mb();
+ bna_rxq_prod_indx_doorbell(&rxqinfo->rxq);
+ }
+}
+
+static inline void bnad_refill_rxq(struct bnad_rxq_info *rxqinfo)
+{
+ if (!test_and_set_bit(BNAD_RXQ_REFILL, &rxqinfo->flags)) {
+ if (BNA_QE_FREE_CNT(&rxqinfo->skb_unmap_q,
+ rxqinfo->skb_unmap_q.q_depth) >>
+ BNAD_RXQ_REFILL_THRESHOLD_SHIFT)
+ bnad_alloc_rxbufs(rxqinfo);
+ smp_mb__before_clear_bit();
+ clear_bit(BNAD_RXQ_REFILL, &rxqinfo->flags);
+ }
+}
+
+static unsigned int
+bnad_poll_cq(struct bnad *bnad, struct bnad_cq_info *cqinfo, int budget)
+{
+ struct bna_cq_entry *cmpl, *next_cmpl;
+ unsigned int wi_range, packets = 0, wis = 0;
+ struct bnad_rxq_info *rxqinfo = NULL;
+ struct bnad_unmap_q *unmap_q;
+ struct sk_buff *skb;
+ u32 flags;
+ struct bna_pkt_rate *pkt_rt = &cqinfo->pkt_rate;
+
+ prefetch(bnad);
+ prefetch(bnad->netdev);
+ cmpl = bna_cq_pg_prod_ptr(&cqinfo->cq, &wi_range);
+ BNA_ASSERT(wi_range && wi_range <= cqinfo->cq.q.q_depth);
+ while (cmpl->valid && packets < budget) {
+ packets++;
+ BNA_UPDATE_PKT_CNT(pkt_rt, ntohs(cmpl->length));
+ rxqinfo = &bnad->rxq_table[cmpl->rxq_id];
+ unmap_q = &rxqinfo->skb_unmap_q;
+ skb = unmap_q->unmap_array[unmap_q->consumer_index].skb;
+ BNA_ASSERT(skb);
+ prefetch(skb->data - NET_IP_ALIGN);
+ unmap_q->unmap_array[unmap_q->consumer_index].skb = NULL;
+ pci_unmap_single(bnad->pcidev,
+ pci_unmap_addr(
+ &unmap_q->unmap_array[unmap_q->consumer_index],
+ dma_addr),
+ rxqinfo->rxq_config.buffer_size, PCI_DMA_FROMDEVICE);
+ BNA_QE_INDX_ADD(unmap_q->consumer_index, 1, unmap_q->q_depth);
+ /* XXX May be bad for performance. */
+ BNA_Q_CI_ADD(&rxqinfo->rxq, 1);
+ wis++;
+ if (likely(--wi_range)) {
+ next_cmpl = cmpl + 1;
+ } else {
+ BNA_Q_PI_ADD(&cqinfo->cq, wis);
+ wis = 0;
+ next_cmpl = bna_cq_pg_prod_ptr(&cqinfo->cq, &wi_range);
+ BNA_ASSERT(wi_range &&
+ wi_range <= cqinfo->cq.q.q_depth);
+ }
+ prefetch(next_cmpl);
+
+ flags = ntohl(cmpl->flags);
+ if (unlikely(flags & (BNA_CQ_EF_MAC_ERROR |
+ BNA_CQ_EF_FCS_ERROR | BNA_CQ_EF_TOO_LONG))) {
+ dev_kfree_skb_any(skb);
+ rxqinfo->rx_packets_with_error++;
+ goto next;
+ }
+
+ skb_put(skb, ntohs(cmpl->length));
+ if (likely(bnad->rx_csum &&
+ (((flags & BNA_CQ_EF_IPV4) &&
+ (flags & BNA_CQ_EF_L3_CKSUM_OK)) ||
+ (flags & BNA_CQ_EF_IPV6)) &&
+ (flags & (BNA_CQ_EF_TCP | BNA_CQ_EF_UDP)) &&
+ (flags & BNA_CQ_EF_L4_CKSUM_OK)))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+
+ rxqinfo->rx_packets++;
+ rxqinfo->rx_bytes += skb->len;
+ skb->protocol = eth_type_trans(skb, bnad->netdev);
+
+ if (bnad->vlangrp && (flags & BNA_CQ_EF_VLAN) &&
+ bnad_vlan_strip) {
+ BNA_ASSERT(cmpl->vlan_tag);
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY
+#ifdef NETIF_F_LRO
+ && (bnad->netdev->features & NETIF_F_LRO)
+#endif
+) {
+ lro_vlan_hwaccel_receive_skb(&cqinfo->lro, skb,
+ bnad->vlangrp, ntohs(cmpl->vlan_tag), cmpl);
+ } else {
+ vlan_hwaccel_receive_skb(skb, bnad->vlangrp,
+ ntohs(cmpl->vlan_tag));
+ }
+
+ } else {
+
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY
+#ifdef NETIF_F_LRO
+ && (bnad->netdev->features & NETIF_F_LRO)
+#endif
+) {
+ lro_receive_skb(&cqinfo->lro, skb, cmpl);
+ } else {
+ netif_receive_skb(skb);
+ }
+
+ }
+
+ bnad->netdev->last_rx = jiffies;
+next:
+ cmpl->valid = 0;
+ cmpl = next_cmpl;
+ }
+
+ lro_flush_all(&cqinfo->lro);
+
+ BNA_Q_PI_ADD(&cqinfo->cq, wis);
+
+ if (likely(rxqinfo)) {
+ bna_ib_ack(bnad->priv, &cqinfo->ib, packets);
+ /* Check the current queue first. */
+ bnad_refill_rxq(rxqinfo);
+
+ /* XXX counters per queue for refill? */
+ if (likely(bnad_small_large_rxbufs)) {
+ /* There are 2 RxQs - small and large buffer queues */
+ unsigned int rxq_id = (rxqinfo->rxq_id ^ 1);
+ bnad_refill_rxq(&bnad->rxq_table[rxq_id]);
+ }
+ } else {
+ bna_ib_ack(bnad->priv, &cqinfo->ib, 0);
+ }
+
+ return packets;
+}
+
+static irqreturn_t bnad_msix_rx(int irq, void *data)
+{
+ struct bnad_cq_info *cqinfo = (struct bnad_cq_info *)data;
+ struct bnad *bnad = cqinfo->bnad;
+
+ if (likely(netif_rx_schedule_prep(bnad->netdev,
+ &cqinfo->napi))) {
+ bnad_disable_rx_irq(bnad, cqinfo);
+ __netif_rx_schedule(bnad->netdev, &cqinfo->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bnad_msix_err_mbox(int irq, void *data)
+{
+ struct net_device *netdev = data;
+ struct bnad *bnad = netdev_priv(netdev);
+ u32 intr_status;
+
+ spin_lock(&bnad->priv_lock);
+ bna_intr_status_get(bnad->priv, &intr_status);
+ if (BNA_IS_MBOX_ERR_INTR(intr_status)) {
+ DPRINTK(DEBUG, "port %d msix err/mbox irq status 0x%x\n",
+ bnad->bna_id, intr_status);
+ bna_mbox_err_handler(bnad->priv, intr_status);
+ } else {
+ DPRINTK(WARNING, "port %d msix err/mbox irq status 0x%x\n",
+ bnad->bna_id, intr_status);
+ }
+ spin_unlock(&bnad->priv_lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bnad_isr(int irq, void *data)
+{
+ struct net_device *netdev = data;
+ struct bnad *bnad = netdev_priv(netdev);
+ u32 intr_status;
+
+ spin_lock(&bnad->priv_lock);
+ bna_intr_status_get(bnad->priv, &intr_status);
+ spin_unlock(&bnad->priv_lock);
+
+ if (!intr_status)
+ return IRQ_NONE;
+
+ DPRINTK(DEBUG, "port %u bnad_isr: 0x%x\n", bnad->bna_id, intr_status);
+ if (BNA_IS_MBOX_ERR_INTR(intr_status)) {
+ spin_lock(&bnad->priv_lock);
+ bna_mbox_err_handler(bnad->priv, intr_status);
+ spin_unlock(&bnad->priv_lock);
+ if (BNA_IS_ERR_INTR(intr_status) ||
+ !BNA_IS_INTX_DATA_INTR(intr_status))
+ goto exit_isr;
+ }
+
+ if (likely(netif_rx_schedule_prep(bnad->netdev,
+ &bnad->cq_table[0].napi))) {
+ bnad_disable_txrx_irqs(bnad);
+ __netif_rx_schedule(bnad->netdev, &bnad->cq_table[0].napi);
+ }
+
+exit_isr:
+ return IRQ_HANDLED;
+}
+
+static int bnad_request_mbox_irq(struct bnad *bnad)
+{
+ int err;
+
+ if (bnad->flags & BNAD_F_MSIX) {
+ DPRINTK(DEBUG,
+ "port %u requests IRQ %u for mailbox in MSI-X mode\n",
+ bnad->bna_id,
+ bnad->msix_table[bnad->msix_num - 1].vector);
+ err = request_irq(bnad->msix_table[bnad->msix_num - 1].vector,
+ (irq_handler_t)&bnad_msix_err_mbox, 0, bnad->netdev->name,
+ bnad->netdev);
+ } else {
+ DPRINTK(DEBUG, "port %u requests IRQ %u in INTx mode\n",
+ bnad->bna_id, bnad->pcidev->irq);
+ err = request_irq(bnad->pcidev->irq, (irq_handler_t)&bnad_isr,
+ IRQF_SHARED, bnad->netdev->name, bnad->netdev);
+ }
+
+ if (err) {
+ dev_err(&bnad->pcidev->dev,
+ "Request irq for mailbox failed: %d\n", err);
+ return err;
+ }
+
+ if (bnad->flags & BNAD_F_MSIX)
+ bna_mbox_msix_idx_set(bnad->priv, bnad->msix_num - 1);
+
+ bna_mbox_intr_enable(bnad->priv);
+ return 0;
+}
+
+
+static void bnad_sync_mbox_irq(struct bnad *bnad)
+{
+ uint irq;
+
+ if (bnad->flags & BNAD_F_MSIX)
+ irq = bnad->msix_table[bnad->msix_num - 1].vector;
+ else
+ irq = bnad->pcidev->irq;
+ synchronize_irq(irq);
+}
+
+static void bnad_free_mbox_irq(struct bnad *bnad)
+{
+ uint irq;
+
+ if (bnad->flags & BNAD_F_MSIX)
+ irq = bnad->msix_table[bnad->msix_num - 1].vector;
+ else
+ irq = bnad->pcidev->irq;
+
+ spin_lock_irq(&bnad->priv_lock);
+ bna_mbox_intr_disable(bnad->priv);
+ spin_unlock_irq(&bnad->priv_lock);
+ free_irq(irq, bnad->netdev);
+}
+
+static int bnad_request_txq_irq(struct bnad *bnad, uint txq_id)
+{
+ BNA_ASSERT(txq_id < bnad->txq_num);
+ if (!(bnad->flags & BNAD_F_MSIX))
+ return 0;
+ DPRINTK(DEBUG, "port %u requests irq %u for TxQ %u in MSIX mode\n",
+ bnad->bna_id, bnad->msix_table[txq_id].vector, txq_id);
+ return request_irq(bnad->msix_table[txq_id].vector,
+ (irq_handler_t)&bnad_msix_tx, 0, bnad->txq_table[txq_id].name,
+ &bnad->txq_table[txq_id]);
+}
+
+int bnad_request_cq_irq(struct bnad *bnad, uint cq_id)
+{
+ BNA_ASSERT(cq_id < bnad->cq_num);
+ if (!(bnad->flags & BNAD_F_MSIX))
+ return 0;
+ DPRINTK(DEBUG, "port %u requests irq %u for CQ %u in MSIX mode\n",
+ bnad->bna_id,
+ bnad->msix_table[bnad->txq_num + cq_id].vector, cq_id);
+ return request_irq(bnad->msix_table[bnad->txq_num + cq_id].vector,
+ (irq_handler_t)&bnad_msix_rx, 0, bnad->cq_table[cq_id].name,
+ &bnad->cq_table[cq_id]);
+}
+
+static void bnad_intx_enable_txrx(struct bnad *bnad)
+{
+ u32 mask;
+ int i;
+
+ spin_lock_irq(&bnad->priv_lock);
+ bna_intx_disable(bnad->priv, &mask);
+ mask &= ~0xffff;
+ bna_intx_enable(bnad->priv, mask);
+ for (i = 0; i < bnad->ib_num; i++)
+ bna_ib_ack(bnad->priv, bnad->ib_table[i].ib, 0);
+ spin_unlock_irq(&bnad->priv_lock);
+}
+
+static int bnad_request_txrx_irqs(struct bnad *bnad)
+{
+ struct msix_entry *entries;
+ int i;
+ int err;
+
+ if (!(bnad->flags & BNAD_F_MSIX)) {
+ bnad_intx_enable_txrx(bnad);
+ return 0;
+ }
+
+ entries = bnad->msix_table;
+ for (i = 0; i < bnad->txq_num; i++) {
+ err = bnad_request_txq_irq(bnad, i);
+ if (err) {
+ printk(KERN_ERR "%s request irq for TxQ %d failed %d\n",
+ bnad->netdev->name, i, err);
+ while (--i >= 0) {
+ free_irq(entries[i].vector,
+ &bnad->txq_table[i]);
+ }
+ return err;
+ }
+ }
+
+ for (i = 0; i < bnad->cq_num; i++) {
+ err = bnad_request_cq_irq(bnad, i);
+ if (err) {
+ printk(KERN_ERR "%s request irq for CQ %u failed %d\n",
+ bnad->netdev->name, i, err);
+ while (--i >= 0) {
+ free_irq(entries[bnad->txq_num + i].vector,
+ &bnad->cq_table[i]);
+ }
+ goto free_txq_irqs;
+ }
+ }
+
+ return 0;
+
+free_txq_irqs:
+ for (i = 0; i < bnad->txq_num; i++)
+ free_irq(entries[i].vector, &bnad->txq_table[i]);
+
+ bnad_disable_msix(bnad);
+
+ return err;
+}
+
+static void bnad_free_txrx_irqs(struct bnad *bnad)
+{
+ struct msix_entry *entries;
+ uint i;
+
+ if (bnad->flags & BNAD_F_MSIX) {
+ entries = bnad->msix_table;
+ for (i = 0; i < bnad->txq_num; i++)
+ free_irq(entries[i].vector, &bnad->txq_table[i]);
+
+ for (i = 0; i < bnad->cq_num; i++)
+ free_irq(entries[bnad->txq_num + i].vector,
+ &bnad->cq_table[i]);
+ } else {
+ synchronize_irq(bnad->pcidev->irq);
+ }
+}
+
+void bnad_setup_ib(struct bnad *bnad, uint ib_id)
+{
+ struct bnad_ib_entry *ib_entry;
+
+ BNA_ASSERT(ib_id < bnad->ib_num);
+ ib_entry = &bnad->ib_table[ib_id];
+ spin_lock_irq(&bnad->priv_lock);
+ bna_ib_config_set(bnad->priv, ib_entry->ib, ib_id,
+ &ib_entry->ib_config);
+ /* Start the IB */
+ bna_ib_ack(bnad->priv, ib_entry->ib, 0);
+ spin_unlock_irq(&bnad->priv_lock);
+}
+
+static void bnad_setup_ibs(struct bnad *bnad)
+{
+ int i;
+
+ for (i = 0; i < bnad->txq_num; i++)
+ bnad_setup_ib(bnad, bnad->txq_table[i].txq_config.ib_id);
+
+ for (i = 0; i < bnad->cq_num; i++)
+ bnad_setup_ib(bnad, bnad->cq_table[i].cq_config.ib_id);
+}
+
+/* These functions are called back with priv_lock held. */
+
+static void bnad_lldp_get_cfg_cb(void *arg, u8 status)
+{
+ struct bnad *bnad = arg;
+ bnad->lldp_comp_status = status;
+ complete(&bnad->lldp_comp);
+}
+
+static void bnad_cee_get_attr_cb(void *arg, bfa_status_t status)
+{
+ struct bnad *bnad = arg;
+ bnad->lldp_comp_status = status;
+ complete(&bnad->lldp_comp);
+}
+
+static void bnad_cee_get_stats_cb(void *arg, bfa_status_t status)
+{
+ struct bnad *bnad = arg;
+ bnad->cee_stats_comp_status = status;
+ complete(&bnad->cee_stats_comp);
+}
+
+static void bnad_cee_reset_stats_cb(void *arg, bfa_status_t status)
+{
+ struct bnad *bnad = arg;
+ bnad->cee_reset_stats_status = status;
+ complete(&bnad->cee_reset_stats_comp);
+}
+
+static void bnad_ucast_set_cb(void *arg, u8 status)
+{
+ struct bnad *bnad = (struct bnad *)arg;
+
+ bnad->ucast_comp_status = status;
+ complete(&bnad->ucast_comp);
+}
+
+static void bnad_q_stop_cb(void *arg, u8 status)
+{
+ struct bnad *bnad = arg;
+
+ bnad->qstop_comp_status = status;
+ complete(&bnad->qstop_comp);
+}
+
+static void bnad_link_up_cb(void *arg, u8 status)
+{
+ struct bnad *bnad = (struct bnad *)arg;
+ struct net_device *netdev = bnad->netdev;
+
+ DPRINTK(INFO, "%s bnad_link_up_cb\n", netdev->name);
+ if (netif_running(netdev)) {
+ if (!netif_carrier_ok(netdev) &&
+ !test_bit(BNAD_DISABLED, &bnad->state)) {
+ printk(KERN_INFO "%s link up\n", netdev->name);
+ netif_carrier_on(netdev);
+ netif_wake_queue(netdev);
+ bnad->stats.netif_queue_wakeup++;
+ }
+ }
+}
+
+static void bnad_link_down_cb(void *arg, u8 status)
+{
+ struct bnad *bnad = (struct bnad *)arg;
+ struct net_device *netdev = bnad->netdev;
+
+ DPRINTK(INFO, "%s bnad_link_down_cb\n", netdev->name);
+ if (netif_running(netdev)) {
+ if (netif_carrier_ok(netdev)) {
+ printk(KERN_INFO "%s link down\n", netdev->name);
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ bnad->stats.netif_queue_stop++;
+ }
+ }
+}
+
+static void bnad_stats_get_cb(void *arg, u8 status)
+{
+ struct bnad *bnad = (struct bnad *)arg;
+
+ bnad->stats.hw_stats_updates++;
+ if (!test_bit(BNAD_DISABLED, &bnad->state))
+ mod_timer(&bnad->stats_timer, jiffies + HZ);
+}
+
+/* Diagnostics */
+static void bnad_set_diag_lb_cb(void *arg, u8 status)
+{
+ struct bnad_diag_lb_params *dlbp =
+ (struct bnad_diag_lb_params *)arg;
+
+ dlbp->diag_lb_comp_status = status;
+ DPRINTK(INFO, "bnad_set_diag_lb_cb() for %s %d\n",
+ dlbp->bnad->netdev->name, status);
+ complete(&dlbp->diag_lb_comp);
+}
+
+/* Called with bnad priv_lock held. */
+static void bnad_hw_error(struct bnad *bnad, u8 status)
+{
+ unsigned int irq;
+
+ bna_mbox_intr_disable(bnad->priv);
+ if (bnad->flags & BNAD_F_MSIX) {
+ if (!test_and_set_bit(BNAD_MBOX_IRQ_DISABLED, &bnad->state)) {
+ irq = bnad->msix_table[bnad->txq_num +
+ bnad->cq_num].vector;
+ DPRINTK(WARNING, "Disabling Mbox IRQ %d for port %d\n",
+ irq, bnad->bna_id);
+ disable_irq_nosync(irq);
+ }
+ }
+
+ bna_cleanup(bnad->priv);
+ bnad->work_flags = BNAD_WF_ERROR;
+ if (!test_bit(BNAD_REMOVED, &bnad->state))
+ schedule_work(&bnad->work);
+}
+
+static void bnad_hw_error_cb(void *arg, u8 status)
+{
+ struct bnad *bnad = (struct bnad *)arg;
+
+ DPRINTK(WARNING, "port %d HW error callback %u\n",
+ bnad->bna_id, status);
+
+ bnad_hw_error(bnad, status);
+}
+
+int bnad_alloc_unmap_q(struct bnad_unmap_q *unmap_q, u32 q_depth)
+{
+ /* Q_depth must be power of 2 for macros to work. */
+ BNA_ASSERT(BNA_POWER_OF_2(q_depth));
+ unmap_q->q_depth = q_depth;
+ unmap_q->unmap_array = vmalloc(q_depth *
+ sizeof(struct bnad_skb_unmap));
+ if (!unmap_q->unmap_array)
+ return -ENOMEM;
+ memset(unmap_q->unmap_array, 0,
+ q_depth * sizeof(struct bnad_skb_unmap));
+ return 0;
+}
+
+static int bnad_alloc_unmap_queues(struct bnad *bnad)
+{
+ int i, err = 0;
+ struct bnad_txq_info *txqinfo;
+ struct bnad_rxq_info *rxqinfo;
+
+ for (i = 0; i < bnad->txq_num; i++) {
+ txqinfo = &bnad->txq_table[i];
+ err = bnad_alloc_unmap_q(&txqinfo->skb_unmap_q,
+ txqinfo->txq.q.q_depth * 4);
+ DPRINTK(DEBUG, "%s allocating Tx unmap Q %d depth %u\n",
+ bnad->netdev->name, i, txqinfo->txq.q.q_depth * 4);
+ if (err) {
+ DPRINTK(ERR, "%s allocating Tx unmap Q %d failed: %d\n",
+ bnad->netdev->name, i, err);
+ return err;
+ }
+ }
+ for (i = 0; i < bnad->rxq_num; i++) {
+ rxqinfo = &bnad->rxq_table[i];
+ err = bnad_alloc_unmap_q(&rxqinfo->skb_unmap_q,
+ rxqinfo->rxq.q.q_depth);
+ DPRINTK(INFO, "%s allocating Rx unmap Q %d depth %u\n",
+ bnad->netdev->name, i, rxqinfo->rxq.q.q_depth);
+ if (err) {
+ DPRINTK(ERR, "%s allocating Rx unmap Q %d failed: %d\n",
+ bnad->netdev->name, i, err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* Called with priv_lock. */
+static void bnad_flush_rxbufs(struct bnad_rxq_info *rxqinfo)
+{
+ struct bnad *bnad = rxqinfo->bnad;
+ struct bnad_unmap_q *unmap_q;
+ struct sk_buff *skb;
+ u32 cq_id;
+
+ unmap_q = &rxqinfo->skb_unmap_q;
+ while (BNA_QE_IN_USE_CNT(unmap_q, unmap_q->q_depth)) {
+ skb = unmap_q->unmap_array[unmap_q->consumer_index].skb;
+ BNA_ASSERT(skb);
+ unmap_q->unmap_array[unmap_q->consumer_index].skb = NULL;
+ pci_unmap_single(bnad->pcidev,
+ pci_unmap_addr(
+ &unmap_q->unmap_array[unmap_q->consumer_index], dma_addr),
+ rxqinfo->rxq_config.buffer_size + NET_IP_ALIGN,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(skb);
+ BNA_QE_INDX_ADD(unmap_q->consumer_index, 1, unmap_q->q_depth);
+ BNA_Q_CI_ADD(&rxqinfo->rxq, 1);
+ }
+
+ BNAD_RESET_Q(bnad, &rxqinfo->rxq.q, &rxqinfo->skb_unmap_q);
+ cq_id = rxqinfo->rxq_id / bnad_rxqs_per_cq;
+ *bnad->cq_table[cq_id].hw_producer_index = 0;
+}
+
+static int bnad_disable_txq(struct bnad *bnad, u32 txq_id)
+{
+ struct bnad_txq_info *txqinfo;
+ int err;
+
+ WARN_ON(in_interrupt());
+
+ init_completion(&bnad->qstop_comp);
+ txqinfo = &bnad->txq_table[txq_id];
+ spin_lock_irq(&bnad->priv_lock);
+ err = bna_txq_stop(bnad->priv, txq_id);
+ spin_unlock_irq(&bnad->priv_lock);
+ if (err)
+ goto txq_stop_exit;
+
+ DPRINTK(INFO, "Waiting for %s TxQ %d stop reply\n",
+ bnad->netdev->name, txq_id);
+ wait_for_completion(&bnad->qstop_comp);
+
+ err = bnad->qstop_comp_status;
+txq_stop_exit:
+ if (err)
+ DPRINTK(ERR, "%s bna_txq_stop %d failed %d\n",
+ bnad->netdev->name, txq_id, err);
+ return err;
+}
+
+int bnad_disable_rxqs(struct bnad *bnad, u64 rxq_id_mask)
+{
+ int err;
+
+ struct timeval tv;
+
+ BNA_ASSERT(!in_interrupt());
+
+ init_completion(&bnad->qstop_comp);
+
+ spin_lock_irq(&bnad->priv_lock);
+ do_gettimeofday(&tv);
+ DPRINTK(DEBUG, "Calling bna_multi_rxq_stop at %ld:%ld\n",
+ tv.tv_sec, tv.tv_usec);
+ err = bna_multi_rxq_stop(bnad->priv, rxq_id_mask);
+ spin_unlock_irq(&bnad->priv_lock);
+ if (err)
+ goto rxq_stop_exit;
+
+ DPRINTK(INFO, "Waiting for %s RxQs(0x%llx) stop reply\n",
+ bnad->netdev->name, rxq_id_mask);
+ wait_for_completion(&bnad->qstop_comp);
+
+ do_gettimeofday(&tv);
+ DPRINTK(DEBUG, "bna_multi_rxq_stop returned at %ld:%ld\n",
+ tv.tv_sec, tv.tv_usec);
+ err = bnad->qstop_comp_status;
+rxq_stop_exit:
+ if (err)
+ DPRINTK(ERR, "%s bna_multi_rxq_stop(0x%llx) failed %d\n",
+ bnad->netdev->name, rxq_id_mask, err);
+ return err;
+
+}
+
+static int bnad_poll_rx(struct napi_struct *napi, int budget)
+{
+ struct bnad_cq_info *cqinfo =
+ container_of(napi, struct bnad_cq_info, napi);
+ struct bnad *bnad = cqinfo->bnad;
+ unsigned int rcvd;
+
+ rcvd = bnad_poll_cq(bnad, cqinfo, budget);
+ if (rcvd == budget)
+ return rcvd;
+ netif_rx_complete(bnad->netdev, napi);
+ bnad->stats.netif_rx_complete++;
+ bnad_enable_rx_irq(bnad, cqinfo);
+ return rcvd;
+}
+
+static int bnad_poll_txrx(struct napi_struct *napi, int budget)
+{
+ struct bnad_cq_info *cqinfo =
+ container_of(napi, struct bnad_cq_info, napi);
+ struct bnad *bnad = cqinfo->bnad;
+ unsigned int rcvd;
+
+ bnad_tx(bnad, &bnad->txq_table[0]);
+ rcvd = bnad_poll_cq(bnad, cqinfo, budget);
+ if (rcvd == budget)
+ return rcvd;
+ netif_rx_complete(bnad->netdev, napi);
+ bnad->stats.netif_rx_complete++;
+ bnad_enable_txrx_irqs(bnad);
+ return rcvd;
+}
+
+static void bnad_napi_init(struct bnad *bnad)
+{
+ int (*napi_poll)(struct napi_struct *, int);
+ int i;
+
+ if (bnad->flags & BNAD_F_MSIX)
+ napi_poll = bnad_poll_rx;
+ else
+ napi_poll = bnad_poll_txrx;
+
+ for (i = 0; i < bnad->cq_num; i++)
+ netif_napi_add(bnad->netdev, &bnad->cq_table[i].napi,
+ napi_poll, 64);
+}
+
+static void bnad_napi_enable(struct bnad *bnad)
+{
+ int i;
+
+ for (i = 0; i < bnad->cq_num; i++)
+ napi_enable(&bnad->cq_table[i].napi);
+}
+
+static void bnad_napi_disable(struct bnad *bnad)
+{
+ int i;
+
+ for (i = 0; i < bnad->cq_num; i++)
+ napi_disable(&bnad->cq_table[i].napi);
+}
+
+static void bnad_napi_uninit(struct bnad *bnad)
+{
+ int i;
+
+ for (i = 0; i < bnad->cq_num; i++)
+ netif_napi_del(&bnad->cq_table[i].napi);
+}
+
+
+static void bnad_detach(struct bnad *bnad)
+{
+ int i;
+
+ ASSERT_RTNL();
+
+ spin_lock_irq(&bnad->priv_lock);
+ if (!test_bit(BNAD_RESETTING, &bnad->state)) {
+ /* Graceful detach */
+
+ bna_txf_disable(bnad->priv, BNAD_TX_FUNC_ID);
+ bna_multi_rxf_disable(bnad->priv, (1 << bnad->rxf_num) - 1);
+ for (i = 0; i < bnad->txq_num; i++)
+ bna_ib_disable(bnad->priv, &bnad->txq_table[i].ib);
+ for (i = 0; i < bnad->cq_num; i++)
+ bna_ib_disable(bnad->priv, &bnad->cq_table[i].ib);
+ } else {
+ /* Error */
+ /* XXX Should not write to registers if RESETTING. */
+
+ bna_txf_disable(bnad->priv, BNAD_TX_FUNC_ID);
+ bna_rxf_disable_old(bnad->priv, BNAD_RX_FUNC_ID);
+
+ for (i = 0; i < bnad->txq_num; i++)
+ bna_ib_disable(bnad->priv, &bnad->txq_table[i].ib);
+ for (i = 0; i < bnad->cq_num; i++)
+ bna_ib_disable(bnad->priv, &bnad->cq_table[i].ib);
+ }
+ spin_unlock_irq(&bnad->priv_lock);
+
+ /* Wait to make sure Tx and Rx are stopped. */
+ msleep(1000);
+ bnad_free_txrx_irqs(bnad);
+ bnad_sync_mbox_irq(bnad);
+
+ bnad_napi_disable(bnad);
+ bnad_napi_uninit(bnad);
+
+ /* Delete the stats timer after synchronize with mbox irq. */
+ del_timer_sync(&bnad->stats_timer);
+ netif_tx_disable(bnad->netdev);
+ netif_carrier_off(bnad->netdev);
+}
+
+static int bnad_disable(struct bnad *bnad)
+{
+ int err, i;
+ u64 rxq_id_mask = 0;
+
+ ASSERT_RTNL();
+ DPRINTK(INFO, "bring %s link down\n", bnad->netdev->name);
+ spin_lock_irq(&bnad->priv_lock);
+ bna_port_admin(bnad->priv, BNA_DISABLE);
+ spin_unlock_irq(&bnad->priv_lock);
+
+ bnad_detach(bnad);
+
+ for (i = 0; i < bnad->txq_num; i++) {
+ err = bnad_disable_txq(bnad, i);
+ if (err)
+ return err;
+ }
+
+ for (i = 0; i < bnad->rxq_num; i++)
+ rxq_id_mask |= (1 << i);
+ if (rxq_id_mask) {
+ err = bnad_disable_rxqs(bnad, rxq_id_mask);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int bnad_sw_reset(struct net_device *netdev)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ int err;
+
+ if (!netif_running(bnad->netdev))
+ return 0;
+
+ err = bnad_stop_locked(netdev);
+ if (err) {
+ DPRINTK(WARNING, "%s sw reset: disable failed %d\n",
+ bnad->netdev->name, err);
+ /* Recoverable */
+ return 0;
+ }
+
+ err = bnad_open_locked(netdev);
+ if (err) {
+ DPRINTK(WARNING, "%s sw reset: enable failed %d\n",
+ bnad->netdev->name, err);
+ return err;
+ }
+
+ return 0;
+}
+
+int bnad_resetting(struct bnad *bnad)
+{
+ rtnl_lock();
+ if (netif_running(bnad->netdev))
+ bnad_stop_locked(bnad->netdev);
+ set_bit(BNAD_RESETTING, &bnad->state);
+ rtnl_unlock();
+ return 0;
+}
+
+int bnad_alloc_ib(struct bnad *bnad, uint ib_id)
+{
+ struct bnad_ib_entry *ib_entry;
+ dma_addr_t dma_addr;
+
+ BNA_ASSERT(bnad->ib_table && ib_id < bnad->ib_num);
+ ib_entry = &bnad->ib_table[ib_id];
+ ib_entry->ib_seg_addr = pci_alloc_consistent(bnad->pcidev,
+ L1_CACHE_BYTES, &dma_addr);
+ if (!ib_entry->ib_seg_addr)
+ return -ENOMEM;
+ DPRINTK(DEBUG, "%s IB %d dma addr 0x%llx\n",
+ bnad->netdev->name, ib_id, dma_addr);
+
+ BNA_SET_DMA_ADDR(dma_addr, &ib_entry->ib_config.ib_seg_addr);
+ return 0;
+}
+static int bnad_alloc_ibs(struct bnad *bnad)
+{
+ uint i;
+ int err;
+
+ bnad->ib_num = bnad->txq_num + bnad->cq_num;
+ bnad->ib_table = kzalloc(bnad->ib_num *
+ sizeof(struct bnad_ib_entry), GFP_KERNEL);
+ if (!bnad->ib_table)
+ return -ENOMEM;
+
+ for (i = 0; i < bnad->ib_num; i++) {
+ err = bnad_alloc_ib(bnad, i);
+ if (err)
+ goto free_ibs;
+ }
+ return 0;
+
+free_ibs:
+ bnad_free_ibs(bnad);
+ return err;
+}
+
+void bnad_free_ib(struct bnad *bnad, uint ib_id)
+{
+ struct bnad_ib_entry *ib_entry;
+ dma_addr_t dma_addr;
+
+ BNA_ASSERT(bnad->ib_table && ib_id < bnad->ib_num);
+ ib_entry = &bnad->ib_table[ib_id];
+ if (ib_entry->ib_seg_addr) {
+ BNA_GET_DMA_ADDR(&ib_entry->ib_config.ib_seg_addr, dma_addr);
+ pci_free_consistent(bnad->pcidev, L1_CACHE_BYTES,
+ ib_entry->ib_seg_addr, dma_addr);
+ ib_entry->ib_seg_addr = NULL;
+ }
+}
+
+static void bnad_free_ibs(struct bnad *bnad)
+{
+ uint i;
+
+ if (!bnad->ib_table)
+ return;
+
+ for (i = 0; i < bnad->ib_num; i++)
+ bnad_free_ib(bnad, i);
+ kfree(bnad->ib_table);
+ bnad->ib_table = NULL;
+}
+
+/* Let the caller deal with error - free memory. */
+static int bnad_alloc_q(struct bnad *bnad, struct bna_qpt *qpt,
+ struct bna_q *q, size_t qsize)
+{
+ size_t i;
+ dma_addr_t dma_addr;
+
+ qsize = ALIGN(qsize, PAGE_SIZE);
+ qpt->page_count = qsize >> PAGE_SHIFT;
+ qpt->page_size = PAGE_SIZE;
+
+ DPRINTK(DEBUG, "qpt page count 0x%x, ", qpt->page_count);
+ DPRINTK(DEBUG, "page size 0x%x\n", qpt->page_size);
+
+ qpt->kv_qpt_ptr = pci_alloc_consistent(bnad->pcidev,
+ qpt->page_count * sizeof(struct bna_dma_addr), &dma_addr);
+ if (!qpt->kv_qpt_ptr)
+ return -ENOMEM;
+ BNA_SET_DMA_ADDR(dma_addr, &qpt->hw_qpt_ptr);
+ DPRINTK(DEBUG, "qpt host addr %p, ", qpt->kv_qpt_ptr);
+ DPRINTK(DEBUG, "dma addr 0x%llx\n", dma_addr);
+
+ q->qpt_ptr = kzalloc(qpt->page_count * sizeof(void *), GFP_KERNEL);
+ if (!q->qpt_ptr)
+ return -ENOMEM;
+ qpt->qpt_ptr = q->qpt_ptr;
+ for (i = 0; i < qpt->page_count; i++) {
+ q->qpt_ptr[i] = pci_alloc_consistent(bnad->pcidev, PAGE_SIZE,
+ &dma_addr);
+ if (!q->qpt_ptr[i])
+ return -ENOMEM;
+ BNA_SET_DMA_ADDR(dma_addr,
+ &((struct bna_dma_addr *)qpt->kv_qpt_ptr)[i]);
+
+ DPRINTK(DEBUG, "page %d ", (int)i);
+ DPRINTK(DEBUG, "host addr %p, ", q->qpt_ptr[i]);
+ DPRINTK(DEBUG, "dma addr 0x%llx\n", dma_addr);
+ }
+
+ return 0;
+}
+
+static void
+bnad_free_q(struct bnad *bnad, struct bna_qpt *qpt, struct bna_q *q)
+{
+ int i;
+ dma_addr_t dma_addr;
+
+ if (qpt->kv_qpt_ptr && q->qpt_ptr) {
+ for (i = 0; i < qpt->page_count; i++) {
+ if (q->qpt_ptr[i]) {
+ BNA_GET_DMA_ADDR(
+ &((struct bna_dma_addr *)
+ qpt->kv_qpt_ptr)[i], dma_addr);
+ pci_free_consistent(bnad->pcidev, PAGE_SIZE,
+ q->qpt_ptr[i], dma_addr);
+ }
+ }
+ }
+
+ kfree(q->qpt_ptr);
+ qpt->qpt_ptr = q->qpt_ptr = NULL;
+
+ if (qpt->kv_qpt_ptr) {
+ BNA_GET_DMA_ADDR(&qpt->hw_qpt_ptr, dma_addr);
+ pci_free_consistent(bnad->pcidev,
+ qpt->page_count * sizeof(struct bna_dma_addr),
+ qpt->kv_qpt_ptr, dma_addr);
+ qpt->kv_qpt_ptr = NULL;
+ }
+}
+
+static void bnad_free_txq(struct bnad *bnad, uint txq_id)
+{
+ struct bnad_txq_info *txqinfo;
+
+ BNA_ASSERT(bnad->txq_table && txq_id < bnad->txq_num);
+ txqinfo = &bnad->txq_table[txq_id];
+ bnad_free_q(bnad, &txqinfo->txq_config.qpt, &txqinfo->txq.q);
+ if (txqinfo->skb_unmap_q.unmap_array) {
+ bnad_free_txbufs(txqinfo, txqinfo->txq.q.producer_index);
+ vfree(txqinfo->skb_unmap_q.unmap_array);
+ txqinfo->skb_unmap_q.unmap_array = NULL;
+ }
+}
+
+void bnad_free_rxq(struct bnad *bnad, uint rxq_id)
+{
+ struct bnad_rxq_info *rxqinfo;
+
+ BNA_ASSERT(bnad->rxq_table && rxq_id < bnad->rxq_num);
+ rxqinfo = &bnad->rxq_table[rxq_id];
+ bnad_free_q(bnad, &rxqinfo->rxq_config.qpt, &rxqinfo->rxq.q);
+ if (rxqinfo->skb_unmap_q.unmap_array) {
+ bnad_flush_rxbufs(rxqinfo);
+ vfree(rxqinfo->skb_unmap_q.unmap_array);
+ rxqinfo->skb_unmap_q.unmap_array = NULL;
+ }
+}
+
+void bnad_free_cq(struct bnad *bnad, uint cq_id)
+{
+ struct bnad_cq_info *cqinfo = &bnad->cq_table[cq_id];
+
+ BNA_ASSERT(bnad->cq_table && cq_id < bnad->cq_num);
+ bnad_free_q(bnad, &cqinfo->cq_config.qpt, &cqinfo->cq.q);
+ vfree(cqinfo->lro.lro_arr);
+ cqinfo->lro.lro_arr = NULL;
+}
+
+static void bnad_free_queues(struct bnad *bnad)
+{
+ uint i;
+
+ if (bnad->txq_table) {
+ for (i = 0; i < bnad->txq_num; i++)
+ bnad_free_txq(bnad, i);
+ kfree(bnad->txq_table);
+ bnad->txq_table = NULL;
+ }
+
+ if (bnad->rxq_table) {
+ for (i = 0; i < bnad->rxq_num; i++)
+ bnad_free_rxq(bnad, i);
+ kfree(bnad->rxq_table);
+ bnad->rxq_table = NULL;
+ }
+
+ if (bnad->cq_table) {
+ for (i = 0; i < bnad->cq_num; i++)
+ bnad_free_cq(bnad, i);
+ kfree(bnad->cq_table);
+ bnad->cq_table = NULL;
+ }
+}
+
+static int bnad_txq_init(struct bnad *bnad, uint txq_id)
+{
+ struct bnad_txq_info *txqinfo;
+ int err;
+
+ BNA_ASSERT(bnad->txq_table && txq_id < bnad->txq_num);
+ txqinfo = &bnad->txq_table[txq_id];
+ DPRINTK(DEBUG, "%s allocating TxQ %d\n", bnad->netdev->name, txq_id);
+ err = bnad_alloc_q(bnad, &txqinfo->txq_config.qpt, &txqinfo->txq.q,
+ bnad->txq_depth * sizeof(struct bna_txq_entry));
+ if (err) {
+ bnad_free_q(bnad, &txqinfo->txq_config.qpt, &txqinfo->txq.q);
+ return err;
+ }
+ txqinfo->txq.q.q_depth = bnad->txq_depth;
+ txqinfo->bnad = bnad;
+ txqinfo->txq_config.txf_id = BNAD_TX_FUNC_ID;
+ snprintf(txqinfo->name, sizeof(txqinfo->name), "%s TxQ %d",
+ bnad->netdev->name, txq_id);
+ return 0;
+}
+
+static int bnad_txqs_init(struct bnad *bnad)
+{
+ int i, err = 0;
+
+ bnad->txq_table = kzalloc(bnad->txq_num *
+ sizeof(struct bnad_txq_info), GFP_KERNEL);
+ if (!bnad->txq_table)
+ return -ENOMEM;
+
+ for (i = 0; i < bnad->txq_num; i++) {
+ err = bnad_txq_init(bnad, i);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+int bnad_rxq_init(struct bnad *bnad, uint rxq_id)
+{
+ struct bnad_rxq_info *rxqinfo;
+ int err;
+
+ BNA_ASSERT(bnad->rxq_table && rxq_id < bnad->rxq_num);
+ rxqinfo = &bnad->rxq_table[rxq_id];
+ DPRINTK(DEBUG, "%s allocating RxQ %d\n", bnad->netdev->name, rxq_id);
+ err = bnad_alloc_q(bnad, &rxqinfo->rxq_config.qpt, &rxqinfo->rxq.q,
+ bnad->rxq_depth * sizeof(struct bna_rxq_entry));
+ if (err) {
+ bnad_free_q(bnad, &rxqinfo->rxq_config.qpt, &rxqinfo->rxq.q);
+ return err;
+ }
+ rxqinfo->rxq.q.q_depth = bnad->rxq_depth;
+ rxqinfo->bnad = bnad;
+ rxqinfo->rxq_id = rxq_id;
+ rxqinfo->rxq_config.cq_id = rxq_id / bnad_rxqs_per_cq;
+
+ return 0;
+}
+
+static int bnad_rxqs_init(struct bnad *bnad)
+{
+ int i, err = 0;
+
+ bnad->rxq_table = kzalloc(bnad->rxq_num *
+ sizeof(struct bnad_rxq_info), GFP_KERNEL);
+ if (!bnad->rxq_table)
+ return -EINVAL;
+
+ for (i = 0; i < bnad->rxq_num; i++) {
+ err = bnad_rxq_init(bnad, i);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+int bnad_cq_init(struct bnad *bnad, uint cq_id)
+{
+ struct bnad_cq_info *cqinfo;
+ int err;
+
+ BNA_ASSERT(bnad->cq_table && cq_id < bnad->cq_num);
+ cqinfo = &bnad->cq_table[cq_id];
+ DPRINTK(DEBUG, "%s allocating CQ %d\n", bnad->netdev->name, cq_id);
+ err = bnad_alloc_q(bnad, &cqinfo->cq_config.qpt, &cqinfo->cq.q,
+ bnad->rxq_depth * bnad_rxqs_per_cq * sizeof(struct bna_cq_entry));
+ if (err) {
+ bnad_free_q(bnad, &cqinfo->cq_config.qpt, &cqinfo->cq.q);
+ return err;
+ }
+
+ cqinfo->cq.q.q_depth = bnad->rxq_depth * bnad_rxqs_per_cq;
+ cqinfo->bnad = bnad;
+
+ cqinfo->lro.dev = bnad->netdev;
+ cqinfo->lro.features |= LRO_F_NAPI;
+ if (bnad_vlan_strip)
+ cqinfo->lro.features |= LRO_F_EXTRACT_VLAN_ID;
+ cqinfo->lro.ip_summed = CHECKSUM_UNNECESSARY;
+ cqinfo->lro.ip_summed_aggr = CHECKSUM_UNNECESSARY;
+ cqinfo->lro.max_desc = BNAD_LRO_MAX_DESC;
+ cqinfo->lro.max_aggr = BNAD_LRO_MAX_AGGR;
+ /* XXX */
+ cqinfo->lro.frag_align_pad = 0;
+ cqinfo->lro.lro_arr = vmalloc(BNAD_LRO_MAX_DESC *
+ sizeof(struct net_lro_desc));
+ if (!cqinfo->lro.lro_arr) {
+ bnad_free_q(bnad, &cqinfo->cq_config.qpt, &cqinfo->cq.q);
+ return err;
+ }
+ memset(cqinfo->lro.lro_arr, 0, BNAD_LRO_MAX_DESC *
+ sizeof(struct net_lro_desc));
+ cqinfo->lro.get_skb_header = bnad_lro_get_skb_header;
+
+ cqinfo->rx_coalescing_timeo = bnad->rx_coalescing_timeo;
+
+ cqinfo->cq_id = cq_id;
+ snprintf(cqinfo->name, sizeof(cqinfo->name), "%s CQ %d",
+ bnad->netdev->name, cq_id);
+
+ return 0;
+}
+
+static int bnad_cqs_init(struct bnad *bnad)
+{
+ int i, err = 0;
+
+ bnad->cq_table = kzalloc(bnad->cq_num * sizeof(struct bnad_cq_info),
+ GFP_KERNEL);
+ if (!bnad->cq_table)
+ return -ENOMEM;
+
+ for (i = 0; i < bnad->cq_num; i++) {
+ err = bnad_cq_init(bnad, i);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+static uint bnad_get_qsize(uint qsize_conf, uint mtu)
+{
+ uint qsize;
+
+ if (mtu > ETH_DATA_LEN) {
+ qsize = qsize_conf / (mtu / ETH_DATA_LEN);
+ if (!BNA_POWER_OF_2(qsize))
+ BNA_TO_POWER_OF_2_HIGH(qsize);
+ if (qsize < BNAD_MIN_Q_DEPTH)
+ qsize = BNAD_MIN_Q_DEPTH;
+ } else
+ qsize = bnad_txq_depth;
+
+ return qsize;
+}
+
+static int bnad_init_queues(struct bnad *bnad)
+{
+ int err;
+
+ if (!(bnad->flags & BNAD_F_TXQ_DEPTH))
+ bnad->txq_depth = bnad_get_qsize(bnad_txq_depth,
+ bnad->netdev->mtu);
+ if (!(bnad->flags & BNAD_F_RXQ_DEPTH))
+ bnad->rxq_depth = bnad_get_qsize(bnad_rxq_depth,
+ bnad->netdev->mtu);
+
+ err = bnad_txqs_init(bnad);
+ if (err)
+ return err;
+
+ err = bnad_rxqs_init(bnad);
+ if (err)
+ return err;
+
+ err = bnad_cqs_init(bnad);
+
+ return err;
+}
+
+void bnad_rxib_init(struct bnad *bnad, uint cq_id, uint ib_id)
+{
+ struct bnad_cq_info *cqinfo;
+ struct bnad_ib_entry *ib_entry;
+ struct bna_ib_config *ib_config;
+
+ BNA_ASSERT(cq_id < bnad->cq_num && ib_id < bnad->ib_num);
+ cqinfo = &bnad->cq_table[cq_id];
+ ib_entry = &bnad->ib_table[ib_id];
+
+ cqinfo->hw_producer_index = (u32 *)(ib_entry->ib_seg_addr);
+ cqinfo->cq_config.ib_id = ib_id;
+ cqinfo->cq_config.ib_seg_index = 0;
+
+ ib_entry->ib = &cqinfo->ib;
+ ib_config = &ib_entry->ib_config;
+ ib_config->coalescing_timer = bnad->rx_coalescing_timeo;
+#if 1
+ ib_config->control_flags = BNA_IB_CF_INT_ENABLE |
+ BNA_IB_CF_MASTER_ENABLE;
+#else
+ ib_config->control_flags = BNA_IB_CF_INT_ENABLE |
+ BNA_IB_CF_INTER_PKT_ENABLE | BNA_IB_CF_MASTER_ENABLE;
+ ib_config->interpkt_count = bnad->rx_interpkt_count;
+ ib_config->interpkt_timer = bnad->rx_interpkt_timeo;
+#endif
+ if (bnad->flags & BNAD_F_MSIX) {
+ ib_config->control_flags |= BNA_IB_CF_MSIX_MODE;
+ ib_config->msix_vector = ib_id;
+ } else
+ ib_config->msix_vector = 1 << ib_id;
+
+ /* Every CQ has its own IB. */
+ ib_config->seg_size = 1;
+ ib_config->index_table_offset = ib_id;
+}
+
+static void bnad_ibs_init(struct bnad *bnad)
+{
+ struct bnad_ib_entry *ib_entry;
+ struct bna_ib_config *ib_config;
+ struct bnad_txq_info *txqinfo;
+
+ int ib_id, i;
+
+ ib_id = 0;
+ for (i = 0; i < bnad->txq_num; i++) {
+ txqinfo = &bnad->txq_table[i];
+ ib_entry = &bnad->ib_table[ib_id];
+
+ txqinfo->hw_consumer_index = ib_entry->ib_seg_addr;
+ txqinfo->txq_config.ib_id = ib_id;
+ txqinfo->txq_config.ib_seg_index = 0;
+
+ ib_entry->ib = &txqinfo->ib;
+ ib_config = &ib_entry->ib_config;
+ ib_config->coalescing_timer = bnad->tx_coalescing_timeo;
+ ib_config->control_flags = BNA_IB_CF_INTER_PKT_DMA |
+ BNA_IB_CF_INT_ENABLE | BNA_IB_CF_COALESCING_MODE |
+ BNA_IB_CF_MASTER_ENABLE;
+ if (bnad->flags & BNAD_F_MSIX) {
+ ib_config->control_flags |= BNA_IB_CF_MSIX_MODE;
+ ib_config->msix_vector = ib_id;
+ } else
+ ib_config->msix_vector = 1 << ib_id;
+ ib_config->interpkt_count = bnad->tx_interpkt_count;
+
+ /* Every TxQ has its own IB. */
+ ib_config->seg_size = 1;
+ ib_config->index_table_offset = ib_id;
+ ib_id++;
+ }
+
+ for (i = 0; i < bnad->cq_num; i++, ib_id++)
+ bnad_rxib_init(bnad, i, ib_id);
+}
+
+static void bnad_txf_init(struct bnad *bnad, uint txf_id)
+{
+ struct bnad_txf_info *txf_info;
+
+ BNA_ASSERT(bnad->txf_table && txf_id < bnad->txf_num);
+ txf_info = &bnad->txf_table[txf_id];
+ txf_info->txf_id = txf_id;
+ txf_info->txf_config.flags = BNA_TXF_CF_VLAN_WI_BASED |
+ BNA_TXF_CF_ENABLE;
+}
+
+void bnad_rxf_init(struct bnad *bnad, uint rxf_id, u8 rit_offset, int rss)
+{
+ struct bnad_rxf_info *rxf_info;
+
+ BNA_ASSERT(bnad->rxf_table && rxf_id < bnad->rxf_num);
+ rxf_info = &bnad->rxf_table[rxf_id];
+ rxf_info->rxf_id = rxf_id;
+ rxf_info->rxf_config.rit_offset = rit_offset;
+ rxf_info->rxf_config.mcast_rxq_id = BNAD_MULTICAST_RXQ_ID;
+ if (bnad_small_large_rxbufs)
+ rxf_info->rxf_config.flags |= BNA_RXF_CF_SM_LG_RXQ;
+ if (bnad_vlan_strip)
+ rxf_info->rxf_config.flags |= BNA_RXF_CF_VLAN_STRIP;
+ if (rss) {
+ struct bna_rxf_rss *rxf_rss;
+
+ rxf_info->rxf_config.flags |= BNA_RXF_CF_RSS_ENABLE;
+ rxf_rss = &rxf_info->rxf_config.rss;
+ rxf_rss->type = BNA_RSS_V4_TCP | BNA_RSS_V4_IP |
+ BNA_RSS_V6_TCP | BNA_RSS_V6_IP;
+ rxf_rss->hash_mask = bnad->cq_num - 1;
+ get_random_bytes(rxf_rss->toeplitz_hash_key,
+ sizeof(rxf_rss->toeplitz_hash_key));
+ }
+ DPRINTK(DEBUG, "%s RxF %u config flags 0x%x\n",
+ bnad->netdev->name, rxf_id, rxf_info->rxf_config.flags);
+}
+
+static int bnad_init_funcs(struct bnad *bnad)
+{
+ bnad->txf_table = kzalloc(sizeof(struct bnad_txf_info) * bnad->txf_num,
+ GFP_KERNEL);
+ if (!bnad->txf_table)
+ return -ENOMEM;
+ bnad_txf_init(bnad, BNAD_TX_FUNC_ID);
+
+ bnad->rxf_table = kzalloc(sizeof(struct bnad_rxf_info) * bnad->rxf_num,
+ GFP_KERNEL);
+ if (!bnad->rxf_table)
+ return -ENOMEM;
+ bnad_rxf_init(bnad, BNAD_RX_FUNC_ID, BNAD_RIT_OFFSET,
+ (bnad->cq_num > 1) ? 1 : 0);
+ return 0;
+}
+
+static void bnad_setup_txq(struct bnad *bnad, uint txq_id)
+{
+ struct bnad_txq_info *txqinfo;
+
+ BNA_ASSERT(txq_id < bnad->txq_num);
+ txqinfo = &bnad->txq_table[txq_id];
+ txqinfo->txq_config.priority = txq_id;
+ /* Set wrr_quota properly if multiple priorities/TxQs are enabled. */
+ txqinfo->txq_config.wrr_quota = BNAD_TX_MAX_WRR_QUOTA;
+
+ spin_lock_irq(&bnad->priv_lock);
+ bna_txq_config(bnad->priv, &txqinfo->txq, txq_id,
+ &txqinfo->txq_config);
+ spin_unlock_irq(&bnad->priv_lock);
+}
+
+void bnad_setup_rxq(struct bnad *bnad, uint rxq_id)
+{
+ struct bnad_rxq_info *rxqinfo;
+
+ BNA_ASSERT(rxq_id < bnad->rxq_num);
+ rxqinfo = &bnad->rxq_table[rxq_id];
+ /*
+ * Every RxQ set has 2 RxQs: the first is large buffer RxQ,
+ * the second is small buffer RxQ.
+ */
+ if ((rxq_id % bnad_rxqs_per_cq) == 0)
+ rxqinfo->rxq_config.buffer_size =
+ (bnad_vlan_strip ? VLAN_ETH_HLEN : ETH_HLEN) +
+ bnad->netdev->mtu + ETH_FCS_LEN;
+ else
+ rxqinfo->rxq_config.buffer_size = BNAD_SMALL_RXBUF_SIZE;
+
+ spin_lock_irq(&bnad->priv_lock);
+ bna_rxq_config(bnad->priv, &rxqinfo->rxq, rxq_id,
+ &rxqinfo->rxq_config);
+ spin_unlock_irq(&bnad->priv_lock);
+}
+
+void bnad_setup_cq(struct bnad *bnad, uint cq_id)
+{
+ struct bnad_cq_info *cqinfo;
+
+ BNA_ASSERT(cq_id < bnad->cq_num);
+ cqinfo = &bnad->cq_table[cq_id];
+ spin_lock_irq(&bnad->priv_lock);
+ bna_cq_config(bnad->priv, &cqinfo->cq, cq_id,
+ &cqinfo->cq_config);
+ spin_unlock_irq(&bnad->priv_lock);
+}
+
+static void bnad_setup_queues(struct bnad *bnad)
+{
+ uint i;
+
+ for (i = 0; i < bnad->txq_num; i++)
+ bnad_setup_txq(bnad, i);
+
+ for (i = 0; i < bnad->rxq_num; i++)
+ bnad_setup_rxq(bnad, i);
+
+ for (i = 0; i < bnad->cq_num; i++)
+ bnad_setup_cq(bnad, i);
+}
+
+
+static void bnad_setup_rit(struct bnad *bnad)
+{
+ int i, size;
+
+ size = bnad->cq_num;
+
+ for (i = 0; i < size; i++) {
+ if (bnad_small_large_rxbufs) {
+ bnad->rit[i].large_rxq_id = (i << 1);
+ bnad->rit[i].small_rxq_id = (i << 1) + 1;
+ } else
+ bnad->rit[i].large_rxq_id = i;
+ }
+
+ spin_lock_irq(&bnad->priv_lock);
+ bna_rit_config_set(bnad->priv, BNAD_RIT_OFFSET,
+ bnad->rit, size);
+ spin_unlock_irq(&bnad->priv_lock);
+}
+
+void bnad_alloc_for_rxq(struct bnad *bnad, uint rxq_id)
+{
+ struct bnad_rxq_info *rxqinfo = &bnad->rxq_table[rxq_id];
+ u16 rxbufs;
+
+ BNA_ASSERT(bnad->rxq_table && rxq_id < bnad->rxq_num);
+ bnad_alloc_rxbufs(rxqinfo);
+ rxbufs = BNA_QE_IN_USE_CNT(&rxqinfo->skb_unmap_q,
+ rxqinfo->skb_unmap_q.q_depth);
+ DPRINTK(INFO, "%s allocated %u rx buffers for RxQ %u\n",
+ bnad->netdev->name, rxbufs, rxq_id);
+}
+
+static int bnad_config_hw(struct bnad *bnad)
+{
+ int i, err;
+ u64 rxq_id_mask = 0;
+ struct sockaddr sa;
+ struct net_device *netdev = bnad->netdev;
+
+ spin_lock_irq(&bnad->priv_lock);
+ /* Disable the RxF until later bringing port up. */
+ bna_multi_rxf_disable(bnad->priv, (1 << bnad->rxf_num) - 1);
+ spin_unlock_irq(&bnad->priv_lock);
+ for (i = 0; i < bnad->txq_num; i++) {
+ err = bnad_disable_txq(bnad, i);
+ if (err)
+ return err;
+ }
+ for (i = 0; i < bnad->rxq_num; i++)
+ rxq_id_mask |= (1 << i);
+ if (rxq_id_mask) {
+ err = bnad_disable_rxqs(bnad, rxq_id_mask);
+ if (err)
+ return err;
+ }
+
+ bnad_setup_queues(bnad);
+
+ bnad_setup_rit(bnad);
+
+ spin_lock_irq(&bnad->priv_lock);
+ bna_txf_config_set(bnad->priv, BNAD_TX_FUNC_ID,
+ &bnad->txf_table->txf_config);
+ for (i = 0; i < bnad->rxf_num; i++) {
+ bna_rxf_config_set(bnad->priv, i,
+ &bnad->rxf_table[i].rxf_config);
+ bna_rxf_vlan_filter(bnad->priv, i, BNA_ENABLE);
+ }
+ spin_unlock_irq(&bnad->priv_lock);
+
+ /* Mailbox should be enabled before this! */
+ memcpy(sa.sa_data, netdev->dev_addr, netdev->addr_len);
+ bnad_set_mac_address_locked(netdev, &sa);
+
+ spin_lock_irq(&bnad->priv_lock);
+ /* Receive broadcasts */
+ bna_rxf_broadcast(bnad->priv, BNAD_RX_FUNC_ID, BNA_ENABLE);
+
+ bna_mtu_info(bnad->priv, netdev->mtu, bnad);
+ bna_set_pause_config(bnad->priv, &bnad->pause_config, bnad);
+
+ bna_rxf_mcast_del_all(bnad->priv, BNAD_RX_FUNC_ID);
+ bna_mcast_mac_reset_list(bnad->priv);
+ spin_unlock_irq(&bnad->priv_lock);
+
+ bnad_set_rx_mode_locked(bnad->netdev);
+
+ bnad_reconfig_vlans(bnad);
+
+ bnad_setup_ibs(bnad);
+
+ return 0;
+}
+
+/* Note: bnad_cleanup doesn't not free irqs and queues. */
+static void bnad_cleanup(struct bnad *bnad)
+{
+ kfree(bnad->rit);
+ bnad->rit = NULL;
+ kfree(bnad->txf_table);
+ bnad->txf_table = NULL;
+ kfree(bnad->rxf_table);
+ bnad->rxf_table = NULL;
+
+ bnad_free_ibs(bnad);
+ bnad_free_queues(bnad);
+}
+
+/* Should be called with rtnl_lock held. */
+static int bnad_start(struct bnad *bnad)
+{
+ int err;
+
+ ASSERT_RTNL();
+
+ err = bnad_alloc_ibs(bnad);
+ if (err)
+ return err;
+
+ err = bnad_init_queues(bnad);
+ if (err)
+ goto finished;
+
+ bnad_ibs_init(bnad);
+
+ err = bnad_init_funcs(bnad);
+ if (err)
+ goto finished;
+
+ err = bnad_alloc_unmap_queues(bnad);
+ if (err)
+ goto finished;
+
+ bnad->rit = kzalloc(bnad->cq_num * sizeof(struct bna_rit_entry),
+ GFP_KERNEL);
+
+ if (!bnad->rit)
+ goto finished;
+
+ err = bnad_config_hw(bnad);
+ if (err)
+ goto finished;
+
+ bnad_napi_init(bnad);
+ bnad_napi_enable(bnad);
+
+ err = bnad_request_txrx_irqs(bnad);
+ if (err) {
+ DPRINTK(ERR, "%s requests Tx/Rx irqs failed: %d\n",
+ bnad->netdev->name, err);
+ goto finished;
+ }
+ return 0;
+
+finished:
+ bnad_cleanup(bnad);
+ return err;
+}
+
+int bnad_open_locked(struct net_device *netdev)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ uint i;
+ int err;
+
+ ASSERT_RTNL();
+ DPRINTK(WARNING, "%s open\n", netdev->name);
+
+ if (BNAD_NOT_READY(bnad)) {
+ DPRINTK(WARNING, "%s is not ready yet (0x%lx)\n",
+ netdev->name, bnad->state);
+ return 0;
+ }
+
+ if (!test_bit(BNAD_DISABLED, &bnad->state)) {
+ DPRINTK(WARNING, "%s is already opened (0x%lx)\n",
+ netdev->name, bnad->state);
+
+ return 0;
+ }
+
+ err = bnad_start(bnad);
+ if (err) {
+ DPRINTK(ERR, "%s failed to start %d\n", netdev->name, err);
+ return err;
+ }
+ for (i = 0; i < bnad->rxq_num; i++)
+ bnad_alloc_for_rxq(bnad, i);
+
+ smp_mb__before_clear_bit();
+ clear_bit(BNAD_DISABLED, &bnad->state);
+ DPRINTK(INFO, "%s is opened\n", bnad->netdev->name);
+
+ /* XXX Packet may be come before we bring the port up. */
+ spin_lock_irq(&bnad->priv_lock);
+
+ /* RxF was disabled earlier. */
+ bna_rxf_enable(bnad->priv, BNAD_RX_FUNC_ID);
+ spin_unlock_irq(&bnad->priv_lock);
+
+
+ DPRINTK(INFO, "Bring %s link up\n", netdev->name);
+ spin_lock_irq(&bnad->priv_lock);
+ bna_port_admin(bnad->priv, BNA_ENABLE);
+ spin_unlock_irq(&bnad->priv_lock);
+
+ mod_timer(&bnad->stats_timer, jiffies + HZ);
+
+ return 0;
+}
+
+int bnad_stop_locked(struct net_device *netdev)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+
+ ASSERT_RTNL();
+ DPRINTK(WARNING, "%s stop\n", netdev->name);
+
+ if (test_and_set_bit(BNAD_DISABLED, &bnad->state)) {
+ if (BNAD_NOT_READY(bnad))
+ DPRINTK(WARNING, "%s is not ready (0x%lx)\n",
+ netdev->name, bnad->state);
+ else
+ DPRINTK(WARNING, "%s is already stopped (0x%lx)\n",
+ netdev->name, bnad->state);
+ return 0;
+ }
+
+ bnad_disable(bnad);
+ bnad_cleanup(bnad);
+ DPRINTK(INFO, "%s is stopped\n", bnad->netdev->name);
+ return 0;
+}
+
+int bnad_open(struct net_device *netdev)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ int error = 0;
+
+ bnad_lock();
+ if (!test_bit(BNAD_PORT_DISABLED, &bnad->state))
+ error = bnad_open_locked(netdev);
+ bnad_unlock();
+ return error;
+}
+
+int bnad_stop(struct net_device *netdev)
+{
+ int error = 0;
+
+ bnad_lock();
+ error = bnad_stop_locked(netdev);
+ bnad_unlock();
+ return error;
+}
+
+static int bnad_tso_prepare(struct bnad *bnad, struct sk_buff *skb)
+{
+#ifdef NETIF_F_TSO
+ int err;
+
+#ifdef SKB_GSO_TCPV4
+ /* SKB_GSO_TCPV4 and SKB_GSO_TCPV6 is defined since 2.6.18. */
+ BNA_ASSERT(skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4 ||
+ skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6);
+#endif
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (err) {
+ bnad->stats.tso_err++;
+ return err;
+ }
+ }
+
+ /*
+ * For TSO, the TCP checksum field is seeded with pseudo-header sum
+ * excluding the length field.
+ */
+ if (skb->protocol == htons(ETH_P_IP)) {
+ struct iphdr *iph = ip_hdr(skb);
+
+ /* Do we really need these? */
+ iph->tot_len = 0;
+ iph->check = 0;
+
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(
+ iph->saddr, iph->daddr, 0, IPPROTO_TCP, 0);
+ bnad->stats.tso4++;
+#ifdef NETIF_F_TSO6
+ } else {
+ struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+
+ BNA_ASSERT(skb->protocol == htons(ETH_P_IPV6));
+ ipv6h->payload_len = 0;
+ tcp_hdr(skb)->check = ~csum_ipv6_magic(
+ &ipv6h->saddr, &ipv6h->daddr, 0, IPPROTO_TCP, 0);
+ bnad->stats.tso6++;
+#endif
+ }
+
+ return 0;
+#else
+ return -EINVAL;
+#endif
+}
+
+int bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ struct bnad_txq_info *txqinfo;
+ struct bna_txq *txq;
+ struct bnad_unmap_q *unmap_q;
+ u16 txq_prod;
+ unsigned int unmap_prod, wis, wis_used, wi_range;
+ unsigned int vectors, vect_id, i, acked;
+ int err;
+ dma_addr_t dma_addr;
+ struct bna_txq_entry *txqent;
+ bna_txq_wi_ctrl_flag_t flags;
+
+ if (unlikely(skb->len <= ETH_HLEN ||
+ skb->len > BNAD_TX_MAX_DATA_PER_WI)) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ txqinfo = &bnad->txq_table[0];
+ txq = &txqinfo->txq;
+ unmap_q = &txqinfo->skb_unmap_q;
+
+ vectors = 1 + skb_shinfo(skb)->nr_frags;
+ if (vectors > BNAD_TX_MAX_VECTORS) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+ wis = BNAD_TXQ_WI_NEEDED(vectors); /* 4 vectors per work item */
+ acked = 0;
+ if (unlikely(wis > BNA_Q_FREE_COUNT(txq) ||
+ vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
+ if ((u16)(*txqinfo->hw_consumer_index) !=
+ txq->q.consumer_index &&
+ !test_and_set_bit(BNAD_TXQ_FREE_SENT, &txqinfo->flags)) {
+ acked = bnad_free_txbufs(txqinfo,
+ (u16)(*txqinfo->hw_consumer_index));
+ bna_ib_ack(bnad->priv, &txqinfo->ib, acked);
+ DPRINTK(DEBUG, "%s ack TxQ IB %u packets\n",
+ netdev->name, acked);
+ smp_mb__before_clear_bit();
+ clear_bit(BNAD_TXQ_FREE_SENT, &txqinfo->flags);
+ } else {
+ netif_stop_queue(netdev);
+ }
+
+ smp_mb();
+ /*
+ * Check again to deal with race condition between
+ * netif_stop_queue here, and netif_wake_queue in
+ * interrupt handler which is not inside netif tx lock.
+ */
+ if (likely(wis > BNA_Q_FREE_COUNT(txq) ||
+ vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
+ bnad->stats.netif_queue_stop++;
+ return NETDEV_TX_BUSY;
+ } else {
+ netif_wake_queue(netdev);
+ }
+ }
+
+ unmap_prod = unmap_q->producer_index;
+ wis_used = 1;
+ vect_id = 0;
+ flags = 0;
+
+ txq_prod = txq->q.producer_index;
+ BNA_TXQ_QPGE_PTR_GET(txq_prod, &txq->q, txqent, wi_range);
+ BNA_ASSERT(wi_range && wi_range <= txq->q.q_depth);
+ txqent->hdr.wi.reserved = 0;
+ txqent->hdr.wi.num_vectors = vectors;
+ txqent->hdr.wi.opcode = htons((skb_is_gso(skb) ?
+ BNA_TXQ_WI_SEND_LSO : BNA_TXQ_WI_SEND));
+
+ if (bnad_ipid_mode)
+ flags |= BNA_TXQ_WI_CF_IPID_MODE;
+
+ if (bnad->vlangrp && vlan_tx_tag_present(skb)) {
+ u16 vlan_tag = (u16)vlan_tx_tag_get(skb);
+ if ((vlan_tag >> 13) & 0x7)
+ flags |= BNA_TXQ_WI_CF_INS_PRIO;
+ if (vlan_tag & VLAN_VID_MASK)
+ flags |= BNA_TXQ_WI_CF_INS_VLAN;
+ txqent->hdr.wi.vlan_tag = htons(vlan_tag);
+ } else
+ txqent->hdr.wi.vlan_tag = 0;
+
+ if (skb_is_gso(skb)) {
+ err = bnad_tso_prepare(bnad, skb);
+ if (err) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+ txqent->hdr.wi.lso_mss = htons(skb_is_gso(skb));
+ flags |= (BNA_TXQ_WI_CF_IP_CKSUM | BNA_TXQ_WI_CF_TCP_CKSUM);
+ txqent->hdr.wi.l4_hdr_size_n_offset = htons(
+ BNA_TXQ_WI_L4_HDR_N_OFFSET(tcp_hdrlen(skb) >> 2,
+ skb_transport_offset(skb)));
+ } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ u8 proto = 0;
+
+ txqent->hdr.wi.lso_mss = 0;
+
+ if (skb->protocol == htons(ETH_P_IP))
+ proto = ip_hdr(skb)->protocol;
+#ifdef NETIF_F_IPV6_CSUM
+ else if (skb->protocol == htons(ETH_P_IPV6)) {
+ /* XXX the nexthdr may not be TCP immediately. */
+ proto = ipv6_hdr(skb)->nexthdr;
+ }
+#endif
+ if (proto == IPPROTO_TCP) {
+ flags |= BNA_TXQ_WI_CF_TCP_CKSUM;
+ txqent->hdr.wi.l4_hdr_size_n_offset = htons(
+ BNA_TXQ_WI_L4_HDR_N_OFFSET(0,
+ skb_transport_offset(skb)));
+ bnad->stats.tcpcsum_offload++;
+ BNA_ASSERT(skb_headlen(skb) >=
+ skb_transport_offset(skb) + tcp_hdrlen(skb));
+ } else if (proto == IPPROTO_UDP) {
+ flags |= BNA_TXQ_WI_CF_UDP_CKSUM;
+ txqent->hdr.wi.l4_hdr_size_n_offset = htons(
+ BNA_TXQ_WI_L4_HDR_N_OFFSET(0,
+ skb_transport_offset(skb)));
+ bnad->stats.udpcsum_offload++;
+ BNA_ASSERT(skb_headlen(skb) >=
+ skb_transport_offset(skb) + sizeof(struct udphdr));
+ } else {
+ err = skb_checksum_help(skb);
+ bnad->stats.csum_help++;
+ if (err) {
+ dev_kfree_skb(skb);
+ bnad->stats.csum_help_err++;
+ return NETDEV_TX_OK;
+ }
+ }
+ } else {
+ txqent->hdr.wi.lso_mss = 0;
+ txqent->hdr.wi.l4_hdr_size_n_offset = 0;
+ }
+
+ txqent->hdr.wi.flags = htons(flags);
+
+ txqent->hdr.wi.frame_length = htonl(skb->len);
+
+ unmap_q->unmap_array[unmap_prod].skb = skb;
+ BNA_ASSERT(skb_headlen(skb) <= BNAD_TX_MAX_DATA_PER_VECTOR);
+ txqent->vector[vect_id].length = htons(skb_headlen(skb));
+ dma_addr = pci_map_single(bnad->pcidev, skb->data, skb_headlen(skb),
+ PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
+ dma_addr);
+ BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
+ BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+
+ if (++vect_id == BNAD_TX_MAX_VECTORS_PER_WI) {
+ vect_id = 0;
+ if (--wi_range)
+ txqent++;
+ else {
+ BNA_QE_INDX_ADD(txq_prod, wis_used,
+ txq->q.q_depth);
+ wis_used = 0;
+ BNA_TXQ_QPGE_PTR_GET(txq_prod, &txq->q, txqent,
+ wi_range);
+ BNA_ASSERT(wi_range &&
+ wi_range <= txq->q.q_depth);
+ }
+ wis_used++;
+ txqent->hdr.wi_ext.opcode = htons(BNA_TXQ_WI_EXTENSION);
+ }
+
+ BNA_ASSERT(frag->size <= BNAD_TX_MAX_DATA_PER_VECTOR);
+ txqent->vector[vect_id].length = htons(frag->size);
+ BNA_ASSERT(unmap_q->unmap_array[unmap_prod].skb == NULL);
+ dma_addr = pci_map_page(bnad->pcidev, frag->page,
+ frag->page_offset, frag->size, PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
+ dma_addr);
+ BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
+ BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+ }
+
+ unmap_q->producer_index = unmap_prod;
+ BNA_QE_INDX_ADD(txq_prod, wis_used, txq->q.q_depth);
+ txq->q.producer_index = txq_prod;
+
+ smp_mb();
+ bna_txq_prod_indx_doorbell(txq);
+ netdev->trans_start = jiffies;
+
+ if ((u16)(*txqinfo->hw_consumer_index) !=
+ txq->q.consumer_index &&
+ !test_and_set_bit(BNAD_TXQ_FREE_SENT, &txqinfo->flags)) {
+ acked = bnad_free_txbufs(txqinfo,
+ (u16)(*txqinfo->hw_consumer_index));
+ bna_ib_ack(bnad->priv, &txqinfo->ib, acked);
+ smp_mb__before_clear_bit();
+ clear_bit(BNAD_TXQ_FREE_SENT, &txqinfo->flags);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+struct net_device_stats *bnad_get_stats(struct net_device *netdev)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ struct net_device_stats *net_stats = &bnad->net_stats;
+ struct cna_stats_mac_rx *rxstats = &bnad->hw_stats->mac_rx_stats;
+ struct cna_stats_mac_tx *txstats = &bnad->hw_stats->mac_tx_stats;
+ int i;
+
+ memset(net_stats, 0, sizeof(*net_stats));
+ if (bnad->rxq_table) {
+ for (i = 0; i < bnad->rxq_num; i++) {
+ net_stats->rx_packets += bnad->rxq_table[i].rx_packets;
+ net_stats->rx_bytes += bnad->rxq_table[i].rx_bytes;
+ }
+ }
+ if (bnad->txq_table) {
+ for (i = 0; i < bnad->txq_num; i++) {
+ net_stats->tx_packets += bnad->txq_table[i].tx_packets;
+ net_stats->tx_bytes += bnad->txq_table[i].tx_bytes;
+ }
+ }
+ net_stats->rx_errors = rxstats->rx_fcs_error +
+ rxstats->rx_alignment_error + rxstats->rx_frame_length_error +
+ rxstats->rx_code_error + rxstats->rx_undersize;
+ net_stats->tx_errors = txstats->tx_fcs_error + txstats->tx_undersize;
+ net_stats->rx_dropped = rxstats->rx_drop;
+ net_stats->tx_dropped = txstats->tx_drop;
+ net_stats->multicast = rxstats->rx_multicast;
+ net_stats->collisions = txstats->tx_total_collision;
+
+ net_stats->rx_length_errors = rxstats->rx_frame_length_error;
+ net_stats->rx_crc_errors = rxstats->rx_fcs_error;
+ net_stats->rx_frame_errors = rxstats->rx_alignment_error;
+ /* recv'r fifo overrun */
+ net_stats->rx_fifo_errors =
+ bnad->hw_stats->rxf_stats[0].frame_drops;
+
+ return net_stats;
+}
+
+void bnad_reset_stats(struct net_device *netdev)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ struct bnad_rxq_info *rxqinfo;
+ struct bnad_txq_info *txqinfo;
+ int i;
+ memset(&bnad->stats, 0, sizeof(bnad->stats));
+
+ if (bnad->rxq_table) {
+ for (i = 0; i < bnad->rxq_num; i++) {
+ rxqinfo = &bnad->rxq_table[i];
+ rxqinfo->rx_packets = 0;
+ rxqinfo->rx_bytes = 0;
+ rxqinfo->rx_packets_with_error = 0;
+ rxqinfo->rxbuf_alloc_failed = 0;
+ }
+ }
+ if (bnad->txq_table) {
+ for (i = 0; i < bnad->txq_num; i++) {
+ txqinfo = &bnad->txq_table[i];
+ txqinfo->tx_packets = 0;
+ txqinfo->tx_bytes = 0;
+ }
+ }
+}
+
+static void bnad_set_rx_mode_locked(struct net_device *netdev)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ int err;
+ unsigned long irq_flags;
+
+ if (BNAD_NOT_READY(bnad))
+ return;
+
+ spin_lock_irqsave(&bnad->priv_lock, irq_flags);
+ if (netdev->flags & IFF_PROMISC) {
+ if (!(bnad->flags & BNAD_F_PROMISC)) {
+ bna_rxf_promiscuous(bnad->priv,
+ BNAD_RX_FUNC_ID, BNA_ENABLE);
+ bnad->flags |= BNAD_F_PROMISC;
+ }
+ } else {
+ if (bnad->flags & BNAD_F_PROMISC) {
+ bna_rxf_promiscuous(bnad->priv,
+ BNAD_RX_FUNC_ID, BNA_DISABLE);
+ bnad->flags &= ~BNAD_F_PROMISC;
+ }
+ }
+
+ if (netdev->flags & IFF_ALLMULTI) {
+ if (!(bnad->flags & BNAD_F_ALLMULTI)) {
+ bna_rxf_mcast_filter(bnad->priv,
+ BNAD_RX_FUNC_ID, BNA_DISABLE);
+ bnad->flags |= BNAD_F_ALLMULTI;
+ }
+ } else {
+ if (bnad->flags & BNAD_F_ALLMULTI) {
+ bna_rxf_mcast_filter(bnad->priv,
+ BNAD_RX_FUNC_ID, BNA_ENABLE);
+ bnad->flags &= ~BNAD_F_ALLMULTI;
+ }
+ }
+ spin_unlock_irqrestore(&bnad->priv_lock, irq_flags);
+
+ if (netdev->mc_count) {
+ u8 *mcaddr_list;
+ u8 bcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ struct dev_mc_list *mc;
+ int i;
+
+ mcaddr_list = kzalloc((netdev->mc_count + 1) *
+ (ETH_ALEN * sizeof(u8)), GFP_ATOMIC);
+ if (!mcaddr_list)
+ return;
+ memcpy(&mcaddr_list[0], bcast_addr, ETH_ALEN * sizeof(u8));
+
+ mc = netdev->mc_list;
+ for (i = 1; mc && i < netdev->mc_count + 1; i++, mc = mc->next)
+ memcpy(&mcaddr_list[i], mc->dmi_addr,
+ ETH_ALEN * sizeof(u8));
+
+ spin_lock_irqsave(&bnad->priv_lock, irq_flags);
+ err = bna_rxf_mcast_mac_set_list(bnad->priv, BNAD_RX_FUNC_ID,
+ (const u8 *)mcaddr_list, netdev->mc_count + 1);
+ spin_unlock_irqrestore(&bnad->priv_lock, irq_flags);
+
+ kfree(mcaddr_list);
+ }
+}
+
+static void bnad_set_rx_mode(struct net_device *netdev)
+{
+ bnad_lock();
+ bnad_set_rx_mode_locked(netdev);
+ bnad_unlock();
+}
+
+int bnad_ucast_mac(struct bnad *bnad, unsigned int rxf_id,
+ u8 *mac_ptr, unsigned int cmd)
+{
+ int err = 0;
+ enum bna_status_e (*ucast_mac_func)(struct bna_dev_s *bna_dev,
+ unsigned int rxf_id, const u8 *mac_addr_ptr) = NULL;
+
+ WARN_ON(in_interrupt());
+ if (!is_valid_ether_addr(mac_ptr))
+ return -EINVAL;
+
+ switch (cmd) {
+ case BNAD_UCAST_MAC_SET:
+ ucast_mac_func = bna_rxf_ucast_mac_set;
+ break;
+ case BNAD_UCAST_MAC_ADD:
+ ucast_mac_func = bna_rxf_ucast_mac_add;
+ break;
+ case BNAD_UCAST_MAC_DEL:
+ ucast_mac_func = bna_rxf_ucast_mac_del;
+ break;
+ }
+
+ while (test_and_set_bit(BNAD_SET_UCAST, &bnad->state))
+ msleep(1);
+ init_completion(&bnad->ucast_comp);
+ spin_lock_irq(&bnad->priv_lock);
+ err = ucast_mac_func(bnad->priv, rxf_id, (const u8 *)mac_ptr);
+ spin_unlock_irq(&bnad->priv_lock);
+ if (err)
+ goto ucast_mac_exit;
+
+ DPRINTK(INFO, "Waiting for %s MAC operation %d reply\n",
+ bnad->netdev->name, cmd);
+ wait_for_completion(&bnad->ucast_comp);
+ err = bnad->ucast_comp_status;
+ucast_mac_exit:
+ smp_mb__before_clear_bit();
+ clear_bit(BNAD_SET_UCAST, &bnad->state);
+ if (err) {
+ printk(KERN_INFO
+ "%s unicast MAC address command %d failed: %d\n",
+ bnad->netdev->name, cmd, err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bnad_set_mac_address_locked(struct net_device *netdev, void *addr)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ struct sockaddr *sa = (struct sockaddr *)addr;
+ int err;
+
+ if (!is_valid_ether_addr(sa->sa_data))
+ return -EADDRNOTAVAIL;
+
+ if (!BNAD_NOT_READY(bnad)) {
+ err = bnad_ucast_mac(bnad, BNAD_RX_FUNC_ID, (u8 *)sa->sa_data,
+ BNAD_UCAST_MAC_SET);
+ if (err)
+ return err;
+ }
+
+ memcpy(netdev->dev_addr, sa->sa_data, netdev->addr_len);
+ return 0;
+}
+
+static int bnad_set_mac_address(struct net_device *netdev, void *addr)
+{
+ int err = 0;
+
+ bnad_lock();
+ err = bnad_set_mac_address_locked(netdev, addr);
+ bnad_unlock();
+ return err;
+
+}
+
+static int bnad_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ int err = 0;
+
+ WARN_ON(in_interrupt());
+
+ if (new_mtu + ETH_HLEN < ETH_ZLEN || new_mtu > BNAD_JUMBO_MTU)
+ return -EINVAL;
+
+ bnad_lock();
+
+ netdev->mtu = new_mtu;
+
+ err = bnad_sw_reset(netdev);
+
+ bnad_unlock();
+
+ return err;
+}
+
+static int bnad_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ return -EOPNOTSUPP;
+}
+
+static void
+bnad_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+
+ bnad_lock();
+ bnad->vlangrp = grp;
+ bnad_unlock();
+}
+
+static void bnad_vlan_rx_add_vid(struct net_device *netdev, unsigned short vid)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ unsigned long irq_flags;
+
+ DPRINTK(INFO, "%s add vlan %u\n", netdev->name, vid);
+ bnad_lock();
+ if (BNAD_NOT_READY(bnad)) {
+ bnad_unlock();
+ return;
+ }
+ spin_lock_irqsave(&bnad->priv_lock, irq_flags);
+ bna_rxf_vlan_add(bnad->priv, BNAD_RX_FUNC_ID, (unsigned int)vid);
+ spin_unlock_irqrestore(&bnad->priv_lock, irq_flags);
+ bnad_unlock();
+}
+
+static void
+bnad_vlan_rx_kill_vid(struct net_device *netdev, unsigned short vid)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+ unsigned long irq_flags;
+
+ DPRINTK(INFO, "%s remove vlan %u\n", netdev->name, vid);
+ bnad_lock();
+ if (BNAD_NOT_READY(bnad)) {
+ bnad_unlock();
+ return;
+ }
+ spin_lock_irqsave(&bnad->priv_lock, irq_flags);
+ bna_rxf_vlan_del(bnad->priv, BNAD_RX_FUNC_ID, (unsigned int)vid);
+ spin_unlock_irqrestore(&bnad->priv_lock, irq_flags);
+ bnad_unlock();
+}
+
+static void bnad_reconfig_vlans(struct bnad *bnad)
+{
+ u16 vlan_id;
+
+ spin_lock_irq(&bnad->priv_lock);
+ bna_rxf_vlan_del_all(bnad->priv, BNAD_RX_FUNC_ID);
+ if (bnad->vlangrp) {
+ for (vlan_id = 0; vlan_id < VLAN_GROUP_ARRAY_LEN; vlan_id++) {
+ if (vlan_group_get_device(bnad->vlangrp, vlan_id))
+ bna_rxf_vlan_add(bnad->priv, BNAD_RX_FUNC_ID,
+ (unsigned int)vlan_id);
+ }
+ }
+ spin_unlock_irq(&bnad->priv_lock);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void bnad_netpoll(struct net_device *netdev)
+{
+ struct bnad *bnad = netdev_priv(netdev);
+
+ DPRINTK(INFO, "%s bnad_netpoll\n", netdev->name);
+ disable_irq(bnad->pcidev->irq);
+ bnad_isr(bnad->pcidev->irq, netdev);
+ enable_irq(bnad->pcidev->irq);
+}
+#endif
+
+static void bnad_q_num_init(struct bnad *bnad, uint rxqsets)
+{
+ bnad->txq_num = BNAD_TXQ_NUM;
+ bnad->txf_num = 1;
+
+ if (bnad->flags & BNAD_F_MSIX) {
+ if (rxqsets) {
+ bnad->cq_num = rxqsets;
+ if (bnad->cq_num > BNAD_MAX_CQS)
+ bnad->cq_num = BNAD_MAX_CQS;
+ } else
+ bnad->cq_num = min((uint)num_online_cpus(),
+ (uint)BNAD_MAX_RXQSETS_USED);
+ if (!BNA_POWER_OF_2(bnad->cq_num))
+ BNA_TO_POWER_OF_2(bnad->cq_num);
+ bnad->rxq_num = bnad->cq_num * bnad_rxqs_per_cq;
+
+ bnad->rxf_num = 1;
+ bnad->msix_num = bnad->txq_num + bnad->cq_num +
+ BNAD_MSIX_ERR_MAILBOX_NUM;
+ } else {
+ bnad->cq_num = 1;
+ bnad->rxq_num = bnad->cq_num * bnad_rxqs_per_cq;
+ bnad->rxf_num = 1;
+ bnad->msix_num = 0;
+ }
+}
+
+static void bnad_enable_msix(struct bnad *bnad)
+{
+ int i, ret;
+
+ if (!(bnad->flags & BNAD_F_MSIX) || bnad->msix_table)
+ return;
+
+ bnad->msix_table = kzalloc(
+ bnad->msix_num * sizeof(struct msix_entry), GFP_KERNEL);
+ if (!bnad->msix_table)
+ goto intx_mode;
+
+ for (i = 0; i < bnad->msix_num; i++)
+ bnad->msix_table[i].entry = i;
+
+ ret = pci_enable_msix(bnad->pcidev, bnad->msix_table,
+ bnad->msix_num);
+ if (ret > 0) {
+ /* Not enough MSI-X vectors. */
+ int rxqsets = ret;
+
+ dev_err(&bnad->pcidev->dev,
+ "Tried to get %d MSI-X vectors, only got %d\n",
+ bnad->msix_num, ret);
+ BNA_TO_POWER_OF_2(rxqsets);
+ while (bnad->msix_num > ret && rxqsets) {
+ bnad_q_num_init(bnad, rxqsets);
+ rxqsets >>= 1;
+ }
+ if (bnad->msix_num <= ret) {
+ ret = pci_enable_msix(bnad->pcidev, bnad->msix_table,
+ bnad->msix_num);
+ if (ret) {
+ dev_err(&bnad->pcidev->dev,
+ "Enabling MSI-X failed: %d\n", ret);
+ goto intx_mode;
+ }
+ } else {
+ dev_err(&bnad->pcidev->dev,
+ "Enabling MSI-X failed: limited (%d) vectors\n",
+ ret);
+ goto intx_mode;
+ }
+ } else if (ret < 0) {
+ dev_err(&bnad->pcidev->dev, "Enabling MSI-X failed: %d\n", ret);
+ goto intx_mode;
+ }
+
+ dev_info(&bnad->pcidev->dev,
+ "Enabling MSI-X succeeded with %d vectors, %s\n", bnad->msix_num,
+ (bnad->cq_num > 1) ? "RSS is enabled" : "RSS is not enabled");
+ return;
+
+intx_mode:
+ dev_warn(&bnad->pcidev->dev, "Switching to INTx mode with no RSS\n");
+ kfree(bnad->msix_table);
+ bnad->msix_table = NULL;
+ bnad->flags &= ~BNAD_F_MSIX;
+ bnad_q_num_init(bnad, 0);
+}
+
+static void bnad_disable_msix(struct bnad *bnad)
+{
+ if ((bnad->flags & BNAD_F_MSIX) && bnad->msix_table) {
+ pci_disable_msix(bnad->pcidev);
+ kfree(bnad->msix_table);
+ bnad->msix_table = NULL;
+ bnad->flags &= ~BNAD_F_MSIX;
+ }
+}
+
+static void bnad_error(struct bnad *bnad)
+{
+ DPRINTK(INFO, "%s bnad_error\n", bnad->netdev->name);
+
+ rtnl_lock();
+ set_bit(BNAD_RESETTING, &bnad->state);
+ if (!test_and_set_bit(BNAD_DISABLED, &bnad->state)) {
+ bnad_detach(bnad);
+ bnad_cleanup(bnad);
+ DPRINTK(WARNING, "%s is disabled upon error\n",
+ bnad->netdev->name);
+ }
+ rtnl_unlock();
+}
+
+static void bnad_resume_after_reset(struct bnad *bnad)
+{
+ int err;
+ struct net_device *netdev = bnad->netdev;
+
+ DPRINTK(WARNING, "port %d resumes after reset\n", bnad->bna_id);
+
+ rtnl_lock();
+ clear_bit(BNAD_RESETTING, &bnad->state);
+
+ bna_port_mac_get(bnad->priv, (u8 *)bnad->perm_addr);
+ BNA_ASSERT(netdev->addr_len == sizeof(bnad->perm_addr));
+#ifdef ETHTOOL_GPERMADDR
+ memcpy(netdev->perm_addr, bnad->perm_addr, netdev->addr_len);
+#endif
+ if (is_zero_ether_addr(netdev->dev_addr))
+ memcpy(netdev->dev_addr, bnad->perm_addr, netdev->addr_len);
+
+ if (netif_running(bnad->netdev)) {
+ err = bnad_open_locked(bnad->netdev);
+ if (err)
+ DPRINTK(ERR, "%s bnad_open failed after reset: %d\n",
+ bnad->netdev->name, err);
+ }
+ rtnl_unlock();
+}
+
+static void bnad_work(struct work_struct *work)
+{
+ struct bnad *bnad = container_of(work, struct bnad, work);
+ unsigned long work_flags;
+
+ DPRINTK(INFO, "port %u bnad_work flags 0x%x\n",
+ bnad->bna_id, bnad->work_flags);
+
+ spin_lock_irq(&bnad->priv_lock);
+ work_flags = bnad->work_flags;
+ bnad->work_flags = 0;
+ spin_unlock_irq(&bnad->priv_lock);
+
+ if (work_flags & BNAD_WF_ERROR) {
+ DPRINTK(INFO, "port %u bnad_work: BNAD_WF_ERROR\n",
+ bnad->bna_id);
+ bnad_error(bnad);
+ }
+
+ if (work_flags & BNAD_WF_RESETDONE) {
+ DPRINTK(INFO, "port %u bnad_work: BNAD_WF_RESETDONE\n",
+ bnad->bna_id);
+ bnad_resume_after_reset(bnad);
+ }
+}
+
+static void bnad_stats_timeo(unsigned long data)
+{
+ struct bnad *bnad = (struct bnad *)data;
+ int i;
+ struct bnad_rxq_info *rxqinfo;
+
+ spin_lock_irq(&bnad->priv_lock);
+ bna_stats_get(bnad->priv);
+ spin_unlock_irq(&bnad->priv_lock);
+
+ if (bnad->rx_dyn_coalesce_on) {
+ u8 cls_timer;
+ struct bnad_cq_info *cq;
+ for (i = 0; i < bnad->cq_num; i++) {
+ cq = &bnad->cq_table[i];
+
+ if ((cq->pkt_rate.small_pkt_cnt == 0)
+ && (cq->pkt_rate.large_pkt_cnt == 0))
+ continue;
+
+ cls_timer = bna_calc_coalescing_timer(
+ bnad->priv, &cq->pkt_rate);
+
+ /*For NAPI version, coalescing timer need to stored*/
+ cq->rx_coalescing_timeo = cls_timer;
+
+ bna_ib_coalescing_timer_set(bnad->priv, &cq->ib,
+ cls_timer);
+ }
+ }
+
+ for (i = 0; i < bnad->rxq_num; i++) {
+ rxqinfo = &bnad->rxq_table[i];
+ if (!(BNA_QE_IN_USE_CNT(&rxqinfo->skb_unmap_q,
+ rxqinfo->skb_unmap_q.q_depth) >>
+ BNAD_RXQ_REFILL_THRESHOLD_SHIFT)) {
+ DPRINTK(INFO, "%s: RxQ %d more buffers to allocate\n",
+ bnad->netdev->name, i);
+ if (test_and_set_bit(BNAD_RXQ_REFILL, &rxqinfo->flags))
+ continue;
+ bnad_alloc_rxbufs(rxqinfo);
+ smp_mb__before_clear_bit();
+ clear_bit(BNAD_RXQ_REFILL, &rxqinfo->flags);
+ }
+ }
+}
+
+static void bnad_free_ioc_mem(struct bnad *bnad)
+{
+ enum bna_dma_mem_type i;
+
+ for (i = 0; i < BNA_MEM_T_MAX; i++) {
+ if (!bnad->ioc_meminfo[i].len)
+ continue;
+ if (bnad->ioc_meminfo[i].kva && bnad->ioc_meminfo[i].dma)
+ pci_free_consistent(bnad->pcidev,
+ bnad->ioc_meminfo[i].len, bnad->ioc_meminfo[i].kva,
+ *(dma_addr_t *)&bnad->ioc_meminfo[i].dma);
+ else if (bnad->ioc_meminfo[i].kva)
+ vfree(bnad->ioc_meminfo[i].kva);
+ bnad->ioc_meminfo[i].kva = NULL;
+ }
+}
+
+/* The following IOC callback functions are called with priv_lock held. */
+
+void bna_iocll_enable_cbfn(void *arg, enum bfa_status status)
+{
+ struct bnad *bnad = arg;
+
+ DPRINTK(WARNING, "port %u IOC enable callback, status %d\n",
+ bnad->bna_id, status);
+
+ bnad->ioc_comp_status = status;
+ complete(&bnad->ioc_comp);
+
+ if (!status) {
+ bnad->work_flags |= BNAD_WF_RESETDONE;
+ if (!test_bit(BNAD_REMOVED, &bnad->state))
+ schedule_work(&bnad->work);
+ }
+}
+
+void bna_iocll_disable_cbfn(void *arg)
+{
+ struct bnad *bnad = arg;
+
+ DPRINTK(WARNING, "port %u IOC disable callback\n",
+ bnad->bna_id);
+ complete(&bnad->ioc_comp);
+}
+
+void bna_iocll_hbfail_cbfn(void *arg)
+{
+ struct bnad *bnad = arg;
+
+ DPRINTK(ERR, "port %u IOC HBFail callback\n", bnad->bna_id);
+ bnad_hw_error(bnad, BFA_STATUS_IOC_FAILURE);
+}
+
+void bna_iocll_reset_cbfn(void *arg)
+{
+ struct bnad *bnad = arg;
+ u32 int_status, int_mask;
+ unsigned int irq;
+
+ DPRINTK(WARNING, "port %u IOC reset callback\n", bnad->bna_id);
+
+ /* Clear the status */
+ bna_intr_status_get(bnad->priv, &int_status);
+
+ if (bnad->flags & BNAD_F_MSIX) {
+ if (test_and_clear_bit(BNAD_MBOX_IRQ_DISABLED, &bnad->state)) {
+ irq = bnad->msix_table[bnad->txq_num +
+ bnad->cq_num].vector;
+ DPRINTK(WARNING, "Enabling Mbox IRQ %d for port %d\n",
+ irq, bnad->bna_id);
+ enable_irq(irq);
+ }
+ }
+
+ int_mask = ~(__LPU2HOST_MBOX_MASK_BITS | __ERROR_MASK_BITS);
+ bna_intx_enable(bnad->priv, int_mask);
+}
+
+static void bnad_ioc_timeout(unsigned long data)
+{
+ struct bnad *bnad = (struct bnad *)data;
+
+ spin_lock_irq(&bnad->priv_lock);
+ bna_iocll_timer(bnad->priv);
+ spin_unlock_irq(&bnad->priv_lock);
+
+ if (!test_bit(BNAD_REMOVED, &bnad->state))
+ mod_timer(&bnad->ioc_timer,
+ jiffies + HZ * BNA_IOC_TIMER_FREQ / 1000);
+}
+
+s32
+bnad_cee_attach(struct bnad *bnad)
+{
+ u8 *dma_kva;
+ dma_addr_t dma_pa;
+ struct bfa_cee_s *cee = &bnad->cee;
+
+ memset(cee, 0, sizeof(struct bfa_cee_s));
+
+ /*Allocate memory for dma*/
+ dma_kva = pci_alloc_consistent(bnad->pcidev, bfa_cee_meminfo(),
+ &dma_pa);
+ if (dma_kva == NULL)
+ return -ENOMEM;
+
+ /*Ugly... need to remove once CAL is fixed.*/
+ ((struct bna_dev_s *)bnad->priv)->cee = cee;
+
+ bnad->cee_cbfn.get_attr_cbfn = bnad_cee_get_attr_cb;
+ bnad->cee_cbfn.get_stats_cbfn = bnad_cee_get_stats_cb;
+ bnad->cee_cbfn.reset_stats_cbfn = bnad_cee_reset_stats_cb;
+ bnad->cee_cbfn.reset_stats_cbfn = NULL;
+
+ /*Invoke cee attach function*/
+ bfa_cee_attach(cee, &bnad->priv->ioc, bnad,
+ bnad->trcmod, bnad->logmod);
+ bfa_cee_mem_claim(cee, dma_kva, dma_pa);
+ return 0;
+}
+
+static void
+bnad_cee_detach(struct bnad *bnad)
+{
+ struct bfa_cee_s *cee = &bnad->cee;
+ if (cee->attr_dma.kva) {
+ pci_free_consistent(bnad->pcidev, bfa_cee_meminfo(),
+ cee->attr_dma.kva, cee->attr_dma.pa);
+ }
+ bfa_cee_detach(&bnad->cee);
+}
+
+
+static int bnad_priv_init(struct bnad *bnad)
+{
+ dma_addr_t dma_addr;
+ struct bna_dma_addr bna_dma_addr;
+ char inst_name[16];
+ int err, i;
+ struct bfa_pcidev_s pcidev_info;
+ u32 intr_mask;
+
+ DPRINTK(DEBUG, "port %u bnad_priv_init\n", bnad->bna_id);
+
+ if (bnad_msix)
+ bnad->flags |= BNAD_F_MSIX;
+ bnad_q_num_init(bnad, bnad_rxqsets_used);
+
+ bnad->work_flags = 0;
+ INIT_WORK(&bnad->work, bnad_work);
+
+ init_timer(&bnad->stats_timer);
+ bnad->stats_timer.function = &bnad_stats_timeo;
+ bnad->stats_timer.data = (unsigned long)bnad;
+
+ bnad->tx_coalescing_timeo = BNAD_TX_COALESCING_TIMEO;
+ bnad->tx_interpkt_count = BNAD_TX_INTERPKT_COUNT;
+
+ bnad->rx_coalescing_timeo = BNAD_RX_COALESCING_TIMEO;
+ bnad->rx_interpkt_count = BNAD_RX_INTERPKT_COUNT;
+ bnad->rx_interpkt_timeo = BNAD_RX_INTERPKT_TIMEO;
+ bnad->rx_dyn_coalesce_on = BNA_TRUE;
+
+ bnad->rx_csum = 1;
+ bnad->pause_config.tx_pause = 0;
+ bnad->pause_config.rx_pause = 0;
+
+ /* XXX could be vmalloc? */
+ bnad->trcmod = kzalloc(sizeof(struct bfa_trc_mod_s), GFP_KERNEL);
+ if (!bnad->trcmod) {
+ DPRINTK(ERR, "port %u failed allocating trace buffer!\n",
+ bnad->bna_id);
+ return -ENOMEM;
+ }
+ bfa_trc_init(bnad->trcmod);
+
+ bnad->logmod = NULL;
+ sprintf(inst_name, "%u", bnad->bna_id);
+
+ bnad->aen = NULL;
+ INIT_LIST_HEAD(&bnad->file_q);
+ INIT_LIST_HEAD(&bnad->file_free_q);
+ for (i = 0; i < BNAD_AEN_MAX_APPS; i++) {
+ bfa_q_qe_init(&bnad->file_buf[i].qe);
+ list_add_tail(&bnad->file_buf[i].qe, &bnad->file_free_q);
+ }
+
+ bnad->priv = kzalloc(bna_get_handle_size(), GFP_KERNEL);
+ if (!bnad->priv) {
+ DPRINTK(ERR, "port %u failed allocating memory for bna\n",
+ bnad->bna_id);
+ err = -ENOMEM;
+ goto free_trcmod;
+ }
+ bnad->priv_stats = pci_alloc_consistent(bnad->pcidev,
+ BNA_HW_STATS_SIZE, &dma_addr);
+ if (!bnad->priv_stats) {
+ DPRINTK(ERR, "port %u failed allocating memory for bna stats\n",
+ bnad->bna_id);
+ err = -ENOMEM;
+ goto free_priv_mem;
+ }
+ pci_unmap_addr_set(bnad, priv_stats_dma, dma_addr);
+ DPRINTK(DEBUG, "port %u priv_stats dma addr 0x%llx\n",
+ bnad->bna_id, dma_addr);
+
+ BNA_SET_DMA_ADDR(dma_addr, &bna_dma_addr);
+ bna_init(bnad->priv, (void *)bnad->bar0, bnad->priv_stats,
+ bna_dma_addr, bnad->trcmod);
+ bna_all_stats_get(bnad->priv, &bnad->hw_stats);
+ spin_lock_init(&bnad->priv_lock);
+ bnad->priv_cbfn.ucast_set_cb = bnad_ucast_set_cb;
+ bnad->priv_cbfn.txq_stop_cb = bnad_q_stop_cb;
+ bnad->priv_cbfn.rxq_stop_cb = bnad_q_stop_cb;
+ bnad->priv_cbfn.link_up_cb = bnad_link_up_cb;
+ bnad->priv_cbfn.link_down_cb = bnad_link_down_cb;
+ bnad->priv_cbfn.stats_get_cb = bnad_stats_get_cb;
+ bnad->priv_cbfn.hw_error_cb = bnad_hw_error_cb;
+ bnad->priv_cbfn.lldp_get_cfg_cb = bnad_lldp_get_cfg_cb;
+ /* Diagnostics */
+ bnad->priv_cbfn.set_diag_lb_cb = bnad_set_diag_lb_cb;
+
+ bna_register_callback(bnad->priv, &bnad->priv_cbfn, bnad);
+
+ bna_iocll_meminfo(bnad->priv, bnad->ioc_meminfo);
+ for (i = 0; i < BNA_MEM_T_MAX; i++) {
+ if (!bnad->ioc_meminfo[i].len)
+ continue;
+ switch (i) {
+ case BNA_KVA_MEM_T_FWTRC:
+ bnad->ioc_meminfo[i].kva = vmalloc(
+ bnad->ioc_meminfo[i].len);
+ break;
+ default:
+ bnad->ioc_meminfo[i].kva = pci_alloc_consistent(
+ bnad->pcidev, bnad->ioc_meminfo[i].len,
+ (dma_addr_t *)&bnad->ioc_meminfo[i].dma);
+
+ break;
+ }
+ if (!bnad->ioc_meminfo[i].kva) {
+ DPRINTK(ERR,
+ "port %u failed allocating %u bytes"
+ "memory for IOC\n",
+ bnad->bna_id, bnad->ioc_meminfo[i].len);
+ err = -ENOMEM;
+ goto free_ioc_mem;
+ }
+ }
+
+ pcidev_info.pci_slot = PCI_SLOT(bnad->pcidev->devfn);
+ pcidev_info.pci_func = PCI_FUNC(bnad->pcidev->devfn);
+ pcidev_info.device_id = bnad->pcidev->device;
+ pcidev_info.pci_bar_kva = bnad->bar0;
+ bna_iocll_attach(bnad->priv, bnad, bnad->ioc_meminfo,
+ &pcidev_info, bnad->trcmod, bnad->aen, bnad->logmod);
+
+ err = bnad_cee_attach(bnad);
+ if (err) {
+ DPRINTK(ERR, "port %u cee_attach failed: %d\n",
+ bnad->bna_id, err);
+ goto iocll_detach;
+ }
+
+ if (bnad->flags & BNAD_F_MSIX)
+ bnad_enable_msix(bnad);
+ else
+ dev_info(&bnad->pcidev->dev, "Working in INTx mode, no RSS\n");
+ bna_intx_disable(bnad->priv, &intr_mask);
+ err = bnad_request_mbox_irq(bnad);
+ if (err)
+ goto disable_msix;
+
+ init_completion(&bnad->ioc_comp);
+ DPRINTK(DEBUG, "port %u enabling IOC ...\n", bnad->bna_id);
+ spin_lock_irq(&bnad->priv_lock);
+ bna_iocll_enable(bnad->priv);
+ spin_unlock_irq(&bnad->priv_lock);
+
+ init_timer(&bnad->ioc_timer);
+ bnad->ioc_timer.function = &bnad_ioc_timeout;
+ bnad->ioc_timer.data = (unsigned long)bnad;
+ mod_timer(&bnad->ioc_timer, jiffies + HZ * BNA_IOC_TIMER_FREQ / 1000);
+
+ DPRINTK(DEBUG, "port %u waiting for IOC ready.\n", bnad->bna_id);
+ wait_for_completion(&bnad->ioc_comp);
+ if (!bnad->ioc_comp_status) {
+ DPRINTK(INFO, "port %u IOC is enabled.\n", bnad->bna_id);
+ bna_port_mac_get(bnad->priv,
+ (u8 *)bnad->perm_addr);
+ } else {
+ DPRINTK(ERR, "port %u enabling IOC failed: %d\n",
+ bnad->bna_id, bnad->ioc_comp_status);
+ set_bit(BNAD_RESETTING, &bnad->state);
+ }
+
+ return 0;
+
+disable_msix:
+ bnad_disable_msix(bnad);
+ bnad_cee_detach(bnad);
+iocll_detach:
+ bna_iocll_detach(bnad->priv);
+free_ioc_mem:
+ bnad_free_ioc_mem(bnad);
+ bna_uninit(bnad->priv);
+ pci_free_consistent(bnad->pcidev, BNA_HW_STATS_SIZE, bnad->priv_stats,
+ pci_unmap_addr(bnad, priv_stats_dma));
+ bnad->priv_stats = NULL;
+free_priv_mem:
+ kfree(bnad->priv);
+ bnad->priv = NULL;
+free_trcmod:
+ kfree(bnad->trcmod);
+ bnad->trcmod = NULL;
+
+ return err;
+}
+
+static void bnad_priv_uninit(struct bnad *bnad)
+{
+ int i;
+ enum bna_status_e err;
+
+ if (bnad->priv) {
+ DPRINTK(INFO, "port %u disabling IOC ...\n", bnad->bna_id);
+ init_completion(&bnad->ioc_comp);
+ for (i = 0; i < 10; i++) {
+ spin_lock_irq(&bnad->priv_lock);
+ err = bna_iocll_disable(bnad->priv);
+ spin_unlock_irq(&bnad->priv_lock);
+ BNA_ASSERT(!err || err == BNA_BUSY);
+ if (!err)
+ break;
+ msleep(1000);
+ }
+ if (err) {
+ /* Probably firmware crashed. */
+ DPRINTK(INFO,
+ "bna_iocll_disable failed,"
+ "clean up and try again\n");
+ spin_lock_irq(&bnad->priv_lock);
+ bna_cleanup(bnad->priv);
+ err = bna_iocll_disable(bnad->priv);
+ spin_unlock_irq(&bnad->priv_lock);
+ BNA_ASSERT(!err);
+ }
+ wait_for_completion(&bnad->ioc_comp);
+ set_bit(BNAD_IOC_DISABLED, &bnad->state);
+ DPRINTK(INFO, "port %u IOC is disabled\n", bnad->bna_id);
+
+ set_bit(BNAD_REMOVED, &bnad->state);
+ /* Stop the timer after disabling IOC. */
+ del_timer_sync(&bnad->ioc_timer);
+ bnad_free_ioc_mem(bnad);
+ bna_iocll_detach(bnad->priv);
+
+ flush_scheduled_work();
+ bnad_free_mbox_irq(bnad);
+ bnad_disable_msix(bnad);
+
+ bnad_cee_detach(bnad);
+
+ bna_uninit(bnad->priv);
+ if (bnad->priv_stats) {
+ pci_free_consistent(bnad->pcidev, BNA_HW_STATS_SIZE,
+ bnad->priv_stats,
+ pci_unmap_addr(bnad, priv_stats_dma));
+ bnad->priv_stats = NULL;
+ }
+ kfree(bnad->priv);
+ bnad->priv = NULL;
+ }
+ BNA_ASSERT(list_empty(&bnad->file_q));
+ kfree(bnad->trcmod);
+ bnad->trcmod = NULL;
+}
+
+static struct pci_device_id bnad_pci_id_table[] = {
+ {
+ .vendor = PCI_VENDOR_ID_BROCADE,
+ .device = PCI_DEVICE_ID_BROCADE_CATAPULT,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = PCI_CLASS_NETWORK_ETHERNET << 8,
+ .class_mask = 0xffff00
+ },
+ {0, 0}
+};
+MODULE_DEVICE_TABLE(pci, bnad_pci_id_table);
+
+static int __devinit
+bnad_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidev_id)
+{
+ int err, using_dac;
+ struct net_device *netdev;
+ struct bnad *bnad;
+ unsigned long mmio_start, mmio_len;
+ static u32 bna_id;
+
+ DPRINTK(INFO, "bnad_pci_probe(0x%p, 0x%p)\n", pcidev, pcidev_id);
+
+ DPRINTK(DEBUG, "PCI func %d\n", PCI_FUNC(pcidev->devfn));
+ if (!bfad_get_firmware_buf(pcidev)) {
+ printk(KERN_WARNING "Failed to load Firmware Image!\n");
+ return 0;
+ }
+
+ err = pci_enable_device(pcidev);
+ if (err) {
+ dev_err(&pcidev->dev, "pci_enable_device failed: %d\n", err);
+ return err;
+ }
+
+ err = pci_request_regions(pcidev, BNAD_NAME);
+ if (err) {
+ dev_err(&pcidev->dev, "pci_request_regions failed: %d\n", err);
+ goto disable_device;
+ }
+
+ if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(64)) &&
+ !pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64))) {
+ using_dac = 1;
+ DPRINTK(INFO, "64bit DMA mask\n");
+ } else {
+ err = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32));
+ if (err) {
+ err = pci_set_consistent_dma_mask(pcidev,
+ DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pcidev->dev,
+ "set 32bit consistent DMA mask failed: %d\n"
+ , err);
+ goto release_regions;
+ }
+ }
+ using_dac = 0;
+ DPRINTK(INFO, "32bit DMA mask\n");
+ }
+
+ pci_set_master(pcidev);
+
+ netdev = alloc_etherdev(sizeof(struct bnad));
+ if (!netdev) {
+ dev_err(&pcidev->dev, "alloc_etherdev failed\n");
+ err = -ENOMEM;
+ goto release_regions;
+ }
+ SET_MODULE_OWNER(netdev);
+ SET_NETDEV_DEV(netdev, &pcidev->dev);
+ pci_set_drvdata(pcidev, netdev);
+
+ bnad = netdev_priv(netdev);
+ set_bit(BNAD_DISABLED, &bnad->state);
+ bnad->netdev = netdev;
+ bnad->pcidev = pcidev;
+ mmio_start = pci_resource_start(pcidev, 0);
+ mmio_len = pci_resource_len(pcidev, 0);
+ bnad->bar0 = ioremap_nocache(mmio_start, mmio_len);
+ if (!bnad->bar0) {
+ dev_err(&pcidev->dev, "ioremap for bar0 failed\n");
+ err = -ENOMEM;
+ goto free_devices;
+ }
+ DPRINTK(INFO, "bar0 mapped to %p, len %lu\n", bnad->bar0, mmio_len);
+
+ netdev->netdev_ops = &bnad_netdev_ops;
+ netdev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
+#ifdef NETIF_F_IPV6_CSUM
+ netdev->features |= NETIF_F_IPV6_CSUM;
+#endif
+#ifdef NETIF_F_TSO
+ netdev->features |= NETIF_F_TSO;
+#endif
+#ifdef NETIF_F_TSO6
+ netdev->features |= NETIF_F_TSO6;
+#endif
+#ifdef NETIF_F_LRO
+ netdev->features |= NETIF_F_LRO;
+#endif
+#ifdef BNAD_VLAN_FEATURES
+ netdev->vlan_features = netdev->features;
+#endif
+ if (using_dac)
+ netdev->features |= NETIF_F_HIGHDMA;
+ netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
+ NETIF_F_HW_VLAN_FILTER;
+
+ netdev->mem_start = mmio_start;
+ netdev->mem_end = mmio_start + mmio_len - 1;
+
+ bnad_set_ethtool_ops(netdev);
+
+ bnad->bna_id = bna_id;
+ err = bnad_priv_init(bnad);
+ if (err) {
+ printk(KERN_ERR "port %u init failed: %d\n", bnad->bna_id, err);
+ goto unmap_bar0;
+ }
+
+ BNA_ASSERT(netdev->addr_len == ETH_ALEN);
+#ifdef ETHTOOL_GPERMADDR
+ memcpy(netdev->perm_addr, bnad->perm_addr, netdev->addr_len);
+#endif
+ memcpy(netdev->dev_addr, bnad->perm_addr, netdev->addr_len);
+
+
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ err = register_netdev(netdev);
+ if (err) {
+ printk(KERN_ERR "port %u register_netdev failed: %d\n",
+ bnad->bna_id, err);
+ goto bnad_device_uninit;
+ }
+
+
+ bna_id++;
+ return 0;
+
+bnad_device_uninit:
+ bnad_priv_uninit(bnad);
+unmap_bar0:
+ iounmap(bnad->bar0);
+free_devices:
+ pci_set_drvdata(pcidev, NULL);
+ free_netdev(netdev);
+release_regions:
+ pci_release_regions(pcidev);
+disable_device:
+ pci_disable_device(pcidev);
+
+ return err;
+}
+
+static void __devexit bnad_pci_remove(struct pci_dev *pcidev)
+{
+ struct net_device *netdev = pci_get_drvdata(pcidev);
+ struct bnad *bnad;
+
+ DPRINTK(INFO, "%s bnad_pci_remove\n", netdev->name);
+ if (!netdev)
+ return;
+ bnad = netdev_priv(netdev);
+
+
+ unregister_netdev(netdev);
+
+ bnad_priv_uninit(bnad);
+ iounmap(bnad->bar0);
+ pci_set_drvdata(pcidev, NULL);
+ free_netdev(netdev);
+ pci_release_regions(pcidev);
+ pci_disable_device(pcidev);
+}
+
+static struct pci_driver bnad_pci_driver = {
+ .name = BNAD_NAME,
+ .id_table = bnad_pci_id_table,
+ .probe = bnad_pci_probe,
+ .remove = __devexit_p(bnad_pci_remove),
+};
+
+static int __init bnad_module_init(void)
+{
+ int err;
+
+ printk(KERN_INFO "Brocade 10G Ethernet driver %s\n", bfa_version);
+ DPRINTK(INFO, "Module bna is loaded at 0x%p\n",
+ __this_module.module_core);
+ err = bnad_check_module_params();
+ if (err)
+ return err;
+
+ bfa_ioc_auto_recover(bnad_ioc_auto_recover);
+
+ return pci_register_driver(&bnad_pci_driver);
+}
+
+static void __exit bnad_module_exit(void)
+{
+ pci_unregister_driver(&bnad_pci_driver);
+
+ if (bfi_image_ct_size && bfi_image_ct)
+ vfree(bfi_image_ct);
+}
+
+module_init(bnad_module_init);
+module_exit(bnad_module_exit);
+
+MODULE_AUTHOR("Brocade");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Brocade 10G PCIe Ethernet driver");
+MODULE_VERSION(BNAD_VERSION);
+
diff -ruP linux-2.6.32-rc4-orig/drivers/net/bna/bnad.h linux-2.6.32-rc4-mod/drivers/net/bna/bnad.h
--- linux-2.6.32-rc4-orig/drivers/net/bna/bnad.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.32-rc4-mod/drivers/net/bna/bnad.h 2009-10-16 10:30:53.075436000 -0700
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ *
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _BNAD_H_
+#define _BNAD_H_
+
+#include <cee/bfa_cee.h>
+#include "bna.h"
+
+#if !defined(CONFIG_INET_LRO) && !defined(CONFIG_INET_LRO_MODULE)
+#include <net/ip.h>
+#include <net/tcp.h>
+#else
+#include <linux/inet_lro.h>
+#endif
+
+#include "bnad_compat.h"
+
+#if !defined(CONFIG_INET_LRO) && !defined(CONFIG_INET_LRO_MODULE)
+#include "inet_lro.h"
+#endif
+
+#define BNAD_LRO_MAX_DESC 8
+#define BNAD_LRO_MAX_AGGR 64
+
+
+#define BNAD_MAX_Q_DEPTH 0x10000
+#define BNAD_MIN_Q_DEPTH 0x200
+
+#define BNAD_TXQ_NUM 1
+#define BNAD_TX_FUNC_ID 0
+#define BNAD_ENTRIES_PER_TXQ 2048
+
+#define BNAD_MAX_RXQS 64
+#define BNAD_MAX_RXQSETS_USED 16
+#define BNAD_RX_FUNC_ID 0
+#define BNAD_ENTRIES_PER_RXQ 2048
+
+#define BNAD_MAX_CQS 64
+#define BNAD_MAX_RXQS_PER_CQ 2
+
+#define BNAD_MSIX_ERR_MAILBOX_NUM 1
+
+#define BNAD_INTX_MAX_IB_NUM 16
+#define BNAD_INTX_IB_NUM 2 /* 1 for Tx, 1 for Rx */
+#define BNAD_INTX_TX_IB_ID 0
+#define BNAD_INTX_RX_IB_ID 1
+
+#define BNAD_QUEUE_NAME_SIZE 16
+
+#define BNAD_JUMBO_MTU 9000
+
+#define BNAD_COALESCING_TIMER_UNIT 5 /* 5us */
+#define BNAD_MAX_COALESCING_TIMEO 0xFF /* in 5us units */
+#define BNAD_MAX_INTERPKT_COUNT 0xFF
+#define BNAD_MAX_INTERPKT_TIMEO 0xF /* in 0.5us units */
+
+#define BNAD_TX_COALESCING_TIMEO 20 /* 20 * 5 = 100us */
+#define BNAD_TX_INTERPKT_COUNT 32
+
+#define BNAD_RX_COALESCING_TIMEO 12 /* 12 * 5 = 60us */
+#define BNAD_RX_INTERPKT_COUNT 6
+#define BNAD_RX_INTERPKT_TIMEO 3 /* 3 * 0.5 = 1.5us */
+
+#define BNAD_SMALL_RXBUF_SIZE 128
+
+#define BNAD_RIT_OFFSET 0
+#define BNAD_MULTICAST_RXQ_ID 0
+
+#define BNAD_NETIF_WAKE_THRESHOLD 8
+
+#define BNAD_TX_MAX_VECTORS 255
+#define BNAD_TX_MAX_VECTORS_PER_WI 4
+#define BNAD_TX_MAX_DATA_PER_WI 0xFFFFFF /* 24 bits */
+#define BNAD_TX_MAX_DATA_PER_VECTOR 0x3FFF /* 14 bits */
+#define BNAD_TX_MAX_WRR_QUOTA 0xFFF /* 12 bits */
+
+#define BNAD_RXQ_REFILL_THRESHOLD_SHIFT 3
+
+#define BNAD_CQ_PROCESS_LIMIT 512
+
+#define BNAD_NOT_READY(_bnad) test_bit(BNAD_RESETTING, &(_bnad)->state)
+
+#define BNAD_Q_INDEX_CHANGE(_old_idx, _updated_idx, _q_depth) \
+ (((_updated_idx) - (_old_idx)) & ((_q_depth) - 1))
+
+#define bnad_lock()
+#define bnad_unlock()
+
+extern u32 bfi_image_ct_size;
+extern u32 *bfi_image_ct;
+extern u32 *bfad_get_firmware_buf(struct pci_dev *pdev);
+
+struct bnad_skb_unmap {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(dma_addr)
+};
+
+struct bnad_unmap_q {
+ u32 producer_index;
+ u32 consumer_index;
+ struct bnad_skb_unmap *unmap_array;
+ u32 q_depth;
+};
+
+struct bnad_ib_entry {
+ struct bna_ib *ib;
+ void *ib_seg_addr;
+ struct bna_ib_config ib_config;
+};
+
+struct bnad_txq_info {
+ unsigned long flags;
+#define BNAD_TXQ_FREE_SENT 0
+ struct bna_txq txq;
+ struct bna_ib ib;
+ struct bnad_unmap_q skb_unmap_q;
+ u64 tx_packets;
+ u64 tx_bytes;
+ struct bnad *bnad;
+ volatile u32 *hw_consumer_index;
+ struct bna_txq_config txq_config;
+ char name[BNAD_QUEUE_NAME_SIZE];
+#ifdef DEBUG_TX
+ u32 max_tso;
+ u32 tx_vectors[32];
+#endif
+} ____cacheline_aligned;
+
+struct bnad_rxq_info {
+ unsigned long flags;
+#define BNAD_RXQ_REFILL 0
+ struct bna_rxq rxq;
+ struct bnad_unmap_q skb_unmap_q;
+ u64 rx_packets;
+ u64 rx_bytes;
+ u64 rx_packets_with_error;
+ u64 rxbuf_alloc_failed;
+ struct bnad *bnad;
+ u32 rxq_id;
+ struct bna_rxq_config rxq_config;
+} ____cacheline_aligned;
+
+struct bnad_cq_info {
+ struct bna_cq cq;
+ struct bna_ib ib;
+ struct bnad *bnad;
+ struct bna_pkt_rate pkt_rate;
+ u8 rx_coalescing_timeo; /* Unit is 5usec. */
+ volatile u32 *hw_producer_index;
+ struct net_lro_mgr lro;
+ struct napi_struct napi;
+ u32 cq_id;
+ struct bna_cq_config cq_config;
+ char name[BNAD_QUEUE_NAME_SIZE];
+} ____cacheline_aligned;
+
+struct bnad_txf_info {
+ u32 txf_id;
+ struct bna_txf_config txf_config;
+};
+
+struct bnad_rxf_info {
+ u32 rxf_id;
+ struct bna_rxf_config rxf_config;
+};
+
+enum bnad_ucast_cmd {
+ BNAD_UCAST_MAC_SET,
+ BNAD_UCAST_MAC_ADD,
+ BNAD_UCAST_MAC_DEL
+};
+
+struct bnad_diag_lb_params {
+ struct bnad *bnad;
+ struct completion diag_lb_comp;
+ int diag_lb_comp_status;
+ int diag_lb_link_state;
+#define BNAD_DIAG_LB_LS_UNKNOWN -1
+#define BNAD_DIAG_LB_LS_UP 0
+#define BNAD_DIAG_LB_LS_DOWN 1
+};
+
+#define BNAD_AEN_MAX_APPS 8
+struct bnad_aen_file_s {
+ struct list_head qe;
+ struct bnad *bnad;
+ s32 ri;
+ s32 app_id;
+};
+
+struct bnad {
+ struct net_device *netdev;
+ struct pci_dev *pcidev;
+ struct bna_dev_s *priv;
+
+ unsigned long state;
+#define BNAD_DISABLED 0
+#define BNAD_RESETTING 1
+#define BNAD_REMOVED 2
+#define BNAD_SET_UCAST 4
+#define BNAD_IOC_DISABLED 5
+#define BNAD_PORT_DISABLED 6
+#define BNAD_MBOX_IRQ_DISABLED 7
+
+ unsigned int flags;
+#define BNAD_F_MSIX 0x01
+#define BNAD_F_PROMISC 0x02
+#define BNAD_F_ALLMULTI 0x04
+#define BNAD_F_WOL 0x08
+#define BNAD_F_TXQ_DEPTH 0x10
+#define BNAD_F_RXQ_DEPTH 0x20
+
+
+ uint txq_num;
+ uint txq_depth;
+ struct bnad_txq_info *txq_table;
+ uint rxq_num;
+ uint rxq_depth;
+ struct bnad_rxq_info *rxq_table;
+ uint cq_num;
+ struct bnad_cq_info *cq_table;
+
+ struct vlan_group *vlangrp;
+
+ u32 rx_csum;
+
+ uint msix_num;
+ struct msix_entry *msix_table;
+
+ uint ib_num;
+ struct bnad_ib_entry *ib_table;
+
+ struct bna_rit_entry *rit; /* RxQ Indirection Table */
+
+ spinlock_t priv_lock ____cacheline_aligned;
+
+ uint txf_num;
+ struct bnad_txf_info *txf_table;
+ uint rxf_num;
+ struct bnad_rxf_info *rxf_table;
+
+ struct timer_list stats_timer;
+ struct net_device_stats net_stats;
+
+ u8 tx_coalescing_timeo; /* Unit is 5usec. */
+ u8 tx_interpkt_count;
+
+ u8 rx_coalescing_timeo; /* Unit is 5usec. */
+ u8 rx_interpkt_count;
+ u8 rx_interpkt_timeo; /* 4 bits, unit is 0.5usec. */
+ u8 rx_dyn_coalesce_on; /* Rx Dynamic Intr Moderation Flag */
+ u8 ref_count;
+ u8 lldp_comp_status;
+ u8 cee_stats_comp_status;
+ u8 cee_reset_stats_status;
+ u8 ucast_comp_status;
+ u8 qstop_comp_status;
+ u16 rsvd_2;
+ int ioc_comp_status;
+
+ struct bna_pause_config pause_config;
+
+ struct bna_stats *hw_stats;
+ struct bnad_drv_stats stats;
+
+ struct work_struct work;
+ unsigned int work_flags;
+#define BNAD_WF_ERROR 0x1
+#define BNAD_WF_RESETDONE 0x2
+
+ struct completion lldp_comp;
+ struct completion cee_stats_comp;
+ struct completion cee_reset_stats_comp;
+ struct completion ucast_comp;
+ struct completion qstop_comp;
+ struct completion ioc_comp;
+
+ u32 bna_id;
+ u8 __iomem *bar0; /* registers */
+ unsigned char perm_addr[ETH_ALEN];
+ u32 pci_saved_config[16];
+
+ void *priv_stats;
+ DECLARE_PCI_UNMAP_ADDR(priv_stats_dma)
+
+ struct bfa_trc_mod_s *trcmod;
+ struct bfa_log_mod_s *logmod;
+ struct bfa_aen_s *aen;
+ struct bnad_aen_file_s file_buf[BNAD_AEN_MAX_APPS];
+ struct list_head file_q;
+ struct list_head file_free_q;
+ struct bna_meminfo ioc_meminfo[BNA_MEM_T_MAX];
+ struct timer_list ioc_timer;
+
+ struct bna_mbox_cbfn priv_cbfn;
+
+ char adapter_name[64];
+ char port_name[64];
+
+ /* Diagnostics */
+ struct bna_diag_lb_pkt_stats *lb_stats;
+ struct bnad_diag_lb_params *dlbp;
+
+ /* CEE Stuff */
+ struct bfa_cee_cbfn_s cee_cbfn;
+ struct bfa_cee_s cee;
+
+ struct list_head list_entry;
+};
+
+extern uint bnad_rxqs_per_cq;
+extern uint bnad_rxq_depth;
+extern uint bnad_txq_depth;
+extern uint bnad_small_large_rxbufs;
+
+extern struct list_head bnad_list;
+
+int bnad_open(struct net_device *netdev);
+int bnad_stop(struct net_device *netdev);
+int bnad_stop_locked(struct net_device *netdev);
+int bnad_open_locked(struct net_device *netdev);
+int bnad_sw_reset(struct net_device *netdev);
+int bnad_resetting(struct bnad *bnad);
+void bnad_set_ethtool_ops(struct net_device *netdev);
+void bnad_ioctl_init(void);
+void bnad_ioctl_exit(void);
+struct net_device_stats *bnad_get_stats(struct net_device *netdev);
+void bnad_reset_stats(struct net_device *netdev);
+
+int bnad_ucast_mac(struct bnad *bnad, unsigned int rxf_id,
+ u8 *mac_ptr, unsigned int cmd);
+int bnad_rxq_init(struct bnad *bnad, uint rxq_id);
+void bnad_setup_rxq(struct bnad *bnad, uint rxq_id);
+void bnad_alloc_for_rxq(struct bnad *bnad, uint rxq_id);
+void bnad_free_rxq(struct bnad *bnad, uint rxq_id);
+int bnad_cq_init(struct bnad *bnad, uint cq_id);
+void bnad_setup_cq(struct bnad *bnad, uint cq_id);
+int bnad_alloc_ib(struct bnad *bnad, uint ib_id);
+void bnad_setup_ib(struct bnad *bnad, uint ib_id);
+void bnad_rxib_init(struct bnad *bnad, uint cq_id, uint ib_id);
+void bnad_free_ib(struct bnad *bnad, uint ib_id);
+int bnad_request_cq_irq(struct bnad *bnad, uint cq_id);
+u32 bnad_get_msglevel(struct net_device *netdev);
+void bnad_set_msglevel(struct net_device *netdev, u32 msglevel);
+int bnad_alloc_unmap_q(struct bnad_unmap_q *unmap_q, u32 q_depth);
+int bnad_disable_rxq(struct bnad *bnad, u32 rxq_id);
+void bnad_free_cq(struct bnad *bnad, uint cq_id);
+void bnad_add_to_list(struct bnad *bnad);
+void bnad_remove_from_list(struct bnad *bnad);
+struct bnad *get_bnadev(int bna_id);
+/* For diagnostics */
+int bnad_diag_lb_rx(struct bnad *bnad, struct sk_buff *skb);
+int bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev);
+
+#endif /* _BNAD_H_ */
^ permalink raw reply
* [net-next-2.6 PATCH 1/4 revised] TCPCT part 1a: extend struct tcp_request_sock
From: William Allen Simpson @ 2009-10-16 17:39 UTC (permalink / raw)
To: Linux Kernel Network Developers
[-- Attachment #1: Type: text/plain, Size: 489 bytes --]
Pass additional parameters associated with sending SYNACK. This
is not as straightforward or architecturally clean as previously
proposed, and has the unfortunate side effect of potentially
including otherwise unneeded headers for related protocols, but
that problem will affect very few files.
---
include/net/extend_request_sock.h | 37 +++++++++++++++++++++++++++++++++++++
1 files changed, 37 insertions(+), 0 deletions(-)
create mode 100644 include/net/extend_request_sock.h
[-- Attachment #2: TCPCT+1+1.patch --]
[-- Type: text/plain, Size: 1247 bytes --]
diff --git a/include/net/extend_request_sock.h b/include/net/extend_request_sock.h
new file mode 100644
index 0000000..c991af9
--- /dev/null
+++ b/include/net/extend_request_sock.h
@@ -0,0 +1,37 @@
+#ifndef _EXTEND_REQUEST_SOCK_H
+#define _EXTEND_REQUEST_SOCK_H
+/*
+ * Short term extension of struct tcp_request_sock and related.
+ *
+ * Copyright (C) 2009 William.Allen.Simpson@gmail.com
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/tcp.h>
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+#include <linux/ipv6.h>
+#endif
+
+struct extend_request_sock {
+ union {
+ struct tcp_request_sock tcp4rsk;
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+ struct tcp6_request_sock tcp6rsk;
+#endif
+ } header;
+
+ u8 cookie_plus;
+ u8 cookie_in_always:1,
+ cookie_out_never:1;
+};
+
+static inline struct extend_request_sock *ext_rsk(const struct request_sock *req)
+{
+ return (struct extend_request_sock *)req;
+}
+
+#endif /* _EXTEND_REQUEST_SOCK_H */
--
1.6.0.4
^ permalink raw reply related
* [PATCH net-next-2.6] filter: Add SKF_AD_QUEUE instruction
From: Eric Dumazet @ 2009-10-16 17:10 UTC (permalink / raw)
To: David S. Miller, Linux Netdev List
It can help being able to filter packets on their queue_mapping.
If filter performance is not good, we could add a "numqueue" field
in struct packet_type, so that netif_nit_deliver() and other functions
can directly ignore packets with not expected queue number.
Lets experiment this simple filter extension first.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 1354aaf..a93f885 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -123,7 +123,8 @@ struct sock_fprog /* Required for SO_ATTACH_FILTER. */
#define SKF_AD_IFINDEX 8
#define SKF_AD_NLATTR 12
#define SKF_AD_NLATTR_NEST 16
-#define SKF_AD_MAX 20
+#define SKF_AD_QUEUE 20
+#define SKF_AD_MAX 24
#define SKF_NET_OFF (-0x100000)
#define SKF_LL_OFF (-0x200000)
diff --git a/net/core/filter.c b/net/core/filter.c
index d1d779c..0fc83f7 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -303,6 +303,9 @@ load_b:
case SKF_AD_IFINDEX:
A = skb->dev->ifindex;
continue;
+ case SKF_AD_QUEUE:
+ A = skb->queue_mapping;
+ continue;
case SKF_AD_NLATTR: {
struct nlattr *nla;
^ permalink raw reply related
* Re: not supported broadcom (?) NIC revision
From: Michael Chan @ 2009-10-16 16:30 UTC (permalink / raw)
To: Wojtek Sawasciuk
Cc: netdev@vger.kernel.org, davem@davemloft.net, jgarzik@pobox.com
In-Reply-To: <4AD86F2F.90809@no-ip.pl>
On Fri, 2009-10-16 at 06:03 -0700, Wojtek Sawasciuk wrote:
> Hi,
>
> I'v got server with built-in 9 NIC ports. Two ports are not recognized
> by any driver, I suppose it *should* be by tg3 or bnx2
> Below is lspci info, not supported NIC are in slots 00:10.0 and 00:10.2
> strange thing is that this vendor is described as Serverworks, but lspci
> says its Broadcom.
ServerWorks was bought by Broadcom many years ago.
>
> # grep 1166 include/linux/pci_ids.h
> #define PCI_VENDOR_ID_SERVERWORKS 0x1166
>
> Can someone help here ?
>
>
> # lspci
> 00:00.0 Host bridge: Broadcom GCNB-LE Host Bridge (rev 32)
> 00:00.1 Host bridge: Broadcom GCNB-LE Host Bridge
> 00:02.0 Ethernet controller: Intel Corporation 82541GI Gigabit Ethernet
> Controller (rev 05)
> 00:0f.0 ISA bridge: Broadcom CSB5 South Bridge (rev 93)
> 00:0f.1 IDE interface: Broadcom CSB5 IDE Controller (rev 93)
> 00:0f.2 USB Controller: Broadcom OSB4/CSB5 OHCI USB Controller (rev 05)
> 00:0f.3 Host bridge: Broadcom CSB5 LPC bridge
>
> 00:10.0 Host bridge: Broadcom CIOB-E I/O Bridge with Gigabit Ethernet
> (rev 12)
> 00:10.2 Host bridge: Broadcom CIOB-E I/O Bridge with Gigabit Ethernet
> (rev 12)
These 2 are bridge devices and no driver is needed. I think the
integrated ethernet device should be behind these bridges showing up as
BCM5704 below.
>
> 02:02.0 Ethernet controller: Intel Corporation 82546GB Gigabit Ethernet
> Controller (rev 03)
> 02:02.1 Ethernet controller: Intel Corporation 82546GB Gigabit Ethernet
> Controller (rev 03)
> 02:04.0 Ethernet controller: Intel Corporation 82546GB Gigabit Ethernet
> Controller (rev 03)
> 02:04.1 Ethernet controller: Intel Corporation 82546GB Gigabit Ethernet
> Controller (rev 03)
> 03:00.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5704
> Gigabit Ethernet (rev 02)
> 03:00.1 Ethernet controller: Broadcom Corporation NetXtreme BCM5704
> Gigabit Ethernet (rev 02)
>
>
> 00:10.0 0600: 1166:0110 (rev 12)
> Control: I/O- Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop-
> ParErr- Stepping- SERR+ FastB2B-
> Status: Cap+ 66MHz+ UDF- FastB2B- ParErr- DEVSEL=medium >TAbort-
> <TAbort- <MAbort+ >SERR- <PERR-
> Capabilities: [60] PCI-X non-bridge device
> Command: DPERE- ERO- RBC=512 OST=8
> Status: Dev=00:00.0 64bit+ 133MHz+ SCD- USC- DC=bridge
> DMMRBC=512 DMOST=8 DMCRS=8 RSCEM- 266MHz- 533MHz-
>
> 00:10.2 0600: 1166:0110 (rev 12)
> Control: I/O- Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop-
> ParErr- Stepping- SERR+ FastB2B-
> Status: Cap+ 66MHz+ UDF- FastB2B- ParErr- DEVSEL=medium >TAbort-
> <TAbort- <MAbort+ >SERR- <PERR-
> Capabilities: [60] PCI-X non-bridge device
> Command: DPERE- ERO- RBC=512 OST=8
> Status: Dev=00:00.0 64bit+ 133MHz+ SCD- USC- DC=bridge
> DMMRBC=512 DMOST=8 DMCRS=8 RSCEM- 266MHz- 533MHz-
>
>
>
>
>
>
^ permalink raw reply
* [PATCH net-next-2.6] af_packet: mc_drop/flush_mclist changes
From: Eric Dumazet @ 2009-10-16 16:38 UTC (permalink / raw)
To: David S. Miller; +Cc: Linux Netdev List
We hold RTNL, we can use __dev_get_by_index() instead of dev_get_by_index()
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index bf3a295..12b6d5b 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1664,11 +1664,9 @@ static int packet_mc_drop(struct sock *sk, struct packet_mreq_max *mreq)
if (--ml->count == 0) {
struct net_device *dev;
*mlp = ml->next;
- dev = dev_get_by_index(sock_net(sk), ml->ifindex);
- if (dev) {
+ dev = __dev_get_by_index(sock_net(sk), ml->ifindex);
+ if (dev)
packet_dev_mc(dev, ml, -1);
- dev_put(dev);
- }
kfree(ml);
}
rtnl_unlock();
@@ -1692,11 +1690,9 @@ static void packet_flush_mclist(struct sock *sk)
struct net_device *dev;
po->mclist = ml->next;
- dev = dev_get_by_index(sock_net(sk), ml->ifindex);
- if (dev != NULL) {
+ dev = __dev_get_by_index(sock_net(sk), ml->ifindex);
+ if (dev != NULL)
packet_dev_mc(dev, ml, -1);
- dev_put(dev);
- }
kfree(ml);
}
rtnl_unlock();
^ permalink raw reply related
* [Fwd: [PATCH] [NIU] VLAN does not work with niu driver]
From: Joyce Yu @ 2009-10-16 16:21 UTC (permalink / raw)
To: netdev
[-- Attachment #1: Type: text/plain, Size: 839 bytes --]
Can this patch be accepted and integrated to the main tree?
Thanks,
Joyce
=============================
From eb878a6887fed77e6b4c69a014a2c98ea2b52736 Mon Sep 17 00:00:00 2001
From: Joyce Yu <joyce.yu@sun.com>
Date: Thu, 15 Oct 2009 06:49:29 -0700
Subject: [PATCH] VLAN does not work with niu driver
---
drivers/net/niu.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index f9364d0..d6c7ac6 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -3545,7 +3545,7 @@ static int niu_process_rx_pkt(struct napi_struct
*napi, struct niu *np,
rp->rcr_index = index;
skb_reserve(skb, NET_IP_ALIGN);
- __pskb_pull_tail(skb, min(len, NIU_RXPULL_MAX));
+ __pskb_pull_tail(skb, min(len, VLAN_ETH_HLEN));
rp->rx_packets++;
rp->rx_bytes += skb->len;
-- 1.6.4
[-- Attachment #2: 0001-VLAN-does-not-work-with-niu-driver.patch --]
[-- Type: text/x-patch, Size: 728 bytes --]
>From eb878a6887fed77e6b4c69a014a2c98ea2b52736 Mon Sep 17 00:00:00 2001
From: Joyce Yu <joyce.yu@sun.com>
Date: Thu, 15 Oct 2009 06:49:29 -0700
Subject: [PATCH] VLAN does not work with niu driver
---
drivers/net/niu.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index f9364d0..d6c7ac6 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -3545,7 +3545,7 @@ static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np,
rp->rcr_index = index;
skb_reserve(skb, NET_IP_ALIGN);
- __pskb_pull_tail(skb, min(len, NIU_RXPULL_MAX));
+ __pskb_pull_tail(skb, min(len, VLAN_ETH_HLEN));
rp->rx_packets++;
rp->rx_bytes += skb->len;
--
1.6.4
^ permalink raw reply related
* Re: [PATCH 6/8] trivial: fix typo s/refference/reference/ in comment
From: Marcel Holtmann @ 2009-10-16 16:08 UTC (permalink / raw)
To: Jiri Kosina
Cc: Thadeu Lima de Souza Cascardo, David S. Miller, Stephen Hemminger,
Wang Chen, linux-bluetooth, netdev, linux-kernel
In-Reply-To: <alpine.LSU.2.00.0910161524480.8582@wotan.suse.de>
Hi Jiri,
> On Thu, 15 Oct 2009, Thadeu Lima de Souza Cascardo wrote:
>
> > diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
> > index cafe9f5..1bd9398 100644
> > --- a/net/bluetooth/bnep/core.c
> > +++ b/net/bluetooth/bnep/core.c
> > @@ -78,7 +78,7 @@ static struct bnep_session *__bnep_get_session(u8 *dst)
> > static void __bnep_link_session(struct bnep_session *s)
> > {
> > /* It's safe to call __module_get() here because sessions are added
> > - by the socket layer which has to hold the refference to this module.
> > + by the socket layer which has to hold the reference to this module.
> > */
> > __module_get(THIS_MODULE);
> > list_add(&s->list, &bnep_session_list);
>
> Merged together with patches 2-8 from your series and applied to trivial
> queue.
trivial, but neverless ...
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Regards
Marcel
^ permalink raw reply
* Re: [PATCH 2/4 v2] net: Use sk_tx_queue_mapping for connected sockets
From: Krishna Kumar2 @ 2009-10-16 15:47 UTC (permalink / raw)
To: Eric Dumazet; +Cc: davem, herbert, netdev
In-Reply-To: <4AD87E22.9040500@gmail.com>
Hi Eric,
Eric Dumazet <eric.dumazet@gmail.com> wrote on 10/16/2009 07:37:30 PM:
> > I wait for tcp_v4_syn_recv_sock to finish and create a connection.
> > sk_setup_caps/__sk_dst_set are called for the new socket resulting
> > in sk_dst_cache getting set (I cannot set the cache till the
> > connection is finished and dst is set). I guess that means that I
> > will wait for another skb to come and the response will use skb_tx_hash
> > instead of using the rx recorded.
>
> Well, I see no relation between rx mapping and tx mapping currently.
You are suggesting an enhancement to assign the txq# at ack processing
using the skb->rx#. As you said it will help cards that are hashing the
rx# internally. I will try that separately after this patch.
> > Will this suffice, but if not, is it something I should handle or an
> > add-on patch should do instead?
> >
>
> Your patch is fine, I was only giving ideas for future patches :)
Thanks for the suggestions, I just wanted to make sure.
- KK
^ permalink raw reply
* Re: PATCH: Network Device Naming mechanism and policy
From: dann frazier @ 2009-10-16 15:41 UTC (permalink / raw)
To: Ben Hutchings
Cc: Narendra_K, netdev, linux-hotplug, Matt_Domsch, Jordan_Hargrave,
Charles_Rose
In-Reply-To: <1255707193.2869.12.camel@achroite>
On Fri, Oct 16, 2009 at 04:33:13PM +0100, Ben Hutchings wrote:
> On Fri, 2009-10-16 at 09:20 -0600, dann frazier wrote:
> > On Fri, Oct 16, 2009 at 07:32:50PM +0530, Narendra_K@Dell.com wrote:
> [...]
> > > And how would the regular file look like in terms of holding ifindex of
> > > the interface, which can be passed to libnetdevname.
> >
> > I can't think of anything we need to store in the regular file. If we
> > have the kernel name for the device, we can look up the ifindex in
> > /sys. Correct me if I'm wrong, but storing it ourselves seems
> > redundant.
>
> But the name of a netdev can change whereas its ifindex never does.
> Identifying netdevs by name would require additional work to update the
> links when a netdev is renamed and would still be prone to race
> conditions. This is why Narendra and Matt were proposing to store the
> ifindex in the node all along...
ah, yes - I see that now - the ability to rename an interface is what
prevents this from working. Thanks for the explanation.
--
dann frazier
^ permalink raw reply
* Re: PATCH: Network Device Naming mechanism and policy
From: Ben Hutchings @ 2009-10-16 15:33 UTC (permalink / raw)
To: dann frazier
Cc: Narendra_K, netdev, linux-hotplug, Matt_Domsch, Jordan_Hargrave,
Charles_Rose
In-Reply-To: <20091016152053.GA9862@ldl.fc.hp.com>
On Fri, 2009-10-16 at 09:20 -0600, dann frazier wrote:
> On Fri, Oct 16, 2009 at 07:32:50PM +0530, Narendra_K@Dell.com wrote:
[...]
> > And how would the regular file look like in terms of holding ifindex of
> > the interface, which can be passed to libnetdevname.
>
> I can't think of anything we need to store in the regular file. If we
> have the kernel name for the device, we can look up the ifindex in
> /sys. Correct me if I'm wrong, but storing it ourselves seems
> redundant.
But the name of a netdev can change whereas its ifindex never does.
Identifying netdevs by name would require additional work to update the
links when a netdev is renamed and would still be prone to race
conditions. This is why Narendra and Matt were proposing to store the
ifindex in the node all along...
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* Re: PATCH: Network Device Naming mechanism and policy
From: dann frazier @ 2009-10-16 15:20 UTC (permalink / raw)
To: Narendra_K
Cc: netdev, linux-hotplug, Matt_Domsch, Jordan_Hargrave, Charles_Rose
In-Reply-To: <EDA0A4495861324DA2618B4C45DCB3EE589579@blrx3m08.blr.amer.dell.com>
On Fri, Oct 16, 2009 at 07:32:50PM +0530, Narendra_K@Dell.com wrote:
>
> >On Tue, Oct 13, 2009 at 11:36:38AM -0600, dann frazier wrote:
> >> Right - so any reason this couldn't be implemented completely in
> >> userspace by having udev manipulate plain text files under say
> >> /etc/udev/net/?
> >>
> >> I do agree that it would be nice for admins/installers to tweak/use
> >> nic names in a similar way to storage names (udev rules),
> >and it might
> >> let us take advantage of a lot of the existing udev code.
> >
> >Is there interest in this approach?
> > - modify udev to manage network devices names as regular (non-device)
> > files (stored in /etc/udev, /dev/netdev, or wherever)
>
> Yes. Would you elaborate little more on "modify udev to manage network
> devices as regular files".
Sure. We already get an event when netifs get added/removed - udev
just doesn't create a device file for it. And since all we care about
is the file's name (and the symlinks to it), there's really no point
in creating a real device file anyway.
So, instead of 'mknod /dev/netdev/eth0', why not just 'touch
/dev/netdev/eth0'? A file exists, so we can still maintain
aliases as symlinks, we just don't need to modify the kernel.
> Does it mean some custom rules which will
> generate a regular file under, say, /dev/netdev/ or extend udev
> itself ?
I believe we have to extend udev itself. We could probably do this
completely within udev rules by running programs that do the touching
and symlinking, but it would be nicer and more consistent/familiar to
take advantage of the udev syntax (SYMLINK) to do this
natively. Besides, udev already has the logic to know when/how to
instantiate and unlink symlinks, it would suck to duplicate that.
So, udev would need to be modified to know how to go through the
normal "node" creation for net devices, and to call creat() instead of
mknod().
> And how would the regular file look like in terms of holding ifindex of
> the interface, which can be passed to libnetdevname.
I can't think of anything we need to store in the regular file. If we
have the kernel name for the device, we can look up the ifindex in
/sys. Correct me if I'm wrong, but storing it ourselves seems
redundant.
>
>
> > - use the existing udev rules to manage symlinks to these files
> > - point libnetdevname at these text files for its name resolution
> >
> >I've started prototyping this, and it certainly looks possible
> >w/o any kernel changes. However, I could probably use some
> >advice from a udev person to do a proper implementation.
>
> With regards,
> Narendra K
--
dann frazier
^ permalink raw reply
* TCP_NODELAY and CORK - should they be added for network fs case?
From: Steve French @ 2009-10-16 14:53 UTC (permalink / raw)
To: netdev
Looking at calls to kernel_sendmsg, and thinking about why I only see
a few places that do TCP_NODELAY and TCP_CORK in kernel.
Looking at the cifs example. cifs is trying to send packets which
vary from about 50-100 bytes for common calls (like lookup) to about
56K for file writes (can be larger if override wsize and max buffer
size via insmod parameter), and cifs always uses kernel_sendmsg. For
the cifs case, sending individual SMB/CIFS requests to a particular
server (socket) are serialized, protected by a mutex, even if many
processes are writing to different remote files at one time.
Usually one kernel_sendmsg is all that is needed to send an SMB
request - does kernel_sendmsg implicitly "cork" the request so that
the SMB is not unnecessarily fragmented? If the socket is full, and
only a few bytes are sent, multiple sendmsg's may be required to send
one smb - should cifs be doing a cork before the loop which calls
kernel_sendmsg in smb_sendv in fs/cifs/transport.c and uncork
afterward (since the server can't do much processing without getting
the whole SMB request except in one narrow case of receivefile on
certain write requests)? Especially if we add code to allow setting
"TCP_NODELAY" ... to improve GigE performance
Are there any cases where we should be setting LOWDELAY instead for
this kind of socket?
--
Thanks,
Steve
^ permalink raw reply
* Re: [PATCH 2/4 v2] net: Use sk_tx_queue_mapping for connected sockets
From: Eric Dumazet @ 2009-10-16 14:07 UTC (permalink / raw)
To: Krishna Kumar2; +Cc: davem, herbert, netdev
In-Reply-To: <OFAE5CC975.C26B600E-ON65257651.00401683-65257651.004721BC@in.ibm.com>
Krishna Kumar2 a écrit :
> I wait for tcp_v4_syn_recv_sock to finish and create a connection.
> sk_setup_caps/__sk_dst_set are called for the new socket resulting
> in sk_dst_cache getting set (I cannot set the cache till the
> connection is finished and dst is set). I guess that means that I
> will wait for another skb to come and the response will use skb_tx_hash
> instead of using the rx recorded.
Well, I see no relation between rx mapping and tx mapping currently.
>
> Will this suffice, but if not, is it something I should handle or an
> add-on patch should do instead?
>
Your patch is fine, I was only giving ideas for future patches :)
^ permalink raw reply
* RE: PATCH: Network Device Naming mechanism and policy
From: Narendra_K @ 2009-10-16 14:02 UTC (permalink / raw)
To: dannf; +Cc: netdev, linux-hotplug, Matt_Domsch, Jordan_Hargrave, Charles_Rose
In-Reply-To: <20091016003245.GD29672@ldl.fc.hp.com>
>On Tue, Oct 13, 2009 at 11:36:38AM -0600, dann frazier wrote:
>> Right - so any reason this couldn't be implemented completely in
>> userspace by having udev manipulate plain text files under say
>> /etc/udev/net/?
>>
>> I do agree that it would be nice for admins/installers to tweak/use
>> nic names in a similar way to storage names (udev rules),
>and it might
>> let us take advantage of a lot of the existing udev code.
>
>Is there interest in this approach?
> - modify udev to manage network devices names as regular (non-device)
> files (stored in /etc/udev, /dev/netdev, or wherever)
Yes. Would you elaborate little more on "modify udev to manage network
devices as regular files". Does it mean some custom rules which will
generate a regular file under, say, /dev/netdev/ or extend udev itself ?
And how would the regular file look like in terms of holding ifindex of
the interface, which can be passed to libnetdevname.
> - use the existing udev rules to manage symlinks to these files
> - point libnetdevname at these text files for its name resolution
>
>I've started prototyping this, and it certainly looks possible
>w/o any kernel changes. However, I could probably use some
>advice from a udev person to do a proper implementation.
With regards,
Narendra K
^ permalink raw reply
* [PATCH] af_packet: Avoid cache line dirtying
From: Eric Dumazet @ 2009-10-16 14:02 UTC (permalink / raw)
To: David S. Miller; +Cc: Linux Netdev List, Luca Deri
While doing multiple captures, I found af_packet was dirtying cache line
containing its prot_hook.
This slow down machines where several cpus are necessary to handle capture
traffic, as each prot_hook is traversed for each packet coming in or out
the host.
This patches moves "struct packet_type prot_hook" to the end of
packet_sock, and uses a ____cacheline_aligned_in_smp to make sure
this remains shared by all cpus.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index bf3a295..dac775e 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -188,7 +188,6 @@ struct packet_sock {
struct packet_ring_buffer tx_ring;
int copy_thresh;
#endif
- struct packet_type prot_hook;
spinlock_t bind_lock;
struct mutex pg_vec_lock;
unsigned int running:1, /* prot_hook is attached*/
@@ -204,6 +203,7 @@ struct packet_sock {
unsigned int tp_reserve;
unsigned int tp_loss:1;
#endif
+ struct packet_type prot_hook ____cacheline_aligned_in_smp;
};
struct packet_skb_cb {
^ permalink raw reply related
* not supported broadcom (?) NIC revision
From: Wojtek Sawasciuk @ 2009-10-16 13:03 UTC (permalink / raw)
To: netdev; +Cc: mchan, davem, jgarzik
Hi,
I'v got server with built-in 9 NIC ports. Two ports are not recognized
by any driver, I suppose it *should* be by tg3 or bnx2
Below is lspci info, not supported NIC are in slots 00:10.0 and 00:10.2
strange thing is that this vendor is described as Serverworks, but lspci
says its Broadcom.
# grep 1166 include/linux/pci_ids.h
#define PCI_VENDOR_ID_SERVERWORKS 0x1166
Can someone help here ?
# lspci
00:00.0 Host bridge: Broadcom GCNB-LE Host Bridge (rev 32)
00:00.1 Host bridge: Broadcom GCNB-LE Host Bridge
00:02.0 Ethernet controller: Intel Corporation 82541GI Gigabit Ethernet
Controller (rev 05)
00:0f.0 ISA bridge: Broadcom CSB5 South Bridge (rev 93)
00:0f.1 IDE interface: Broadcom CSB5 IDE Controller (rev 93)
00:0f.2 USB Controller: Broadcom OSB4/CSB5 OHCI USB Controller (rev 05)
00:0f.3 Host bridge: Broadcom CSB5 LPC bridge
00:10.0 Host bridge: Broadcom CIOB-E I/O Bridge with Gigabit Ethernet
(rev 12)
00:10.2 Host bridge: Broadcom CIOB-E I/O Bridge with Gigabit Ethernet
(rev 12)
02:02.0 Ethernet controller: Intel Corporation 82546GB Gigabit Ethernet
Controller (rev 03)
02:02.1 Ethernet controller: Intel Corporation 82546GB Gigabit Ethernet
Controller (rev 03)
02:04.0 Ethernet controller: Intel Corporation 82546GB Gigabit Ethernet
Controller (rev 03)
02:04.1 Ethernet controller: Intel Corporation 82546GB Gigabit Ethernet
Controller (rev 03)
03:00.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5704
Gigabit Ethernet (rev 02)
03:00.1 Ethernet controller: Broadcom Corporation NetXtreme BCM5704
Gigabit Ethernet (rev 02)
00:10.0 0600: 1166:0110 (rev 12)
Control: I/O- Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop-
ParErr- Stepping- SERR+ FastB2B-
Status: Cap+ 66MHz+ UDF- FastB2B- ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort+ >SERR- <PERR-
Capabilities: [60] PCI-X non-bridge device
Command: DPERE- ERO- RBC=512 OST=8
Status: Dev=00:00.0 64bit+ 133MHz+ SCD- USC- DC=bridge
DMMRBC=512 DMOST=8 DMCRS=8 RSCEM- 266MHz- 533MHz-
00:10.2 0600: 1166:0110 (rev 12)
Control: I/O- Mem+ BusMaster- SpecCycle- MemWINV- VGASnoop-
ParErr- Stepping- SERR+ FastB2B-
Status: Cap+ 66MHz+ UDF- FastB2B- ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort+ >SERR- <PERR-
Capabilities: [60] PCI-X non-bridge device
Command: DPERE- ERO- RBC=512 OST=8
Status: Dev=00:00.0 64bit+ 133MHz+ SCD- USC- DC=bridge
DMMRBC=512 DMOST=8 DMCRS=8 RSCEM- 266MHz- 533MHz-
^ permalink raw reply
* [PATCH 2/2] venet: fix locking issue with dev_kfree_skb()
From: Gregory Haskins @ 2009-10-16 13:36 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, alacrityvm-devel
In-Reply-To: <20091016133411.13423.36106.stgit@dev.haskins.net>
We currently hold the priv->lock with interrupts disabled while calling
dev_kfree_skb(). lockdep indicated that this arrangement is problematic
with higher stack components which handle the wmem facility. It is
probably a bad idea to hold the lock/interrupts over the duration of this
function independent of the lock-conflict issue, so lets rectify this.
This new design switches to a finer-grained model, where we acquire/release
the lock for each packet that we reap from the tx queue. This adds
theoretical lock acquistion overhead, but gains the ability to release
the skbs without holding a lock and while improving critical section
granularity.
Signed-off-by: Gregory Haskins <ghaskins@novell.com>
---
drivers/net/vbus-enet.c | 71 +++++++++++++++++++++++------------------------
1 files changed, 35 insertions(+), 36 deletions(-)
diff --git a/drivers/net/vbus-enet.c b/drivers/net/vbus-enet.c
index 9d48674..228c366 100644
--- a/drivers/net/vbus-enet.c
+++ b/drivers/net/vbus-enet.c
@@ -883,7 +883,7 @@ vbus_enet_tx_start(struct sk_buff *skb, struct net_device *dev)
priv->dev->stats.tx_packets++;
priv->dev->stats.tx_bytes += skb->len;
- __skb_queue_tail(&priv->tx.outstanding, skb);
+ skb_queue_tail(&priv->tx.outstanding, skb);
/*
* This advances both indexes together implicitly, and then
@@ -914,7 +914,7 @@ vbus_enet_skb_complete(struct vbus_enet_priv *priv, struct sk_buff *skb)
PDEBUG(priv->dev, "completed sending %d bytes\n",
skb->len);
- __skb_unlink(skb, &priv->tx.outstanding);
+ skb_unlink(skb, &priv->tx.outstanding);
dev_kfree_skb(skb);
}
@@ -923,12 +923,16 @@ vbus_enet_skb_complete(struct vbus_enet_priv *priv, struct sk_buff *skb)
*
* assumes priv->lock held
*/
-static void
-vbus_enet_tx_reap(struct vbus_enet_priv *priv)
+static struct sk_buff *
+vbus_enet_tx_reap_one(struct vbus_enet_priv *priv)
{
+ struct sk_buff *skb = NULL;
struct ioq_iterator iter;
+ unsigned long flags;
int ret;
+ spin_lock_irqsave(&priv->lock, flags);
+
/*
* We want to iterate on the head of the valid index, but we
* do not want the iter_pop (below) to flip the ownership, so
@@ -941,29 +945,15 @@ vbus_enet_tx_reap(struct vbus_enet_priv *priv)
ret = ioq_iter_seek(&iter, ioq_seek_head, 0, 0);
BUG_ON(ret < 0);
- /*
- * We are done once we find the first packet either invalid or still
- * owned by the south-side
- */
- while (iter.desc->valid && !iter.desc->sown) {
-
- if (!priv->evq.txc) {
- struct sk_buff *skb;
+ if (iter.desc->valid && !iter.desc->sown) {
- if (priv->sg) {
- struct venet_sg *vsg;
-
- vsg = (struct venet_sg *)iter.desc->cookie;
- skb = (struct sk_buff *)vsg->cookie;
- } else
- skb = (struct sk_buff *)iter.desc->cookie;
+ if (priv->sg) {
+ struct venet_sg *vsg;
- /*
- * If TXC is not enabled, we are required to free
- * the buffer resources now
- */
- vbus_enet_skb_complete(priv, skb);
- }
+ vsg = (struct venet_sg *)iter.desc->cookie;
+ skb = (struct sk_buff *)vsg->cookie;
+ } else
+ skb = (struct sk_buff *)iter.desc->cookie;
/* Reset the descriptor */
iter.desc->valid = 0;
@@ -982,19 +972,35 @@ vbus_enet_tx_reap(struct vbus_enet_priv *priv)
PDEBUG(priv->dev, "re-enabling tx queue\n");
netif_wake_queue(priv->dev);
}
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return skb;
+}
+
+static void
+vbus_enet_tx_reap(struct vbus_enet_priv *priv)
+{
+ struct sk_buff *skb;
+
+ while ((skb = vbus_enet_tx_reap_one(priv))) {
+ if (!priv->evq.txc)
+ /*
+ * We are responsible for freeing the packet upon
+ * reap if TXC is not enabled
+ */
+ vbus_enet_skb_complete(priv, skb);
+ }
}
static void
vbus_enet_timeout(struct net_device *dev)
{
struct vbus_enet_priv *priv = netdev_priv(dev);
- unsigned long flags;
dev_dbg(&dev->dev, "Transmit timeout\n");
- spin_lock_irqsave(&priv->lock, flags);
vbus_enet_tx_reap(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
}
static void
@@ -1014,13 +1020,10 @@ static void
deferred_tx_isr(unsigned long data)
{
struct vbus_enet_priv *priv = (struct vbus_enet_priv *)data;
- unsigned long flags;
PDEBUG(priv->dev, "deferred_tx_isr\n");
- spin_lock_irqsave(&priv->lock, flags);
vbus_enet_tx_reap(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
ioq_notify_enable(priv->tx.veq.queue, 0);
}
@@ -1063,14 +1066,10 @@ evq_txc_event(struct vbus_enet_priv *priv,
{
struct venet_event_txc *event =
(struct venet_event_txc *)header;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
vbus_enet_tx_reap(priv);
- vbus_enet_skb_complete(priv, (struct sk_buff *)event->cookie);
- spin_unlock_irqrestore(&priv->lock, flags);
+ vbus_enet_skb_complete(priv, (struct sk_buff *)event->cookie);
}
static void
^ permalink raw reply related
* [PATCH 1/2] net: fix vbus-enet Kconfig dependencies
From: Gregory Haskins @ 2009-10-16 13:36 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, alacrityvm-devel
In-Reply-To: <20091016133411.13423.36106.stgit@dev.haskins.net>
We currently select VBUS_PROXY when vbus-enet is enabled, which is
the wrong direction. Not all platforms will define VBUS-PROXY, and
venet depends on its inclusion. Therefore, lets fix vbus-enet to
properly depend on the presence of VBUS_PROXY to get this right.
Signed-off-by: Gregory Haskins <ghaskins@novell.com>
---
drivers/net/Kconfig | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 47dfa04..c9128ea 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -3233,7 +3233,7 @@ config VIRTIO_NET
config VBUS_ENET
tristate "VBUS Ethernet Driver"
default n
- select VBUS_PROXY
+ depends on VBUS_PROXY
help
A virtualized 802.x network device based on the VBUS
"virtual-ethernet" interface. It can be used with any
^ permalink raw reply related
* [PATCH 0/2] net: vbus-enet fixes
From: Gregory Haskins @ 2009-10-16 13:36 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, alacrityvm-devel
The following apply to the linux-next branch for AlacrityVM:
git://git.kernel.org/pub/scm/linux/kernel/git/ghaskins/alacrityvm/linux-2.6.git
The series fixes a few problems discovered via community feedback and
lockdep. Please see the patch headers for more details.
Kind Regards,
-Greg
---
Gregory Haskins (2):
venet: fix locking issue with dev_kfree_skb()
net: fix vbus-enet Kconfig dependencies
drivers/net/Kconfig | 2 +
drivers/net/vbus-enet.c | 71 +++++++++++++++++++++++------------------------
2 files changed, 36 insertions(+), 37 deletions(-)
--
Signature
^ permalink raw reply
* Re: [PATCH 4/8] trivial: fix some typos and punctuation in comments
From: Jiri Kosina @ 2009-10-16 13:27 UTC (permalink / raw)
To: Thadeu Lima de Souza Cascardo
Cc: linville, IvDoorn, johannes, users, linux-kernel, linux-wireless,
netdev
In-Reply-To: <1255636103-5817-1-git-send-email-cascardo@holoscopio.com>
On Thu, 15 Oct 2009, Thadeu Lima de Souza Cascardo wrote:
> Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
> ---
> drivers/net/wireless/rt2x00/rt61pci.c | 36 ++++++++++++++++----------------
> 1 files changed, 18 insertions(+), 18 deletions(-)
Merged together with patches 2-8 from your series and applied to trivial
queue.
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply
* Re: [PATCH 5/8] trivial: fix typo s/assocate/associate/ in comment
From: Jiri Kosina @ 2009-10-16 13:25 UTC (permalink / raw)
To: Thadeu Lima de Souza Cascardo
Cc: Jiri Slaby, Nick Kossifidis, Luis R. Rodriguez, Bob Copeland,
John W. Linville, linux-wireless, ath5k-devel, netdev,
linux-kernel
In-Reply-To: <1255637738-6069-1-git-send-email-cascardo@holoscopio.com>
On Thu, 15 Oct 2009, Thadeu Lima de Souza Cascardo wrote:
> Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
> ---
> drivers/net/wireless/ath/ath5k/base.h | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h
> index b14ba07..b73e7d3 100644
> --- a/drivers/net/wireless/ath/ath5k/base.h
> +++ b/drivers/net/wireless/ath/ath5k/base.h
> @@ -192,7 +192,7 @@ struct ath5k_softc {
> struct ath5k_txq *cabq; /* content after beacon */
>
> int power_level; /* Requested tx power in dbm */
> - bool assoc; /* assocate state */
> + bool assoc; /* associate state */
> bool enable_beacon; /* true if beacons are on */
> };
Merged together with patches 2-8 from your series and applied to trivial
queue.
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply
* Re: [PATCH 6/8] trivial: fix typo s/refference/reference/ in comment
From: Jiri Kosina @ 2009-10-16 13:25 UTC (permalink / raw)
To: Thadeu Lima de Souza Cascardo
Cc: Marcel Holtmann, David S. Miller, Stephen Hemminger, Wang Chen,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1255637783-6109-1-git-send-email-cascardo-DmMZpsCg3uxeGPcbtGPokg@public.gmane.org>
On Thu, 15 Oct 2009, Thadeu Lima de Souza Cascardo wrote:
> diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
> index cafe9f5..1bd9398 100644
> --- a/net/bluetooth/bnep/core.c
> +++ b/net/bluetooth/bnep/core.c
> @@ -78,7 +78,7 @@ static struct bnep_session *__bnep_get_session(u8 *dst)
> static void __bnep_link_session(struct bnep_session *s)
> {
> /* It's safe to call __module_get() here because sessions are added
> - by the socket layer which has to hold the refference to this module.
> + by the socket layer which has to hold the reference to this module.
> */
> __module_get(THIS_MODULE);
> list_add(&s->list, &bnep_session_list);
Merged together with patches 2-8 from your series and applied to trivial
queue.
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply
* Re: [PATCH 7/8] trivial: fix many typos s/untill/until/
From: Jiri Kosina @ 2009-10-16 13:24 UTC (permalink / raw)
To: Thadeu Lima de Souza Cascardo
Cc: linux-rdma-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
bonding-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
users-IdT//7ivld+3fTS+yoWyeR8Sddau0WhX,
linux-scsi-u79uwXL29TY76Z2rM5mHXA,
devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
linux-ext4-u79uwXL29TY76Z2rM5mHXA,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
linux-sctp-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1255638072-6236-1-git-send-email-cascardo-DmMZpsCg3uxeGPcbtGPokg@public.gmane.org>
On Thu, 15 Oct 2009, Thadeu Lima de Souza Cascardo wrote:
> Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo-DmMZpsCg3uxeGPcbtGPokg@public.gmane.org>
> ---
> drivers/infiniband/ulp/iser/iser_verbs.c | 2 +-
> drivers/net/bonding/bond_alb.c | 2 +-
> drivers/net/wireless/rt2x00/rt2800usb.c | 2 +-
> drivers/scsi/bnx2i/bnx2i_iscsi.c | 2 +-
> drivers/staging/rtl8187se/r8180.h | 2 +-
> fs/ext4/balloc.c | 2 +-
> net/bluetooth/bnep/core.c | 2 +-
> net/sctp/sm_statefuns.c | 2 +-
> 8 files changed, 8 insertions(+), 8 deletions(-)
Merged together with patches 2-8 from your series and applied to trivial
queue.
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox