#define FW_CAPS_TO_LMM(__fw_name, __lmm_name) \ do { \ if (fw_caps & FW_PORT_CAP32_ ## __fw_name) \
SET_LMM(__lmm_name); \
} while (0)
switch (port_type) { case FW_PORT_TYPE_BT_SGMII: case FW_PORT_TYPE_BT_XFI: case FW_PORT_TYPE_BT_XAUI:
SET_LMM(TP);
FW_CAPS_TO_LMM(SPEED_100M, 100baseT_Full);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full); break;
case FW_PORT_TYPE_KX4: case FW_PORT_TYPE_KX:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKX4_Full); break;
case FW_PORT_TYPE_KR:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); break;
case FW_PORT_TYPE_BP_AP:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseR_FEC);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full); break;
case FW_PORT_TYPE_FIBER_XFI: case FW_PORT_TYPE_FIBER_XAUI: case FW_PORT_TYPE_SFP: case FW_PORT_TYPE_QSFP_10G: case FW_PORT_TYPE_QSA:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full); break;
case FW_PORT_TYPE_BP40_BA: case FW_PORT_TYPE_QSFP:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full);
FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full); break;
case FW_PORT_TYPE_CR_QSFP: case FW_PORT_TYPE_SFP28:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseT_Full);
FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full); break;
case FW_PORT_TYPE_KR_SFP28:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
FW_CAPS_TO_LMM(SPEED_25G, 25000baseKR_Full); break;
case FW_PORT_TYPE_KR_XLAUI:
SET_LMM(Backplane);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseKX_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
FW_CAPS_TO_LMM(SPEED_40G, 40000baseKR4_Full); break;
case FW_PORT_TYPE_CR2_QSFP:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_50G, 50000baseSR2_Full); break;
case FW_PORT_TYPE_KR4_100G: case FW_PORT_TYPE_CR4_QSFP:
SET_LMM(FIBRE);
FW_CAPS_TO_LMM(SPEED_1G, 1000baseT_Full);
FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full);
FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full);
FW_CAPS_TO_LMM(SPEED_50G, 50000baseCR2_Full);
FW_CAPS_TO_LMM(SPEED_100G, 100000baseCR4_Full); break;
/* For the nonce, the Firmware doesn't send up Port State changes *whentheVirtualInterfaceattachedtothePortisdown.So *ifit'sdown,let'sgrabanychanges.
*/ if (!netif_running(dev))
(void)t4_update_port_info(pi);
/* If the firmware rejects the Link Configuration request, back out *thechangesandreporttheerror.
*/
ret = t4_link_l1cfg(pi->adapter, pi->adapter->mbox, pi->tx_chan, lc); if (ret)
*lc = old_lc;
return ret;
}
/* Translate the Firmware FEC value into the ethtool value. */ staticinlineunsignedint fwcap_to_eth_fec(unsignedint fw_fec)
{ unsignedint eth_fec = 0;
if (fw_fec & FW_PORT_CAP32_FEC_RS)
eth_fec |= ETHTOOL_FEC_RS; if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS)
eth_fec |= ETHTOOL_FEC_BASER;
/* if nothing is set, then FEC is off */ if (!eth_fec)
eth_fec = ETHTOOL_FEC_OFF;
return eth_fec;
}
/* Translate Common Code FEC value into ethtool value. */ staticinlineunsignedint cc_to_eth_fec(unsignedint cc_fec)
{ unsignedint eth_fec = 0;
if (cc_fec & FEC_AUTO)
eth_fec |= ETHTOOL_FEC_AUTO; if (cc_fec & FEC_RS)
eth_fec |= ETHTOOL_FEC_RS; if (cc_fec & FEC_BASER_RS)
eth_fec |= ETHTOOL_FEC_BASER;
/* if nothing is set, then FEC is off */ if (!eth_fec)
eth_fec = ETHTOOL_FEC_OFF;
return eth_fec;
}
/* Translate ethtool FEC value into Common Code value. */ staticinlineunsignedint eth_to_cc_fec(unsignedint eth_fec)
{ unsignedint cc_fec = 0;
if (eth_fec & ETHTOOL_FEC_OFF) return cc_fec;
if (eth_fec & ETHTOOL_FEC_AUTO)
cc_fec |= FEC_AUTO; if (eth_fec & ETHTOOL_FEC_RS)
cc_fec |= FEC_RS; if (eth_fec & ETHTOOL_FEC_BASER)
cc_fec |= FEC_BASER_RS;
/* Translate the Firmware FEC Support into the ethtool value. We *alwayssupportIEEE802.3"automatic"selectionofLinkFECtypeif *anyFECissupported.
*/
fec->fec = fwcap_to_eth_fec(lc->pcaps); if (fec->fec != ETHTOOL_FEC_OFF)
fec->fec |= ETHTOOL_FEC_AUTO;
/* Translate the current internal FEC parameters into the *ethtoolvalues.
*/
fec->active_fec = cc_to_eth_fec(lc->fec);
/* Save old Link Configuration in case the L1 Configure below *fails.
*/
old_lc = *lc;
/* Try to perform the L1 Configure and return the result of that *effort.Ifitfails,reverttheattemptedchange.
*/
lc->requested_fec = eth_to_cc_fec(fec->fec);
ret = t4_link_l1cfg(pi->adapter, pi->adapter->mbox,
pi->tx_chan, lc); if (ret)
*lc = old_lc; return ret;
}
/* Return the current global Adapter SGE Doorbell Queue Timer Tick for all *EthernetTXQueues.
*/ staticint get_dbqtimer_tick(struct net_device *dev)
{ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter;
if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) return0;
return adap->sge.dbqtimer_tick;
}
/* Return the SGE Doorbell Queue Timer Value for the Ethernet TX Queues *associatedwithaNetworkDevice.
*/ staticint get_dbqtimer(struct net_device *dev)
{ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; struct sge_eth_txq *txq;
txq = &adap->sge.ethtxq[pi->first_qset];
if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) return0;
/* all of the TX Queues use the same Timer Index */ return adap->sge.dbqtimer_val[txq->dbqtimerix];
}
/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX *Queues.Thisisthefundamental"Tick"thatsetsthescaleofvalueswhich *canbeused.IndividualEthernetTXQueuesindexintoarelativelysmall *arrayofTickMultipliers.ChangingthebaseTickwillthuschangeallof *theresultingTimerValuesassociatedwiththosemultipliersforall *EthernetTXQueues.
*/ staticint set_dbqtimer_tick(struct net_device *dev, int usecs)
{ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; struct sge *s = &adap->sge;
u32 param, val; int ret;
if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) return0;
/* return early if it's the same Timer Tick we're already using */ if (s->dbqtimer_tick == usecs) return0;
/* attempt to set the new Timer Tick value */
param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DBQ_TIMERTICK));
val = usecs;
ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1, ¶m, &val); if (ret) return ret;
s->dbqtimer_tick = usecs;
/* if successful, reread resulting dependent Timer values */
ret = t4_read_sge_dbqtimers(adap, ARRAY_SIZE(s->dbqtimer_val),
s->dbqtimer_val); return ret;
}
/* Set the SGE Doorbell Queue Timer Value for the Ethernet TX Queues *associatedwithaNetworkDevice.Thereisarelativelysmallarrayof *possibleTimerValuessoweneedtopicktheclosestvalueavailable.
*/ staticint set_dbqtimer(struct net_device *dev, int usecs)
{ int qix, timerix, min_timerix, delta, min_delta; struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; struct sge *s = &adap->sge; struct sge_eth_txq *txq;
u32 param, val; int ret;
if (!(adap->flags & CXGB4_SGE_DBQ_TIMER)) return0;
/* Find the SGE Doorbell Timer Value that's closest to the requested *value.
*/
min_delta = INT_MAX;
min_timerix = 0; for (timerix = 0; timerix < ARRAY_SIZE(s->dbqtimer_val); timerix++) {
delta = s->dbqtimer_val[timerix] - usecs; if (delta < 0)
delta = -delta; if (delta < min_delta) {
min_delta = delta;
min_timerix = timerix;
}
}
/* Return early if it's the same Timer Index we're already using. *WeusethesameTimerIndexforalloftheTXQueuesforan *interfacesoit'sonlynecessarytocheckthefirstone.
*/
txq = &s->ethtxq[pi->first_qset]; if (txq->dbqtimerix == min_timerix) return0;
for (qix = 0; qix < pi->nqsets; qix++, txq++) { if (adap->flags & CXGB4_FULL_INIT_DONE) {
param =
(FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DMAQ_EQ_TIMERIX) |
FW_PARAMS_PARAM_YZ_V(txq->q.cntxt_id));
val = min_timerix;
ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1, ¶m, &val); if (ret) return ret;
}
txq->dbqtimerix = min_timerix;
} return0;
}
/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX *QueuesandtheTimerValuefortheEthernetTXQueuesassociatedwitha *NetworkDevice.SincechangingtheglobalTickchangesallofthe *availableTimerValues,weneedtodothisfirstbeforeselectingthe *resultingclosestTimerValue.Moreover,sincetheTickisglobal, *changingitaffectstheTimerValuesforallNetworkDevicesonthe *adapter.So,beforechangingtheTick,wegraballofthecurrentTimer *ValuesforotherNetworkDevicesonthisAdapterandthenattempttoselect *newTimerValueswhichareclosetotheoldvalues...
*/ staticint set_dbqtimer_tickval(struct net_device *dev, int tick_usecs, int timer_usecs)
{ struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; int timer[MAX_NPORTS]; unsignedint port; int ret;
/* Grab the other adapter Network Interface current timers and fill in *thenewoneforthisNetworkInterface.
*/
for_each_port(adap, port) if (port == pi->port_id)
timer[port] = timer_usecs; else
timer[port] = get_dbqtimer(adap->port[port]);
/* Change the global Tick first ... */
ret = set_dbqtimer_tick(dev, tick_usecs); if (ret) return ret;
/* ... and then set all of the Network Interface Timer Values ... */
for_each_port(adap, port) {
ret = set_dbqtimer(adap->port[port], timer[port]); if (ret) return ret;
}
ret = cxgb4_validate_phy_image(data, NULL); if (ret) {
dev_err(adap->pdev_dev, "PHY signature mismatch\n"); return ret;
}
/* We have to RESET the chip/firmware because we need the *chipinuninitializedstateforloadingnewPHYimage. *Otherwise,therunningfirmwarewillonlystorethePHY *imageinlocalRAMwhichwillbelostafternextreset.
*/
ret = t4_fw_reset(adap, adap->mbox, PIORSTMODE_F | PIORST_F); if (ret < 0) {
dev_err(adap->pdev_dev, "Set FW to RESET for flashing PHY FW failed. ret: %d\n",
ret); return ret;
}
ret = t4_load_phy_fw(adap, MEMWIN_NIC, NULL, data, size); if (ret < 0) {
dev_err(adap->pdev_dev, "Failed to load PHY FW. ret: %d\n",
ret); return ret;
}
/* If the adapter has been fully initialized then we'll go ahead and *trytogetthefirmware'scooperationinupgradingtothenew *firmwareimageotherwisewe'lltrytodotheentirejobfromthe *host...andwealways"force"theoperationinthispath.
*/ if (adap->flags & CXGB4_FULL_INIT_DONE)
mbox = adap->mbox;
ret = t4_fw_upgrade(adap, mbox, data, size, 1); if (ret)
dev_err(adap->pdev_dev, "Failed to flash firmware\n");
switch (region) { case CXGB4_ETHTOOL_FLASH_FW:
ret = cxgb4_ethtool_flash_fw(netdev, data, size); break; case CXGB4_ETHTOOL_FLASH_PHY:
ret = cxgb4_ethtool_flash_phy(netdev, data, size); break; case CXGB4_ETHTOOL_FLASH_BOOT:
ret = cxgb4_ethtool_flash_boot(netdev, data, size); break; case CXGB4_ETHTOOL_FLASH_BOOTCFG:
ret = cxgb4_ethtool_flash_bootcfg(netdev, data, size); break; default:
ret = -EOPNOTSUPP; break;
}
pcie_fw = t4_read_reg(adap, PCIE_FW_A);
master = PCIE_FW_MASTER_G(pcie_fw); if (pcie_fw & PCIE_FW_MASTER_VLD_F)
master_vld = 1; /* if csiostor is the master return */ if (master_vld && (master != adap->pf)) {
dev_warn(adap->pdev_dev, "cxgb4 driver needs to be loaded as MASTER to support FW flash\n"); return -EOPNOTSUPP;
}
ef->data[sizeof(ef->data) - 1] = '\0';
ret = request_firmware(&fw, ef->data, adap->pdev_dev); if (ret < 0) return ret;
fw_data = fw->data;
fw_size = fw->size; if (ef->region == ETHTOOL_FLASH_ALL_REGIONS) { while (fw_size > 0) {
size = 0;
region = cxgb4_ethtool_get_flash_region(fw_data, &size); if (region < 0 || !size) {
ret = region; goto out_free_fw;
}
ret = cxgb4_ethtool_flash_region(netdev, fw_data, size,
region); if (ret) goto out_free_fw;
/* We require at least one supported parameter to be changed and no *changeinanyoftheunsupportedparameters
*/ if (rxfh->key ||
(rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE &&
rxfh->hfunc != ETH_RSS_HASH_TOP)) return -EOPNOTSUPP; if (!rxfh->indir) return0;
/* Interface must be brought up atleast once */ if (pi->adapter->flags & CXGB4_FULL_INIT_DONE) { for (i = 0; i < pi->rss_size; i++)
pi->rss[i] = rxfh->indir[i];
if (!(adapter->flags & CXGB4_FULL_INIT_DONE)) return -EAGAIN; /* can still change nfilters */
if (!adapter->ethtool_filters) return -EOPNOTSUPP;
if (cmd->fs.location >= adapter->ethtool_filters->nentries) {
dev_err(adapter->pdev_dev, "Location must be < %u",
adapter->ethtool_filters->nentries); return -ERANGE;
}
if (!(adapter->flags & CXGB4_FULL_INIT_DONE)) return -EAGAIN; /* can still change nfilters */
if (!adapter->ethtool_filters) return -EOPNOTSUPP;
if (cmd->fs.location >= adapter->ethtool_filters->nentries) {
dev_err(adapter->pdev_dev, "Location must be < %u",
adapter->ethtool_filters->nentries); return -ERANGE;
}
if (test_bit(cmd->fs.location,
adapter->ethtool_filters->port[pi->port_id].bmap)) return -EEXIST;
memset(&fs, 0, sizeof(fs));
input.fs = &cmd->fs;
flow = ethtool_rx_flow_rule_create(&input); if (IS_ERR(flow)) {
ret = PTR_ERR(flow); gotoexit;
}
fs.hitcnts = 1;
ret = cxgb4_flow_rule_replace(netdev, flow->rule, cmd->fs.location,
NULL, &fs, &tid); if (ret) goto free;
staticint set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
{ int ret = -EOPNOTSUPP;
switch (cmd->cmd) { case ETHTOOL_SRXCLSRLINS:
ret = cxgb4_ntuple_set_filter(dev, cmd); break; case ETHTOOL_SRXCLSRLDEL:
ret = cxgb4_ntuple_del_filter(dev, cmd); break; default: break;
}
staticbool cxgb4_fw_mod_type_info_available(unsignedint fw_mod_type)
{ /* Read port module EEPROM as long as it is plugged-in and *safetoread.
*/ return (fw_mod_type != FW_PORT_MOD_TYPE_NONE &&
fw_mod_type != FW_PORT_MOD_TYPE_ERROR);
}
if (!cxgb4_fw_mod_type_info_available(pi->mod_type)) return -EINVAL;
switch (pi->port_type) { case FW_PORT_TYPE_SFP: case FW_PORT_TYPE_QSA: case FW_PORT_TYPE_SFP28:
ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan,
I2C_DEV_ADDR_A0, SFF_8472_COMP_ADDR,
SFF_8472_COMP_LEN, &sff8472_comp); if (ret) return ret;
ret = t4_i2c_rd(adapter, adapter->mbox, pi->tx_chan,
I2C_DEV_ADDR_A0, SFP_DIAG_TYPE_ADDR,
SFP_DIAG_TYPE_LEN, &sff_diag_type); if (ret) return ret;
if (eth_filter_info) { for (i = 0; i < adap->params.nports; i++) {
kvfree(eth_filter_info[i].loc_array);
bitmap_free(eth_filter_info[i].bmap);
}
kfree(eth_filter_info);
}
for (i = 0; i < adap->params.nports; i++) {
eth_filter->port[i].loc_array = kvzalloc(nentries, GFP_KERNEL); if (!eth_filter->port[i].loc_array) {
ret = -ENOMEM; goto free_eth_finfo;
}
eth_filter->port[i].bmap = bitmap_zalloc(nentries, GFP_KERNEL); if (!eth_filter->port[i].bmap) {
ret = -ENOMEM;
kvfree(eth_filter->port[i].loc_array); goto free_eth_finfo;
}
}
adap->ethtool_filters = eth_filter; return0;
free_eth_finfo: while (i-- > 0) {
bitmap_free(eth_filter->port[i].bmap);
kvfree(eth_filter->port[i].loc_array);
}
kfree(eth_filter_info);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.