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