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