1 /***********************license start***************
2 * Copyright (c) 2003-2011 Cavium Inc. (support@cavium.com). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17
18 * * Neither the name of Cavium Inc. nor the names of
19 * its contributors may be used to endorse or promote products
20 * derived from this software without specific prior written
21 * permission.
22
23 * This Software, including technical data, may be subject to U.S. export control
24 * laws, including the U.S. Export Administration Act and its associated
25 * regulations, and may be subject to export or import regulations in other
26 * countries.
27
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39
40
41
42
43
44
45
46 /**
47 * @file
48 *
49 * Helper functions to abstract board specific data about
50 * network ports from the rest of the cvmx-helper files.
51 *
52 * <hr>$Revision: 70030 $<hr>
53 */
54 #ifdef CVMX_BUILD_FOR_LINUX_KERNEL
55 #include <linux/module.h>
56 #include <asm/octeon/cvmx.h>
57 #include <asm/octeon/cvmx-bootinfo.h>
58 #include <asm/octeon/cvmx-smix-defs.h>
59 #include <asm/octeon/cvmx-gmxx-defs.h>
60 #include <asm/octeon/cvmx-asxx-defs.h>
61 #include <asm/octeon/cvmx-mdio.h>
62 #include <asm/octeon/cvmx-helper.h>
63 #include <asm/octeon/cvmx-helper-util.h>
64 #include <asm/octeon/cvmx-helper-board.h>
65 #include <asm/octeon/cvmx-twsi.h>
66 #else
67 #include "cvmx.h"
68 #include "cvmx-app-init.h"
69 #include "cvmx-sysinfo.h"
70 #include "cvmx-twsi.h"
71 #include "cvmx-mdio.h"
72 #include "cvmx-helper.h"
73 #include "cvmx-helper-util.h"
74 #include "cvmx-helper-board.h"
75 #include "cvmx-gpio.h"
76 #if !defined(__FreeBSD__) || !defined(_KERNEL)
77 #ifdef __U_BOOT__
78 # include <libfdt.h>
79 #else
80 # include "libfdt/libfdt.h"
81 #endif
82 #endif
83 #include "cvmx-swap.h"
84 #endif
85
86 /**
87 * cvmx_override_board_link_get(int ipd_port) is a function
88 * pointer. It is meant to allow customization of the process of
89 * talking to a PHY to determine link speed. It is called every
90 * time a PHY must be polled for link status. Users should set
91 * this pointer to a function before calling any cvmx-helper
92 * operations.
93 */
94 CVMX_SHARED cvmx_helper_link_info_t (*cvmx_override_board_link_get)(int ipd_port) = NULL;
95
96 #if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
97
cvmx_retry_i2c_write(int twsi_id,uint8_t dev_addr,uint16_t internal_addr,int num_bytes,int ia_width_bytes,uint64_t data)98 static void cvmx_retry_i2c_write(int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes, uint64_t data)
99 {
100 int tries = 3;
101 int r;
102 do {
103 r = cvmx_twsix_write_ia(twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes, data);
104 } while (tries-- > 0 && r < 0);
105 }
106
__pip_eth_node(const void * fdt_addr,int aliases,int ipd_port)107 static int __pip_eth_node(const void *fdt_addr, int aliases, int ipd_port)
108 {
109 char name_buffer[20];
110 const char*pip_path;
111 int pip, iface, eth;
112 int interface_num = cvmx_helper_get_interface_num(ipd_port);
113 int interface_index = cvmx_helper_get_interface_index_num(ipd_port);
114
115 pip_path = fdt_getprop(fdt_addr, aliases, "pip", NULL);
116 if (!pip_path)
117 {
118 cvmx_dprintf("ERROR: pip path not found in device tree\n");
119 return -1;
120 }
121 pip = fdt_path_offset(fdt_addr, pip_path);
122 if (pip < 0)
123 {
124 cvmx_dprintf("ERROR: pip not found in device tree\n");
125 return -1;
126 }
127 #ifdef __U_BOOT__
128 sprintf(name_buffer, "interface@%d", interface_num);
129 #else
130 snprintf(name_buffer, sizeof(name_buffer), "interface@%d", interface_num);
131 #endif
132 iface = fdt_subnode_offset(fdt_addr, pip, name_buffer);
133 if (iface < 0)
134 {
135 cvmx_dprintf("ERROR : pip intf %d not found in device tree \n",
136 interface_num);
137 return -1;
138 }
139 #ifdef __U_BOOT__
140 sprintf(name_buffer, "ethernet@%x", interface_index);
141 #else
142 snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", interface_index);
143 #endif
144 eth = fdt_subnode_offset(fdt_addr, iface, name_buffer);
145 if (eth < 0)
146 {
147 cvmx_dprintf("ERROR : pip interface@%d ethernet@%d not found in device "
148 "tree\n", interface_num, interface_index);
149 return -1;
150 }
151 return eth;
152 }
153
__mix_eth_node(const void * fdt_addr,int aliases,int interface_index)154 static int __mix_eth_node(const void *fdt_addr, int aliases, int interface_index)
155 {
156 char name_buffer[20];
157 const char*mix_path;
158 int mix;
159
160 #ifdef __U_BOOT__
161 sprintf(name_buffer, "mix%d", interface_index);
162 #else
163 snprintf(name_buffer, sizeof(name_buffer), "mix%d", interface_index);
164 #endif
165 mix_path = fdt_getprop(fdt_addr, aliases, name_buffer, NULL);
166 if (!mix_path)
167 {
168 cvmx_dprintf("ERROR: mix%d path not found in device tree\n",interface_index);
169 }
170 mix = fdt_path_offset(fdt_addr, mix_path);
171 if (mix < 0)
172 {
173 cvmx_dprintf("ERROR: %s not found in device tree\n", mix_path);
174 return -1;
175 }
176 return mix;
177 }
178
179 typedef struct cvmx_phy_info
180 {
181 int phy_addr;
182 int direct_connect;
183 cvmx_phy_type_t phy_type;
184 }cvmx_phy_info_t;
185
186
__mdiobus_addr_to_unit(uint32_t addr)187 static int __mdiobus_addr_to_unit(uint32_t addr)
188 {
189 int unit = (addr >> 7) & 3;
190 if (!OCTEON_IS_MODEL(OCTEON_CN68XX))
191 unit >>= 1;
192 return unit;
193 }
194 /**
195 * Return the MII PHY address associated with the given IPD
196 * port. The phy address is obtained from the device tree.
197 *
198 * @param ipd_port Octeon IPD port to get the MII address for.
199 *
200 * @return MII PHY address and bus number or -1.
201 */
202
__get_phy_info_from_dt(int ipd_port)203 static cvmx_phy_info_t __get_phy_info_from_dt(int ipd_port)
204 {
205 const void *fdt_addr = CASTPTR(const void *, cvmx_sysinfo_get()->fdt_addr);
206 uint32_t *phy_handle;
207 int aliases, eth, phy, phy_parent, phandle, ret;
208 cvmx_phy_info_t phy_info;
209 int mdio_unit=-1;
210 const char *phy_comaptible_str;
211 uint32_t *phy_addr_ptr;
212
213 phy_info.phy_addr = -1;
214 phy_info.direct_connect = -1;
215 phy_info.phy_type = (cvmx_phy_type_t) -1;
216
217 if (!fdt_addr)
218 {
219 cvmx_dprintf("No device tree found.\n");
220 return phy_info;
221 }
222 aliases = fdt_path_offset(fdt_addr, "/aliases");
223 if (aliases < 0) {
224 cvmx_dprintf("Error: No /aliases node in device tree.\n");
225 return phy_info;
226 }
227 if (ipd_port < 0)
228 {
229 int interface_index = ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT;
230 eth = __mix_eth_node(fdt_addr, aliases, interface_index) ;
231 }
232 else
233 {
234 eth = __pip_eth_node(fdt_addr, aliases, ipd_port);
235 }
236 if (eth < 0 )
237 {
238 cvmx_dprintf("ERROR : cannot find interface for ipd_port=%d\n", ipd_port);
239 return phy_info;
240 }
241 /* Get handle to phy */
242 phy_handle = (uint32_t *) fdt_getprop(fdt_addr, eth, "phy-handle", NULL);
243 if (!phy_handle)
244 {
245 cvmx_dprintf("ERROR : phy handle not found in device tree ipd_port=%d"
246 "\n", ipd_port);
247 return phy_info;
248 }
249 phandle = cvmx_be32_to_cpu(*phy_handle);
250 phy = fdt_node_offset_by_phandle(fdt_addr, phandle);
251 if (phy < 0)
252 {
253 cvmx_dprintf("ERROR : cannot find phy for ipd_port=%d ret=%d\n",
254 ipd_port, phy);
255 return phy_info;
256 }
257 phy_comaptible_str = (const char *) fdt_getprop(fdt_addr, phy,
258 "compatible", NULL);
259 if (!phy_comaptible_str)
260 {
261 cvmx_dprintf("ERROR : no compatible prop in phy\n");
262 return phy_info;
263 }
264 if (memcmp("marvell", phy_comaptible_str, strlen("marvell")) == 0)
265 {
266 phy_info.phy_type = MARVELL_GENERIC_PHY;
267 }
268 else if (memcmp("broadcom", phy_comaptible_str, strlen("broadcom")) == 0)
269 {
270 phy_info.phy_type = BROADCOM_GENERIC_PHY;
271 }
272 else
273 {
274 phy_info.phy_type = -1;
275 }
276
277 /* Check if PHY parent is the octeon MDIO bus. Some boards are connected
278 though a MUX and for them direct_connect_to_phy will be 0 */
279 phy_parent = fdt_parent_offset(fdt_addr, phy);
280 if (phy_parent < 0)
281 {
282 cvmx_dprintf("ERROR : cannot find phy parent for ipd_port=%d ret=%d\n",
283 ipd_port, phy_parent);
284 return phy_info;
285 }
286 ret = fdt_node_check_compatible(fdt_addr, phy_parent,
287 "cavium,octeon-3860-mdio");
288 if (ret == 0)
289 {
290 phy_info.direct_connect = 1 ;
291 uint32_t *mdio_reg_base = (uint32_t *) fdt_getprop(fdt_addr, phy_parent,"reg",0);
292 if (mdio_reg_base == 0)
293 {
294 cvmx_dprintf("ERROR : unable to get reg property in phy mdio\n");
295 return phy_info;
296 }
297 mdio_unit = __mdiobus_addr_to_unit(mdio_reg_base[1]);
298 //cvmx_dprintf("phy parent=%s reg_base=%08x unit=%d \n",
299 // fdt_get_name(fdt_addr,phy_parent, NULL), mdio_reg_base[1], mdio_unit);
300 }
301 else
302 {
303 phy_info.direct_connect = 0;
304 /* The PHY is not directly connected to the Octeon MDIO bus.
305 SE doesn't have abstractions for MDIO MUX or MDIO MUX drivers and
306 hence for the non direct cases code will be needed which is
307 board specific.
308 For now the the MDIO Unit is defaulted to 1.
309 */
310 mdio_unit = 1;
311 }
312
313 phy_addr_ptr = (uint32_t *) fdt_getprop(fdt_addr, phy, "reg", NULL);
314 phy_info.phy_addr = cvmx_be32_to_cpu(*phy_addr_ptr) | mdio_unit << 8;
315 return phy_info;
316
317 }
318
319 /**
320 * Return the MII PHY address associated with the given IPD
321 * port. The phy address is obtained from the device tree.
322 *
323 * @param ipd_port Octeon IPD port to get the MII address for.
324 *
325 * @return MII PHY address and bus number or -1.
326 */
327
cvmx_helper_board_get_mii_address_from_dt(int ipd_port)328 int cvmx_helper_board_get_mii_address_from_dt(int ipd_port)
329 {
330 cvmx_phy_info_t phy_info = __get_phy_info_from_dt(ipd_port);
331 return phy_info.phy_addr;
332 }
333 #endif
334
335 /**
336 * Return the MII PHY address associated with the given IPD
337 * port. A result of -1 means there isn't a MII capable PHY
338 * connected to this port. On chips supporting multiple MII
339 * busses the bus number is encoded in bits <15:8>.
340 *
341 * This function must be modified for every new Octeon board.
342 * Internally it uses switch statements based on the cvmx_sysinfo
343 * data to determine board types and revisions. It replies on the
344 * fact that every Octeon board receives a unique board type
345 * enumeration from the bootloader.
346 *
347 * @param ipd_port Octeon IPD port to get the MII address for.
348 *
349 * @return MII PHY address and bus number or -1.
350 */
cvmx_helper_board_get_mii_address(int ipd_port)351 int cvmx_helper_board_get_mii_address(int ipd_port)
352 {
353 /*
354 * Board types we have to know at compile-time.
355 */
356 #ifdef OCTEON_BOARD_CAPK_0100ND
357 switch (ipd_port) {
358 case 0:
359 return 2;
360 case 1:
361 return 3;
362 case 2:
363 /* XXX Switch PHY? */
364 return -1;
365 default:
366 return -1;
367 }
368 #endif
369
370 /*
371 * For board types we can determine at runtime.
372 */
373 if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
374 return -1;
375 #if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
376 if (cvmx_sysinfo_get()->fdt_addr)
377 {
378 cvmx_phy_info_t phy_info = __get_phy_info_from_dt(ipd_port);
379 //cvmx_dprintf("ipd_port=%d phy_addr=%d\n", ipd_port, phy_info.phy_addr);
380 if (phy_info.phy_addr >= 0) return phy_info.phy_addr;
381 }
382 #endif
383 switch (cvmx_sysinfo_get()->board_type)
384 {
385 case CVMX_BOARD_TYPE_SIM:
386 /* Simulator doesn't have MII */
387 return -1;
388 #if !defined(OCTEON_VENDOR_GEFES)
389 case CVMX_BOARD_TYPE_EBT5800:
390 case CVMX_BOARD_TYPE_NICPRO2:
391 #endif
392 case CVMX_BOARD_TYPE_EBT3000:
393 case CVMX_BOARD_TYPE_THUNDER:
394 /* Interface 0 is SPI4, interface 1 is RGMII */
395 if ((ipd_port >= 16) && (ipd_port < 20))
396 return ipd_port - 16;
397 else
398 return -1;
399 case CVMX_BOARD_TYPE_LANAI2_A:
400 if (ipd_port == 0)
401 return 0;
402 else
403 return -1;
404 case CVMX_BOARD_TYPE_LANAI2_U:
405 case CVMX_BOARD_TYPE_LANAI2_G:
406 if (ipd_port == 0)
407 return 0x1c;
408 else
409 return -1;
410 case CVMX_BOARD_TYPE_KODAMA:
411 case CVMX_BOARD_TYPE_EBH3100:
412 case CVMX_BOARD_TYPE_HIKARI:
413 case CVMX_BOARD_TYPE_CN3010_EVB_HS5:
414 case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
415 #if !defined(OCTEON_VENDOR_GEFES)
416 case CVMX_BOARD_TYPE_CN3020_EVB_HS5:
417 #endif
418 /* Port 0 is WAN connected to a PHY, Port 1 is GMII connected to a
419 switch */
420 if (ipd_port == 0)
421 return 4;
422 else if (ipd_port == 1)
423 return 9;
424 else
425 return -1;
426 case CVMX_BOARD_TYPE_EBH3000:
427 /* Board has dual SPI4 and no PHYs */
428 return -1;
429 case CVMX_BOARD_TYPE_EBT5810:
430 /* Board has 10g PHYs hooked up to the MII controller on the
431 ** IXF18201 MAC. The 10G PHYS use clause 45 MDIO which the CN58XX
432 ** does not support. All MII accesses go through the IXF part. */
433 return -1;
434 case CVMX_BOARD_TYPE_EBH5200:
435 case CVMX_BOARD_TYPE_EBH5201:
436 case CVMX_BOARD_TYPE_EBT5200:
437 /* Board has 2 management ports */
438 if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
439 return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT;
440 /* Board has 4 SGMII ports. The PHYs start right after the MII
441 ports MII0 = 0, MII1 = 1, SGMII = 2-5 */
442 if ((ipd_port >= 0) && (ipd_port < 4))
443 return ipd_port+2;
444 else
445 return -1;
446 case CVMX_BOARD_TYPE_EBH5600:
447 case CVMX_BOARD_TYPE_EBH5601:
448 case CVMX_BOARD_TYPE_EBH5610:
449 /* Board has 1 management port */
450 if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
451 return 0;
452 /* Board has 8 SGMII ports. 4 connect out, two connect to a switch,
453 and 2 loop to each other */
454 if ((ipd_port >= 0) && (ipd_port < 4))
455 return ipd_port+1;
456 else
457 return -1;
458 case CVMX_BOARD_TYPE_EBT5600:
459 /* Board has 1 management port */
460 if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
461 return 0;
462 /* Board has 1 XAUI port connected to a switch. */
463 return -1;
464 case CVMX_BOARD_TYPE_EBB5600:
465 {
466 static unsigned char qlm_switch_addr = 0;
467
468 /* Board has 1 management port */
469 if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
470 return 0;
471
472 /* Board has 8 SGMII ports. 4 connected QLM1, 4 connected QLM3 */
473 if ((ipd_port >= 0) && (ipd_port < 4))
474 {
475 if (qlm_switch_addr != 0x3)
476 {
477 qlm_switch_addr = 0x3; /* QLM1 */
478 cvmx_twsix_write_ia(0, 0x71, 0, 1, 1, qlm_switch_addr);
479 cvmx_wait_usec(11000); /* Let the write complete */
480 }
481 return ipd_port+1 + (1<<8);
482 }
483 else if ((ipd_port >= 16) && (ipd_port < 20))
484 {
485 if (qlm_switch_addr != 0xC)
486 {
487 qlm_switch_addr = 0xC; /* QLM3 */
488 cvmx_twsix_write_ia(0, 0x71, 0, 1, 1, qlm_switch_addr);
489 cvmx_wait_usec(11000); /* Let the write complete */
490 }
491 return ipd_port-16+1 + (1<<8);
492 }
493 else
494 return -1;
495 }
496 case CVMX_BOARD_TYPE_EBB6300:
497 /* Board has 2 management ports */
498 if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
499 return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT + 4;
500 if ((ipd_port >= 0) && (ipd_port < 4))
501 return ipd_port + 1 + (1<<8);
502 else
503 return -1;
504 case CVMX_BOARD_TYPE_EBB6800:
505 /* Board has 1 management ports */
506 if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
507 return 6;
508 if (ipd_port >= 0x800 && ipd_port < 0x900) /* QLM 0*/
509 return 0x101 + ((ipd_port >> 4) & 3); /* SMI 1*/
510 if (ipd_port >= 0xa00 && ipd_port < 0xb00) /* QLM 2*/
511 return 0x201 + ((ipd_port >> 4) & 3); /* SMI 2*/
512 if (ipd_port >= 0xb00 && ipd_port < 0xc00) /* QLM 3*/
513 return 0x301 + ((ipd_port >> 4) & 3); /* SMI 3*/
514 if (ipd_port >= 0xc00 && ipd_port < 0xd00) /* QLM 4*/
515 return 0x001 + ((ipd_port >> 4) & 3); /* SMI 0*/
516 return -1;
517 case CVMX_BOARD_TYPE_EP6300C:
518 if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
519 return 0x01;
520 if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT+1)
521 return 0x02;
522 #ifdef CVMX_ENABLE_PKO_FUNCTIONS
523 {
524 int interface = cvmx_helper_get_interface_num(ipd_port);
525 int mode = cvmx_helper_interface_get_mode(interface);
526 if (mode == CVMX_HELPER_INTERFACE_MODE_XAUI)
527 return ipd_port;
528 else if ((ipd_port >= 0) && (ipd_port < 4))
529 return ipd_port + 3;
530 else
531 return -1;
532 }
533 #endif
534 break;
535 case CVMX_BOARD_TYPE_CUST_NB5:
536 if (ipd_port == 2)
537 return 4;
538 else
539 return -1;
540 case CVMX_BOARD_TYPE_NIC_XLE_4G:
541 /* Board has 4 SGMII ports. connected QLM3(interface 1) */
542 if ((ipd_port >= 16) && (ipd_port < 20))
543 return ipd_port - 16 + 1;
544 else
545 return -1;
546 case CVMX_BOARD_TYPE_NIC_XLE_10G:
547 case CVMX_BOARD_TYPE_NIC10E:
548 return -1; /* We don't use clause 45 MDIO for anything */
549 case CVMX_BOARD_TYPE_NIC4E:
550 if (ipd_port >= 0 && ipd_port <= 3)
551 return (ipd_port + 0x1f) & 0x1f;
552 else
553 return -1;
554 case CVMX_BOARD_TYPE_NIC2E:
555 if (ipd_port >= 0 && ipd_port <= 1)
556 return (ipd_port + 1);
557 else
558 return -1;
559 case CVMX_BOARD_TYPE_REDWING:
560 return -1; /* No PHYs connected to Octeon */
561 case CVMX_BOARD_TYPE_BBGW_REF:
562 return -1; /* No PHYs are connected to Octeon, everything is through switch */
563 case CVMX_BOARD_TYPE_CUST_WSX16:
564 if (ipd_port >= 0 && ipd_port <= 3)
565 return ipd_port;
566 else if (ipd_port >= 16 && ipd_port <= 19)
567 return ipd_port - 16 + 4;
568 else
569 return -1;
570
571 /* Private vendor-defined boards. */
572 #if defined(OCTEON_VENDOR_LANNER)
573 case CVMX_BOARD_TYPE_CUST_LANNER_MR955:
574 /* Interface 1 is 12 BCM5482S PHYs. */
575 if ((ipd_port >= 16) && (ipd_port < 28))
576 return ipd_port - 16;
577 return -1;
578 case CVMX_BOARD_TYPE_CUST_LANNER_MR730:
579 if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
580 return (ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT) + 0x81;
581 if ((ipd_port >= 0) && (ipd_port < 4))
582 return ipd_port;
583 return -1;
584 case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
585 case CVMX_BOARD_TYPE_CUST_LANNER_MR321X:
586 /* Port 0 is a Marvell 88E6161 switch, ports 1 and 2 are Marvell
587 88E1111 interfaces. */
588 switch (ipd_port) {
589 case 0:
590 return 16;
591 case 1:
592 return 1;
593 case 2:
594 return 2;
595 default:
596 return -1;
597 }
598 #endif
599 #if defined(OCTEON_VENDOR_UBIQUITI)
600 case CVMX_BOARD_TYPE_CUST_UBIQUITI_E100:
601 if (ipd_port > 2)
602 return -1;
603 return (7 - ipd_port);
604 #endif
605 #if defined(OCTEON_VENDOR_RADISYS)
606 case CVMX_BOARD_TYPE_CUST_RADISYS_RSYS4GBE:
607 /* No MII. */
608 return -1;
609 #endif
610 #if defined(OCTEON_VENDOR_GEFES)
611 case CVMX_BOARD_TYPE_AT5810:
612 return -1;
613 case CVMX_BOARD_TYPE_TNPA3804:
614 case CVMX_BOARD_TYPE_CUST_TNPA5804:
615 case CVMX_BOARD_TYPE_CUST_W5800:
616 case CVMX_BOARD_TYPE_WNPA3850:
617 case CVMX_BOARD_TYPE_W3860:
618 return -1;// RGMII boards should use inbad status
619 case CVMX_BOARD_TYPE_CUST_W5651X:
620 case CVMX_BOARD_TYPE_CUST_W5650:
621 case CVMX_BOARD_TYPE_CUST_TNPA56X4:
622 case CVMX_BOARD_TYPE_CUST_TNPA5651X:
623 case CVMX_BOARD_TYPE_CUST_W63XX:
624 return -1; /* No PHYs are connected to Octeon, PHYs inside of SFPs which is accessed over TWSI */
625 case CVMX_BOARD_TYPE_CUST_W5434:
626 /* Board has 4 SGMII ports. 4 connect out
627 * must return the MII address of the PHY connected to each IPD port
628 */
629 if ((ipd_port >= 16) && (ipd_port < 20))
630 return ipd_port - 16 + 0x40;
631 else
632 return -1;
633 #endif
634 }
635
636 /* Some unknown board. Somebody forgot to update this function... */
637 cvmx_dprintf("%s: Unknown board type %d\n",
638 __FUNCTION__, cvmx_sysinfo_get()->board_type);
639 return -1;
640 }
641 #ifdef CVMX_BUILD_FOR_LINUX_KERNEL
642 EXPORT_SYMBOL(cvmx_helper_board_get_mii_address);
643 #endif
644
645 /**
646 * @INTERNAL
647 * Get link state of marvell PHY
648 */
__get_marvell_phy_link_state(int phy_addr)649 static cvmx_helper_link_info_t __get_marvell_phy_link_state(int phy_addr)
650 {
651 cvmx_helper_link_info_t result;
652 int phy_status;
653
654 result.u64 = 0;
655 /*All the speed information can be read from register 17 in one go.*/
656 phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17);
657
658 /* If the resolve bit 11 isn't set, see if autoneg is turned off
659 (bit 12, reg 0). The resolve bit doesn't get set properly when
660 autoneg is off, so force it */
661 if ((phy_status & (1<<11)) == 0)
662 {
663 int auto_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0);
664 if ((auto_status & (1<<12)) == 0)
665 phy_status |= 1<<11;
666 }
667
668 /* Only return a link if the PHY has finished auto negotiation
669 and set the resolved bit (bit 11) */
670 if (phy_status & (1<<11))
671 {
672 result.s.link_up = 1;
673 result.s.full_duplex = ((phy_status>>13)&1);
674 switch ((phy_status>>14)&3)
675 {
676 case 0: /* 10 Mbps */
677 result.s.speed = 10;
678 break;
679 case 1: /* 100 Mbps */
680 result.s.speed = 100;
681 break;
682 case 2: /* 1 Gbps */
683 result.s.speed = 1000;
684 break;
685 case 3: /* Illegal */
686 result.u64 = 0;
687 break;
688 }
689 }
690 return result;
691 }
692
693 /**
694 * @INTERNAL
695 * Get link state of broadcom PHY
696 */
__get_broadcom_phy_link_state(int phy_addr)697 static cvmx_helper_link_info_t __get_broadcom_phy_link_state(int phy_addr)
698 {
699 cvmx_helper_link_info_t result;
700 int phy_status;
701
702 result.u64 = 0;
703 /* Below we are going to read SMI/MDIO register 0x19 which works
704 on Broadcom parts */
705 phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x19);
706 switch ((phy_status>>8) & 0x7)
707 {
708 case 0:
709 result.u64 = 0;
710 break;
711 case 1:
712 result.s.link_up = 1;
713 result.s.full_duplex = 0;
714 result.s.speed = 10;
715 break;
716 case 2:
717 result.s.link_up = 1;
718 result.s.full_duplex = 1;
719 result.s.speed = 10;
720 break;
721 case 3:
722 result.s.link_up = 1;
723 result.s.full_duplex = 0;
724 result.s.speed = 100;
725 break;
726 case 4:
727 result.s.link_up = 1;
728 result.s.full_duplex = 1;
729 result.s.speed = 100;
730 break;
731 case 5:
732 result.s.link_up = 1;
733 result.s.full_duplex = 1;
734 result.s.speed = 100;
735 break;
736 case 6:
737 result.s.link_up = 1;
738 result.s.full_duplex = 0;
739 result.s.speed = 1000;
740 break;
741 case 7:
742 result.s.link_up = 1;
743 result.s.full_duplex = 1;
744 result.s.speed = 1000;
745 break;
746 }
747 return result;
748 }
749
750
751 /**
752 * @INTERNAL
753 * Get link state using inband status
754 */
__get_inband_link_state(int ipd_port)755 static cvmx_helper_link_info_t __get_inband_link_state(int ipd_port)
756 {
757 cvmx_helper_link_info_t result;
758 cvmx_gmxx_rxx_rx_inbnd_t inband_status;
759 int interface = cvmx_helper_get_interface_num(ipd_port);
760 int index = cvmx_helper_get_interface_index_num(ipd_port);
761
762 result.u64 = 0;
763 inband_status.u64 = cvmx_read_csr(CVMX_GMXX_RXX_RX_INBND(index, interface));
764 result.s.link_up = inband_status.s.status;
765 result.s.full_duplex = inband_status.s.duplex;
766 switch (inband_status.s.speed)
767 {
768 case 0: /* 10 Mbps */
769 result.s.speed = 10;
770 break;
771 case 1: /* 100 Mbps */
772 result.s.speed = 100;
773 break;
774 case 2: /* 1 Gbps */
775 result.s.speed = 1000;
776 break;
777 case 3: /* Illegal */
778 result.u64 = 0;
779 break;
780 }
781 return result;
782 }
783
784 #if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
785 /**
786 * @INTERNAL
787 * Switch MDIO mux to the specified port.
788 */
__switch_mdio_mux(int ipd_port)789 static int __switch_mdio_mux(int ipd_port)
790 {
791 /* This method is board specific and doesn't use the device tree
792 information as SE doesn't implement MDIO MUX abstration */
793 switch (cvmx_sysinfo_get()->board_type)
794 {
795 case CVMX_BOARD_TYPE_EBB5600:
796 {
797 static unsigned char qlm_switch_addr = 0;
798 /* Board has 1 management port */
799 if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
800 return 0;
801 /* Board has 8 SGMII ports. 4 connected QLM1, 4 connected QLM3 */
802 if ((ipd_port >= 0) && (ipd_port < 4))
803 {
804 if (qlm_switch_addr != 0x3)
805 {
806 qlm_switch_addr = 0x3; /* QLM1 */
807 cvmx_twsix_write_ia(0, 0x71, 0, 1, 1, qlm_switch_addr);
808 cvmx_wait_usec(11000); /* Let the write complete */
809 }
810 return ipd_port+1 + (1<<8);
811 }
812 else if ((ipd_port >= 16) && (ipd_port < 20))
813 {
814 if (qlm_switch_addr != 0xC)
815 {
816 qlm_switch_addr = 0xC; /* QLM3 */
817 cvmx_twsix_write_ia(0, 0x71, 0, 1, 1, qlm_switch_addr);
818 cvmx_wait_usec(11000); /* Let the write complete */
819 }
820 return ipd_port-16+1 + (1<<8);
821 }
822 else
823 return -1;
824 }
825 case CVMX_BOARD_TYPE_EBB6600:
826 {
827 static unsigned char qlm_switch_addr = 0;
828 int old_twsi_switch_reg;
829 /* Board has 2 management ports */
830 if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) &&
831 (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
832 return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT + 4;
833 if ((ipd_port >= 0) && (ipd_port < 4)) /* QLM 2 */
834 {
835 if (qlm_switch_addr != 2)
836 {
837 int tries;
838 qlm_switch_addr = 2;
839 tries = 3;
840 do {
841 old_twsi_switch_reg = cvmx_twsix_read8(0, 0x70, 0);
842 } while (tries-- > 0 && old_twsi_switch_reg < 0);
843 /* Set I2C MUX to enable port expander */
844 cvmx_retry_i2c_write(0, 0x70, 0, 1, 0, 8);
845 /* Set selecter to QLM 1 */
846 cvmx_retry_i2c_write(0, 0x38, 0, 1, 0, 0xff);
847 /* disable port expander */
848 cvmx_retry_i2c_write(0, 0x70, 0, 1, 0, old_twsi_switch_reg);
849 }
850 return 0x101 + ipd_port;
851 }
852 else if ((ipd_port >= 16) && (ipd_port < 20)) /* QLM 1 */
853 {
854 if (qlm_switch_addr != 1)
855 {
856 int tries;
857 qlm_switch_addr = 1;
858 tries = 3;
859 do {
860 old_twsi_switch_reg = cvmx_twsix_read8(0, 0x70, 0);
861 } while (tries-- > 0 && old_twsi_switch_reg < 0);
862 /* Set I2C MUX to enable port expander */
863 cvmx_retry_i2c_write(0, 0x70, 0, 1, 0, 8);
864 /* Set selecter to QLM 2 */
865 cvmx_retry_i2c_write(0, 0x38, 0, 1, 0, 0xf7);
866 /* disable port expander */
867 cvmx_retry_i2c_write(0, 0x70, 0, 1, 0, old_twsi_switch_reg);
868 }
869 return 0x101 + (ipd_port - 16);
870 } else
871 return -1;
872 }
873 case CVMX_BOARD_TYPE_EBB6100:
874 {
875 static char gpio_configured = 0;
876
877 if (!gpio_configured)
878 {
879 cvmx_gpio_cfg(3, 1);
880 gpio_configured = 1;
881 }
882 /* Board has 2 management ports */
883 if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) &&
884 (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
885 return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT + 4;
886 if ((ipd_port >= 0) && (ipd_port < 4)) /* QLM 2 */
887 {
888 cvmx_gpio_set(1ull << 3);
889 return 0x101 + ipd_port;
890 }
891 else if ((ipd_port >= 16) && (ipd_port < 20)) /* QLM 0 */
892 {
893 cvmx_gpio_clear(1ull << 3);
894 return 0x101 + (ipd_port - 16);
895 }
896 else
897 {
898 printf("%s: Unknown ipd port 0x%x\n", __func__, ipd_port);
899 return -1;
900 }
901 }
902 default:
903 {
904 cvmx_dprintf("ERROR : unexpected mdio switch for board=%08x\n",
905 cvmx_sysinfo_get()->board_type);
906 return -1;
907 }
908 }
909 /* should never get here */
910 return -1;
911 }
912
913 /**
914 * @INTERNAL
915 * This function is used ethernet ports link speed. This functions uses the
916 * device tree information to determine the phy address and type of PHY.
917 * The only supproted PHYs are Marvell and Broadcom.
918 *
919 * @param ipd_port IPD input port associated with the port we want to get link
920 * status for.
921 *
922 * @return The ports link status. If the link isn't fully resolved, this must
923 * return zero.
924 */
925
__cvmx_helper_board_link_get_from_dt(int ipd_port)926 cvmx_helper_link_info_t __cvmx_helper_board_link_get_from_dt(int ipd_port)
927 {
928 cvmx_helper_link_info_t result;
929 cvmx_phy_info_t phy_info;
930
931 result.u64 = 0;
932 if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
933 {
934 /* The simulator gives you a simulated 1Gbps full duplex link */
935 result.s.link_up = 1;
936 result.s.full_duplex = 1;
937 result.s.speed = 1000;
938 return result;
939 }
940 phy_info = __get_phy_info_from_dt(ipd_port);
941 //cvmx_dprintf("ipd_port=%d phy_addr=%d dc=%d type=%d \n", ipd_port,
942 // phy_info.phy_addr, phy_info.direct_connect, phy_info.phy_type);
943 if (phy_info.phy_addr < 0) return result;
944
945 if (phy_info.direct_connect == 0)
946 __switch_mdio_mux(ipd_port);
947 switch(phy_info.phy_type)
948 {
949 case BROADCOM_GENERIC_PHY:
950 result = __get_broadcom_phy_link_state(phy_info.phy_addr);
951 break;
952 case MARVELL_GENERIC_PHY:
953 result = __get_marvell_phy_link_state(phy_info.phy_addr);
954 break;
955 default:
956 result = __get_inband_link_state(ipd_port);
957 }
958 return result;
959
960 }
961 #endif
962
963 /**
964 * @INTERNAL
965 * This function invokes __cvmx_helper_board_link_get_from_dt when device tree
966 * info is available. When the device tree information is not available then
967 * this function is the board specific method of determining an
968 * ethernet ports link speed. Most Octeon boards have Marvell PHYs
969 * and are handled by the fall through case. This function must be
970 * updated for boards that don't have the normal Marvell PHYs.
971 *
972 * This function must be modified for every new Octeon board.
973 * Internally it uses switch statements based on the cvmx_sysinfo
974 * data to determine board types and revisions. It relies on the
975 * fact that every Octeon board receives a unique board type
976 * enumeration from the bootloader.
977 *
978 * @param ipd_port IPD input port associated with the port we want to get link
979 * status for.
980 *
981 * @return The ports link status. If the link isn't fully resolved, this must
982 * return zero.
983 */
__cvmx_helper_board_link_get(int ipd_port)984 cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port)
985 {
986 cvmx_helper_link_info_t result;
987 int phy_addr;
988 int is_broadcom_phy = 0;
989
990 #if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
991 if (cvmx_sysinfo_get()->fdt_addr)
992 {
993 return __cvmx_helper_board_link_get_from_dt(ipd_port);
994 }
995 #endif
996
997 /* Give the user a chance to override the processing of this function */
998 if (cvmx_override_board_link_get)
999 return cvmx_override_board_link_get(ipd_port);
1000
1001 /* Unless we fix it later, all links are defaulted to down */
1002 result.u64 = 0;
1003
1004 #if !defined(OCTEON_BOARD_CAPK_0100ND)
1005 /* This switch statement should handle all ports that either don't use
1006 Marvell PHYS, or don't support in-band status */
1007 switch (cvmx_sysinfo_get()->board_type)
1008 {
1009 case CVMX_BOARD_TYPE_SIM:
1010 /* The simulator gives you a simulated 1Gbps full duplex link */
1011 result.s.link_up = 1;
1012 result.s.full_duplex = 1;
1013 result.s.speed = 1000;
1014 return result;
1015 case CVMX_BOARD_TYPE_LANAI2_A:
1016 case CVMX_BOARD_TYPE_LANAI2_U:
1017 case CVMX_BOARD_TYPE_LANAI2_G:
1018 break;
1019 case CVMX_BOARD_TYPE_EBH3100:
1020 case CVMX_BOARD_TYPE_CN3010_EVB_HS5:
1021 case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
1022 #if !defined(OCTEON_VENDOR_GEFES)
1023 case CVMX_BOARD_TYPE_CN3020_EVB_HS5:
1024 #endif
1025 /* Port 1 on these boards is always Gigabit */
1026 if (ipd_port == 1)
1027 {
1028 result.s.link_up = 1;
1029 result.s.full_duplex = 1;
1030 result.s.speed = 1000;
1031 return result;
1032 }
1033 /* Fall through to the generic code below */
1034 break;
1035 case CVMX_BOARD_TYPE_EBT5600:
1036 case CVMX_BOARD_TYPE_EBH5600:
1037 case CVMX_BOARD_TYPE_EBH5601:
1038 case CVMX_BOARD_TYPE_EBH5610:
1039 /* Board has 1 management ports */
1040 if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
1041 is_broadcom_phy = 1;
1042 break;
1043 case CVMX_BOARD_TYPE_EBH5200:
1044 case CVMX_BOARD_TYPE_EBH5201:
1045 case CVMX_BOARD_TYPE_EBT5200:
1046 /* Board has 2 management ports */
1047 if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
1048 is_broadcom_phy = 1;
1049 break;
1050 case CVMX_BOARD_TYPE_EBB6100:
1051 case CVMX_BOARD_TYPE_EBB6300: /* Only for MII mode, with PHY addresses 0/1. Default is RGMII*/
1052 case CVMX_BOARD_TYPE_EBB6600: /* Only for MII mode, with PHY addresses 0/1. Default is RGMII*/
1053 if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2))
1054 && cvmx_helper_board_get_mii_address(ipd_port) >= 0 && cvmx_helper_board_get_mii_address(ipd_port) <= 1)
1055 is_broadcom_phy = 1;
1056 break;
1057 case CVMX_BOARD_TYPE_EP6300C:
1058 is_broadcom_phy = 1;
1059 break;
1060 case CVMX_BOARD_TYPE_CUST_NB5:
1061 /* Port 1 on these boards is always Gigabit */
1062 if (ipd_port == 1)
1063 {
1064 result.s.link_up = 1;
1065 result.s.full_duplex = 1;
1066 result.s.speed = 1000;
1067 return result;
1068 }
1069 else /* The other port uses a broadcom PHY */
1070 is_broadcom_phy = 1;
1071 break;
1072 case CVMX_BOARD_TYPE_BBGW_REF:
1073 /* Port 1 on these boards is always Gigabit */
1074 if (ipd_port == 2)
1075 {
1076 /* Port 2 is not hooked up */
1077 result.u64 = 0;
1078 return result;
1079 }
1080 else
1081 {
1082 /* Ports 0 and 1 connect to the switch */
1083 result.s.link_up = 1;
1084 result.s.full_duplex = 1;
1085 result.s.speed = 1000;
1086 return result;
1087 }
1088 case CVMX_BOARD_TYPE_NIC4E:
1089 case CVMX_BOARD_TYPE_NIC2E:
1090 is_broadcom_phy = 1;
1091 break;
1092 /* Private vendor-defined boards. */
1093 #if defined(OCTEON_VENDOR_LANNER)
1094 case CVMX_BOARD_TYPE_CUST_LANNER_MR730:
1095 /* Ports are BCM5482S */
1096 is_broadcom_phy = 1;
1097 break;
1098 case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
1099 case CVMX_BOARD_TYPE_CUST_LANNER_MR321X:
1100 /* Port 0 connects to the switch */
1101 if (ipd_port == 0)
1102 {
1103 result.s.link_up = 1;
1104 result.s.full_duplex = 1;
1105 result.s.speed = 1000;
1106 return result;
1107 }
1108 break;
1109 #endif
1110 #if defined(OCTEON_VENDOR_GEFES)
1111 case CVMX_BOARD_TYPE_CUST_TNPA5651X:
1112 /* Since we don't auto-negotiate... 1Gbps full duplex link */
1113 result.s.link_up = 1;
1114 result.s.full_duplex = 1;
1115 result.s.speed = 1000;
1116 return result;
1117 break;
1118 #endif
1119 }
1120 #endif
1121
1122 phy_addr = cvmx_helper_board_get_mii_address(ipd_port);
1123 //cvmx_dprintf("ipd_port=%d phy_addr=%d broadcom=%d\n",
1124 // ipd_port, phy_addr, is_broadcom_phy);
1125 if (phy_addr != -1)
1126 {
1127 if (is_broadcom_phy)
1128 {
1129 result = __get_broadcom_phy_link_state(phy_addr);
1130 }
1131 else
1132 {
1133 /* This code assumes we are using a Marvell Gigabit PHY. */
1134 result = __get_marvell_phy_link_state(phy_addr);
1135 }
1136 }
1137 else if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN58XX)
1138 || OCTEON_IS_MODEL(OCTEON_CN50XX))
1139 {
1140 /* We don't have a PHY address, so attempt to use in-band status. It is
1141 really important that boards not supporting in-band status never get
1142 here. Reading broken in-band status tends to do bad things */
1143 result = __get_inband_link_state(ipd_port);
1144 }
1145 #if defined(OCTEON_VENDOR_GEFES)
1146 else if( (OCTEON_IS_MODEL(OCTEON_CN56XX)) || (OCTEON_IS_MODEL(OCTEON_CN63XX)) )
1147 {
1148 int interface = cvmx_helper_get_interface_num(ipd_port);
1149 int index = cvmx_helper_get_interface_index_num(ipd_port);
1150 cvmx_pcsx_miscx_ctl_reg_t mode_type;
1151 cvmx_pcsx_mrx_status_reg_t mrx_status;
1152 cvmx_pcsx_anx_adv_reg_t anxx_adv;
1153 cvmx_pcsx_sgmx_lp_adv_reg_t sgmii_inband_status;
1154
1155 anxx_adv.u64 = cvmx_read_csr(CVMX_PCSX_ANX_ADV_REG(index, interface));
1156 mrx_status.u64 = cvmx_read_csr(CVMX_PCSX_MRX_STATUS_REG(index, interface));
1157
1158 mode_type.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
1159
1160 /* Read Octeon's inband status */
1161 sgmii_inband_status.u64 = cvmx_read_csr(CVMX_PCSX_SGMX_LP_ADV_REG(index, interface));
1162
1163 result.s.link_up = sgmii_inband_status.s.link;
1164 result.s.full_duplex = sgmii_inband_status.s.dup;
1165 switch (sgmii_inband_status.s.speed)
1166 {
1167 case 0: /* 10 Mbps */
1168 result.s.speed = 10;
1169 break;
1170 case 1: /* 100 Mbps */
1171 result.s.speed = 100;
1172 break;
1173 case 2: /* 1 Gbps */
1174 result.s.speed = 1000;
1175 break;
1176 case 3: /* Illegal */
1177 result.s.speed = 0;
1178 result.s.link_up = 0;
1179 break;
1180 }
1181 }
1182 #endif
1183 else
1184 {
1185 /* We don't have a PHY address and we don't have in-band status. There
1186 is no way to determine the link speed. Return down assuming this
1187 port isn't wired */
1188 result.u64 = 0;
1189 }
1190
1191 /* If link is down, return all fields as zero. */
1192 if (!result.s.link_up)
1193 result.u64 = 0;
1194
1195 return result;
1196 }
1197
1198
1199 /**
1200 * This function as a board specific method of changing the PHY
1201 * speed, duplex, and autonegotiation. This programs the PHY and
1202 * not Octeon. This can be used to force Octeon's links to
1203 * specific settings.
1204 *
1205 * @param phy_addr The address of the PHY to program
1206 * @param link_flags
1207 * Flags to control autonegotiation. Bit 0 is autonegotiation
1208 * enable/disable to maintain backward compatibility.
1209 * @param link_info Link speed to program. If the speed is zero and autonegotiation
1210 * is enabled, all possible negotiation speeds are advertised.
1211 *
1212 * @return Zero on success, negative on failure
1213 */
cvmx_helper_board_link_set_phy(int phy_addr,cvmx_helper_board_set_phy_link_flags_types_t link_flags,cvmx_helper_link_info_t link_info)1214 int cvmx_helper_board_link_set_phy(int phy_addr, cvmx_helper_board_set_phy_link_flags_types_t link_flags,
1215 cvmx_helper_link_info_t link_info)
1216 {
1217
1218 /* Set the flow control settings based on link_flags */
1219 if ((link_flags & set_phy_link_flags_flow_control_mask) != set_phy_link_flags_flow_control_dont_touch)
1220 {
1221 cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
1222 reg_autoneg_adver.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
1223 reg_autoneg_adver.s.asymmetric_pause = (link_flags & set_phy_link_flags_flow_control_mask) == set_phy_link_flags_flow_control_enable;
1224 reg_autoneg_adver.s.pause = (link_flags & set_phy_link_flags_flow_control_mask) == set_phy_link_flags_flow_control_enable;
1225 cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER, reg_autoneg_adver.u16);
1226 }
1227
1228 /* If speed isn't set and autoneg is on advertise all supported modes */
1229 if ((link_flags & set_phy_link_flags_autoneg) && (link_info.s.speed == 0))
1230 {
1231 cvmx_mdio_phy_reg_control_t reg_control;
1232 cvmx_mdio_phy_reg_status_t reg_status;
1233 cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
1234 cvmx_mdio_phy_reg_extended_status_t reg_extended_status;
1235 cvmx_mdio_phy_reg_control_1000_t reg_control_1000;
1236
1237 reg_status.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_STATUS);
1238 reg_autoneg_adver.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
1239 reg_autoneg_adver.s.advert_100base_t4 = reg_status.s.capable_100base_t4;
1240 reg_autoneg_adver.s.advert_10base_tx_full = reg_status.s.capable_10_full;
1241 reg_autoneg_adver.s.advert_10base_tx_half = reg_status.s.capable_10_half;
1242 reg_autoneg_adver.s.advert_100base_tx_full = reg_status.s.capable_100base_x_full;
1243 reg_autoneg_adver.s.advert_100base_tx_half = reg_status.s.capable_100base_x_half;
1244 cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER, reg_autoneg_adver.u16);
1245 if (reg_status.s.capable_extended_status)
1246 {
1247 reg_extended_status.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_EXTENDED_STATUS);
1248 reg_control_1000.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL_1000);
1249 reg_control_1000.s.advert_1000base_t_full = reg_extended_status.s.capable_1000base_t_full;
1250 reg_control_1000.s.advert_1000base_t_half = reg_extended_status.s.capable_1000base_t_half;
1251 cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL_1000, reg_control_1000.u16);
1252 }
1253 reg_control.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL);
1254 reg_control.s.autoneg_enable = 1;
1255 reg_control.s.restart_autoneg = 1;
1256 cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
1257 }
1258 else if ((link_flags & set_phy_link_flags_autoneg))
1259 {
1260 cvmx_mdio_phy_reg_control_t reg_control;
1261 cvmx_mdio_phy_reg_status_t reg_status;
1262 cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
1263 cvmx_mdio_phy_reg_control_1000_t reg_control_1000;
1264
1265 reg_status.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_STATUS);
1266 reg_autoneg_adver.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
1267 reg_autoneg_adver.s.advert_100base_t4 = 0;
1268 reg_autoneg_adver.s.advert_10base_tx_full = 0;
1269 reg_autoneg_adver.s.advert_10base_tx_half = 0;
1270 reg_autoneg_adver.s.advert_100base_tx_full = 0;
1271 reg_autoneg_adver.s.advert_100base_tx_half = 0;
1272 if (reg_status.s.capable_extended_status)
1273 {
1274 reg_control_1000.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL_1000);
1275 reg_control_1000.s.advert_1000base_t_full = 0;
1276 reg_control_1000.s.advert_1000base_t_half = 0;
1277 }
1278 switch (link_info.s.speed)
1279 {
1280 case 10:
1281 reg_autoneg_adver.s.advert_10base_tx_full = link_info.s.full_duplex;
1282 reg_autoneg_adver.s.advert_10base_tx_half = !link_info.s.full_duplex;
1283 break;
1284 case 100:
1285 reg_autoneg_adver.s.advert_100base_tx_full = link_info.s.full_duplex;
1286 reg_autoneg_adver.s.advert_100base_tx_half = !link_info.s.full_duplex;
1287 break;
1288 case 1000:
1289 reg_control_1000.s.advert_1000base_t_full = link_info.s.full_duplex;
1290 reg_control_1000.s.advert_1000base_t_half = !link_info.s.full_duplex;
1291 break;
1292 }
1293 cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER, reg_autoneg_adver.u16);
1294 if (reg_status.s.capable_extended_status)
1295 cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL_1000, reg_control_1000.u16);
1296 reg_control.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL);
1297 reg_control.s.autoneg_enable = 1;
1298 reg_control.s.restart_autoneg = 1;
1299 cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
1300 }
1301 else
1302 {
1303 cvmx_mdio_phy_reg_control_t reg_control;
1304 reg_control.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL);
1305 reg_control.s.autoneg_enable = 0;
1306 reg_control.s.restart_autoneg = 1;
1307 reg_control.s.duplex = link_info.s.full_duplex;
1308 if (link_info.s.speed == 1000)
1309 {
1310 reg_control.s.speed_msb = 1;
1311 reg_control.s.speed_lsb = 0;
1312 }
1313 else if (link_info.s.speed == 100)
1314 {
1315 reg_control.s.speed_msb = 0;
1316 reg_control.s.speed_lsb = 1;
1317 }
1318 else if (link_info.s.speed == 10)
1319 {
1320 reg_control.s.speed_msb = 0;
1321 reg_control.s.speed_lsb = 0;
1322 }
1323 cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
1324 }
1325 return 0;
1326 }
1327
1328
1329 /**
1330 * @INTERNAL
1331 * This function is called by cvmx_helper_interface_probe() after it
1332 * determines the number of ports Octeon can support on a specific
1333 * interface. This function is the per board location to override
1334 * this value. It is called with the number of ports Octeon might
1335 * support and should return the number of actual ports on the
1336 * board.
1337 *
1338 * This function must be modified for every new Octeon board.
1339 * Internally it uses switch statements based on the cvmx_sysinfo
1340 * data to determine board types and revisions. It relies on the
1341 * fact that every Octeon board receives a unique board type
1342 * enumeration from the bootloader.
1343 *
1344 * @param interface Interface to probe
1345 * @param supported_ports
1346 * Number of ports Octeon supports.
1347 *
1348 * @return Number of ports the actual board supports. Many times this will
1349 * simple be "support_ports".
1350 */
__cvmx_helper_board_interface_probe(int interface,int supported_ports)1351 int __cvmx_helper_board_interface_probe(int interface, int supported_ports)
1352 {
1353 switch (cvmx_sysinfo_get()->board_type)
1354 {
1355 case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
1356 case CVMX_BOARD_TYPE_LANAI2_A:
1357 case CVMX_BOARD_TYPE_LANAI2_U:
1358 case CVMX_BOARD_TYPE_LANAI2_G:
1359 if (interface == 0)
1360 return 2;
1361 break;
1362 case CVMX_BOARD_TYPE_BBGW_REF:
1363 if (interface == 0)
1364 return 2;
1365 break;
1366 case CVMX_BOARD_TYPE_NIC_XLE_4G:
1367 if (interface == 0)
1368 return 0;
1369 break;
1370 /* The 2nd interface on the EBH5600 is connected to the Marvel switch,
1371 which we don't support. Disable ports connected to it */
1372 case CVMX_BOARD_TYPE_EBH5600:
1373 if (interface == 1)
1374 return 0;
1375 break;
1376 case CVMX_BOARD_TYPE_EBB5600:
1377 #ifdef CVMX_ENABLE_PKO_FUNCTIONS
1378 if (cvmx_helper_interface_get_mode(interface) == CVMX_HELPER_INTERFACE_MODE_PICMG)
1379 return 0;
1380 #endif
1381 break;
1382 case CVMX_BOARD_TYPE_EBT5600:
1383 /* Disable loopback. */
1384 if (interface == 3)
1385 return 0;
1386 break;
1387 case CVMX_BOARD_TYPE_EBT5810:
1388 return 1; /* Two ports on each SPI: 1 hooked to MAC, 1 loopback
1389 ** Loopback disabled by default. */
1390 case CVMX_BOARD_TYPE_NIC2E:
1391 if (interface == 0)
1392 return 2;
1393 #if defined(OCTEON_VENDOR_LANNER)
1394 case CVMX_BOARD_TYPE_CUST_LANNER_MR955:
1395 if (interface == 1)
1396 return 12;
1397 break;
1398 #endif
1399 #if defined(OCTEON_VENDOR_GEFES)
1400 case CVMX_BOARD_TYPE_CUST_TNPA5651X:
1401 if (interface < 2) /* interface can be EITHER 0 or 1 */
1402 return 1;//always return 1 for XAUI and SGMII mode.
1403 break;
1404 case CVMX_BOARD_TYPE_CUST_TNPA56X4:
1405 if ((interface == 0) &&
1406 (cvmx_helper_interface_get_mode(interface) == CVMX_HELPER_INTERFACE_MODE_SGMII))
1407 {
1408 cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg;
1409
1410 /* For this port we need to set the mode to 1000BaseX */
1411 pcsx_miscx_ctl_reg.u64 =
1412 cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(0, interface));
1413 pcsx_miscx_ctl_reg.cn56xx.mode = 1;
1414 cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(0, interface),
1415 pcsx_miscx_ctl_reg.u64);
1416 pcsx_miscx_ctl_reg.u64 =
1417 cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(1, interface));
1418 pcsx_miscx_ctl_reg.cn56xx.mode = 1;
1419 cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(1, interface),
1420 pcsx_miscx_ctl_reg.u64);
1421
1422 return 2;
1423 }
1424 break;
1425 #endif
1426 }
1427 #ifdef CVMX_BUILD_FOR_UBOOT
1428 if (CVMX_HELPER_INTERFACE_MODE_SPI == cvmx_helper_interface_get_mode(interface) && getenv("disable_spi"))
1429 return 0;
1430 #endif
1431 return supported_ports;
1432 }
1433
1434
1435 /**
1436 * @INTERNAL
1437 * Enable packet input/output from the hardware. This function is
1438 * called after by cvmx_helper_packet_hardware_enable() to
1439 * perform board specific initialization. For most boards
1440 * nothing is needed.
1441 *
1442 * @param interface Interface to enable
1443 *
1444 * @return Zero on success, negative on failure
1445 */
__cvmx_helper_board_hardware_enable(int interface)1446 int __cvmx_helper_board_hardware_enable(int interface)
1447 {
1448 if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3005_EVB_HS5)
1449 {
1450 if (interface == 0)
1451 {
1452 /* Different config for switch port */
1453 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(1, interface), 0);
1454 cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(1, interface), 0);
1455 /* Boards with gigabit WAN ports need a different setting that is
1456 compatible with 100 Mbit settings */
1457 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), 0xc);
1458 cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 0xc);
1459 }
1460 }
1461 else if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_LANAI2_U)
1462 {
1463 if (interface == 0)
1464 {
1465 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), 16);
1466 cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 16);
1467 }
1468 }
1469 else if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3010_EVB_HS5)
1470 {
1471 /* Broadcom PHYs require different ASX clocks. Unfortunately
1472 many customer don't define a new board Id and simply
1473 mangle the CN3010_EVB_HS5 */
1474 if (interface == 0)
1475 {
1476 /* Some customers boards use a hacked up bootloader that identifies them as
1477 ** CN3010_EVB_HS5 evaluation boards. This leads to all kinds of configuration
1478 ** problems. Detect one case, and print warning, while trying to do the right thing.
1479 */
1480 int phy_addr = cvmx_helper_board_get_mii_address(0);
1481 if (phy_addr != -1)
1482 {
1483 int phy_identifier = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x2);
1484 /* Is it a Broadcom PHY? */
1485 if (phy_identifier == 0x0143)
1486 {
1487 cvmx_dprintf("\n");
1488 cvmx_dprintf("ERROR:\n");
1489 cvmx_dprintf("ERROR: Board type is CVMX_BOARD_TYPE_CN3010_EVB_HS5, but Broadcom PHY found.\n");
1490 cvmx_dprintf("ERROR: The board type is mis-configured, and software malfunctions are likely.\n");
1491 cvmx_dprintf("ERROR: All boards require a unique board type to identify them.\n");
1492 cvmx_dprintf("ERROR:\n");
1493 cvmx_dprintf("\n");
1494 cvmx_wait(1000000000);
1495 cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 5);
1496 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), 5);
1497 }
1498 }
1499 }
1500 }
1501 #if defined(OCTEON_VENDOR_UBIQUITI)
1502 else if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CUST_UBIQUITI_E100)
1503 {
1504 /* Configure ASX cloks for all ports on interface 0. */
1505 if (interface == 0)
1506 {
1507 int port;
1508
1509 for (port = 0; port < 3; port++) {
1510 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 16);
1511 cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), 0);
1512 }
1513 }
1514 }
1515 #endif
1516 return 0;
1517 }
1518
1519
1520 /**
1521 * @INTERNAL
1522 * Gets the clock type used for the USB block based on board type.
1523 * Used by the USB code for auto configuration of clock type.
1524 *
1525 * @return USB clock type enumeration
1526 */
__cvmx_helper_board_usb_get_clock_type(void)1527 cvmx_helper_board_usb_clock_types_t __cvmx_helper_board_usb_get_clock_type(void)
1528 {
1529 #if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
1530 const void *fdt_addr = CASTPTR(const void *, cvmx_sysinfo_get()->fdt_addr);
1531 int nodeoffset;
1532 const void *nodep;
1533 int len;
1534 uint32_t speed = 0;
1535 const char *type = NULL;
1536
1537 if (fdt_addr)
1538 {
1539 nodeoffset = fdt_path_offset(fdt_addr, "/soc/uctl");
1540 if (nodeoffset < 0)
1541 nodeoffset = fdt_path_offset(fdt_addr, "/soc/usbn");
1542
1543 if (nodeoffset >= 0)
1544 {
1545 nodep = fdt_getprop(fdt_addr, nodeoffset, "refclk-type", &len);
1546 if (nodep != NULL && len > 0)
1547 type = (const char *)nodep;
1548 else
1549 type = "unknown";
1550 nodep = fdt_getprop(fdt_addr, nodeoffset, "refclk-frequency", &len);
1551 if (nodep != NULL && len == sizeof(uint32_t))
1552 speed = fdt32_to_cpu(*(int *)nodep);
1553 else
1554 speed = 0;
1555 if (!strcmp(type, "crystal"))
1556 {
1557 if (speed == 0 || speed == 12000000)
1558 return USB_CLOCK_TYPE_CRYSTAL_12;
1559 else
1560 printf("Warning: invalid crystal speed for USB clock type in FDT\n");
1561 }
1562 else if (!strcmp(type, "external"))
1563 {
1564 switch (speed) {
1565 case 12000000:
1566 return USB_CLOCK_TYPE_REF_12;
1567 case 24000000:
1568 return USB_CLOCK_TYPE_REF_24;
1569 case 0:
1570 case 48000000:
1571 return USB_CLOCK_TYPE_REF_48;
1572 default:
1573 printf("Warning: invalid USB clock speed of %u hz in FDT\n", speed);
1574 }
1575 }
1576 else
1577 printf("Warning: invalid USB reference clock type \"%s\" in FDT\n", type ? type : "NULL");
1578 }
1579 }
1580 #endif
1581 switch (cvmx_sysinfo_get()->board_type)
1582 {
1583 case CVMX_BOARD_TYPE_BBGW_REF:
1584 case CVMX_BOARD_TYPE_LANAI2_A:
1585 case CVMX_BOARD_TYPE_LANAI2_U:
1586 case CVMX_BOARD_TYPE_LANAI2_G:
1587 #if defined(OCTEON_VENDOR_LANNER)
1588 case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
1589 case CVMX_BOARD_TYPE_CUST_LANNER_MR321X:
1590 #endif
1591 #if defined(OCTEON_VENDOR_UBIQUITI)
1592 case CVMX_BOARD_TYPE_CUST_UBIQUITI_E100:
1593 #endif
1594 #if defined(OCTEON_BOARD_CAPK_0100ND)
1595 case CVMX_BOARD_TYPE_CN3010_EVB_HS5:
1596 #endif
1597 #if defined(OCTEON_VENDOR_GEFES) /* All GEFES' boards use same xtal type */
1598 case CVMX_BOARD_TYPE_TNPA3804:
1599 case CVMX_BOARD_TYPE_AT5810:
1600 case CVMX_BOARD_TYPE_WNPA3850:
1601 case CVMX_BOARD_TYPE_W3860:
1602 case CVMX_BOARD_TYPE_CUST_TNPA5804:
1603 case CVMX_BOARD_TYPE_CUST_W5434:
1604 case CVMX_BOARD_TYPE_CUST_W5650:
1605 case CVMX_BOARD_TYPE_CUST_W5800:
1606 case CVMX_BOARD_TYPE_CUST_W5651X:
1607 case CVMX_BOARD_TYPE_CUST_TNPA5651X:
1608 case CVMX_BOARD_TYPE_CUST_TNPA56X4:
1609 case CVMX_BOARD_TYPE_CUST_W63XX:
1610 #endif
1611 case CVMX_BOARD_TYPE_NIC10E_66:
1612 return USB_CLOCK_TYPE_CRYSTAL_12;
1613 case CVMX_BOARD_TYPE_NIC10E:
1614 return USB_CLOCK_TYPE_REF_12;
1615 default:
1616 break;
1617 }
1618 if (OCTEON_IS_MODEL(OCTEON_CN6XXX) /* Most boards except NIC10e use a 12MHz crystal */
1619 || OCTEON_IS_MODEL(OCTEON_CNF7XXX))
1620 return USB_CLOCK_TYPE_CRYSTAL_12;
1621 return USB_CLOCK_TYPE_REF_48;
1622 }
1623
1624
1625 /**
1626 * @INTERNAL
1627 * Adjusts the number of available USB ports on Octeon based on board
1628 * specifics.
1629 *
1630 * @param supported_ports expected number of ports based on chip type;
1631 *
1632 *
1633 * @return number of available usb ports, based on board specifics.
1634 * Return value is supported_ports if function does not
1635 * override.
1636 */
__cvmx_helper_board_usb_get_num_ports(int supported_ports)1637 int __cvmx_helper_board_usb_get_num_ports(int supported_ports)
1638 {
1639 switch (cvmx_sysinfo_get()->board_type)
1640 {
1641 case CVMX_BOARD_TYPE_NIC_XLE_4G:
1642 case CVMX_BOARD_TYPE_NIC2E:
1643 return 0;
1644 }
1645
1646 return supported_ports;
1647 }
1648
1649
1650