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