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