cvmx-helper-rgmii.c revision 216476
190792Sgshapiro/***********************license start***************
290792Sgshapiro * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
390792Sgshapiro * reserved.
490792Sgshapiro *
590792Sgshapiro *
690792Sgshapiro * Redistribution and use in source and binary forms, with or without
790792Sgshapiro * modification, are permitted provided that the following conditions are
890792Sgshapiro * met:
990792Sgshapiro *
1090792Sgshapiro *   * Redistributions of source code must retain the above copyright
1190792Sgshapiro *     notice, this list of conditions and the following disclaimer.
1290792Sgshapiro *
1390792Sgshapiro *   * Redistributions in binary form must reproduce the above
1490792Sgshapiro *     copyright notice, this list of conditions and the following
1590792Sgshapiro *     disclaimer in the documentation and/or other materials provided
1690792Sgshapiro *     with the distribution.
1790792Sgshapiro
1890792Sgshapiro *   * Neither the name of Cavium Networks nor the names of
1990792Sgshapiro *     its contributors may be used to endorse or promote products
2090792Sgshapiro *     derived from this software without specific prior written
2190792Sgshapiro *     permission.
2290792Sgshapiro
2390792Sgshapiro * This Software, including technical data, may be subject to U.S. export  control
2490792Sgshapiro * laws, including the U.S. Export Administration Act and its  associated
2590792Sgshapiro * regulations, and may be subject to export or import  regulations in other
2690792Sgshapiro * countries.
2790792Sgshapiro
2890792Sgshapiro * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
2990792Sgshapiro * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
3090792Sgshapiro * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
3190792Sgshapiro * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
3290792Sgshapiro * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
3390792Sgshapiro * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
3490792Sgshapiro * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
3590792Sgshapiro * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
3690792Sgshapiro * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
3790792Sgshapiro * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
3890792Sgshapiro ***********************license end**************************************/
3990792Sgshapiro
4090792Sgshapiro
4190792Sgshapiro
4290792Sgshapiro
4390792Sgshapiro
4490792Sgshapiro
4590792Sgshapiro
4690792Sgshapiro/**
4790792Sgshapiro * @file
4890792Sgshapiro *
4990792Sgshapiro * Functions for RGMII/GMII/MII initialization, configuration,
5090792Sgshapiro * and monitoring.
5190792Sgshapiro *
5290792Sgshapiro * <hr>$Revision: 49448 $<hr>
5390792Sgshapiro */
5490792Sgshapiro#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
5590792Sgshapiro#include <asm/octeon/cvmx.h>
5690792Sgshapiro#include <asm/octeon/cvmx-config.h>
5790792Sgshapiro#ifdef CVMX_ENABLE_PKO_FUNCTIONS
5890792Sgshapiro#include <asm/octeon/cvmx-pko.h>
5990792Sgshapiro#include <asm/octeon/cvmx-helper.h>
6090792Sgshapiro#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	case CVMX_BOARD_TYPE_CUST_LANNER_MR321X:
243            if (port == 0) {
244                cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 4);
245	    } else {
246                cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 7);
247            }
248            cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), 0);
249	    break;
250	}
251#else
252        /*
253         * For board types we can determine at runtime.
254         */
255        if (OCTEON_IS_MODEL(OCTEON_CN50XX))
256        {
257            cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 16);
258            cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), 16);
259        }
260        else
261        {
262            cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), 24);
263            cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), 24);
264        }
265#endif
266#endif
267    }
268
269    __cvmx_helper_setup_gmx(interface, num_ports);
270
271    /* enable the ports now */
272    for (port=0; port<num_ports; port++)
273    {
274        cvmx_gmxx_prtx_cfg_t gmx_cfg;
275        cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port(interface, port));
276        gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(port, interface));
277        gmx_cfg.s.en = 1;
278        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(port, interface), gmx_cfg.u64);
279    }
280    return 0;
281}
282
283
284/**
285 * @INTERNAL
286 * Return the link state of an IPD/PKO port as returned by
287 * auto negotiation. The result of this function may not match
288 * Octeon's link config if auto negotiation has changed since
289 * the last call to cvmx_helper_link_set().
290 *
291 * @param ipd_port IPD/PKO port to query
292 *
293 * @return Link state
294 */
295cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port)
296{
297    int interface = cvmx_helper_get_interface_num(ipd_port);
298    int index = cvmx_helper_get_interface_index_num(ipd_port);
299    cvmx_asxx_prt_loop_t asxx_prt_loop;
300
301    asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface));
302    if (asxx_prt_loop.s.int_loop & (1<<index))
303    {
304        /* Force 1Gbps full duplex on internal loopback */
305        cvmx_helper_link_info_t result;
306        result.u64 = 0;
307        result.s.full_duplex = 1;
308        result.s.link_up = 1;
309        result.s.speed = 1000;
310        return result;
311    }
312    else
313        return __cvmx_helper_board_link_get(ipd_port);
314}
315
316
317/**
318 * @INTERNAL
319 * Configure an IPD/PKO port for the specified link state. This
320 * function does not influence auto negotiation at the PHY level.
321 * The passed link state must always match the link state returned
322 * by cvmx_helper_link_get(). It is normally best to use
323 * cvmx_helper_link_autoconf() instead.
324 *
325 * @param ipd_port  IPD/PKO port to configure
326 * @param link_info The new link state
327 *
328 * @return Zero on success, negative on failure
329 */
330int __cvmx_helper_rgmii_link_set(int ipd_port, cvmx_helper_link_info_t link_info)
331{
332    int result = 0;
333    int interface = cvmx_helper_get_interface_num(ipd_port);
334    int index = cvmx_helper_get_interface_index_num(ipd_port);
335    cvmx_gmxx_prtx_cfg_t original_gmx_cfg;
336    cvmx_gmxx_prtx_cfg_t new_gmx_cfg;
337    cvmx_pko_mem_queue_qos_t pko_mem_queue_qos;
338    cvmx_pko_mem_queue_qos_t pko_mem_queue_qos_save[16];
339    cvmx_gmxx_tx_ovr_bp_t gmx_tx_ovr_bp;
340    cvmx_gmxx_tx_ovr_bp_t gmx_tx_ovr_bp_save;
341    int i;
342
343    /* Ignore speed sets in the simulator */
344    if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
345        return 0;
346
347    /* Read the current settings so we know the current enable state */
348    original_gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
349    new_gmx_cfg = original_gmx_cfg;
350
351    /* Disable the lowest level RX */
352    cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface),
353                   cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) & ~(1<<index));
354
355    /* Disable all queues so that TX should become idle */
356    for (i=0; i<cvmx_pko_get_num_queues(ipd_port); i++)
357    {
358        int queue = cvmx_pko_get_base_queue(ipd_port) + i;
359        cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue);
360        pko_mem_queue_qos.u64 = cvmx_read_csr(CVMX_PKO_MEM_QUEUE_QOS);
361        pko_mem_queue_qos.s.pid = ipd_port;
362        pko_mem_queue_qos.s.qid = queue;
363        pko_mem_queue_qos_save[i] = pko_mem_queue_qos;
364        pko_mem_queue_qos.s.qos_mask = 0;
365        cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos.u64);
366    }
367
368    /* Disable backpressure */
369    gmx_tx_ovr_bp.u64 = cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface));
370    gmx_tx_ovr_bp_save = gmx_tx_ovr_bp;
371    gmx_tx_ovr_bp.s.bp &= ~(1<<index);
372    gmx_tx_ovr_bp.s.en |= 1<<index;
373    cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp.u64);
374    cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface));
375
376    /* Poll the GMX state machine waiting for it to become idle. Preferably we
377        should only change speed when it is idle. If it doesn't become idle we
378        will still do the speed change, but there is a slight chance that GMX
379        will lockup */
380    cvmx_write_csr(CVMX_NPI_DBG_SELECT, interface*0x800 + index*0x100 + 0x880);
381    CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, cvmx_dbg_data_t, data&7, ==, 0, 10000);
382    CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, cvmx_dbg_data_t, data&0xf, ==, 0, 10000);
383
384    /* Disable the port before we make any changes */
385    new_gmx_cfg.s.en = 0;
386    cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64);
387    cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
388
389    /* Set full/half duplex */
390    if (!link_info.s.link_up)
391        new_gmx_cfg.s.duplex = 1;   /* Force full duplex on down links */
392    else
393        new_gmx_cfg.s.duplex = link_info.s.full_duplex;
394
395    /* Set the link speed. Anything unknown is set to 1Gbps */
396    if (link_info.s.speed == 10)
397    {
398        new_gmx_cfg.s.slottime = 0;
399        new_gmx_cfg.s.speed = 0;
400    }
401    else if (link_info.s.speed == 100)
402    {
403        new_gmx_cfg.s.slottime = 0;
404        new_gmx_cfg.s.speed = 0;
405    }
406    else
407    {
408        new_gmx_cfg.s.slottime = 1;
409        new_gmx_cfg.s.speed = 1;
410    }
411
412    /* Adjust the clocks */
413    if (link_info.s.speed == 10)
414    {
415        cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 50);
416        cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40);
417        cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
418    }
419    else if (link_info.s.speed == 100)
420    {
421        cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 5);
422        cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40);
423        cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
424    }
425    else
426    {
427        cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1);
428        cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200);
429        cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000);
430    }
431
432    if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
433    {
434        if ((link_info.s.speed == 10) || (link_info.s.speed == 100))
435        {
436            cvmx_gmxx_inf_mode_t mode;
437            mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
438
439            /*
440            ** Port  .en  .type  .p0mii  Configuration
441            ** ----  ---  -----  ------  -----------------------------------------
442            **  X      0     X      X    All links are disabled.
443            **  0      1     X      0    Port 0 is RGMII
444            **  0      1     X      1    Port 0 is MII
445            **  1      1     0      X    Ports 1 and 2 are configured as RGMII ports.
446            **  1      1     1      X    Port 1: GMII/MII; Port 2: disabled. GMII or
447            **                           MII port is selected by GMX_PRT1_CFG[SPEED].
448            */
449
450            /* In MII mode, CLK_CNT = 1. */
451            if (((index == 0) && (mode.s.p0mii == 1)) || ((index != 0) && (mode.s.type == 1)))
452            {
453                cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1);
454            }
455        }
456    }
457
458    /* Do a read to make sure all setup stuff is complete */
459    cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
460
461    /* Save the new GMX setting without enabling the port */
462    cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64);
463
464    /* Enable the lowest level RX */
465    cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface),
466                   cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) | (1<<index));
467
468    /* Re-enable the TX path */
469    for (i=0; i<cvmx_pko_get_num_queues(ipd_port); i++)
470    {
471        int queue = cvmx_pko_get_base_queue(ipd_port) + i;
472        cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue);
473        cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos_save[i].u64);
474    }
475
476    /* Restore backpressure */
477    cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp_save.u64);
478
479    /* Restore the GMX enable state. Port config is complete */
480    new_gmx_cfg.s.en = original_gmx_cfg.s.en;
481    cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64);
482
483    return result;
484}
485
486
487/**
488 * @INTERNAL
489 * Configure a port for internal and/or external loopback. Internal loopback
490 * causes packets sent by the port to be received by Octeon. External loopback
491 * causes packets received from the wire to sent out again.
492 *
493 * @param ipd_port IPD/PKO port to loopback.
494 * @param enable_internal
495 *                 Non zero if you want internal loopback
496 * @param enable_external
497 *                 Non zero if you want external loopback
498 *
499 * @return Zero on success, negative on failure.
500 */
501int __cvmx_helper_rgmii_configure_loopback(int ipd_port, int enable_internal, int enable_external)
502{
503    int interface = cvmx_helper_get_interface_num(ipd_port);
504    int index = cvmx_helper_get_interface_index_num(ipd_port);
505    int original_enable;
506    cvmx_gmxx_prtx_cfg_t gmx_cfg;
507    cvmx_asxx_prt_loop_t asxx_prt_loop;
508
509    /* Read the current enable state and save it */
510    gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
511    original_enable = gmx_cfg.s.en;
512    /* Force port to be disabled */
513    gmx_cfg.s.en = 0;
514    if (enable_internal)
515    {
516        /* Force speed if we're doing internal loopback */
517        gmx_cfg.s.duplex = 1;
518        gmx_cfg.s.slottime = 1;
519        gmx_cfg.s.speed = 1;
520        cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1);
521        cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200);
522        cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000);
523    }
524    cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
525
526    /* Set the loopback bits */
527    asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface));
528    if (enable_internal)
529        asxx_prt_loop.s.int_loop |= 1<<index;
530    else
531        asxx_prt_loop.s.int_loop &= ~(1<<index);
532    if (enable_external)
533        asxx_prt_loop.s.ext_loop |= 1<<index;
534    else
535        asxx_prt_loop.s.ext_loop &= ~(1<<index);
536    cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), asxx_prt_loop.u64);
537
538    /* Force enables in internal loopback */
539    if (enable_internal)
540    {
541        uint64_t tmp;
542        tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface));
543        cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), (1 << index) | tmp);
544        tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface));
545        cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), (1 << index) | tmp);
546        original_enable = 1;
547    }
548
549    /* Restore the enable state */
550    gmx_cfg.s.en = original_enable;
551    cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
552    return 0;
553}
554
555#endif /* CVMX_ENABLE_PKO_FUNCTIONS */
556
557