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 error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &reset_wd);
534 if (error != 0) {
535 error = IXGBE_ERR_INVALID_ARGUMENT;
536 break;
537 }
538 } while (!hw->mac.ops.bypass_valid_rd(cmd, reset_wd));
539
540 reset_wd = 0;
541 ixgbe_bypass_wd_mutex_clear(sc);
542 return (error);
543 } /* ixgbe_bp_wd_reset */
544
545 /************************************************************************
546 * ixgbe_bp_log - Display the bypass log
547 *
548 * You must pass a non-zero arg to sysctl
549 ************************************************************************/
550 static int
ixgbe_bp_log(SYSCTL_HANDLER_ARGS)551 ixgbe_bp_log(SYSCTL_HANDLER_ARGS)
552 {
553 struct ixgbe_softc *sc = (struct ixgbe_softc *) arg1;
554 struct ixgbe_hw *hw = &sc->hw;
555 u32 cmd, base, head;
556 u32 log_off, count = 0;
557 static int status = 0;
558 u8 data;
559 struct ixgbe_bypass_eeprom eeprom[BYPASS_MAX_LOGS];
560 int i, error = 0;
561
562 error = sysctl_handle_int(oidp, &status, 0, req);
563 if ((error) || (req->newptr == NULL))
564 return (error);
565
566 /* Keep the log display single-threaded */
567 while (atomic_cmpset_int(&sc->bypass.log, 0, 1) == 0)
568 usec_delay(3000);
569
570 ixgbe_bypass_mutex_enter(sc);
571
572 /* Find Current head of the log eeprom offset */
573 cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
574 cmd |= (0x1 << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
575 error = hw->mac.ops.bypass_rw(hw, cmd, &status);
576 if (error)
577 goto unlock_err;
578
579 /* wait for the write to stick */
580 msec_delay(100);
581
582 /* Now read the results */
583 cmd &= ~BYPASS_WE;
584 error = hw->mac.ops.bypass_rw(hw, cmd, &status);
585 if (error)
586 goto unlock_err;
587
588 ixgbe_bypass_mutex_clear(sc);
589
590 base = status & BYPASS_CTL2_DATA_M;
591 head = (status & BYPASS_CTL2_HEAD_M) >> BYPASS_CTL2_HEAD_SHIFT;
592
593 /* address of the first log */
594 log_off = base + (head * 5);
595
596 /* extract all the log entries */
597 while (count < BYPASS_MAX_LOGS) {
598 eeprom[count].logs = 0;
599 eeprom[count].actions = 0;
600
601 /* Log 5 bytes store in on u32 and a u8 */
602 for (i = 0; i < 4; i++) {
603 ixgbe_bypass_mutex_enter(sc);
604 error = hw->mac.ops.bypass_rd_eep(hw, log_off + i,
605 &data);
606 ixgbe_bypass_mutex_clear(sc);
607 if (error)
608 return (EINVAL);
609 eeprom[count].logs += data << (8 * i);
610 }
611
612 ixgbe_bypass_mutex_enter(sc);
613 error = hw->mac.ops.bypass_rd_eep(hw,
614 log_off + i, &eeprom[count].actions);
615 ixgbe_bypass_mutex_clear(sc);
616 if (error)
617 return (EINVAL);
618
619 /* Quit if not a unread log */
620 if (!(eeprom[count].logs & BYPASS_LOG_CLEAR_M))
621 break;
622 /*
623 * Log looks good so store the address where it's
624 * Unread Log bit is so we can clear it after safely
625 * pulling out all of the log data.
626 */
627 eeprom[count].clear_off = log_off;
628
629 count++;
630 head = head ? head - 1 : BYPASS_MAX_LOGS;
631 log_off = base + (head * 5);
632 }
633
634 /* reverse order (oldest first) for output */
635 while (count--) {
636 int year;
637 u32 mon, days, hours, min, sec;
638 u32 time = eeprom[count].logs & BYPASS_LOG_TIME_M;
639 u32 event = (eeprom[count].logs & BYPASS_LOG_EVENT_M) >>
640 BYPASS_LOG_EVENT_SHIFT;
641 u8 action = eeprom[count].actions & BYPASS_LOG_ACTION_M;
642 u16 day_mon[2][13] = {
643 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
644 {0, 31, 59, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
645 };
646 char *event_str[] = {"unknown", "main on", "aux on",
647 "main off", "aux off", "WDT", "user" };
648 char *action_str[] = {"ignore", "normal", "bypass", "isolate",};
649
650 /* verify vaild data 1 - 6 */
651 if (event < BYPASS_EVENT_MAIN_ON || event > BYPASS_EVENT_USR)
652 event = 0;
653
654 /*
655 * time is in sec's this year, so convert to something
656 * printable.
657 */
658 ixgbe_get_bypass_time(&year, &sec);
659 days = time / SEC_PER_DAY;
660 for (i = 11; days < day_mon[LEAP_YR(year)][i]; i--)
661 continue;
662 mon = i + 1; /* display month as 1-12 */
663 time -= (day_mon[LEAP_YR(year)][i] * SEC_PER_DAY);
664 days = (time / SEC_PER_DAY) + 1; /* first day is 1 */
665 time %= SEC_PER_DAY;
666 hours = time / (60 * 60);
667 time %= (60 * 60);
668 min = time / 60;
669 sec = time % 60;
670 device_printf(sc->dev,
671 "UT %02d/%02d %02d:%02d:%02d %8.8s -> %7.7s\n",
672 mon, days, hours, min, sec, event_str[event],
673 action_str[action]);
674 cmd = BYPASS_PAGE_CTL2 | BYPASS_WE | BYPASS_CTL2_RW;
675 cmd |= ((eeprom[count].clear_off + 3)
676 << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
677 cmd |= ((eeprom[count].logs & ~BYPASS_LOG_CLEAR_M) >> 24);
678
679 ixgbe_bypass_mutex_enter(sc);
680
681 error = hw->mac.ops.bypass_rw(hw, cmd, &status);
682
683 /* wait for the write to stick */
684 msec_delay(100);
685
686 ixgbe_bypass_mutex_clear(sc);
687
688 if (error)
689 return (EINVAL);
690 }
691
692 status = 0; /* reset */
693 /* Another log command can now run */
694 while (atomic_cmpset_int(&sc->bypass.log, 1, 0) == 0)
695 usec_delay(3000);
696 return (error);
697
698 unlock_err:
699 ixgbe_bypass_mutex_clear(sc);
700 status = 0; /* reset */
701 while (atomic_cmpset_int(&sc->bypass.log, 1, 0) == 0)
702 usec_delay(3000);
703 return (EINVAL);
704 } /* ixgbe_bp_log */
705
706 /************************************************************************
707 * ixgbe_bypass_init - Set up infrastructure for the bypass feature
708 *
709 * Do time and sysctl initialization here. This feature is
710 * only enabled for the first port of a bypass adapter.
711 ************************************************************************/
712 void
ixgbe_bypass_init(struct ixgbe_softc * sc)713 ixgbe_bypass_init(struct ixgbe_softc *sc)
714 {
715 struct ixgbe_hw *hw = &sc->hw;
716 device_t dev = sc->dev;
717 struct sysctl_oid *bp_node;
718 struct sysctl_oid_list *bp_list;
719 u32 mask, value, sec, year;
720
721 if (!(sc->feat_cap & IXGBE_FEATURE_BYPASS))
722 return;
723
724 /* First set up time for the hardware */
725 ixgbe_get_bypass_time(&year, &sec);
726
727 mask = BYPASS_CTL1_TIME_M
728 | BYPASS_CTL1_VALID_M
729 | BYPASS_CTL1_OFFTRST_M;
730
731 value = (sec & BYPASS_CTL1_TIME_M)
732 | BYPASS_CTL1_VALID
733 | BYPASS_CTL1_OFFTRST;
734
735 ixgbe_bypass_mutex_enter(sc);
736 hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL1, mask, value);
737 ixgbe_bypass_mutex_clear(sc);
738
739 /* Now set up the SYSCTL infrastructure */
740
741 /*
742 * The log routine is kept separate from the other
743 * children so a general display command like:
744 * `sysctl dev.ix.0.bypass` will not show the log.
745 */
746 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
747 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
748 OID_AUTO, "bypass_log",
749 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
750 sc, 0, ixgbe_bp_log, "I", "Bypass Log");
751
752 /* All other setting are hung from the 'bypass' node */
753 bp_node = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
754 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
755 OID_AUTO, "bypass", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Bypass");
756
757 bp_list = SYSCTL_CHILDREN(bp_node);
758
759 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
760 OID_AUTO, "version", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
761 sc, 0, ixgbe_bp_version, "I", "Bypass Version");
762
763 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
764 OID_AUTO, "state", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
765 sc, 0, ixgbe_bp_set_state, "I", "Bypass State");
766
767 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
768 OID_AUTO, "timeout", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
769 sc, 0, ixgbe_bp_timeout, "I", "Bypass Timeout");
770
771 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
772 OID_AUTO, "main_on", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
773 sc, 0, ixgbe_bp_main_on, "I", "Bypass Main On");
774
775 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
776 OID_AUTO, "main_off", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
777 sc, 0, ixgbe_bp_main_off, "I", "Bypass Main Off");
778
779 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
780 OID_AUTO, "aux_on", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
781 sc, 0, ixgbe_bp_aux_on, "I", "Bypass Aux On");
782
783 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
784 OID_AUTO, "aux_off", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
785 sc, 0, ixgbe_bp_aux_off, "I", "Bypass Aux Off");
786
787 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
788 OID_AUTO, "wd_set", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
789 sc, 0, ixgbe_bp_wd_set, "I", "Set BP Watchdog");
790
791 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
792 OID_AUTO, "wd_reset", CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
793 sc, 0, ixgbe_bp_wd_reset, "S", "Bypass WD Reset");
794
795 sc->feat_en |= IXGBE_FEATURE_BYPASS;
796 } /* ixgbe_bypass_init */
797
798