1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <time.h> 6 #include <rte_atomic.h> 7 #include <ethdev_driver.h> 8 #include "ixgbe_ethdev.h" 9 #include "ixgbe_bypass_api.h" 10 #include "rte_pmd_ixgbe.h" 11 12 #define BYPASS_STATUS_OFF_MASK 3 13 14 /* Macros to check for invalid function pointers. */ 15 #define FUNC_PTR_OR_ERR_RET(func, retval) do { \ 16 if ((func) == NULL) { \ 17 PMD_DRV_LOG(ERR, "%s:%d function not supported", \ 18 __func__, __LINE__); \ 19 return retval; \ 20 } \ 21 } while (0) 22 23 #define FUNC_PTR_OR_RET(func) do { \ 24 if ((func) == NULL) { \ 25 PMD_DRV_LOG(ERR, "%s:%d function not supported", \ 26 __func__, __LINE__); \ 27 return; \ 28 } \ 29 } while (0) 30 31 32 /** 33 * ixgbe_bypass_set_time - Set bypass FW time epoc. 34 * 35 * @hw: pointer to hardware structure 36 * 37 * This function with sync the FW date stamp with that of the 38 * system clock. 39 **/ 40 static void 41 ixgbe_bypass_set_time(struct ixgbe_adapter *adapter) 42 { 43 u32 mask, value; 44 u32 sec; 45 struct ixgbe_hw *hw = &adapter->hw; 46 47 sec = 0; 48 49 /* 50 * Send the FW our current time and turn on time_valid and 51 * timer_reset bits. 52 */ 53 mask = BYPASS_CTL1_TIME_M | 54 BYPASS_CTL1_VALID_M | 55 BYPASS_CTL1_OFFTRST_M; 56 value = (sec & BYPASS_CTL1_TIME_M) | 57 BYPASS_CTL1_VALID | 58 BYPASS_CTL1_OFFTRST; 59 60 FUNC_PTR_OR_RET(adapter->bps.ops.bypass_set); 61 62 /* Store FW reset time (in seconds from epoch). */ 63 adapter->bps.reset_tm = time(NULL); 64 65 /* reset FW timer. */ 66 adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL1, mask, value); 67 } 68 69 /** 70 * ixgbe_bypass_init - Make some environment changes for bypass 71 * 72 * @adapter: pointer to ixgbe_adapter structure for access to state bits 73 * 74 * This function collects all the modifications needed by the bypass 75 * driver. 76 **/ 77 void 78 ixgbe_bypass_init(struct rte_eth_dev *dev) 79 { 80 struct ixgbe_adapter *adapter; 81 struct ixgbe_hw *hw; 82 83 adapter = IXGBE_DEV_TO_ADPATER(dev); 84 hw = &adapter->hw; 85 86 /* Only allow BYPASS ops on the first port */ 87 if (hw->device_id != IXGBE_DEV_ID_82599_BYPASS || 88 hw->bus.func != 0) { 89 PMD_DRV_LOG(ERR, "bypass function is not supported on that device"); 90 return; 91 } 92 93 /* set bypass ops. */ 94 adapter->bps.ops.bypass_rw = &ixgbe_bypass_rw_generic; 95 adapter->bps.ops.bypass_valid_rd = &ixgbe_bypass_valid_rd_generic; 96 adapter->bps.ops.bypass_set = &ixgbe_bypass_set_generic; 97 adapter->bps.ops.bypass_rd_eep = &ixgbe_bypass_rd_eep_generic; 98 99 /* set the time for logging. */ 100 ixgbe_bypass_set_time(adapter); 101 102 /* Don't have the SDP to the laser */ 103 hw->mac.ops.disable_tx_laser = NULL; 104 hw->mac.ops.enable_tx_laser = NULL; 105 hw->mac.ops.flap_tx_laser = NULL; 106 } 107 108 s32 109 ixgbe_bypass_state_show(struct rte_eth_dev *dev, u32 *state) 110 { 111 struct ixgbe_hw *hw; 112 s32 ret_val; 113 u32 cmd; 114 u32 by_ctl = 0; 115 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev); 116 117 hw = &adapter->hw; 118 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP); 119 120 cmd = BYPASS_PAGE_CTL0; 121 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &by_ctl); 122 123 /* Assume bypass_rw didn't error out, if it did state will 124 * be ignored anyway. 125 */ 126 *state = (by_ctl >> BYPASS_STATUS_OFF_SHIFT) & BYPASS_STATUS_OFF_MASK; 127 128 return ret_val; 129 } 130 131 132 s32 133 ixgbe_bypass_state_store(struct rte_eth_dev *dev, u32 *new_state) 134 { 135 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev); 136 struct ixgbe_hw *hw; 137 s32 ret_val; 138 139 hw = &adapter->hw; 140 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_set, -ENOTSUP); 141 142 /* Set the new state */ 143 ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0, 144 BYPASS_MODE_OFF_M, *new_state); 145 if (ret_val) 146 goto exit; 147 148 /* Set AUTO back on so FW can receive events */ 149 ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0, 150 BYPASS_MODE_OFF_M, BYPASS_AUTO); 151 152 exit: 153 return ret_val; 154 155 } 156 157 s32 158 ixgbe_bypass_event_show(struct rte_eth_dev *dev, u32 event, 159 u32 *state) 160 { 161 struct ixgbe_hw *hw; 162 s32 ret_val; 163 u32 shift; 164 u32 cmd; 165 u32 by_ctl = 0; 166 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev); 167 168 hw = &adapter->hw; 169 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP); 170 171 cmd = BYPASS_PAGE_CTL0; 172 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &by_ctl); 173 174 /* Assume bypass_rw didn't error out, if it did event will 175 * be ignored anyway. 176 */ 177 switch (event) { 178 case BYPASS_EVENT_WDT_TO: 179 shift = BYPASS_WDTIMEOUT_SHIFT; 180 break; 181 case BYPASS_EVENT_MAIN_ON: 182 shift = BYPASS_MAIN_ON_SHIFT; 183 break; 184 case BYPASS_EVENT_MAIN_OFF: 185 shift = BYPASS_MAIN_OFF_SHIFT; 186 break; 187 case BYPASS_EVENT_AUX_ON: 188 shift = BYPASS_AUX_ON_SHIFT; 189 break; 190 case BYPASS_EVENT_AUX_OFF: 191 shift = BYPASS_AUX_OFF_SHIFT; 192 break; 193 default: 194 return EINVAL; 195 } 196 197 *state = (by_ctl >> shift) & 0x3; 198 199 return ret_val; 200 } 201 202 s32 203 ixgbe_bypass_event_store(struct rte_eth_dev *dev, u32 event, 204 u32 state) 205 { 206 struct ixgbe_hw *hw; 207 u32 status; 208 u32 off; 209 s32 ret_val; 210 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev); 211 212 hw = &adapter->hw; 213 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_set, -ENOTSUP); 214 215 switch (event) { 216 case BYPASS_EVENT_WDT_TO: 217 off = BYPASS_WDTIMEOUT_M; 218 status = state << BYPASS_WDTIMEOUT_SHIFT; 219 break; 220 case BYPASS_EVENT_MAIN_ON: 221 off = BYPASS_MAIN_ON_M; 222 status = state << BYPASS_MAIN_ON_SHIFT; 223 break; 224 case BYPASS_EVENT_MAIN_OFF: 225 off = BYPASS_MAIN_OFF_M; 226 status = state << BYPASS_MAIN_OFF_SHIFT; 227 break; 228 case BYPASS_EVENT_AUX_ON: 229 off = BYPASS_AUX_ON_M; 230 status = state << BYPASS_AUX_ON_SHIFT; 231 break; 232 case BYPASS_EVENT_AUX_OFF: 233 off = BYPASS_AUX_OFF_M; 234 status = state << BYPASS_AUX_OFF_SHIFT; 235 break; 236 default: 237 return EINVAL; 238 } 239 240 ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0, 241 off, status); 242 243 return ret_val; 244 } 245 246 s32 247 ixgbe_bypass_wd_timeout_store(struct rte_eth_dev *dev, u32 timeout) 248 { 249 struct ixgbe_hw *hw; 250 u32 status; 251 u32 mask; 252 s32 ret_val; 253 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev); 254 255 hw = &adapter->hw; 256 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_set, -ENOTSUP); 257 258 /* disable the timer with timeout of zero */ 259 if (timeout == RTE_PMD_IXGBE_BYPASS_TMT_OFF) { 260 status = 0x0; /* WDG enable off */ 261 mask = BYPASS_WDT_ENABLE_M; 262 } else { 263 /* set time out value */ 264 mask = BYPASS_WDT_VALUE_M; 265 266 /* enable the timer */ 267 status = timeout << BYPASS_WDT_TIME_SHIFT; 268 status |= 0x1 << BYPASS_WDT_ENABLE_SHIFT; 269 mask |= BYPASS_WDT_ENABLE_M; 270 } 271 272 ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0, 273 mask, status); 274 275 return ret_val; 276 } 277 278 s32 279 ixgbe_bypass_ver_show(struct rte_eth_dev *dev, u32 *ver) 280 { 281 struct ixgbe_hw *hw; 282 u32 cmd; 283 u32 status; 284 s32 ret_val; 285 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev); 286 287 hw = &adapter->hw; 288 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP); 289 290 cmd = BYPASS_PAGE_CTL2 | BYPASS_WE; 291 cmd |= (BYPASS_EEPROM_VER_ADD << BYPASS_CTL2_OFFSET_SHIFT) & 292 BYPASS_CTL2_OFFSET_M; 293 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &status); 294 if (ret_val) 295 goto exit; 296 297 /* wait for the write to stick */ 298 msleep(100); 299 300 /* Now read the results */ 301 cmd &= ~BYPASS_WE; 302 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &status); 303 if (ret_val) 304 goto exit; 305 306 *ver = status & BYPASS_CTL2_DATA_M; /* only one byte of date */ 307 308 exit: 309 return ret_val; 310 } 311 312 s32 313 ixgbe_bypass_wd_timeout_show(struct rte_eth_dev *dev, u32 *wd_timeout) 314 { 315 struct ixgbe_hw *hw; 316 u32 by_ctl = 0; 317 u32 cmd; 318 u32 wdg; 319 s32 ret_val; 320 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev); 321 322 hw = &adapter->hw; 323 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP); 324 325 cmd = BYPASS_PAGE_CTL0; 326 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &by_ctl); 327 328 wdg = by_ctl & BYPASS_WDT_ENABLE_M; 329 if (!wdg) 330 *wd_timeout = RTE_PMD_IXGBE_BYPASS_TMT_OFF; 331 else 332 *wd_timeout = (by_ctl >> BYPASS_WDT_TIME_SHIFT) & 333 BYPASS_WDT_MASK; 334 335 return ret_val; 336 } 337 338 s32 339 ixgbe_bypass_wd_reset(struct rte_eth_dev *dev) 340 { 341 u32 cmd; 342 u32 status; 343 u32 sec; 344 u32 count = 0; 345 s32 ret_val; 346 struct ixgbe_hw *hw; 347 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev); 348 349 hw = &adapter->hw; 350 351 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP); 352 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_valid_rd, -ENOTSUP); 353 354 /* Use the lower level bit-bang functions since we don't need 355 * to read the register first to get it's current state as we 356 * are setting every thing in this write. 357 */ 358 /* Set up WD pet */ 359 cmd = BYPASS_PAGE_CTL1 | BYPASS_WE | BYPASS_CTL1_WDT_PET; 360 361 /* Resync the FW time while writing to CTL1 anyway */ 362 adapter->bps.reset_tm = time(NULL); 363 sec = 0; 364 365 cmd |= (sec & BYPASS_CTL1_TIME_M) | BYPASS_CTL1_VALID; 366 367 /* reset FW timer offset since we are resetting the clock */ 368 cmd |= BYPASS_CTL1_OFFTRST; 369 370 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &status); 371 372 /* Read until it matches what we wrote, or we time out */ 373 do { 374 if (count++ > 10) { 375 ret_val = IXGBE_BYPASS_FW_WRITE_FAILURE; 376 break; 377 } 378 379 if (adapter->bps.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &status)) { 380 ret_val = IXGBE_ERR_INVALID_ARGUMENT; 381 break; 382 } 383 } while (!adapter->bps.ops.bypass_valid_rd(cmd, status)); 384 385 return ret_val; 386 } 387