cvmx-helper-rgmii.c revision 215990
1/***********************license start*************** 2 * Copyright (c) 2003-2010 Cavium Networks (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 Networks 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 NETWORKS 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 * Functions for RGMII/GMII/MII initialization, configuration, 50 * and monitoring. 51 * 52 * <hr>$Revision: 49448 $<hr> 53 */ 54#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 55#include <asm/octeon/cvmx.h> 56#include <asm/octeon/cvmx-config.h> 57#ifdef CVMX_ENABLE_PKO_FUNCTIONS 58#include <asm/octeon/cvmx-pko.h> 59#include <asm/octeon/cvmx-helper.h> 60#include <asm/octeon/cvmx-helper-board.h> 61#endif 62#include <asm/octeon/cvmx-asxx-defs.h> 63#include <asm/octeon/cvmx-gmxx-defs.h> 64#include <asm/octeon/cvmx-pko-defs.h> 65#include <asm/octeon/cvmx-npi-defs.h> 66#include <asm/octeon/cvmx-dbg-defs.h> 67 68#else 69#if !defined(__FreeBSD__) || !defined(_KERNEL) 70#include "executive-config.h" 71#include "cvmx-config.h" 72#ifdef CVMX_ENABLE_PKO_FUNCTIONS 73 74#include "cvmx.h" 75#include "cvmx-sysinfo.h" 76#include "cvmx-mdio.h" 77#include "cvmx-pko.h" 78#include "cvmx-helper.h" 79#include "cvmx-helper-board.h" 80#endif 81#else 82#include "cvmx.h" 83#include "cvmx-sysinfo.h" 84#include "cvmx-mdio.h" 85#include "cvmx-pko.h" 86#include "cvmx-helper.h" 87#include "cvmx-helper-board.h" 88#endif 89#endif 90 91#ifdef CVMX_ENABLE_PKO_FUNCTIONS 92 93/** 94 * @INTERNAL 95 * Probe RGMII ports and determine the number present 96 * 97 * @param interface Interface to probe 98 * 99 * @return Number of RGMII/GMII/MII ports (0-4). 100 */ 101int __cvmx_helper_rgmii_probe(int interface) 102{ 103 int num_ports = 0; 104 cvmx_gmxx_inf_mode_t mode; 105 mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); 106 107 if (mode.s.type) 108 { 109 if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) 110 { 111 cvmx_dprintf("ERROR: RGMII initialize called in SPI interface\n"); 112 } 113 else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN50XX)) 114 { 115 /* On these chips "type" says we're in GMII/MII mode. This 116 limits us to 2 ports */ 117 num_ports = 2; 118 } 119 else 120 { 121 cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n", __FUNCTION__); 122 } 123 } 124 else 125 { 126 if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) 127 { 128 num_ports = 4; 129 } 130 else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN50XX)) 131 { 132 num_ports = 3; 133 } 134 else 135 { 136 cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n", __FUNCTION__); 137 } 138 } 139 return num_ports; 140} 141 142 143/** 144 * Put an RGMII interface in loopback mode. Internal packets sent 145 * out will be received back again on the same port. Externally 146 * received packets will echo back out. 147 * 148 * @param port IPD port number to loop. 149 */ 150void cvmx_helper_rgmii_internal_loopback(int port) 151{ 152 int interface = (port >> 4) & 1; 153 int index = port & 0xf; 154 uint64_t tmp; 155 156 cvmx_gmxx_prtx_cfg_t gmx_cfg; 157 gmx_cfg.u64 = 0; 158 gmx_cfg.s.duplex = 1; 159 gmx_cfg.s.slottime = 1; 160 gmx_cfg.s.speed = 1; 161 cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); 162 cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); 163 cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); 164 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); 165 tmp = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); 166 cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), (1 << index) | tmp); 167 tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface)); 168 cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), (1 << index) | tmp); 169 tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)); 170 cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), (1 << index) | tmp); 171 gmx_cfg.s.en = 1; 172 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); 173} 174 175 176/** 177 * @INTERNAL 178 * Configure all of the ASX, GMX, and PKO regsiters required 179 * to get RGMII to function on the supplied interface. 180 * 181 * @param interface PKO Interface to configure (0 or 1) 182 * 183 * @return Zero on success 184 */ 185int __cvmx_helper_rgmii_enable(int interface) 186{ 187 int num_ports = cvmx_helper_ports_on_interface(interface); 188 int port; 189 cvmx_gmxx_inf_mode_t mode; 190 cvmx_asxx_tx_prt_en_t asx_tx; 191 cvmx_asxx_rx_prt_en_t asx_rx; 192 193 mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); 194 195 if (mode.s.en == 0) 196 return -1; 197 if ((OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) && mode.s.type == 1) /* Ignore SPI interfaces */ 198 return -1; 199 200 /* Configure the ASX registers needed to use the RGMII ports */ 201 asx_tx.u64 = 0; 202 asx_tx.s.prt_en = cvmx_build_mask(num_ports); 203 cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), asx_tx.u64); 204 205 asx_rx.u64 = 0; 206 asx_rx.s.prt_en = cvmx_build_mask(num_ports); 207 cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), asx_rx.u64); 208 209 /* Configure the GMX registers needed to use the RGMII ports */ 210 for (port=0; port<num_ports; port++) 211 { 212 /* Setting of CVMX_GMXX_TXX_THRESH has been moved to 213 __cvmx_helper_setup_gmx() */ 214 215 /* Configure more flexible RGMII preamble checking. Pass 1 doesn't 216 support this feature. */ 217 cvmx_gmxx_rxx_frm_ctl_t frm_ctl; 218 frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(port, interface)); 219 frm_ctl.s.pre_free = 1; /* New field, so must be compile time */ 220 cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(port, interface), frm_ctl.u64); 221 222 /* Each pause frame transmitted will ask for about 10M bit times 223 before resume. If buffer space comes available before that time 224 has expired, an XON pause frame (0 time) will be transmitted to 225 restart the flow. */ 226 cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_TIME(port, interface), 20000); 227 cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_INTERVAL(port, interface), 19000); 228 229 /* 230 * Board types we have to know at compile-time. 231 */ 232#if defined(OCTEON_BOARD_CAPK_0100ND) 233 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 26); 234 cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), 26); 235#else 236 /* 237 * Vendor-defined board types. 238 */ 239#if defined(OCTEON_VENDOR_LANNER) 240 switch (cvmx_sysinfo_get()->board_type) { 241 case CVMX_BOARD_TYPE_CUST_LANNER_MR320: 242 if (port == 0) { 243 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 4); 244 } else { 245 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 7); 246 } 247 cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), 0); 248 break; 249 } 250#else 251 /* 252 * For board types we can determine at runtime. 253 */ 254 if (OCTEON_IS_MODEL(OCTEON_CN50XX)) 255 { 256 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 16); 257 cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), 16); 258 } 259 else 260 { 261 cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 24); 262 cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), 24); 263 } 264#endif 265#endif 266 } 267 268 __cvmx_helper_setup_gmx(interface, num_ports); 269 270 /* enable the ports now */ 271 for (port=0; port<num_ports; port++) 272 { 273 cvmx_gmxx_prtx_cfg_t gmx_cfg; 274 cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port(interface, port)); 275 gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(port, interface)); 276 gmx_cfg.s.en = 1; 277 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(port, interface), gmx_cfg.u64); 278 } 279 return 0; 280} 281 282 283/** 284 * @INTERNAL 285 * Return the link state of an IPD/PKO port as returned by 286 * auto negotiation. The result of this function may not match 287 * Octeon's link config if auto negotiation has changed since 288 * the last call to cvmx_helper_link_set(). 289 * 290 * @param ipd_port IPD/PKO port to query 291 * 292 * @return Link state 293 */ 294cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port) 295{ 296 int interface = cvmx_helper_get_interface_num(ipd_port); 297 int index = cvmx_helper_get_interface_index_num(ipd_port); 298 cvmx_asxx_prt_loop_t asxx_prt_loop; 299 300 asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); 301 if (asxx_prt_loop.s.int_loop & (1<<index)) 302 { 303 /* Force 1Gbps full duplex on internal loopback */ 304 cvmx_helper_link_info_t result; 305 result.u64 = 0; 306 result.s.full_duplex = 1; 307 result.s.link_up = 1; 308 result.s.speed = 1000; 309 return result; 310 } 311 else 312 return __cvmx_helper_board_link_get(ipd_port); 313} 314 315 316/** 317 * @INTERNAL 318 * Configure an IPD/PKO port for the specified link state. This 319 * function does not influence auto negotiation at the PHY level. 320 * The passed link state must always match the link state returned 321 * by cvmx_helper_link_get(). It is normally best to use 322 * cvmx_helper_link_autoconf() instead. 323 * 324 * @param ipd_port IPD/PKO port to configure 325 * @param link_info The new link state 326 * 327 * @return Zero on success, negative on failure 328 */ 329int __cvmx_helper_rgmii_link_set(int ipd_port, cvmx_helper_link_info_t link_info) 330{ 331 int result = 0; 332 int interface = cvmx_helper_get_interface_num(ipd_port); 333 int index = cvmx_helper_get_interface_index_num(ipd_port); 334 cvmx_gmxx_prtx_cfg_t original_gmx_cfg; 335 cvmx_gmxx_prtx_cfg_t new_gmx_cfg; 336 cvmx_pko_mem_queue_qos_t pko_mem_queue_qos; 337 cvmx_pko_mem_queue_qos_t pko_mem_queue_qos_save[16]; 338 cvmx_gmxx_tx_ovr_bp_t gmx_tx_ovr_bp; 339 cvmx_gmxx_tx_ovr_bp_t gmx_tx_ovr_bp_save; 340 int i; 341 342 /* Ignore speed sets in the simulator */ 343 if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) 344 return 0; 345 346 /* Read the current settings so we know the current enable state */ 347 original_gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 348 new_gmx_cfg = original_gmx_cfg; 349 350 /* Disable the lowest level RX */ 351 cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), 352 cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) & ~(1<<index)); 353 354 /* Disable all queues so that TX should become idle */ 355 for (i=0; i<cvmx_pko_get_num_queues(ipd_port); i++) 356 { 357 int queue = cvmx_pko_get_base_queue(ipd_port) + i; 358 cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue); 359 pko_mem_queue_qos.u64 = cvmx_read_csr(CVMX_PKO_MEM_QUEUE_QOS); 360 pko_mem_queue_qos.s.pid = ipd_port; 361 pko_mem_queue_qos.s.qid = queue; 362 pko_mem_queue_qos_save[i] = pko_mem_queue_qos; 363 pko_mem_queue_qos.s.qos_mask = 0; 364 cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos.u64); 365 } 366 367 /* Disable backpressure */ 368 gmx_tx_ovr_bp.u64 = cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface)); 369 gmx_tx_ovr_bp_save = gmx_tx_ovr_bp; 370 gmx_tx_ovr_bp.s.bp &= ~(1<<index); 371 gmx_tx_ovr_bp.s.en |= 1<<index; 372 cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp.u64); 373 cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface)); 374 375 /* Poll the GMX state machine waiting for it to become idle. Preferably we 376 should only change speed when it is idle. If it doesn't become idle we 377 will still do the speed change, but there is a slight chance that GMX 378 will lockup */ 379 cvmx_write_csr(CVMX_NPI_DBG_SELECT, interface*0x800 + index*0x100 + 0x880); 380 CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, cvmx_dbg_data_t, data&7, ==, 0, 10000); 381 CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, cvmx_dbg_data_t, data&0xf, ==, 0, 10000); 382 383 /* Disable the port before we make any changes */ 384 new_gmx_cfg.s.en = 0; 385 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); 386 cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 387 388 /* Set full/half duplex */ 389 if (!link_info.s.link_up) 390 new_gmx_cfg.s.duplex = 1; /* Force full duplex on down links */ 391 else 392 new_gmx_cfg.s.duplex = link_info.s.full_duplex; 393 394 /* Set the link speed. Anything unknown is set to 1Gbps */ 395 if (link_info.s.speed == 10) 396 { 397 new_gmx_cfg.s.slottime = 0; 398 new_gmx_cfg.s.speed = 0; 399 } 400 else if (link_info.s.speed == 100) 401 { 402 new_gmx_cfg.s.slottime = 0; 403 new_gmx_cfg.s.speed = 0; 404 } 405 else 406 { 407 new_gmx_cfg.s.slottime = 1; 408 new_gmx_cfg.s.speed = 1; 409 } 410 411 /* Adjust the clocks */ 412 if (link_info.s.speed == 10) 413 { 414 cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 50); 415 cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40); 416 cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); 417 } 418 else if (link_info.s.speed == 100) 419 { 420 cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 5); 421 cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40); 422 cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); 423 } 424 else 425 { 426 cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); 427 cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); 428 cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); 429 } 430 431 if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN50XX)) 432 { 433 if ((link_info.s.speed == 10) || (link_info.s.speed == 100)) 434 { 435 cvmx_gmxx_inf_mode_t mode; 436 mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); 437 438 /* 439 ** Port .en .type .p0mii Configuration 440 ** ---- --- ----- ------ ----------------------------------------- 441 ** X 0 X X All links are disabled. 442 ** 0 1 X 0 Port 0 is RGMII 443 ** 0 1 X 1 Port 0 is MII 444 ** 1 1 0 X Ports 1 and 2 are configured as RGMII ports. 445 ** 1 1 1 X Port 1: GMII/MII; Port 2: disabled. GMII or 446 ** MII port is selected by GMX_PRT1_CFG[SPEED]. 447 */ 448 449 /* In MII mode, CLK_CNT = 1. */ 450 if (((index == 0) && (mode.s.p0mii == 1)) || ((index != 0) && (mode.s.type == 1))) 451 { 452 cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); 453 } 454 } 455 } 456 457 /* Do a read to make sure all setup stuff is complete */ 458 cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 459 460 /* Save the new GMX setting without enabling the port */ 461 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); 462 463 /* Enable the lowest level RX */ 464 cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), 465 cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) | (1<<index)); 466 467 /* Re-enable the TX path */ 468 for (i=0; i<cvmx_pko_get_num_queues(ipd_port); i++) 469 { 470 int queue = cvmx_pko_get_base_queue(ipd_port) + i; 471 cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue); 472 cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos_save[i].u64); 473 } 474 475 /* Restore backpressure */ 476 cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp_save.u64); 477 478 /* Restore the GMX enable state. Port config is complete */ 479 new_gmx_cfg.s.en = original_gmx_cfg.s.en; 480 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); 481 482 return result; 483} 484 485 486/** 487 * @INTERNAL 488 * Configure a port for internal and/or external loopback. Internal loopback 489 * causes packets sent by the port to be received by Octeon. External loopback 490 * causes packets received from the wire to sent out again. 491 * 492 * @param ipd_port IPD/PKO port to loopback. 493 * @param enable_internal 494 * Non zero if you want internal loopback 495 * @param enable_external 496 * Non zero if you want external loopback 497 * 498 * @return Zero on success, negative on failure. 499 */ 500int __cvmx_helper_rgmii_configure_loopback(int ipd_port, int enable_internal, int enable_external) 501{ 502 int interface = cvmx_helper_get_interface_num(ipd_port); 503 int index = cvmx_helper_get_interface_index_num(ipd_port); 504 int original_enable; 505 cvmx_gmxx_prtx_cfg_t gmx_cfg; 506 cvmx_asxx_prt_loop_t asxx_prt_loop; 507 508 /* Read the current enable state and save it */ 509 gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 510 original_enable = gmx_cfg.s.en; 511 /* Force port to be disabled */ 512 gmx_cfg.s.en = 0; 513 if (enable_internal) 514 { 515 /* Force speed if we're doing internal loopback */ 516 gmx_cfg.s.duplex = 1; 517 gmx_cfg.s.slottime = 1; 518 gmx_cfg.s.speed = 1; 519 cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); 520 cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); 521 cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); 522 } 523 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); 524 525 /* Set the loopback bits */ 526 asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); 527 if (enable_internal) 528 asxx_prt_loop.s.int_loop |= 1<<index; 529 else 530 asxx_prt_loop.s.int_loop &= ~(1<<index); 531 if (enable_external) 532 asxx_prt_loop.s.ext_loop |= 1<<index; 533 else 534 asxx_prt_loop.s.ext_loop &= ~(1<<index); 535 cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), asxx_prt_loop.u64); 536 537 /* Force enables in internal loopback */ 538 if (enable_internal) 539 { 540 uint64_t tmp; 541 tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface)); 542 cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), (1 << index) | tmp); 543 tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)); 544 cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), (1 << index) | tmp); 545 original_enable = 1; 546 } 547 548 /* Restore the enable state */ 549 gmx_cfg.s.en = original_enable; 550 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); 551 return 0; 552} 553 554#endif /* CVMX_ENABLE_PKO_FUNCTIONS */ 555 556