cvmx-helper-sgmii.c revision 232812
1210284Sjmallett/***********************license start*************** 2232812Sjmallett * Copyright (c) 2003-2010 Cavium Inc. (support@cavium.com). All rights 3215990Sjmallett * reserved. 4210284Sjmallett * 5210284Sjmallett * 6215990Sjmallett * Redistribution and use in source and binary forms, with or without 7215990Sjmallett * modification, are permitted provided that the following conditions are 8215990Sjmallett * met: 9210284Sjmallett * 10215990Sjmallett * * Redistributions of source code must retain the above copyright 11215990Sjmallett * notice, this list of conditions and the following disclaimer. 12210284Sjmallett * 13215990Sjmallett * * Redistributions in binary form must reproduce the above 14215990Sjmallett * copyright notice, this list of conditions and the following 15215990Sjmallett * disclaimer in the documentation and/or other materials provided 16215990Sjmallett * with the distribution. 17215990Sjmallett 18232812Sjmallett * * Neither the name of Cavium Inc. nor the names of 19215990Sjmallett * its contributors may be used to endorse or promote products 20215990Sjmallett * derived from this software without specific prior written 21215990Sjmallett * permission. 22215990Sjmallett 23215990Sjmallett * This Software, including technical data, may be subject to U.S. export control 24215990Sjmallett * laws, including the U.S. Export Administration Act and its associated 25215990Sjmallett * regulations, and may be subject to export or import regulations in other 26215990Sjmallett * countries. 27215990Sjmallett 28215990Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 29232812Sjmallett * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR 30215990Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO 31215990Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR 32215990Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM 33215990Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, 34215990Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF 35215990Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR 36215990Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR 37215990Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. 38210284Sjmallett ***********************license end**************************************/ 39210284Sjmallett 40210284Sjmallett 41210284Sjmallett 42210284Sjmallett 43210284Sjmallett 44210284Sjmallett 45215990Sjmallett 46210284Sjmallett/** 47210284Sjmallett * @file 48210284Sjmallett * 49210284Sjmallett * Functions for SGMII initialization, configuration, 50210284Sjmallett * and monitoring. 51210284Sjmallett * 52232812Sjmallett * <hr>$Revision: 70030 $<hr> 53210284Sjmallett */ 54215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 55215990Sjmallett#include <asm/octeon/cvmx.h> 56215990Sjmallett#include <asm/octeon/cvmx-config.h> 57215990Sjmallett#include <asm/octeon/cvmx-clock.h> 58232812Sjmallett#include <asm/octeon/cvmx-qlm.h> 59215990Sjmallett#ifdef CVMX_ENABLE_PKO_FUNCTIONS 60215990Sjmallett#include <asm/octeon/cvmx-helper.h> 61215990Sjmallett#include <asm/octeon/cvmx-helper-board.h> 62232812Sjmallett#include <asm/octeon/cvmx-helper-cfg.h> 63215990Sjmallett#endif 64215990Sjmallett#include <asm/octeon/cvmx-pcsx-defs.h> 65215990Sjmallett#include <asm/octeon/cvmx-gmxx-defs.h> 66215990Sjmallett#include <asm/octeon/cvmx-ciu-defs.h> 67215990Sjmallett#else 68215990Sjmallett#if !defined(__FreeBSD__) || !defined(_KERNEL) 69215990Sjmallett#include "executive-config.h" 70215990Sjmallett#include "cvmx-config.h" 71215990Sjmallett#ifdef CVMX_ENABLE_PKO_FUNCTIONS 72215990Sjmallett 73210284Sjmallett#include "cvmx.h" 74210284Sjmallett#include "cvmx-sysinfo.h" 75210284Sjmallett#include "cvmx-mdio.h" 76210284Sjmallett#include "cvmx-helper.h" 77210284Sjmallett#include "cvmx-helper-board.h" 78232812Sjmallett#include "cvmx-helper-cfg.h" 79232812Sjmallett#include "cvmx-qlm.h" 80215990Sjmallett#endif 81215990Sjmallett#else 82215990Sjmallett#include "cvmx.h" 83215990Sjmallett#include "cvmx-sysinfo.h" 84215990Sjmallett#include "cvmx-mdio.h" 85215990Sjmallett#include "cvmx-helper.h" 86215990Sjmallett#include "cvmx-helper-board.h" 87232812Sjmallett#include "cvmx-qlm.h" 88215990Sjmallett#endif 89215990Sjmallett#endif 90210284Sjmallett 91210311Sjmallett#ifdef CVMX_ENABLE_PKO_FUNCTIONS 92215990Sjmallett 93210284Sjmallett/** 94210284Sjmallett * @INTERNAL 95210284Sjmallett * Perform initialization required only once for an SGMII port. 96210284Sjmallett * 97210284Sjmallett * @param interface Interface to init 98210284Sjmallett * @param index Index of prot on the interface 99210284Sjmallett * 100210284Sjmallett * @return Zero on success, negative on failure 101210284Sjmallett */ 102210284Sjmallettstatic int __cvmx_helper_sgmii_hardware_init_one_time(int interface, int index) 103210284Sjmallett{ 104215990Sjmallett const uint64_t clock_mhz = cvmx_clock_get_rate(CVMX_CLOCK_SCLK) / 1000000; 105215990Sjmallett cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg; 106210284Sjmallett cvmx_pcsx_linkx_timer_count_reg_t pcsx_linkx_timer_count_reg; 107210284Sjmallett cvmx_gmxx_prtx_cfg_t gmxx_prtx_cfg; 108210284Sjmallett 109210284Sjmallett /* Disable GMX */ 110210284Sjmallett gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 111210284Sjmallett gmxx_prtx_cfg.s.en = 0; 112210284Sjmallett cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); 113210284Sjmallett 114210284Sjmallett /* Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the appropriate 115210284Sjmallett value. 1000BASE-X specifies a 10ms interval. SGMII specifies a 1.6ms 116210284Sjmallett interval. */ 117215990Sjmallett pcsx_miscx_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); 118210284Sjmallett pcsx_linkx_timer_count_reg.u64 = cvmx_read_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface)); 119215990Sjmallett if (pcsx_miscx_ctl_reg.s.mode) 120210284Sjmallett { 121210284Sjmallett /* 1000BASE-X */ 122210284Sjmallett pcsx_linkx_timer_count_reg.s.count = (10000ull * clock_mhz) >> 10; 123210284Sjmallett } 124210284Sjmallett else 125210284Sjmallett { 126210284Sjmallett /* SGMII */ 127210284Sjmallett pcsx_linkx_timer_count_reg.s.count = (1600ull * clock_mhz) >> 10; 128210284Sjmallett } 129210284Sjmallett cvmx_write_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface), pcsx_linkx_timer_count_reg.u64); 130210284Sjmallett 131210284Sjmallett /* Write the advertisement register to be used as the 132210284Sjmallett tx_Config_Reg<D15:D0> of the autonegotiation. 133210284Sjmallett In 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG. 134210284Sjmallett In SGMII PHY mode, tx_Config_Reg<D15:D0> is PCS*_SGM*_AN_ADV_REG. 135210284Sjmallett In SGMII MAC mode, tx_Config_Reg<D15:D0> is the fixed value 0x4001, so 136210284Sjmallett this step can be skipped. */ 137215990Sjmallett if (pcsx_miscx_ctl_reg.s.mode) 138210284Sjmallett { 139210284Sjmallett /* 1000BASE-X */ 140210284Sjmallett cvmx_pcsx_anx_adv_reg_t pcsx_anx_adv_reg; 141210284Sjmallett pcsx_anx_adv_reg.u64 = cvmx_read_csr(CVMX_PCSX_ANX_ADV_REG(index, interface)); 142210284Sjmallett pcsx_anx_adv_reg.s.rem_flt = 0; 143210284Sjmallett pcsx_anx_adv_reg.s.pause = 3; 144210284Sjmallett pcsx_anx_adv_reg.s.hfd = 1; 145210284Sjmallett pcsx_anx_adv_reg.s.fd = 1; 146210284Sjmallett cvmx_write_csr(CVMX_PCSX_ANX_ADV_REG(index, interface), pcsx_anx_adv_reg.u64); 147210284Sjmallett } 148210284Sjmallett else 149210284Sjmallett { 150232812Sjmallett#ifdef CVMX_HELPER_CONFIG_NO_PHY 151232812Sjmallett /* If the interface does not have PHY, then set explicitly in PHY mode 152232812Sjmallett so that link will be set during auto negotiation. */ 153232812Sjmallett if (!pcsx_miscx_ctl_reg.s.mac_phy) 154232812Sjmallett { 155232812Sjmallett cvmx_dprintf("SGMII%d%d: Forcing PHY mode as PHY address is not set\n", interface, index); 156232812Sjmallett pcsx_miscx_ctl_reg.s.mac_phy = 1; 157232812Sjmallett cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), pcsx_miscx_ctl_reg.u64); 158232812Sjmallett } 159232812Sjmallett#endif 160210284Sjmallett if (pcsx_miscx_ctl_reg.s.mac_phy) 161210284Sjmallett { 162210284Sjmallett /* PHY Mode */ 163210284Sjmallett cvmx_pcsx_sgmx_an_adv_reg_t pcsx_sgmx_an_adv_reg; 164210284Sjmallett pcsx_sgmx_an_adv_reg.u64 = cvmx_read_csr(CVMX_PCSX_SGMX_AN_ADV_REG(index, interface)); 165210284Sjmallett pcsx_sgmx_an_adv_reg.s.dup = 1; 166210284Sjmallett pcsx_sgmx_an_adv_reg.s.speed= 2; 167210284Sjmallett cvmx_write_csr(CVMX_PCSX_SGMX_AN_ADV_REG(index, interface), pcsx_sgmx_an_adv_reg.u64); 168210284Sjmallett } 169210284Sjmallett else 170210284Sjmallett { 171210284Sjmallett /* MAC Mode - Nothing to do */ 172210284Sjmallett } 173210284Sjmallett } 174210284Sjmallett return 0; 175210284Sjmallett} 176210284Sjmallett 177232812Sjmallettstatic int __cvmx_helper_need_g15618(void) 178232812Sjmallett{ 179232812Sjmallett if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM 180232812Sjmallett || OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X) 181232812Sjmallett || OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_0) 182232812Sjmallett || OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_1) 183232812Sjmallett || OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_X) 184232812Sjmallett || OCTEON_IS_MODEL(OCTEON_CN68XX_PASS1_X)) 185232812Sjmallett return 1; 186232812Sjmallett else 187232812Sjmallett return 0; 188232812Sjmallett } 189210284Sjmallett 190210284Sjmallett/** 191210284Sjmallett * @INTERNAL 192210284Sjmallett * Initialize the SERTES link for the first time or after a loss 193210284Sjmallett * of link. 194210284Sjmallett * 195210284Sjmallett * @param interface Interface to init 196210284Sjmallett * @param index Index of prot on the interface 197210284Sjmallett * 198210284Sjmallett * @return Zero on success, negative on failure 199210284Sjmallett */ 200210284Sjmallettstatic int __cvmx_helper_sgmii_hardware_init_link(int interface, int index) 201210284Sjmallett{ 202210284Sjmallett cvmx_pcsx_mrx_control_reg_t control_reg; 203210284Sjmallett 204210284Sjmallett /* Take PCS through a reset sequence. 205210284Sjmallett PCS*_MR*_CONTROL_REG[PWR_DN] should be cleared to zero. 206210284Sjmallett Write PCS*_MR*_CONTROL_REG[RESET]=1 (while not changing the value of 207210284Sjmallett the other PCS*_MR*_CONTROL_REG bits). 208210284Sjmallett Read PCS*_MR*_CONTROL_REG[RESET] until it changes value to zero. */ 209210284Sjmallett control_reg.u64 = cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); 210232812Sjmallett 211232812Sjmallett /* Errata G-15618 requires disabling PCS soft reset in CN63XX pass upto 2.1. */ 212232812Sjmallett if (!__cvmx_helper_need_g15618()) 213210284Sjmallett { 214210284Sjmallett control_reg.s.reset = 1; 215210284Sjmallett cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), control_reg.u64); 216210284Sjmallett if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_CONTROL_REG(index, interface), cvmx_pcsx_mrx_control_reg_t, reset, ==, 0, 10000)) 217210284Sjmallett { 218210284Sjmallett cvmx_dprintf("SGMII%d: Timeout waiting for port %d to finish reset\n", interface, index); 219210284Sjmallett return -1; 220210284Sjmallett } 221210284Sjmallett } 222210284Sjmallett 223210284Sjmallett /* Write PCS*_MR*_CONTROL_REG[RST_AN]=1 to ensure a fresh sgmii negotiation starts. */ 224210284Sjmallett control_reg.s.rst_an = 1; 225210284Sjmallett control_reg.s.an_en = 1; 226210284Sjmallett control_reg.s.pwr_dn = 0; 227210284Sjmallett cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), control_reg.u64); 228210284Sjmallett 229210284Sjmallett /* Wait for PCS*_MR*_STATUS_REG[AN_CPT] to be set, indicating that 230210284Sjmallett sgmii autonegotiation is complete. In MAC mode this isn't an ethernet 231210284Sjmallett link, but a link between Octeon and the PHY */ 232210284Sjmallett if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) && 233210284Sjmallett CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_STATUS_REG(index, interface), cvmx_pcsx_mrx_status_reg_t, an_cpt, ==, 1, 10000)) 234210284Sjmallett { 235210284Sjmallett //cvmx_dprintf("SGMII%d: Port %d link timeout\n", interface, index); 236210284Sjmallett return -1; 237210284Sjmallett } 238210284Sjmallett return 0; 239210284Sjmallett} 240210284Sjmallett 241210284Sjmallett 242210284Sjmallett/** 243210284Sjmallett * @INTERNAL 244210284Sjmallett * Configure an SGMII link to the specified speed after the SERTES 245210284Sjmallett * link is up. 246210284Sjmallett * 247210284Sjmallett * @param interface Interface to init 248210284Sjmallett * @param index Index of prot on the interface 249210284Sjmallett * @param link_info Link state to configure 250210284Sjmallett * 251210284Sjmallett * @return Zero on success, negative on failure 252210284Sjmallett */ 253210284Sjmallettstatic int __cvmx_helper_sgmii_hardware_init_link_speed(int interface, int index, cvmx_helper_link_info_t link_info) 254210284Sjmallett{ 255210284Sjmallett int is_enabled; 256210284Sjmallett cvmx_gmxx_prtx_cfg_t gmxx_prtx_cfg; 257210284Sjmallett cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg; 258210284Sjmallett 259210284Sjmallett /* Disable GMX before we make any changes. Remember the enable state */ 260210284Sjmallett gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 261210284Sjmallett is_enabled = gmxx_prtx_cfg.s.en; 262210284Sjmallett gmxx_prtx_cfg.s.en = 0; 263210284Sjmallett cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); 264210284Sjmallett 265210284Sjmallett /* Wait for GMX to be idle */ 266210284Sjmallett if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), cvmx_gmxx_prtx_cfg_t, rx_idle, ==, 1, 10000) || 267210284Sjmallett CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), cvmx_gmxx_prtx_cfg_t, tx_idle, ==, 1, 10000)) 268210284Sjmallett { 269210284Sjmallett cvmx_dprintf("SGMII%d: Timeout waiting for port %d to be idle\n", interface, index); 270210284Sjmallett return -1; 271210284Sjmallett } 272210284Sjmallett 273210284Sjmallett /* Read GMX CFG again to make sure the disable completed */ 274210284Sjmallett gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 275210284Sjmallett 276210284Sjmallett /* Get the misc control for PCS. We will need to set the duplication amount */ 277210284Sjmallett pcsx_miscx_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); 278210284Sjmallett 279210284Sjmallett /* Use GMXENO to force the link down if the status we get says it should be down */ 280210284Sjmallett pcsx_miscx_ctl_reg.s.gmxeno = !link_info.s.link_up; 281210284Sjmallett 282210284Sjmallett /* Only change the duplex setting if the link is up */ 283210284Sjmallett if (link_info.s.link_up) 284210284Sjmallett gmxx_prtx_cfg.s.duplex = link_info.s.full_duplex; 285210284Sjmallett 286210284Sjmallett /* Do speed based setting for GMX */ 287210284Sjmallett switch (link_info.s.speed) 288210284Sjmallett { 289210284Sjmallett case 10: 290210284Sjmallett gmxx_prtx_cfg.s.speed = 0; 291210284Sjmallett gmxx_prtx_cfg.s.speed_msb = 1; 292210284Sjmallett gmxx_prtx_cfg.s.slottime = 0; 293210284Sjmallett pcsx_miscx_ctl_reg.s.samp_pt = 25; /* Setting from GMX-603 */ 294210284Sjmallett cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64); 295210284Sjmallett cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); 296210284Sjmallett break; 297210284Sjmallett case 100: 298210284Sjmallett gmxx_prtx_cfg.s.speed = 0; 299210284Sjmallett gmxx_prtx_cfg.s.speed_msb = 0; 300210284Sjmallett gmxx_prtx_cfg.s.slottime = 0; 301210284Sjmallett pcsx_miscx_ctl_reg.s.samp_pt = 0x5; 302210284Sjmallett cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64); 303210284Sjmallett cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); 304210284Sjmallett break; 305210284Sjmallett case 1000: 306210284Sjmallett gmxx_prtx_cfg.s.speed = 1; 307210284Sjmallett gmxx_prtx_cfg.s.speed_msb = 0; 308210284Sjmallett gmxx_prtx_cfg.s.slottime = 1; 309210284Sjmallett pcsx_miscx_ctl_reg.s.samp_pt = 1; 310210284Sjmallett cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 512); 311215990Sjmallett if (gmxx_prtx_cfg.s.duplex) 312215990Sjmallett cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); // full duplex 313215990Sjmallett else 314215990Sjmallett cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 8192); // half duplex 315210284Sjmallett break; 316210284Sjmallett default: 317210284Sjmallett break; 318210284Sjmallett } 319210284Sjmallett 320210284Sjmallett /* Write the new misc control for PCS */ 321210284Sjmallett cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), pcsx_miscx_ctl_reg.u64); 322210284Sjmallett 323210284Sjmallett /* Write the new GMX settings with the port still disabled */ 324210284Sjmallett cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); 325210284Sjmallett 326210284Sjmallett /* Read GMX CFG again to make sure the config completed */ 327210284Sjmallett gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 328210284Sjmallett 329210284Sjmallett /* Restore the enabled / disabled state */ 330210284Sjmallett gmxx_prtx_cfg.s.en = is_enabled; 331210284Sjmallett cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); 332210284Sjmallett 333210284Sjmallett return 0; 334210284Sjmallett} 335210284Sjmallett 336210284Sjmallett 337210284Sjmallett/** 338210284Sjmallett * @INTERNAL 339210284Sjmallett * Bring up the SGMII interface to be ready for packet I/O but 340210284Sjmallett * leave I/O disabled using the GMX override. This function 341210284Sjmallett * follows the bringup documented in 10.6.3 of the manual. 342210284Sjmallett * 343210284Sjmallett * @param interface Interface to bringup 344210284Sjmallett * @param num_ports Number of ports on the interface 345210284Sjmallett * 346210284Sjmallett * @return Zero on success, negative on failure 347210284Sjmallett */ 348210284Sjmallettstatic int __cvmx_helper_sgmii_hardware_init(int interface, int num_ports) 349210284Sjmallett{ 350210284Sjmallett int index; 351232812Sjmallett int do_link_set = 1; 352210284Sjmallett 353215990Sjmallett /* CN63XX Pass 1.0 errata G-14395 requires the QLM De-emphasis be programmed */ 354215990Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_0)) 355215990Sjmallett { 356215990Sjmallett cvmx_ciu_qlm2_t ciu_qlm; 357215990Sjmallett ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM2); 358215990Sjmallett ciu_qlm.s.txbypass = 1; 359215990Sjmallett ciu_qlm.s.txdeemph = 0xf; 360215990Sjmallett ciu_qlm.s.txmargin = 0xd; 361215990Sjmallett cvmx_write_csr(CVMX_CIU_QLM2, ciu_qlm.u64); 362215990Sjmallett } 363215990Sjmallett 364232812Sjmallett /* CN63XX Pass 2.0 and 2.1 errata G-15273 requires the QLM De-emphasis be 365232812Sjmallett programmed when using a 156.25Mhz ref clock */ 366232812Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_0) || 367232812Sjmallett OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_1)) 368232812Sjmallett { 369232812Sjmallett /* Read the QLM speed pins */ 370232812Sjmallett cvmx_mio_rst_boot_t mio_rst_boot; 371232812Sjmallett mio_rst_boot.u64 = cvmx_read_csr(CVMX_MIO_RST_BOOT); 372232812Sjmallett 373232812Sjmallett if (mio_rst_boot.cn63xx.qlm2_spd == 4) 374232812Sjmallett { 375232812Sjmallett cvmx_ciu_qlm2_t ciu_qlm; 376232812Sjmallett ciu_qlm.u64 = cvmx_read_csr(CVMX_CIU_QLM2); 377232812Sjmallett ciu_qlm.s.txbypass = 1; 378232812Sjmallett ciu_qlm.s.txdeemph = 0x0; 379232812Sjmallett ciu_qlm.s.txmargin = 0xf; 380232812Sjmallett cvmx_write_csr(CVMX_CIU_QLM2, ciu_qlm.u64); 381232812Sjmallett } 382232812Sjmallett } 383232812Sjmallett 384210284Sjmallett __cvmx_helper_setup_gmx(interface, num_ports); 385210284Sjmallett 386210284Sjmallett for (index=0; index<num_ports; index++) 387210284Sjmallett { 388210284Sjmallett int ipd_port = cvmx_helper_get_ipd_port(interface, index); 389210284Sjmallett __cvmx_helper_sgmii_hardware_init_one_time(interface, index); 390232812Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 391232812Sjmallett /* Linux kernel driver will call ....link_set with the proper link 392232812Sjmallett state. In the simulator there is no link state polling and 393232812Sjmallett hence it is set from here. */ 394232812Sjmallett if (!(cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)) 395232812Sjmallett do_link_set = 0; 396232812Sjmallett#endif 397232812Sjmallett if (do_link_set) 398232812Sjmallett __cvmx_helper_sgmii_link_set(ipd_port, __cvmx_helper_sgmii_link_get(ipd_port)); 399210284Sjmallett } 400210284Sjmallett 401210284Sjmallett return 0; 402210284Sjmallett} 403210284Sjmallett 404232812Sjmallettint __cvmx_helper_sgmii_enumerate(int interface) 405232812Sjmallett{ 406232812Sjmallett if (OCTEON_IS_MODEL(OCTEON_CNF71XX)) 407232812Sjmallett return 2; 408210284Sjmallett 409232812Sjmallett return 4; 410232812Sjmallett} 411232812Sjmallett 412210284Sjmallett/** 413210284Sjmallett * @INTERNAL 414210284Sjmallett * Probe a SGMII interface and determine the number of ports 415210284Sjmallett * connected to it. The SGMII interface should still be down after 416210284Sjmallett * this call. 417210284Sjmallett * 418210284Sjmallett * @param interface Interface to probe 419210284Sjmallett * 420210284Sjmallett * @return Number of ports on the interface. Zero to disable. 421210284Sjmallett */ 422210284Sjmallettint __cvmx_helper_sgmii_probe(int interface) 423210284Sjmallett{ 424210284Sjmallett cvmx_gmxx_inf_mode_t mode; 425210284Sjmallett 426232812Sjmallett /* Check if QLM is configured correct for SGMII, verify the speed 427232812Sjmallett as well as mode */ 428232812Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) 429232812Sjmallett { 430232812Sjmallett int qlm = cvmx_qlm_interface(interface); 431232812Sjmallett 432232812Sjmallett if (cvmx_qlm_get_status(qlm) != 1) 433232812Sjmallett return 0; 434232812Sjmallett } 435232812Sjmallett 436210284Sjmallett /* Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the interface 437232812Sjmallett needs to be enabled before IPD otherwise per port backpressure 438232812Sjmallett may not work properly */ 439210284Sjmallett mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); 440210284Sjmallett mode.s.en = 1; 441210284Sjmallett cvmx_write_csr(CVMX_GMXX_INF_MODE(interface), mode.u64); 442232812Sjmallett 443232812Sjmallett return __cvmx_helper_sgmii_enumerate(interface); 444210284Sjmallett} 445210284Sjmallett 446210284Sjmallett 447210284Sjmallett/** 448210284Sjmallett * @INTERNAL 449210284Sjmallett * Bringup and enable a SGMII interface. After this call packet 450210284Sjmallett * I/O should be fully functional. This is called with IPD 451210284Sjmallett * enabled but PKO disabled. 452210284Sjmallett * 453210284Sjmallett * @param interface Interface to bring up 454210284Sjmallett * 455210284Sjmallett * @return Zero on success, negative on failure 456210284Sjmallett */ 457210284Sjmallettint __cvmx_helper_sgmii_enable(int interface) 458210284Sjmallett{ 459210284Sjmallett int num_ports = cvmx_helper_ports_on_interface(interface); 460210284Sjmallett int index; 461210284Sjmallett 462232812Sjmallett /* Setup PKND and BPID */ 463232812Sjmallett if (octeon_has_feature(OCTEON_FEATURE_PKND)) 464232812Sjmallett { 465232812Sjmallett for (index = 0; index < num_ports; index++) 466232812Sjmallett { 467232812Sjmallett cvmx_gmxx_bpid_msk_t bpid_msk; 468232812Sjmallett cvmx_gmxx_bpid_mapx_t bpid_map; 469232812Sjmallett cvmx_gmxx_prtx_cfg_t gmxx_prtx_cfg; 470232812Sjmallett 471232812Sjmallett /* Setup PKIND */ 472232812Sjmallett gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 473232812Sjmallett gmxx_prtx_cfg.s.pknd = cvmx_helper_get_pknd(interface, index); 474232812Sjmallett cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); 475232812Sjmallett 476232812Sjmallett /* Setup BPID */ 477232812Sjmallett bpid_map.u64 = cvmx_read_csr(CVMX_GMXX_BPID_MAPX(index, interface)); 478232812Sjmallett bpid_map.s.val = 1; 479232812Sjmallett bpid_map.s.bpid = cvmx_helper_get_bpid(interface, index); 480232812Sjmallett cvmx_write_csr(CVMX_GMXX_BPID_MAPX(index, interface), bpid_map.u64); 481232812Sjmallett 482232812Sjmallett bpid_msk.u64 = cvmx_read_csr(CVMX_GMXX_BPID_MSK(interface)); 483232812Sjmallett bpid_msk.s.msk_or |= (1<<index); 484232812Sjmallett bpid_msk.s.msk_and &= ~(1<<index); 485232812Sjmallett cvmx_write_csr(CVMX_GMXX_BPID_MSK(interface), bpid_msk.u64); 486232812Sjmallett } 487232812Sjmallett } 488232812Sjmallett 489210284Sjmallett __cvmx_helper_sgmii_hardware_init(interface, num_ports); 490210284Sjmallett 491232812Sjmallett /* CN68XX adds the padding and FCS in PKO, not GMX */ 492232812Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN68XX)) 493232812Sjmallett { 494232812Sjmallett cvmx_gmxx_txx_append_t gmxx_txx_append_cfg; 495232812Sjmallett 496232812Sjmallett for (index = 0; index < num_ports; index++) 497232812Sjmallett { 498232812Sjmallett gmxx_txx_append_cfg.u64 = cvmx_read_csr( 499232812Sjmallett CVMX_GMXX_TXX_APPEND(index, interface)); 500232812Sjmallett gmxx_txx_append_cfg.s.fcs = 0; 501232812Sjmallett gmxx_txx_append_cfg.s.pad = 0; 502232812Sjmallett cvmx_write_csr(CVMX_GMXX_TXX_APPEND(index, interface), 503232812Sjmallett gmxx_txx_append_cfg.u64); 504232812Sjmallett } 505232812Sjmallett } 506232812Sjmallett 507210284Sjmallett for (index=0; index<num_ports; index++) 508210284Sjmallett { 509232812Sjmallett cvmx_gmxx_txx_append_t append_cfg; 510232812Sjmallett cvmx_gmxx_txx_sgmii_ctl_t sgmii_ctl; 511210284Sjmallett cvmx_gmxx_prtx_cfg_t gmxx_prtx_cfg; 512232812Sjmallett 513232812Sjmallett /* Clear the align bit if preamble is set to attain maximum tx rate. */ 514232812Sjmallett append_cfg.u64 = cvmx_read_csr(CVMX_GMXX_TXX_APPEND(index, interface)); 515232812Sjmallett sgmii_ctl.u64 = cvmx_read_csr(CVMX_GMXX_TXX_SGMII_CTL(index, interface)); 516232812Sjmallett sgmii_ctl.s.align = append_cfg.s.preamble ? 0 : 1; 517232812Sjmallett cvmx_write_csr(CVMX_GMXX_TXX_SGMII_CTL(index, interface), sgmii_ctl.u64); 518232812Sjmallett 519210284Sjmallett gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 520210284Sjmallett gmxx_prtx_cfg.s.en = 1; 521210284Sjmallett cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); 522210284Sjmallett } 523210284Sjmallett return 0; 524210284Sjmallett} 525210284Sjmallett 526210284Sjmallett 527210284Sjmallett/** 528210284Sjmallett * @INTERNAL 529210284Sjmallett * Return the link state of an IPD/PKO port as returned by 530210284Sjmallett * auto negotiation. The result of this function may not match 531210284Sjmallett * Octeon's link config if auto negotiation has changed since 532210284Sjmallett * the last call to cvmx_helper_link_set(). 533210284Sjmallett * 534210284Sjmallett * @param ipd_port IPD/PKO port to query 535210284Sjmallett * 536210284Sjmallett * @return Link state 537210284Sjmallett */ 538210284Sjmallettcvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port) 539210284Sjmallett{ 540210284Sjmallett cvmx_helper_link_info_t result; 541215990Sjmallett cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg; 542210284Sjmallett int interface = cvmx_helper_get_interface_num(ipd_port); 543210284Sjmallett int index = cvmx_helper_get_interface_index_num(ipd_port); 544210284Sjmallett cvmx_pcsx_mrx_control_reg_t pcsx_mrx_control_reg; 545232812Sjmallett int speed = 1000; 546232812Sjmallett int qlm; 547210284Sjmallett 548210284Sjmallett if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) 549210284Sjmallett { 550210284Sjmallett /* The simulator gives you a simulated 1Gbps full duplex link */ 551210284Sjmallett result.s.link_up = 1; 552210284Sjmallett result.s.full_duplex = 1; 553232812Sjmallett result.s.speed = speed; 554210284Sjmallett return result; 555210284Sjmallett } 556210284Sjmallett 557232812Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN66XX)) 558232812Sjmallett { 559232812Sjmallett cvmx_gmxx_inf_mode_t inf_mode; 560232812Sjmallett inf_mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); 561232812Sjmallett if (inf_mode.s.rate & (1<<index)) 562232812Sjmallett speed = 2500; 563232812Sjmallett else 564232812Sjmallett speed = 1000; 565232812Sjmallett } 566232812Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) 567232812Sjmallett { 568232812Sjmallett qlm = cvmx_qlm_interface(interface); 569232812Sjmallett 570232812Sjmallett speed = cvmx_qlm_get_gbaud_mhz(qlm) * 8 / 10; 571232812Sjmallett } 572232812Sjmallett 573232812Sjmallett result.u64 = 0; 574232812Sjmallett 575210284Sjmallett pcsx_mrx_control_reg.u64 = cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); 576210284Sjmallett if (pcsx_mrx_control_reg.s.loopbck1) 577210284Sjmallett { 578210284Sjmallett /* Force 1Gbps full duplex link for internal loopback */ 579210284Sjmallett result.s.link_up = 1; 580210284Sjmallett result.s.full_duplex = 1; 581232812Sjmallett result.s.speed = speed; 582210284Sjmallett return result; 583210284Sjmallett } 584210284Sjmallett 585210284Sjmallett 586215990Sjmallett pcsx_miscx_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); 587215990Sjmallett if (pcsx_miscx_ctl_reg.s.mode) 588210284Sjmallett { 589210284Sjmallett /* 1000BASE-X */ 590210284Sjmallett // FIXME 591210284Sjmallett } 592210284Sjmallett else 593210284Sjmallett { 594210284Sjmallett if (pcsx_miscx_ctl_reg.s.mac_phy) 595210284Sjmallett { 596210284Sjmallett /* PHY Mode */ 597210284Sjmallett cvmx_pcsx_mrx_status_reg_t pcsx_mrx_status_reg; 598210284Sjmallett cvmx_pcsx_anx_results_reg_t pcsx_anx_results_reg; 599210284Sjmallett 600210284Sjmallett /* Don't bother continuing if the SERTES low level link is down */ 601210284Sjmallett pcsx_mrx_status_reg.u64 = cvmx_read_csr(CVMX_PCSX_MRX_STATUS_REG(index, interface)); 602210284Sjmallett if (pcsx_mrx_status_reg.s.lnk_st == 0) 603210284Sjmallett { 604210284Sjmallett if (__cvmx_helper_sgmii_hardware_init_link(interface, index) != 0) 605210284Sjmallett return result; 606210284Sjmallett } 607210284Sjmallett 608210284Sjmallett /* Read the autoneg results */ 609210284Sjmallett pcsx_anx_results_reg.u64 = cvmx_read_csr(CVMX_PCSX_ANX_RESULTS_REG(index, interface)); 610210284Sjmallett if (pcsx_anx_results_reg.s.an_cpt) 611210284Sjmallett { 612210284Sjmallett /* Auto negotiation is complete. Set status accordingly */ 613210284Sjmallett result.s.full_duplex = pcsx_anx_results_reg.s.dup; 614210284Sjmallett result.s.link_up = pcsx_anx_results_reg.s.link_ok; 615210284Sjmallett switch (pcsx_anx_results_reg.s.spd) 616210284Sjmallett { 617210284Sjmallett case 0: 618232812Sjmallett result.s.speed = speed / 100; 619210284Sjmallett break; 620210284Sjmallett case 1: 621232812Sjmallett result.s.speed = speed / 10; 622210284Sjmallett break; 623210284Sjmallett case 2: 624232812Sjmallett result.s.speed = speed; 625210284Sjmallett break; 626210284Sjmallett default: 627210284Sjmallett result.s.speed = 0; 628210284Sjmallett result.s.link_up = 0; 629210284Sjmallett break; 630210284Sjmallett } 631210284Sjmallett } 632210284Sjmallett else 633210284Sjmallett { 634210284Sjmallett /* Auto negotiation isn't complete. Return link down */ 635210284Sjmallett result.s.speed = 0; 636210284Sjmallett result.s.link_up = 0; 637210284Sjmallett } 638210284Sjmallett } 639210284Sjmallett else /* MAC Mode */ 640210284Sjmallett { 641210284Sjmallett result = __cvmx_helper_board_link_get(ipd_port); 642210284Sjmallett } 643210284Sjmallett } 644210284Sjmallett return result; 645210284Sjmallett} 646210284Sjmallett 647210284Sjmallett 648210284Sjmallett/** 649210284Sjmallett * @INTERNAL 650210284Sjmallett * Configure an IPD/PKO port for the specified link state. This 651210284Sjmallett * function does not influence auto negotiation at the PHY level. 652210284Sjmallett * The passed link state must always match the link state returned 653210284Sjmallett * by cvmx_helper_link_get(). It is normally best to use 654210284Sjmallett * cvmx_helper_link_autoconf() instead. 655210284Sjmallett * 656210284Sjmallett * @param ipd_port IPD/PKO port to configure 657210284Sjmallett * @param link_info The new link state 658210284Sjmallett * 659210284Sjmallett * @return Zero on success, negative on failure 660210284Sjmallett */ 661210284Sjmallettint __cvmx_helper_sgmii_link_set(int ipd_port, cvmx_helper_link_info_t link_info) 662210284Sjmallett{ 663210284Sjmallett int interface = cvmx_helper_get_interface_num(ipd_port); 664210284Sjmallett int index = cvmx_helper_get_interface_index_num(ipd_port); 665232812Sjmallett 666232812Sjmallett if (link_info.s.link_up || !__cvmx_helper_need_g15618()) { 667232812Sjmallett __cvmx_helper_sgmii_hardware_init_link(interface, index); 668232812Sjmallett } else { 669232812Sjmallett cvmx_pcsx_mrx_control_reg_t control_reg; 670232812Sjmallett cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg; 671232812Sjmallett 672232812Sjmallett control_reg.u64 = cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); 673232812Sjmallett control_reg.s.an_en = 0; 674232812Sjmallett cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), control_reg.u64); 675232812Sjmallett cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); 676232812Sjmallett /* Use GMXENO to force the link down it will get reenabled later... */ 677232812Sjmallett pcsx_miscx_ctl_reg.s.gmxeno = 1; 678232812Sjmallett cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), pcsx_miscx_ctl_reg.u64); 679232812Sjmallett cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); 680232812Sjmallett return 0; 681232812Sjmallett } 682210284Sjmallett return __cvmx_helper_sgmii_hardware_init_link_speed(interface, index, link_info); 683210284Sjmallett} 684210284Sjmallett 685210284Sjmallett/** 686210284Sjmallett * @INTERNAL 687210284Sjmallett * Configure a port for internal and/or external loopback. Internal loopback 688210284Sjmallett * causes packets sent by the port to be received by Octeon. External loopback 689210284Sjmallett * causes packets received from the wire to sent out again. 690210284Sjmallett * 691210284Sjmallett * @param ipd_port IPD/PKO port to loopback. 692210284Sjmallett * @param enable_internal 693210284Sjmallett * Non zero if you want internal loopback 694210284Sjmallett * @param enable_external 695210284Sjmallett * Non zero if you want external loopback 696210284Sjmallett * 697210284Sjmallett * @return Zero on success, negative on failure. 698210284Sjmallett */ 699210284Sjmallettint __cvmx_helper_sgmii_configure_loopback(int ipd_port, int enable_internal, int enable_external) 700210284Sjmallett{ 701210284Sjmallett int interface = cvmx_helper_get_interface_num(ipd_port); 702210284Sjmallett int index = cvmx_helper_get_interface_index_num(ipd_port); 703210284Sjmallett cvmx_pcsx_mrx_control_reg_t pcsx_mrx_control_reg; 704210284Sjmallett cvmx_pcsx_miscx_ctl_reg_t pcsx_miscx_ctl_reg; 705210284Sjmallett 706210284Sjmallett pcsx_mrx_control_reg.u64 = cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); 707210284Sjmallett pcsx_mrx_control_reg.s.loopbck1 = enable_internal; 708210284Sjmallett cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), pcsx_mrx_control_reg.u64); 709210284Sjmallett 710210284Sjmallett pcsx_miscx_ctl_reg.u64 = cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); 711210284Sjmallett pcsx_miscx_ctl_reg.s.loopbck2 = enable_external; 712210284Sjmallett cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), pcsx_miscx_ctl_reg.u64); 713210284Sjmallett 714210284Sjmallett __cvmx_helper_sgmii_hardware_init_link(interface, index); 715210284Sjmallett return 0; 716210284Sjmallett} 717210284Sjmallett 718210284Sjmallett#endif /* CVMX_ENABLE_PKO_FUNCTIONS */ 719