linux/drivers/staging/octeon/cvmx-helper-rgmii.c
<<
>>
Prefs
   1/***********************license start***************
   2 * Author: Cavium Networks
   3 *
   4 * Contact: support@caviumnetworks.com
   5 * This file is part of the OCTEON SDK
   6 *
   7 * Copyright (c) 2003-2008 Cavium Networks
   8 *
   9 * This file is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License, Version 2, as
  11 * published by the Free Software Foundation.
  12 *
  13 * This file is distributed in the hope that it will be useful, but
  14 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
  15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  16 * NONINFRINGEMENT.  See the GNU General Public License for more
  17 * details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this file; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  22 * or visit http://www.gnu.org/licenses/.
  23 *
  24 * This file may also be available under a different license from Cavium.
  25 * Contact Cavium Networks for more information
  26 ***********************license end**************************************/
  27
  28/*
  29 * Functions for RGMII/GMII/MII initialization, configuration,
  30 * and monitoring.
  31 */
  32#include <asm/octeon/octeon.h>
  33
  34#include "cvmx-config.h"
  35
  36
  37#include "cvmx-mdio.h"
  38#include "cvmx-pko.h"
  39#include "cvmx-helper.h"
  40#include "cvmx-helper-board.h"
  41
  42#include <asm/octeon/cvmx-npi-defs.h>
  43#include "cvmx-gmxx-defs.h"
  44#include "cvmx-asxx-defs.h"
  45#include "cvmx-dbg-defs.h"
  46
  47void __cvmx_interrupt_gmxx_enable(int interface);
  48void __cvmx_interrupt_asxx_enable(int block);
  49
  50/**
  51 * Probe RGMII ports and determine the number present
  52 *
  53 * @interface: Interface to probe
  54 *
  55 * Returns Number of RGMII/GMII/MII ports (0-4).
  56 */
  57int __cvmx_helper_rgmii_probe(int interface)
  58{
  59        int num_ports = 0;
  60        union cvmx_gmxx_inf_mode mode;
  61        mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
  62
  63        if (mode.s.type) {
  64                if (OCTEON_IS_MODEL(OCTEON_CN38XX)
  65                    || OCTEON_IS_MODEL(OCTEON_CN58XX)) {
  66                        cvmx_dprintf("ERROR: RGMII initialize called in "
  67                                     "SPI interface\n");
  68                } else if (OCTEON_IS_MODEL(OCTEON_CN31XX)
  69                           || OCTEON_IS_MODEL(OCTEON_CN30XX)
  70                           || OCTEON_IS_MODEL(OCTEON_CN50XX)) {
  71                        /*
  72                         * On these chips "type" says we're in
  73                         * GMII/MII mode. This limits us to 2 ports
  74                         */
  75                        num_ports = 2;
  76                } else {
  77                        cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n",
  78                                     __func__);
  79                }
  80        } else {
  81                if (OCTEON_IS_MODEL(OCTEON_CN38XX)
  82                    || OCTEON_IS_MODEL(OCTEON_CN58XX)) {
  83                        num_ports = 4;
  84                } else if (OCTEON_IS_MODEL(OCTEON_CN31XX)
  85                           || OCTEON_IS_MODEL(OCTEON_CN30XX)
  86                           || OCTEON_IS_MODEL(OCTEON_CN50XX)) {
  87                        num_ports = 3;
  88                } else {
  89                        cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n",
  90                                     __func__);
  91                }
  92        }
  93        return num_ports;
  94}
  95
  96/**
  97 * Put an RGMII interface in loopback mode. Internal packets sent
  98 * out will be received back again on the same port. Externally
  99 * received packets will echo back out.
 100 *
 101 * @port:   IPD port number to loop.
 102 */
 103void cvmx_helper_rgmii_internal_loopback(int port)
 104{
 105        int interface = (port >> 4) & 1;
 106        int index = port & 0xf;
 107        uint64_t tmp;
 108
 109        union cvmx_gmxx_prtx_cfg gmx_cfg;
 110        gmx_cfg.u64 = 0;
 111        gmx_cfg.s.duplex = 1;
 112        gmx_cfg.s.slottime = 1;
 113        gmx_cfg.s.speed = 1;
 114        cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1);
 115        cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200);
 116        cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000);
 117        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
 118        tmp = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface));
 119        cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), (1 << index) | tmp);
 120        tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface));
 121        cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), (1 << index) | tmp);
 122        tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface));
 123        cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), (1 << index) | tmp);
 124        gmx_cfg.s.en = 1;
 125        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
 126}
 127
 128/**
 129 * Workaround ASX setup errata with CN38XX pass1
 130 *
 131 * @interface: Interface to setup
 132 * @port:      Port to setup (0..3)
 133 * @cpu_clock_hz:
 134 *                  Chip frequency in Hertz
 135 *
 136 * Returns Zero on success, negative on failure
 137 */
 138static int __cvmx_helper_errata_asx_pass1(int interface, int port,
 139                                          int cpu_clock_hz)
 140{
 141        /* Set hi water mark as per errata GMX-4 */
 142        if (cpu_clock_hz >= 325000000 && cpu_clock_hz < 375000000)
 143                cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 12);
 144        else if (cpu_clock_hz >= 375000000 && cpu_clock_hz < 437000000)
 145                cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 11);
 146        else if (cpu_clock_hz >= 437000000 && cpu_clock_hz < 550000000)
 147                cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 10);
 148        else if (cpu_clock_hz >= 550000000 && cpu_clock_hz < 687000000)
 149                cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 9);
 150        else
 151                cvmx_dprintf("Illegal clock frequency (%d). "
 152                        "CVMX_ASXX_TX_HI_WATERX not set\n", cpu_clock_hz);
 153        return 0;
 154}
 155
 156/**
 157 * Configure all of the ASX, GMX, and PKO regsiters required
 158 * to get RGMII to function on the supplied interface.
 159 *
 160 * @interface: PKO Interface to configure (0 or 1)
 161 *
 162 * Returns Zero on success
 163 */
 164int __cvmx_helper_rgmii_enable(int interface)
 165{
 166        int num_ports = cvmx_helper_ports_on_interface(interface);
 167        int port;
 168        struct cvmx_sysinfo *sys_info_ptr = cvmx_sysinfo_get();
 169        union cvmx_gmxx_inf_mode mode;
 170        union cvmx_asxx_tx_prt_en asx_tx;
 171        union cvmx_asxx_rx_prt_en asx_rx;
 172
 173        mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
 174
 175        if (mode.s.en == 0)
 176                return -1;
 177        if ((OCTEON_IS_MODEL(OCTEON_CN38XX) ||
 178             OCTEON_IS_MODEL(OCTEON_CN58XX)) && mode.s.type == 1)
 179                /* Ignore SPI interfaces */
 180                return -1;
 181
 182        /* Configure the ASX registers needed to use the RGMII ports */
 183        asx_tx.u64 = 0;
 184        asx_tx.s.prt_en = cvmx_build_mask(num_ports);
 185        cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), asx_tx.u64);
 186
 187        asx_rx.u64 = 0;
 188        asx_rx.s.prt_en = cvmx_build_mask(num_ports);
 189        cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), asx_rx.u64);
 190
 191        /* Configure the GMX registers needed to use the RGMII ports */
 192        for (port = 0; port < num_ports; port++) {
 193                /* Setting of CVMX_GMXX_TXX_THRESH has been moved to
 194                   __cvmx_helper_setup_gmx() */
 195
 196                if (cvmx_octeon_is_pass1())
 197                        __cvmx_helper_errata_asx_pass1(interface, port,
 198                                                       sys_info_ptr->
 199                                                       cpu_clock_hz);
 200                else {
 201                        /*
 202                         * Configure more flexible RGMII preamble
 203                         * checking. Pass 1 doesn't support this
 204                         * feature.
 205                         */
 206                        union cvmx_gmxx_rxx_frm_ctl frm_ctl;
 207                        frm_ctl.u64 =
 208                            cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL
 209                                          (port, interface));
 210                        /* New field, so must be compile time */
 211                        frm_ctl.s.pre_free = 1;
 212                        cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(port, interface),
 213                                       frm_ctl.u64);
 214                }
 215
 216                /*
 217                 * Each pause frame transmitted will ask for about 10M
 218                 * bit times before resume.  If buffer space comes
 219                 * available before that time has expired, an XON
 220                 * pause frame (0 time) will be transmitted to restart
 221                 * the flow.
 222                 */
 223                cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_TIME(port, interface),
 224                               20000);
 225                cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_INTERVAL
 226                               (port, interface), 19000);
 227
 228                if (OCTEON_IS_MODEL(OCTEON_CN50XX)) {
 229                        cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface),
 230                                       16);
 231                        cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface),
 232                                       16);
 233                } else {
 234                        cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface),
 235                                       24);
 236                        cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface),
 237                                       24);
 238                }
 239        }
 240
 241        __cvmx_helper_setup_gmx(interface, num_ports);
 242
 243        /* enable the ports now */
 244        for (port = 0; port < num_ports; port++) {
 245                union cvmx_gmxx_prtx_cfg gmx_cfg;
 246                cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port
 247                                          (interface, port));
 248                gmx_cfg.u64 =
 249                    cvmx_read_csr(CVMX_GMXX_PRTX_CFG(port, interface));
 250                gmx_cfg.s.en = 1;
 251                cvmx_write_csr(CVMX_GMXX_PRTX_CFG(port, interface),
 252                               gmx_cfg.u64);
 253        }
 254        __cvmx_interrupt_asxx_enable(interface);
 255        __cvmx_interrupt_gmxx_enable(interface);
 256
 257        return 0;
 258}
 259
 260/**
 261 * Return the link state of an IPD/PKO port as returned by
 262 * auto negotiation. The result of this function may not match
 263 * Octeon's link config if auto negotiation has changed since
 264 * the last call to cvmx_helper_link_set().
 265 *
 266 * @ipd_port: IPD/PKO port to query
 267 *
 268 * Returns Link state
 269 */
 270cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port)
 271{
 272        int interface = cvmx_helper_get_interface_num(ipd_port);
 273        int index = cvmx_helper_get_interface_index_num(ipd_port);
 274        union cvmx_asxx_prt_loop asxx_prt_loop;
 275
 276        asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface));
 277        if (asxx_prt_loop.s.int_loop & (1 << index)) {
 278                /* Force 1Gbps full duplex on internal loopback */
 279                cvmx_helper_link_info_t result;
 280                result.u64 = 0;
 281                result.s.full_duplex = 1;
 282                result.s.link_up = 1;
 283                result.s.speed = 1000;
 284                return result;
 285        } else
 286                return __cvmx_helper_board_link_get(ipd_port);
 287}
 288
 289/**
 290 * Configure an IPD/PKO port for the specified link state. This
 291 * function does not influence auto negotiation at the PHY level.
 292 * The passed link state must always match the link state returned
 293 * by cvmx_helper_link_get(). It is normally best to use
 294 * cvmx_helper_link_autoconf() instead.
 295 *
 296 * @ipd_port:  IPD/PKO port to configure
 297 * @link_info: The new link state
 298 *
 299 * Returns Zero on success, negative on failure
 300 */
 301int __cvmx_helper_rgmii_link_set(int ipd_port,
 302                                 cvmx_helper_link_info_t link_info)
 303{
 304        int result = 0;
 305        int interface = cvmx_helper_get_interface_num(ipd_port);
 306        int index = cvmx_helper_get_interface_index_num(ipd_port);
 307        union cvmx_gmxx_prtx_cfg original_gmx_cfg;
 308        union cvmx_gmxx_prtx_cfg new_gmx_cfg;
 309        union cvmx_pko_mem_queue_qos pko_mem_queue_qos;
 310        union cvmx_pko_mem_queue_qos pko_mem_queue_qos_save[16];
 311        union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp;
 312        union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp_save;
 313        int i;
 314
 315        /* Ignore speed sets in the simulator */
 316        if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
 317                return 0;
 318
 319        /* Read the current settings so we know the current enable state */
 320        original_gmx_cfg.u64 =
 321            cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
 322        new_gmx_cfg = original_gmx_cfg;
 323
 324        /* Disable the lowest level RX */
 325        cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface),
 326                       cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) &
 327                                     ~(1 << index));
 328
 329        /* Disable all queues so that TX should become idle */
 330        for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) {
 331                int queue = cvmx_pko_get_base_queue(ipd_port) + i;
 332                cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue);
 333                pko_mem_queue_qos.u64 = cvmx_read_csr(CVMX_PKO_MEM_QUEUE_QOS);
 334                pko_mem_queue_qos.s.pid = ipd_port;
 335                pko_mem_queue_qos.s.qid = queue;
 336                pko_mem_queue_qos_save[i] = pko_mem_queue_qos;
 337                pko_mem_queue_qos.s.qos_mask = 0;
 338                cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos.u64);
 339        }
 340
 341        /* Disable backpressure */
 342        gmx_tx_ovr_bp.u64 = cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface));
 343        gmx_tx_ovr_bp_save = gmx_tx_ovr_bp;
 344        gmx_tx_ovr_bp.s.bp &= ~(1 << index);
 345        gmx_tx_ovr_bp.s.en |= 1 << index;
 346        cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp.u64);
 347        cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface));
 348
 349        /*
 350         * Poll the GMX state machine waiting for it to become
 351         * idle. Preferably we should only change speed when it is
 352         * idle. If it doesn't become idle we will still do the speed
 353         * change, but there is a slight chance that GMX will
 354         * lockup.
 355         */
 356        cvmx_write_csr(CVMX_NPI_DBG_SELECT,
 357                       interface * 0x800 + index * 0x100 + 0x880);
 358        CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, data & 7,
 359                        ==, 0, 10000);
 360        CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, data & 0xf,
 361                        ==, 0, 10000);
 362
 363        /* Disable the port before we make any changes */
 364        new_gmx_cfg.s.en = 0;
 365        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64);
 366        cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
 367
 368        /* Set full/half duplex */
 369        if (cvmx_octeon_is_pass1())
 370                /* Half duplex is broken for 38XX Pass 1 */
 371                new_gmx_cfg.s.duplex = 1;
 372        else if (!link_info.s.link_up)
 373                /* Force full duplex on down links */
 374                new_gmx_cfg.s.duplex = 1;
 375        else
 376                new_gmx_cfg.s.duplex = link_info.s.full_duplex;
 377
 378        /* Set the link speed. Anything unknown is set to 1Gbps */
 379        if (link_info.s.speed == 10) {
 380                new_gmx_cfg.s.slottime = 0;
 381                new_gmx_cfg.s.speed = 0;
 382        } else if (link_info.s.speed == 100) {
 383                new_gmx_cfg.s.slottime = 0;
 384                new_gmx_cfg.s.speed = 0;
 385        } else {
 386                new_gmx_cfg.s.slottime = 1;
 387                new_gmx_cfg.s.speed = 1;
 388        }
 389
 390        /* Adjust the clocks */
 391        if (link_info.s.speed == 10) {
 392                cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 50);
 393                cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40);
 394                cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
 395        } else if (link_info.s.speed == 100) {
 396                cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 5);
 397                cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40);
 398                cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
 399        } else {
 400                cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1);
 401                cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200);
 402                cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000);
 403        }
 404
 405        if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN50XX)) {
 406                if ((link_info.s.speed == 10) || (link_info.s.speed == 100)) {
 407                        union cvmx_gmxx_inf_mode mode;
 408                        mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
 409
 410        /*
 411         * Port  .en  .type  .p0mii  Configuration
 412         * ----  ---  -----  ------  -----------------------------------------
 413         *  X      0     X      X    All links are disabled.
 414         *  0      1     X      0    Port 0 is RGMII
 415         *  0      1     X      1    Port 0 is MII
 416         *  1      1     0      X    Ports 1 and 2 are configured as RGMII ports.
 417         *  1      1     1      X    Port 1: GMII/MII; Port 2: disabled. GMII or
 418         *                           MII port is selected by GMX_PRT1_CFG[SPEED].
 419         */
 420
 421                        /* In MII mode, CLK_CNT = 1. */
 422                        if (((index == 0) && (mode.s.p0mii == 1))
 423                            || ((index != 0) && (mode.s.type == 1))) {
 424                                cvmx_write_csr(CVMX_GMXX_TXX_CLK
 425                                               (index, interface), 1);
 426                        }
 427                }
 428        }
 429
 430        /* Do a read to make sure all setup stuff is complete */
 431        cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
 432
 433        /* Save the new GMX setting without enabling the port */
 434        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64);
 435
 436        /* Enable the lowest level RX */
 437        cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface),
 438                       cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) | (1 <<
 439                                                                        index));
 440
 441        /* Re-enable the TX path */
 442        for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) {
 443                int queue = cvmx_pko_get_base_queue(ipd_port) + i;
 444                cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue);
 445                cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS,
 446                               pko_mem_queue_qos_save[i].u64);
 447        }
 448
 449        /* Restore backpressure */
 450        cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp_save.u64);
 451
 452        /* Restore the GMX enable state. Port config is complete */
 453        new_gmx_cfg.s.en = original_gmx_cfg.s.en;
 454        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64);
 455
 456        return result;
 457}
 458
 459/**
 460 * Configure a port for internal and/or external loopback. Internal loopback
 461 * causes packets sent by the port to be received by Octeon. External loopback
 462 * causes packets received from the wire to sent out again.
 463 *
 464 * @ipd_port: IPD/PKO port to loopback.
 465 * @enable_internal:
 466 *                 Non zero if you want internal loopback
 467 * @enable_external:
 468 *                 Non zero if you want external loopback
 469 *
 470 * Returns Zero on success, negative on failure.
 471 */
 472int __cvmx_helper_rgmii_configure_loopback(int ipd_port, int enable_internal,
 473                                           int enable_external)
 474{
 475        int interface = cvmx_helper_get_interface_num(ipd_port);
 476        int index = cvmx_helper_get_interface_index_num(ipd_port);
 477        int original_enable;
 478        union cvmx_gmxx_prtx_cfg gmx_cfg;
 479        union cvmx_asxx_prt_loop asxx_prt_loop;
 480
 481        /* Read the current enable state and save it */
 482        gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
 483        original_enable = gmx_cfg.s.en;
 484        /* Force port to be disabled */
 485        gmx_cfg.s.en = 0;
 486        if (enable_internal) {
 487                /* Force speed if we're doing internal loopback */
 488                gmx_cfg.s.duplex = 1;
 489                gmx_cfg.s.slottime = 1;
 490                gmx_cfg.s.speed = 1;
 491                cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1);
 492                cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200);
 493                cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000);
 494        }
 495        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
 496
 497        /* Set the loopback bits */
 498        asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface));
 499        if (enable_internal)
 500                asxx_prt_loop.s.int_loop |= 1 << index;
 501        else
 502                asxx_prt_loop.s.int_loop &= ~(1 << index);
 503        if (enable_external)
 504                asxx_prt_loop.s.ext_loop |= 1 << index;
 505        else
 506                asxx_prt_loop.s.ext_loop &= ~(1 << index);
 507        cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), asxx_prt_loop.u64);
 508
 509        /* Force enables in internal loopback */
 510        if (enable_internal) {
 511                uint64_t tmp;
 512                tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface));
 513                cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface),
 514                               (1 << index) | tmp);
 515                tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface));
 516                cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface),
 517                               (1 << index) | tmp);
 518                original_enable = 1;
 519        }
 520
 521        /* Restore the enable state */
 522        gmx_cfg.s.en = original_enable;
 523        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
 524        return 0;
 525}
 526