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(¤t);
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