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