diff options
author | Sucheta Chakraborty <sucheta.chakraborty@qlogic.com> | 2013-08-23 21:38:25 +0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-08-27 23:21:13 +0400 |
commit | 14d385b9905920cc0136721316c185c45ee6e26c (patch) | |
tree | fabf005ce5e16992386cba6f864472210c7c3537 /drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | |
parent | d853f1116630ecc9172278762cc75f441182a056 (diff) | |
download | linux-14d385b9905920cc0136721316c185c45ee6e26c.tar.xz |
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 <sucheta.chakraborty@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c')
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 213 |
1 files changed, 213 insertions, 0 deletions
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; +} |