1315333Serj/******************************************************************************
2315333Serj
3315333Serj  Copyright (c) 2001-2017, Intel Corporation
4315333Serj  All rights reserved.
5315333Serj
6315333Serj  Redistribution and use in source and binary forms, with or without
7315333Serj  modification, are permitted provided that the following conditions are met:
8315333Serj
9315333Serj   1. Redistributions of source code must retain the above copyright notice,
10315333Serj      this list of conditions and the following disclaimer.
11315333Serj
12315333Serj   2. Redistributions in binary form must reproduce the above copyright
13315333Serj      notice, this list of conditions and the following disclaimer in the
14315333Serj      documentation and/or other materials provided with the distribution.
15315333Serj
16315333Serj   3. Neither the name of the Intel Corporation nor the names of its
17315333Serj      contributors may be used to endorse or promote products derived from
18315333Serj      this software without specific prior written permission.
19315333Serj
20315333Serj  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21315333Serj  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22315333Serj  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23315333Serj  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24315333Serj  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25315333Serj  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26315333Serj  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27315333Serj  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28315333Serj  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29315333Serj  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30315333Serj  POSSIBILITY OF SUCH DAMAGE.
31315333Serj
32315333Serj******************************************************************************/
33315333Serj/*$FreeBSD: stable/10/sys/dev/ixgbe/if_bypass.c 315333 2017-03-15 21:20:17Z erj $*/
34315333Serj
35315333Serj
36315333Serj#include "ixgbe.h"
37315333Serj
38315333Serj/************************************************************************
39315333Serj * ixgbe_bypass_mutex_enter
40315333Serj *
41315333Serj *   Mutex support for the bypass feature. Using a dual lock
42315333Serj *   to facilitate a privileged access to the watchdog update
43315333Serj *   over other threads.
44315333Serj ************************************************************************/
45315333Serjstatic void
46315333Serjixgbe_bypass_mutex_enter(struct adapter *adapter)
47315333Serj{
48315333Serj	while (atomic_cmpset_int(&adapter->bypass.low, 0, 1) == 0)
49315333Serj		usec_delay(3000);
50315333Serj	while (atomic_cmpset_int(&adapter->bypass.high, 0, 1) == 0)
51315333Serj		usec_delay(3000);
52315333Serj	return;
53315333Serj} /* ixgbe_bypass_mutex_enter */
54315333Serj
55315333Serj/************************************************************************
56315333Serj * ixgbe_bypass_mutex_clear
57315333Serj ************************************************************************/
58315333Serjstatic void
59315333Serjixgbe_bypass_mutex_clear(struct adapter *adapter)
60315333Serj{
61315333Serj	while (atomic_cmpset_int(&adapter->bypass.high, 1, 0) == 0)
62315333Serj		usec_delay(6000);
63315333Serj	while (atomic_cmpset_int(&adapter->bypass.low, 1, 0) == 0)
64315333Serj		usec_delay(6000);
65315333Serj	return;
66315333Serj} /* ixgbe_bypass_mutex_clear */
67315333Serj
68315333Serj/************************************************************************
69315333Serj * ixgbe_bypass_wd_mutex_enter
70315333Serj *
71315333Serj *   Watchdog entry is allowed to simply grab the high priority
72315333Serj ************************************************************************/
73315333Serjstatic void
74315333Serjixgbe_bypass_wd_mutex_enter(struct adapter *adapter)
75315333Serj{
76315333Serj	while (atomic_cmpset_int(&adapter->bypass.high, 0, 1) == 0)
77315333Serj		usec_delay(3000);
78315333Serj	return;
79315333Serj} /* ixgbe_bypass_wd_mutex_enter */
80315333Serj
81315333Serj/************************************************************************
82315333Serj * ixgbe_bypass_wd_mutex_clear
83315333Serj ************************************************************************/
84315333Serjstatic void
85315333Serjixgbe_bypass_wd_mutex_clear(struct adapter *adapter)
86315333Serj{
87315333Serj	while (atomic_cmpset_int(&adapter->bypass.high, 1, 0) == 0)
88315333Serj		usec_delay(6000);
89315333Serj	return;
90315333Serj} /* ixgbe_bypass_wd_mutex_clear */
91315333Serj
92315333Serj/************************************************************************
93315333Serj * ixgbe_get_bypass_time
94315333Serj ************************************************************************/
95315333Serjstatic void
96315333Serjixgbe_get_bypass_time(u32 *year, u32 *sec)
97315333Serj{
98315333Serj	struct timespec current;
99315333Serj
100315333Serj	*year = 1970;           /* time starts at 01/01/1970 */
101315333Serj	nanotime(&current);
102315333Serj	*sec = current.tv_sec;
103315333Serj
104315333Serj	while(*sec > SEC_THIS_YEAR(*year)) {
105315333Serj		*sec -= SEC_THIS_YEAR(*year);
106315333Serj		(*year)++;
107315333Serj	}
108315333Serj} /* ixgbe_get_bypass_time */
109315333Serj
110315333Serj/************************************************************************
111315333Serj * ixgbe_bp_version
112315333Serj *
113315333Serj *   Display the feature version
114315333Serj ************************************************************************/
115315333Serjstatic int
116315333Serjixgbe_bp_version(SYSCTL_HANDLER_ARGS)
117315333Serj{
118315333Serj	struct adapter  *adapter = (struct adapter *) arg1;
119315333Serj	struct ixgbe_hw *hw = &adapter->hw;
120315333Serj	int             error = 0;
121315333Serj	static int      version = 0;
122315333Serj	u32             cmd;
123315333Serj
124315333Serj	ixgbe_bypass_mutex_enter(adapter);
125315333Serj	cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
126315333Serj	cmd |= (BYPASS_EEPROM_VER_ADD << BYPASS_CTL2_OFFSET_SHIFT) &
127315333Serj	    BYPASS_CTL2_OFFSET_M;
128315333Serj	if ((error = hw->mac.ops.bypass_rw(hw, cmd, &version) != 0))
129315333Serj		goto err;
130315333Serj	msec_delay(100);
131315333Serj	cmd &= ~BYPASS_WE;
132315333Serj	if ((error = hw->mac.ops.bypass_rw(hw, cmd, &version) != 0))
133315333Serj		goto err;
134315333Serj	ixgbe_bypass_mutex_clear(adapter);
135315333Serj	version &= BYPASS_CTL2_DATA_M;
136315333Serj	error = sysctl_handle_int(oidp, &version, 0, req);
137315333Serj	return (error);
138315333Serjerr:
139315333Serj	ixgbe_bypass_mutex_clear(adapter);
140315333Serj	return (error);
141315333Serj
142315333Serj} /* ixgbe_bp_version */
143315333Serj
144315333Serj/************************************************************************
145315333Serj * ixgbe_bp_set_state
146315333Serj *
147315333Serj *   Show/Set the Bypass State:
148315333Serj *	1 = NORMAL
149315333Serj *	2 = BYPASS
150315333Serj *	3 = ISOLATE
151315333Serj *
152315333Serj *	With no argument the state is displayed,
153315333Serj *	passing a value will set it.
154315333Serj ************************************************************************/
155315333Serjstatic int
156315333Serjixgbe_bp_set_state(SYSCTL_HANDLER_ARGS)
157315333Serj{
158315333Serj	struct adapter  *adapter = (struct adapter *) arg1;
159315333Serj	struct ixgbe_hw *hw = &adapter->hw;
160315333Serj	int             error = 0;
161315333Serj	static int      state = 0;
162315333Serj
163315333Serj	/* Get the current state */
164315333Serj	ixgbe_bypass_mutex_enter(adapter);
165315333Serj	error = hw->mac.ops.bypass_rw(hw,
166315333Serj	    BYPASS_PAGE_CTL0, &state);
167315333Serj	ixgbe_bypass_mutex_clear(adapter);
168315333Serj	if (error)
169315333Serj		return (error);
170315333Serj	state = (state >> BYPASS_STATUS_OFF_SHIFT) & 0x3;
171315333Serj
172315333Serj	error = sysctl_handle_int(oidp, &state, 0, req);
173315333Serj	if ((error) || (req->newptr == NULL))
174315333Serj		return (error);
175315333Serj
176315333Serj	/* Sanity check new state */
177315333Serj	switch (state) {
178315333Serj	case BYPASS_NORM:
179315333Serj	case BYPASS_BYPASS:
180315333Serj	case BYPASS_ISOLATE:
181315333Serj		break;
182315333Serj	default:
183315333Serj		return (EINVAL);
184315333Serj	}
185315333Serj	ixgbe_bypass_mutex_enter(adapter);
186315333Serj	if ((error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
187315333Serj	    BYPASS_MODE_OFF_M, state) != 0))
188315333Serj		goto out;
189315333Serj	/* Set AUTO back on so FW can receive events */
190315333Serj	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
191315333Serj	    BYPASS_MODE_OFF_M, BYPASS_AUTO);
192315333Serjout:
193315333Serj	ixgbe_bypass_mutex_clear(adapter);
194315333Serj	usec_delay(6000);
195315333Serj	return (error);
196315333Serj} /* ixgbe_bp_set_state */
197315333Serj
198315333Serj/************************************************************************
199315333Serj * The following routines control the operational
200315333Serj * "rules" of the feature, what behavior will occur
201315333Serj * when particular events occur.
202315333Serj * 	Values are:
203315333Serj *		0 - no change for the event (NOP)
204315333Serj *		1 - go to Normal operation
205315333Serj *		2 - go to Bypass operation
206315333Serj *		3 - go to Isolate operation
207315333Serj * Calling the entry with no argument just displays
208315333Serj * the current rule setting.
209315333Serj ************************************************************************/
210315333Serj
211315333Serj/************************************************************************
212315333Serj * ixgbe_bp_timeout
213315333Serj *
214315333Serj * This is to set the Rule for the watchdog,
215315333Serj * not the actual watchdog timeout value.
216315333Serj ************************************************************************/
217315333Serjstatic int
218315333Serjixgbe_bp_timeout(SYSCTL_HANDLER_ARGS)
219315333Serj{
220315333Serj	struct adapter  *adapter = (struct adapter *) arg1;
221315333Serj	struct ixgbe_hw *hw = &adapter->hw;
222315333Serj	int             error = 0;
223315333Serj	static int      timeout = 0;
224315333Serj
225315333Serj	/* Get the current value */
226315333Serj	ixgbe_bypass_mutex_enter(adapter);
227315333Serj	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &timeout);
228315333Serj	ixgbe_bypass_mutex_clear(adapter);
229315333Serj	if (error)
230315333Serj		return (error);
231315333Serj	timeout = (timeout >> BYPASS_WDTIMEOUT_SHIFT) & 0x3;
232315333Serj
233315333Serj	error = sysctl_handle_int(oidp, &timeout, 0, req);
234315333Serj	if ((error) || (req->newptr == NULL))
235315333Serj		return (error);
236315333Serj
237315333Serj	/* Sanity check on the setting */
238315333Serj	switch (timeout) {
239315333Serj	case BYPASS_NOP:
240315333Serj	case BYPASS_NORM:
241315333Serj	case BYPASS_BYPASS:
242315333Serj	case BYPASS_ISOLATE:
243315333Serj		break;
244315333Serj	default:
245315333Serj		return (EINVAL);
246315333Serj	}
247315333Serj
248315333Serj	/* Set the new state */
249315333Serj	ixgbe_bypass_mutex_enter(adapter);
250315333Serj	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
251315333Serj	    BYPASS_WDTIMEOUT_M, timeout << BYPASS_WDTIMEOUT_SHIFT);
252315333Serj	ixgbe_bypass_mutex_clear(adapter);
253315333Serj	usec_delay(6000);
254315333Serj	return (error);
255315333Serj} /* ixgbe_bp_timeout */
256315333Serj
257315333Serj/************************************************************************
258315333Serj * ixgbe_bp_main_on
259315333Serj ************************************************************************/
260315333Serjstatic int
261315333Serjixgbe_bp_main_on(SYSCTL_HANDLER_ARGS)
262315333Serj{
263315333Serj	struct adapter  *adapter = (struct adapter *) arg1;
264315333Serj	struct ixgbe_hw *hw = &adapter->hw;
265315333Serj	int             error = 0;
266315333Serj	static int      main_on = 0;
267315333Serj
268315333Serj	ixgbe_bypass_mutex_enter(adapter);
269315333Serj	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &main_on);
270315333Serj	main_on = (main_on >> BYPASS_MAIN_ON_SHIFT) & 0x3;
271315333Serj	ixgbe_bypass_mutex_clear(adapter);
272315333Serj	if (error)
273315333Serj		return (error);
274315333Serj
275315333Serj	error = sysctl_handle_int(oidp, &main_on, 0, req);
276315333Serj	if ((error) || (req->newptr == NULL))
277315333Serj		return (error);
278315333Serj
279315333Serj	/* Sanity check on the setting */
280315333Serj	switch (main_on) {
281315333Serj	case BYPASS_NOP:
282315333Serj	case BYPASS_NORM:
283315333Serj	case BYPASS_BYPASS:
284315333Serj	case BYPASS_ISOLATE:
285315333Serj		break;
286315333Serj	default:
287315333Serj		return (EINVAL);
288315333Serj	}
289315333Serj
290315333Serj	/* Set the new state */
291315333Serj	ixgbe_bypass_mutex_enter(adapter);
292315333Serj	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
293315333Serj	    BYPASS_MAIN_ON_M, main_on << BYPASS_MAIN_ON_SHIFT);
294315333Serj	ixgbe_bypass_mutex_clear(adapter);
295315333Serj	usec_delay(6000);
296315333Serj	return (error);
297315333Serj} /* ixgbe_bp_main_on */
298315333Serj
299315333Serj/************************************************************************
300315333Serj * ixgbe_bp_main_off
301315333Serj ************************************************************************/
302315333Serjstatic int
303315333Serjixgbe_bp_main_off(SYSCTL_HANDLER_ARGS)
304315333Serj{
305315333Serj	struct adapter  *adapter = (struct adapter *) arg1;
306315333Serj	struct ixgbe_hw *hw = &adapter->hw;
307315333Serj	int             error = 0;
308315333Serj	static int      main_off = 0;
309315333Serj
310315333Serj	ixgbe_bypass_mutex_enter(adapter);
311315333Serj	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &main_off);
312315333Serj	ixgbe_bypass_mutex_clear(adapter);
313315333Serj	if (error)
314315333Serj		return (error);
315315333Serj	main_off = (main_off >> BYPASS_MAIN_OFF_SHIFT) & 0x3;
316315333Serj
317315333Serj	error = sysctl_handle_int(oidp, &main_off, 0, req);
318315333Serj	if ((error) || (req->newptr == NULL))
319315333Serj		return (error);
320315333Serj
321315333Serj	/* Sanity check on the setting */
322315333Serj	switch (main_off) {
323315333Serj	case BYPASS_NOP:
324315333Serj	case BYPASS_NORM:
325315333Serj	case BYPASS_BYPASS:
326315333Serj	case BYPASS_ISOLATE:
327315333Serj		break;
328315333Serj	default:
329315333Serj		return (EINVAL);
330315333Serj	}
331315333Serj
332315333Serj	/* Set the new state */
333315333Serj	ixgbe_bypass_mutex_enter(adapter);
334315333Serj	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
335315333Serj	    BYPASS_MAIN_OFF_M, main_off << BYPASS_MAIN_OFF_SHIFT);
336315333Serj	ixgbe_bypass_mutex_clear(adapter);
337315333Serj	usec_delay(6000);
338315333Serj	return (error);
339315333Serj} /* ixgbe_bp_main_off */
340315333Serj
341315333Serj/************************************************************************
342315333Serj * ixgbe_bp_aux_on
343315333Serj ************************************************************************/
344315333Serjstatic int
345315333Serjixgbe_bp_aux_on(SYSCTL_HANDLER_ARGS)
346315333Serj{
347315333Serj	struct adapter  *adapter = (struct adapter *) arg1;
348315333Serj	struct ixgbe_hw *hw = &adapter->hw;
349315333Serj	int             error = 0;
350315333Serj	static int      aux_on = 0;
351315333Serj
352315333Serj	ixgbe_bypass_mutex_enter(adapter);
353315333Serj	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &aux_on);
354315333Serj	ixgbe_bypass_mutex_clear(adapter);
355315333Serj	if (error)
356315333Serj		return (error);
357315333Serj	aux_on = (aux_on >> BYPASS_AUX_ON_SHIFT) & 0x3;
358315333Serj
359315333Serj	error = sysctl_handle_int(oidp, &aux_on, 0, req);
360315333Serj	if ((error) || (req->newptr == NULL))
361315333Serj		return (error);
362315333Serj
363315333Serj	/* Sanity check on the setting */
364315333Serj	switch (aux_on) {
365315333Serj	case BYPASS_NOP:
366315333Serj	case BYPASS_NORM:
367315333Serj	case BYPASS_BYPASS:
368315333Serj	case BYPASS_ISOLATE:
369315333Serj		break;
370315333Serj	default:
371315333Serj		return (EINVAL);
372315333Serj	}
373315333Serj
374315333Serj	/* Set the new state */
375315333Serj	ixgbe_bypass_mutex_enter(adapter);
376315333Serj	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
377315333Serj	    BYPASS_AUX_ON_M, aux_on << BYPASS_AUX_ON_SHIFT);
378315333Serj	ixgbe_bypass_mutex_clear(adapter);
379315333Serj	usec_delay(6000);
380315333Serj	return (error);
381315333Serj} /* ixgbe_bp_aux_on */
382315333Serj
383315333Serj/************************************************************************
384315333Serj * ixgbe_bp_aux_off
385315333Serj ************************************************************************/
386315333Serjstatic int
387315333Serjixgbe_bp_aux_off(SYSCTL_HANDLER_ARGS)
388315333Serj{
389315333Serj	struct adapter  *adapter = (struct adapter *) arg1;
390315333Serj	struct ixgbe_hw *hw = &adapter->hw;
391315333Serj	int             error = 0;
392315333Serj	static int      aux_off = 0;
393315333Serj
394315333Serj	ixgbe_bypass_mutex_enter(adapter);
395315333Serj	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &aux_off);
396315333Serj	ixgbe_bypass_mutex_clear(adapter);
397315333Serj	if (error)
398315333Serj		return (error);
399315333Serj	aux_off = (aux_off >> BYPASS_AUX_OFF_SHIFT) & 0x3;
400315333Serj
401315333Serj	error = sysctl_handle_int(oidp, &aux_off, 0, req);
402315333Serj	if ((error) || (req->newptr == NULL))
403315333Serj		return (error);
404315333Serj
405315333Serj	/* Sanity check on the setting */
406315333Serj	switch (aux_off) {
407315333Serj	case BYPASS_NOP:
408315333Serj	case BYPASS_NORM:
409315333Serj	case BYPASS_BYPASS:
410315333Serj	case BYPASS_ISOLATE:
411315333Serj		break;
412315333Serj	default:
413315333Serj		return (EINVAL);
414315333Serj	}
415315333Serj
416315333Serj	/* Set the new state */
417315333Serj	ixgbe_bypass_mutex_enter(adapter);
418315333Serj	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
419315333Serj	    BYPASS_AUX_OFF_M, aux_off << BYPASS_AUX_OFF_SHIFT);
420315333Serj	ixgbe_bypass_mutex_clear(adapter);
421315333Serj	usec_delay(6000);
422315333Serj	return (error);
423315333Serj} /* ixgbe_bp_aux_off */
424315333Serj
425315333Serj/************************************************************************
426315333Serj * ixgbe_bp_wd_set - Set the Watchdog timer value
427315333Serj *
428315333Serj *   Valid settings are:
429315333Serj *	- 0 will disable the watchdog
430315333Serj *	- 1, 2, 3, 4, 8, 16, 32
431315333Serj *	- anything else is invalid and will be ignored
432315333Serj ************************************************************************/
433315333Serjstatic int
434315333Serjixgbe_bp_wd_set(SYSCTL_HANDLER_ARGS)
435315333Serj{
436315333Serj	struct adapter  *adapter = (struct adapter *) arg1;
437315333Serj	struct ixgbe_hw *hw = &adapter->hw;
438315333Serj	int             error, tmp;
439315333Serj	static int      timeout = 0;
440315333Serj	u32             mask, arg = BYPASS_PAGE_CTL0;
441315333Serj
442315333Serj	/* Get the current hardware value */
443315333Serj	ixgbe_bypass_mutex_enter(adapter);
444315333Serj	error = hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL0, &tmp);
445315333Serj	ixgbe_bypass_mutex_clear(adapter);
446315333Serj	if (error)
447315333Serj		return (error);
448315333Serj	/*
449315333Serj	 * If armed keep the displayed value,
450315333Serj	 * else change the display to zero.
451315333Serj	 */
452315333Serj	if ((tmp & (0x1 << BYPASS_WDT_ENABLE_SHIFT)) == 0)
453315333Serj		timeout = 0;
454315333Serj
455315333Serj	error = sysctl_handle_int(oidp, &timeout, 0, req);
456315333Serj	if ((error) || (req->newptr == NULL))
457315333Serj		return (error);
458315333Serj
459315333Serj	mask = BYPASS_WDT_ENABLE_M;
460315333Serj	switch (timeout) {
461315333Serj		case 0: /* disables the timer */
462315333Serj			break;
463315333Serj		case 1:
464315333Serj			arg = BYPASS_WDT_1_5 << BYPASS_WDT_TIME_SHIFT;
465315333Serj			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
466315333Serj			mask |= BYPASS_WDT_VALUE_M;
467315333Serj			break;
468315333Serj		case 2:
469315333Serj			arg = BYPASS_WDT_2 << BYPASS_WDT_TIME_SHIFT;
470315333Serj			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
471315333Serj			mask |= BYPASS_WDT_VALUE_M;
472315333Serj			break;
473315333Serj		case 3:
474315333Serj			arg = BYPASS_WDT_3 << BYPASS_WDT_TIME_SHIFT;
475315333Serj			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
476315333Serj			mask |= BYPASS_WDT_VALUE_M;
477315333Serj			break;
478315333Serj		case 4:
479315333Serj			arg = BYPASS_WDT_4 << BYPASS_WDT_TIME_SHIFT;
480315333Serj			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
481315333Serj			mask |= BYPASS_WDT_VALUE_M;
482315333Serj			break;
483315333Serj		case 8:
484315333Serj			arg = BYPASS_WDT_8 << BYPASS_WDT_TIME_SHIFT;
485315333Serj			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
486315333Serj			mask |= BYPASS_WDT_VALUE_M;
487315333Serj			break;
488315333Serj		case 16:
489315333Serj			arg = BYPASS_WDT_16 << BYPASS_WDT_TIME_SHIFT;
490315333Serj			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
491315333Serj			mask |= BYPASS_WDT_VALUE_M;
492315333Serj			break;
493315333Serj		case 32:
494315333Serj			arg = BYPASS_WDT_32 << BYPASS_WDT_TIME_SHIFT;
495315333Serj			arg |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
496315333Serj			mask |= BYPASS_WDT_VALUE_M;
497315333Serj			break;
498315333Serj		default:
499315333Serj			return (EINVAL);
500315333Serj	}
501315333Serj	/* Set the new watchdog */
502315333Serj	ixgbe_bypass_mutex_enter(adapter);
503315333Serj	error = hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL0, mask, arg);
504315333Serj	ixgbe_bypass_mutex_clear(adapter);
505315333Serj
506315333Serj	return (error);
507315333Serj} /* ixgbe_bp_wd_set */
508315333Serj
509315333Serj/************************************************************************
510315333Serj * ixgbe_bp_wd_reset - Reset the Watchdog timer
511315333Serj *
512315333Serj *    To activate this it must be called with any argument.
513315333Serj ************************************************************************/
514315333Serjstatic int
515315333Serjixgbe_bp_wd_reset(SYSCTL_HANDLER_ARGS)
516315333Serj{
517315333Serj	struct adapter  *adapter = (struct adapter *) arg1;
518315333Serj	struct ixgbe_hw *hw = &adapter->hw;
519315333Serj	u32             sec, year;
520315333Serj	int             cmd, count = 0, error = 0;
521315333Serj	int             reset_wd = 0;
522315333Serj
523315333Serj	error = sysctl_handle_int(oidp, &reset_wd, 0, req);
524315333Serj	if ((error) || (req->newptr == NULL))
525315333Serj		return (error);
526315333Serj
527315333Serj	cmd = BYPASS_PAGE_CTL1 | BYPASS_WE | BYPASS_CTL1_WDT_PET;
528315333Serj
529315333Serj	/* Resync the FW time while writing to CTL1 anyway */
530315333Serj	ixgbe_get_bypass_time(&year, &sec);
531315333Serj
532315333Serj	cmd |= (sec & BYPASS_CTL1_TIME_M) | BYPASS_CTL1_VALID;
533315333Serj	cmd |= BYPASS_CTL1_OFFTRST;
534315333Serj
535315333Serj	ixgbe_bypass_wd_mutex_enter(adapter);
536315333Serj	error = hw->mac.ops.bypass_rw(hw, cmd, &reset_wd);
537315333Serj
538315333Serj	/* Read until it matches what we wrote, or we time out */
539315333Serj	do {
540315333Serj		if (count++ > 10) {
541315333Serj			error = IXGBE_BYPASS_FW_WRITE_FAILURE;
542315333Serj			break;
543315333Serj		}
544315333Serj		if (hw->mac.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &reset_wd)) {
545315333Serj			error = IXGBE_ERR_INVALID_ARGUMENT;
546315333Serj			break;
547315333Serj		}
548315333Serj	} while (!hw->mac.ops.bypass_valid_rd(cmd, reset_wd));
549315333Serj
550315333Serj	reset_wd = 0;
551315333Serj	ixgbe_bypass_wd_mutex_clear(adapter);
552315333Serj	return (error);
553315333Serj} /* ixgbe_bp_wd_reset */
554315333Serj
555315333Serj/************************************************************************
556315333Serj * ixgbe_bp_log - Display the bypass log
557315333Serj *
558315333Serj *   You must pass a non-zero arg to sysctl
559315333Serj ************************************************************************/
560315333Serjstatic int
561315333Serjixgbe_bp_log(SYSCTL_HANDLER_ARGS)
562315333Serj{
563315333Serj	struct adapter             *adapter = (struct adapter *) arg1;
564315333Serj	struct ixgbe_hw            *hw = &adapter->hw;
565315333Serj	u32                        cmd, base, head;
566315333Serj	u32                        log_off, count = 0;
567315333Serj	static int                 status = 0;
568315333Serj	u8                         data;
569315333Serj	struct ixgbe_bypass_eeprom eeprom[BYPASS_MAX_LOGS];
570315333Serj	int                        i, error = 0;
571315333Serj
572315333Serj	error = sysctl_handle_int(oidp, &status, 0, req);
573315333Serj	if ((error) || (req->newptr == NULL))
574315333Serj		return (error);
575315333Serj
576315333Serj	/* Keep the log display single-threaded */
577315333Serj	while (atomic_cmpset_int(&adapter->bypass.log, 0, 1) == 0)
578315333Serj		usec_delay(3000);
579315333Serj
580315333Serj	ixgbe_bypass_mutex_enter(adapter);
581315333Serj
582315333Serj	/* Find Current head of the log eeprom offset */
583315333Serj	cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
584315333Serj	cmd |= (0x1 << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
585315333Serj	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
586315333Serj	if (error)
587315333Serj		goto unlock_err;
588315333Serj
589315333Serj	/* wait for the write to stick */
590315333Serj	msec_delay(100);
591315333Serj
592315333Serj	/* Now read the results */
593315333Serj	cmd &= ~BYPASS_WE;
594315333Serj	error = hw->mac.ops.bypass_rw(hw, cmd, &status);
595315333Serj	if (error)
596315333Serj		goto unlock_err;
597315333Serj
598315333Serj	ixgbe_bypass_mutex_clear(adapter);
599315333Serj
600315333Serj	base = status & BYPASS_CTL2_DATA_M;
601315333Serj	head = (status & BYPASS_CTL2_HEAD_M) >> BYPASS_CTL2_HEAD_SHIFT;
602315333Serj
603315333Serj	/* address of the first log */
604315333Serj	log_off = base + (head * 5);
605315333Serj
606315333Serj	/* extract all the log entries */
607315333Serj	while (count < BYPASS_MAX_LOGS) {
608315333Serj		eeprom[count].logs = 0;
609315333Serj		eeprom[count].actions = 0;
610315333Serj
611315333Serj		/* Log 5 bytes store in on u32 and a u8 */
612315333Serj		for (i = 0; i < 4; i++) {
613315333Serj			ixgbe_bypass_mutex_enter(adapter);
614315333Serj			error = hw->mac.ops.bypass_rd_eep(hw, log_off + i,
615315333Serj			    &data);
616315333Serj			ixgbe_bypass_mutex_clear(adapter);
617315333Serj			if (error)
618315333Serj				return (-EINVAL);
619315333Serj			eeprom[count].logs += data << (8 * i);
620315333Serj		}
621315333Serj
622315333Serj		ixgbe_bypass_mutex_enter(adapter);
623315333Serj		error = hw->mac.ops.bypass_rd_eep(hw,
624315333Serj		    log_off + i, &eeprom[count].actions);
625315333Serj		ixgbe_bypass_mutex_clear(adapter);
626315333Serj		if (error)
627315333Serj			return (-EINVAL);
628315333Serj
629315333Serj		/* Quit if not a unread log */
630315333Serj		if (!(eeprom[count].logs & BYPASS_LOG_CLEAR_M))
631315333Serj			break;
632315333Serj		/*
633315333Serj		 * Log looks good so store the address where it's
634315333Serj		 * Unread Log bit is so we can clear it after safely
635315333Serj		 * pulling out all of the log data.
636315333Serj		 */
637315333Serj		eeprom[count].clear_off = log_off;
638315333Serj
639315333Serj		count++;
640315333Serj		head = head ? head - 1 : BYPASS_MAX_LOGS;
641315333Serj		log_off = base + (head * 5);
642315333Serj	}
643315333Serj
644315333Serj	/* reverse order (oldest first) for output */
645315333Serj	while (count--) {
646315333Serj		int year;
647315333Serj		u32 mon, days, hours, min, sec;
648315333Serj		u32 time = eeprom[count].logs & BYPASS_LOG_TIME_M;
649315333Serj		u32 event = (eeprom[count].logs & BYPASS_LOG_EVENT_M) >>
650315333Serj		    BYPASS_LOG_EVENT_SHIFT;
651315333Serj		u8 action =  eeprom[count].actions & BYPASS_LOG_ACTION_M;
652315333Serj		u16 day_mon[2][13] = {
653315333Serj		  {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
654315333Serj		  {0, 31, 59, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
655315333Serj		};
656315333Serj		char *event_str[] = {"unknown", "main on", "aux on",
657315333Serj		    "main off", "aux off", "WDT", "user" };
658315333Serj		char *action_str[] = {"ignore", "normal", "bypass", "isolate",};
659315333Serj
660315333Serj		/* verify vaild data  1 - 6 */
661315333Serj		if (event < BYPASS_EVENT_MAIN_ON || event > BYPASS_EVENT_USR)
662315333Serj			event = 0;
663315333Serj
664315333Serj		/*
665315333Serj		 * time is in sec's this year, so convert to something
666315333Serj		 * printable.
667315333Serj		 */
668315333Serj		ixgbe_get_bypass_time(&year, &sec);
669315333Serj		days = time / SEC_PER_DAY;
670315333Serj		for (i = 11; days < day_mon[LEAP_YR(year)][i]; i--)
671315333Serj			continue;
672315333Serj		mon = i + 1;    /* display month as 1-12 */
673315333Serj		time -= (day_mon[LEAP_YR(year)][i] * SEC_PER_DAY);
674315333Serj		days = (time / SEC_PER_DAY) + 1;  /* first day is 1 */
675315333Serj		time %= SEC_PER_DAY;
676315333Serj		hours = time / (60 * 60);
677315333Serj		time %= (60 * 60);
678315333Serj		min = time / 60;
679315333Serj		sec = time % 60;
680315333Serj		device_printf(adapter->dev,
681315333Serj		    "UT %02d/%02d %02d:%02d:%02d %8.8s -> %7.7s\n",
682315333Serj		    mon, days, hours, min, sec, event_str[event],
683315333Serj		    action_str[action]);
684315333Serj		cmd = BYPASS_PAGE_CTL2 | BYPASS_WE | BYPASS_CTL2_RW;
685315333Serj		cmd |= ((eeprom[count].clear_off + 3)
686315333Serj		    << BYPASS_CTL2_OFFSET_SHIFT) & BYPASS_CTL2_OFFSET_M;
687315333Serj		cmd |= ((eeprom[count].logs & ~BYPASS_LOG_CLEAR_M) >> 24);
688315333Serj
689315333Serj		ixgbe_bypass_mutex_enter(adapter);
690315333Serj
691315333Serj		error = hw->mac.ops.bypass_rw(hw, cmd, &status);
692315333Serj
693315333Serj		/* wait for the write to stick */
694315333Serj		msec_delay(100);
695315333Serj
696315333Serj		ixgbe_bypass_mutex_clear(adapter);
697315333Serj
698315333Serj		if (error)
699315333Serj			return (-EINVAL);
700315333Serj	}
701315333Serj
702315333Serj	status = 0; /* reset */
703315333Serj	/* Another log command can now run */
704315333Serj	while (atomic_cmpset_int(&adapter->bypass.log, 1, 0) == 0)
705315333Serj		usec_delay(3000);
706315333Serj	return(error);
707315333Serj
708315333Serjunlock_err:
709315333Serj	ixgbe_bypass_mutex_clear(adapter);
710315333Serj	status = 0; /* reset */
711315333Serj	while (atomic_cmpset_int(&adapter->bypass.log, 1, 0) == 0)
712315333Serj		usec_delay(3000);
713315333Serj	return (-EINVAL);
714315333Serj} /* ixgbe_bp_log */
715315333Serj
716315333Serj/************************************************************************
717315333Serj * ixgbe_bypass_init - Set up infrastructure for the bypass feature
718315333Serj *
719315333Serj *   Do time and sysctl initialization here.  This feature is
720315333Serj *   only enabled for the first port of a bypass adapter.
721315333Serj ************************************************************************/
722315333Serjvoid
723315333Serjixgbe_bypass_init(struct adapter *adapter)
724315333Serj{
725315333Serj	struct ixgbe_hw        *hw = &adapter->hw;
726315333Serj	device_t               dev = adapter->dev;
727315333Serj	struct sysctl_oid      *bp_node;
728315333Serj	struct sysctl_oid_list *bp_list;
729315333Serj	u32                    mask, value, sec, year;
730315333Serj
731315333Serj	if (!(adapter->feat_cap & IXGBE_FEATURE_BYPASS))
732315333Serj		return;
733315333Serj
734315333Serj	/* First set up time for the hardware */
735315333Serj	ixgbe_get_bypass_time(&year, &sec);
736315333Serj
737315333Serj	mask = BYPASS_CTL1_TIME_M
738315333Serj	     | BYPASS_CTL1_VALID_M
739315333Serj	     | BYPASS_CTL1_OFFTRST_M;
740315333Serj
741315333Serj	value = (sec & BYPASS_CTL1_TIME_M)
742315333Serj	      | BYPASS_CTL1_VALID
743315333Serj	      | BYPASS_CTL1_OFFTRST;
744315333Serj
745315333Serj	ixgbe_bypass_mutex_enter(adapter);
746315333Serj	hw->mac.ops.bypass_set(hw, BYPASS_PAGE_CTL1, mask, value);
747315333Serj	ixgbe_bypass_mutex_clear(adapter);
748315333Serj
749315333Serj	/* Now set up the SYSCTL infrastructure */
750315333Serj
751315333Serj	/*
752315333Serj	 * The log routine is kept separate from the other
753315333Serj	 * children so a general display command like:
754315333Serj	 * `sysctl dev.ix.0.bypass` will not show the log.
755315333Serj	 */
756315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
757315333Serj	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
758315333Serj	    OID_AUTO, "bypass_log", CTLTYPE_INT | CTLFLAG_RW,
759315333Serj	    adapter, 0, ixgbe_bp_log, "I", "Bypass Log");
760315333Serj
761315333Serj	/* All other setting are hung from the 'bypass' node */
762315333Serj	bp_node = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
763315333Serj	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
764315333Serj	    OID_AUTO, "bypass", CTLFLAG_RD, NULL, "Bypass");
765315333Serj
766315333Serj	bp_list = SYSCTL_CHILDREN(bp_node);
767315333Serj
768315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
769315333Serj	    OID_AUTO, "version", CTLTYPE_INT | CTLFLAG_RD,
770315333Serj	    adapter, 0, ixgbe_bp_version, "I", "Bypass Version");
771315333Serj
772315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
773315333Serj	    OID_AUTO, "state", CTLTYPE_INT | CTLFLAG_RW,
774315333Serj	    adapter, 0, ixgbe_bp_set_state, "I", "Bypass State");
775315333Serj
776315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
777315333Serj	    OID_AUTO, "timeout", CTLTYPE_INT | CTLFLAG_RW,
778315333Serj	    adapter, 0, ixgbe_bp_timeout, "I", "Bypass Timeout");
779315333Serj
780315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
781315333Serj	    OID_AUTO, "main_on", CTLTYPE_INT | CTLFLAG_RW,
782315333Serj	    adapter, 0, ixgbe_bp_main_on, "I", "Bypass Main On");
783315333Serj
784315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
785315333Serj	    OID_AUTO, "main_off", CTLTYPE_INT | CTLFLAG_RW,
786315333Serj	    adapter, 0, ixgbe_bp_main_off, "I", "Bypass Main Off");
787315333Serj
788315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
789315333Serj	    OID_AUTO, "aux_on", CTLTYPE_INT | CTLFLAG_RW,
790315333Serj	    adapter, 0, ixgbe_bp_aux_on, "I", "Bypass Aux On");
791315333Serj
792315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
793315333Serj	    OID_AUTO, "aux_off", CTLTYPE_INT | CTLFLAG_RW,
794315333Serj	    adapter, 0, ixgbe_bp_aux_off, "I", "Bypass Aux Off");
795315333Serj
796315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
797315333Serj	    OID_AUTO, "wd_set", CTLTYPE_INT | CTLFLAG_RW,
798315333Serj	    adapter, 0, ixgbe_bp_wd_set, "I", "Set BP Watchdog");
799315333Serj
800315333Serj	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), bp_list,
801315333Serj	    OID_AUTO, "wd_reset", CTLTYPE_INT | CTLFLAG_WR,
802315333Serj	    adapter, 0, ixgbe_bp_wd_reset, "S", "Bypass WD Reset");
803315333Serj
804315333Serj	adapter->feat_en |= IXGBE_FEATURE_BYPASS;
805315333Serj
806315333Serj	return;
807315333Serj} /* ixgbe_bypass_init */
808315333Serj
809