xref: /freebsd/sys/dev/ixgbe/if_bypass.c (revision 7ef62cebc2f965b0f640263e179276928885e33d)
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$*/
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 ixgbe_softc *sc)
47 {
48 	while (atomic_cmpset_int(&sc->bypass.low, 0, 1) == 0)
49 		usec_delay(3000);
50 	while (atomic_cmpset_int(&sc->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 ixgbe_softc *sc)
60 {
61 	while (atomic_cmpset_int(&sc->bypass.high, 1, 0) == 0)
62 		usec_delay(6000);
63 	while (atomic_cmpset_int(&sc->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 ixgbe_softc *sc)
75 {
76 	while (atomic_cmpset_int(&sc->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 ixgbe_softc *sc)
86 {
87 	while (atomic_cmpset_int(&sc->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(SYSCTL_HANDLER_ARGS)
117 {
118 	struct ixgbe_softc  *sc = (struct ixgbe_softc *) arg1;
119 	struct ixgbe_hw *hw = &sc->hw;
120 	int             error = 0;
121 	static int      version = 0;
122 	u32             cmd;
123 
124 	ixgbe_bypass_mutex_enter(sc);
125 	cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
126 	cmd |= (BYPASS_EEPROM_VER_ADD << BYPASS_CTL2_OFFSET_SHIFT) &
127 	    BYPASS_CTL2_OFFSET_M;
128 	if ((error = hw->mac.ops.bypass_rw(hw, cmd, &version) != 0))
129 		goto err;
130 	msec_delay(100);
131 	cmd &= ~BYPASS_WE;
132 	if ((error = hw->mac.ops.bypass_rw(hw, cmd, &version) != 0))
133 		goto err;
134 	ixgbe_bypass_mutex_clear(sc);
135 	version &= BYPASS_CTL2_DATA_M;
136 	error = sysctl_handle_int(oidp, &version, 0, req);
137 	return (error);
138 err:
139 	ixgbe_bypass_mutex_clear(sc);
140 	return (error);
141 
142 } /* ixgbe_bp_version */
143 
144 /************************************************************************
145  * ixgbe_bp_set_state
146  *
147  *   Show/Set the Bypass State:
148  *	1 = NORMAL
149  *	2 = BYPASS
150  *	3 = ISOLATE
151  *
152  *	With no argument the state is displayed,
153  *	passing a value will set it.
154  ************************************************************************/
155 static int
156 ixgbe_bp_set_state(SYSCTL_HANDLER_ARGS)
157 {
158 	struct ixgbe_softc  *sc = (struct ixgbe_softc *) arg1;
159 	struct ixgbe_hw *hw = &sc->hw;
160 	int             error = 0;
161 	static int      state = 0;
162 
163 	/* Get the current state */
164 	ixgbe_bypass_mutex_enter(sc);
165 	error = hw->mac.ops.bypass_rw(hw,
166 	    BYPASS_PAGE_CTL0, &state);
167 	ixgbe_bypass_mutex_clear(sc);
168 	if (error != 0)
169 		return (error);
170 	state = (state >> BYPASS_STATUS_OFF_SHIFT) & 0x3;
171 
172 	error = sysctl_handle_int(oidp, &state, 0, req);
173 	if ((error != 0) || (req->newptr == NULL))
174 		return (error);
175 
176 	/* Sanity check new state */
177 	switch (state) {
178 	case BYPASS_NORM:
179 	case BYPASS_BYPASS:
180 	case BYPASS_ISOLATE:
181 		break;
182 	default:
183 		return (EINVAL);
184 	}
185 	ixgbe_bypass_mutex_enter(sc);
186 	if ((error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
187 	    BYPASS_MODE_OFF_M, state) != 0))
188 		goto out;
189 	/* Set AUTO back on so FW can receive events */
190 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
191 	    BYPASS_MODE_OFF_M, BYPASS_AUTO);
192 out:
193 	ixgbe_bypass_mutex_clear(sc);
194 	usec_delay(6000);
195 	return (error);
196 } /* ixgbe_bp_set_state */
197 
198 /************************************************************************
199  * The following routines control the operational
200  * "rules" of the feature, what behavior will occur
201  * when particular events occur.
202  * 	Values are:
203  *		0 - no change for the event (NOP)
204  *		1 - go to Normal operation
205  *		2 - go to Bypass operation
206  *		3 - go to Isolate operation
207  * Calling the entry with no argument just displays
208  * the current rule setting.
209  ************************************************************************/
210 
211 /************************************************************************
212  * ixgbe_bp_timeout
213  *
214  * This is to set the Rule for the watchdog,
215  * not the actual watchdog timeout value.
216  ************************************************************************/
217 static int
218 ixgbe_bp_timeout(SYSCTL_HANDLER_ARGS)
219 {
220 	struct ixgbe_softc  *sc = (struct ixgbe_softc *) arg1;
221 	struct ixgbe_hw *hw = &sc->hw;
222 	int             error = 0;
223 	static int      timeout = 0;
224 
225 	/* Get the current value */
226 	ixgbe_bypass_mutex_enter(sc);
227 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &timeout);
228 	ixgbe_bypass_mutex_clear(sc);
229 	if (error)
230 		return (error);
231 	timeout = (timeout >> BYPASS_WDTIMEOUT_SHIFT) & 0x3;
232 
233 	error = sysctl_handle_int(oidp, &timeout, 0, req);
234 	if ((error) || (req->newptr == NULL))
235 		return (error);
236 
237 	/* Sanity check on the setting */
238 	switch (timeout) {
239 	case BYPASS_NOP:
240 	case BYPASS_NORM:
241 	case BYPASS_BYPASS:
242 	case BYPASS_ISOLATE:
243 		break;
244 	default:
245 		return (EINVAL);
246 	}
247 
248 	/* Set the new state */
249 	ixgbe_bypass_mutex_enter(sc);
250 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
251 	    BYPASS_WDTIMEOUT_M, timeout << BYPASS_WDTIMEOUT_SHIFT);
252 	ixgbe_bypass_mutex_clear(sc);
253 	usec_delay(6000);
254 	return (error);
255 } /* ixgbe_bp_timeout */
256 
257 /************************************************************************
258  * ixgbe_bp_main_on
259  ************************************************************************/
260 static int
261 ixgbe_bp_main_on(SYSCTL_HANDLER_ARGS)
262 {
263 	struct ixgbe_softc  *sc = (struct ixgbe_softc *) arg1;
264 	struct ixgbe_hw *hw = &sc->hw;
265 	int             error = 0;
266 	static int      main_on = 0;
267 
268 	ixgbe_bypass_mutex_enter(sc);
269 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &main_on);
270 	main_on = (main_on >> BYPASS_MAIN_ON_SHIFT) & 0x3;
271 	ixgbe_bypass_mutex_clear(sc);
272 	if (error)
273 		return (error);
274 
275 	error = sysctl_handle_int(oidp, &main_on, 0, req);
276 	if ((error) || (req->newptr == NULL))
277 		return (error);
278 
279 	/* Sanity check on the setting */
280 	switch (main_on) {
281 	case BYPASS_NOP:
282 	case BYPASS_NORM:
283 	case BYPASS_BYPASS:
284 	case BYPASS_ISOLATE:
285 		break;
286 	default:
287 		return (EINVAL);
288 	}
289 
290 	/* Set the new state */
291 	ixgbe_bypass_mutex_enter(sc);
292 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
293 	    BYPASS_MAIN_ON_M, main_on << BYPASS_MAIN_ON_SHIFT);
294 	ixgbe_bypass_mutex_clear(sc);
295 	usec_delay(6000);
296 	return (error);
297 } /* ixgbe_bp_main_on */
298 
299 /************************************************************************
300  * ixgbe_bp_main_off
301  ************************************************************************/
302 static int
303 ixgbe_bp_main_off(SYSCTL_HANDLER_ARGS)
304 {
305 	struct ixgbe_softc  *sc = (struct ixgbe_softc *) arg1;
306 	struct ixgbe_hw *hw = &sc->hw;
307 	int             error = 0;
308 	static int      main_off = 0;
309 
310 	ixgbe_bypass_mutex_enter(sc);
311 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &main_off);
312 	ixgbe_bypass_mutex_clear(sc);
313 	if (error)
314 		return (error);
315 	main_off = (main_off >> BYPASS_MAIN_OFF_SHIFT) & 0x3;
316 
317 	error = sysctl_handle_int(oidp, &main_off, 0, req);
318 	if ((error) || (req->newptr == NULL))
319 		return (error);
320 
321 	/* Sanity check on the setting */
322 	switch (main_off) {
323 	case BYPASS_NOP:
324 	case BYPASS_NORM:
325 	case BYPASS_BYPASS:
326 	case BYPASS_ISOLATE:
327 		break;
328 	default:
329 		return (EINVAL);
330 	}
331 
332 	/* Set the new state */
333 	ixgbe_bypass_mutex_enter(sc);
334 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
335 	    BYPASS_MAIN_OFF_M, main_off << BYPASS_MAIN_OFF_SHIFT);
336 	ixgbe_bypass_mutex_clear(sc);
337 	usec_delay(6000);
338 	return (error);
339 } /* ixgbe_bp_main_off */
340 
341 /************************************************************************
342  * ixgbe_bp_aux_on
343  ************************************************************************/
344 static int
345 ixgbe_bp_aux_on(SYSCTL_HANDLER_ARGS)
346 {
347 	struct ixgbe_softc  *sc = (struct ixgbe_softc *) arg1;
348 	struct ixgbe_hw *hw = &sc->hw;
349 	int             error = 0;
350 	static int      aux_on = 0;
351 
352 	ixgbe_bypass_mutex_enter(sc);
353 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &aux_on);
354 	ixgbe_bypass_mutex_clear(sc);
355 	if (error)
356 		return (error);
357 	aux_on = (aux_on >> BYPASS_AUX_ON_SHIFT) & 0x3;
358 
359 	error = sysctl_handle_int(oidp, &aux_on, 0, req);
360 	if ((error) || (req->newptr == NULL))
361 		return (error);
362 
363 	/* Sanity check on the setting */
364 	switch (aux_on) {
365 	case BYPASS_NOP:
366 	case BYPASS_NORM:
367 	case BYPASS_BYPASS:
368 	case BYPASS_ISOLATE:
369 		break;
370 	default:
371 		return (EINVAL);
372 	}
373 
374 	/* Set the new state */
375 	ixgbe_bypass_mutex_enter(sc);
376 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
377 	    BYPASS_AUX_ON_M, aux_on << BYPASS_AUX_ON_SHIFT);
378 	ixgbe_bypass_mutex_clear(sc);
379 	usec_delay(6000);
380 	return (error);
381 } /* ixgbe_bp_aux_on */
382 
383 /************************************************************************
384  * ixgbe_bp_aux_off
385  ************************************************************************/
386 static int
387 ixgbe_bp_aux_off(SYSCTL_HANDLER_ARGS)
388 {
389 	struct ixgbe_softc  *sc = (struct ixgbe_softc *) arg1;
390 	struct ixgbe_hw *hw = &sc->hw;
391 	int             error = 0;
392 	static int      aux_off = 0;
393 
394 	ixgbe_bypass_mutex_enter(sc);
395 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &aux_off);
396 	ixgbe_bypass_mutex_clear(sc);
397 	if (error)
398 		return (error);
399 	aux_off = (aux_off >> BYPASS_AUX_OFF_SHIFT) & 0x3;
400 
401 	error = sysctl_handle_int(oidp, &aux_off, 0, req);
402 	if ((error) || (req->newptr == NULL))
403 		return (error);
404 
405 	/* Sanity check on the setting */
406 	switch (aux_off) {
407 	case BYPASS_NOP:
408 	case BYPASS_NORM:
409 	case BYPASS_BYPASS:
410 	case BYPASS_ISOLATE:
411 		break;
412 	default:
413 		return (EINVAL);
414 	}
415 
416 	/* Set the new state */
417 	ixgbe_bypass_mutex_enter(sc);
418 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
419 	    BYPASS_AUX_OFF_M, aux_off << BYPASS_AUX_OFF_SHIFT);
420 	ixgbe_bypass_mutex_clear(sc);
421 	usec_delay(6000);
422 	return (error);
423 } /* ixgbe_bp_aux_off */
424 
425 /************************************************************************
426  * ixgbe_bp_wd_set - Set the Watchdog timer value
427  *
428  *   Valid settings are:
429  *	- 0 will disable the watchdog
430  *	- 1, 2, 3, 4, 8, 16, 32
431  *	- anything else is invalid and will be ignored
432  ************************************************************************/
433 static int
434 ixgbe_bp_wd_set(SYSCTL_HANDLER_ARGS)
435 {
436 	struct ixgbe_softc  *sc = (struct ixgbe_softc *) arg1;
437 	struct ixgbe_hw *hw = &sc->hw;
438 	int             error, tmp;
439 	static int      timeout = 0;
440 	u32             mask, arg;
441 
442 	/* Get the current hardware value */
443 	ixgbe_bypass_mutex_enter(sc);
444 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &tmp);
445 	ixgbe_bypass_mutex_clear(sc);
446 	if (error)
447 		return (error);
448 	/*
449 	 * If armed keep the displayed value,
450 	 * else change the display to zero.
451 	 */
452 	if ((tmp & (0x1 << BYPASS_WDT_ENABLE_SHIFT)) == 0)
453 		timeout = 0;
454 
455 	error = sysctl_handle_int(oidp, &timeout, 0, req);
456 	if ((error) || (req->newptr == NULL))
457 		return (error);
458 
459 	arg = 0x1 << BYPASS_WDT_ENABLE_SHIFT;
460 	mask = BYPASS_WDT_ENABLE_M | BYPASS_WDT_VALUE_M;
461 	switch (timeout) {
462 	case 0: /* disables the timer */
463 		arg = BYPASS_PAGE_CTL0;
464 		mask = BYPASS_WDT_ENABLE_M;
465 		break;
466 	case 1:
467 		arg |= BYPASS_WDT_1_5 << BYPASS_WDT_TIME_SHIFT;
468 		break;
469 	case 2:
470 		arg |= BYPASS_WDT_2 << BYPASS_WDT_TIME_SHIFT;
471 		break;
472 	case 3:
473 		arg |= BYPASS_WDT_3 << BYPASS_WDT_TIME_SHIFT;
474 		break;
475 	case 4:
476 		arg |= BYPASS_WDT_4 << BYPASS_WDT_TIME_SHIFT;
477 		break;
478 	case 8:
479 		arg |= BYPASS_WDT_8 << BYPASS_WDT_TIME_SHIFT;
480 		break;
481 	case 16:
482 		arg |= BYPASS_WDT_16 << BYPASS_WDT_TIME_SHIFT;
483 		break;
484 	case 32:
485 		arg |= BYPASS_WDT_32 << BYPASS_WDT_TIME_SHIFT;
486 		break;
487 	default:
488 		return (EINVAL);
489 	}
490 
491 	/* Set the new watchdog */
492 	ixgbe_bypass_mutex_enter(sc);
493 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0, mask, arg);
494 	ixgbe_bypass_mutex_clear(sc);
495 
496 	return (error);
497 } /* ixgbe_bp_wd_set */
498 
499 /************************************************************************
500  * ixgbe_bp_wd_reset - Reset the Watchdog timer
501  *
502  *    To activate this it must be called with any argument.
503  ************************************************************************/
504 static int
505 ixgbe_bp_wd_reset(SYSCTL_HANDLER_ARGS)
506 {
507 	struct ixgbe_softc  *sc = (struct ixgbe_softc *) arg1;
508 	struct ixgbe_hw *hw = &sc->hw;
509 	u32             sec, year;
510 	int             cmd, count = 0, error = 0;
511 	int             reset_wd = 0;
512 
513 	error = sysctl_handle_int(oidp, &reset_wd, 0, req);
514 	if ((error) || (req->newptr == NULL))
515 		return (error);
516 
517 	cmd = BYPASS_PAGE_CTL1 | BYPASS_WE | BYPASS_CTL1_WDT_PET;
518 
519 	/* Resync the FW time while writing to CTL1 anyway */
520 	ixgbe_get_bypass_time(&year, &sec);
521 
522 	cmd |= (sec & BYPASS_CTL1_TIME_M) | BYPASS_CTL1_VALID;
523 	cmd |= BYPASS_CTL1_OFFTRST;
524 
525 	ixgbe_bypass_wd_mutex_enter(sc);
526 	error = hw->mac.ops.bypass_rw(hw, cmd, &reset_wd);
527 
528 	/* Read until it matches what we wrote, or we time out */
529 	do {
530 		if (count++ > 10) {
531 			error = IXGBE_BYPASS_FW_WRITE_FAILURE;
532 			break;
533 		}
534 		error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &reset_wd);
535 		if (error != 0) {
536 			error = IXGBE_ERR_INVALID_ARGUMENT;
537 			break;
538 		}
539 	} while (!hw->mac.ops.bypass_valid_rd(cmd, reset_wd));
540 
541 	reset_wd = 0;
542 	ixgbe_bypass_wd_mutex_clear(sc);
543 	return (error);
544 } /* ixgbe_bp_wd_reset */
545 
546 /************************************************************************
547  * ixgbe_bp_log - Display the bypass log
548  *
549  *   You must pass a non-zero arg to sysctl
550  ************************************************************************/
551 static int
552 ixgbe_bp_log(SYSCTL_HANDLER_ARGS)
553 {
554 	struct ixgbe_softc             *sc = (struct ixgbe_softc *) arg1;
555 	struct ixgbe_hw            *hw = &sc->hw;
556 	u32                        cmd, base, head;
557 	u32                        log_off, count = 0;
558 	static int                 status = 0;
559 	u8                         data;
560 	struct ixgbe_bypass_eeprom eeprom[BYPASS_MAX_LOGS];
561 	int                        i, error = 0;
562 
563 	error = sysctl_handle_int(oidp, &status, 0, req);
564 	if ((error) || (req->newptr == NULL))
565 		return (error);
566 
567 	/* Keep the log display single-threaded */
568 	while (atomic_cmpset_int(&sc->bypass.log, 0, 1) == 0)
569 		usec_delay(3000);
570 
571 	ixgbe_bypass_mutex_enter(sc);
572 
573 	/* Find Current head of the log eeprom offset */
574 	cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
575 	cmd |= (0x1 << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
576 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
577 	if (error)
578 		goto unlock_err;
579 
580 	/* wait for the write to stick */
581 	msec_delay(100);
582 
583 	/* Now read the results */
584 	cmd &= ~BYPASS_WE;
585 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
586 	if (error)
587 		goto unlock_err;
588 
589 	ixgbe_bypass_mutex_clear(sc);
590 
591 	base = status & BYPASS_CTL2_DATA_M;
592 	head = (status & BYPASS_CTL2_HEAD_M) >> BYPASS_CTL2_HEAD_SHIFT;
593 
594 	/* address of the first log */
595 	log_off = base + (head * 5);
596 
597 	/* extract all the log entries */
598 	while (count < BYPASS_MAX_LOGS) {
599 		eeprom[count].logs = 0;
600 		eeprom[count].actions = 0;
601 
602 		/* Log 5 bytes store in on u32 and a u8 */
603 		for (i = 0; i < 4; i++) {
604 			ixgbe_bypass_mutex_enter(sc);
605 			error = hw->mac.ops.bypass_rd_eep(hw, log_off + i,
606 			    &data);
607 			ixgbe_bypass_mutex_clear(sc);
608 			if (error)
609 				return (EINVAL);
610 			eeprom[count].logs += data << (8 * i);
611 		}
612 
613 		ixgbe_bypass_mutex_enter(sc);
614 		error = hw->mac.ops.bypass_rd_eep(hw,
615 		    log_off + i, &eeprom[count].actions);
616 		ixgbe_bypass_mutex_clear(sc);
617 		if (error)
618 			return (EINVAL);
619 
620 		/* Quit if not a unread log */
621 		if (!(eeprom[count].logs & BYPASS_LOG_CLEAR_M))
622 			break;
623 		/*
624 		 * Log looks good so store the address where it's
625 		 * Unread Log bit is so we can clear it after safely
626 		 * pulling out all of the log data.
627 		 */
628 		eeprom[count].clear_off = log_off;
629 
630 		count++;
631 		head = head ? head - 1 : BYPASS_MAX_LOGS;
632 		log_off = base + (head * 5);
633 	}
634 
635 	/* reverse order (oldest first) for output */
636 	while (count--) {
637 		int year;
638 		u32 mon, days, hours, min, sec;
639 		u32 time = eeprom[count].logs & BYPASS_LOG_TIME_M;
640 		u32 event = (eeprom[count].logs & BYPASS_LOG_EVENT_M) >>
641 		    BYPASS_LOG_EVENT_SHIFT;
642 		u8 action =  eeprom[count].actions & BYPASS_LOG_ACTION_M;
643 		u16 day_mon[2][13] = {
644 		  {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
645 		  {0, 31, 59, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
646 		};
647 		char *event_str[] = {"unknown", "main on", "aux on",
648 		    "main off", "aux off", "WDT", "user" };
649 		char *action_str[] = {"ignore", "normal", "bypass", "isolate",};
650 
651 		/* verify vaild data  1 - 6 */
652 		if (event < BYPASS_EVENT_MAIN_ON || event > BYPASS_EVENT_USR)
653 			event = 0;
654 
655 		/*
656 		 * time is in sec's this year, so convert to something
657 		 * printable.
658 		 */
659 		ixgbe_get_bypass_time(&year, &sec);
660 		days = time / SEC_PER_DAY;
661 		for (i = 11; days < day_mon[LEAP_YR(year)][i]; i--)
662 			continue;
663 		mon = i + 1;    /* display month as 1-12 */
664 		time -= (day_mon[LEAP_YR(year)][i] * SEC_PER_DAY);
665 		days = (time / SEC_PER_DAY) + 1;  /* first day is 1 */
666 		time %= SEC_PER_DAY;
667 		hours = time / (60 * 60);
668 		time %= (60 * 60);
669 		min = time / 60;
670 		sec = time % 60;
671 		device_printf(sc->dev,
672 		    "UT %02d/%02d %02d:%02d:%02d %8.8s -> %7.7s\n",
673 		    mon, days, hours, min, sec, event_str[event],
674 		    action_str[action]);
675 		cmd = BYPASS_PAGE_CTL2 | BYPASS_WE | BYPASS_CTL2_RW;
676 		cmd |= ((eeprom[count].clear_off + 3)
677 		    << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
678 		cmd |= ((eeprom[count].logs & ~BYPASS_LOG_CLEAR_M) >> 24);
679 
680 		ixgbe_bypass_mutex_enter(sc);
681 
682 		error = hw->mac.ops.bypass_rw(hw, cmd, &status);
683 
684 		/* wait for the write to stick */
685 		msec_delay(100);
686 
687 		ixgbe_bypass_mutex_clear(sc);
688 
689 		if (error)
690 			return (EINVAL);
691 	}
692 
693 	status = 0; /* reset */
694 	/* Another log command can now run */
695 	while (atomic_cmpset_int(&sc->bypass.log, 1, 0) == 0)
696 		usec_delay(3000);
697 	return (error);
698 
699 unlock_err:
700 	ixgbe_bypass_mutex_clear(sc);
701 	status = 0; /* reset */
702 	while (atomic_cmpset_int(&sc->bypass.log, 1, 0) == 0)
703 		usec_delay(3000);
704 	return (EINVAL);
705 } /* ixgbe_bp_log */
706 
707 /************************************************************************
708  * ixgbe_bypass_init - Set up infrastructure for the bypass feature
709  *
710  *   Do time and sysctl initialization here.  This feature is
711  *   only enabled for the first port of a bypass adapter.
712  ************************************************************************/
713 void
714 ixgbe_bypass_init(struct ixgbe_softc *sc)
715 {
716 	struct ixgbe_hw        *hw = &sc->hw;
717 	device_t               dev = sc->dev;
718 	struct sysctl_oid      *bp_node;
719 	struct sysctl_oid_list *bp_list;
720 	u32                    mask, value, sec, year;
721 
722 	if (!(sc->feat_cap & IXGBE_FEATURE_BYPASS))
723 		return;
724 
725 	/* First set up time for the hardware */
726 	ixgbe_get_bypass_time(&year, &sec);
727 
728 	mask = BYPASS_CTL1_TIME_M
729 	     | BYPASS_CTL1_VALID_M
730 	     | BYPASS_CTL1_OFFTRST_M;
731 
732 	value = (sec & BYPASS_CTL1_TIME_M)
733 	      | BYPASS_CTL1_VALID
734 	      | BYPASS_CTL1_OFFTRST;
735 
736 	ixgbe_bypass_mutex_enter(sc);
737 	hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL1, mask, value);
738 	ixgbe_bypass_mutex_clear(sc);
739 
740 	/* Now set up the SYSCTL infrastructure */
741 
742 	/*
743 	 * The log routine is kept separate from the other
744 	 * children so a general display command like:
745 	 * `sysctl dev.ix.0.bypass` will not show the log.
746 	 */
747 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
748 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
749 	    OID_AUTO, "bypass_log",
750 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
751 	    sc, 0, ixgbe_bp_log, "I", "Bypass Log");
752 
753 	/* All other setting are hung from the 'bypass' node */
754 	bp_node = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
755 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
756 	    OID_AUTO, "bypass", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Bypass");
757 
758 	bp_list = SYSCTL_CHILDREN(bp_node);
759 
760 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
761 	    OID_AUTO, "version", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
762 	    sc, 0, ixgbe_bp_version, "I", "Bypass Version");
763 
764 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
765 	    OID_AUTO, "state", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
766 	    sc, 0, ixgbe_bp_set_state, "I", "Bypass State");
767 
768 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
769 	    OID_AUTO, "timeout", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
770 	    sc, 0, ixgbe_bp_timeout, "I", "Bypass Timeout");
771 
772 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
773 	    OID_AUTO, "main_on", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
774 	    sc, 0, ixgbe_bp_main_on, "I", "Bypass Main On");
775 
776 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
777 	    OID_AUTO, "main_off", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
778 	    sc, 0, ixgbe_bp_main_off, "I", "Bypass Main Off");
779 
780 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
781 	    OID_AUTO, "aux_on", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
782 	    sc, 0, ixgbe_bp_aux_on, "I", "Bypass Aux On");
783 
784 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
785 	    OID_AUTO, "aux_off", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
786 	    sc, 0, ixgbe_bp_aux_off, "I", "Bypass Aux Off");
787 
788 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
789 	    OID_AUTO, "wd_set", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
790 	    sc, 0, ixgbe_bp_wd_set, "I", "Set BP Watchdog");
791 
792 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
793 	    OID_AUTO, "wd_reset", CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
794 	    sc, 0, ixgbe_bp_wd_reset, "S", "Bypass WD Reset");
795 
796 	sc->feat_en |= IXGBE_FEATURE_BYPASS;
797 } /* ixgbe_bypass_init */
798 
799