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