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