summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/chelsio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/chelsio')
-rw-r--r--drivers/net/ethernet/chelsio/Kconfig11
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/Makefile1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h37
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c971
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h141
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c487
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h14
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c37
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c364
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_msg.h7
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_regs.h17
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h99
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c38
16 files changed, 1960 insertions, 270 deletions
diff --git a/drivers/net/ethernet/chelsio/Kconfig b/drivers/net/ethernet/chelsio/Kconfig
index 570222c33410..c3ce9df0041a 100644
--- a/drivers/net/ethernet/chelsio/Kconfig
+++ b/drivers/net/ethernet/chelsio/Kconfig
@@ -86,6 +86,17 @@ config CHELSIO_T4
To compile this driver as a module choose M here; the module
will be called cxgb4.
+config CHELSIO_T4_DCB
+ bool "Data Center Bridging (DCB) Support for Chelsio T4/T5 cards"
+ default n
+ depends on CHELSIO_T4 && DCB
+ ---help---
+ Enable DCB support through rtNetlink interface.
+ Say Y here if you want to enable Data Center Bridging (DCB) support
+ in the driver.
+
+ If unsure, say N.
+
config CHELSIO_T4VF
tristate "Chelsio Communications T4/T5 Virtual Function Ethernet support"
depends on PCI
diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile
index 498667487f52..1df65c915b99 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/Makefile
+++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o
+cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index f503dce4ab17..c9b922cc3e67 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -85,7 +85,8 @@ enum {
MEMWIN1_BASE_T5 = 0x52000,
MEMWIN2_APERTURE = 65536,
MEMWIN2_BASE = 0x30000,
- MEMWIN2_BASE_T5 = 0x54000,
+ MEMWIN2_APERTURE_T5 = 131072,
+ MEMWIN2_BASE_T5 = 0x60000,
};
enum dev_master {
@@ -309,6 +310,9 @@ struct adapter_params {
unsigned int ofldq_wr_cred;
bool ulptx_memwrite_dsgl; /* use of T5 DSGL allowed */
+
+ unsigned int max_ordird_qp; /* Max read depth per RDMA QP */
+ unsigned int max_ird_adapter; /* Max read depth per adapter */
};
#include "t4fw_api.h"
@@ -373,6 +377,8 @@ enum {
struct adapter;
struct sge_rspq;
+#include "cxgb4_dcb.h"
+
struct port_info {
struct adapter *adapter;
u16 viid;
@@ -389,6 +395,9 @@ struct port_info {
u8 rss_mode;
struct link_config link_cfg;
u16 *rss;
+#ifdef CONFIG_CHELSIO_T4_DCB
+ struct port_dcb_info dcb; /* Data Center Bridging support */
+#endif
};
struct dentry;
@@ -603,6 +612,7 @@ struct l2t_data;
struct adapter {
void __iomem *regs;
void __iomem *bar2;
+ u32 t4_bar0;
struct pci_dev *pdev;
struct device *pdev_dev;
unsigned int mbox;
@@ -647,6 +657,7 @@ struct adapter {
struct dentry *debugfs_root;
spinlock_t stats_lock;
+ spinlock_t win0_lock ____cacheline_aligned_in_smp;
};
/* Defined bit width of user definable filter tuples
@@ -855,6 +866,7 @@ void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat);
void *t4_alloc_mem(size_t size);
void t4_free_sge_resources(struct adapter *adap);
+void t4_free_ofld_rxqs(struct adapter *adap, int n, struct sge_ofld_rxq *q);
irq_handler_t t4_intr_handler(struct adapter *adap);
netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev);
int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
@@ -941,6 +953,7 @@ void t4_write_indirect(struct adapter *adap, unsigned int addr_reg,
void t4_read_indirect(struct adapter *adap, unsigned int addr_reg,
unsigned int data_reg, u32 *vals, unsigned int nregs,
unsigned int start_idx);
+void t4_hw_pci_read_cfg4(struct adapter *adapter, int reg, u32 *val);
struct fw_filter_wr;
@@ -952,8 +965,17 @@ int t4_wait_dev_ready(struct adapter *adap);
int t4_link_start(struct adapter *adap, unsigned int mbox, unsigned int port,
struct link_config *lc);
int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port);
-int t4_memory_write(struct adapter *adap, int mtype, u32 addr, u32 len,
- __be32 *buf);
+
+#define T4_MEMORY_WRITE 0
+#define T4_MEMORY_READ 1
+int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, u32 len,
+ __be32 *buf, int dir);
+static inline int t4_memory_write(struct adapter *adap, int mtype, u32 addr,
+ u32 len, __be32 *buf)
+{
+ return t4_memory_rw(adap, 0, mtype, addr, len, buf, 0);
+}
+
int t4_seeprom_wp(struct adapter *adapter, bool enable);
int get_vpd_params(struct adapter *adapter, struct vpd_params *p);
int t4_load_fw(struct adapter *adapter, const u8 *fw_data, unsigned int size);
@@ -1007,6 +1029,10 @@ int t4_query_params(struct adapter *adap, unsigned int mbox, unsigned int pf,
int t4_set_params(struct adapter *adap, unsigned int mbox, unsigned int pf,
unsigned int vf, unsigned int nparams, const u32 *params,
const u32 *val);
+int t4_set_params_nosleep(struct adapter *adap, unsigned int mbox,
+ unsigned int pf, unsigned int vf,
+ unsigned int nparams, const u32 *params,
+ const u32 *val);
int t4_cfg_pfvf(struct adapter *adap, unsigned int mbox, unsigned int pf,
unsigned int vf, unsigned int txq, unsigned int txq_eth_ctrl,
unsigned int rxqi, unsigned int rxq, unsigned int tc,
@@ -1025,6 +1051,8 @@ int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
int idx, const u8 *addr, bool persist, bool add_smt);
int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid,
bool ucast, u64 vec, bool sleep_ok);
+int t4_enable_vi_params(struct adapter *adap, unsigned int mbox,
+ unsigned int viid, bool rx_en, bool tx_en, bool dcb_en);
int t4_enable_vi(struct adapter *adap, unsigned int mbox, unsigned int viid,
bool rx_en, bool tx_en);
int t4_identify_port(struct adapter *adap, unsigned int mbox, unsigned int viid,
@@ -1045,7 +1073,6 @@ int t4_ofld_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf,
int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl);
void t4_db_full(struct adapter *adapter);
void t4_db_dropped(struct adapter *adapter);
-int t4_mem_win_read_len(struct adapter *adap, u32 addr, __be32 *data, int len);
int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox,
u32 addr, u32 val);
void t4_sge_decode_idma_state(struct adapter *adapter, int state);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c
new file mode 100644
index 000000000000..0d3a9df5be36
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c
@@ -0,0 +1,971 @@
+/*
+ * Copyright (C) 2013-2014 Chelsio Communications. All rights reserved.
+ *
+ * Written by Anish Bhatt (anish@chelsio.com)
+ * Casey Leedom (leedom@chelsio.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ */
+
+#include "cxgb4.h"
+
+/* Initialize a port's Data Center Bridging state. Typically used after a
+ * Link Down event.
+ */
+void cxgb4_dcb_state_init(struct net_device *dev)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+ struct port_dcb_info *dcb = &pi->dcb;
+
+ memset(dcb, 0, sizeof(struct port_dcb_info));
+ dcb->state = CXGB4_DCB_STATE_START;
+}
+
+/* Finite State machine for Data Center Bridging.
+ */
+void cxgb4_dcb_state_fsm(struct net_device *dev,
+ enum cxgb4_dcb_state_input input)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+ struct port_dcb_info *dcb = &pi->dcb;
+ struct adapter *adap = pi->adapter;
+
+ switch (input) {
+ case CXGB4_DCB_INPUT_FW_DISABLED: {
+ /* Firmware tells us it's not doing DCB */
+ switch (dcb->state) {
+ case CXGB4_DCB_STATE_START: {
+ /* we're going to use Host DCB */
+ dcb->state = CXGB4_DCB_STATE_HOST;
+ dcb->supported = CXGB4_DCBX_HOST_SUPPORT;
+ dcb->enabled = 1;
+ break;
+ }
+
+ case CXGB4_DCB_STATE_HOST: {
+ /* we're alreaady in Host DCB mode */
+ break;
+ }
+
+ default:
+ goto bad_state_transition;
+ }
+ break;
+ }
+
+ case CXGB4_DCB_INPUT_FW_ENABLED: {
+ /* Firmware tells us that it is doing DCB */
+ switch (dcb->state) {
+ case CXGB4_DCB_STATE_START: {
+ /* we're going to use Firmware DCB */
+ dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE;
+ dcb->supported = CXGB4_DCBX_FW_SUPPORT;
+ break;
+ }
+
+ case CXGB4_DCB_STATE_FW_INCOMPLETE:
+ case CXGB4_DCB_STATE_FW_ALLSYNCED: {
+ /* we're alreaady in firmware DCB mode */
+ break;
+ }
+
+ default:
+ goto bad_state_transition;
+ }
+ break;
+ }
+
+ case CXGB4_DCB_INPUT_FW_INCOMPLETE: {
+ /* Firmware tells us that its DCB state is incomplete */
+ switch (dcb->state) {
+ case CXGB4_DCB_STATE_FW_INCOMPLETE: {
+ /* we're already incomplete */
+ break;
+ }
+
+ case CXGB4_DCB_STATE_FW_ALLSYNCED: {
+ /* We were successfully running with firmware DCB but
+ * now it's telling us that it's in an "incomplete
+ * state. We need to reset back to a ground state
+ * of incomplete.
+ */
+ cxgb4_dcb_state_init(dev);
+ dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE;
+ dcb->supported = CXGB4_DCBX_FW_SUPPORT;
+ linkwatch_fire_event(dev);
+ break;
+ }
+
+ default:
+ goto bad_state_transition;
+ }
+ break;
+ }
+
+ case CXGB4_DCB_INPUT_FW_ALLSYNCED: {
+ /* Firmware tells us that its DCB state is complete */
+ switch (dcb->state) {
+ case CXGB4_DCB_STATE_FW_INCOMPLETE: {
+ dcb->state = CXGB4_DCB_STATE_FW_ALLSYNCED;
+ dcb->enabled = 1;
+ linkwatch_fire_event(dev);
+ break;
+ }
+
+ case CXGB4_DCB_STATE_FW_ALLSYNCED: {
+ /* we're already all sync'ed */
+ break;
+ }
+
+ default:
+ goto bad_state_transition;
+ }
+ break;
+ }
+
+ default:
+ goto bad_state_input;
+ }
+ return;
+
+bad_state_input:
+ dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: illegal input symbol %d\n",
+ input);
+ return;
+
+bad_state_transition:
+ dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: bad state transition, state = %d, input = %d\n",
+ dcb->state, input);
+}
+
+/* Handle a DCB/DCBX update message from the firmware.
+ */
+void cxgb4_dcb_handle_fw_update(struct adapter *adap,
+ const struct fw_port_cmd *pcmd)
+{
+ const union fw_port_dcb *fwdcb = &pcmd->u.dcb;
+ int port = FW_PORT_CMD_PORTID_GET(be32_to_cpu(pcmd->op_to_portid));
+ struct net_device *dev = adap->port[port];
+ struct port_info *pi = netdev_priv(dev);
+ struct port_dcb_info *dcb = &pi->dcb;
+ int dcb_type = pcmd->u.dcb.pgid.type;
+
+ /* Handle Firmware DCB Control messages separately since they drive
+ * our state machine.
+ */
+ if (dcb_type == FW_PORT_DCB_TYPE_CONTROL) {
+ enum cxgb4_dcb_state_input input =
+ ((pcmd->u.dcb.control.all_syncd_pkd &
+ FW_PORT_CMD_ALL_SYNCD)
+ ? CXGB4_DCB_STATE_FW_ALLSYNCED
+ : CXGB4_DCB_STATE_FW_INCOMPLETE);
+
+ cxgb4_dcb_state_fsm(dev, input);
+ return;
+ }
+
+ /* It's weird, and almost certainly an error, to get Firmware DCB
+ * messages when we either haven't been told whether we're going to be
+ * doing Host or Firmware DCB; and even worse when we've been told
+ * that we're doing Host DCB!
+ */
+ if (dcb->state == CXGB4_DCB_STATE_START ||
+ dcb->state == CXGB4_DCB_STATE_HOST) {
+ dev_err(adap->pdev_dev, "Receiving Firmware DCB messages in State %d\n",
+ dcb->state);
+ return;
+ }
+
+ /* Now handle the general Firmware DCB update messages ...
+ */
+ switch (dcb_type) {
+ case FW_PORT_DCB_TYPE_PGID:
+ dcb->pgid = be32_to_cpu(fwdcb->pgid.pgid);
+ dcb->msgs |= CXGB4_DCB_FW_PGID;
+ break;
+
+ case FW_PORT_DCB_TYPE_PGRATE:
+ dcb->pg_num_tcs_supported = fwdcb->pgrate.num_tcs_supported;
+ memcpy(dcb->pgrate, &fwdcb->pgrate.pgrate,
+ sizeof(dcb->pgrate));
+ dcb->msgs |= CXGB4_DCB_FW_PGRATE;
+ break;
+
+ case FW_PORT_DCB_TYPE_PRIORATE:
+ memcpy(dcb->priorate, &fwdcb->priorate.strict_priorate,
+ sizeof(dcb->priorate));
+ dcb->msgs |= CXGB4_DCB_FW_PRIORATE;
+ break;
+
+ case FW_PORT_DCB_TYPE_PFC:
+ dcb->pfcen = fwdcb->pfc.pfcen;
+ dcb->pfc_num_tcs_supported = fwdcb->pfc.max_pfc_tcs;
+ dcb->msgs |= CXGB4_DCB_FW_PFC;
+ break;
+
+ case FW_PORT_DCB_TYPE_APP_ID: {
+ const struct fw_port_app_priority *fwap = &fwdcb->app_priority;
+ int idx = fwap->idx;
+ struct app_priority *ap = &dcb->app_priority[idx];
+
+ struct dcb_app app = {
+ .selector = fwap->sel_field,
+ .protocol = be16_to_cpu(fwap->protocolid),
+ .priority = fwap->user_prio_map,
+ };
+ int err;
+
+ err = dcb_setapp(dev, &app);
+ if (err)
+ dev_err(adap->pdev_dev,
+ "Failed DCB Set Application Priority: sel=%d, prot=%d, prio=%d, err=%d\n",
+ app.selector, app.protocol, app.priority, -err);
+
+ ap->user_prio_map = fwap->user_prio_map;
+ ap->sel_field = fwap->sel_field;
+ ap->protocolid = be16_to_cpu(fwap->protocolid);
+ dcb->msgs |= CXGB4_DCB_FW_APP_ID;
+ break;
+ }
+
+ default:
+ dev_err(adap->pdev_dev, "Unknown DCB update type received %x\n",
+ dcb_type);
+ break;
+ }
+}
+
+/* Data Center Bridging netlink operations.
+ */
+
+
+/* Get current DCB enabled/disabled state.
+ */
+static u8 cxgb4_getstate(struct net_device *dev)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+
+ return pi->dcb.enabled;
+}
+
+/* Set DCB enabled/disabled.
+ */
+static u8 cxgb4_setstate(struct net_device *dev, u8 enabled)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+
+ /* Firmware doesn't provide any mechanism to control the DCB state.
+ */
+ if (enabled != (pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED))
+ return 1;
+
+ return 0;
+}
+
+static void cxgb4_getpgtccfg(struct net_device *dev, int tc,
+ u8 *prio_type, u8 *pgid, u8 *bw_per,
+ u8 *up_tc_map, int local)
+{
+ struct fw_port_cmd pcmd;
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ int err;
+
+ *prio_type = *pgid = *bw_per = *up_tc_map = 0;
+
+ if (local)
+ INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
+ else
+ INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
+
+ pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID;
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err);
+ return;
+ }
+ *pgid = (be32_to_cpu(pcmd.u.dcb.pgid.pgid) >> (tc * 4)) & 0xf;
+
+ INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
+ pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
+ -err);
+ return;
+ }
+
+ *bw_per = pcmd.u.dcb.pgrate.pgrate[*pgid];
+ *up_tc_map = (1 << tc);
+
+ /* prio_type is link strict */
+ *prio_type = 0x2;
+}
+
+static void cxgb4_getpgtccfg_tx(struct net_device *dev, int tc,
+ u8 *prio_type, u8 *pgid, u8 *bw_per,
+ u8 *up_tc_map)
+{
+ return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 1);
+}
+
+
+static void cxgb4_getpgtccfg_rx(struct net_device *dev, int tc,
+ u8 *prio_type, u8 *pgid, u8 *bw_per,
+ u8 *up_tc_map)
+{
+ return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 0);
+}
+
+static void cxgb4_setpgtccfg_tx(struct net_device *dev, int tc,
+ u8 prio_type, u8 pgid, u8 bw_per,
+ u8 up_tc_map)
+{
+ struct fw_port_cmd pcmd;
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ u32 _pgid;
+ int err;
+
+ if (pgid == DCB_ATTR_VALUE_UNDEFINED)
+ return;
+ if (bw_per == DCB_ATTR_VALUE_UNDEFINED)
+ return;
+
+ INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
+ pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID;
+
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err);
+ return;
+ }
+
+ _pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid);
+ _pgid &= ~(0xF << (tc * 4));
+ _pgid |= pgid << (tc * 4);
+ pcmd.u.dcb.pgid.pgid = cpu_to_be32(_pgid);
+
+ INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
+
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB write PGID failed with %d\n",
+ -err);
+ return;
+ }
+
+ memset(&pcmd, 0, sizeof(struct fw_port_cmd));
+
+ INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
+ pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
+
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
+ -err);
+ return;
+ }
+
+ pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per;
+
+ INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
+ if (pi->dcb.state == CXGB4_DCB_STATE_HOST)
+ pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY);
+
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS)
+ dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n",
+ -err);
+}
+
+static void cxgb4_getpgbwgcfg(struct net_device *dev, int pgid, u8 *bw_per,
+ int local)
+{
+ struct fw_port_cmd pcmd;
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ int err;
+
+ if (local)
+ INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
+ else
+ INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
+
+ pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
+ -err);
+ } else {
+ *bw_per = pcmd.u.dcb.pgrate.pgrate[pgid];
+ }
+}
+
+static void cxgb4_getpgbwgcfg_tx(struct net_device *dev, int pgid, u8 *bw_per)
+{
+ return cxgb4_getpgbwgcfg(dev, pgid, bw_per, 1);
+}
+
+static void cxgb4_getpgbwgcfg_rx(struct net_device *dev, int pgid, u8 *bw_per)
+{
+ return cxgb4_getpgbwgcfg(dev, pgid, bw_per, 0);
+}
+
+static void cxgb4_setpgbwgcfg_tx(struct net_device *dev, int pgid,
+ u8 bw_per)
+{
+ struct fw_port_cmd pcmd;
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ int err;
+
+ INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
+ pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
+
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
+ -err);
+ return;
+ }
+
+ pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per;
+
+ INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
+ if (pi->dcb.state == CXGB4_DCB_STATE_HOST)
+ pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY);
+
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+
+ if (err != FW_PORT_DCB_CFG_SUCCESS)
+ dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n",
+ -err);
+}
+
+/* Return whether the specified Traffic Class Priority has Priority Pause
+ * Frames enabled.
+ */
+static void cxgb4_getpfccfg(struct net_device *dev, int priority, u8 *pfccfg)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+ struct port_dcb_info *dcb = &pi->dcb;
+
+ if (dcb->state != CXGB4_DCB_STATE_FW_ALLSYNCED ||
+ priority >= CXGB4_MAX_PRIORITY)
+ *pfccfg = 0;
+ else
+ *pfccfg = (pi->dcb.pfcen >> priority) & 1;
+}
+
+/* Enable/disable Priority Pause Frames for the specified Traffic Class
+ * Priority.
+ */
+static void cxgb4_setpfccfg(struct net_device *dev, int priority, u8 pfccfg)
+{
+ struct fw_port_cmd pcmd;
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ int err;
+
+ if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED ||
+ priority >= CXGB4_MAX_PRIORITY)
+ return;
+
+ INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
+ if (pi->dcb.state == CXGB4_DCB_STATE_HOST)
+ pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY);
+
+ pcmd.u.dcb.pfc.type = FW_PORT_DCB_TYPE_PFC;
+ pcmd.u.dcb.pfc.pfcen = pi->dcb.pfcen;
+
+ if (pfccfg)
+ pcmd.u.dcb.pfc.pfcen |= (1 << priority);
+ else
+ pcmd.u.dcb.pfc.pfcen &= (~(1 << priority));
+
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB PFC write failed with %d\n", -err);
+ return;
+ }
+
+ pi->dcb.pfcen = pcmd.u.dcb.pfc.pfcen;
+}
+
+static u8 cxgb4_setall(struct net_device *dev)
+{
+ return 0;
+}
+
+/* Return DCB capabilities.
+ */
+static u8 cxgb4_getcap(struct net_device *dev, int cap_id, u8 *caps)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+
+ switch (cap_id) {
+ case DCB_CAP_ATTR_PG:
+ case DCB_CAP_ATTR_PFC:
+ *caps = true;
+ break;
+
+ case DCB_CAP_ATTR_PG_TCS:
+ /* 8 priorities for PG represented by bitmap */
+ *caps = 0x80;
+ break;
+
+ case DCB_CAP_ATTR_PFC_TCS:
+ /* 8 priorities for PFC represented by bitmap */
+ *caps = 0x80;
+ break;
+
+ case DCB_CAP_ATTR_GSP:
+ *caps = true;
+ break;
+
+ case DCB_CAP_ATTR_UP2TC:
+ case DCB_CAP_ATTR_BCN:
+ *caps = false;
+ break;
+
+ case DCB_CAP_ATTR_DCBX:
+ *caps = pi->dcb.supported;
+ break;
+
+ default:
+ *caps = false;
+ }
+
+ return 0;
+}
+
+/* Return the number of Traffic Classes for the indicated Traffic Class ID.
+ */
+static int cxgb4_getnumtcs(struct net_device *dev, int tcs_id, u8 *num)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+
+ switch (tcs_id) {
+ case DCB_NUMTCS_ATTR_PG:
+ if (pi->dcb.msgs & CXGB4_DCB_FW_PGRATE)
+ *num = pi->dcb.pg_num_tcs_supported;
+ else
+ *num = 0x8;
+ break;
+
+ case DCB_NUMTCS_ATTR_PFC:
+ *num = 0x8;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Set the number of Traffic Classes supported for the indicated Traffic Class
+ * ID.
+ */
+static int cxgb4_setnumtcs(struct net_device *dev, int tcs_id, u8 num)
+{
+ /* Setting the number of Traffic Classes isn't supported.
+ */
+ return -ENOSYS;
+}
+
+/* Return whether Priority Flow Control is enabled. */
+static u8 cxgb4_getpfcstate(struct net_device *dev)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+
+ if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
+ return false;
+
+ return pi->dcb.pfcen != 0;
+}
+
+/* Enable/disable Priority Flow Control. */
+static void cxgb4_setpfcstate(struct net_device *dev, u8 state)
+{
+ /* We can't enable/disable Priority Flow Control but we also can't
+ * return an error ...
+ */
+}
+
+/* Return the Application User Priority Map associated with the specified
+ * Application ID.
+ */
+static int __cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id,
+ int peer)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ int i;
+
+ if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
+ return 0;
+
+ for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) {
+ struct fw_port_cmd pcmd;
+ int err;
+
+ if (peer)
+ INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
+ else
+ INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
+
+ pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
+ pcmd.u.dcb.app_priority.idx = i;
+
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB APP read failed with %d\n",
+ -err);
+ return err;
+ }
+ if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id)
+ return pcmd.u.dcb.app_priority.user_prio_map;
+
+ /* exhausted app list */
+ if (!pcmd.u.dcb.app_priority.protocolid)
+ break;
+ }
+
+ return -EEXIST;
+}
+
+/* Return the Application User Priority Map associated with the specified
+ * Application ID.
+ */
+static int cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id)
+{
+ return __cxgb4_getapp(dev, app_idtype, app_id, 0);
+}
+
+/* Write a new Application User Priority Map for the specified Application ID
+ */
+static int cxgb4_setapp(struct net_device *dev, u8 app_idtype, u16 app_id,
+ u8 app_prio)
+{
+ struct fw_port_cmd pcmd;
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ int i, err;
+
+
+ if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
+ return -EINVAL;
+
+ /* DCB info gets thrown away on link up */
+ if (!netif_carrier_ok(dev))
+ return -ENOLINK;
+
+ if (app_idtype != DCB_APP_IDTYPE_ETHTYPE &&
+ app_idtype != DCB_APP_IDTYPE_PORTNUM)
+ return -EINVAL;
+
+ for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) {
+ INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
+ pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
+ pcmd.u.dcb.app_priority.idx = i;
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB app table read failed with %d\n",
+ -err);
+ return err;
+ }
+ if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id) {
+ /* overwrite existing app table */
+ pcmd.u.dcb.app_priority.protocolid = 0;
+ break;
+ }
+ /* find first empty slot */
+ if (!pcmd.u.dcb.app_priority.protocolid)
+ break;
+ }
+
+ if (i == CXGB4_MAX_DCBX_APP_SUPPORTED) {
+ /* no empty slots available */
+ dev_err(adap->pdev_dev, "DCB app table full\n");
+ return -EBUSY;
+ }
+
+ /* write out new app table entry */
+ INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
+ if (pi->dcb.state == CXGB4_DCB_STATE_HOST)
+ pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY);
+
+ pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
+ pcmd.u.dcb.app_priority.protocolid = cpu_to_be16(app_id);
+ pcmd.u.dcb.app_priority.sel_field = app_idtype;
+ pcmd.u.dcb.app_priority.user_prio_map = app_prio;
+ pcmd.u.dcb.app_priority.idx = i;
+
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB app table write failed with %d\n",
+ -err);
+ return err;
+ }
+
+ return 0;
+}
+
+/* Return whether IEEE Data Center Bridging has been negotiated.
+ */
+static inline int cxgb4_ieee_negotiation_complete(struct net_device *dev)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+ struct port_dcb_info *dcb = &pi->dcb;
+
+ return (dcb->state == CXGB4_DCB_STATE_FW_ALLSYNCED &&
+ (dcb->supported & DCB_CAP_DCBX_VER_IEEE));
+}
+
+/* Fill in the Application User Priority Map associated with the
+ * specified Application.
+ */
+static int cxgb4_ieee_getapp(struct net_device *dev, struct dcb_app *app)
+{
+ int prio;
+
+ if (!cxgb4_ieee_negotiation_complete(dev))
+ return -EINVAL;
+ if (!(app->selector && app->protocol))
+ return -EINVAL;
+
+ prio = dcb_getapp(dev, app);
+ if (prio == 0) {
+ /* If app doesn't exist in dcb_app table, try firmware
+ * directly.
+ */
+ prio = __cxgb4_getapp(dev, app->selector, app->protocol, 0);
+ }
+
+ app->priority = prio;
+ return 0;
+}
+
+/* Write a new Application User Priority Map for the specified App id. */
+static int cxgb4_ieee_setapp(struct net_device *dev, struct dcb_app *app)
+{
+ if (!cxgb4_ieee_negotiation_complete(dev))
+ return -EINVAL;
+ if (!(app->selector && app->protocol && app->priority))
+ return -EINVAL;
+
+ cxgb4_setapp(dev, app->selector, app->protocol, app->priority);
+ return dcb_setapp(dev, app);
+}
+
+/* Return our DCBX parameters.
+ */
+static u8 cxgb4_getdcbx(struct net_device *dev)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+
+ /* This is already set by cxgb4_set_dcb_caps, so just return it */
+ return pi->dcb.supported;
+}
+
+/* Set our DCBX parameters.
+ */
+static u8 cxgb4_setdcbx(struct net_device *dev, u8 dcb_request)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+
+ /* Filter out requests which exceed our capabilities.
+ */
+ if ((dcb_request & (CXGB4_DCBX_FW_SUPPORT | CXGB4_DCBX_HOST_SUPPORT))
+ != dcb_request)
+ return 1;
+
+ /* Can't set DCBX capabilities if DCBX isn't enabled. */
+ if (!pi->dcb.state)
+ return 1;
+
+ /* There's currently no mechanism to allow for the firmware DCBX
+ * negotiation to be changed from the Host Driver. If the caller
+ * requests exactly the same parameters that we already have then
+ * we'll allow them to be successfully "set" ...
+ */
+ if (dcb_request != pi->dcb.supported)
+ return 1;
+
+ pi->dcb.supported = dcb_request;
+ return 0;
+}
+
+static int cxgb4_getpeer_app(struct net_device *dev,
+ struct dcb_peer_app_info *info, u16 *app_count)
+{
+ struct fw_port_cmd pcmd;
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ int i, err = 0;
+
+ if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
+ return 1;
+
+ info->willing = 0;
+ info->error = 0;
+
+ *app_count = 0;
+ for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) {
+ INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
+ pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
+ pcmd.u.dcb.app_priority.idx = *app_count;
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB app table read failed with %d\n",
+ -err);
+ return err;
+ }
+
+ /* find first empty slot */
+ if (!pcmd.u.dcb.app_priority.protocolid)
+ break;
+ }
+ *app_count = i;
+ return err;
+}
+
+static int cxgb4_getpeerapp_tbl(struct net_device *dev, struct dcb_app *table)
+{
+ struct fw_port_cmd pcmd;
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ int i, err = 0;
+
+ if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
+ return 1;
+
+ for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) {
+ INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
+ pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
+ pcmd.u.dcb.app_priority.idx = i;
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB app table read failed with %d\n",
+ -err);
+ return err;
+ }
+
+ /* find first empty slot */
+ if (!pcmd.u.dcb.app_priority.protocolid)
+ break;
+
+ table[i].selector = pcmd.u.dcb.app_priority.sel_field;
+ table[i].protocol =
+ be16_to_cpu(pcmd.u.dcb.app_priority.protocolid);
+ table[i].priority = pcmd.u.dcb.app_priority.user_prio_map;
+ }
+ return err;
+}
+
+/* Return Priority Group information.
+ */
+static int cxgb4_cee_peer_getpg(struct net_device *dev, struct cee_pg *pg)
+{
+ struct fw_port_cmd pcmd;
+ struct port_info *pi = netdev2pinfo(dev);
+ struct adapter *adap = pi->adapter;
+ u32 pgid;
+ int i, err;
+
+ /* We're always "willing" -- the Switch Fabric always dictates the
+ * DCBX parameters to us.
+ */
+ pg->willing = true;
+
+ INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
+ pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID;
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err);
+ return err;
+ }
+ pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid);
+
+ for (i = 0; i < CXGB4_MAX_PRIORITY; i++)
+ pg->prio_pg[i] = (pgid >> (i * 4)) & 0xF;
+
+ INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
+ pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
+ err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
+ if (err != FW_PORT_DCB_CFG_SUCCESS) {
+ dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
+ -err);
+ return err;
+ }
+
+ for (i = 0; i < CXGB4_MAX_PRIORITY; i++)
+ pg->pg_bw[i] = pcmd.u.dcb.pgrate.pgrate[i];
+
+ return 0;
+}
+
+/* Return Priority Flow Control information.
+ */
+static int cxgb4_cee_peer_getpfc(struct net_device *dev, struct cee_pfc *pfc)
+{
+ struct port_info *pi = netdev2pinfo(dev);
+
+ cxgb4_getnumtcs(dev, DCB_NUMTCS_ATTR_PFC, &(pfc->tcs_supported));
+ pfc->pfc_en = pi->dcb.pfcen;
+
+ return 0;
+}
+
+const struct dcbnl_rtnl_ops cxgb4_dcb_ops = {
+ .ieee_getapp = cxgb4_ieee_getapp,
+ .ieee_setapp = cxgb4_ieee_setapp,
+
+ /* CEE std */
+ .getstate = cxgb4_getstate,
+ .setstate = cxgb4_setstate,
+ .getpgtccfgtx = cxgb4_getpgtccfg_tx,
+ .getpgbwgcfgtx = cxgb4_getpgbwgcfg_tx,
+ .getpgtccfgrx = cxgb4_getpgtccfg_rx,
+ .getpgbwgcfgrx = cxgb4_getpgbwgcfg_rx,
+ .setpgtccfgtx = cxgb4_setpgtccfg_tx,
+ .setpgbwgcfgtx = cxgb4_setpgbwgcfg_tx,
+ .setpfccfg = cxgb4_setpfccfg,
+ .getpfccfg = cxgb4_getpfccfg,
+ .setall = cxgb4_setall,
+ .getcap = cxgb4_getcap,
+ .getnumtcs = cxgb4_getnumtcs,
+ .setnumtcs = cxgb4_setnumtcs,
+ .getpfcstate = cxgb4_getpfcstate,
+ .setpfcstate = cxgb4_setpfcstate,
+ .getapp = cxgb4_getapp,
+ .setapp = cxgb4_setapp,
+
+ /* DCBX configuration */
+ .getdcbx = cxgb4_getdcbx,
+ .setdcbx = cxgb4_setdcbx,
+
+ /* peer apps */
+ .peer_getappinfo = cxgb4_getpeer_app,
+ .peer_getapptable = cxgb4_getpeerapp_tbl,
+
+ /* CEE peer */
+ .cee_peer_getpg = cxgb4_cee_peer_getpg,
+ .cee_peer_getpfc = cxgb4_cee_peer_getpfc,
+};
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h
new file mode 100644
index 000000000000..1ec1d834e257
--- /dev/null
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013-2014 Chelsio Communications. All rights reserved.
+ *
+ * Written by Anish Bhatt (anish@chelsio.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ */
+
+#ifndef __CXGB4_DCB_H
+#define __CXGB4_DCB_H
+
+#include <linux/netdevice.h>
+#include <linux/dcbnl.h>
+#include <net/dcbnl.h>
+
+#ifdef CONFIG_CHELSIO_T4_DCB
+
+#define CXGB4_DCBX_FW_SUPPORT \
+ (DCB_CAP_DCBX_VER_CEE | \
+ DCB_CAP_DCBX_VER_IEEE | \
+ DCB_CAP_DCBX_LLD_MANAGED)
+#define CXGB4_DCBX_HOST_SUPPORT \
+ (DCB_CAP_DCBX_VER_CEE | \
+ DCB_CAP_DCBX_VER_IEEE | \
+ DCB_CAP_DCBX_HOST)
+
+#define CXGB4_MAX_PRIORITY CXGB4_MAX_DCBX_APP_SUPPORTED
+#define CXGB4_MAX_TCS CXGB4_MAX_DCBX_APP_SUPPORTED
+
+#define INIT_PORT_DCB_CMD(__pcmd, __port, __op, __action) \
+ do { \
+ memset(&(__pcmd), 0, sizeof(__pcmd)); \
+ (__pcmd).op_to_portid = \
+ cpu_to_be32(FW_CMD_OP(FW_PORT_CMD) | \
+ FW_CMD_REQUEST | \
+ FW_CMD_##__op | \
+ FW_PORT_CMD_PORTID(__port)); \
+ (__pcmd).action_to_len16 = \
+ cpu_to_be32(FW_PORT_CMD_ACTION(__action) | \
+ FW_LEN16(pcmd)); \
+ } while (0)
+
+#define INIT_PORT_DCB_READ_PEER_CMD(__pcmd, __port) \
+ INIT_PORT_DCB_CMD(__pcmd, __port, READ, FW_PORT_ACTION_DCB_READ_RECV)
+
+#define INIT_PORT_DCB_READ_LOCAL_CMD(__pcmd, __port) \
+ INIT_PORT_DCB_CMD(__pcmd, __port, READ, FW_PORT_ACTION_DCB_READ_TRANS)
+
+#define INIT_PORT_DCB_READ_SYNC_CMD(__pcmd, __port) \
+ INIT_PORT_DCB_CMD(__pcmd, __port, READ, FW_PORT_ACTION_DCB_READ_DET)
+
+#define INIT_PORT_DCB_WRITE_CMD(__pcmd, __port) \
+ INIT_PORT_DCB_CMD(__pcmd, __port, EXEC, FW_PORT_ACTION_L2_DCB_CFG)
+
+/* States we can be in for a port's Data Center Bridging.
+ */
+enum cxgb4_dcb_state {
+ CXGB4_DCB_STATE_START, /* initial unknown state */
+ CXGB4_DCB_STATE_HOST, /* we're using Host DCB (if at all) */
+ CXGB4_DCB_STATE_FW_INCOMPLETE, /* using firmware DCB, incomplete */
+ CXGB4_DCB_STATE_FW_ALLSYNCED, /* using firmware DCB, all sync'ed */
+};
+
+/* Data Center Bridging state input for the Finite State Machine.
+ */
+enum cxgb4_dcb_state_input {
+ /* Input from the firmware.
+ */
+ CXGB4_DCB_INPUT_FW_DISABLED, /* firmware DCB disabled */
+ CXGB4_DCB_INPUT_FW_ENABLED, /* firmware DCB enabled */
+ CXGB4_DCB_INPUT_FW_INCOMPLETE, /* firmware reports incomplete DCB */
+ CXGB4_DCB_INPUT_FW_ALLSYNCED, /* firmware reports all sync'ed */
+
+};
+
+/* Firmware DCB messages that we've received so far ...
+ */
+enum cxgb4_dcb_fw_msgs {
+ CXGB4_DCB_FW_PGID = 0x01,
+ CXGB4_DCB_FW_PGRATE = 0x02,
+ CXGB4_DCB_FW_PRIORATE = 0x04,
+ CXGB4_DCB_FW_PFC = 0x08,
+ CXGB4_DCB_FW_APP_ID = 0x10,
+};
+
+#define CXGB4_MAX_DCBX_APP_SUPPORTED 8
+
+/* Data Center Bridging support;
+ */
+struct port_dcb_info {
+ enum cxgb4_dcb_state state; /* DCB State Machine */
+ enum cxgb4_dcb_fw_msgs msgs; /* DCB Firmware messages received */
+ unsigned int supported; /* OS DCB capabilities supported */
+ bool enabled; /* OS Enabled state */
+
+ /* Cached copies of DCB information sent by the firmware (in Host
+ * Native Endian format).
+ */
+ u32 pgid; /* Priority Group[0..7] */
+ u8 pfcen; /* Priority Flow Control[0..7] */
+ u8 pg_num_tcs_supported; /* max PG Traffic Classes */
+ u8 pfc_num_tcs_supported; /* max PFC Traffic Classes */
+ u8 pgrate[8]; /* Priority Group Rate[0..7] */
+ u8 priorate[8]; /* Priority Rate[0..7] */
+ struct app_priority { /* Application Information */
+ u8 user_prio_map; /* Priority Map bitfield */
+ u8 sel_field; /* Protocol ID interpretation */
+ u16 protocolid; /* Protocol ID */
+ } app_priority[CXGB4_MAX_DCBX_APP_SUPPORTED];
+};
+
+void cxgb4_dcb_state_init(struct net_device *);
+void cxgb4_dcb_state_fsm(struct net_device *, enum cxgb4_dcb_state_input);
+void cxgb4_dcb_handle_fw_update(struct adapter *, const struct fw_port_cmd *);
+void cxgb4_dcb_set_caps(struct adapter *, const struct fw_port_cmd *);
+extern const struct dcbnl_rtnl_ops cxgb4_dcb_ops;
+
+#define CXGB4_DCB_ENABLED true
+
+#else /* !CONFIG_CHELSIO_T4_DCB */
+
+static inline void cxgb4_dcb_state_init(struct net_device *dev)
+{
+}
+
+#define CXGB4_DCB_ENABLED false
+
+#endif /* !CONFIG_CHELSIO_T4_DCB */
+
+#endif /* __CXGB4_DCB_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index a83271cf17c3..4247356c16ff 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -67,6 +67,7 @@
#include "t4_regs.h"
#include "t4_msg.h"
#include "t4fw_api.h"
+#include "cxgb4_dcb.h"
#include "l2t.h"
#include <../drivers/net/bonding/bonding.h>
@@ -223,6 +224,17 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4_pci_tbl) = {
CH_DEVICE(0x4008, -1),
CH_DEVICE(0x4009, -1),
CH_DEVICE(0x400a, -1),
+ CH_DEVICE(0x400d, -1),
+ CH_DEVICE(0x400e, -1),
+ CH_DEVICE(0x4080, -1),
+ CH_DEVICE(0x4081, -1),
+ CH_DEVICE(0x4082, -1),
+ CH_DEVICE(0x4083, -1),
+ CH_DEVICE(0x4084, -1),
+ CH_DEVICE(0x4085, -1),
+ CH_DEVICE(0x4086, -1),
+ CH_DEVICE(0x4087, -1),
+ CH_DEVICE(0x4088, -1),
CH_DEVICE(0x4401, 4),
CH_DEVICE(0x4402, 4),
CH_DEVICE(0x4403, 4),
@@ -235,6 +247,15 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4_pci_tbl) = {
CH_DEVICE(0x440a, 4),
CH_DEVICE(0x440d, 4),
CH_DEVICE(0x440e, 4),
+ CH_DEVICE(0x4480, 4),
+ CH_DEVICE(0x4481, 4),
+ CH_DEVICE(0x4482, 4),
+ CH_DEVICE(0x4483, 4),
+ CH_DEVICE(0x4484, 4),
+ CH_DEVICE(0x4485, 4),
+ CH_DEVICE(0x4486, 4),
+ CH_DEVICE(0x4487, 4),
+ CH_DEVICE(0x4488, 4),
CH_DEVICE(0x5001, 4),
CH_DEVICE(0x5002, 4),
CH_DEVICE(0x5003, 4),
@@ -391,6 +412,17 @@ module_param_array(num_vf, uint, NULL, 0644);
MODULE_PARM_DESC(num_vf, "number of VFs for each of PFs 0-3");
#endif
+/* TX Queue select used to determine what algorithm to use for selecting TX
+ * queue. Select between the kernel provided function (select_queue=0) or user
+ * cxgb_select_queue function (select_queue=1)
+ *
+ * Default: select_queue=0
+ */
+static int select_queue;
+module_param(select_queue, int, 0644);
+MODULE_PARM_DESC(select_queue,
+ "Select between kernel provided method of selecting or driver method of selecting TX queue. Default is kernel method.");
+
/*
* The filter TCAM has a fixed portion and a variable portion. The fixed
* portion can match on source/destination IP IPv4/IPv6 addresses and TCP/UDP
@@ -458,6 +490,42 @@ static void link_report(struct net_device *dev)
}
}
+#ifdef CONFIG_CHELSIO_T4_DCB
+/* Set up/tear down Data Center Bridging Priority mapping for a net device. */
+static void dcb_tx_queue_prio_enable(struct net_device *dev, int enable)
+{
+ struct port_info *pi = netdev_priv(dev);
+ struct adapter *adap = pi->adapter;
+ struct sge_eth_txq *txq = &adap->sge.ethtxq[pi->first_qset];
+ int i;
+
+ /* We use a simple mapping of Port TX Queue Index to DCB
+ * Priority when we're enabling DCB.
+ */
+ for (i = 0; i < pi->nqsets; i++, txq++) {
+ u32 name, value;
+ int err;
+
+ name = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DMAQ) |
+ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH) |
+ FW_PARAMS_PARAM_YZ(txq->q.cntxt_id));
+ value = enable ? i : 0xffffffff;
+
+ /* Since we can be called while atomic (from "interrupt
+ * level") we need to issue the Set Parameters Commannd
+ * without sleeping (timeout < 0).
+ */
+ err = t4_set_params_nosleep(adap, adap->mbox, adap->fn, 0, 1,
+ &name, &value);
+
+ if (err)
+ dev_err(adap->pdev_dev,
+ "Can't %s DCB Priority on port %d, TX Queue %d: err=%d\n",
+ enable ? "set" : "unset", pi->port_id, i, -err);
+ }
+}
+#endif /* CONFIG_CHELSIO_T4_DCB */
+
void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat)
{
struct net_device *dev = adapter->port[port_id];
@@ -466,8 +534,13 @@ void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat)
if (netif_running(dev) && link_stat != netif_carrier_ok(dev)) {
if (link_stat)
netif_carrier_on(dev);
- else
+ else {
+#ifdef CONFIG_CHELSIO_T4_DCB
+ cxgb4_dcb_state_init(dev);
+ dcb_tx_queue_prio_enable(dev, false);
+#endif /* CONFIG_CHELSIO_T4_DCB */
netif_carrier_off(dev);
+ }
link_report(dev);
}
@@ -600,11 +673,49 @@ static int link_start(struct net_device *dev)
if (ret == 0)
ret = t4_link_start(pi->adapter, mb, pi->tx_chan,
&pi->link_cfg);
- if (ret == 0)
- ret = t4_enable_vi(pi->adapter, mb, pi->viid, true, true);
+ if (ret == 0) {
+ local_bh_disable();
+ ret = t4_enable_vi_params(pi->adapter, mb, pi->viid, true,
+ true, CXGB4_DCB_ENABLED);
+ local_bh_enable();
+ }
+
return ret;
}
+int cxgb4_dcb_enabled(const struct net_device *dev)
+{
+#ifdef CONFIG_CHELSIO_T4_DCB
+ struct port_info *pi = netdev_priv(dev);
+
+ return pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED;
+#else
+ return 0;
+#endif
+}
+EXPORT_SYMBOL(cxgb4_dcb_enabled);
+
+#ifdef CONFIG_CHELSIO_T4_DCB
+/* Handle a Data Center Bridging update message from the firmware. */
+static void dcb_rpl(struct adapter *adap, const struct fw_port_cmd *pcmd)
+{
+ int port = FW_PORT_CMD_PORTID_GET(ntohl(pcmd->op_to_portid));
+ struct net_device *dev = adap->port[port];
+ int old_dcb_enabled = cxgb4_dcb_enabled(dev);
+ int new_dcb_enabled;
+
+ cxgb4_dcb_handle_fw_update(adap, pcmd);
+ new_dcb_enabled = cxgb4_dcb_enabled(dev);
+
+ /* If the DCB has become enabled or disabled on the port then we're
+ * going to need to set up/tear down DCB Priority parameters for the
+ * TX Queues associated with the port.
+ */
+ if (new_dcb_enabled != old_dcb_enabled)
+ dcb_tx_queue_prio_enable(dev, new_dcb_enabled);
+}
+#endif /* CONFIG_CHELSIO_T4_DCB */
+
/* Clear a filter and release any of its resources that we own. This also
* clears the filter's "pending" status.
*/
@@ -709,8 +820,32 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,
} else if (opcode == CPL_FW6_MSG || opcode == CPL_FW4_MSG) {
const struct cpl_fw6_msg *p = (void *)rsp;
- if (p->type == 0)
- t4_handle_fw_rpl(q->adap, p->data);
+#ifdef CONFIG_CHELSIO_T4_DCB
+ const struct fw_port_cmd *pcmd = (const void *)p->data;
+ unsigned int cmd = FW_CMD_OP_GET(ntohl(pcmd->op_to_portid));
+ unsigned int action =
+ FW_PORT_CMD_ACTION_GET(ntohl(pcmd->action_to_len16));
+
+ if (cmd == FW_PORT_CMD &&
+ action == FW_PORT_ACTION_GET_PORT_INFO) {
+ int port = FW_PORT_CMD_PORTID_GET(
+ be32_to_cpu(pcmd->op_to_portid));
+ struct net_device *dev = q->adap->port[port];
+ int state_input = ((pcmd->u.info.dcbxdis_pkd &
+ FW_PORT_CMD_DCBXDIS)
+ ? CXGB4_DCB_INPUT_FW_DISABLED
+ : CXGB4_DCB_INPUT_FW_ENABLED);
+
+ cxgb4_dcb_state_fsm(dev, state_input);
+ }
+
+ if (cmd == FW_PORT_CMD &&
+ action == FW_PORT_ACTION_L2_DCB_CFG)
+ dcb_rpl(q->adap, pcmd);
+ else
+#endif
+ if (p->type == 0)
+ t4_handle_fw_rpl(q->adap, p->data);
} else if (opcode == CPL_L2T_WRITE_RPL) {
const struct cpl_l2t_write_rpl *p = (void *)rsp;
@@ -1290,6 +1425,48 @@ static int del_filter_wr(struct adapter *adapter, int fidx)
return 0;
}
+static u16 cxgb_select_queue(struct net_device *dev, struct sk_buff *skb,
+ void *accel_priv, select_queue_fallback_t fallback)
+{
+ int txq;
+
+#ifdef CONFIG_CHELSIO_T4_DCB
+ /* If a Data Center Bridging has been successfully negotiated on this
+ * link then we'll use the skb's priority to map it to a TX Queue.
+ * The skb's priority is determined via the VLAN Tag Priority Code
+ * Point field.
+ */
+ if (cxgb4_dcb_enabled(dev)) {
+ u16 vlan_tci;
+ int err;
+
+ err = vlan_get_tag(skb, &vlan_tci);
+ if (unlikely(err)) {
+ if (net_ratelimit())
+ netdev_warn(dev,
+ "TX Packet without VLAN Tag on DCB Link\n");
+ txq = 0;
+ } else {
+ txq = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+ }
+ return txq;
+ }
+#endif /* CONFIG_CHELSIO_T4_DCB */
+
+ if (select_queue) {
+ txq = (skb_rx_queue_recorded(skb)
+ ? skb_get_rx_queue(skb)
+ : smp_processor_id());
+
+ while (unlikely(txq >= dev->real_num_tx_queues))
+ txq -= dev->real_num_tx_queues;
+
+ return txq;
+ }
+
+ return fallback(dev, skb) % dev->real_num_tx_queues;
+}
+
static inline int is_offload(const struct adapter *adap)
{
return adap->params.offload;
@@ -2912,6 +3089,8 @@ static ssize_t mem_read(struct file *file, char __user *buf, size_t count,
loff_t avail = file_inode(file)->i_size;
unsigned int mem = (uintptr_t)file->private_data & 3;
struct adapter *adap = file->private_data - mem;
+ __be32 *data;
+ int ret;
if (pos < 0)
return -EINVAL;
@@ -2920,29 +3099,24 @@ static ssize_t mem_read(struct file *file, char __user *buf, size_t count,
if (count > avail - pos)
count = avail - pos;
- while (count) {
- size_t len;
- int ret, ofst;
- __be32 data[16];
+ data = t4_alloc_mem(count);
+ if (!data)
+ return -ENOMEM;
- if ((mem == MEM_MC) || (mem == MEM_MC1))
- ret = t4_mc_read(adap, mem % MEM_MC, pos, data, NULL);
- else
- ret = t4_edc_read(adap, mem, pos, data, NULL);
- if (ret)
- return ret;
+ spin_lock(&adap->win0_lock);
+ ret = t4_memory_rw(adap, 0, mem, pos, count, data, T4_MEMORY_READ);
+ spin_unlock(&adap->win0_lock);
+ if (ret) {
+ t4_free_mem(data);
+ return ret;
+ }
+ ret = copy_to_user(buf, data, count);
- ofst = pos % sizeof(data);
- len = min(count, sizeof(data) - ofst);
- if (copy_to_user(buf, (u8 *)data + ofst, len))
- return -EFAULT;
+ t4_free_mem(data);
+ if (ret)
+ return -EFAULT;
- buf += len;
- pos += len;
- count -= len;
- }
- count = pos - *ppos;
- *ppos = pos;
+ *ppos = pos + count;
return count;
}
@@ -3274,8 +3448,8 @@ static int tid_init(struct tid_info *t)
return 0;
}
-static int cxgb4_clip_get(const struct net_device *dev,
- const struct in6_addr *lip)
+int cxgb4_clip_get(const struct net_device *dev,
+ const struct in6_addr *lip)
{
struct adapter *adap;
struct fw_clip_cmd c;
@@ -3289,9 +3463,10 @@ static int cxgb4_clip_get(const struct net_device *dev,
c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}
+EXPORT_SYMBOL(cxgb4_clip_get);
-static int cxgb4_clip_release(const struct net_device *dev,
- const struct in6_addr *lip)
+int cxgb4_clip_release(const struct net_device *dev,
+ const struct in6_addr *lip)
{
struct adapter *adap;
struct fw_clip_cmd c;
@@ -3305,6 +3480,7 @@ static int cxgb4_clip_release(const struct net_device *dev,
c.ip_lo = *(__be64 *)(lip->s6_addr + 8);
return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
}
+EXPORT_SYMBOL(cxgb4_clip_release);
/**
* cxgb4_create_server - create an IP server
@@ -3603,7 +3779,11 @@ static int read_eq_indices(struct adapter *adap, u16 qid, u16 *pidx, u16 *cidx)
__be64 indices;
int ret;
- ret = t4_mem_win_read_len(adap, addr, (__be32 *)&indices, 8);
+ spin_lock(&adap->win0_lock);
+ ret = t4_memory_rw(adap, 0, MEM_EDC0, addr,
+ sizeof(indices), (__be32 *)&indices,
+ T4_MEMORY_READ);
+ spin_unlock(&adap->win0_lock);
if (!ret) {
*cidx = (be64_to_cpu(indices) >> 25) & 0xffff;
*pidx = (be64_to_cpu(indices) >> 9) & 0xffff;
@@ -3657,6 +3837,85 @@ void cxgb4_enable_db_coalescing(struct net_device *dev)
}
EXPORT_SYMBOL(cxgb4_enable_db_coalescing);
+int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte)
+{
+ struct adapter *adap;
+ u32 offset, memtype, memaddr;
+ u32 edc0_size, edc1_size, mc0_size, mc1_size;
+ u32 edc0_end, edc1_end, mc0_end, mc1_end;
+ int ret;
+
+ adap = netdev2adap(dev);
+
+ offset = ((stag >> 8) * 32) + adap->vres.stag.start;
+
+ /* Figure out where the offset lands in the Memory Type/Address scheme.
+ * This code assumes that the memory is laid out starting at offset 0
+ * with no breaks as: EDC0, EDC1, MC0, MC1. All cards have both EDC0
+ * and EDC1. Some cards will have neither MC0 nor MC1, most cards have
+ * MC0, and some have both MC0 and MC1.
+ */
+ edc0_size = EDRAM_SIZE_GET(t4_read_reg(adap, MA_EDRAM0_BAR)) << 20;
+ edc1_size = EDRAM_SIZE_GET(t4_read_reg(adap, MA_EDRAM1_BAR)) << 20;
+ mc0_size = EXT_MEM_SIZE_GET(t4_read_reg(adap, MA_EXT_MEMORY_BAR)) << 20;
+
+ edc0_end = edc0_size;
+ edc1_end = edc0_end + edc1_size;
+ mc0_end = edc1_end + mc0_size;
+
+ if (offset < edc0_end) {
+ memtype = MEM_EDC0;
+ memaddr = offset;
+ } else if (offset < edc1_end) {
+ memtype = MEM_EDC1;
+ memaddr = offset - edc0_end;
+ } else {
+ if (offset < mc0_end) {
+ memtype = MEM_MC0;
+ memaddr = offset - edc1_end;
+ } else if (is_t4(adap->params.chip)) {
+ /* T4 only has a single memory channel */
+ goto err;
+ } else {
+ mc1_size = EXT_MEM_SIZE_GET(
+ t4_read_reg(adap,
+ MA_EXT_MEMORY1_BAR)) << 20;
+ mc1_end = mc0_end + mc1_size;
+ if (offset < mc1_end) {
+ memtype = MEM_MC1;
+ memaddr = offset - mc0_end;
+ } else {
+ /* offset beyond the end of any memory */
+ goto err;
+ }
+ }
+ }
+
+ spin_lock(&adap->win0_lock);
+ ret = t4_memory_rw(adap, 0, memtype, memaddr, 32, tpte, T4_MEMORY_READ);
+ spin_unlock(&adap->win0_lock);
+ return ret;
+
+err:
+ dev_err(adap->pdev_dev, "stag %#x, offset %#x out of range\n",
+ stag, offset);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(cxgb4_read_tpte);
+
+u64 cxgb4_read_sge_timestamp(struct net_device *dev)
+{
+ u32 hi, lo;
+ struct adapter *adap;
+
+ adap = netdev2adap(dev);
+ lo = t4_read_reg(adap, SGE_TIMESTAMP_LO);
+ hi = GET_TSVAL(t4_read_reg(adap, SGE_TIMESTAMP_HI));
+
+ return ((u64)hi << 32) | (u64)lo;
+}
+EXPORT_SYMBOL(cxgb4_read_sge_timestamp);
+
static struct pci_driver cxgb4_driver;
static void check_neigh_update(struct neighbour *neigh)
@@ -3899,6 +4158,7 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
unsigned short i;
lli.pdev = adap->pdev;
+ lli.pf = adap->fn;
lli.l2t = adap->l2t;
lli.tids = &adap->tids;
lli.ports = adap->port;
@@ -3919,6 +4179,7 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
lli.wr_cred = adap->params.ofldq_wr_cred;
lli.adapter_type = adap->params.chip;
lli.iscsi_iolen = MAXRXDATA_GET(t4_read_reg(adap, TP_PARA_REG2));
+ lli.cclk_ps = 1000000000 / adap->params.vpd.cclk;
lli.udb_density = 1 << QUEUESPERPAGEPF0_GET(
t4_read_reg(adap, SGE_EGRESS_QUEUES_PER_PAGE_PF) >>
(adap->fn * 4));
@@ -3933,8 +4194,12 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
lli.db_reg = adap->regs + MYPF_REG(SGE_PF_KDOORBELL);
lli.fw_vers = adap->params.fw_vers;
lli.dbfifo_int_thresh = dbfifo_int_thresh;
+ lli.sge_ingpadboundary = adap->sge.fl_align;
+ lli.sge_egrstatuspagesize = adap->sge.stat_len;
lli.sge_pktshift = adap->sge.pktshift;
lli.enable_fw_ofld_conn = adap->flags & FW_OFLD_CONN;
+ lli.max_ordird_qp = adap->params.max_ordird_qp;
+ lli.max_ird_adapter = adap->params.max_ird_adapter;
lli.ulptx_memwrite_dsgl = adap->params.ulptx_memwrite_dsgl;
handle = ulds[uld].add(&lli);
@@ -4598,6 +4863,7 @@ static const struct net_device_ops cxgb4_netdev_ops = {
.ndo_open = cxgb_open,
.ndo_stop = cxgb_close,
.ndo_start_xmit = t4_eth_xmit,
+ .ndo_select_queue = cxgb_select_queue,
.ndo_get_stats64 = cxgb_get_stats,
.ndo_set_rx_mode = cxgb_set_rxmode,
.ndo_set_mac_address = cxgb_set_mac_addr,
@@ -4617,20 +4883,75 @@ void t4_fatal_err(struct adapter *adap)
dev_alert(adap->pdev_dev, "encountered fatal error, adapter stopped\n");
}
+/* Return the specified PCI-E Configuration Space register from our Physical
+ * Function. We try first via a Firmware LDST Command since we prefer to let
+ * the firmware own all of these registers, but if that fails we go for it
+ * directly ourselves.
+ */
+static u32 t4_read_pcie_cfg4(struct adapter *adap, int reg)
+{
+ struct fw_ldst_cmd ldst_cmd;
+ u32 val;
+ int ret;
+
+ /* Construct and send the Firmware LDST Command to retrieve the
+ * specified PCI-E Configuration Space register.
+ */
+ memset(&ldst_cmd, 0, sizeof(ldst_cmd));
+ ldst_cmd.op_to_addrspace =
+ htonl(FW_CMD_OP(FW_LDST_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_READ |
+ FW_LDST_CMD_ADDRSPACE(FW_LDST_ADDRSPC_FUNC_PCIE));
+ ldst_cmd.cycles_to_len16 = htonl(FW_LEN16(ldst_cmd));
+ ldst_cmd.u.pcie.select_naccess = FW_LDST_CMD_NACCESS(1);
+ ldst_cmd.u.pcie.ctrl_to_fn =
+ (FW_LDST_CMD_LC | FW_LDST_CMD_FN(adap->fn));
+ ldst_cmd.u.pcie.r = reg;
+ ret = t4_wr_mbox(adap, adap->mbox, &ldst_cmd, sizeof(ldst_cmd),
+ &ldst_cmd);
+
+ /* If the LDST Command suucceeded, exctract the returned register
+ * value. Otherwise read it directly ourself.
+ */
+ if (ret == 0)
+ val = ntohl(ldst_cmd.u.pcie.data[0]);
+ else
+ t4_hw_pci_read_cfg4(adap, reg, &val);
+
+ return val;
+}
+
static void setup_memwin(struct adapter *adap)
{
- u32 bar0, mem_win0_base, mem_win1_base, mem_win2_base;
+ u32 mem_win0_base, mem_win1_base, mem_win2_base, mem_win2_aperture;
- bar0 = pci_resource_start(adap->pdev, 0); /* truncation intentional */
if (is_t4(adap->params.chip)) {
+ u32 bar0;
+
+ /* Truncation intentional: we only read the bottom 32-bits of
+ * the 64-bit BAR0/BAR1 ... We use the hardware backdoor
+ * mechanism to read BAR0 instead of using
+ * pci_resource_start() because we could be operating from
+ * within a Virtual Machine which is trapping our accesses to
+ * our Configuration Space and we need to set up the PCI-E
+ * Memory Window decoders with the actual addresses which will
+ * be coming across the PCI-E link.
+ */
+ bar0 = t4_read_pcie_cfg4(adap, PCI_BASE_ADDRESS_0);
+ bar0 &= PCI_BASE_ADDRESS_MEM_MASK;
+ adap->t4_bar0 = bar0;
+
mem_win0_base = bar0 + MEMWIN0_BASE;
mem_win1_base = bar0 + MEMWIN1_BASE;
mem_win2_base = bar0 + MEMWIN2_BASE;
+ mem_win2_aperture = MEMWIN2_APERTURE;
} else {
/* For T5, only relative offset inside the PCIe BAR is passed */
mem_win0_base = MEMWIN0_BASE;
- mem_win1_base = MEMWIN1_BASE_T5;
+ mem_win1_base = MEMWIN1_BASE;
mem_win2_base = MEMWIN2_BASE_T5;
+ mem_win2_aperture = MEMWIN2_APERTURE_T5;
}
t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 0),
mem_win0_base | BIR(0) |
@@ -4640,16 +4961,19 @@ static void setup_memwin(struct adapter *adap)
WINDOW(ilog2(MEMWIN1_APERTURE) - 10));
t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2),
mem_win2_base | BIR(0) |
- WINDOW(ilog2(MEMWIN2_APERTURE) - 10));
+ WINDOW(ilog2(mem_win2_aperture) - 10));
+ t4_read_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2));
}
static void setup_memwin_rdma(struct adapter *adap)
{
if (adap->vres.ocq.size) {
- unsigned int start, sz_kb;
+ u32 start;
+ unsigned int sz_kb;
- start = pci_resource_start(adap->pdev, 2) +
- OCQ_WIN_OFFSET(adap->pdev, &adap->vres);
+ start = t4_read_pcie_cfg4(adap, PCI_BASE_ADDRESS_2);
+ start &= PCI_BASE_ADDRESS_MEM_MASK;
+ start += OCQ_WIN_OFFSET(adap->pdev, &adap->vres);
sz_kb = roundup_pow_of_two(adap->vres.ocq.size) >> 10;
t4_write_reg(adap,
PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 3),
@@ -4862,7 +5186,7 @@ static int adap_init0_config(struct adapter *adapter, int reset)
adapter->fn, 0, 1, params, val);
if (ret == 0) {
/*
- * For t4_memory_write() below addresses and
+ * For t4_memory_rw() below addresses and
* sizes have to be in terms of multiples of 4
* bytes. So, if the Configuration File isn't
* a multiple of 4 bytes in length we'll have
@@ -4878,8 +5202,9 @@ static int adap_init0_config(struct adapter *adapter, int reset)
mtype = FW_PARAMS_PARAM_Y_GET(val[0]);
maddr = FW_PARAMS_PARAM_Z_GET(val[0]) << 16;
- ret = t4_memory_write(adapter, mtype, maddr,
- size, data);
+ spin_lock(&adapter->win0_lock);
+ ret = t4_memory_rw(adapter, 0, mtype, maddr,
+ size, data, T4_MEMORY_WRITE);
if (ret == 0 && resid != 0) {
union {
__be32 word;
@@ -4890,10 +5215,12 @@ static int adap_init0_config(struct adapter *adapter, int reset)
last.word = data[size >> 2];
for (i = resid; i < 4; i++)
last.buf[i] = 0;
- ret = t4_memory_write(adapter, mtype,
- maddr + size,
- 4, &last.word);
+ ret = t4_memory_rw(adapter, 0, mtype,
+ maddr + size,
+ 4, &last.word,
+ T4_MEMORY_WRITE);
}
+ spin_unlock(&adapter->win0_lock);
}
}
@@ -5637,6 +5964,22 @@ static int adap_init0(struct adapter *adap)
adap->vres.cq.size = val[3] - val[2] + 1;
adap->vres.ocq.start = val[4];
adap->vres.ocq.size = val[5] - val[4] + 1;
+
+ params[0] = FW_PARAM_DEV(MAXORDIRD_QP);
+ params[1] = FW_PARAM_DEV(MAXIRD_ADAPTER);
+ ret = t4_query_params(adap, 0, 0, 0, 2, params, val);
+ if (ret < 0) {
+ adap->params.max_ordird_qp = 8;
+ adap->params.max_ird_adapter = 32 * adap->tids.ntids;
+ ret = 0;
+ } else {
+ adap->params.max_ordird_qp = val[0];
+ adap->params.max_ird_adapter = val[1];
+ }
+ dev_info(adap->pdev_dev,
+ "max_ordird_qp %d max_ird_adapter %d\n",
+ adap->params.max_ordird_qp,
+ adap->params.max_ird_adapter);
}
if (caps_cmd.iscsicaps) {
params[0] = FW_PARAM_PFVF(ISCSI_START);
@@ -5838,12 +6181,33 @@ static inline void init_rspq(struct adapter *adap, struct sge_rspq *q,
static void cfg_queues(struct adapter *adap)
{
struct sge *s = &adap->sge;
- int i, q10g = 0, n10g = 0, qidx = 0;
+ int i, n10g = 0, qidx = 0;
+#ifndef CONFIG_CHELSIO_T4_DCB
+ int q10g = 0;
+#endif
int ciq_size;
for_each_port(adap, i)
n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg);
+#ifdef CONFIG_CHELSIO_T4_DCB
+ /* For Data Center Bridging support we need to be able to support up
+ * to 8 Traffic Priorities; each of which will be assigned to its
+ * own TX Queue in order to prevent Head-Of-Line Blocking.
+ */
+ if (adap->params.nports * 8 > MAX_ETH_QSETS) {
+ dev_err(adap->pdev_dev, "MAX_ETH_QSETS=%d < %d!\n",
+ MAX_ETH_QSETS, adap->params.nports * 8);
+ BUG_ON(1);
+ }
+ for_each_port(adap, i) {
+ struct port_info *pi = adap2pinfo(adap, i);
+
+ pi->first_qset = qidx;
+ pi->nqsets = 8;
+ qidx += pi->nqsets;
+ }
+#else /* !CONFIG_CHELSIO_T4_DCB */
/*
* We default to 1 queue per non-10G port and up to # of cores queues
* per 10G port.
@@ -5860,6 +6224,7 @@ static void cfg_queues(struct adapter *adap)
pi->nqsets = is_x_10g_port(&pi->link_cfg) ? q10g : 1;
qidx += pi->nqsets;
}
+#endif /* !CONFIG_CHELSIO_T4_DCB */
s->ethqsets = qidx;
s->max_ethqsets = qidx; /* MSI-X may lower it later */
@@ -5978,8 +6343,14 @@ static int enable_msix(struct adapter *adap)
/* need nchan for each possible ULD */
ofld_need = 3 * nchan;
}
+#ifdef CONFIG_CHELSIO_T4_DCB
+ /* For Data Center Bridging we need 8 Ethernet TX Priority Queues for
+ * each port.
+ */
+ need = 8 * adap->params.nports + EXTRA_VECS + ofld_need;
+#else
need = adap->params.nports + EXTRA_VECS + ofld_need;
-
+#endif
want = pci_enable_msix_range(adap->pdev, entries, need, want);
if (want < 0)
return want;
@@ -6111,13 +6482,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return err;
}
- /* We control everything through one PF */
- func = PCI_FUNC(pdev->devfn);
- if (func != ent->driver_data) {
- pci_save_state(pdev); /* to restore SR-IOV later */
- goto sriov;
- }
-
err = pci_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "cannot enable PCI device\n");
@@ -6161,6 +6525,15 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out_free_adapter;
}
+ /* We control everything through one PF */
+ func = SOURCEPF_GET(readl(adapter->regs + PL_WHOAMI));
+ if ((pdev->device == 0xa000 && func != 0) ||
+ func != ent->driver_data) {
+ pci_save_state(pdev); /* to restore SR-IOV later */
+ err = 0;
+ goto out_unmap_bar0;
+ }
+
adapter->pdev = pdev;
adapter->pdev_dev = &pdev->dev;
adapter->mbox = func;
@@ -6242,6 +6615,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
netdev->priv_flags |= IFF_UNICAST_FLT;
netdev->netdev_ops = &cxgb4_netdev_ops;
+#ifdef CONFIG_CHELSIO_T4_DCB
+ netdev->dcbnl_ops = &cxgb4_dcb_ops;
+ cxgb4_dcb_state_init(netdev);
+#endif
netdev->ethtool_ops = &cxgb_ethtool_ops;
}
@@ -6320,7 +6697,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (is_offload(adapter))
attach_ulds(adapter);
-sriov:
#ifdef CONFIG_PCI_IOV
if (func < ARRAY_SIZE(num_vf) && num_vf[func] > 0)
if (pci_enable_sriov(pdev, num_vf[func]) == 0)
@@ -6366,8 +6742,7 @@ static void remove_one(struct pci_dev *pdev)
if (adapter->port[i]->reg_state == NETREG_REGISTERED)
unregister_netdev(adapter->port[i]);
- if (adapter->debugfs_root)
- debugfs_remove_recursive(adapter->debugfs_root);
+ debugfs_remove_recursive(adapter->debugfs_root);
/* If we allocated filters, free up state associated with any
* valid filters ...
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
index 55e9daf7f9d4..1366ba620c87 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -172,6 +172,10 @@ int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid,
unsigned char port, unsigned char mask);
int cxgb4_remove_server_filter(const struct net_device *dev, unsigned int stid,
unsigned int queue, bool ipv6);
+int cxgb4_clip_get(const struct net_device *dev, const struct in6_addr *lip);
+int cxgb4_clip_release(const struct net_device *dev,
+ const struct in6_addr *lip);
+
static inline void set_wr_txq(struct sk_buff *skb, int prio, int queue)
{
skb_set_queue_mapping(skb, (queue << 1) | prio);
@@ -243,6 +247,7 @@ struct cxgb4_lld_info {
unsigned char fw_api_ver; /* FW API version */
unsigned int fw_vers; /* FW version */
unsigned int iscsi_iolen; /* iSCSI max I/O length */
+ unsigned int cclk_ps; /* Core clock period in psec */
unsigned short udb_density; /* # of user DB/page */
unsigned short ucq_density; /* # of user CQs/page */
unsigned short filt_mode; /* filter optional components */
@@ -251,10 +256,15 @@ struct cxgb4_lld_info {
void __iomem *gts_reg; /* address of GTS register */
void __iomem *db_reg; /* address of kernel doorbell */
int dbfifo_int_thresh; /* doorbell fifo int threshold */
+ unsigned int sge_ingpadboundary; /* SGE ingress padding boundary */
+ unsigned int sge_egrstatuspagesize; /* SGE egress status page size */
unsigned int sge_pktshift; /* Padding between CPL and */
/* packet data */
+ unsigned int pf; /* Physical Function we're using */
bool enable_fw_ofld_conn; /* Enable connection through fw */
/* WR */
+ unsigned int max_ordird_qp; /* Max ORD/IRD depth per RDMA QP */
+ unsigned int max_ird_adapter; /* Max IRD memory per adapter */
bool ulptx_memwrite_dsgl; /* use of T5 DSGL allowed */
};
@@ -291,5 +301,7 @@ int cxgb4_sync_txq_pidx(struct net_device *dev, u16 qid, u16 pidx, u16 size);
int cxgb4_flush_eq_cache(struct net_device *dev);
void cxgb4_disable_db_coalescing(struct net_device *dev);
void cxgb4_enable_db_coalescing(struct net_device *dev);
+int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte);
+u64 cxgb4_read_sge_timestamp(struct net_device *dev);
#endif /* !__CXGB4_OFLD_H */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c
index 8a96572fdde0..96041397ee15 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.h b/drivers/net/ethernet/chelsio/cxgb4/l2t.h
index 85eb5c71358d..a30126ce90cb 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/l2t.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index dd4355d248e4..b0bba32d69d5 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -2480,6 +2480,22 @@ static void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq,
}
/**
+ * t4_free_ofld_rxqs - free a block of consecutive Rx queues
+ * @adap: the adapter
+ * @n: number of queues
+ * @q: pointer to first queue
+ *
+ * Release the resources of a consecutive block of offload Rx queues.
+ */
+void t4_free_ofld_rxqs(struct adapter *adap, int n, struct sge_ofld_rxq *q)
+{
+ for ( ; n; n--, q++)
+ if (q->rspq.desc)
+ free_rspq_fl(adap, &q->rspq,
+ q->fl.size ? &q->fl : NULL);
+}
+
+/**
* t4_free_sge_resources - free SGE resources
* @adap: the adapter
*
@@ -2490,12 +2506,12 @@ void t4_free_sge_resources(struct adapter *adap)
int i;
struct sge_eth_rxq *eq = adap->sge.ethrxq;
struct sge_eth_txq *etq = adap->sge.ethtxq;
- struct sge_ofld_rxq *oq = adap->sge.ofldrxq;
/* clean up Ethernet Tx/Rx queues */
for (i = 0; i < adap->sge.ethqsets; i++, eq++, etq++) {
if (eq->rspq.desc)
- free_rspq_fl(adap, &eq->rspq, &eq->fl);
+ free_rspq_fl(adap, &eq->rspq,
+ eq->fl.size ? &eq->fl : NULL);
if (etq->q.desc) {
t4_eth_eq_free(adap, adap->fn, adap->fn, 0,
etq->q.cntxt_id);
@@ -2506,18 +2522,9 @@ void t4_free_sge_resources(struct adapter *adap)
}
/* clean up RDMA and iSCSI Rx queues */
- for (i = 0; i < adap->sge.ofldqsets; i++, oq++) {
- if (oq->rspq.desc)
- free_rspq_fl(adap, &oq->rspq, &oq->fl);
- }
- for (i = 0, oq = adap->sge.rdmarxq; i < adap->sge.rdmaqs; i++, oq++) {
- if (oq->rspq.desc)
- free_rspq_fl(adap, &oq->rspq, &oq->fl);
- }
- for (i = 0, oq = adap->sge.rdmaciq; i < adap->sge.rdmaciqs; i++, oq++) {
- if (oq->rspq.desc)
- free_rspq_fl(adap, &oq->rspq, &oq->fl);
- }
+ t4_free_ofld_rxqs(adap, adap->sge.ofldqsets, adap->sge.ofldrxq);
+ t4_free_ofld_rxqs(adap, adap->sge.rdmaqs, adap->sge.rdmarxq);
+ t4_free_ofld_rxqs(adap, adap->sge.rdmaciqs, adap->sge.rdmaciq);
/* clean up offload Tx queues */
for (i = 0; i < ARRAY_SIZE(adap->sge.ofldtxq); i++) {
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 931478e7bd28..a853133d8db8 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -144,6 +144,30 @@ void t4_write_indirect(struct adapter *adap, unsigned int addr_reg,
}
/*
+ * Read a 32-bit PCI Configuration Space register via the PCI-E backdoor
+ * mechanism. This guarantees that we get the real value even if we're
+ * operating within a Virtual Machine and the Hypervisor is trapping our
+ * Configuration Space accesses.
+ */
+void t4_hw_pci_read_cfg4(struct adapter *adap, int reg, u32 *val)
+{
+ u32 req = ENABLE | FUNCTION(adap->fn) | reg;
+
+ if (is_t4(adap->params.chip))
+ req |= F_LOCALCFG;
+
+ t4_write_reg(adap, PCIE_CFG_SPACE_REQ, req);
+ *val = t4_read_reg(adap, PCIE_CFG_SPACE_DATA);
+
+ /* Reset ENABLE to 0 so reads of PCIE_CFG_SPACE_DATA won't cause a
+ * Configuration Space read. (None of the other fields matter when
+ * ENABLE is 0 so a simple register write is easier than a
+ * read-modify-write via t4_set_reg_field().)
+ */
+ t4_write_reg(adap, PCIE_CFG_SPACE_REQ, 0);
+}
+
+/*
* Get the reply to a mailbox command and store it in @rpl in big-endian order.
*/
static void get_mbox_rpl(struct adapter *adap, __be64 *rpl, int nflit,
@@ -389,78 +413,41 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc)
return 0;
}
-/*
- * t4_mem_win_rw - read/write memory through PCIE memory window
- * @adap: the adapter
- * @addr: address of first byte requested
- * @data: MEMWIN0_APERTURE bytes of data containing the requested address
- * @dir: direction of transfer 1 => read, 0 => write
- *
- * Read/write MEMWIN0_APERTURE bytes of data from MC starting at a
- * MEMWIN0_APERTURE-byte-aligned address that covers the requested
- * address @addr.
- */
-static int t4_mem_win_rw(struct adapter *adap, u32 addr, __be32 *data, int dir)
-{
- int i;
- u32 win_pf = is_t4(adap->params.chip) ? 0 : V_PFNUM(adap->fn);
-
- /*
- * Setup offset into PCIE memory window. Address must be a
- * MEMWIN0_APERTURE-byte-aligned address. (Read back MA register to
- * ensure that changes propagate before we attempt to use the new
- * values.)
- */
- t4_write_reg(adap, PCIE_MEM_ACCESS_OFFSET,
- (addr & ~(MEMWIN0_APERTURE - 1)) | win_pf);
- t4_read_reg(adap, PCIE_MEM_ACCESS_OFFSET);
-
- /* Collecting data 4 bytes at a time upto MEMWIN0_APERTURE */
- for (i = 0; i < MEMWIN0_APERTURE; i = i+0x4) {
- if (dir)
- *data++ = (__force __be32) t4_read_reg(adap,
- (MEMWIN0_BASE + i));
- else
- t4_write_reg(adap, (MEMWIN0_BASE + i),
- (__force u32) *data++);
- }
-
- return 0;
-}
-
/**
* t4_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window
* @adap: the adapter
+ * @win: PCI-E Memory Window to use
* @mtype: memory type: MEM_EDC0, MEM_EDC1 or MEM_MC
* @addr: address within indicated memory type
* @len: amount of memory to transfer
* @buf: host memory buffer
- * @dir: direction of transfer 1 => read, 0 => write
+ * @dir: direction of transfer T4_MEMORY_READ (1) or T4_MEMORY_WRITE (0)
*
* Reads/writes an [almost] arbitrary memory region in the firmware: the
- * firmware memory address, length and host buffer must be aligned on
- * 32-bit boudaries. The memory is transferred as a raw byte sequence
- * from/to the firmware's memory. If this memory contains data
- * structures which contain multi-byte integers, it's the callers
- * responsibility to perform appropriate byte order conversions.
+ * firmware memory address and host buffer must be aligned on 32-bit
+ * boudaries; the length may be arbitrary. The memory is transferred as
+ * a raw byte sequence from/to the firmware's memory. If this memory
+ * contains data structures which contain multi-byte integers, it's the
+ * caller's responsibility to perform appropriate byte order conversions.
*/
-static int t4_memory_rw(struct adapter *adap, int mtype, u32 addr, u32 len,
- __be32 *buf, int dir)
+int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr,
+ u32 len, __be32 *buf, int dir)
{
- u32 pos, start, end, offset, memoffset;
- u32 edc_size, mc_size;
- int ret = 0;
- __be32 *data;
+ u32 pos, offset, resid, memoffset;
+ u32 edc_size, mc_size, win_pf, mem_reg, mem_aperture, mem_base;
- /*
- * Argument sanity checks ...
+ /* Argument sanity checks ...
*/
- if ((addr & 0x3) || (len & 0x3))
+ if (addr & 0x3)
return -EINVAL;
- data = vmalloc(MEMWIN0_APERTURE);
- if (!data)
- return -ENOMEM;
+ /* It's convenient to be able to handle lengths which aren't a
+ * multiple of 32-bits because we often end up transferring files to
+ * the firmware. So we'll handle that by normalizing the length here
+ * and then handling any residual transfer at the end.
+ */
+ resid = len & 0x3;
+ len -= resid;
/* Offset into the region of memory which is being accessed
* MEM_EDC0 = 0
@@ -481,66 +468,98 @@ static int t4_memory_rw(struct adapter *adap, int mtype, u32 addr, u32 len,
/* Determine the PCIE_MEM_ACCESS_OFFSET */
addr = addr + memoffset;
- /*
- * The underlaying EDC/MC read routines read MEMWIN0_APERTURE bytes
- * at a time so we need to round down the start and round up the end.
- * We'll start copying out of the first line at (addr - start) a word
- * at a time.
+ /* Each PCI-E Memory Window is programmed with a window size -- or
+ * "aperture" -- which controls the granularity of its mapping onto
+ * adapter memory. We need to grab that aperture in order to know
+ * how to use the specified window. The window is also programmed
+ * with the base address of the Memory Window in BAR0's address
+ * space. For T4 this is an absolute PCI-E Bus Address. For T5
+ * the address is relative to BAR0.
*/
- start = addr & ~(MEMWIN0_APERTURE-1);
- end = (addr + len + MEMWIN0_APERTURE-1) & ~(MEMWIN0_APERTURE-1);
- offset = (addr - start)/sizeof(__be32);
+ mem_reg = t4_read_reg(adap,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN,
+ win));
+ mem_aperture = 1 << (GET_WINDOW(mem_reg) + 10);
+ mem_base = GET_PCIEOFST(mem_reg) << 10;
+ if (is_t4(adap->params.chip))
+ mem_base -= adap->t4_bar0;
+ win_pf = is_t4(adap->params.chip) ? 0 : V_PFNUM(adap->fn);
- for (pos = start; pos < end; pos += MEMWIN0_APERTURE, offset = 0) {
+ /* Calculate our initial PCI-E Memory Window Position and Offset into
+ * that Window.
+ */
+ pos = addr & ~(mem_aperture-1);
+ offset = addr - pos;
- /*
- * If we're writing, copy the data from the caller's memory
- * buffer
+ /* Set up initial PCI-E Memory Window to cover the start of our
+ * transfer. (Read it back to ensure that changes propagate before we
+ * attempt to use the new value.)
+ */
+ t4_write_reg(adap,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win),
+ pos | win_pf);
+ t4_read_reg(adap,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win));
+
+ /* Transfer data to/from the adapter as long as there's an integral
+ * number of 32-bit transfers to complete.
+ */
+ while (len > 0) {
+ if (dir == T4_MEMORY_READ)
+ *buf++ = (__force __be32) t4_read_reg(adap,
+ mem_base + offset);
+ else
+ t4_write_reg(adap, mem_base + offset,
+ (__force u32) *buf++);
+ offset += sizeof(__be32);
+ len -= sizeof(__be32);
+
+ /* If we've reached the end of our current window aperture,
+ * move the PCI-E Memory Window on to the next. Note that
+ * doing this here after "len" may be 0 allows us to set up
+ * the PCI-E Memory Window for a possible final residual
+ * transfer below ...
*/
- if (!dir) {
- /*
- * If we're doing a partial write, then we need to do
- * a read-modify-write ...
- */
- if (offset || len < MEMWIN0_APERTURE) {
- ret = t4_mem_win_rw(adap, pos, data, 1);
- if (ret)
- break;
- }
- while (offset < (MEMWIN0_APERTURE/sizeof(__be32)) &&
- len > 0) {
- data[offset++] = *buf++;
- len -= sizeof(__be32);
- }
+ if (offset == mem_aperture) {
+ pos += mem_aperture;
+ offset = 0;
+ t4_write_reg(adap,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET,
+ win), pos | win_pf);
+ t4_read_reg(adap,
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET,
+ win));
}
-
- /*
- * Transfer a block of memory and bail if there's an error.
- */
- ret = t4_mem_win_rw(adap, pos, data, dir);
- if (ret)
- break;
-
- /*
- * If we're reading, copy the data into the caller's memory
- * buffer.
- */
- if (dir)
- while (offset < (MEMWIN0_APERTURE/sizeof(__be32)) &&
- len > 0) {
- *buf++ = data[offset++];
- len -= sizeof(__be32);
- }
}
- vfree(data);
- return ret;
-}
+ /* If the original transfer had a length which wasn't a multiple of
+ * 32-bits, now's where we need to finish off the transfer of the
+ * residual amount. The PCI-E Memory Window has already been moved
+ * above (if necessary) to cover this final transfer.
+ */
+ if (resid) {
+ union {
+ __be32 word;
+ char byte[4];
+ } last;
+ unsigned char *bp;
+ int i;
+
+ if (dir == T4_MEMORY_READ) {
+ last.word = (__force __be32) t4_read_reg(adap,
+ mem_base + offset);
+ for (bp = (unsigned char *)buf, i = resid; i < 4; i++)
+ bp[i] = last.byte[i];
+ } else {
+ last.word = *buf;
+ for (i = resid; i < 4; i++)
+ last.byte[i] = 0;
+ t4_write_reg(adap, mem_base + offset,
+ (__force u32) last.word);
+ }
+ }
-int t4_memory_write(struct adapter *adap, int mtype, u32 addr, u32 len,
- __be32 *buf)
-{
- return t4_memory_rw(adap, mtype, addr, len, buf, 0);
+ return 0;
}
#define EEPROM_STAT_ADDR 0x7bfc
@@ -1700,16 +1719,24 @@ static void mps_intr_handler(struct adapter *adapter)
*/
static void mem_intr_handler(struct adapter *adapter, int idx)
{
- static const char name[3][5] = { "EDC0", "EDC1", "MC" };
+ static const char name[4][7] = { "EDC0", "EDC1", "MC/MC0", "MC1" };
unsigned int addr, cnt_addr, v;
if (idx <= MEM_EDC1) {
addr = EDC_REG(EDC_INT_CAUSE, idx);
cnt_addr = EDC_REG(EDC_ECC_STATUS, idx);
+ } else if (idx == MEM_MC) {
+ if (is_t4(adapter->params.chip)) {
+ addr = MC_INT_CAUSE;
+ cnt_addr = MC_ECC_STATUS;
+ } else {
+ addr = MC_P_INT_CAUSE;
+ cnt_addr = MC_P_ECC_STATUS;
+ }
} else {
- addr = MC_INT_CAUSE;
- cnt_addr = MC_ECC_STATUS;
+ addr = MC_REG(MC_P_INT_CAUSE, 1);
+ cnt_addr = MC_REG(MC_P_ECC_STATUS, 1);
}
v = t4_read_reg(adapter, addr) & MEM_INT_MASK;
@@ -1873,6 +1900,8 @@ int t4_slow_intr_handler(struct adapter *adapter)
pcie_intr_handler(adapter);
if (cause & MC)
mem_intr_handler(adapter, MEM_MC);
+ if (!is_t4(adapter->params.chip) && (cause & MC1))
+ mem_intr_handler(adapter, MEM_MC1);
if (cause & EDC0)
mem_intr_handler(adapter, MEM_EDC0);
if (cause & EDC1)
@@ -2505,39 +2534,6 @@ int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox,
}
/**
- * t4_mem_win_read_len - read memory through PCIE memory window
- * @adap: the adapter
- * @addr: address of first byte requested aligned on 32b.
- * @data: len bytes to hold the data read
- * @len: amount of data to read from window. Must be <=
- * MEMWIN0_APERATURE after adjusting for 16B for T4 and
- * 128B for T5 alignment requirements of the the memory window.
- *
- * Read len bytes of data from MC starting at @addr.
- */
-int t4_mem_win_read_len(struct adapter *adap, u32 addr, __be32 *data, int len)
-{
- int i, off;
- u32 win_pf = is_t4(adap->params.chip) ? 0 : V_PFNUM(adap->fn);
-
- /* Align on a 2KB boundary.
- */
- off = addr & MEMWIN0_APERTURE;
- if ((addr & 3) || (len + off) > MEMWIN0_APERTURE)
- return -EINVAL;
-
- t4_write_reg(adap, PCIE_MEM_ACCESS_OFFSET,
- (addr & ~MEMWIN0_APERTURE) | win_pf);
- t4_read_reg(adap, PCIE_MEM_ACCESS_OFFSET);
-
- for (i = 0; i < len; i += 4)
- *data++ = (__force __be32) t4_read_reg(adap,
- (MEMWIN0_BASE + off + i));
-
- return 0;
-}
-
-/**
* t4_mdio_rd - read a PHY register through MDIO
* @adap: the adapter
* @mbox: mailbox to use for the FW command
@@ -3175,6 +3171,46 @@ int t4_query_params(struct adapter *adap, unsigned int mbox, unsigned int pf,
}
/**
+ * t4_set_params_nosleep - sets FW or device parameters
+ * @adap: the adapter
+ * @mbox: mailbox to use for the FW command
+ * @pf: the PF
+ * @vf: the VF
+ * @nparams: the number of parameters
+ * @params: the parameter names
+ * @val: the parameter values
+ *
+ * Does not ever sleep
+ * Sets the value of FW or device parameters. Up to 7 parameters can be
+ * specified at once.
+ */
+int t4_set_params_nosleep(struct adapter *adap, unsigned int mbox,
+ unsigned int pf, unsigned int vf,
+ unsigned int nparams, const u32 *params,
+ const u32 *val)
+{
+ struct fw_params_cmd c;
+ __be32 *p = &c.param[0].mnem;
+
+ if (nparams > 7)
+ return -EINVAL;
+
+ memset(&c, 0, sizeof(c));
+ c.op_to_vfn = cpu_to_be32(FW_CMD_OP(FW_PARAMS_CMD) |
+ FW_CMD_REQUEST | FW_CMD_WRITE |
+ FW_PARAMS_CMD_PFN(pf) |
+ FW_PARAMS_CMD_VFN(vf));
+ c.retval_len16 = cpu_to_be32(FW_LEN16(c));
+
+ while (nparams--) {
+ *p++ = cpu_to_be32(*params++);
+ *p++ = cpu_to_be32(*val++);
+ }
+
+ return t4_wr_mbox_ns(adap, mbox, &c, sizeof(c), NULL);
+}
+
+/**
* t4_set_params - sets FW or device parameters
* @adap: the adapter
* @mbox: mailbox to use for the FW command
@@ -3499,6 +3535,33 @@ int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid,
}
/**
+ * t4_enable_vi_params - enable/disable a virtual interface
+ * @adap: the adapter
+ * @mbox: mailbox to use for the FW command
+ * @viid: the VI id
+ * @rx_en: 1=enable Rx, 0=disable Rx
+ * @tx_en: 1=enable Tx, 0=disable Tx
+ * @dcb_en: 1=enable delivery of Data Center Bridging messages.
+ *
+ * Enables/disables a virtual interface. Note that setting DCB Enable
+ * only makes sense when enabling a Virtual Interface ...
+ */
+int t4_enable_vi_params(struct adapter *adap, unsigned int mbox,
+ unsigned int viid, bool rx_en, bool tx_en, bool dcb_en)
+{
+ struct fw_vi_enable_cmd c;
+
+ memset(&c, 0, sizeof(c));
+ c.op_to_viid = htonl(FW_CMD_OP(FW_VI_ENABLE_CMD) | FW_CMD_REQUEST |
+ FW_CMD_EXEC | FW_VI_ENABLE_CMD_VIID(viid));
+
+ c.ien_to_len16 = htonl(FW_VI_ENABLE_CMD_IEN(rx_en) |
+ FW_VI_ENABLE_CMD_EEN(tx_en) | FW_LEN16(c) |
+ FW_VI_ENABLE_CMD_DCB_INFO(dcb_en));
+ return t4_wr_mbox_ns(adap, mbox, &c, sizeof(c), NULL);
+}
+
+/**
* t4_enable_vi - enable/disable a virtual interface
* @adap: the adapter
* @mbox: mailbox to use for the FW command
@@ -3511,14 +3574,7 @@ int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid,
int t4_enable_vi(struct adapter *adap, unsigned int mbox, unsigned int viid,
bool rx_en, bool tx_en)
{
- struct fw_vi_enable_cmd c;
-
- memset(&c, 0, sizeof(c));
- c.op_to_viid = htonl(FW_CMD_OP(FW_VI_ENABLE_CMD) | FW_CMD_REQUEST |
- FW_CMD_EXEC | FW_VI_ENABLE_CMD_VIID(viid));
- c.ien_to_len16 = htonl(FW_VI_ENABLE_CMD_IEN(rx_en) |
- FW_VI_ENABLE_CMD_EEN(tx_en) | FW_LEN16(c));
- return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL);
+ return t4_enable_vi_params(adap, mbox, viid, rx_en, tx_en, 0);
}
/**
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
index 71b799b5b0f4..35e3d8e32881 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
index 973eb11aa98a..52e08103f221 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -75,6 +75,7 @@ enum {
CPL_RX_DATA_DDP = 0x42,
CPL_PASS_ACCEPT_REQ = 0x44,
CPL_TRACE_PKT_T5 = 0x48,
+ CPL_RX_ISCSI_DDP = 0x49,
CPL_RDMA_READ_REQ = 0x60,
@@ -86,6 +87,7 @@ enum {
CPL_SGE_EGR_UPDATE = 0xA5,
CPL_TRACE_PKT = 0xB0,
+ CPL_ISCSI_DATA = 0xB2,
CPL_FW4_MSG = 0xC0,
CPL_FW4_PLD = 0xC1,
@@ -270,12 +272,15 @@ struct cpl_pass_accept_rpl {
#define RX_COALESCE_VALID(x) ((x) << 11)
#define RX_COALESCE(x) ((x) << 12)
#define PACE(x) ((x) << 16)
+#define RX_FC_VALID ((1U) << 19)
+#define RX_FC_DISABLE ((1U) << 20)
#define TX_QUEUE(x) ((x) << 23)
#define RX_CHANNEL(x) ((x) << 26)
#define CCTRL_ECN(x) ((x) << 27)
#define WND_SCALE_EN(x) ((x) << 28)
#define TSTAMPS_EN(x) ((x) << 29)
#define SACK_EN(x) ((x) << 30)
+#define T5_OPT_2_VALID ((1U) << 31)
__be64 opt0;
};
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
index 225ad8a5722d..e3146e83df20 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -251,6 +251,12 @@
#define V_NOCOALESCE(x) ((x) << S_NOCOALESCE)
#define F_NOCOALESCE V_NOCOALESCE(1U)
+#define SGE_TIMESTAMP_LO 0x1098
+#define SGE_TIMESTAMP_HI 0x109c
+#define S_TSVAL 0
+#define M_TSVAL 0xfffffffU
+#define GET_TSVAL(x) (((x) >> S_TSVAL) & M_TSVAL)
+
#define SGE_TIMER_VALUE_0_AND_1 0x10b8
#define TIMERVALUE0_MASK 0xffff0000U
#define TIMERVALUE0_SHIFT 16
@@ -387,6 +393,8 @@
#define MSTGRPPERR 0x00000001U
#define PCIE_NONFAT_ERR 0x3010
+#define PCIE_CFG_SPACE_REQ 0x3060
+#define PCIE_CFG_SPACE_DATA 0x3064
#define PCIE_MEM_ACCESS_BASE_WIN 0x3068
#define S_PCIEOFST 10
#define M_PCIEOFST 0x3fffffU
@@ -398,7 +406,11 @@
#define WINDOW_MASK 0x000000ffU
#define WINDOW_SHIFT 0
#define WINDOW(x) ((x) << WINDOW_SHIFT)
+#define GET_WINDOW(x) (((x) >> WINDOW_SHIFT) & WINDOW_MASK)
#define PCIE_MEM_ACCESS_OFFSET 0x306c
+#define ENABLE (1U << 30)
+#define FUNCTION(x) ((x) << 12)
+#define F_LOCALCFG (1U << 28)
#define S_PFNUM 0
#define V_PFNUM(x) ((x) << S_PFNUM)
@@ -436,11 +448,13 @@
#define TDUE 0x00010000U
#define MC_INT_CAUSE 0x7518
+#define MC_P_INT_CAUSE 0x41318
#define ECC_UE_INT_CAUSE 0x00000004U
#define ECC_CE_INT_CAUSE 0x00000002U
#define PERR_INT_CAUSE 0x00000001U
#define MC_ECC_STATUS 0x751c
+#define MC_P_ECC_STATUS 0x4131c
#define ECC_CECNT_MASK 0xffff0000U
#define ECC_CECNT_SHIFT 16
#define ECC_CECNT(x) ((x) << ECC_CECNT_SHIFT)
@@ -1089,6 +1103,7 @@
#define I2CM 0x00000002U
#define CIM 0x00000001U
+#define MC1 0x31
#define PL_INT_ENABLE 0x19410
#define PL_INT_MAP0 0x19414
#define PL_RST 0x19428
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
index 9cc973fbcf26..ff709e3b3e7e 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
@@ -1,7 +1,7 @@
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
- * Copyright (c) 2009-2010 Chelsio Communications, Inc. All rights reserved.
+ * Copyright (c) 2009-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -46,9 +46,11 @@ enum fw_retval {
FW_EFAULT = 14, /* bad address; fw bad */
FW_EBUSY = 16, /* resource busy */
FW_EEXIST = 17, /* file exists */
+ FW_ENODEV = 19, /* no such device */
FW_EINVAL = 22, /* invalid argument */
FW_ENOSPC = 28, /* no space left on device */
FW_ENOSYS = 38, /* functionality not implemented */
+ FW_ENODATA = 61, /* no data available */
FW_EPROTO = 71, /* protocol error */
FW_EADDRINUSE = 98, /* address already in use */
FW_EADDRNOTAVAIL = 99, /* cannot assigned requested address */
@@ -932,6 +934,8 @@ enum fw_params_param_dev {
FW_PARAMS_PARAM_DEV_FWREV = 0x0B,
FW_PARAMS_PARAM_DEV_TPREV = 0x0C,
FW_PARAMS_PARAM_DEV_CF = 0x0D,
+ FW_PARAMS_PARAM_DEV_MAXORDIRD_QP = 0x13, /* max supported QP IRD/ORD */
+ FW_PARAMS_PARAM_DEV_MAXIRD_ADAPTER = 0x14, /* max supported adap IRD */
FW_PARAMS_PARAM_DEV_ULPTX_MEMWRITE_DSGL = 0x17,
};
@@ -989,6 +993,7 @@ enum fw_params_param_dmaq {
FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_MNGT = 0x10,
FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_CTRL = 0x11,
FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH = 0x12,
+ FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH = 0x13,
};
#define FW_PARAMS_MNEM(x) ((x) << 24)
@@ -1422,6 +1427,7 @@ struct fw_vi_enable_cmd {
#define FW_VI_ENABLE_CMD_VIID(x) ((x) << 0)
#define FW_VI_ENABLE_CMD_IEN(x) ((x) << 31)
#define FW_VI_ENABLE_CMD_EEN(x) ((x) << 30)
+#define FW_VI_ENABLE_CMD_DCB_INFO(x) ((x) << 28)
#define FW_VI_ENABLE_CMD_LED (1U << 29)
/* VI VF stats offset definitions */
@@ -1594,6 +1600,9 @@ enum fw_port_action {
FW_PORT_ACTION_GET_PORT_INFO = 0x0003,
FW_PORT_ACTION_L2_PPP_CFG = 0x0004,
FW_PORT_ACTION_L2_DCB_CFG = 0x0005,
+ FW_PORT_ACTION_DCB_READ_TRANS = 0x0006,
+ FW_PORT_ACTION_DCB_READ_RECV = 0x0007,
+ FW_PORT_ACTION_DCB_READ_DET = 0x0008,
FW_PORT_ACTION_LOW_PWR_TO_NORMAL = 0x0010,
FW_PORT_ACTION_L1_LOW_PWR_EN = 0x0011,
FW_PORT_ACTION_L2_WOL_MODE_EN = 0x0012,
@@ -1637,6 +1646,14 @@ enum fw_port_dcb_type {
FW_PORT_DCB_TYPE_PRIORATE = 0x02,
FW_PORT_DCB_TYPE_PFC = 0x03,
FW_PORT_DCB_TYPE_APP_ID = 0x04,
+ FW_PORT_DCB_TYPE_CONTROL = 0x05,
+};
+
+enum fw_port_dcb_feature_state {
+ FW_PORT_DCB_FEATURE_STATE_PENDING = 0x0,
+ FW_PORT_DCB_FEATURE_STATE_SUCCESS = 0x1,
+ FW_PORT_DCB_FEATURE_STATE_ERROR = 0x2,
+ FW_PORT_DCB_FEATURE_STATE_TIMEOUT = 0x3,
};
struct fw_port_cmd {
@@ -1648,9 +1665,11 @@ struct fw_port_cmd {
__be32 r;
} l1cfg;
struct fw_port_l2cfg {
- __be16 ctlbf_to_ivlan0;
+ __u8 ctlbf;
+ __u8 ovlan3_to_ivlan0;
__be16 ivlantype;
- __be32 txipg_pkd;
+ __be16 txipg_force_pinfo;
+ __be16 mtu;
__be16 ovlan0mask;
__be16 ovlan0type;
__be16 ovlan1mask;
@@ -1666,24 +1685,60 @@ struct fw_port_cmd {
__be16 acap;
__be16 mtu;
__u8 cbllen;
- __u8 r9;
- __be32 r10;
- __be64 r11;
+ __u8 auxlinfo;
+ __u8 dcbxdis_pkd;
+ __u8 r8_lo[3];
+ __be64 r9;
} info;
- struct fw_port_ppp {
- __be32 pppen_to_ncsich;
- __be32 r11;
- } ppp;
- struct fw_port_dcb {
- __be16 cfg;
- u8 up_map;
- u8 sf_cfgrc;
- __be16 prot_ix;
- u8 pe7_to_pe0;
- u8 numTCPFCs;
- __be32 pgid0_to_pgid7;
- __be32 numTCs_oui;
- u8 pgpc[8];
+ struct fw_port_diags {
+ __u8 diagop;
+ __u8 r[3];
+ __be32 diagval;
+ } diags;
+ union fw_port_dcb {
+ struct fw_port_dcb_pgid {
+ __u8 type;
+ __u8 apply_pkd;
+ __u8 r10_lo[2];
+ __be32 pgid;
+ __be64 r11;
+ } pgid;
+ struct fw_port_dcb_pgrate {
+ __u8 type;
+ __u8 apply_pkd;
+ __u8 r10_lo[5];
+ __u8 num_tcs_supported;
+ __u8 pgrate[8];
+ } pgrate;
+ struct fw_port_dcb_priorate {
+ __u8 type;
+ __u8 apply_pkd;
+ __u8 r10_lo[6];
+ __u8 strict_priorate[8];
+ } priorate;
+ struct fw_port_dcb_pfc {
+ __u8 type;
+ __u8 pfcen;
+ __u8 r10[5];
+ __u8 max_pfc_tcs;
+ __be64 r11;
+ } pfc;
+ struct fw_port_app_priority {
+ __u8 type;
+ __u8 r10[2];
+ __u8 idx;
+ __u8 user_prio_map;
+ __u8 sel_field;
+ __be16 protocolid;
+ __be64 r12;
+ } app_priority;
+ struct fw_port_dcb_control {
+ __u8 type;
+ __u8 all_syncd_pkd;
+ __be16 pfc_state_to_app_state;
+ __be32 r11;
+ __be64 r12;
+ } control;
} dcb;
} u;
};
@@ -1720,6 +1775,10 @@ struct fw_port_cmd {
#define FW_PORT_CMD_MODTYPE_MASK 0x1f
#define FW_PORT_CMD_MODTYPE_GET(x) (((x) >> 0) & FW_PORT_CMD_MODTYPE_MASK)
+#define FW_PORT_CMD_DCBXDIS (1U << 7)
+#define FW_PORT_CMD_APPLY (1U << 7)
+#define FW_PORT_CMD_ALL_SYNCD (1U << 7)
+
#define FW_PORT_CMD_PPPEN(x) ((x) << 31)
#define FW_PORT_CMD_TPSRC(x) ((x) << 28)
#define FW_PORT_CMD_NCSISRC(x) ((x) << 24)
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
index ff1cdd1788b5..d8d28e82ade1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
@@ -2876,24 +2876,24 @@ static void cxgb4vf_pci_shutdown(struct pci_dev *pdev)
if (!adapter)
return;
- /*
- * Disable all Virtual Interfaces. This will shut down the
+ /* Disable all Virtual Interfaces. This will shut down the
* delivery of all ingress packets into the chip for these
* Virtual Interfaces.
*/
- for_each_port(adapter, pidx) {
- struct net_device *netdev;
- struct port_info *pi;
-
- if (!test_bit(pidx, &adapter->registered_device_map))
- continue;
-
- netdev = adapter->port[pidx];
- if (!netdev)
- continue;
+ for_each_port(adapter, pidx)
+ if (test_bit(pidx, &adapter->registered_device_map))
+ unregister_netdev(adapter->port[pidx]);
- pi = netdev_priv(netdev);
- t4vf_enable_vi(adapter, pi->viid, false, false);
+ /* Free up all Queues which will prevent further DMA and
+ * Interrupts allowing various internal pathways to drain.
+ */
+ t4vf_sge_stop(adapter);
+ if (adapter->flags & USING_MSIX) {
+ pci_disable_msix(adapter->pdev);
+ adapter->flags &= ~USING_MSIX;
+ } else if (adapter->flags & USING_MSI) {
+ pci_disable_msi(adapter->pdev);
+ adapter->flags &= ~USING_MSI;
}
/*
@@ -2901,6 +2901,7 @@ static void cxgb4vf_pci_shutdown(struct pci_dev *pdev)
* Interrupts allowing various internal pathways to drain.
*/
t4vf_free_sge_resources(adapter);
+ pci_set_drvdata(pdev, NULL);
}
/*
@@ -2924,6 +2925,15 @@ static DEFINE_PCI_DEVICE_TABLE(cxgb4vf_pci_tbl) = {
CH_DEVICE(0x480a, 0), /* T404-bt */
CH_DEVICE(0x480d, 0), /* T480-cr */
CH_DEVICE(0x480e, 0), /* T440-lp-cr */
+ CH_DEVICE(0x4880, 0),
+ CH_DEVICE(0x4880, 1),
+ CH_DEVICE(0x4880, 2),
+ CH_DEVICE(0x4880, 3),
+ CH_DEVICE(0x4880, 4),
+ CH_DEVICE(0x4880, 5),
+ CH_DEVICE(0x4880, 6),
+ CH_DEVICE(0x4880, 7),
+ CH_DEVICE(0x4880, 8),
CH_DEVICE(0x5800, 0), /* T580-dbg */
CH_DEVICE(0x5801, 0), /* T520-cr */
CH_DEVICE(0x5802, 0), /* T522-cr */