1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation
3 */
4
5 #include <time.h>
6 #include <rte_atomic.h>
7 #include <rte_ethdev_driver.h>
8 #include "ixgbe_ethdev.h"
9 #include "ixgbe_bypass_api.h"
10 #include "rte_pmd_ixgbe.h"
11
12 #define BYPASS_STATUS_OFF_MASK 3
13
14 /* Macros to check for invlaid function pointers. */
15 #define FUNC_PTR_OR_ERR_RET(func, retval) do { \
16 if ((func) == NULL) { \
17 PMD_DRV_LOG(ERR, "%s:%d function not supported", \
18 __func__, __LINE__); \
19 return retval; \
20 } \
21 } while (0)
22
23 #define FUNC_PTR_OR_RET(func) do { \
24 if ((func) == NULL) { \
25 PMD_DRV_LOG(ERR, "%s:%d function not supported", \
26 __func__, __LINE__); \
27 return; \
28 } \
29 } while (0)
30
31
32 /**
33 * ixgbe_bypass_set_time - Set bypass FW time epoc.
34 *
35 * @hw: pointer to hardware structure
36 *
37 * This function with sync the FW date stamp with that of the
38 * system clock.
39 **/
40 static void
ixgbe_bypass_set_time(struct ixgbe_adapter * adapter)41 ixgbe_bypass_set_time(struct ixgbe_adapter *adapter)
42 {
43 u32 mask, value;
44 u32 sec;
45 struct ixgbe_hw *hw = &adapter->hw;
46
47 sec = 0;
48
49 /*
50 * Send the FW our current time and turn on time_valid and
51 * timer_reset bits.
52 */
53 mask = BYPASS_CTL1_TIME_M |
54 BYPASS_CTL1_VALID_M |
55 BYPASS_CTL1_OFFTRST_M;
56 value = (sec & BYPASS_CTL1_TIME_M) |
57 BYPASS_CTL1_VALID |
58 BYPASS_CTL1_OFFTRST;
59
60 FUNC_PTR_OR_RET(adapter->bps.ops.bypass_set);
61
62 /* Store FW reset time (in seconds from epoch). */
63 adapter->bps.reset_tm = time(NULL);
64
65 /* reset FW timer. */
66 adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL1, mask, value);
67 }
68
69 /**
70 * ixgbe_bypass_init - Make some environment changes for bypass
71 *
72 * @adapter: pointer to ixgbe_adapter structure for access to state bits
73 *
74 * This function collects all the modifications needed by the bypass
75 * driver.
76 **/
77 void
ixgbe_bypass_init(struct rte_eth_dev * dev)78 ixgbe_bypass_init(struct rte_eth_dev *dev)
79 {
80 struct ixgbe_adapter *adapter;
81 struct ixgbe_hw *hw;
82
83 adapter = IXGBE_DEV_TO_ADPATER(dev);
84 hw = &adapter->hw;
85
86 /* Only allow BYPASS ops on the first port */
87 if (hw->device_id != IXGBE_DEV_ID_82599_BYPASS ||
88 hw->bus.func != 0) {
89 PMD_DRV_LOG(ERR, "bypass function is not supported on that device");
90 return;
91 }
92
93 /* set bypass ops. */
94 adapter->bps.ops.bypass_rw = &ixgbe_bypass_rw_generic;
95 adapter->bps.ops.bypass_valid_rd = &ixgbe_bypass_valid_rd_generic;
96 adapter->bps.ops.bypass_set = &ixgbe_bypass_set_generic;
97 adapter->bps.ops.bypass_rd_eep = &ixgbe_bypass_rd_eep_generic;
98
99 /* set the time for logging. */
100 ixgbe_bypass_set_time(adapter);
101
102 /* Don't have the SDP to the laser */
103 hw->mac.ops.disable_tx_laser = NULL;
104 hw->mac.ops.enable_tx_laser = NULL;
105 hw->mac.ops.flap_tx_laser = NULL;
106 }
107
108 s32
ixgbe_bypass_state_show(struct rte_eth_dev * dev,u32 * state)109 ixgbe_bypass_state_show(struct rte_eth_dev *dev, u32 *state)
110 {
111 struct ixgbe_hw *hw;
112 s32 ret_val;
113 u32 cmd;
114 u32 by_ctl = 0;
115 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
116
117 hw = &adapter->hw;
118 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
119
120 cmd = BYPASS_PAGE_CTL0;
121 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &by_ctl);
122
123 /* Assume bypass_rw didn't error out, if it did state will
124 * be ignored anyway.
125 */
126 *state = (by_ctl >> BYPASS_STATUS_OFF_SHIFT) & BYPASS_STATUS_OFF_MASK;
127
128 return ret_val;
129 }
130
131
132 s32
ixgbe_bypass_state_store(struct rte_eth_dev * dev,u32 * new_state)133 ixgbe_bypass_state_store(struct rte_eth_dev *dev, u32 *new_state)
134 {
135 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
136 struct ixgbe_hw *hw;
137 s32 ret_val;
138
139 hw = &adapter->hw;
140 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_set, -ENOTSUP);
141
142 /* Set the new state */
143 ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
144 BYPASS_MODE_OFF_M, *new_state);
145 if (ret_val)
146 goto exit;
147
148 /* Set AUTO back on so FW can receive events */
149 ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
150 BYPASS_MODE_OFF_M, BYPASS_AUTO);
151
152 exit:
153 return ret_val;
154
155 }
156
157 s32
ixgbe_bypass_event_show(struct rte_eth_dev * dev,u32 event,u32 * state)158 ixgbe_bypass_event_show(struct rte_eth_dev *dev, u32 event,
159 u32 *state)
160 {
161 struct ixgbe_hw *hw;
162 s32 ret_val;
163 u32 shift;
164 u32 cmd;
165 u32 by_ctl = 0;
166 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
167
168 hw = &adapter->hw;
169 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
170
171 cmd = BYPASS_PAGE_CTL0;
172 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &by_ctl);
173
174 /* Assume bypass_rw didn't error out, if it did event will
175 * be ignored anyway.
176 */
177 switch (event) {
178 case BYPASS_EVENT_WDT_TO:
179 shift = BYPASS_WDTIMEOUT_SHIFT;
180 break;
181 case BYPASS_EVENT_MAIN_ON:
182 shift = BYPASS_MAIN_ON_SHIFT;
183 break;
184 case BYPASS_EVENT_MAIN_OFF:
185 shift = BYPASS_MAIN_OFF_SHIFT;
186 break;
187 case BYPASS_EVENT_AUX_ON:
188 shift = BYPASS_AUX_ON_SHIFT;
189 break;
190 case BYPASS_EVENT_AUX_OFF:
191 shift = BYPASS_AUX_OFF_SHIFT;
192 break;
193 default:
194 return EINVAL;
195 }
196
197 *state = (by_ctl >> shift) & 0x3;
198
199 return ret_val;
200 }
201
202 s32
ixgbe_bypass_event_store(struct rte_eth_dev * dev,u32 event,u32 state)203 ixgbe_bypass_event_store(struct rte_eth_dev *dev, u32 event,
204 u32 state)
205 {
206 struct ixgbe_hw *hw;
207 u32 status;
208 u32 off;
209 s32 ret_val;
210 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
211
212 hw = &adapter->hw;
213 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_set, -ENOTSUP);
214
215 switch (event) {
216 case BYPASS_EVENT_WDT_TO:
217 off = BYPASS_WDTIMEOUT_M;
218 status = state << BYPASS_WDTIMEOUT_SHIFT;
219 break;
220 case BYPASS_EVENT_MAIN_ON:
221 off = BYPASS_MAIN_ON_M;
222 status = state << BYPASS_MAIN_ON_SHIFT;
223 break;
224 case BYPASS_EVENT_MAIN_OFF:
225 off = BYPASS_MAIN_OFF_M;
226 status = state << BYPASS_MAIN_OFF_SHIFT;
227 break;
228 case BYPASS_EVENT_AUX_ON:
229 off = BYPASS_AUX_ON_M;
230 status = state << BYPASS_AUX_ON_SHIFT;
231 break;
232 case BYPASS_EVENT_AUX_OFF:
233 off = BYPASS_AUX_OFF_M;
234 status = state << BYPASS_AUX_OFF_SHIFT;
235 break;
236 default:
237 return EINVAL;
238 }
239
240 ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
241 off, status);
242
243 return ret_val;
244 }
245
246 s32
ixgbe_bypass_wd_timeout_store(struct rte_eth_dev * dev,u32 timeout)247 ixgbe_bypass_wd_timeout_store(struct rte_eth_dev *dev, u32 timeout)
248 {
249 struct ixgbe_hw *hw;
250 u32 status;
251 u32 mask;
252 s32 ret_val;
253 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
254
255 hw = &adapter->hw;
256 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_set, -ENOTSUP);
257
258 /* disable the timer with timeout of zero */
259 if (timeout == RTE_PMD_IXGBE_BYPASS_TMT_OFF) {
260 status = 0x0; /* WDG enable off */
261 mask = BYPASS_WDT_ENABLE_M;
262 } else {
263 /* set time out value */
264 mask = BYPASS_WDT_VALUE_M;
265
266 /* enable the timer */
267 status = timeout << BYPASS_WDT_TIME_SHIFT;
268 status |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
269 mask |= BYPASS_WDT_ENABLE_M;
270 }
271
272 ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
273 mask, status);
274
275 return ret_val;
276 }
277
278 s32
ixgbe_bypass_ver_show(struct rte_eth_dev * dev,u32 * ver)279 ixgbe_bypass_ver_show(struct rte_eth_dev *dev, u32 *ver)
280 {
281 struct ixgbe_hw *hw;
282 u32 cmd;
283 u32 status;
284 s32 ret_val;
285 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
286
287 hw = &adapter->hw;
288 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
289
290 cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
291 cmd |= (BYPASS_EEPROM_VER_ADD << BYPASS_CTL2_OFFSET_SHIFT) &
292 BYPASS_CTL2_OFFSET_M;
293 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &status);
294 if (ret_val)
295 goto exit;
296
297 /* wait for the write to stick */
298 msleep(100);
299
300 /* Now read the results */
301 cmd &= ~BYPASS_WE;
302 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &status);
303 if (ret_val)
304 goto exit;
305
306 *ver = status & BYPASS_CTL2_DATA_M; /* only one byte of date */
307
308 exit:
309 return ret_val;
310 }
311
312 s32
ixgbe_bypass_wd_timeout_show(struct rte_eth_dev * dev,u32 * wd_timeout)313 ixgbe_bypass_wd_timeout_show(struct rte_eth_dev *dev, u32 *wd_timeout)
314 {
315 struct ixgbe_hw *hw;
316 u32 by_ctl = 0;
317 u32 cmd;
318 u32 wdg;
319 s32 ret_val;
320 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
321
322 hw = &adapter->hw;
323 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
324
325 cmd = BYPASS_PAGE_CTL0;
326 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &by_ctl);
327
328 wdg = by_ctl & BYPASS_WDT_ENABLE_M;
329 if (!wdg)
330 *wd_timeout = RTE_PMD_IXGBE_BYPASS_TMT_OFF;
331 else
332 *wd_timeout = (by_ctl >> BYPASS_WDT_TIME_SHIFT) &
333 BYPASS_WDT_MASK;
334
335 return ret_val;
336 }
337
338 s32
ixgbe_bypass_wd_reset(struct rte_eth_dev * dev)339 ixgbe_bypass_wd_reset(struct rte_eth_dev *dev)
340 {
341 u32 cmd;
342 u32 status;
343 u32 sec;
344 u32 count = 0;
345 s32 ret_val;
346 struct ixgbe_hw *hw;
347 struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
348
349 hw = &adapter->hw;
350
351 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
352 FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_valid_rd, -ENOTSUP);
353
354 /* Use the lower level bit-bang functions since we don't need
355 * to read the register first to get it's current state as we
356 * are setting every thing in this write.
357 */
358 /* Set up WD pet */
359 cmd = BYPASS_PAGE_CTL1 | BYPASS_WE | BYPASS_CTL1_WDT_PET;
360
361 /* Resync the FW time while writing to CTL1 anyway */
362 adapter->bps.reset_tm = time(NULL);
363 sec = 0;
364
365 cmd |= (sec & BYPASS_CTL1_TIME_M) | BYPASS_CTL1_VALID;
366
367 /* reset FW timer offset since we are resetting the clock */
368 cmd |= BYPASS_CTL1_OFFTRST;
369
370 ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &status);
371
372 /* Read until it matches what we wrote, or we time out */
373 do {
374 if (count++ > 10) {
375 ret_val = IXGBE_BYPASS_FW_WRITE_FAILURE;
376 break;
377 }
378
379 if (adapter->bps.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &status)) {
380 ret_val = IXGBE_ERR_INVALID_ARGUMENT;
381 break;
382 }
383 } while (!adapter->bps.ops.bypass_valid_rd(cmd, status));
384
385 return ret_val;
386 }
387