xref: /netbsd-src/sys/dev/pci/ixgbe/if_bypass.c (revision 796c32c94f6e154afc9de0f63da35c91bb739b45)
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 320688 2017-07-05 17:27:03Z 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)
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) || (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 = BYPASS_PAGE_CTL0;
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 	mask = BYPASS_WDT_ENABLE_M;
476 	switch (timeout) {
477 		case 0: /* disables the timer */
478 			break;
479 		case 1:
480 			arg = BYPASS_WDT_1_5 << BYPASS_WDT_TIME_SHIFT;
481 			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
482 			mask |= BYPASS_WDT_VALUE_M;
483 			break;
484 		case 2:
485 			arg = BYPASS_WDT_2 << BYPASS_WDT_TIME_SHIFT;
486 			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
487 			mask |= BYPASS_WDT_VALUE_M;
488 			break;
489 		case 3:
490 			arg = BYPASS_WDT_3 << BYPASS_WDT_TIME_SHIFT;
491 			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
492 			mask |= BYPASS_WDT_VALUE_M;
493 			break;
494 		case 4:
495 			arg = BYPASS_WDT_4 << BYPASS_WDT_TIME_SHIFT;
496 			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
497 			mask |= BYPASS_WDT_VALUE_M;
498 			break;
499 		case 8:
500 			arg = BYPASS_WDT_8 << BYPASS_WDT_TIME_SHIFT;
501 			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
502 			mask |= BYPASS_WDT_VALUE_M;
503 			break;
504 		case 16:
505 			arg = BYPASS_WDT_16 << BYPASS_WDT_TIME_SHIFT;
506 			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
507 			mask |= BYPASS_WDT_VALUE_M;
508 			break;
509 		case 32:
510 			arg = BYPASS_WDT_32 << BYPASS_WDT_TIME_SHIFT;
511 			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
512 			mask |= BYPASS_WDT_VALUE_M;
513 			break;
514 		default:
515 			return (EINVAL);
516 	}
517 	/* Set the new watchdog */
518 	ixgbe_bypass_mutex_enter(adapter);
519 	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0, mask, arg);
520 	ixgbe_bypass_mutex_clear(adapter);
521 
522 	return (error);
523 } /* ixgbe_bp_wd_set */
524 
525 /************************************************************************
526  * ixgbe_bp_wd_reset - Reset the Watchdog timer
527  *
528  *    To activate this it must be called with any argument.
529  ************************************************************************/
530 static int
531 ixgbe_bp_wd_reset(SYSCTLFN_ARGS)
532 {
533 	struct sysctlnode node = *rnode;
534 	struct adapter  *adapter = (struct adapter *)node.sysctl_data;
535 	struct ixgbe_hw *hw = &adapter->hw;
536 	u32             sec, year;
537 	int             cmd, count = 0, error = 0;
538 	int             reset_wd = 0;
539 
540 	node.sysctl_data = &reset_wd;
541 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
542 	if ((error) || (newp == NULL))
543 		return (error);
544 
545 	cmd = BYPASS_PAGE_CTL1 | BYPASS_WE | BYPASS_CTL1_WDT_PET;
546 
547 	/* Resync the FW time while writing to CTL1 anyway */
548 	ixgbe_get_bypass_time(&year, &sec);
549 
550 	cmd |= (sec & BYPASS_CTL1_TIME_M) | BYPASS_CTL1_VALID;
551 	cmd |= BYPASS_CTL1_OFFTRST;
552 
553 	ixgbe_bypass_wd_mutex_enter(adapter);
554 	error = hw->mac.ops.bypass_rw(hw, cmd, &reset_wd);
555 
556 	/* Read until it matches what we wrote, or we time out */
557 	do {
558 		if (count++ > 10) {
559 			error = IXGBE_BYPASS_FW_WRITE_FAILURE;
560 			break;
561 		}
562 		if (hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &reset_wd)) {
563 			error = IXGBE_ERR_INVALID_ARGUMENT;
564 			break;
565 		}
566 	} while (!hw->mac.ops.bypass_valid_rd(cmd, reset_wd));
567 
568 	reset_wd = 0;
569 	ixgbe_bypass_wd_mutex_clear(adapter);
570 	return (error);
571 } /* ixgbe_bp_wd_reset */
572 
573 /************************************************************************
574  * ixgbe_bp_log - Display the bypass log
575  *
576  *   You must pass a non-zero arg to sysctl
577  ************************************************************************/
578 static int
579 ixgbe_bp_log(SYSCTLFN_ARGS)
580 {
581 	struct sysctlnode          node = *rnode;
582 	struct adapter           *adapter = (struct adapter *)node.sysctl_data;
583 	struct ixgbe_hw            *hw = &adapter->hw;
584 	u32                        cmd, base, head;
585 	u32                        log_off, count = 0;
586 	static int                 status = 0;
587 	u8                         data;
588 	struct ixgbe_bypass_eeprom eeprom[BYPASS_MAX_LOGS];
589 	int                        i, error = 0;
590 
591 	node.sysctl_data = &status;
592 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
593 	if ((error) || (newp == NULL))
594 		return (error);
595 
596 	/* Keep the log display single-threaded */
597 	while (atomic_cas_uint(&adapter->bypass.log, 0, 1) == 0)
598 		usec_delay(3000);
599 
600 	ixgbe_bypass_mutex_enter(adapter);
601 
602 	/* Find Current head of the log eeprom offset */
603 	cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
604 	cmd |= (0x1 << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
605 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
606 	if (error)
607 		goto unlock_err;
608 
609 	/* wait for the write to stick */
610 	msec_delay(100);
611 
612 	/* Now read the results */
613 	cmd &= ~BYPASS_WE;
614 	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
615 	if (error)
616 		goto unlock_err;
617 
618 	ixgbe_bypass_mutex_clear(adapter);
619 
620 	base = status & BYPASS_CTL2_DATA_M;
621 	head = (status & BYPASS_CTL2_HEAD_M) >> BYPASS_CTL2_HEAD_SHIFT;
622 
623 	/* address of the first log */
624 	log_off = base + (head * 5);
625 
626 	/* extract all the log entries */
627 	while (count < BYPASS_MAX_LOGS) {
628 		eeprom[count].logs = 0;
629 		eeprom[count].actions = 0;
630 
631 		/* Log 5 bytes store in on u32 and a u8 */
632 		for (i = 0; i < 4; i++) {
633 			ixgbe_bypass_mutex_enter(adapter);
634 			error = hw->mac.ops.bypass_rd_eep(hw, log_off + i,
635 			    &data);
636 			ixgbe_bypass_mutex_clear(adapter);
637 			if (error)
638 				return (-EINVAL);
639 			eeprom[count].logs += data << (8 * i);
640 		}
641 
642 		ixgbe_bypass_mutex_enter(adapter);
643 		error = hw->mac.ops.bypass_rd_eep(hw,
644 		    log_off + i, &eeprom[count].actions);
645 		ixgbe_bypass_mutex_clear(adapter);
646 		if (error)
647 			return (-EINVAL);
648 
649 		/* Quit if not a unread log */
650 		if (!(eeprom[count].logs & BYPASS_LOG_CLEAR_M))
651 			break;
652 		/*
653 		 * Log looks good so store the address where it's
654 		 * Unread Log bit is so we can clear it after safely
655 		 * pulling out all of the log data.
656 		 */
657 		eeprom[count].clear_off = log_off;
658 
659 		count++;
660 		head = head ? head - 1 : BYPASS_MAX_LOGS;
661 		log_off = base + (head * 5);
662 	}
663 
664 	/* reverse order (oldest first) for output */
665 	while (count--) {
666 		int year;
667 		u32 mon, days, hours, min, sec;
668 		u32 time = eeprom[count].logs & BYPASS_LOG_TIME_M;
669 		u32 event = (eeprom[count].logs & BYPASS_LOG_EVENT_M) >>
670 		    BYPASS_LOG_EVENT_SHIFT;
671 		u8 action =  eeprom[count].actions & BYPASS_LOG_ACTION_M;
672 		u16 day_mon[2][13] = {
673 		  {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
674 		  {0, 31, 59, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
675 		};
676 		const char *event_str[] = {"unknown", "main on", "aux on",
677 		    "main off", "aux off", "WDT", "user" };
678 		const char *action_str[] = {"ignore", "normal", "bypass",
679 					    "isolate",};
680 
681 		/* verify vaild data  1 - 6 */
682 		if (event < BYPASS_EVENT_MAIN_ON || event > BYPASS_EVENT_USR)
683 			event = 0;
684 
685 		/*
686 		 * time is in sec's this year, so convert to something
687 		 * printable.
688 		 */
689 		ixgbe_get_bypass_time(&year, &sec);
690 		days = time / SEC_PER_DAY;
691 		for (i = 11; days < day_mon[LEAP_YR(year)][i]; i--)
692 			continue;
693 		mon = i + 1;    /* display month as 1-12 */
694 		time -= (day_mon[LEAP_YR(year)][i] * SEC_PER_DAY);
695 		days = (time / SEC_PER_DAY) + 1;  /* first day is 1 */
696 		time %= SEC_PER_DAY;
697 		hours = time / (60 * 60);
698 		time %= (60 * 60);
699 		min = time / 60;
700 		sec = time % 60;
701 		device_printf(adapter->dev,
702 		    "UT %02d/%02d %02d:%02d:%02d %8.8s -> %7.7s\n",
703 		    mon, days, hours, min, sec, event_str[event],
704 		    action_str[action]);
705 		cmd = BYPASS_PAGE_CTL2 | BYPASS_WE | BYPASS_CTL2_RW;
706 		cmd |= ((eeprom[count].clear_off + 3)
707 		    << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
708 		cmd |= ((eeprom[count].logs & ~BYPASS_LOG_CLEAR_M) >> 24);
709 
710 		ixgbe_bypass_mutex_enter(adapter);
711 
712 		error = hw->mac.ops.bypass_rw(hw, cmd, &status);
713 
714 		/* wait for the write to stick */
715 		msec_delay(100);
716 
717 		ixgbe_bypass_mutex_clear(adapter);
718 
719 		if (error)
720 			return (-EINVAL);
721 	}
722 
723 	status = 0; /* reset */
724 	/* Another log command can now run */
725 	while (atomic_cas_uint(&adapter->bypass.log, 1, 0) == 0)
726 		usec_delay(3000);
727 	return(error);
728 
729 unlock_err:
730 	ixgbe_bypass_mutex_clear(adapter);
731 	status = 0; /* reset */
732 	while (atomic_cas_uint(&adapter->bypass.log, 1, 0) == 0)
733 		usec_delay(3000);
734 	return (-EINVAL);
735 } /* ixgbe_bp_log */
736 
737 /************************************************************************
738  * ixgbe_bypass_init - Set up infrastructure for the bypass feature
739  *
740  *   Do time and sysctl initialization here.  This feature is
741  *   only enabled for the first port of a bypass adapter.
742  ************************************************************************/
743 void
744 ixgbe_bypass_init(struct adapter *adapter)
745 {
746 	struct ixgbe_hw        *hw = &adapter->hw;
747 	device_t               dev = adapter->dev;
748 	u32                    mask, value, sec, year;
749 	struct                 sysctllog **log;
750 	const struct sysctlnode *rnode, *cnode;
751 
752 	if (!(adapter->feat_cap & IXGBE_FEATURE_BYPASS))
753 		return;
754 
755 	/* First set up time for the hardware */
756 	ixgbe_get_bypass_time(&year, &sec);
757 
758 	mask = BYPASS_CTL1_TIME_M
759 	     | BYPASS_CTL1_VALID_M
760 	     | BYPASS_CTL1_OFFTRST_M;
761 
762 	value = (sec & BYPASS_CTL1_TIME_M)
763 	      | BYPASS_CTL1_VALID
764 	      | BYPASS_CTL1_OFFTRST;
765 
766 	ixgbe_bypass_mutex_enter(adapter);
767 	hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL1, mask, value);
768 	ixgbe_bypass_mutex_clear(adapter);
769 
770 	/* Now set up the SYSCTL infrastructure */
771 	log = &adapter->sysctllog;
772 	if ((rnode = ixgbe_sysctl_instance(adapter)) == NULL) {
773 		aprint_error_dev(dev, "could not create sysctl root\n");
774 		return;
775 	}
776 
777 	/*
778 	 * The log routine is kept separate from the other
779 	 * children so a general display command like:
780 	 * `sysctl dev.ix.0.bypass` will not show the log.
781 	 */
782 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
783 	    CTLTYPE_INT, "bypass_log", SYSCTL_DESCR("Bypass Log"),
784 	    ixgbe_bp_log, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
785 
786 	/* All other setting are hung from the 'bypass' node */
787 	sysctl_createv(log, 0, &rnode, &rnode, 0,
788 	    CTLTYPE_NODE, "bypass", SYSCTL_DESCR("Bypass"),
789 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
790 
791 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READONLY,
792 	    CTLTYPE_INT, "version", SYSCTL_DESCR("Bypass Version"),
793 	    ixgbe_bp_version, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
794 
795 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
796 	    CTLTYPE_INT, "state", SYSCTL_DESCR("Bypass State"),
797 	    ixgbe_bp_set_state, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
798 
799 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
800 	    CTLTYPE_INT, "timeout", SYSCTL_DESCR("Bypass Timeout"),
801 	    ixgbe_bp_timeout, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
802 
803 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
804 	    CTLTYPE_INT, "main_on", SYSCTL_DESCR("Bypass Main On"),
805 	    ixgbe_bp_main_on, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
806 
807 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
808 	    CTLTYPE_INT, "main_off", SYSCTL_DESCR("Bypass Main Off"),
809 	    ixgbe_bp_main_off, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
810 
811 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
812 	    CTLTYPE_INT, "aux_on", SYSCTL_DESCR("Bypass Aux On"),
813 	    ixgbe_bp_aux_on, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
814 
815 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
816 	    CTLTYPE_INT, "aux_off", SYSCTL_DESCR("Bypass Aux Off"),
817 	    ixgbe_bp_aux_off, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
818 
819 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
820 	    CTLTYPE_INT, "wd_set", SYSCTL_DESCR("Set BP Watchdog"),
821 	    ixgbe_bp_wd_set, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
822 
823 	sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_READWRITE,
824 	    CTLTYPE_INT, "wd_reset", SYSCTL_DESCR("Bypass WD Reset"),
825 	    ixgbe_bp_wd_reset, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL);
826 
827 	adapter->feat_en |= IXGBE_FEATURE_BYPASS;
828 
829 	return;
830 } /* ixgbe_bypass_init */
831 
832