Commit | Line | Data |
---|---|---|
8ceee660 | 1 | /**************************************************************************** |
177dfcd8 BH |
2 | * Driver for Solarflare Solarstorm network controllers and boards |
3 | * Copyright 2007-2008 Solarflare Communications Inc. | |
8ceee660 BH |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published | |
7 | * by the Free Software Foundation, incorporated herein by reference. | |
8 | */ | |
9 | ||
10 | #include <linux/delay.h> | |
11 | #include <linux/seq_file.h> | |
12 | #include "efx.h" | |
8ceee660 BH |
13 | #include "mdio_10g.h" |
14 | #include "falcon.h" | |
15 | #include "phy.h" | |
16 | #include "falcon_hwdefs.h" | |
17 | #include "boards.h" | |
8ceee660 BH |
18 | |
19 | /* We expect these MMDs to be in the package */ | |
27dd2cac BH |
20 | #define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PMAPMD | \ |
21 | MDIO_MMDREG_DEVS_PCS | \ | |
04cc8cac BH |
22 | MDIO_MMDREG_DEVS_PHYXS | \ |
23 | MDIO_MMDREG_DEVS_AN) | |
8ceee660 | 24 | |
3273c2e8 BH |
25 | #define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \ |
26 | (1 << LOOPBACK_PCS) | \ | |
27 | (1 << LOOPBACK_PMAPMD) | \ | |
28 | (1 << LOOPBACK_NETWORK)) | |
29 | ||
8ceee660 BH |
30 | /* We complain if we fail to see the link partner as 10G capable this many |
31 | * times in a row (must be > 1 as sampling the autoneg. registers is racy) | |
32 | */ | |
33 | #define MAX_BAD_LP_TRIES (5) | |
34 | ||
35 | /* Extended control register */ | |
36 | #define PMA_PMD_XCONTROL_REG 0xc000 | |
37 | #define PMA_PMD_LNPGA_POWERDOWN_LBN 8 | |
38 | #define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1 | |
39 | ||
40 | /* extended status register */ | |
41 | #define PMA_PMD_XSTATUS_REG 0xc001 | |
42 | #define PMA_PMD_XSTAT_FLP_LBN (12) | |
43 | ||
44 | /* LED control register */ | |
45 | #define PMA_PMD_LED_CTRL_REG (0xc007) | |
46 | #define PMA_PMA_LED_ACTIVITY_LBN (3) | |
47 | ||
48 | /* LED function override register */ | |
49 | #define PMA_PMD_LED_OVERR_REG (0xc009) | |
50 | /* Bit positions for different LEDs (there are more but not wired on SFE4001)*/ | |
51 | #define PMA_PMD_LED_LINK_LBN (0) | |
52 | #define PMA_PMD_LED_SPEED_LBN (2) | |
53 | #define PMA_PMD_LED_TX_LBN (4) | |
54 | #define PMA_PMD_LED_RX_LBN (6) | |
55 | /* Override settings */ | |
56 | #define PMA_PMD_LED_AUTO (0) /* H/W control */ | |
57 | #define PMA_PMD_LED_ON (1) | |
58 | #define PMA_PMD_LED_OFF (2) | |
59 | #define PMA_PMD_LED_FLASH (3) | |
04cc8cac | 60 | #define PMA_PMD_LED_MASK 3 |
8ceee660 BH |
61 | /* All LEDs under hardware control */ |
62 | #define PMA_PMD_LED_FULL_AUTO (0) | |
63 | /* Green and Amber under hardware control, Red off */ | |
64 | #define PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) | |
65 | ||
66 | ||
3273c2e8 BH |
67 | /* Special Software reset register */ |
68 | #define PMA_PMD_EXT_CTRL_REG 49152 | |
69 | #define PMA_PMD_EXT_SSR_LBN 15 | |
70 | ||
8ceee660 BH |
71 | /* Misc register defines */ |
72 | #define PCS_CLOCK_CTRL_REG 0xd801 | |
73 | #define PLL312_RST_N_LBN 2 | |
74 | ||
75 | #define PCS_SOFT_RST2_REG 0xd806 | |
76 | #define SERDES_RST_N_LBN 13 | |
77 | #define XGXS_RST_N_LBN 12 | |
78 | ||
79 | #define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */ | |
80 | #define CLK312_EN_LBN 3 | |
81 | ||
3273c2e8 BH |
82 | /* PHYXS registers */ |
83 | #define PHYXS_TEST1 (49162) | |
84 | #define LOOPBACK_NEAR_LBN (8) | |
85 | #define LOOPBACK_NEAR_WIDTH (1) | |
86 | ||
8ceee660 BH |
87 | /* Boot status register */ |
88 | #define PCS_BOOT_STATUS_REG (0xd000) | |
89 | #define PCS_BOOT_FATAL_ERR_LBN (0) | |
90 | #define PCS_BOOT_PROGRESS_LBN (1) | |
91 | #define PCS_BOOT_PROGRESS_WIDTH (2) | |
92 | #define PCS_BOOT_COMPLETE_LBN (3) | |
93 | #define PCS_BOOT_MAX_DELAY (100) | |
94 | #define PCS_BOOT_POLL_DELAY (10) | |
95 | ||
96 | /* Time to wait between powering down the LNPGA and turning off the power | |
97 | * rails */ | |
98 | #define LNPGA_PDOWN_WAIT (HZ / 5) | |
99 | ||
100 | static int crc_error_reset_threshold = 100; | |
101 | module_param(crc_error_reset_threshold, int, 0644); | |
102 | MODULE_PARM_DESC(crc_error_reset_threshold, | |
103 | "Max number of CRC errors before XAUI reset"); | |
104 | ||
105 | struct tenxpress_phy_data { | |
3273c2e8 | 106 | enum efx_loopback_mode loopback_mode; |
8ceee660 | 107 | atomic_t bad_crc_count; |
f8b87c17 | 108 | enum efx_phy_mode phy_mode; |
8ceee660 BH |
109 | int bad_lp_tries; |
110 | }; | |
111 | ||
8ceee660 BH |
112 | void tenxpress_crc_err(struct efx_nic *efx) |
113 | { | |
114 | struct tenxpress_phy_data *phy_data = efx->phy_data; | |
115 | if (phy_data != NULL) | |
116 | atomic_inc(&phy_data->bad_crc_count); | |
117 | } | |
118 | ||
119 | /* Check that the C166 has booted successfully */ | |
120 | static int tenxpress_phy_check(struct efx_nic *efx) | |
121 | { | |
122 | int phy_id = efx->mii.phy_id; | |
123 | int count = PCS_BOOT_MAX_DELAY / PCS_BOOT_POLL_DELAY; | |
124 | int boot_stat; | |
125 | ||
126 | /* Wait for the boot to complete (or not) */ | |
127 | while (count) { | |
128 | boot_stat = mdio_clause45_read(efx, phy_id, | |
129 | MDIO_MMD_PCS, | |
130 | PCS_BOOT_STATUS_REG); | |
131 | if (boot_stat & (1 << PCS_BOOT_COMPLETE_LBN)) | |
132 | break; | |
133 | count--; | |
134 | udelay(PCS_BOOT_POLL_DELAY); | |
135 | } | |
136 | ||
137 | if (!count) { | |
138 | EFX_ERR(efx, "%s: PHY boot timed out. Last status " | |
139 | "%x\n", __func__, | |
140 | (boot_stat >> PCS_BOOT_PROGRESS_LBN) & | |
141 | ((1 << PCS_BOOT_PROGRESS_WIDTH) - 1)); | |
142 | return -ETIMEDOUT; | |
143 | } | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
8ceee660 BH |
148 | static int tenxpress_init(struct efx_nic *efx) |
149 | { | |
150 | int rc, reg; | |
151 | ||
152 | /* Turn on the clock */ | |
153 | reg = (1 << CLK312_EN_LBN); | |
154 | mdio_clause45_write(efx, efx->mii.phy_id, | |
155 | MDIO_MMD_PCS, PCS_TEST_SELECT_REG, reg); | |
156 | ||
157 | rc = tenxpress_phy_check(efx); | |
158 | if (rc < 0) | |
159 | return rc; | |
160 | ||
161 | /* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */ | |
162 | reg = mdio_clause45_read(efx, efx->mii.phy_id, | |
163 | MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG); | |
164 | reg |= (1 << PMA_PMA_LED_ACTIVITY_LBN); | |
165 | mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, | |
166 | PMA_PMD_LED_CTRL_REG, reg); | |
167 | ||
168 | reg = PMA_PMD_LED_DEFAULT; | |
169 | mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, | |
170 | PMA_PMD_LED_OVERR_REG, reg); | |
171 | ||
172 | return rc; | |
173 | } | |
174 | ||
175 | static int tenxpress_phy_init(struct efx_nic *efx) | |
176 | { | |
177 | struct tenxpress_phy_data *phy_data; | |
178 | int rc = 0; | |
179 | ||
180 | phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); | |
9b7bfc4c BH |
181 | if (!phy_data) |
182 | return -ENOMEM; | |
8ceee660 | 183 | efx->phy_data = phy_data; |
f8b87c17 | 184 | phy_data->phy_mode = efx->phy_mode; |
8ceee660 | 185 | |
f8b87c17 BH |
186 | rc = mdio_clause45_wait_reset_mmds(efx, |
187 | TENXPRESS_REQUIRED_DEVS); | |
188 | if (rc < 0) | |
189 | goto fail; | |
8ceee660 BH |
190 | |
191 | rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0); | |
192 | if (rc < 0) | |
193 | goto fail; | |
194 | ||
195 | rc = tenxpress_init(efx); | |
196 | if (rc < 0) | |
197 | goto fail; | |
198 | ||
199 | schedule_timeout_uninterruptible(HZ / 5); /* 200ms */ | |
200 | ||
201 | /* Let XGXS and SerDes out of reset and resets 10XPress */ | |
202 | falcon_reset_xaui(efx); | |
203 | ||
204 | return 0; | |
205 | ||
206 | fail: | |
207 | kfree(efx->phy_data); | |
208 | efx->phy_data = NULL; | |
209 | return rc; | |
210 | } | |
211 | ||
3273c2e8 BH |
212 | static int tenxpress_special_reset(struct efx_nic *efx) |
213 | { | |
214 | int rc, reg; | |
215 | ||
c8fcc49c BH |
216 | /* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so |
217 | * a special software reset can glitch the XGMAC sufficiently for stats | |
218 | * requests to fail. Since we don't ofen special_reset, just lock. */ | |
219 | spin_lock(&efx->stats_lock); | |
3273c2e8 BH |
220 | |
221 | /* Initiate reset */ | |
222 | reg = mdio_clause45_read(efx, efx->mii.phy_id, | |
223 | MDIO_MMD_PMAPMD, PMA_PMD_EXT_CTRL_REG); | |
224 | reg |= (1 << PMA_PMD_EXT_SSR_LBN); | |
225 | mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, | |
226 | PMA_PMD_EXT_CTRL_REG, reg); | |
227 | ||
c8fcc49c | 228 | mdelay(200); |
3273c2e8 BH |
229 | |
230 | /* Wait for the blocks to come out of reset */ | |
231 | rc = mdio_clause45_wait_reset_mmds(efx, | |
232 | TENXPRESS_REQUIRED_DEVS); | |
233 | if (rc < 0) | |
c8fcc49c | 234 | goto unlock; |
3273c2e8 BH |
235 | |
236 | /* Try and reconfigure the device */ | |
237 | rc = tenxpress_init(efx); | |
238 | if (rc < 0) | |
c8fcc49c | 239 | goto unlock; |
3273c2e8 | 240 | |
c8fcc49c BH |
241 | unlock: |
242 | spin_unlock(&efx->stats_lock); | |
243 | return rc; | |
3273c2e8 BH |
244 | } |
245 | ||
04cc8cac | 246 | static void tenxpress_check_bad_lp(struct efx_nic *efx, bool link_ok) |
8ceee660 BH |
247 | { |
248 | struct tenxpress_phy_data *pd = efx->phy_data; | |
04cc8cac BH |
249 | int phy_id = efx->mii.phy_id; |
250 | bool bad_lp; | |
8ceee660 BH |
251 | int reg; |
252 | ||
04cc8cac BH |
253 | if (link_ok) { |
254 | bad_lp = false; | |
255 | } else { | |
256 | /* Check that AN has started but not completed. */ | |
257 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, | |
258 | MDIO_AN_STATUS); | |
259 | if (!(reg & (1 << MDIO_AN_STATUS_LP_AN_CAP_LBN))) | |
260 | return; /* LP status is unknown */ | |
261 | bad_lp = !(reg & (1 << MDIO_AN_STATUS_AN_DONE_LBN)); | |
262 | if (bad_lp) | |
263 | pd->bad_lp_tries++; | |
264 | } | |
265 | ||
8ceee660 | 266 | /* Nothing to do if all is well and was previously so. */ |
04cc8cac | 267 | if (!pd->bad_lp_tries) |
8ceee660 BH |
268 | return; |
269 | ||
04cc8cac BH |
270 | /* Use the RX (red) LED as an error indicator once we've seen AN |
271 | * failure several times in a row, and also log a message. */ | |
272 | if (!bad_lp || pd->bad_lp_tries == MAX_BAD_LP_TRIES) { | |
273 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, | |
274 | PMA_PMD_LED_OVERR_REG); | |
275 | reg &= ~(PMA_PMD_LED_MASK << PMA_PMD_LED_RX_LBN); | |
276 | if (!bad_lp) { | |
277 | reg |= PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN; | |
278 | } else { | |
279 | reg |= PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN; | |
280 | EFX_ERR(efx, "appears to be plugged into a port" | |
281 | " that is not 10GBASE-T capable. The PHY" | |
282 | " supports 10GBASE-T ONLY, so no link can" | |
283 | " be established\n"); | |
284 | } | |
285 | mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, | |
286 | PMA_PMD_LED_OVERR_REG, reg); | |
287 | pd->bad_lp_tries = bad_lp; | |
8ceee660 | 288 | } |
8ceee660 BH |
289 | } |
290 | ||
04cc8cac | 291 | static bool tenxpress_link_ok(struct efx_nic *efx) |
8ceee660 | 292 | { |
04cc8cac BH |
293 | if (efx->loopback_mode == LOOPBACK_NONE) |
294 | return mdio_clause45_links_ok(efx, MDIO_MMDREG_DEVS_AN); | |
295 | else | |
296 | return mdio_clause45_links_ok(efx, | |
297 | MDIO_MMDREG_DEVS_PMAPMD | | |
298 | MDIO_MMDREG_DEVS_PCS | | |
299 | MDIO_MMDREG_DEVS_PHYXS); | |
8ceee660 BH |
300 | } |
301 | ||
3273c2e8 BH |
302 | static void tenxpress_phyxs_loopback(struct efx_nic *efx) |
303 | { | |
304 | int phy_id = efx->mii.phy_id; | |
305 | int ctrl1, ctrl2; | |
306 | ||
307 | ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS, | |
308 | PHYXS_TEST1); | |
309 | if (efx->loopback_mode == LOOPBACK_PHYXS) | |
310 | ctrl2 |= (1 << LOOPBACK_NEAR_LBN); | |
311 | else | |
312 | ctrl2 &= ~(1 << LOOPBACK_NEAR_LBN); | |
313 | if (ctrl1 != ctrl2) | |
314 | mdio_clause45_write(efx, phy_id, MDIO_MMD_PHYXS, | |
315 | PHYXS_TEST1, ctrl2); | |
316 | } | |
317 | ||
8ceee660 BH |
318 | static void tenxpress_phy_reconfigure(struct efx_nic *efx) |
319 | { | |
3273c2e8 | 320 | struct tenxpress_phy_data *phy_data = efx->phy_data; |
dc8cfa55 BH |
321 | bool loop_change = LOOPBACK_OUT_OF(phy_data, efx, |
322 | TENXPRESS_LOOPBACKS); | |
3273c2e8 | 323 | |
f8b87c17 BH |
324 | if (efx->phy_mode & PHY_MODE_SPECIAL) { |
325 | phy_data->phy_mode = efx->phy_mode; | |
8ceee660 | 326 | return; |
f8b87c17 | 327 | } |
8ceee660 | 328 | |
3273c2e8 BH |
329 | /* When coming out of transmit disable, coming out of low power |
330 | * mode, or moving out of any PHY internal loopback mode, | |
331 | * perform a special software reset */ | |
f8b87c17 BH |
332 | if ((efx->phy_mode == PHY_MODE_NORMAL && |
333 | phy_data->phy_mode != PHY_MODE_NORMAL) || | |
3273c2e8 | 334 | loop_change) { |
91ad757c | 335 | tenxpress_special_reset(efx); |
3273c2e8 BH |
336 | falcon_reset_xaui(efx); |
337 | } | |
338 | ||
339 | mdio_clause45_transmit_disable(efx); | |
340 | mdio_clause45_phy_reconfigure(efx); | |
341 | tenxpress_phyxs_loopback(efx); | |
342 | ||
3273c2e8 | 343 | phy_data->loopback_mode = efx->loopback_mode; |
f8b87c17 | 344 | phy_data->phy_mode = efx->phy_mode; |
04cc8cac | 345 | efx->link_up = tenxpress_link_ok(efx); |
f31a45d2 BH |
346 | efx->link_speed = 10000; |
347 | efx->link_fd = true; | |
04cc8cac | 348 | efx->link_fc = mdio_clause45_get_pause(efx); |
8ceee660 BH |
349 | } |
350 | ||
8ceee660 | 351 | /* Poll PHY for interrupt */ |
766ca0fa | 352 | static void tenxpress_phy_poll(struct efx_nic *efx) |
8ceee660 BH |
353 | { |
354 | struct tenxpress_phy_data *phy_data = efx->phy_data; | |
766ca0fa BH |
355 | bool change = false, link_ok; |
356 | unsigned link_fc; | |
8ceee660 | 357 | |
04cc8cac | 358 | link_ok = tenxpress_link_ok(efx); |
766ca0fa BH |
359 | if (link_ok != efx->link_up) { |
360 | change = true; | |
361 | } else { | |
362 | link_fc = mdio_clause45_get_pause(efx); | |
363 | if (link_fc != efx->link_fc) | |
364 | change = true; | |
365 | } | |
04cc8cac | 366 | tenxpress_check_bad_lp(efx, link_ok); |
8ceee660 | 367 | |
766ca0fa | 368 | if (change) |
177dfcd8 | 369 | falcon_sim_phy_event(efx); |
8ceee660 | 370 | |
f8b87c17 | 371 | if (phy_data->phy_mode != PHY_MODE_NORMAL) |
766ca0fa | 372 | return; |
8ceee660 BH |
373 | |
374 | if (atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) { | |
375 | EFX_ERR(efx, "Resetting XAUI due to too many CRC errors\n"); | |
376 | falcon_reset_xaui(efx); | |
377 | atomic_set(&phy_data->bad_crc_count, 0); | |
378 | } | |
8ceee660 BH |
379 | } |
380 | ||
381 | static void tenxpress_phy_fini(struct efx_nic *efx) | |
382 | { | |
383 | int reg; | |
384 | ||
385 | /* Power down the LNPGA */ | |
386 | reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN); | |
387 | mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, | |
388 | PMA_PMD_XCONTROL_REG, reg); | |
389 | ||
390 | /* Waiting here ensures that the board fini, which can turn off the | |
391 | * power to the PHY, won't get run until the LNPGA powerdown has been | |
392 | * given long enough to complete. */ | |
393 | schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */ | |
394 | ||
395 | kfree(efx->phy_data); | |
396 | efx->phy_data = NULL; | |
397 | } | |
398 | ||
399 | ||
400 | /* Set the RX and TX LEDs and Link LED flashing. The other LEDs | |
401 | * (which probably aren't wired anyway) are left in AUTO mode */ | |
dc8cfa55 | 402 | void tenxpress_phy_blink(struct efx_nic *efx, bool blink) |
8ceee660 BH |
403 | { |
404 | int reg; | |
405 | ||
406 | if (blink) | |
407 | reg = (PMA_PMD_LED_FLASH << PMA_PMD_LED_TX_LBN) | | |
408 | (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN) | | |
409 | (PMA_PMD_LED_FLASH << PMA_PMD_LED_LINK_LBN); | |
410 | else | |
411 | reg = PMA_PMD_LED_DEFAULT; | |
412 | ||
413 | mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, | |
414 | PMA_PMD_LED_OVERR_REG, reg); | |
415 | } | |
416 | ||
8c8661e4 BH |
417 | static int tenxpress_phy_test(struct efx_nic *efx) |
418 | { | |
419 | /* BIST is automatically run after a special software reset */ | |
420 | return tenxpress_special_reset(efx); | |
421 | } | |
422 | ||
04cc8cac BH |
423 | static u32 tenxpress_get_xnp_lpa(struct efx_nic *efx) |
424 | { | |
425 | int phy = efx->mii.phy_id; | |
426 | u32 lpa = 0; | |
427 | int reg; | |
428 | ||
429 | reg = mdio_clause45_read(efx, phy, MDIO_MMD_AN, MDIO_AN_10GBT_STATUS); | |
430 | if (reg & (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN)) | |
431 | lpa |= ADVERTISED_10000baseT_Full; | |
432 | return lpa; | |
433 | } | |
434 | ||
435 | static void | |
436 | tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) | |
437 | { | |
438 | mdio_clause45_get_settings_ext(efx, ecmd, ADVERTISED_10000baseT_Full, | |
439 | tenxpress_get_xnp_lpa(efx)); | |
440 | ecmd->supported |= SUPPORTED_10000baseT_Full; | |
441 | ecmd->advertising |= ADVERTISED_10000baseT_Full; | |
442 | } | |
443 | ||
8ceee660 | 444 | struct efx_phy_operations falcon_tenxpress_phy_ops = { |
177dfcd8 | 445 | .macs = EFX_XMAC, |
8ceee660 BH |
446 | .init = tenxpress_phy_init, |
447 | .reconfigure = tenxpress_phy_reconfigure, | |
766ca0fa | 448 | .poll = tenxpress_phy_poll, |
8ceee660 | 449 | .fini = tenxpress_phy_fini, |
766ca0fa | 450 | .clear_interrupt = efx_port_dummy_op_void, |
8c8661e4 | 451 | .test = tenxpress_phy_test, |
04cc8cac | 452 | .get_settings = tenxpress_get_settings, |
177dfcd8 | 453 | .set_settings = mdio_clause45_set_settings, |
8ceee660 | 454 | .mmds = TENXPRESS_REQUIRED_DEVS, |
3273c2e8 | 455 | .loopbacks = TENXPRESS_LOOPBACKS, |
8ceee660 | 456 | }; |