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