1 // SPDX-License-Identifier: GPL-2.0 2 /******************************************************************************* 3 * 4 * Intel Ethernet Controller XL710 Family Linux Driver 5 * Copyright(c) 2013 - 2014 Intel Corporation. 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms and conditions of the GNU General Public License, 9 * version 2, as published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along 17 * with this program. If not, see <http://www.gnu.org/licenses/>. 18 * 19 * The full GNU General Public License is included in this distribution in 20 * the file called "COPYING". 21 * 22 * Contact Information: 23 * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> 24 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 25 * 26 ******************************************************************************/ 27 28 #ifdef CONFIG_I40E_DCB 29 #include "i40e.h" 30 #include <net/dcbnl.h> 31 32 /** 33 * i40e_get_pfc_delay - retrieve PFC Link Delay 34 * @hw: pointer to hardware struct 35 * @delay: holds the PFC Link delay value 36 * 37 * Returns PFC Link Delay from the PRTDCB_GENC.PFCLDA 38 **/ 39 static void i40e_get_pfc_delay(struct i40e_hw *hw, u16 *delay) 40 { 41 u32 val; 42 43 val = rd32(hw, I40E_PRTDCB_GENC); 44 *delay = (u16)((val & I40E_PRTDCB_GENC_PFCLDA_MASK) >> 45 I40E_PRTDCB_GENC_PFCLDA_SHIFT); 46 } 47 48 /** 49 * i40e_dcbnl_ieee_getets - retrieve local IEEE ETS configuration 50 * @netdev: the corresponding netdev 51 * @ets: structure to hold the ETS information 52 * 53 * Returns local IEEE ETS configuration 54 **/ 55 static int i40e_dcbnl_ieee_getets(struct net_device *dev, 56 struct ieee_ets *ets) 57 { 58 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 59 struct i40e_dcbx_config *dcbxcfg; 60 struct i40e_hw *hw = &pf->hw; 61 62 if (!(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) 63 return -EINVAL; 64 65 dcbxcfg = &hw->local_dcbx_config; 66 ets->willing = dcbxcfg->etscfg.willing; 67 ets->ets_cap = dcbxcfg->etscfg.maxtcs; 68 ets->cbs = dcbxcfg->etscfg.cbs; 69 memcpy(ets->tc_tx_bw, dcbxcfg->etscfg.tcbwtable, 70 sizeof(ets->tc_tx_bw)); 71 memcpy(ets->tc_rx_bw, dcbxcfg->etscfg.tcbwtable, 72 sizeof(ets->tc_rx_bw)); 73 memcpy(ets->tc_tsa, dcbxcfg->etscfg.tsatable, 74 sizeof(ets->tc_tsa)); 75 memcpy(ets->prio_tc, dcbxcfg->etscfg.prioritytable, 76 sizeof(ets->prio_tc)); 77 memcpy(ets->tc_reco_bw, dcbxcfg->etsrec.tcbwtable, 78 sizeof(ets->tc_reco_bw)); 79 memcpy(ets->tc_reco_tsa, dcbxcfg->etsrec.tsatable, 80 sizeof(ets->tc_reco_tsa)); 81 memcpy(ets->reco_prio_tc, dcbxcfg->etscfg.prioritytable, 82 sizeof(ets->reco_prio_tc)); 83 84 return 0; 85 } 86 87 /** 88 * i40e_dcbnl_ieee_getpfc - retrieve local IEEE PFC configuration 89 * @netdev: the corresponding netdev 90 * @ets: structure to hold the PFC information 91 * 92 * Returns local IEEE PFC configuration 93 **/ 94 static int i40e_dcbnl_ieee_getpfc(struct net_device *dev, 95 struct ieee_pfc *pfc) 96 { 97 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 98 struct i40e_dcbx_config *dcbxcfg; 99 struct i40e_hw *hw = &pf->hw; 100 int i; 101 102 if (!(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) 103 return -EINVAL; 104 105 dcbxcfg = &hw->local_dcbx_config; 106 pfc->pfc_cap = dcbxcfg->pfc.pfccap; 107 pfc->pfc_en = dcbxcfg->pfc.pfcenable; 108 pfc->mbc = dcbxcfg->pfc.mbc; 109 i40e_get_pfc_delay(hw, &pfc->delay); 110 111 /* Get Requests/Indicatiosn */ 112 for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { 113 pfc->requests[i] = pf->stats.priority_xoff_tx[i]; 114 pfc->indications[i] = pf->stats.priority_xoff_rx[i]; 115 } 116 117 return 0; 118 } 119 120 /** 121 * i40e_dcbnl_getdcbx - retrieve current DCBx capability 122 * @netdev: the corresponding netdev 123 * 124 * Returns DCBx capability features 125 **/ 126 static u8 i40e_dcbnl_getdcbx(struct net_device *dev) 127 { 128 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 129 130 return pf->dcbx_cap; 131 } 132 133 /** 134 * i40e_dcbnl_get_perm_hw_addr - MAC address used by DCBx 135 * @netdev: the corresponding netdev 136 * 137 * Returns the SAN MAC address used for LLDP exchange 138 **/ 139 static void i40e_dcbnl_get_perm_hw_addr(struct net_device *dev, 140 u8 *perm_addr) 141 { 142 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 143 int i, j; 144 145 memset(perm_addr, 0xff, MAX_ADDR_LEN); 146 147 for (i = 0; i < dev->addr_len; i++) 148 perm_addr[i] = pf->hw.mac.perm_addr[i]; 149 150 for (j = 0; j < dev->addr_len; j++, i++) 151 perm_addr[i] = pf->hw.mac.san_addr[j]; 152 } 153 154 static const struct dcbnl_rtnl_ops dcbnl_ops = { 155 .ieee_getets = i40e_dcbnl_ieee_getets, 156 .ieee_getpfc = i40e_dcbnl_ieee_getpfc, 157 .getdcbx = i40e_dcbnl_getdcbx, 158 .getpermhwaddr = i40e_dcbnl_get_perm_hw_addr, 159 }; 160 161 /** 162 * i40e_dcbnl_set_all - set all the apps and ieee data from DCBx config 163 * @vsi: the corresponding vsi 164 * 165 * Set up all the IEEE APPs in the DCBNL App Table and generate event for 166 * other settings 167 **/ 168 void i40e_dcbnl_set_all(struct i40e_vsi *vsi) 169 { 170 struct net_device *dev = vsi->netdev; 171 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 172 struct i40e_dcbx_config *dcbxcfg; 173 struct i40e_hw *hw = &pf->hw; 174 struct dcb_app sapp; 175 u8 prio, tc_map; 176 int i; 177 178 /* DCB not enabled */ 179 if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) 180 return; 181 182 /* MFP mode but not an iSCSI PF so return */ 183 if ((pf->flags & I40E_FLAG_MFP_ENABLED) && !(pf->hw.func_caps.iscsi)) 184 return; 185 186 dcbxcfg = &hw->local_dcbx_config; 187 188 /* Set up all the App TLVs if DCBx is negotiated */ 189 for (i = 0; i < dcbxcfg->numapps; i++) { 190 prio = dcbxcfg->app[i].priority; 191 tc_map = BIT(dcbxcfg->etscfg.prioritytable[prio]); 192 193 /* Add APP only if the TC is enabled for this VSI */ 194 if (tc_map & vsi->tc_config.enabled_tc) { 195 sapp.selector = dcbxcfg->app[i].selector; 196 sapp.protocol = dcbxcfg->app[i].protocolid; 197 sapp.priority = prio; 198 dcb_ieee_setapp(dev, &sapp); 199 } 200 } 201 202 /* Notify user-space of the changes */ 203 dcbnl_ieee_notify(dev, RTM_SETDCB, DCB_CMD_IEEE_SET, 0, 0); 204 } 205 206 /** 207 * i40e_dcbnl_vsi_del_app - Delete APP for given VSI 208 * @vsi: the corresponding vsi 209 * @app: APP to delete 210 * 211 * Delete given APP from the DCBNL APP table for given 212 * VSI 213 **/ 214 static int i40e_dcbnl_vsi_del_app(struct i40e_vsi *vsi, 215 struct i40e_dcb_app_priority_table *app) 216 { 217 struct net_device *dev = vsi->netdev; 218 struct dcb_app sapp; 219 220 if (!dev) 221 return -EINVAL; 222 223 sapp.selector = app->selector; 224 sapp.protocol = app->protocolid; 225 sapp.priority = app->priority; 226 return dcb_ieee_delapp(dev, &sapp); 227 } 228 229 /** 230 * i40e_dcbnl_del_app - Delete APP on all VSIs 231 * @pf: the corresponding PF 232 * @app: APP to delete 233 * 234 * Delete given APP from all the VSIs for given PF 235 **/ 236 static void i40e_dcbnl_del_app(struct i40e_pf *pf, 237 struct i40e_dcb_app_priority_table *app) 238 { 239 int v, err; 240 241 for (v = 0; v < pf->num_alloc_vsi; v++) { 242 if (pf->vsi[v] && pf->vsi[v]->netdev) { 243 err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); 244 dev_dbg(&pf->pdev->dev, "Deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", 245 pf->vsi[v]->seid, err, app->selector, 246 app->protocolid, app->priority); 247 } 248 } 249 } 250 251 /** 252 * i40e_dcbnl_find_app - Search APP in given DCB config 253 * @cfg: DCBX configuration data 254 * @app: APP to search for 255 * 256 * Find given APP in the DCB configuration 257 **/ 258 static bool i40e_dcbnl_find_app(struct i40e_dcbx_config *cfg, 259 struct i40e_dcb_app_priority_table *app) 260 { 261 int i; 262 263 for (i = 0; i < cfg->numapps; i++) { 264 if (app->selector == cfg->app[i].selector && 265 app->protocolid == cfg->app[i].protocolid && 266 app->priority == cfg->app[i].priority) 267 return true; 268 } 269 270 return false; 271 } 272 273 /** 274 * i40e_dcbnl_flush_apps - Delete all removed APPs 275 * @pf: the corresponding PF 276 * @old_cfg: old DCBX configuration data 277 * @new_cfg: new DCBX configuration data 278 * 279 * Find and delete all APPs that are not present in the passed 280 * DCB configuration 281 **/ 282 void i40e_dcbnl_flush_apps(struct i40e_pf *pf, 283 struct i40e_dcbx_config *old_cfg, 284 struct i40e_dcbx_config *new_cfg) 285 { 286 struct i40e_dcb_app_priority_table app; 287 int i; 288 289 /* MFP mode but not an iSCSI PF so return */ 290 if ((pf->flags & I40E_FLAG_MFP_ENABLED) && !(pf->hw.func_caps.iscsi)) 291 return; 292 293 for (i = 0; i < old_cfg->numapps; i++) { 294 app = old_cfg->app[i]; 295 /* The APP is not available anymore delete it */ 296 if (!i40e_dcbnl_find_app(new_cfg, &app)) 297 i40e_dcbnl_del_app(pf, &app); 298 } 299 } 300 301 /** 302 * i40e_dcbnl_setup - DCBNL setup 303 * @vsi: the corresponding vsi 304 * 305 * Set up DCBNL ops and initial APP TLVs 306 **/ 307 void i40e_dcbnl_setup(struct i40e_vsi *vsi) 308 { 309 struct net_device *dev = vsi->netdev; 310 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 311 312 /* Not DCB capable */ 313 if (!(pf->flags & I40E_FLAG_DCB_CAPABLE)) 314 return; 315 316 dev->dcbnl_ops = &dcbnl_ops; 317 318 /* Set initial IEEE DCB settings */ 319 i40e_dcbnl_set_all(vsi); 320 } 321 #endif /* CONFIG_I40E_DCB */ 322