xref: /freebsd/sys/dev/ixgbe/if_bypass.c (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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, BYPASS_PAGE_CTL0, &state);
165 	ixgbe_bypass_mutex_clear(sc);
166 	if (error != 0)
167 		return (error);
168 	state = (state >> BYPASS_STATUS_OFF_SHIFT) & 0x3;
169 
170 	error = sysctl_handle_int(oidp, &state, 0, req);
171 	if ((error != 0) || (req->newptr == NULL))
172 		return (error);
173 
174 	/* Sanity check new state */
175 	switch (state) {
176 	case BYPASS_NORM:
177 	case BYPASS_BYPASS:
178 	case BYPASS_ISOLATE:
179 		break;
180 	default:
181 		return (EINVAL);
182 	}
183 	ixgbe_bypass_mutex_enter(sc);
184 	if ((error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
185 	    BYPASS_MODE_OFF_M, state) != 0))
186 		goto out;
187 	/* Set AUTO back on so FW can receive events */
188 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
189 	    BYPASS_MODE_OFF_M, BYPASS_AUTO);
190 out:
191 	ixgbe_bypass_mutex_clear(sc);
192 	usec_delay(6000);
193 	return (error);
194 } /* ixgbe_bp_set_state */
195 
196 /************************************************************************
197  * The following routines control the operational
198  * "rules" of the feature, what behavior will occur
199  * when particular events occur.
200  * 	Values are:
201  *		0 - no change for the event (NOP)
202  *		1 - go to Normal operation
203  *		2 - go to Bypass operation
204  *		3 - go to Isolate operation
205  * Calling the entry with no argument just displays
206  * the current rule setting.
207  ************************************************************************/
208 
209 /************************************************************************
210  * ixgbe_bp_timeout
211  *
212  * This is to set the Rule for the watchdog,
213  * not the actual watchdog timeout value.
214  ************************************************************************/
215 static int
216 ixgbe_bp_timeout(SYSCTL_HANDLER_ARGS)
217 {
218 	struct ixgbe_softc *sc = (struct ixgbe_softc *) arg1;
219 	struct ixgbe_hw *hw = &sc->hw;
220 	int error = 0;
221 	static int timeout = 0;
222 
223 	/* Get the current value */
224 	ixgbe_bypass_mutex_enter(sc);
225 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &timeout);
226 	ixgbe_bypass_mutex_clear(sc);
227 	if (error)
228 		return (error);
229 	timeout = (timeout >> BYPASS_WDTIMEOUT_SHIFT) & 0x3;
230 
231 	error = sysctl_handle_int(oidp, &timeout, 0, req);
232 	if ((error) || (req->newptr == NULL))
233 		return (error);
234 
235 	/* Sanity check on the setting */
236 	switch (timeout) {
237 	case BYPASS_NOP:
238 	case BYPASS_NORM:
239 	case BYPASS_BYPASS:
240 	case BYPASS_ISOLATE:
241 		break;
242 	default:
243 		return (EINVAL);
244 	}
245 
246 	/* Set the new state */
247 	ixgbe_bypass_mutex_enter(sc);
248 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
249 	    BYPASS_WDTIMEOUT_M, timeout << BYPASS_WDTIMEOUT_SHIFT);
250 	ixgbe_bypass_mutex_clear(sc);
251 	usec_delay(6000);
252 	return (error);
253 } /* ixgbe_bp_timeout */
254 
255 /************************************************************************
256  * ixgbe_bp_main_on
257  ************************************************************************/
258 static int
259 ixgbe_bp_main_on(SYSCTL_HANDLER_ARGS)
260 {
261 	struct ixgbe_softc *sc = (struct ixgbe_softc *) arg1;
262 	struct ixgbe_hw *hw = &sc->hw;
263 	int error = 0;
264 	static int main_on = 0;
265 
266 	ixgbe_bypass_mutex_enter(sc);
267 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &main_on);
268 	main_on = (main_on >> BYPASS_MAIN_ON_SHIFT) & 0x3;
269 	ixgbe_bypass_mutex_clear(sc);
270 	if (error)
271 		return (error);
272 
273 	error = sysctl_handle_int(oidp, &main_on, 0, req);
274 	if ((error) || (req->newptr == NULL))
275 		return (error);
276 
277 	/* Sanity check on the setting */
278 	switch (main_on) {
279 	case BYPASS_NOP:
280 	case BYPASS_NORM:
281 	case BYPASS_BYPASS:
282 	case BYPASS_ISOLATE:
283 		break;
284 	default:
285 		return (EINVAL);
286 	}
287 
288 	/* Set the new state */
289 	ixgbe_bypass_mutex_enter(sc);
290 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
291 	    BYPASS_MAIN_ON_M, main_on << BYPASS_MAIN_ON_SHIFT);
292 	ixgbe_bypass_mutex_clear(sc);
293 	usec_delay(6000);
294 	return (error);
295 } /* ixgbe_bp_main_on */
296 
297 /************************************************************************
298  * ixgbe_bp_main_off
299  ************************************************************************/
300 static int
301 ixgbe_bp_main_off(SYSCTL_HANDLER_ARGS)
302 {
303 	struct ixgbe_softc *sc = (struct ixgbe_softc *) arg1;
304 	struct ixgbe_hw *hw = &sc->hw;
305 	int error = 0;
306 	static int main_off = 0;
307 
308 	ixgbe_bypass_mutex_enter(sc);
309 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &main_off);
310 	ixgbe_bypass_mutex_clear(sc);
311 	if (error)
312 		return (error);
313 	main_off = (main_off >> BYPASS_MAIN_OFF_SHIFT) & 0x3;
314 
315 	error = sysctl_handle_int(oidp, &main_off, 0, req);
316 	if ((error) || (req->newptr == NULL))
317 		return (error);
318 
319 	/* Sanity check on the setting */
320 	switch (main_off) {
321 	case BYPASS_NOP:
322 	case BYPASS_NORM:
323 	case BYPASS_BYPASS:
324 	case BYPASS_ISOLATE:
325 		break;
326 	default:
327 		return (EINVAL);
328 	}
329 
330 	/* Set the new state */
331 	ixgbe_bypass_mutex_enter(sc);
332 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
333 	    BYPASS_MAIN_OFF_M, main_off << BYPASS_MAIN_OFF_SHIFT);
334 	ixgbe_bypass_mutex_clear(sc);
335 	usec_delay(6000);
336 	return (error);
337 } /* ixgbe_bp_main_off */
338 
339 /************************************************************************
340  * ixgbe_bp_aux_on
341  ************************************************************************/
342 static int
343 ixgbe_bp_aux_on(SYSCTL_HANDLER_ARGS)
344 {
345 	struct ixgbe_softc *sc = (struct ixgbe_softc *) arg1;
346 	struct ixgbe_hw *hw = &sc->hw;
347 	int error = 0;
348 	static int aux_on = 0;
349 
350 	ixgbe_bypass_mutex_enter(sc);
351 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &aux_on);
352 	ixgbe_bypass_mutex_clear(sc);
353 	if (error)
354 		return (error);
355 	aux_on = (aux_on >> BYPASS_AUX_ON_SHIFT) & 0x3;
356 
357 	error = sysctl_handle_int(oidp, &aux_on, 0, req);
358 	if ((error) || (req->newptr == NULL))
359 		return (error);
360 
361 	/* Sanity check on the setting */
362 	switch (aux_on) {
363 	case BYPASS_NOP:
364 	case BYPASS_NORM:
365 	case BYPASS_BYPASS:
366 	case BYPASS_ISOLATE:
367 		break;
368 	default:
369 		return (EINVAL);
370 	}
371 
372 	/* Set the new state */
373 	ixgbe_bypass_mutex_enter(sc);
374 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
375 	    BYPASS_AUX_ON_M, aux_on << BYPASS_AUX_ON_SHIFT);
376 	ixgbe_bypass_mutex_clear(sc);
377 	usec_delay(6000);
378 	return (error);
379 } /* ixgbe_bp_aux_on */
380 
381 /************************************************************************
382  * ixgbe_bp_aux_off
383  ************************************************************************/
384 static int
385 ixgbe_bp_aux_off(SYSCTL_HANDLER_ARGS)
386 {
387 	struct ixgbe_softc *sc = (struct ixgbe_softc *) arg1;
388 	struct ixgbe_hw *hw = &sc->hw;
389 	int error = 0;
390 	static int aux_off = 0;
391 
392 	ixgbe_bypass_mutex_enter(sc);
393 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &aux_off);
394 	ixgbe_bypass_mutex_clear(sc);
395 	if (error)
396 		return (error);
397 	aux_off = (aux_off >> BYPASS_AUX_OFF_SHIFT) & 0x3;
398 
399 	error = sysctl_handle_int(oidp, &aux_off, 0, req);
400 	if ((error) || (req->newptr == NULL))
401 		return (error);
402 
403 	/* Sanity check on the setting */
404 	switch (aux_off) {
405 	case BYPASS_NOP:
406 	case BYPASS_NORM:
407 	case BYPASS_BYPASS:
408 	case BYPASS_ISOLATE:
409 		break;
410 	default:
411 		return (EINVAL);
412 	}
413 
414 	/* Set the new state */
415 	ixgbe_bypass_mutex_enter(sc);
416 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
417 	    BYPASS_AUX_OFF_M, aux_off << BYPASS_AUX_OFF_SHIFT);
418 	ixgbe_bypass_mutex_clear(sc);
419 	usec_delay(6000);
420 	return (error);
421 } /* ixgbe_bp_aux_off */
422 
423 /************************************************************************
424  * ixgbe_bp_wd_set - Set the Watchdog timer value
425  *
426  *   Valid settings are:
427  *	- 0 will disable the watchdog
428  *	- 1, 2, 3, 4, 8, 16, 32
429  *	- anything else is invalid and will be ignored
430  ************************************************************************/
431 static int
432 ixgbe_bp_wd_set(SYSCTL_HANDLER_ARGS)
433 {
434 	struct ixgbe_softc *sc = (struct ixgbe_softc *) arg1;
435 	struct ixgbe_hw *hw = &sc->hw;
436 	int error, tmp;
437 	static int timeout = 0;
438 	u32 mask, arg;
439 
440 	/* Get the current hardware value */
441 	ixgbe_bypass_mutex_enter(sc);
442 	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &tmp);
443 	ixgbe_bypass_mutex_clear(sc);
444 	if (error)
445 		return (error);
446 	/*
447 	 * If armed keep the displayed value,
448 	 * else change the display to zero.
449 	 */
450 	if ((tmp & (0x1 << BYPASS_WDT_ENABLE_SHIFT)) == 0)
451 		timeout = 0;
452 
453 	error = sysctl_handle_int(oidp, &timeout, 0, req);
454 	if ((error) || (req->newptr == NULL))
455 		return (error);
456 
457 	arg = 0x1 << BYPASS_WDT_ENABLE_SHIFT;
458 	mask = BYPASS_WDT_ENABLE_M | BYPASS_WDT_VALUE_M;
459 	switch (timeout) {
460 	case 0: /* disables the timer */
461 		arg = BYPASS_PAGE_CTL0;
462 		mask = BYPASS_WDT_ENABLE_M;
463 		break;
464 	case 1:
465 		arg |= BYPASS_WDT_1_5 << BYPASS_WDT_TIME_SHIFT;
466 		break;
467 	case 2:
468 		arg |= BYPASS_WDT_2 << BYPASS_WDT_TIME_SHIFT;
469 		break;
470 	case 3:
471 		arg |= BYPASS_WDT_3 << BYPASS_WDT_TIME_SHIFT;
472 		break;
473 	case 4:
474 		arg |= BYPASS_WDT_4 << BYPASS_WDT_TIME_SHIFT;
475 		break;
476 	case 8:
477 		arg |= BYPASS_WDT_8 << BYPASS_WDT_TIME_SHIFT;
478 		break;
479 	case 16:
480 		arg |= BYPASS_WDT_16 << BYPASS_WDT_TIME_SHIFT;
481 		break;
482 	case 32:
483 		arg |= BYPASS_WDT_32 << BYPASS_WDT_TIME_SHIFT;
484 		break;
485 	default:
486 		return (EINVAL);
487 	}
488 
489 	/* Set the new watchdog */
490 	ixgbe_bypass_mutex_enter(sc);
491 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0, mask, arg);
492 	ixgbe_bypass_mutex_clear(sc);
493 
494 	return (error);
495 } /* ixgbe_bp_wd_set */
496 
497 /************************************************************************
498  * ixgbe_bp_wd_reset - Reset the Watchdog timer
499  *
500  *    To activate this it must be called with any argument.
501  ************************************************************************/
502 static int
503 ixgbe_bp_wd_reset(SYSCTL_HANDLER_ARGS)
504 {
505 	struct ixgbe_softc *sc = (struct ixgbe_softc *) arg1;
506 	struct ixgbe_hw *hw = &sc->hw;
507 	u32 sec, year;
508 	int cmd, count = 0, error = 0;
509 	int reset_wd = 0;
510 
511 	error = sysctl_handle_int(oidp, &reset_wd, 0, req);
512 	if ((error) || (req->newptr == NULL))
513 		return (error);
514 
515 	cmd = BYPASS_PAGE_CTL1 | BYPASS_WE | BYPASS_CTL1_WDT_PET;
516 
517 	/* Resync the FW time while writing to CTL1 anyway */
518 	ixgbe_get_bypass_time(&year, &sec);
519 
520 	cmd |= (sec & BYPASS_CTL1_TIME_M) | BYPASS_CTL1_VALID;
521 	cmd |= BYPASS_CTL1_OFFTRST;
522 
523 	ixgbe_bypass_wd_mutex_enter(sc);
524 	error = hw->mac.ops.bypass_rw(hw, cmd, &reset_wd);
525 
526 	/* Read until it matches what we wrote, or we time out */
527 	do {
528 		if (count++ > 10) {
529 			error = IXGBE_BYPASS_FW_WRITE_FAILURE;
530 			break;
531 		}
532 		if (hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &reset_wd)) {
533 			error = IXGBE_ERR_INVALID_ARGUMENT;
534 			break;
535 		}
536 	} while (!hw->mac.ops.bypass_valid_rd(cmd, reset_wd));
537 
538 	reset_wd = 0;
539 	ixgbe_bypass_wd_mutex_clear(sc);
540 	return (error);
541 } /* ixgbe_bp_wd_reset */
542 
543 /************************************************************************
544  * ixgbe_bp_log - Display the bypass log
545  *
546  *   You must pass a non-zero arg to sysctl
547  ************************************************************************/
548 static int
549 ixgbe_bp_log(SYSCTL_HANDLER_ARGS)
550 {
551 	struct ixgbe_softc *sc = (struct ixgbe_softc *) arg1;
552 	struct ixgbe_hw *hw = &sc->hw;
553 	u32 cmd, base, head;
554 	u32 log_off, count = 0;
555 	static int status = 0;
556 	u8 data;
557 	struct ixgbe_bypass_eeprom eeprom[BYPASS_MAX_LOGS];
558 	int i, error = 0;
559 
560 	error = sysctl_handle_int(oidp, &status, 0, req);
561 	if ((error) || (req->newptr == NULL))
562 		return (error);
563 
564 	/* Keep the log display single-threaded */
565 	while (atomic_cmpset_int(&sc->bypass.log, 0, 1) == 0)
566 		usec_delay(3000);
567 
568 	ixgbe_bypass_mutex_enter(sc);
569 
570 	/* Find Current head of the log eeprom offset */
571 	cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
572 	cmd |= (0x1 << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
573 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
574 	if (error)
575 		goto unlock_err;
576 
577 	/* wait for the write to stick */
578 	msec_delay(100);
579 
580 	/* Now read the results */
581 	cmd &= ~BYPASS_WE;
582 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
583 	if (error)
584 		goto unlock_err;
585 
586 	ixgbe_bypass_mutex_clear(sc);
587 
588 	base = status & BYPASS_CTL2_DATA_M;
589 	head = (status & BYPASS_CTL2_HEAD_M) >> BYPASS_CTL2_HEAD_SHIFT;
590 
591 	/* address of the first log */
592 	log_off = base + (head * 5);
593 
594 	/* extract all the log entries */
595 	while (count < BYPASS_MAX_LOGS) {
596 		eeprom[count].logs = 0;
597 		eeprom[count].actions = 0;
598 
599 		/* Log 5 bytes store in on u32 and a u8 */
600 		for (i = 0; i < 4; i++) {
601 			ixgbe_bypass_mutex_enter(sc);
602 			error = hw->mac.ops.bypass_rd_eep(hw, log_off + i,
603 			    &data);
604 			ixgbe_bypass_mutex_clear(sc);
605 			if (error)
606 				return (EINVAL);
607 			eeprom[count].logs += data << (8 * i);
608 		}
609 
610 		ixgbe_bypass_mutex_enter(sc);
611 		error = hw->mac.ops.bypass_rd_eep(hw,
612 		    log_off + i, &eeprom[count].actions);
613 		ixgbe_bypass_mutex_clear(sc);
614 		if (error)
615 			return (EINVAL);
616 
617 		/* Quit if not a unread log */
618 		if (!(eeprom[count].logs & BYPASS_LOG_CLEAR_M))
619 			break;
620 		/*
621 		 * Log looks good so store the address where it's
622 		 * Unread Log bit is so we can clear it after safely
623 		 * pulling out all of the log data.
624 		 */
625 		eeprom[count].clear_off = log_off;
626 
627 		count++;
628 		head = head ? head - 1 : BYPASS_MAX_LOGS;
629 		log_off = base + (head * 5);
630 	}
631 
632 	/* reverse order (oldest first) for output */
633 	while (count--) {
634 		int year;
635 		u32 mon, days, hours, min, sec;
636 		u32 time = eeprom[count].logs & BYPASS_LOG_TIME_M;
637 		u32 event = (eeprom[count].logs & BYPASS_LOG_EVENT_M) >>
638 		    BYPASS_LOG_EVENT_SHIFT;
639 		u8 action =  eeprom[count].actions & BYPASS_LOG_ACTION_M;
640 		u16 day_mon[2][13] = {
641 			{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304,
642 			    334, 365},
643 			{0, 31, 59, 91, 121, 152, 182, 213, 244, 274, 305,
644 			    335, 366}
645 		};
646 		char *event_str[] = {"unknown", "main on", "aux on",
647 		    "main off", "aux off", "WDT", "user" };
648 		char *action_str[] =
649 		    {"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", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
750 	    sc, 0, ixgbe_bp_log, "I", "Bypass Log");
751 
752 	/* All other setting are hung from the 'bypass' node */
753 	bp_node = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
754 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
755 	    OID_AUTO, "bypass", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Bypass");
756 
757 	bp_list = SYSCTL_CHILDREN(bp_node);
758 
759 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
760 	    OID_AUTO, "version", CTLTYPE_INT | CTLFLAG_RD,
761 	    sc, 0, ixgbe_bp_version, "I", "Bypass Version");
762 
763 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
764 	    OID_AUTO, "state", CTLTYPE_INT | CTLFLAG_RW,
765 	    sc, 0, ixgbe_bp_set_state, "I", "Bypass State");
766 
767 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
768 	    OID_AUTO, "timeout", CTLTYPE_INT | CTLFLAG_RW,
769 	    sc, 0, ixgbe_bp_timeout, "I", "Bypass Timeout");
770 
771 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
772 	    OID_AUTO, "main_on", CTLTYPE_INT | CTLFLAG_RW,
773 	    sc, 0, ixgbe_bp_main_on, "I", "Bypass Main On");
774 
775 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
776 	    OID_AUTO, "main_off", CTLTYPE_INT | CTLFLAG_RW,
777 	    sc, 0, ixgbe_bp_main_off, "I", "Bypass Main Off");
778 
779 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
780 	    OID_AUTO, "aux_on", CTLTYPE_INT | CTLFLAG_RW,
781 	    sc, 0, ixgbe_bp_aux_on, "I", "Bypass Aux On");
782 
783 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
784 	    OID_AUTO, "aux_off", CTLTYPE_INT | CTLFLAG_RW,
785 	    sc, 0, ixgbe_bp_aux_off, "I", "Bypass Aux Off");
786 
787 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
788 	    OID_AUTO, "wd_set", CTLTYPE_INT | CTLFLAG_RW,
789 	    sc, 0, ixgbe_bp_wd_set, "I", "Set BP Watchdog");
790 
791 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
792 	    OID_AUTO, "wd_reset", CTLTYPE_INT | CTLFLAG_WR,
793 	    sc, 0, ixgbe_bp_wd_reset, "S", "Bypass WD Reset");
794 
795 	sc->feat_en |= IXGBE_FEATURE_BYPASS;
796 } /* ixgbe_bypass_init */
797 
798