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