Home | History | Annotate | Line # | Download | only in ixgbe
if_bypass.c revision 1.3
      1 /******************************************************************************
      2 
      3   Copyright (c) 2001-2017, Intel Corporation
      4   All rights reserved.
      5 
      6   Redistribution and use in source and binary forms, with or without
      7   modification, are permitted provided that the following conditions are met:
      8 
      9    1. Redistributions of source code must retain the above copyright notice,
     10       this list of conditions and the following disclaimer.
     11 
     12    2. Redistributions in binary form must reproduce the above copyright
     13       notice, this list of conditions and the following disclaimer in the
     14       documentation and/or other materials provided with the distribution.
     15 
     16    3. Neither the name of the Intel Corporation nor the names of its
     17       contributors may be used to endorse or promote products derived from
     18       this software without specific prior written permission.
     19 
     20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     21   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     24   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30   POSSIBILITY OF SUCH DAMAGE.
     31 
     32 ******************************************************************************/
     33 /*$FreeBSD: head/sys/dev/ixgbe/if_bypass.c 320688 2017-07-05 17:27:03Z erj $*/
     34 
     35 
     36 #include "ixgbe.h"
     37 
     38 /************************************************************************
     39  * ixgbe_bypass_mutex_enter
     40  *
     41  *   Mutex support for the bypass feature. Using a dual lock
     42  *   to facilitate a privileged access to the watchdog update
     43  *   over other threads.
     44  ************************************************************************/
     45 static void
     46 ixgbe_bypass_mutex_enter(struct adapter *adapter)
     47 {
     48 	while (atomic_cas_uint(&adapter->bypass.low, 0, 1) == 0)
     49 		usec_delay(3000);
     50 	while (atomic_cas_uint(&adapter->bypass.high, 0, 1) == 0)
     51 		usec_delay(3000);
     52 	return;
     53 } /* ixgbe_bypass_mutex_enter */
     54 
     55 /************************************************************************
     56  * ixgbe_bypass_mutex_clear
     57  ************************************************************************/
     58 static void
     59 ixgbe_bypass_mutex_clear(struct adapter *adapter)
     60 {
     61 	while (atomic_cas_uint(&adapter->bypass.high, 1, 0) == 0)
     62 		usec_delay(6000);
     63 	while (atomic_cas_uint(&adapter->bypass.low, 1, 0) == 0)
     64 		usec_delay(6000);
     65 	return;
     66 } /* ixgbe_bypass_mutex_clear */
     67 
     68 /************************************************************************
     69  * ixgbe_bypass_wd_mutex_enter
     70  *
     71  *   Watchdog entry is allowed to simply grab the high priority
     72  ************************************************************************/
     73 static void
     74 ixgbe_bypass_wd_mutex_enter(struct adapter *adapter)
     75 {
     76 	while (atomic_cas_uint(&adapter->bypass.high, 0, 1) == 0)
     77 		usec_delay(3000);
     78 	return;
     79 } /* ixgbe_bypass_wd_mutex_enter */
     80 
     81 /************************************************************************
     82  * ixgbe_bypass_wd_mutex_clear
     83  ************************************************************************/
     84 static void
     85 ixgbe_bypass_wd_mutex_clear(struct adapter *adapter)
     86 {
     87 	while (atomic_cas_uint(&adapter->bypass.high, 1, 0) == 0)
     88 		usec_delay(6000);
     89 	return;
     90 } /* ixgbe_bypass_wd_mutex_clear */
     91 
     92 /************************************************************************
     93  * ixgbe_get_bypass_time
     94  ************************************************************************/
     95 static void
     96 ixgbe_get_bypass_time(u32 *year, u32 *sec)
     97 {
     98 	struct timespec current;
     99 
    100 	*year = 1970;           /* time starts at 01/01/1970 */
    101 	nanotime(&current);
    102 	*sec = current.tv_sec;
    103 
    104 	while(*sec > SEC_THIS_YEAR(*year)) {
    105 		*sec -= SEC_THIS_YEAR(*year);
    106 		(*year)++;
    107 	}
    108 } /* ixgbe_get_bypass_time */
    109 
    110 /************************************************************************
    111  * ixgbe_bp_version
    112  *
    113  *   Display the feature version
    114  ************************************************************************/
    115 static int
    116 ixgbe_bp_version(SYSCTLFN_ARGS)
    117 {
    118 	struct sysctlnode node = *rnode;
    119 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    120 	struct ixgbe_hw *hw = &adapter->hw;
    121 	int             error = 0;
    122 	static int      featversion = 0;
    123 	u32             cmd;
    124 
    125 	ixgbe_bypass_mutex_enter(adapter);
    126 	cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
    127 	cmd |= (BYPASS_EEPROM_VER_ADD << BYPASS_CTL2_OFFSET_SHIFT) &
    128 	    BYPASS_CTL2_OFFSET_M;
    129 	if ((error = hw->mac.ops.bypass_rw(hw, cmd, &featversion) != 0))
    130 		goto err;
    131 	msec_delay(100);
    132 	cmd &= ~BYPASS_WE;
    133 	if ((error = hw->mac.ops.bypass_rw(hw, cmd, &featversion) != 0))
    134 		goto err;
    135 	ixgbe_bypass_mutex_clear(adapter);
    136 	featversion &= BYPASS_CTL2_DATA_M;
    137 	node.sysctl_data = &featversion;
    138 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    139 	return (error);
    140 err:
    141 	ixgbe_bypass_mutex_clear(adapter);
    142 	return (error);
    143 
    144 } /* ixgbe_bp_version */
    145 
    146 /************************************************************************
    147  * ixgbe_bp_set_state
    148  *
    149  *   Show/Set the Bypass State:
    150  *	1 = NORMAL
    151  *	2 = BYPASS
    152  *	3 = ISOLATE
    153  *
    154  *	With no argument the state is displayed,
    155  *	passing a value will set it.
    156  ************************************************************************/
    157 static int
    158 ixgbe_bp_set_state(SYSCTLFN_ARGS)
    159 {
    160 	struct sysctlnode node = *rnode;
    161 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    162 	struct ixgbe_hw *hw = &adapter->hw;
    163 	int             error = 0;
    164 	static int      state = 0;
    165 
    166 	/* Get the current state */
    167 	ixgbe_bypass_mutex_enter(adapter);
    168 	error = hw->mac.ops.bypass_rw(hw,
    169 	    BYPASS_PAGE_CTL0, &state);
    170 	ixgbe_bypass_mutex_clear(adapter);
    171 	if (error)
    172 		return (error);
    173 	state = (state >> BYPASS_STATUS_OFF_SHIFT) & 0x3;
    174 
    175 	node.sysctl_data = &state;
    176 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    177 	if ((error) || (newp == NULL))
    178 		return (error);
    179 
    180 	/* Sanity check new state */
    181 	switch (state) {
    182 	case BYPASS_NORM:
    183 	case BYPASS_BYPASS:
    184 	case BYPASS_ISOLATE:
    185 		break;
    186 	default:
    187 		return (EINVAL);
    188 	}
    189 	ixgbe_bypass_mutex_enter(adapter);
    190 	if ((error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
    191 	    BYPASS_MODE_OFF_M, state) != 0))
    192 		goto out;
    193 	/* Set AUTO back on so FW can receive events */
    194 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
    195 	    BYPASS_MODE_OFF_M, BYPASS_AUTO);
    196 out:
    197 	ixgbe_bypass_mutex_clear(adapter);
    198 	usec_delay(6000);
    199 	return (error);
    200 } /* ixgbe_bp_set_state */
    201 
    202 /************************************************************************
    203  * The following routines control the operational
    204  * "rules" of the feature, what behavior will occur
    205  * when particular events occur.
    206  * 	Values are:
    207  *		0 - no change for the event (NOP)
    208  *		1 - go to Normal operation
    209  *		2 - go to Bypass operation
    210  *		3 - go to Isolate operation
    211  * Calling the entry with no argument just displays
    212  * the current rule setting.
    213  ************************************************************************/
    214 
    215 /************************************************************************
    216  * ixgbe_bp_timeout
    217  *
    218  * This is to set the Rule for the watchdog,
    219  * not the actual watchdog timeout value.
    220  ************************************************************************/
    221 static int
    222 ixgbe_bp_timeout(SYSCTLFN_ARGS)
    223 {
    224 	struct sysctlnode node = *rnode;
    225 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    226 	struct ixgbe_hw *hw = &adapter->hw;
    227 	int             error = 0;
    228 	static int      timeout = 0;
    229 
    230 	/* Get the current value */
    231 	ixgbe_bypass_mutex_enter(adapter);
    232 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &timeout);
    233 	ixgbe_bypass_mutex_clear(adapter);
    234 	if (error)
    235 		return (error);
    236 	timeout = (timeout >> BYPASS_WDTIMEOUT_SHIFT) & 0x3;
    237 
    238 	node.sysctl_data = &timeout;
    239 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    240 	if ((error) || (newp == NULL))
    241 		return (error);
    242 
    243 	/* Sanity check on the setting */
    244 	switch (timeout) {
    245 	case BYPASS_NOP:
    246 	case BYPASS_NORM:
    247 	case BYPASS_BYPASS:
    248 	case BYPASS_ISOLATE:
    249 		break;
    250 	default:
    251 		return (EINVAL);
    252 	}
    253 
    254 	/* Set the new state */
    255 	ixgbe_bypass_mutex_enter(adapter);
    256 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
    257 	    BYPASS_WDTIMEOUT_M, timeout << BYPASS_WDTIMEOUT_SHIFT);
    258 	ixgbe_bypass_mutex_clear(adapter);
    259 	usec_delay(6000);
    260 	return (error);
    261 } /* ixgbe_bp_timeout */
    262 
    263 /************************************************************************
    264  * ixgbe_bp_main_on
    265  ************************************************************************/
    266 static int
    267 ixgbe_bp_main_on(SYSCTLFN_ARGS)
    268 {
    269 	struct sysctlnode node = *rnode;
    270 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    271 	struct ixgbe_hw *hw = &adapter->hw;
    272 	int             error = 0;
    273 	static int      main_on = 0;
    274 
    275 	ixgbe_bypass_mutex_enter(adapter);
    276 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &main_on);
    277 	main_on = (main_on >> BYPASS_MAIN_ON_SHIFT) & 0x3;
    278 	ixgbe_bypass_mutex_clear(adapter);
    279 	if (error)
    280 		return (error);
    281 
    282 	node.sysctl_data = &main_on;
    283 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    284 	if ((error) || (newp == NULL))
    285 		return (error);
    286 
    287 	/* Sanity check on the setting */
    288 	switch (main_on) {
    289 	case BYPASS_NOP:
    290 	case BYPASS_NORM:
    291 	case BYPASS_BYPASS:
    292 	case BYPASS_ISOLATE:
    293 		break;
    294 	default:
    295 		return (EINVAL);
    296 	}
    297 
    298 	/* Set the new state */
    299 	ixgbe_bypass_mutex_enter(adapter);
    300 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
    301 	    BYPASS_MAIN_ON_M, main_on << BYPASS_MAIN_ON_SHIFT);
    302 	ixgbe_bypass_mutex_clear(adapter);
    303 	usec_delay(6000);
    304 	return (error);
    305 } /* ixgbe_bp_main_on */
    306 
    307 /************************************************************************
    308  * ixgbe_bp_main_off
    309  ************************************************************************/
    310 static int
    311 ixgbe_bp_main_off(SYSCTLFN_ARGS)
    312 {
    313 	struct sysctlnode node = *rnode;
    314 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    315 	struct ixgbe_hw *hw = &adapter->hw;
    316 	int             error = 0;
    317 	static int      main_off = 0;
    318 
    319 	ixgbe_bypass_mutex_enter(adapter);
    320 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &main_off);
    321 	ixgbe_bypass_mutex_clear(adapter);
    322 	if (error)
    323 		return (error);
    324 	main_off = (main_off >> BYPASS_MAIN_OFF_SHIFT) & 0x3;
    325 
    326 	node.sysctl_data = &main_off;
    327 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    328 	if ((error) || (newp == NULL))
    329 		return (error);
    330 
    331 	/* Sanity check on the setting */
    332 	switch (main_off) {
    333 	case BYPASS_NOP:
    334 	case BYPASS_NORM:
    335 	case BYPASS_BYPASS:
    336 	case BYPASS_ISOLATE:
    337 		break;
    338 	default:
    339 		return (EINVAL);
    340 	}
    341 
    342 	/* Set the new state */
    343 	ixgbe_bypass_mutex_enter(adapter);
    344 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
    345 	    BYPASS_MAIN_OFF_M, main_off << BYPASS_MAIN_OFF_SHIFT);
    346 	ixgbe_bypass_mutex_clear(adapter);
    347 	usec_delay(6000);
    348 	return (error);
    349 } /* ixgbe_bp_main_off */
    350 
    351 /************************************************************************
    352  * ixgbe_bp_aux_on
    353  ************************************************************************/
    354 static int
    355 ixgbe_bp_aux_on(SYSCTLFN_ARGS)
    356 {
    357 	struct sysctlnode node = *rnode;
    358 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    359 	struct ixgbe_hw *hw = &adapter->hw;
    360 	int             error = 0;
    361 	static int      aux_on = 0;
    362 
    363 	ixgbe_bypass_mutex_enter(adapter);
    364 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &aux_on);
    365 	ixgbe_bypass_mutex_clear(adapter);
    366 	if (error)
    367 		return (error);
    368 	aux_on = (aux_on >> BYPASS_AUX_ON_SHIFT) & 0x3;
    369 
    370 	node.sysctl_data = &aux_on;
    371 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    372 	if ((error) || (newp == NULL))
    373 		return (error);
    374 
    375 	/* Sanity check on the setting */
    376 	switch (aux_on) {
    377 	case BYPASS_NOP:
    378 	case BYPASS_NORM:
    379 	case BYPASS_BYPASS:
    380 	case BYPASS_ISOLATE:
    381 		break;
    382 	default:
    383 		return (EINVAL);
    384 	}
    385 
    386 	/* Set the new state */
    387 	ixgbe_bypass_mutex_enter(adapter);
    388 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
    389 	    BYPASS_AUX_ON_M, aux_on << BYPASS_AUX_ON_SHIFT);
    390 	ixgbe_bypass_mutex_clear(adapter);
    391 	usec_delay(6000);
    392 	return (error);
    393 } /* ixgbe_bp_aux_on */
    394 
    395 /************************************************************************
    396  * ixgbe_bp_aux_off
    397  ************************************************************************/
    398 static int
    399 ixgbe_bp_aux_off(SYSCTLFN_ARGS)
    400 {
    401 	struct sysctlnode node = *rnode;
    402 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    403 	struct ixgbe_hw *hw = &adapter->hw;
    404 	int             error = 0;
    405 	static int      aux_off = 0;
    406 
    407 	ixgbe_bypass_mutex_enter(adapter);
    408 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &aux_off);
    409 	ixgbe_bypass_mutex_clear(adapter);
    410 	if (error)
    411 		return (error);
    412 	aux_off = (aux_off >> BYPASS_AUX_OFF_SHIFT) & 0x3;
    413 
    414 	node.sysctl_data = &aux_off;
    415 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    416 	if ((error) || (newp == NULL))
    417 		return (error);
    418 
    419 	/* Sanity check on the setting */
    420 	switch (aux_off) {
    421 	case BYPASS_NOP:
    422 	case BYPASS_NORM:
    423 	case BYPASS_BYPASS:
    424 	case BYPASS_ISOLATE:
    425 		break;
    426 	default:
    427 		return (EINVAL);
    428 	}
    429 
    430 	/* Set the new state */
    431 	ixgbe_bypass_mutex_enter(adapter);
    432 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
    433 	    BYPASS_AUX_OFF_M, aux_off << BYPASS_AUX_OFF_SHIFT);
    434 	ixgbe_bypass_mutex_clear(adapter);
    435 	usec_delay(6000);
    436 	return (error);
    437 } /* ixgbe_bp_aux_off */
    438 
    439 /************************************************************************
    440  * ixgbe_bp_wd_set - Set the Watchdog timer value
    441  *
    442  *   Valid settings are:
    443  *	- 0 will disable the watchdog
    444  *	- 1, 2, 3, 4, 8, 16, 32
    445  *	- anything else is invalid and will be ignored
    446  ************************************************************************/
    447 static int
    448 ixgbe_bp_wd_set(SYSCTLFN_ARGS)
    449 {
    450 	struct sysctlnode node = *rnode;
    451 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    452 	struct ixgbe_hw *hw = &adapter->hw;
    453 	int             error, tmp;
    454 	static int      timeout = 0;
    455 	u32             mask, arg = BYPASS_PAGE_CTL0;
    456 
    457 	/* Get the current hardware value */
    458 	ixgbe_bypass_mutex_enter(adapter);
    459 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &tmp);
    460 	ixgbe_bypass_mutex_clear(adapter);
    461 	if (error)
    462 		return (error);
    463 	/*
    464 	 * If armed keep the displayed value,
    465 	 * else change the display to zero.
    466 	 */
    467 	if ((tmp & (0x1 << BYPASS_WDT_ENABLE_SHIFT)) == 0)
    468 		timeout = 0;
    469 
    470 	node.sysctl_data = &timeout;
    471 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    472 	if ((error) || (newp == NULL))
    473 		return (error);
    474 
    475 	mask = BYPASS_WDT_ENABLE_M;
    476 	switch (timeout) {
    477 	case 0: /* disables the timer */
    478 		break;
    479 	case 1:
    480 		arg = BYPASS_WDT_1_5 << BYPASS_WDT_TIME_SHIFT;
    481 		arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
    482 		mask |= BYPASS_WDT_VALUE_M;
    483 		break;
    484 	case 2:
    485 		arg = BYPASS_WDT_2 << BYPASS_WDT_TIME_SHIFT;
    486 		arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
    487 		mask |= BYPASS_WDT_VALUE_M;
    488 		break;
    489 	case 3:
    490 		arg = BYPASS_WDT_3 << BYPASS_WDT_TIME_SHIFT;
    491 		arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
    492 		mask |= BYPASS_WDT_VALUE_M;
    493 		break;
    494 	case 4:
    495 		arg = BYPASS_WDT_4 << BYPASS_WDT_TIME_SHIFT;
    496 		arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
    497 		mask |= BYPASS_WDT_VALUE_M;
    498 		break;
    499 	case 8:
    500 		arg = BYPASS_WDT_8 << BYPASS_WDT_TIME_SHIFT;
    501 		arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
    502 		mask |= BYPASS_WDT_VALUE_M;
    503 		break;
    504 	case 16:
    505 		arg = BYPASS_WDT_16 << BYPASS_WDT_TIME_SHIFT;
    506 		arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
    507 		mask |= BYPASS_WDT_VALUE_M;
    508 		break;
    509 	case 32:
    510 		arg = BYPASS_WDT_32 << BYPASS_WDT_TIME_SHIFT;
    511 		arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
    512 		mask |= BYPASS_WDT_VALUE_M;
    513 		break;
    514 	default:
    515 		return (EINVAL);
    516 	}
    517 	/* Set the new watchdog */
    518 	ixgbe_bypass_mutex_enter(adapter);
    519 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0, mask, arg);
    520 	ixgbe_bypass_mutex_clear(adapter);
    521 
    522 	return (error);
    523 } /* ixgbe_bp_wd_set */
    524 
    525 /************************************************************************
    526  * ixgbe_bp_wd_reset - Reset the Watchdog timer
    527  *
    528  *    To activate this it must be called with any argument.
    529  ************************************************************************/
    530 static int
    531 ixgbe_bp_wd_reset(SYSCTLFN_ARGS)
    532 {
    533 	struct sysctlnode node = *rnode;
    534 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    535 	struct ixgbe_hw *hw = &adapter->hw;
    536 	u32             sec, year;
    537 	int             cmd, count = 0, error = 0;
    538 	int             reset_wd = 0;
    539 
    540 	node.sysctl_data = &reset_wd;
    541 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    542 	if ((error) || (newp == NULL))
    543 		return (error);
    544 
    545 	cmd = BYPASS_PAGE_CTL1 | BYPASS_WE | BYPASS_CTL1_WDT_PET;
    546 
    547 	/* Resync the FW time while writing to CTL1 anyway */
    548 	ixgbe_get_bypass_time(&year, &sec);
    549 
    550 	cmd |= (sec & BYPASS_CTL1_TIME_M) | BYPASS_CTL1_VALID;
    551 	cmd |= BYPASS_CTL1_OFFTRST;
    552 
    553 	ixgbe_bypass_wd_mutex_enter(adapter);
    554 	error = hw->mac.ops.bypass_rw(hw, cmd, &reset_wd);
    555 
    556 	/* Read until it matches what we wrote, or we time out */
    557 	do {
    558 		if (count++ > 10) {
    559 			error = IXGBE_BYPASS_FW_WRITE_FAILURE;
    560 			break;
    561 		}
    562 		if (hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &reset_wd)) {
    563 			error = IXGBE_ERR_INVALID_ARGUMENT;
    564 			break;
    565 		}
    566 	} while (!hw->mac.ops.bypass_valid_rd(cmd, reset_wd));
    567 
    568 	reset_wd = 0;
    569 	ixgbe_bypass_wd_mutex_clear(adapter);
    570 	return (error);
    571 } /* ixgbe_bp_wd_reset */
    572 
    573 /************************************************************************
    574  * ixgbe_bp_log - Display the bypass log
    575  *
    576  *   You must pass a non-zero arg to sysctl
    577  ************************************************************************/
    578 static int
    579 ixgbe_bp_log(SYSCTLFN_ARGS)
    580 {
    581 	struct sysctlnode          node = *rnode;
    582 	struct adapter           *adapter = (struct adapter *)node.sysctl_data;
    583 	struct ixgbe_hw            *hw = &adapter->hw;
    584 	u32                        cmd, base, head;
    585 	u32                        log_off, count = 0;
    586 	static int                 status = 0;
    587 	u8                         data;
    588 	struct ixgbe_bypass_eeprom eeprom[BYPASS_MAX_LOGS];
    589 	int                        i, error = 0;
    590 
    591 	node.sysctl_data = &status;
    592 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    593 	if ((error) || (newp == NULL))
    594 		return (error);
    595 
    596 	/* Keep the log display single-threaded */
    597 	while (atomic_cas_uint(&adapter->bypass.log, 0, 1) == 0)
    598 		usec_delay(3000);
    599 
    600 	ixgbe_bypass_mutex_enter(adapter);
    601 
    602 	/* Find Current head of the log eeprom offset */
    603 	cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
    604 	cmd |= (0x1 << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
    605 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
    606 	if (error)
    607 		goto unlock_err;
    608 
    609 	/* wait for the write to stick */
    610 	msec_delay(100);
    611 
    612 	/* Now read the results */
    613 	cmd &= ~BYPASS_WE;
    614 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
    615 	if (error)
    616 		goto unlock_err;
    617 
    618 	ixgbe_bypass_mutex_clear(adapter);
    619 
    620 	base = status & BYPASS_CTL2_DATA_M;
    621 	head = (status & BYPASS_CTL2_HEAD_M) >> BYPASS_CTL2_HEAD_SHIFT;
    622 
    623 	/* address of the first log */
    624 	log_off = base + (head * 5);
    625 
    626 	/* extract all the log entries */
    627 	while (count < BYPASS_MAX_LOGS) {
    628 		eeprom[count].logs = 0;
    629 		eeprom[count].actions = 0;
    630 
    631 		/* Log 5 bytes store in on u32 and a u8 */
    632 		for (i = 0; i < 4; i++) {
    633 			ixgbe_bypass_mutex_enter(adapter);
    634 			error = hw->mac.ops.bypass_rd_eep(hw, log_off + i,
    635 			    &data);
    636 			ixgbe_bypass_mutex_clear(adapter);
    637 			if (error)
    638 				return (EINVAL);
    639 			eeprom[count].logs += data << (8 * i);
    640 		}
    641 
    642 		ixgbe_bypass_mutex_enter(adapter);
    643 		error = hw->mac.ops.bypass_rd_eep(hw,
    644 		    log_off + i, &eeprom[count].actions);
    645 		ixgbe_bypass_mutex_clear(adapter);
    646 		if (error)
    647 			return (EINVAL);
    648 
    649 		/* Quit if not a unread log */
    650 		if (!(eeprom[count].logs & BYPASS_LOG_CLEAR_M))
    651 			break;
    652 		/*
    653 		 * Log looks good so store the address where it's
    654 		 * Unread Log bit is so we can clear it after safely
    655 		 * pulling out all of the log data.
    656 		 */
    657 		eeprom[count].clear_off = log_off;
    658 
    659 		count++;
    660 		head = head ? head - 1 : BYPASS_MAX_LOGS;
    661 		log_off = base + (head * 5);
    662 	}
    663 
    664 	/* reverse order (oldest first) for output */
    665 	while (count--) {
    666 		int year;
    667 		u32 mon, days, hours, min, sec;
    668 		u32 time = eeprom[count].logs & BYPASS_LOG_TIME_M;
    669 		u32 event = (eeprom[count].logs & BYPASS_LOG_EVENT_M) >>
    670 		    BYPASS_LOG_EVENT_SHIFT;
    671 		u8 action =  eeprom[count].actions & BYPASS_LOG_ACTION_M;
    672 		u16 day_mon[2][13] = {
    673 		  {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
    674 		  {0, 31, 59, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
    675 		};
    676 		const char *event_str[] = {"unknown", "main on", "aux on",
    677 		    "main off", "aux off", "WDT", "user" };
    678 		const char *action_str[] = {"ignore", "normal", "bypass",
    679 					    "isolate",};
    680 
    681 		/* verify vaild data  1 - 6 */
    682 		if (event < BYPASS_EVENT_MAIN_ON || event > BYPASS_EVENT_USR)
    683 			event = 0;
    684 
    685 		/*
    686 		 * time is in sec's this year, so convert to something
    687 		 * printable.
    688 		 */
    689 		ixgbe_get_bypass_time(&year, &sec);
    690 		days = time / SEC_PER_DAY;
    691 		for (i = 11; days < day_mon[LEAP_YR(year)][i]; i--)
    692 			continue;
    693 		mon = i + 1;    /* display month as 1-12 */
    694 		time -= (day_mon[LEAP_YR(year)][i] * SEC_PER_DAY);
    695 		days = (time / SEC_PER_DAY) + 1;  /* first day is 1 */
    696 		time %= SEC_PER_DAY;
    697 		hours = time / (60 * 60);
    698 		time %= (60 * 60);
    699 		min = time / 60;
    700 		sec = time % 60;
    701 		device_printf(adapter->dev,
    702 		    "UT %02d/%02d %02d:%02d:%02d %8.8s -> %7.7s\n",
    703 		    mon, days, hours, min, sec, event_str[event],
    704 		    action_str[action]);
    705 		cmd = BYPASS_PAGE_CTL2 | BYPASS_WE | BYPASS_CTL2_RW;
    706 		cmd |= ((eeprom[count].clear_off + 3)
    707 		    << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
    708 		cmd |= ((eeprom[count].logs & ~BYPASS_LOG_CLEAR_M) >> 24);
    709 
    710 		ixgbe_bypass_mutex_enter(adapter);
    711 
    712 		error = hw->mac.ops.bypass_rw(hw, cmd, &status);
    713 
    714 		/* wait for the write to stick */
    715 		msec_delay(100);
    716 
    717 		ixgbe_bypass_mutex_clear(adapter);
    718 
    719 		if (error)
    720 			return (EINVAL);
    721 	}
    722 
    723 	status = 0; /* reset */
    724 	/* Another log command can now run */
    725 	while (atomic_cas_uint(&adapter->bypass.log, 1, 0) == 0)
    726 		usec_delay(3000);
    727 	return(error);
    728 
    729 unlock_err:
    730 	ixgbe_bypass_mutex_clear(adapter);
    731 	status = 0; /* reset */
    732 	while (atomic_cas_uint(&adapter->bypass.log, 1, 0) == 0)
    733 		usec_delay(3000);
    734 	return (EINVAL);
    735 } /* ixgbe_bp_log */
    736 
    737 /************************************************************************
    738  * ixgbe_bypass_init - Set up infrastructure for the bypass feature
    739  *
    740  *   Do time and sysctl initialization here.  This feature is
    741  *   only enabled for the first port of a bypass adapter.
    742  ************************************************************************/
    743 void
    744 ixgbe_bypass_init(struct adapter *adapter)
    745 {
    746 	struct ixgbe_hw        *hw = &adapter->hw;
    747 	device_t               dev = adapter->dev;
    748 	u32                    mask, value, sec, year;
    749 	struct                 sysctllog **log;
    750 	const struct sysctlnode *rnode, *cnode;
    751 
    752 	if (!(adapter->feat_cap & IXGBE_FEATURE_BYPASS))
    753 		return;
    754 
    755 	/* First set up time for the hardware */
    756 	ixgbe_get_bypass_time(&year, &sec);
    757 
    758 	mask = BYPASS_CTL1_TIME_M
    759 	     | BYPASS_CTL1_VALID_M
    760 	     | BYPASS_CTL1_OFFTRST_M;
    761 
    762 	value = (sec & BYPASS_CTL1_TIME_M)
    763 	      | BYPASS_CTL1_VALID
    764 	      | BYPASS_CTL1_OFFTRST;
    765 
    766 	ixgbe_bypass_mutex_enter(adapter);
    767 	hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL1, mask, value);
    768 	ixgbe_bypass_mutex_clear(adapter);
    769 
    770 	/* Now set up the SYSCTL infrastructure */
    771 	log = &adapter->sysctllog;
    772 	if ((rnode = ixgbe_sysctl_instance(adapter)) == NULL) {
    773 		aprint_error_dev(dev, "could not create sysctl root\n");
    774 		return;
    775 	}
    776 
    777 	/*
    778 	 * The log routine is kept separate from the other
    779 	 * children so a general display command like:
    780 	 * `sysctl dev.ix.0.bypass` will not show the log.
    781 	 */
    782 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    783 	    CTLTYPE_INT, "bypass_log", SYSCTL_DESCR("Bypass Log"),
    784 	    ixgbe_bp_log, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    785 
    786 	/* All other setting are hung from the 'bypass' node */
    787 	sysctl_createv(log, 0, &rnode, &rnode, 0,
    788 	    CTLTYPE_NODE, "bypass", SYSCTL_DESCR("Bypass"),
    789 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
    790 
    791 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READONLY,
    792 	    CTLTYPE_INT, "version", SYSCTL_DESCR("Bypass Version"),
    793 	    ixgbe_bp_version, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    794 
    795 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    796 	    CTLTYPE_INT, "state", SYSCTL_DESCR("Bypass State"),
    797 	    ixgbe_bp_set_state, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    798 
    799 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    800 	    CTLTYPE_INT, "timeout", SYSCTL_DESCR("Bypass Timeout"),
    801 	    ixgbe_bp_timeout, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    802 
    803 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    804 	    CTLTYPE_INT, "main_on", SYSCTL_DESCR("Bypass Main On"),
    805 	    ixgbe_bp_main_on, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    806 
    807 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    808 	    CTLTYPE_INT, "main_off", SYSCTL_DESCR("Bypass Main Off"),
    809 	    ixgbe_bp_main_off, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    810 
    811 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    812 	    CTLTYPE_INT, "aux_on", SYSCTL_DESCR("Bypass Aux On"),
    813 	    ixgbe_bp_aux_on, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    814 
    815 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    816 	    CTLTYPE_INT, "aux_off", SYSCTL_DESCR("Bypass Aux Off"),
    817 	    ixgbe_bp_aux_off, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    818 
    819 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    820 	    CTLTYPE_INT, "wd_set", SYSCTL_DESCR("Set BP Watchdog"),
    821 	    ixgbe_bp_wd_set, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    822 
    823 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    824 	    CTLTYPE_INT, "wd_reset", SYSCTL_DESCR("Bypass WD Reset"),
    825 	    ixgbe_bp_wd_reset, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    826 
    827 	adapter->feat_en |= IXGBE_FEATURE_BYPASS;
    828 
    829 	return;
    830 } /* ixgbe_bypass_init */
    831 
    832