cvmx-helper-sgmii.c revision 210286
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 SGMII initialization, configuration,
48 * and monitoring.
49 *
50 * <hr>$Revision: 42417 $<hr>
51 */
52#include "executive-config.h"
53#include "cvmx-config.h"
54#ifdef CVMX_ENABLE_PKO_FUNCTIONS
55
56#include "cvmx.h"
57#include "cvmx-sysinfo.h"
58#include "cvmx-mdio.h"
59#include "cvmx-helper.h"
60#include "cvmx-helper-board.h"
61
62
63/**
64 * @INTERNAL
65 * Perform initialization required only once for an SGMII port.
66 *
67 * @param interface Interface to init
68 * @param index     Index of prot on the interface
69 *
70 * @return Zero on success, negative on failure
71 */
72static int __cvmx_helper_sgmii_hardware_init_one_time(int interface, int index)
73{
74    const uint64_t clock_mhz = cvmx_sysinfo_get()->cpu_clock_hz / 1000000;
75    cvmx_pcsx_miscx_ctl_reg_t pcs_misc_ctl_reg;
76    cvmx_pcsx_linkx_timer_count_reg_t pcsx_linkx_timer_count_reg;
77    cvmx_gmxx_prtx_cfg_t gmxx_prtx_cfg;
78
79    /* Disable GMX */
80    gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
81    gmxx_prtx_cfg.s.en = 0;
82    cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
83
84    /* Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the appropriate
85        value. 1000BASE-X specifies a 10ms interval. SGMII specifies a 1.6ms
86        interval. */
87    pcs_misc_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
88    pcsx_linkx_timer_count_reg.u64 = cvmx_read_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface));
89    if (pcs_misc_ctl_reg.s.mode)
90    {
91        /* 1000BASE-X */
92        pcsx_linkx_timer_count_reg.s.count = (10000ull * clock_mhz) >> 10;
93    }
94    else
95    {
96        /* SGMII */
97        pcsx_linkx_timer_count_reg.s.count = (1600ull * clock_mhz) >> 10;
98    }
99    cvmx_write_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface), pcsx_linkx_timer_count_reg.u64);
100
101    /* Write the advertisement register to be used as the
102        tx_Config_Reg<D15:D0> of the autonegotiation.
103        In 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG.
104        In SGMII PHY mode, tx_Config_Reg<D15:D0> is PCS*_SGM*_AN_ADV_REG.
105        In SGMII MAC mode, tx_Config_Reg<D15:D0> is the fixed value 0x4001, so
106        this step can be skipped. */
107    if (pcs_misc_ctl_reg.s.mode)
108    {
109        /* 1000BASE-X */
110        cvmx_pcsx_anx_adv_reg_t pcsx_anx_adv_reg;
111        pcsx_anx_adv_reg.u64 = cvmx_read_csr(CVMX_PCSX_ANX_ADV_REG(index, interface));
112        pcsx_anx_adv_reg.s.rem_flt = 0;
113        pcsx_anx_adv_reg.s.pause = 3;
114        pcsx_anx_adv_reg.s.hfd = 1;
115        pcsx_anx_adv_reg.s.fd = 1;
116        cvmx_write_csr(CVMX_PCSX_ANX_ADV_REG(index, interface), pcsx_anx_adv_reg.u64);
117    }
118    else
119    {
120        cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg;
121        pcsx_miscx_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
122        if (pcsx_miscx_ctl_reg.s.mac_phy)
123        {
124            /* PHY Mode */
125            cvmx_pcsx_sgmx_an_adv_reg_t pcsx_sgmx_an_adv_reg;
126            pcsx_sgmx_an_adv_reg.u64 = cvmx_read_csr(CVMX_PCSX_SGMX_AN_ADV_REG(index, interface));
127            pcsx_sgmx_an_adv_reg.s.link = 1;
128            pcsx_sgmx_an_adv_reg.s.dup = 1;
129            pcsx_sgmx_an_adv_reg.s.speed= 2;
130            cvmx_write_csr(CVMX_PCSX_SGMX_AN_ADV_REG(index, interface), pcsx_sgmx_an_adv_reg.u64);
131        }
132        else
133        {
134            /* MAC Mode - Nothing to do */
135        }
136    }
137    return 0;
138}
139
140
141/**
142 * @INTERNAL
143 * Initialize the SERTES link for the first time or after a loss
144 * of link.
145 *
146 * @param interface Interface to init
147 * @param index     Index of prot on the interface
148 *
149 * @return Zero on success, negative on failure
150 */
151static int __cvmx_helper_sgmii_hardware_init_link(int interface, int index)
152{
153    cvmx_pcsx_mrx_control_reg_t control_reg;
154
155    /* Take PCS through a reset sequence.
156        PCS*_MR*_CONTROL_REG[PWR_DN] should be cleared to zero.
157        Write PCS*_MR*_CONTROL_REG[RESET]=1 (while not changing the value of
158            the other PCS*_MR*_CONTROL_REG bits).
159        Read PCS*_MR*_CONTROL_REG[RESET] until it changes value to zero. */
160    control_reg.u64 = cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
161    if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM)
162    {
163        control_reg.s.reset = 1;
164        cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), control_reg.u64);
165        if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_CONTROL_REG(index, interface), cvmx_pcsx_mrx_control_reg_t, reset, ==, 0, 10000))
166        {
167            cvmx_dprintf("SGMII%d: Timeout waiting for port %d to finish reset\n", interface, index);
168            return -1;
169        }
170    }
171
172    /* Write PCS*_MR*_CONTROL_REG[RST_AN]=1 to ensure a fresh sgmii negotiation starts. */
173    control_reg.s.rst_an = 1;
174    control_reg.s.an_en = 1;
175    control_reg.s.pwr_dn = 0;
176    cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), control_reg.u64);
177
178    /* Wait for PCS*_MR*_STATUS_REG[AN_CPT] to be set, indicating that
179        sgmii autonegotiation is complete. In MAC mode this isn't an ethernet
180        link, but a link between Octeon and the PHY */
181    if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) &&
182        CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_STATUS_REG(index, interface), cvmx_pcsx_mrx_status_reg_t, an_cpt, ==, 1, 10000))
183    {
184        //cvmx_dprintf("SGMII%d: Port %d link timeout\n", interface, index);
185        return -1;
186    }
187    return 0;
188}
189
190
191/**
192 * @INTERNAL
193 * Configure an SGMII link to the specified speed after the SERTES
194 * link is up.
195 *
196 * @param interface Interface to init
197 * @param index     Index of prot on the interface
198 * @param link_info Link state to configure
199 *
200 * @return Zero on success, negative on failure
201 */
202static int __cvmx_helper_sgmii_hardware_init_link_speed(int interface, int index, cvmx_helper_link_info_t link_info)
203{
204    int is_enabled;
205    cvmx_gmxx_prtx_cfg_t gmxx_prtx_cfg;
206    cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg;
207
208    /* Disable GMX before we make any changes. Remember the enable state */
209    gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
210    is_enabled = gmxx_prtx_cfg.s.en;
211    gmxx_prtx_cfg.s.en = 0;
212    cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
213
214    /* Wait for GMX to be idle */
215    if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), cvmx_gmxx_prtx_cfg_t, rx_idle, ==, 1, 10000) ||
216        CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), cvmx_gmxx_prtx_cfg_t, tx_idle, ==, 1, 10000))
217    {
218        cvmx_dprintf("SGMII%d: Timeout waiting for port %d to be idle\n", interface, index);
219        return -1;
220    }
221
222    /* Read GMX CFG again to make sure the disable completed */
223    gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
224
225    /* Get the misc control for PCS. We will need to set the duplication amount */
226    pcsx_miscx_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
227
228    /* Use GMXENO to force the link down if the status we get says it should be down */
229    pcsx_miscx_ctl_reg.s.gmxeno = !link_info.s.link_up;
230
231    /* Only change the duplex setting if the link is up */
232    if (link_info.s.link_up)
233        gmxx_prtx_cfg.s.duplex = link_info.s.full_duplex;
234
235    /* Do speed based setting for GMX */
236    switch (link_info.s.speed)
237    {
238        case 10:
239            gmxx_prtx_cfg.s.speed = 0;
240            gmxx_prtx_cfg.s.speed_msb = 1;
241            gmxx_prtx_cfg.s.slottime = 0;
242            pcsx_miscx_ctl_reg.s.samp_pt = 25; /* Setting from GMX-603 */
243            cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
244            cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
245            break;
246        case 100:
247            gmxx_prtx_cfg.s.speed = 0;
248            gmxx_prtx_cfg.s.speed_msb = 0;
249            gmxx_prtx_cfg.s.slottime = 0;
250            pcsx_miscx_ctl_reg.s.samp_pt = 0x5;
251            cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
252            cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
253            break;
254        case 1000:
255            gmxx_prtx_cfg.s.speed = 1;
256            gmxx_prtx_cfg.s.speed_msb = 0;
257            gmxx_prtx_cfg.s.slottime = 1;
258            pcsx_miscx_ctl_reg.s.samp_pt = 1;
259            cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 512);
260            cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 8192);
261            break;
262        default:
263            break;
264    }
265
266    /* Write the new misc control for PCS */
267    cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), pcsx_miscx_ctl_reg.u64);
268
269    /* Write the new GMX settings with the port still disabled */
270    cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
271
272    /* Read GMX CFG again to make sure the config completed */
273    gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
274
275    /* Restore the enabled / disabled state */
276    gmxx_prtx_cfg.s.en = is_enabled;
277    cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
278
279    return 0;
280}
281
282
283/**
284 * @INTERNAL
285 * Bring up the SGMII interface to be ready for packet I/O but
286 * leave I/O disabled using the GMX override. This function
287 * follows the bringup documented in 10.6.3 of the manual.
288 *
289 * @param interface Interface to bringup
290 * @param num_ports Number of ports on the interface
291 *
292 * @return Zero on success, negative on failure
293 */
294static int __cvmx_helper_sgmii_hardware_init(int interface, int num_ports)
295{
296    int index;
297
298    __cvmx_helper_setup_gmx(interface, num_ports);
299
300    for (index=0; index<num_ports; index++)
301    {
302        int ipd_port = cvmx_helper_get_ipd_port(interface, index);
303        __cvmx_helper_sgmii_hardware_init_one_time(interface, index);
304        __cvmx_helper_sgmii_link_set(ipd_port, __cvmx_helper_sgmii_link_get(ipd_port));
305
306    }
307
308    return 0;
309}
310
311
312/**
313 * @INTERNAL
314 * Probe a SGMII interface and determine the number of ports
315 * connected to it. The SGMII interface should still be down after
316 * this call.
317 *
318 * @param interface Interface to probe
319 *
320 * @return Number of ports on the interface. Zero to disable.
321 */
322int __cvmx_helper_sgmii_probe(int interface)
323{
324    cvmx_gmxx_inf_mode_t mode;
325
326    /* Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the interface
327        needs to be enabled before IPD otherwise per port backpressure
328        may not work properly */
329    mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
330    mode.s.en = 1;
331    cvmx_write_csr(CVMX_GMXX_INF_MODE(interface), mode.u64);
332    return 4;
333}
334
335
336/**
337 * @INTERNAL
338 * Bringup and enable a SGMII interface. After this call packet
339 * I/O should be fully functional. This is called with IPD
340 * enabled but PKO disabled.
341 *
342 * @param interface Interface to bring up
343 *
344 * @return Zero on success, negative on failure
345 */
346int __cvmx_helper_sgmii_enable(int interface)
347{
348    int num_ports = cvmx_helper_ports_on_interface(interface);
349    int index;
350
351    __cvmx_helper_sgmii_hardware_init(interface, num_ports);
352
353    for (index=0; index<num_ports; index++)
354    {
355        cvmx_gmxx_prtx_cfg_t gmxx_prtx_cfg;
356        gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
357        gmxx_prtx_cfg.s.en = 1;
358        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
359    }
360    return 0;
361}
362
363
364/**
365 * @INTERNAL
366 * Return the link state of an IPD/PKO port as returned by
367 * auto negotiation. The result of this function may not match
368 * Octeon's link config if auto negotiation has changed since
369 * the last call to cvmx_helper_link_set().
370 *
371 * @param ipd_port IPD/PKO port to query
372 *
373 * @return Link state
374 */
375cvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port)
376{
377    cvmx_helper_link_info_t result;
378    cvmx_pcsx_miscx_ctl_reg_t pcs_misc_ctl_reg;
379    int interface = cvmx_helper_get_interface_num(ipd_port);
380    int index = cvmx_helper_get_interface_index_num(ipd_port);
381    cvmx_pcsx_mrx_control_reg_t pcsx_mrx_control_reg;
382
383    result.u64 = 0;
384
385    if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
386    {
387        /* The simulator gives you a simulated 1Gbps full duplex link */
388        result.s.link_up = 1;
389        result.s.full_duplex = 1;
390        result.s.speed = 1000;
391        return result;
392    }
393
394    pcsx_mrx_control_reg.u64 = cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
395    if (pcsx_mrx_control_reg.s.loopbck1)
396    {
397        /* Force 1Gbps full duplex link for internal loopback */
398        result.s.link_up = 1;
399        result.s.full_duplex = 1;
400        result.s.speed = 1000;
401        return result;
402    }
403
404
405    pcs_misc_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
406    if (pcs_misc_ctl_reg.s.mode)
407    {
408        /* 1000BASE-X */
409        // FIXME
410    }
411    else
412    {
413        cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg;
414        pcsx_miscx_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
415        if (pcsx_miscx_ctl_reg.s.mac_phy)
416        {
417            /* PHY Mode */
418            cvmx_pcsx_mrx_status_reg_t pcsx_mrx_status_reg;
419            cvmx_pcsx_anx_results_reg_t pcsx_anx_results_reg;
420
421            /* Don't bother continuing if the SERTES low level link is down */
422            pcsx_mrx_status_reg.u64 = cvmx_read_csr(CVMX_PCSX_MRX_STATUS_REG(index, interface));
423            if (pcsx_mrx_status_reg.s.lnk_st == 0)
424            {
425                if (__cvmx_helper_sgmii_hardware_init_link(interface, index) != 0)
426                    return result;
427            }
428
429            /* Read the autoneg results */
430            pcsx_anx_results_reg.u64 = cvmx_read_csr(CVMX_PCSX_ANX_RESULTS_REG(index, interface));
431            if (pcsx_anx_results_reg.s.an_cpt)
432            {
433                /* Auto negotiation is complete. Set status accordingly */
434                result.s.full_duplex = pcsx_anx_results_reg.s.dup;
435                result.s.link_up = pcsx_anx_results_reg.s.link_ok;
436                switch (pcsx_anx_results_reg.s.spd)
437                {
438                    case 0:
439                        result.s.speed = 10;
440                        break;
441                    case 1:
442                        result.s.speed = 100;
443                        break;
444                    case 2:
445                        result.s.speed = 1000;
446                        break;
447                    default:
448                        result.s.speed = 0;
449                        result.s.link_up = 0;
450                        break;
451                }
452            }
453            else
454            {
455                /* Auto negotiation isn't complete. Return link down */
456                result.s.speed = 0;
457                result.s.link_up = 0;
458            }
459        }
460        else /* MAC Mode */
461        {
462            result = __cvmx_helper_board_link_get(ipd_port);
463        }
464    }
465    return result;
466}
467
468
469/**
470 * @INTERNAL
471 * Configure an IPD/PKO port for the specified link state. This
472 * function does not influence auto negotiation at the PHY level.
473 * The passed link state must always match the link state returned
474 * by cvmx_helper_link_get(). It is normally best to use
475 * cvmx_helper_link_autoconf() instead.
476 *
477 * @param ipd_port  IPD/PKO port to configure
478 * @param link_info The new link state
479 *
480 * @return Zero on success, negative on failure
481 */
482int __cvmx_helper_sgmii_link_set(int ipd_port, cvmx_helper_link_info_t link_info)
483{
484    int interface = cvmx_helper_get_interface_num(ipd_port);
485    int index = cvmx_helper_get_interface_index_num(ipd_port);
486    __cvmx_helper_sgmii_hardware_init_link(interface, index);
487    return __cvmx_helper_sgmii_hardware_init_link_speed(interface, index, link_info);
488}
489
490/**
491 * @INTERNAL
492 * Configure a port for internal and/or external loopback. Internal loopback
493 * causes packets sent by the port to be received by Octeon. External loopback
494 * causes packets received from the wire to sent out again.
495 *
496 * @param ipd_port IPD/PKO port to loopback.
497 * @param enable_internal
498 *                 Non zero if you want internal loopback
499 * @param enable_external
500 *                 Non zero if you want external loopback
501 *
502 * @return Zero on success, negative on failure.
503 */
504int __cvmx_helper_sgmii_configure_loopback(int ipd_port, int enable_internal, int enable_external)
505{
506    int interface = cvmx_helper_get_interface_num(ipd_port);
507    int index = cvmx_helper_get_interface_index_num(ipd_port);
508    cvmx_pcsx_mrx_control_reg_t pcsx_mrx_control_reg;
509    cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg;
510
511    pcsx_mrx_control_reg.u64 = cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
512    pcsx_mrx_control_reg.s.loopbck1 = enable_internal;
513    cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), pcsx_mrx_control_reg.u64);
514
515    pcsx_miscx_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
516    pcsx_miscx_ctl_reg.s.loopbck2 = enable_external;
517    cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), pcsx_miscx_ctl_reg.u64);
518
519    __cvmx_helper_sgmii_hardware_init_link(interface, index);
520    return 0;
521}
522
523#endif /* CVMX_ENABLE_PKO_FUNCTIONS */
524