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