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