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 ************************************************************************/
44static void
45ixgbe_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 ************************************************************************/
57static void
58ixgbe_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 ************************************************************************/
72static void
73ixgbe_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 ************************************************************************/
83static void
84ixgbe_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 ************************************************************************/
94static void
95ixgbe_get_bypass_time(u32 *year, u32 *sec)
96{
97	struct timespec current;
98
99	*year = 1970;           /* time starts at 01/01/1970 */
100	nanotime(&current);
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 ************************************************************************/
114static int
115ixgbe_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);
137err:
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 ************************************************************************/
154static int
155ixgbe_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);
191out:
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 ************************************************************************/
216static int
217ixgbe_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 ************************************************************************/
259static int
260ixgbe_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 ************************************************************************/
301static int
302ixgbe_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 ************************************************************************/
343static int
344ixgbe_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 ************************************************************************/
385static int
386ixgbe_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 ************************************************************************/
432static int
433ixgbe_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 ************************************************************************/
503static int
504ixgbe_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 ************************************************************************/
550static int
551ixgbe_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
698unlock_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 ************************************************************************/
712void
713ixgbe_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