Home | History | Annotate | Line # | Download | only in ixgbe
if_bypass.c revision 1.5
      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 327031 2017-12-20 18:15:06Z 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 != 0)
    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 != 0) || (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;
    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 	arg = 0x1 << BYPASS_WDT_ENABLE_SHIFT;
    476 	mask = BYPASS_WDT_ENABLE_M | BYPASS_WDT_VALUE_M;
    477 	switch (timeout) {
    478 	case 0: /* disables the timer */
    479 		arg = BYPASS_PAGE_CTL0;
    480 		mask = BYPASS_WDT_ENABLE_M;
    481 		break;
    482 	case 1:
    483 		arg |= BYPASS_WDT_1_5 << BYPASS_WDT_TIME_SHIFT;
    484 		break;
    485 	case 2:
    486 		arg |= BYPASS_WDT_2 << BYPASS_WDT_TIME_SHIFT;
    487 		break;
    488 	case 3:
    489 		arg |= BYPASS_WDT_3 << BYPASS_WDT_TIME_SHIFT;
    490 		break;
    491 	case 4:
    492 		arg |= BYPASS_WDT_4 << BYPASS_WDT_TIME_SHIFT;
    493 		break;
    494 	case 8:
    495 		arg |= BYPASS_WDT_8 << BYPASS_WDT_TIME_SHIFT;
    496 		break;
    497 	case 16:
    498 		arg |= BYPASS_WDT_16 << BYPASS_WDT_TIME_SHIFT;
    499 		break;
    500 	case 32:
    501 		arg |= BYPASS_WDT_32 << BYPASS_WDT_TIME_SHIFT;
    502 		break;
    503 	default:
    504 		return (EINVAL);
    505 	}
    506 
    507 	/* Set the new watchdog */
    508 	ixgbe_bypass_mutex_enter(adapter);
    509 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0, mask, arg);
    510 	ixgbe_bypass_mutex_clear(adapter);
    511 
    512 	return (error);
    513 } /* ixgbe_bp_wd_set */
    514 
    515 /************************************************************************
    516  * ixgbe_bp_wd_reset - Reset the Watchdog timer
    517  *
    518  *    To activate this it must be called with any argument.
    519  ************************************************************************/
    520 static int
    521 ixgbe_bp_wd_reset(SYSCTLFN_ARGS)
    522 {
    523 	struct sysctlnode node = *rnode;
    524 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
    525 	struct ixgbe_hw *hw = &adapter->hw;
    526 	u32             sec, year;
    527 	int             cmd, count = 0, error = 0;
    528 	int             reset_wd = 0;
    529 
    530 	node.sysctl_data = &reset_wd;
    531 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    532 	if ((error) || (newp == NULL))
    533 		return (error);
    534 
    535 	cmd = BYPASS_PAGE_CTL1 | BYPASS_WE | BYPASS_CTL1_WDT_PET;
    536 
    537 	/* Resync the FW time while writing to CTL1 anyway */
    538 	ixgbe_get_bypass_time(&year, &sec);
    539 
    540 	cmd |= (sec & BYPASS_CTL1_TIME_M) | BYPASS_CTL1_VALID;
    541 	cmd |= BYPASS_CTL1_OFFTRST;
    542 
    543 	ixgbe_bypass_wd_mutex_enter(adapter);
    544 	error = hw->mac.ops.bypass_rw(hw, cmd, &reset_wd);
    545 
    546 	/* Read until it matches what we wrote, or we time out */
    547 	do {
    548 		if (count++ > 10) {
    549 			error = IXGBE_BYPASS_FW_WRITE_FAILURE;
    550 			break;
    551 		}
    552 		error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &reset_wd);
    553 		if (error != 0) {
    554 			error = IXGBE_ERR_INVALID_ARGUMENT;
    555 			break;
    556 		}
    557 	} while (!hw->mac.ops.bypass_valid_rd(cmd, reset_wd));
    558 
    559 	reset_wd = 0;
    560 	ixgbe_bypass_wd_mutex_clear(adapter);
    561 	return (error);
    562 } /* ixgbe_bp_wd_reset */
    563 
    564 /************************************************************************
    565  * ixgbe_bp_log - Display the bypass log
    566  *
    567  *   You must pass a non-zero arg to sysctl
    568  ************************************************************************/
    569 static int
    570 ixgbe_bp_log(SYSCTLFN_ARGS)
    571 {
    572 	struct sysctlnode          node = *rnode;
    573 	struct adapter           *adapter = (struct adapter *)node.sysctl_data;
    574 	struct ixgbe_hw            *hw = &adapter->hw;
    575 	u32                        cmd, base, head;
    576 	u32                        log_off, count = 0;
    577 	static int                 status = 0;
    578 	u8                         data;
    579 	struct ixgbe_bypass_eeprom eeprom[BYPASS_MAX_LOGS];
    580 	int                        i, error = 0;
    581 
    582 	node.sysctl_data = &status;
    583 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    584 	if ((error) || (newp == NULL))
    585 		return (error);
    586 
    587 	/* Keep the log display single-threaded */
    588 	while (atomic_cas_uint(&adapter->bypass.log, 0, 1) == 0)
    589 		usec_delay(3000);
    590 
    591 	ixgbe_bypass_mutex_enter(adapter);
    592 
    593 	/* Find Current head of the log eeprom offset */
    594 	cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
    595 	cmd |= (0x1 << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
    596 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
    597 	if (error)
    598 		goto unlock_err;
    599 
    600 	/* wait for the write to stick */
    601 	msec_delay(100);
    602 
    603 	/* Now read the results */
    604 	cmd &= ~BYPASS_WE;
    605 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
    606 	if (error)
    607 		goto unlock_err;
    608 
    609 	ixgbe_bypass_mutex_clear(adapter);
    610 
    611 	base = status & BYPASS_CTL2_DATA_M;
    612 	head = (status & BYPASS_CTL2_HEAD_M) >> BYPASS_CTL2_HEAD_SHIFT;
    613 
    614 	/* address of the first log */
    615 	log_off = base + (head * 5);
    616 
    617 	/* extract all the log entries */
    618 	while (count < BYPASS_MAX_LOGS) {
    619 		eeprom[count].logs = 0;
    620 		eeprom[count].actions = 0;
    621 
    622 		/* Log 5 bytes store in on u32 and a u8 */
    623 		for (i = 0; i < 4; i++) {
    624 			ixgbe_bypass_mutex_enter(adapter);
    625 			error = hw->mac.ops.bypass_rd_eep(hw, log_off + i,
    626 			    &data);
    627 			ixgbe_bypass_mutex_clear(adapter);
    628 			if (error)
    629 				return (EINVAL);
    630 			eeprom[count].logs += data << (8 * i);
    631 		}
    632 
    633 		ixgbe_bypass_mutex_enter(adapter);
    634 		error = hw->mac.ops.bypass_rd_eep(hw,
    635 		    log_off + i, &eeprom[count].actions);
    636 		ixgbe_bypass_mutex_clear(adapter);
    637 		if (error)
    638 			return (EINVAL);
    639 
    640 		/* Quit if not a unread log */
    641 		if (!(eeprom[count].logs & BYPASS_LOG_CLEAR_M))
    642 			break;
    643 		/*
    644 		 * Log looks good so store the address where it's
    645 		 * Unread Log bit is so we can clear it after safely
    646 		 * pulling out all of the log data.
    647 		 */
    648 		eeprom[count].clear_off = log_off;
    649 
    650 		count++;
    651 		head = head ? head - 1 : BYPASS_MAX_LOGS;
    652 		log_off = base + (head * 5);
    653 	}
    654 
    655 	/* reverse order (oldest first) for output */
    656 	while (count--) {
    657 		int year;
    658 		u32 mon, days, hours, min, sec;
    659 		u32 time = eeprom[count].logs & BYPASS_LOG_TIME_M;
    660 		u32 event = (eeprom[count].logs & BYPASS_LOG_EVENT_M) >>
    661 		    BYPASS_LOG_EVENT_SHIFT;
    662 		u8 action =  eeprom[count].actions & BYPASS_LOG_ACTION_M;
    663 		u16 day_mon[2][13] = {
    664 		  {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
    665 		  {0, 31, 59, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
    666 		};
    667 		const char *event_str[] = {"unknown", "main on", "aux on",
    668 		    "main off", "aux off", "WDT", "user" };
    669 		const char *action_str[] = {"ignore", "normal", "bypass",
    670 					    "isolate",};
    671 
    672 		/* verify vaild data  1 - 6 */
    673 		if (event < BYPASS_EVENT_MAIN_ON || event > BYPASS_EVENT_USR)
    674 			event = 0;
    675 
    676 		/*
    677 		 * time is in sec's this year, so convert to something
    678 		 * printable.
    679 		 */
    680 		ixgbe_get_bypass_time(&year, &sec);
    681 		days = time / SEC_PER_DAY;
    682 		for (i = 11; days < day_mon[LEAP_YR(year)][i]; i--)
    683 			continue;
    684 		mon = i + 1;    /* display month as 1-12 */
    685 		time -= (day_mon[LEAP_YR(year)][i] * SEC_PER_DAY);
    686 		days = (time / SEC_PER_DAY) + 1;  /* first day is 1 */
    687 		time %= SEC_PER_DAY;
    688 		hours = time / (60 * 60);
    689 		time %= (60 * 60);
    690 		min = time / 60;
    691 		sec = time % 60;
    692 		device_printf(adapter->dev,
    693 		    "UT %02d/%02d %02d:%02d:%02d %8.8s -> %7.7s\n",
    694 		    mon, days, hours, min, sec, event_str[event],
    695 		    action_str[action]);
    696 		cmd = BYPASS_PAGE_CTL2 | BYPASS_WE | BYPASS_CTL2_RW;
    697 		cmd |= ((eeprom[count].clear_off + 3)
    698 		    << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
    699 		cmd |= ((eeprom[count].logs & ~BYPASS_LOG_CLEAR_M) >> 24);
    700 
    701 		ixgbe_bypass_mutex_enter(adapter);
    702 
    703 		error = hw->mac.ops.bypass_rw(hw, cmd, &status);
    704 
    705 		/* wait for the write to stick */
    706 		msec_delay(100);
    707 
    708 		ixgbe_bypass_mutex_clear(adapter);
    709 
    710 		if (error)
    711 			return (EINVAL);
    712 	}
    713 
    714 	status = 0; /* reset */
    715 	/* Another log command can now run */
    716 	while (atomic_cas_uint(&adapter->bypass.log, 1, 0) == 0)
    717 		usec_delay(3000);
    718 	return (error);
    719 
    720 unlock_err:
    721 	ixgbe_bypass_mutex_clear(adapter);
    722 	status = 0; /* reset */
    723 	while (atomic_cas_uint(&adapter->bypass.log, 1, 0) == 0)
    724 		usec_delay(3000);
    725 	return (EINVAL);
    726 } /* ixgbe_bp_log */
    727 
    728 /************************************************************************
    729  * ixgbe_bypass_init - Set up infrastructure for the bypass feature
    730  *
    731  *   Do time and sysctl initialization here.  This feature is
    732  *   only enabled for the first port of a bypass adapter.
    733  ************************************************************************/
    734 void
    735 ixgbe_bypass_init(struct adapter *adapter)
    736 {
    737 	struct ixgbe_hw        *hw = &adapter->hw;
    738 	device_t               dev = adapter->dev;
    739 	struct                 sysctllog **log;
    740 	const struct sysctlnode *rnode, *cnode;
    741 	u32                    mask, value, sec, year;
    742 
    743 	if (!(adapter->feat_cap & IXGBE_FEATURE_BYPASS))
    744 		return;
    745 
    746 	/* First set up time for the hardware */
    747 	ixgbe_get_bypass_time(&year, &sec);
    748 
    749 	mask = BYPASS_CTL1_TIME_M
    750 	     | BYPASS_CTL1_VALID_M
    751 	     | BYPASS_CTL1_OFFTRST_M;
    752 
    753 	value = (sec & BYPASS_CTL1_TIME_M)
    754 	      | BYPASS_CTL1_VALID
    755 	      | BYPASS_CTL1_OFFTRST;
    756 
    757 	ixgbe_bypass_mutex_enter(adapter);
    758 	hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL1, mask, value);
    759 	ixgbe_bypass_mutex_clear(adapter);
    760 
    761 	/* Now set up the SYSCTL infrastructure */
    762 	log = &adapter->sysctllog;
    763 	if ((rnode = ixgbe_sysctl_instance(adapter)) == NULL) {
    764 		aprint_error_dev(dev, "could not create sysctl root\n");
    765 		return;
    766 	}
    767 
    768 	/*
    769 	 * The log routine is kept separate from the other
    770 	 * children so a general display command like:
    771 	 * `sysctl dev.ix.0.bypass` will not show the log.
    772 	 */
    773 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    774 	    CTLTYPE_INT, "bypass_log", SYSCTL_DESCR("Bypass Log"),
    775 	    ixgbe_bp_log, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    776 
    777 	/* All other setting are hung from the 'bypass' node */
    778 	sysctl_createv(log, 0, &rnode, &rnode, 0,
    779 	    CTLTYPE_NODE, "bypass", SYSCTL_DESCR("Bypass"),
    780 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
    781 
    782 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READONLY,
    783 	    CTLTYPE_INT, "version", SYSCTL_DESCR("Bypass Version"),
    784 	    ixgbe_bp_version, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    785 
    786 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    787 	    CTLTYPE_INT, "state", SYSCTL_DESCR("Bypass State"),
    788 	    ixgbe_bp_set_state, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    789 
    790 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    791 	    CTLTYPE_INT, "timeout", SYSCTL_DESCR("Bypass Timeout"),
    792 	    ixgbe_bp_timeout, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    793 
    794 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    795 	    CTLTYPE_INT, "main_on", SYSCTL_DESCR("Bypass Main On"),
    796 	    ixgbe_bp_main_on, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    797 
    798 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    799 	    CTLTYPE_INT, "main_off", SYSCTL_DESCR("Bypass Main Off"),
    800 	    ixgbe_bp_main_off, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    801 
    802 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    803 	    CTLTYPE_INT, "aux_on", SYSCTL_DESCR("Bypass Aux On"),
    804 	    ixgbe_bp_aux_on, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    805 
    806 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    807 	    CTLTYPE_INT, "aux_off", SYSCTL_DESCR("Bypass Aux Off"),
    808 	    ixgbe_bp_aux_off, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    809 
    810 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    811 	    CTLTYPE_INT, "wd_set", SYSCTL_DESCR("Set BP Watchdog"),
    812 	    ixgbe_bp_wd_set, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    813 
    814 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
    815 	    CTLTYPE_INT, "wd_reset", SYSCTL_DESCR("Bypass WD Reset"),
    816 	    ixgbe_bp_wd_reset, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
    817 
    818 	adapter->feat_en |= IXGBE_FEATURE_BYPASS;
    819 } /* ixgbe_bypass_init */
    820 
    821