From 14d385b9905920cc0136721316c185c45ee6e26c Mon Sep 17 00:00:00 2001 From: Sucheta Chakraborty Date: Fri, 23 Aug 2013 13:38:25 -0400 Subject: [PATCH] qlcnic: dcb: Query adapter DCB capabilities. o Query adapter DCB capabilities and populate local data structures with relevant information. o Add QLCNIC_DCB to Kconfig for enabling/disabling DCB. Signed-off-by: Sucheta Chakraborty Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/Kconfig | 11 + drivers/net/ethernet/qlogic/qlcnic/Makefile | 2 + drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 50 ++++ .../ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 1 + .../ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 5 + .../net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 1 + .../net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 213 ++++++++++++++++++ .../net/ethernet/qlogic/qlcnic/qlcnic_dcb.h | 32 +++ .../net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 1 + .../net/ethernet/qlogic/qlcnic/qlcnic_main.c | 22 ++ .../qlogic/qlcnic/qlcnic_sriov_common.c | 6 + .../ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | 1 + 12 files changed, 345 insertions(+) create mode 100644 drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c create mode 100644 drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h diff --git a/drivers/net/ethernet/qlogic/Kconfig b/drivers/net/ethernet/qlogic/Kconfig index 0e1797295a48..f59e6be4a66e 100644 --- a/drivers/net/ethernet/qlogic/Kconfig +++ b/drivers/net/ethernet/qlogic/Kconfig @@ -45,6 +45,17 @@ config QLCNIC_SRIOV This allows for virtual function acceleration in virtualized environments. +config QLCNIC_DCB + bool "QLOGIC QLCNIC 82XX and 83XX family DCB Support" + depends on QLCNIC && DCB + default y + ---help--- + This configuration parameter enables DCB support in QLE83XX + and QLE82XX Converged Ethernet devices. This allows for DCB + get operations support through rtNetlink interface. Only CEE + mode of DCB is supported. PG and PFC values are related only + to Tx. + config QLGE tristate "QLogic QLGE 10Gb Ethernet Driver Support" depends on PCI diff --git a/drivers/net/ethernet/qlogic/qlcnic/Makefile b/drivers/net/ethernet/qlogic/qlcnic/Makefile index 4b1fb3faa3b7..a848d2979722 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/Makefile +++ b/drivers/net/ethernet/qlogic/qlcnic/Makefile @@ -11,3 +11,5 @@ qlcnic-y := qlcnic_hw.o qlcnic_main.o qlcnic_init.o \ qlcnic_minidump.o qlcnic_sriov_common.o qlcnic-$(CONFIG_QLCNIC_SRIOV) += qlcnic_sriov_pf.o + +qlcnic-$(CONFIG_QLCNIC_DCB) += qlcnic_dcb.o diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 3f03856768a8..2196279b2a18 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -34,6 +34,7 @@ #include "qlcnic_hdr.h" #include "qlcnic_hw.h" #include "qlcnic_83xx_hw.h" +#include "qlcnic_dcb.h" #define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MINOR 3 @@ -959,6 +960,7 @@ struct qlcnic_ipaddr { #define __QLCNIC_SRIOV_CAPABLE 11 #define __QLCNIC_MBX_POLL_ENABLE 12 #define __QLCNIC_DIAG_MODE 13 +#define __QLCNIC_DCB_STATE 14 #define QLCNIC_INTERRUPT_TEST 1 #define QLCNIC_LOOPBACK_TEST 2 @@ -1062,6 +1064,7 @@ struct qlcnic_adapter { struct delayed_work fw_work; struct delayed_work idc_aen_work; struct delayed_work mbx_poll_work; + struct qlcnic_dcb *dcb; struct qlcnic_filter_hash fhash; struct qlcnic_filter_hash rx_fhash; @@ -2092,4 +2095,51 @@ static inline bool qlcnic_sriov_vf_check(struct qlcnic_adapter *adapter) return status; } + +static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_adapter *adapter) +{ + struct qlcnic_dcb *dcb = adapter->dcb; + + if (dcb && dcb->ops->get_hw_capability) + return dcb->ops->get_hw_capability(adapter); + + return 0; +} + +static inline void qlcnic_dcb_free(struct qlcnic_adapter *adapter) +{ + struct qlcnic_dcb *dcb = adapter->dcb; + + if (dcb && dcb->ops->free) + dcb->ops->free(adapter); +} + +static inline int qlcnic_dcb_attach(struct qlcnic_adapter *adapter) +{ + struct qlcnic_dcb *dcb = adapter->dcb; + + if (dcb && dcb->ops->attach) + return dcb->ops->attach(adapter); + + return 0; +} + +static inline int +qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter, char *buf) +{ + struct qlcnic_dcb *dcb = adapter->dcb; + + if (dcb && dcb->ops->query_hw_capability) + return dcb->ops->query_hw_capability(adapter, buf); + + return 0; +} + +static inline void qlcnic_dcb_get_info(struct qlcnic_adapter *adapter) +{ + struct qlcnic_dcb *dcb = adapter->dcb; + + if (dcb && dcb->ops->get_info) + dcb->ops->get_info(adapter); +} #endif /* __QLCNIC_H_ */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 6c059f97ae9f..d6d1b10537ea 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -67,6 +67,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = { {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26}, {QLCNIC_CMD_CONFIG_VPORT, 4, 4}, {QLCNIC_CMD_BC_EVENT_SETUP, 2, 1}, + {QLCNIC_CMD_DCB_QUERY_CAP, 1, 2}, }; const u32 qlcnic_83xx_ext_reg_tbl[] = { diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c index fb0ef36b529b..a969ac27633c 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c @@ -635,6 +635,8 @@ int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter) if (adapter->portnum == 0) qlcnic_set_drv_version(adapter); + + qlcnic_dcb_get_info(adapter); qlcnic_83xx_idc_attach_driver(adapter); return 0; @@ -2228,6 +2230,9 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac) if (err) goto disable_mbx_intr; + if (adapter->dcb && qlcnic_dcb_attach(adapter)) + qlcnic_clear_dcb_ops(adapter); + /* Periodically monitor device status */ qlcnic_83xx_idc_poll_dev_state(&adapter->fw_work.work); return 0; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index d4f0e9591644..4af37847213b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -39,6 +39,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_mbx_tbl[] = { {QLCNIC_CMD_82XX_SET_DRV_VER, 4, 1}, {QLCNIC_CMD_GET_LED_STATUS, 4, 2}, {QLCNIC_CMD_MQ_TX_CONFIG_INTR, 2, 3}, + {QLCNIC_CMD_DCB_QUERY_CAP, 1, 2}, }; static inline u32 qlcnic_get_cmd_signature(struct qlcnic_hardware_context *ahw) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c new file mode 100644 index 000000000000..121e492f2f87 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c @@ -0,0 +1,213 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + +#include "qlcnic.h" + +#define QLC_DCB_MAX_TC 0x8 + +#define QLC_DCB_TSA_SUPPORT(V) (V & 0x1) +#define QLC_DCB_ETS_SUPPORT(V) ((V >> 1) & 0x1) +#define QLC_DCB_VERSION_SUPPORT(V) ((V >> 2) & 0xf) +#define QLC_DCB_MAX_NUM_TC(V) ((V >> 20) & 0xf) +#define QLC_DCB_MAX_NUM_ETS_TC(V) ((V >> 24) & 0xf) +#define QLC_DCB_MAX_NUM_PFC_TC(V) ((V >> 28) & 0xf) + +static void __qlcnic_dcb_free(struct qlcnic_adapter *); +static int __qlcnic_dcb_attach(struct qlcnic_adapter *); +static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *, char *); +static void __qlcnic_dcb_get_info(struct qlcnic_adapter *); + +static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *); + +static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *); + +struct qlcnic_dcb_capability { + bool tsa_capability; + bool ets_capability; + u8 max_num_tc; + u8 max_ets_tc; + u8 max_pfc_tc; + u8 dcb_capability; +}; + +struct qlcnic_dcb_cfg { + struct qlcnic_dcb_capability capability; + u32 version; +}; + +static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = { + .free = __qlcnic_dcb_free, + .attach = __qlcnic_dcb_attach, + .query_hw_capability = __qlcnic_dcb_query_hw_capability, + .get_info = __qlcnic_dcb_get_info, + + .get_hw_capability = qlcnic_83xx_dcb_get_hw_capability, +}; + +static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = { + .free = __qlcnic_dcb_free, + .attach = __qlcnic_dcb_attach, + .query_hw_capability = __qlcnic_dcb_query_hw_capability, + .get_info = __qlcnic_dcb_get_info, + + .get_hw_capability = qlcnic_82xx_dcb_get_hw_capability, +}; + +void qlcnic_set_dcb_ops(struct qlcnic_adapter *adapter) +{ + if (qlcnic_82xx_check(adapter)) + adapter->dcb->ops = &qlcnic_82xx_dcb_ops; + else if (qlcnic_83xx_check(adapter)) + adapter->dcb->ops = &qlcnic_83xx_dcb_ops; +} + +int __qlcnic_register_dcb(struct qlcnic_adapter *adapter) +{ + struct qlcnic_dcb *dcb; + + dcb = kzalloc(sizeof(struct qlcnic_dcb), GFP_ATOMIC); + if (!dcb) + return -ENOMEM; + + adapter->dcb = dcb; + qlcnic_set_dcb_ops(adapter); + + return 0; +} + +static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter) +{ + struct qlcnic_dcb *dcb = adapter->dcb; + + if (!dcb) + return; + + kfree(dcb->cfg); + dcb->cfg = NULL; + kfree(dcb); + adapter->dcb = NULL; +} + +static void __qlcnic_dcb_get_info(struct qlcnic_adapter *adapter) +{ + qlcnic_dcb_get_hw_capability(adapter); +} + +static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter) +{ + struct qlcnic_dcb *dcb = adapter->dcb; + + dcb->cfg = kzalloc(sizeof(struct qlcnic_dcb_cfg), GFP_ATOMIC); + if (!dcb->cfg) + return -ENOMEM; + + qlcnic_dcb_get_info(adapter); + + return 0; +} + +static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter, + char *buf) +{ + struct qlcnic_cmd_args cmd; + u32 mbx_out; + int err; + + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DCB_QUERY_CAP); + if (err) + return err; + + err = qlcnic_issue_cmd(adapter, &cmd); + if (err) { + dev_err(&adapter->pdev->dev, + "Failed to query DCBX capability, err %d\n", err); + } else { + mbx_out = cmd.rsp.arg[1]; + if (buf) + memcpy(buf, &mbx_out, sizeof(u32)); + } + + qlcnic_free_mbx_args(&cmd); + + return err; +} + +static int __qlcnic_dcb_get_capability(struct qlcnic_adapter *adapter, u32 *val) +{ + struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability; + u32 mbx_out; + int err; + + memset(cap, 0, sizeof(struct qlcnic_dcb_capability)); + + err = qlcnic_dcb_query_hw_capability(adapter, (char *)val); + if (err) + return err; + + mbx_out = *val; + if (QLC_DCB_TSA_SUPPORT(mbx_out)) + cap->tsa_capability = true; + + if (QLC_DCB_ETS_SUPPORT(mbx_out)) + cap->ets_capability = true; + + cap->max_num_tc = QLC_DCB_MAX_NUM_TC(mbx_out); + cap->max_ets_tc = QLC_DCB_MAX_NUM_ETS_TC(mbx_out); + cap->max_pfc_tc = QLC_DCB_MAX_NUM_PFC_TC(mbx_out); + + if (cap->max_num_tc > QLC_DCB_MAX_TC || + cap->max_ets_tc > cap->max_num_tc || + cap->max_pfc_tc > cap->max_num_tc) { + dev_err(&adapter->pdev->dev, "Invalid DCB configuration\n"); + return -EINVAL; + } + + return err; +} + +static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter) +{ + struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg; + struct qlcnic_dcb_capability *cap; + u32 mbx_out; + int err; + + err = __qlcnic_dcb_get_capability(adapter, &mbx_out); + if (err) + return err; + + cap = &cfg->capability; + cap->dcb_capability = DCB_CAP_DCBX_VER_CEE | DCB_CAP_DCBX_LLD_MANAGED; + + if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability) + set_bit(__QLCNIC_DCB_STATE, &adapter->state); + + return err; +} + +static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter) +{ + struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability; + u32 mbx_out; + int err; + + err = __qlcnic_dcb_get_capability(adapter, &mbx_out); + if (err) + return err; + + if (mbx_out & BIT_2) + cap->dcb_capability = DCB_CAP_DCBX_VER_CEE; + if (mbx_out & BIT_3) + cap->dcb_capability |= DCB_CAP_DCBX_VER_IEEE; + if (cap->dcb_capability) + cap->dcb_capability |= DCB_CAP_DCBX_LLD_MANAGED; + + if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability) + set_bit(__QLCNIC_DCB_STATE, &adapter->state); + + return err; +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h new file mode 100644 index 000000000000..45dc1fab4608 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h @@ -0,0 +1,32 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + +#ifndef __QLCNIC_DCBX_H +#define __QLCNIC_DCBX_H + +void qlcnic_clear_dcb_ops(struct qlcnic_adapter *); + +#ifdef CONFIG_QLCNIC_DCB +int __qlcnic_register_dcb(struct qlcnic_adapter *); +#else +static inline int __qlcnic_register_dcb(struct qlcnic_adapter *adapter) +{ return 0; } +#endif + +struct qlcnic_dcb_ops { + void (*free) (struct qlcnic_adapter *); + int (*attach) (struct qlcnic_adapter *); + int (*query_hw_capability) (struct qlcnic_adapter *, char *); + int (*get_hw_capability) (struct qlcnic_adapter *); + void (*get_info) (struct qlcnic_adapter *); +}; + +struct qlcnic_dcb { + struct qlcnic_dcb_ops *ops; + struct qlcnic_dcb_cfg *cfg; +}; +#endif diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h index 786366c64b06..243018b69809 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h @@ -85,6 +85,7 @@ enum qlcnic_regs { #define QLCNIC_CMD_GET_TEMP_HDR 0x30 #define QLCNIC_CMD_BC_EVENT_SETUP 0x31 #define QLCNIC_CMD_CONFIG_VPORT 0x32 +#define QLCNIC_CMD_DCB_QUERY_CAP 0x34 #define QLCNIC_CMD_GET_MAC_STATS 0x37 #define QLCNIC_CMD_82XX_SET_DRV_VER 0x38 #define QLCNIC_CMD_MQ_TX_CONFIG_INTR 0x39 diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 8321d1a3f4b9..343c6a04a64b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -2121,6 +2121,17 @@ void qlcnic_set_drv_version(struct qlcnic_adapter *adapter) qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd); } +static int qlcnic_register_dcb(struct qlcnic_adapter *adapter) +{ + return __qlcnic_register_dcb(adapter); +} + +void qlcnic_clear_dcb_ops(struct qlcnic_adapter *adapter) +{ + kfree(adapter->dcb); + adapter->dcb = NULL; +} + static int qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -2217,6 +2228,8 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_LIST_HEAD(&adapter->mac_list); + qlcnic_register_dcb(adapter); + if (qlcnic_82xx_check(adapter)) { qlcnic_check_vf(adapter, ent); adapter->portnum = adapter->ahw->pci_func; @@ -2245,6 +2258,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_free_hw; adapter->flags |= QLCNIC_NEED_FLR; + + if (adapter->dcb && qlcnic_dcb_attach(adapter)) + qlcnic_clear_dcb_ops(adapter); + } else if (qlcnic_83xx_check(adapter)) { adapter->max_drv_tx_rings = 1; qlcnic_83xx_check_vf(adapter, ent); @@ -2369,6 +2386,8 @@ static void qlcnic_remove(struct pci_dev *pdev) qlcnic_cancel_idc_work(adapter); ahw = adapter->ahw; + qlcnic_dcb_free(adapter); + unregister_netdev(netdev); qlcnic_sriov_cleanup(adapter); @@ -2411,6 +2430,7 @@ static void qlcnic_remove(struct pci_dev *pdev) destroy_workqueue(adapter->qlcnic_wq); adapter->qlcnic_wq = NULL; } + qlcnic_free_adapter_resources(adapter); kfree(ahw); free_netdev(netdev); @@ -3228,6 +3248,8 @@ qlcnic_attach_work(struct work_struct *work) return; } attach: + qlcnic_dcb_get_info(adapter); + if (netif_running(netdev)) { if (qlcnic_up(adapter, netdev)) goto done; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 2f79ec5246dc..26f9aa66403d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -538,6 +538,9 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, if (err) goto err_out_send_channel_term; + if (adapter->dcb && qlcnic_dcb_attach(adapter)) + qlcnic_clear_dcb_ops(adapter); + err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac); if (err) goto err_out_send_channel_term; @@ -545,6 +548,7 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, pci_set_drvdata(adapter->pdev, adapter); dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n", adapter->netdev->name); + qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state, adapter->ahw->idc.delay); return 0; @@ -1577,6 +1581,8 @@ static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter) if (err) goto err_out_term_channel; + qlcnic_dcb_get_info(adapter); + return 0; err_out_term_channel: diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index eb49cd65378c..b154048383e9 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c @@ -1284,6 +1284,7 @@ static const int qlcnic_pf_passthru_supp_cmds[] = { QLCNIC_CMD_GET_STATISTICS, QLCNIC_CMD_GET_PORT_CONFIG, QLCNIC_CMD_GET_LINK_STATUS, + QLCNIC_CMD_DCB_QUERY_CAP, }; static const struct qlcnic_sriov_cmd_handler qlcnic_pf_bc_cmd_hdlr[] = { -- 2.34.1