if_bypass.c revision 1.3 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(¤t);
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