Back to home page

OSCL-LXR

 
 

    


0001 /***********************license start***************
0002  * Author: Cavium Networks
0003  *
0004  * Contact: support@caviumnetworks.com
0005  * This file is part of the OCTEON SDK
0006  *
0007  * Copyright (C) 2003-2018 Cavium, Inc.
0008  *
0009  * This file is free software; you can redistribute it and/or modify
0010  * it under the terms of the GNU General Public License, Version 2, as
0011  * published by the Free Software Foundation.
0012  *
0013  * This file is distributed in the hope that it will be useful, but
0014  * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
0015  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
0016  * NONINFRINGEMENT.  See the GNU General Public License for more
0017  * details.
0018  *
0019  * You should have received a copy of the GNU General Public License
0020  * along with this file; if not, write to the Free Software
0021  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
0022  * or visit http://www.gnu.org/licenses/.
0023  *
0024  * This file may also be available under a different license from Cavium.
0025  * Contact Cavium Networks for more information
0026  ***********************license end**************************************/
0027 
0028 /*
0029  * Functions for SGMII initialization, configuration,
0030  * and monitoring.
0031  */
0032 
0033 #include <asm/octeon/octeon.h>
0034 
0035 #include <asm/octeon/cvmx-config.h>
0036 
0037 #include <asm/octeon/cvmx-helper.h>
0038 #include <asm/octeon/cvmx-helper-board.h>
0039 
0040 #include <asm/octeon/cvmx-gmxx-defs.h>
0041 #include <asm/octeon/cvmx-pcsx-defs.h>
0042 #include <asm/octeon/cvmx-pcsxx-defs.h>
0043 
0044 /**
0045  * Perform initialization required only once for an SGMII port.
0046  *
0047  * @interface: Interface to init
0048  * @index:     Index of prot on the interface
0049  *
0050  * Returns Zero on success, negative on failure
0051  */
0052 static int __cvmx_helper_sgmii_hardware_init_one_time(int interface, int index)
0053 {
0054     const uint64_t clock_mhz = cvmx_sysinfo_get()->cpu_clock_hz / 1000000;
0055     union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg;
0056     union cvmx_pcsx_linkx_timer_count_reg pcsx_linkx_timer_count_reg;
0057     union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
0058 
0059     /* Disable GMX */
0060     gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
0061     gmxx_prtx_cfg.s.en = 0;
0062     cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
0063 
0064     /*
0065      * Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the
0066      * appropriate value. 1000BASE-X specifies a 10ms
0067      * interval. SGMII specifies a 1.6ms interval.
0068      */
0069     pcs_misc_ctl_reg.u64 =
0070         cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
0071     pcsx_linkx_timer_count_reg.u64 =
0072         cvmx_read_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface));
0073     if (pcs_misc_ctl_reg.s.mode) {
0074         /* 1000BASE-X */
0075         pcsx_linkx_timer_count_reg.s.count =
0076             (10000ull * clock_mhz) >> 10;
0077     } else {
0078         /* SGMII */
0079         pcsx_linkx_timer_count_reg.s.count =
0080             (1600ull * clock_mhz) >> 10;
0081     }
0082     cvmx_write_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface),
0083                pcsx_linkx_timer_count_reg.u64);
0084 
0085     /*
0086      * Write the advertisement register to be used as the
0087      * tx_Config_Reg<D15:D0> of the autonegotiation.  In
0088      * 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG.
0089      * In SGMII PHY mode, tx_Config_Reg<D15:D0> is
0090      * PCS*_SGM*_AN_ADV_REG.  In SGMII MAC mode,
0091      * tx_Config_Reg<D15:D0> is the fixed value 0x4001, so this
0092      * step can be skipped.
0093      */
0094     if (pcs_misc_ctl_reg.s.mode) {
0095         /* 1000BASE-X */
0096         union cvmx_pcsx_anx_adv_reg pcsx_anx_adv_reg;
0097         pcsx_anx_adv_reg.u64 =
0098             cvmx_read_csr(CVMX_PCSX_ANX_ADV_REG(index, interface));
0099         pcsx_anx_adv_reg.s.rem_flt = 0;
0100         pcsx_anx_adv_reg.s.pause = 3;
0101         pcsx_anx_adv_reg.s.hfd = 1;
0102         pcsx_anx_adv_reg.s.fd = 1;
0103         cvmx_write_csr(CVMX_PCSX_ANX_ADV_REG(index, interface),
0104                    pcsx_anx_adv_reg.u64);
0105     } else {
0106         union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
0107         pcsx_miscx_ctl_reg.u64 =
0108             cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
0109         if (pcsx_miscx_ctl_reg.s.mac_phy) {
0110             /* PHY Mode */
0111             union cvmx_pcsx_sgmx_an_adv_reg pcsx_sgmx_an_adv_reg;
0112             pcsx_sgmx_an_adv_reg.u64 =
0113                 cvmx_read_csr(CVMX_PCSX_SGMX_AN_ADV_REG
0114                       (index, interface));
0115             pcsx_sgmx_an_adv_reg.s.link = 1;
0116             pcsx_sgmx_an_adv_reg.s.dup = 1;
0117             pcsx_sgmx_an_adv_reg.s.speed = 2;
0118             cvmx_write_csr(CVMX_PCSX_SGMX_AN_ADV_REG
0119                        (index, interface),
0120                        pcsx_sgmx_an_adv_reg.u64);
0121         } else {
0122             /* MAC Mode - Nothing to do */
0123         }
0124     }
0125     return 0;
0126 }
0127 
0128 /**
0129  * Initialize the SERTES link for the first time or after a loss
0130  * of link.
0131  *
0132  * @interface: Interface to init
0133  * @index:     Index of prot on the interface
0134  *
0135  * Returns Zero on success, negative on failure
0136  */
0137 static int __cvmx_helper_sgmii_hardware_init_link(int interface, int index)
0138 {
0139     union cvmx_pcsx_mrx_control_reg control_reg;
0140 
0141     /*
0142      * Take PCS through a reset sequence.
0143      * PCS*_MR*_CONTROL_REG[PWR_DN] should be cleared to zero.
0144      * Write PCS*_MR*_CONTROL_REG[RESET]=1 (while not changing the
0145      * value of the other PCS*_MR*_CONTROL_REG bits).  Read
0146      * PCS*_MR*_CONTROL_REG[RESET] until it changes value to
0147      * zero.
0148      */
0149     control_reg.u64 =
0150         cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
0151     if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) {
0152         control_reg.s.reset = 1;
0153         cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
0154                    control_reg.u64);
0155         if (CVMX_WAIT_FOR_FIELD64
0156             (CVMX_PCSX_MRX_CONTROL_REG(index, interface),
0157              union cvmx_pcsx_mrx_control_reg, reset, ==, 0, 10000)) {
0158             cvmx_dprintf("SGMII%d: Timeout waiting for port %d "
0159                      "to finish reset\n",
0160                  interface, index);
0161             return -1;
0162         }
0163     }
0164 
0165     /*
0166      * Write PCS*_MR*_CONTROL_REG[RST_AN]=1 to ensure a fresh
0167      * sgmii negotiation starts.
0168      */
0169     control_reg.s.rst_an = 1;
0170     control_reg.s.an_en = 1;
0171     control_reg.s.pwr_dn = 0;
0172     cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface),
0173                control_reg.u64);
0174 
0175     /*
0176      * Wait for PCS*_MR*_STATUS_REG[AN_CPT] to be set, indicating
0177      * that sgmii autonegotiation is complete. In MAC mode this
0178      * isn't an ethernet link, but a link between Octeon and the
0179      * PHY.
0180      */
0181     if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) &&
0182         CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_STATUS_REG(index, interface),
0183                   union cvmx_pcsx_mrx_status_reg, an_cpt, ==, 1,
0184                   10000)) {
0185         /* cvmx_dprintf("SGMII%d: Port %d link timeout\n", interface, index); */
0186         return -1;
0187     }
0188     return 0;
0189 }
0190 
0191 /**
0192  * Configure an SGMII link to the specified speed after the SERTES
0193  * link is up.
0194  *
0195  * @interface: Interface to init
0196  * @index:     Index of prot on the interface
0197  * @link_info: Link state to configure
0198  *
0199  * Returns Zero on success, negative on failure
0200  */
0201 static int __cvmx_helper_sgmii_hardware_init_link_speed(int interface,
0202                             int index,
0203                             union cvmx_helper_link_info
0204                             link_info)
0205 {
0206     int is_enabled;
0207     union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
0208     union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
0209 
0210     /* Disable GMX before we make any changes. Remember the enable state */
0211     gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
0212     is_enabled = gmxx_prtx_cfg.s.en;
0213     gmxx_prtx_cfg.s.en = 0;
0214     cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
0215 
0216     /* Wait for GMX to be idle */
0217     if (CVMX_WAIT_FOR_FIELD64
0218         (CVMX_GMXX_PRTX_CFG(index, interface), union cvmx_gmxx_prtx_cfg,
0219          rx_idle, ==, 1, 10000)
0220         || CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface),
0221                      union cvmx_gmxx_prtx_cfg, tx_idle, ==, 1,
0222                      10000)) {
0223         cvmx_dprintf
0224             ("SGMII%d: Timeout waiting for port %d to be idle\n",
0225              interface, index);
0226         return -1;
0227     }
0228 
0229     /* Read GMX CFG again to make sure the disable completed */
0230     gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
0231 
0232     /*
0233      * Get the misc control for PCS. We will need to set the
0234      * duplication amount.
0235      */
0236     pcsx_miscx_ctl_reg.u64 =
0237         cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
0238 
0239     /*
0240      * Use GMXENO to force the link down if the status we get says
0241      * it should be down.
0242      */
0243     pcsx_miscx_ctl_reg.s.gmxeno = !link_info.s.link_up;
0244 
0245     /* Only change the duplex setting if the link is up */
0246     if (link_info.s.link_up)
0247         gmxx_prtx_cfg.s.duplex = link_info.s.full_duplex;
0248 
0249     /* Do speed based setting for GMX */
0250     switch (link_info.s.speed) {
0251     case 10:
0252         gmxx_prtx_cfg.s.speed = 0;
0253         gmxx_prtx_cfg.s.speed_msb = 1;
0254         gmxx_prtx_cfg.s.slottime = 0;
0255         /* Setting from GMX-603 */
0256         pcsx_miscx_ctl_reg.s.samp_pt = 25;
0257         cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
0258         cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
0259         break;
0260     case 100:
0261         gmxx_prtx_cfg.s.speed = 0;
0262         gmxx_prtx_cfg.s.speed_msb = 0;
0263         gmxx_prtx_cfg.s.slottime = 0;
0264         pcsx_miscx_ctl_reg.s.samp_pt = 0x5;
0265         cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64);
0266         cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0);
0267         break;
0268     case 1000:
0269         gmxx_prtx_cfg.s.speed = 1;
0270         gmxx_prtx_cfg.s.speed_msb = 0;
0271         gmxx_prtx_cfg.s.slottime = 1;
0272         pcsx_miscx_ctl_reg.s.samp_pt = 1;
0273         cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 512);
0274         cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 8192);
0275         break;
0276     default:
0277         break;
0278     }
0279 
0280     /* Write the new misc control for PCS */
0281     cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface),
0282                pcsx_miscx_ctl_reg.u64);
0283 
0284     /* Write the new GMX settings with the port still disabled */
0285     cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
0286 
0287     /* Read GMX CFG again to make sure the config completed */
0288     gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
0289 
0290     /* Restore the enabled / disabled state */
0291     gmxx_prtx_cfg.s.en = is_enabled;
0292     cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64);
0293 
0294     return 0;
0295 }
0296 
0297 /**
0298  * Bring up the SGMII interface to be ready for packet I/O but
0299  * leave I/O disabled using the GMX override. This function
0300  * follows the bringup documented in 10.6.3 of the manual.
0301  *
0302  * @interface: Interface to bringup
0303  * @num_ports: Number of ports on the interface
0304  *
0305  * Returns Zero on success, negative on failure
0306  */
0307 static int __cvmx_helper_sgmii_hardware_init(int interface, int num_ports)
0308 {
0309     int index;
0310 
0311     __cvmx_helper_setup_gmx(interface, num_ports);
0312 
0313     for (index = 0; index < num_ports; index++) {
0314         int ipd_port = cvmx_helper_get_ipd_port(interface, index);
0315         __cvmx_helper_sgmii_hardware_init_one_time(interface, index);
0316         /* Linux kernel driver will call ....link_set with the
0317          * proper link state. In the simulator there is no
0318          * link state polling and hence it is set from
0319          * here.
0320          */
0321         if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
0322             __cvmx_helper_sgmii_link_set(ipd_port,
0323                        __cvmx_helper_sgmii_link_get(ipd_port));
0324     }
0325 
0326     return 0;
0327 }
0328 
0329 int __cvmx_helper_sgmii_enumerate(int interface)
0330 {
0331     return 4;
0332 }
0333 /**
0334  * Probe a SGMII interface and determine the number of ports
0335  * connected to it. The SGMII interface should still be down after
0336  * this call.
0337  *
0338  * @interface: Interface to probe
0339  *
0340  * Returns Number of ports on the interface. Zero to disable.
0341  */
0342 int __cvmx_helper_sgmii_probe(int interface)
0343 {
0344     union cvmx_gmxx_inf_mode mode;
0345 
0346     /*
0347      * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the
0348      * interface needs to be enabled before IPD otherwise per port
0349      * backpressure may not work properly
0350      */
0351     mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface));
0352     mode.s.en = 1;
0353     cvmx_write_csr(CVMX_GMXX_INF_MODE(interface), mode.u64);
0354     return __cvmx_helper_sgmii_enumerate(interface);
0355 }
0356 
0357 /**
0358  * Bringup and enable a SGMII interface. After this call packet
0359  * I/O should be fully functional. This is called with IPD
0360  * enabled but PKO disabled.
0361  *
0362  * @interface: Interface to bring up
0363  *
0364  * Returns Zero on success, negative on failure
0365  */
0366 int __cvmx_helper_sgmii_enable(int interface)
0367 {
0368     int num_ports = cvmx_helper_ports_on_interface(interface);
0369     int index;
0370 
0371     __cvmx_helper_sgmii_hardware_init(interface, num_ports);
0372 
0373     for (index = 0; index < num_ports; index++) {
0374         union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
0375         gmxx_prtx_cfg.u64 =
0376             cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
0377         gmxx_prtx_cfg.s.en = 1;
0378         cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
0379                    gmxx_prtx_cfg.u64);
0380         __cvmx_interrupt_pcsx_intx_en_reg_enable(index, interface);
0381     }
0382     __cvmx_interrupt_pcsxx_int_en_reg_enable(interface);
0383     __cvmx_interrupt_gmxx_enable(interface);
0384     return 0;
0385 }
0386 
0387 /**
0388  * Return the link state of an IPD/PKO port as returned by
0389  * auto negotiation. The result of this function may not match
0390  * Octeon's link config if auto negotiation has changed since
0391  * the last call to cvmx_helper_link_set().
0392  *
0393  * @ipd_port: IPD/PKO port to query
0394  *
0395  * Returns Link state
0396  */
0397 union cvmx_helper_link_info __cvmx_helper_sgmii_link_get(int ipd_port)
0398 {
0399     union cvmx_helper_link_info result;
0400     union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg;
0401     int interface = cvmx_helper_get_interface_num(ipd_port);
0402     int index = cvmx_helper_get_interface_index_num(ipd_port);
0403     union cvmx_pcsx_mrx_control_reg pcsx_mrx_control_reg;
0404 
0405     result.u64 = 0;
0406 
0407     if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) {
0408         /* The simulator gives you a simulated 1Gbps full duplex link */
0409         result.s.link_up = 1;
0410         result.s.full_duplex = 1;
0411         result.s.speed = 1000;
0412         return result;
0413     }
0414 
0415     pcsx_mrx_control_reg.u64 =
0416         cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface));
0417     if (pcsx_mrx_control_reg.s.loopbck1) {
0418         /* Force 1Gbps full duplex link for internal loopback */
0419         result.s.link_up = 1;
0420         result.s.full_duplex = 1;
0421         result.s.speed = 1000;
0422         return result;
0423     }
0424 
0425     pcs_misc_ctl_reg.u64 =
0426         cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
0427     if (pcs_misc_ctl_reg.s.mode) {
0428         /* 1000BASE-X */
0429         /* FIXME */
0430     } else {
0431         union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg;
0432         pcsx_miscx_ctl_reg.u64 =
0433             cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface));
0434         if (pcsx_miscx_ctl_reg.s.mac_phy) {
0435             /* PHY Mode */
0436             union cvmx_pcsx_mrx_status_reg pcsx_mrx_status_reg;
0437             union cvmx_pcsx_anx_results_reg pcsx_anx_results_reg;
0438 
0439             /*
0440              * Don't bother continuing if the SERTES low
0441              * level link is down
0442              */
0443             pcsx_mrx_status_reg.u64 =
0444                 cvmx_read_csr(CVMX_PCSX_MRX_STATUS_REG
0445                       (index, interface));
0446             if (pcsx_mrx_status_reg.s.lnk_st == 0) {
0447                 if (__cvmx_helper_sgmii_hardware_init_link
0448                     (interface, index) != 0)
0449                     return result;
0450             }
0451 
0452             /* Read the autoneg results */
0453             pcsx_anx_results_reg.u64 =
0454                 cvmx_read_csr(CVMX_PCSX_ANX_RESULTS_REG
0455                       (index, interface));
0456             if (pcsx_anx_results_reg.s.an_cpt) {
0457                 /*
0458                  * Auto negotiation is complete. Set
0459                  * status accordingly.
0460                  */
0461                 result.s.full_duplex =
0462                     pcsx_anx_results_reg.s.dup;
0463                 result.s.link_up =
0464                     pcsx_anx_results_reg.s.link_ok;
0465                 switch (pcsx_anx_results_reg.s.spd) {
0466                 case 0:
0467                     result.s.speed = 10;
0468                     break;
0469                 case 1:
0470                     result.s.speed = 100;
0471                     break;
0472                 case 2:
0473                     result.s.speed = 1000;
0474                     break;
0475                 default:
0476                     result.s.speed = 0;
0477                     result.s.link_up = 0;
0478                     break;
0479                 }
0480             } else {
0481                 /*
0482                  * Auto negotiation isn't
0483                  * complete. Return link down.
0484                  */
0485                 result.s.speed = 0;
0486                 result.s.link_up = 0;
0487             }
0488         } else {    /* MAC Mode */
0489 
0490             result = __cvmx_helper_board_link_get(ipd_port);
0491         }
0492     }
0493     return result;
0494 }
0495 
0496 /**
0497  * Configure an IPD/PKO port for the specified link state. This
0498  * function does not influence auto negotiation at the PHY level.
0499  * The passed link state must always match the link state returned
0500  * by cvmx_helper_link_get().
0501  *
0502  * @ipd_port:  IPD/PKO port to configure
0503  * @link_info: The new link state
0504  *
0505  * Returns Zero on success, negative on failure
0506  */
0507 int __cvmx_helper_sgmii_link_set(int ipd_port,
0508                  union cvmx_helper_link_info link_info)
0509 {
0510     int interface = cvmx_helper_get_interface_num(ipd_port);
0511     int index = cvmx_helper_get_interface_index_num(ipd_port);
0512     __cvmx_helper_sgmii_hardware_init_link(interface, index);
0513     return __cvmx_helper_sgmii_hardware_init_link_speed(interface, index,
0514                                 link_info);
0515 }