blob: ab325f34432b9b0d19254399be2deffd257a2b7d [file] [log] [blame]
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/nxge/nxge_impl.h>
#include <sys/nxge/nxge_mac.h>
#include <sys/nxge/nxge_hio.h>
#define LINK_MONITOR_PERIOD (1000 * 1000)
#define LM_WAIT_MULTIPLIER 8
#define SERDES_RDY_WT_INTERVAL 50
#define MAX_SERDES_RDY_RETRIES 10
#define TN1010_SPEED_1G 1
#define TN1010_SPEED_10G 0
#define TN1010_AN_IN_PROG 0 /* Auto negotiation in progress */
#define TN1010_AN_COMPLETE 1
#define TN1010_AN_RSVD 2
#define TN1010_AN_FAILED 3
extern uint32_t nxge_no_link_notify;
extern boolean_t nxge_no_msg;
extern uint32_t nxge_lb_dbg;
extern uint32_t nxge_jumbo_mtu;
typedef enum {
CHECK_LINK_RESCHEDULE,
CHECK_LINK_STOP
} check_link_state_t;
static check_link_state_t nxge_check_link_stop(nxge_t *);
/*
* Ethernet broadcast address definition.
*/
static ether_addr_st etherbroadcastaddr =
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
/*
* Ethernet zero address definition.
*/
static ether_addr_st etherzeroaddr =
{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
/*
* Supported chip types
*/
static uint32_t nxge_supported_cl45_ids[] = {
BCM8704_DEV_ID,
MARVELL_88X_201X_DEV_ID,
BCM8706_DEV_ID,
TN1010_DEV_ID
};
static uint32_t nxge_supported_cl22_ids[] = {
BCM5464R_PHY_ID,
BCM5482_PHY_ID
};
#define NUM_CLAUSE_45_IDS (sizeof (nxge_supported_cl45_ids) / \
sizeof (uint32_t))
#define NUM_CLAUSE_22_IDS (sizeof (nxge_supported_cl22_ids) / \
sizeof (uint32_t))
/*
* static functions
*/
static uint32_t nxge_get_cl45_pma_pmd_id(p_nxge_t, int);
static uint32_t nxge_get_cl45_pcs_id(p_nxge_t, int);
static uint32_t nxge_get_cl22_phy_id(p_nxge_t, int);
static boolean_t nxge_is_supported_phy(uint32_t, uint8_t);
static boolean_t nxge_hswap_phy_present(p_nxge_t, uint8_t);
static boolean_t nxge_is_phy_present(p_nxge_t, int, uint32_t, uint32_t);
static nxge_status_t nxge_n2_serdes_init(p_nxge_t);
static nxge_status_t nxge_n2_kt_serdes_init(p_nxge_t);
static nxge_status_t nxge_neptune_10G_serdes_init(p_nxge_t);
static nxge_status_t nxge_1G_serdes_init(p_nxge_t);
static nxge_status_t nxge_10G_link_intr_stop(p_nxge_t);
static nxge_status_t nxge_10G_link_intr_start(p_nxge_t);
static nxge_status_t nxge_1G_copper_link_intr_stop(p_nxge_t);
static nxge_status_t nxge_1G_copper_link_intr_start(p_nxge_t);
static nxge_status_t nxge_1G_fiber_link_intr_stop(p_nxge_t);
static nxge_status_t nxge_1G_fiber_link_intr_start(p_nxge_t);
static nxge_status_t nxge_check_mii_link(p_nxge_t);
static nxge_status_t nxge_check_10g_link(p_nxge_t);
static nxge_status_t nxge_10G_xcvr_init(p_nxge_t);
static nxge_status_t nxge_BCM8704_xcvr_init(p_nxge_t);
static nxge_status_t nxge_BCM8706_xcvr_init(p_nxge_t);
static nxge_status_t nxge_1G_xcvr_init(p_nxge_t);
static void nxge_bcm5464_link_led_off(p_nxge_t);
static nxge_status_t nxge_check_mrvl88x2011_link(p_nxge_t, boolean_t *);
static nxge_status_t nxge_mrvl88x2011_xcvr_init(p_nxge_t);
static nxge_status_t nxge_check_nlp2020_link(p_nxge_t, boolean_t *);
static nxge_status_t nxge_nlp2020_xcvr_init(p_nxge_t);
static int nxge_nlp2020_i2c_read(p_nxge_t, uint8_t, uint16_t, uint16_t,
uint8_t *);
static boolean_t nxge_is_nlp2020_phy(p_nxge_t);
static uint8_t nxge_get_nlp2020_connector_type(p_nxge_t);
static nxge_status_t nxge_set_nlp2020_param(p_nxge_t);
static nxge_status_t nxge_get_num_of_xaui(uint32_t *port_pma_pmd_dev_id,
uint32_t *port_pcs_dev_id, uint32_t *port_phy_id, uint8_t *num_xaui);
static nxge_status_t nxge_get_tn1010_speed(p_nxge_t nxgep, uint16_t *speed);
static nxge_status_t nxge_set_tn1010_param(p_nxge_t nxgep);
static nxge_status_t nxge_tn1010_check(p_nxge_t nxgep,
nxge_link_state_t *link_up);
static boolean_t nxge_is_tn1010_phy(p_nxge_t nxgep);
static nxge_status_t nxge_tn1010_xcvr_init(p_nxge_t nxgep);
nxge_status_t nxge_mac_init(p_nxge_t);
static nxge_status_t nxge_mii_get_link_mode(p_nxge_t);
#ifdef NXGE_DEBUG
static void nxge_mii_dump(p_nxge_t);
static nxge_status_t nxge_tn1010_reset(p_nxge_t nxgep);
static void nxge_dump_tn1010_status_regs(p_nxge_t nxgep);
#endif
/*
* xcvr tables for supported transceivers
*/
/*
* nxge_n2_10G_table is for 10G fiber or serdes on N2-NIU systems.
* The Teranetics TN1010 based copper XAUI card can also be used
* on N2-NIU systems in 10G mode, but it uses its own table
* nxge_n2_10G_tn1010_table below.
*/
static nxge_xcvr_table_t nxge_n2_10G_table = {
nxge_n2_serdes_init,
nxge_10G_xcvr_init,
nxge_10G_link_intr_stop,
nxge_10G_link_intr_start,
nxge_check_10g_link,
PCS_XCVR
};
/*
* For the Teranetics TN1010 based copper XAUI card
*/
static nxge_xcvr_table_t nxge_n2_10G_tn1010_table = {
nxge_n2_serdes_init, /* Handle both 1G and 10G */
nxge_tn1010_xcvr_init, /* Handle both 1G and 10G */
nxge_10G_link_intr_stop,
nxge_10G_link_intr_start,
nxge_check_tn1010_link, /* Will figure out speed */
XPCS_XCVR
};
static nxge_xcvr_table_t nxge_n2_1G_table = {
nxge_n2_serdes_init,
nxge_1G_xcvr_init,
nxge_1G_fiber_link_intr_stop,
nxge_1G_fiber_link_intr_start,
nxge_check_mii_link,
PCS_XCVR
};
static nxge_xcvr_table_t nxge_n2_1G_tn1010_table = {
nxge_n2_serdes_init,
nxge_tn1010_xcvr_init,
nxge_1G_fiber_link_intr_stop, /* TN1010 is a Cu PHY, but it uses */
nxge_1G_fiber_link_intr_start, /* PCS for 1G, so call fiber func */
nxge_check_tn1010_link,
PCS_XCVR
};
static nxge_xcvr_table_t nxge_10G_tn1010_table = {
nxge_neptune_10G_serdes_init,
nxge_tn1010_xcvr_init,
nxge_10G_link_intr_stop,
nxge_10G_link_intr_start,
nxge_check_tn1010_link,
XPCS_XCVR
};
static nxge_xcvr_table_t nxge_1G_tn1010_table = {
nxge_1G_serdes_init,
nxge_tn1010_xcvr_init,
nxge_1G_fiber_link_intr_stop,
nxge_1G_fiber_link_intr_start,
nxge_check_tn1010_link,
PCS_XCVR
};
static nxge_xcvr_table_t nxge_10G_fiber_table = {
nxge_neptune_10G_serdes_init,
nxge_10G_xcvr_init,
nxge_10G_link_intr_stop,
nxge_10G_link_intr_start,
nxge_check_10g_link,
PCS_XCVR
};
static nxge_xcvr_table_t nxge_1G_copper_table = {
NULL,
nxge_1G_xcvr_init,
nxge_1G_copper_link_intr_stop,
nxge_1G_copper_link_intr_start,
nxge_check_mii_link,
INT_MII_XCVR
};
/* This table is for Neptune portmode == PORT_1G_SERDES cases */
static nxge_xcvr_table_t nxge_1G_fiber_table = {
nxge_1G_serdes_init,
nxge_1G_xcvr_init,
nxge_1G_fiber_link_intr_stop,
nxge_1G_fiber_link_intr_start,
nxge_check_mii_link,
PCS_XCVR
};
static nxge_xcvr_table_t nxge_10G_copper_table = {
nxge_neptune_10G_serdes_init,
NULL,
NULL,
NULL,
NULL,
PCS_XCVR
};
/*
* NXGE_PORT_TN1010 is defined as,
* NXGE_PORT_SPD_NONE | (NXGE_PHY_TN1010 << NXGE_PHY_SHIFT)
* = 0 | 5 << 16 = 0x50000
*
* So NEPTUNE_2_TN1010 =
* (NXGE_PORT_TN1010 |
* (NXGE_PORT_TN1010 << 4) |
* (NXGE_PORT_NONE << 8) |
* (NXGE_PORT_NONE << 12)),
* = 0x50000 | (0x50000 << 4)
* = 0x550000
*
* This function partitions nxgep->nxge_hw_p->niu_type (which may have
* value NEPTUNE_2_TN1010) and checks if a port has type = NXGE_PORT_TN1010
* = 0x50000
*/
static boolean_t nxge_is_tn1010_phy(p_nxge_t nxgep)
{
uint8_t portn = NXGE_GET_PORT_NUM(nxgep->function_num);
if (((nxgep->nxge_hw_p->niu_type >> (NXGE_PORT_TYPE_SHIFT * portn))
& NXGE_PHY_MASK) == NXGE_PORT_TN1010) {
return (B_TRUE);
} else {
return (B_FALSE);
}
}
/*
* Figure out nxgep->mac.portmode from nxge.conf, OBP's device properties,
* serial EEPROM or VPD if possible. Note that not all systems could get
* the portmode information by calling this function. For example, the
* Maramba system figures out the portmode information by calling function
* nxge_setup_xcvr_table.
*/
nxge_status_t
nxge_get_xcvr_type(p_nxge_t nxgep)
{
nxge_status_t status = NXGE_OK;
char *phy_type;
char *prop_val;
uint8_t portn = NXGE_GET_PORT_NUM(nxgep->function_num);
uint32_t val;
npi_status_t rs;
/* For Opus NEM, skip xcvr checking if 10G Serdes link is up */
if (nxgep->mac.portmode == PORT_10G_SERDES &&
nxgep->statsp->mac_stats.link_up) {
nxgep->statsp->mac_stats.xcvr_inuse = XPCS_XCVR;
return (status);
}
nxgep->mac.portmode = 0;
nxgep->xcvr_addr = 0;
/*
* First check for hot swappable phy property.
*/
if (nxgep->hot_swappable_phy == B_TRUE) {
nxgep->statsp->mac_stats.xcvr_inuse = HSP_XCVR;
nxgep->mac.portmode = PORT_HSP_MODE;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "Other: Hot Swappable"));
} else if (ddi_prop_exists(DDI_DEV_T_ANY, nxgep->dip,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
"hot-swappable-phy") == 1) {
nxgep->statsp->mac_stats.xcvr_inuse = HSP_XCVR;
nxgep->mac.portmode = PORT_HSP_MODE;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, ".conf: Hot Swappable"));
} else if (nxgep->niu_type == N2_NIU &&
ddi_prop_exists(DDI_DEV_T_ANY, nxgep->dip, 0,
"hot-swappable-phy") == 1) {
nxgep->statsp->mac_stats.xcvr_inuse = HSP_XCVR;
nxgep->mac.portmode = PORT_HSP_MODE;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "OBP: Hot Swappable"));
}
/*
* MDIO polling support for Monza RTM card, Goa NEM card
*/
if (nxgep->mac.portmode == PORT_HSP_MODE) {
nxgep->hot_swappable_phy = B_TRUE;
if (portn > 1) {
return (NXGE_ERROR);
}
if (nxge_hswap_phy_present(nxgep, portn))
goto found_phy;
nxgep->phy_absent = B_TRUE;
/* Check Serdes link to detect Opus NEM */
rs = npi_xmac_xpcs_read(nxgep->npi_handle, nxgep->mac.portnum,
XPCS_REG_STATUS, &val);
if (rs == 0 && val & XPCS_STATUS_LANE_ALIGN) {
nxgep->statsp->mac_stats.xcvr_inuse = XPCS_XCVR;
nxgep->mac.portmode = PORT_10G_SERDES;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"HSP 10G Serdes FOUND!!"));
}
goto check_phy_done;
found_phy:
nxgep->statsp->mac_stats.xcvr_inuse = XPCS_XCVR;
nxgep->mac.portmode = PORT_10G_FIBER;
nxgep->phy_absent = B_FALSE;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "10G Fiber Xcvr "
"found for hot swappable phy"));
check_phy_done:
return (status);
}
/* Get phy-type property (May have been set by nxge.conf) */
if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, nxgep->dip,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
"phy-type", &prop_val)) == DDI_PROP_SUCCESS) {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"found conf file: phy-type %s", prop_val));
if (strcmp("xgsd", prop_val) == 0) {
nxgep->statsp->mac_stats.xcvr_inuse = XPCS_XCVR;
nxgep->mac.portmode = PORT_10G_SERDES;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"found: 10G Serdes"));
} else if (strcmp("gsd", prop_val) == 0) {
nxgep->statsp->mac_stats.xcvr_inuse = PCS_XCVR;
nxgep->mac.portmode = PORT_1G_SERDES;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "1G Serdes"));
} else if (strcmp("mif", prop_val) == 0) {
nxgep->statsp->mac_stats.xcvr_inuse = INT_MII_XCVR;
nxgep->mac.portmode = PORT_1G_COPPER;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "1G Copper Xcvr"));
} else if (strcmp("pcs", prop_val) == 0) {
nxgep->statsp->mac_stats.xcvr_inuse = PCS_XCVR;
nxgep->mac.portmode = PORT_1G_FIBER;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "1G FIBER Xcvr"));
} else if (strcmp("xgf", prop_val) == 0) {
/*
* Before OBP supports new phy-type property
* value "xgc", the 10G copper XAUI may carry
* "xgf" instead of "xgc". If the OBP is
* upgraded to a newer version which supports
* "xgc", then the TN1010 related code in this
* "xgf" case will not be used anymore.
*/
if (nxge_is_tn1010_phy(nxgep)) {
if ((status = nxge_set_tn1010_param(nxgep))
!= NXGE_OK) {
return (status);
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "TN1010 Xcvr"));
} else { /* For Fiber XAUI */
nxgep->statsp->mac_stats.xcvr_inuse = XPCS_XCVR;
nxgep->mac.portmode = PORT_10G_FIBER;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"10G Fiber Xcvr"));
}
} else if (strcmp("xgc", prop_val) == 0) {
if ((status = nxge_set_tn1010_param(nxgep)) != NXGE_OK)
return (status);
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "TN1010 Xcvr"));
}
(void) ddi_prop_update_string(DDI_DEV_T_NONE, nxgep->dip,
"phy-type", prop_val);
ddi_prop_free(prop_val);
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "nxge_get_xcvr_type: "
"Got phy type [0x%x] from conf file",
nxgep->mac.portmode));
return (NXGE_OK);
}
/* Get phy-type property from OBP */
if (nxgep->niu_type == N2_NIU) {
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, nxgep->dip, 0,
"phy-type", &prop_val) == DDI_PROP_SUCCESS) {
if (strcmp("xgf", prop_val) == 0) {
/*
* Before OBP supports new phy-type property
* value "xgc", the 10G copper XAUI may carry
* "xgf" instead of "xgc". If the OBP is
* upgraded to a newer version which supports
* "xgc", then the TN1010 related code in this
* "xgf" case will not be used anymore.
*/
if (nxge_is_tn1010_phy(nxgep)) {
if ((status =
nxge_set_tn1010_param(nxgep))
!= NXGE_OK) {
return (status);
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"TN1010 Xcvr"));
} else if (nxge_is_nlp2020_phy(nxgep)) {
if ((status =
nxge_set_nlp2020_param(nxgep))
!= NXGE_OK) {
return (status);
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"NLP2020 Xcvr"));
} else { /* For Fiber XAUI */
nxgep->statsp->mac_stats.xcvr_inuse
= XPCS_XCVR;
nxgep->mac.portmode = PORT_10G_FIBER;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"10G Fiber Xcvr"));
}
} else if (strcmp("mif", prop_val) == 0) {
nxgep->statsp->mac_stats.xcvr_inuse =
INT_MII_XCVR;
nxgep->mac.portmode = PORT_1G_COPPER;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"1G Copper Xcvr"));
} else if (strcmp("pcs", prop_val) == 0) {
nxgep->statsp->mac_stats.xcvr_inuse = PCS_XCVR;
nxgep->mac.portmode = PORT_1G_FIBER;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"1G Fiber Xcvr"));
} else if (strcmp("xgc", prop_val) == 0) {
status = nxge_set_tn1010_param(nxgep);
if (status != NXGE_OK)
return (status);
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "TN1010 Xcvr"));
} else if (strcmp("xgsd", prop_val) == 0) {
nxgep->statsp->mac_stats.xcvr_inuse = XPCS_XCVR;
nxgep->mac.portmode = PORT_10G_SERDES;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"OBP: 10G Serdes"));
} else if (strcmp("gsd", prop_val) == 0) {
nxgep->statsp->mac_stats.xcvr_inuse = PCS_XCVR;
nxgep->mac.portmode = PORT_1G_SERDES;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"OBP: 1G Serdes"));
} else {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"Unknown phy-type: %s", prop_val));
ddi_prop_free(prop_val);
return (NXGE_ERROR);
}
status = NXGE_OK;
(void) ddi_prop_update_string(DDI_DEV_T_NONE,
nxgep->dip, "phy-type", prop_val);
ddi_prop_free(prop_val);
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "nxge_get_xcvr_type: "
"Got phy type [0x%x] from OBP",
nxgep->mac.portmode));
return (status);
} else {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"Exiting...phy-type property not found"));
return (NXGE_ERROR);
}
}
if (!nxgep->vpd_info.present) {
return (NXGE_OK);
}
if (!nxgep->vpd_info.ver_valid) {
goto read_seeprom;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"Reading phy type from expansion ROM"));
/*
* Try to read the phy type from the vpd data read off the
* expansion ROM.
*/
phy_type = nxgep->vpd_info.phy_type;
if (strncmp(phy_type, "mif", 3) == 0) {
nxgep->mac.portmode = PORT_1G_COPPER;
nxgep->statsp->mac_stats.xcvr_inuse = INT_MII_XCVR;
} else if (strncmp(phy_type, "xgf", 3) == 0) {
nxgep->mac.portmode = PORT_10G_FIBER;
nxgep->statsp->mac_stats.xcvr_inuse = XPCS_XCVR;
} else if (strncmp(phy_type, "pcs", 3) == 0) {
nxgep->mac.portmode = PORT_1G_FIBER;
nxgep->statsp->mac_stats.xcvr_inuse = PCS_XCVR;
} else if (strncmp(phy_type, "xgc", 3) == 0) {
status = nxge_set_tn1010_param(nxgep);
if (status != NXGE_OK) {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_get_xcvr_type: Failed to set TN1010 param"));
goto read_seeprom;
}
} else if (strncmp(phy_type, "xgsd", 4) == 0) {
nxgep->mac.portmode = PORT_10G_SERDES;
nxgep->statsp->mac_stats.xcvr_inuse = XPCS_XCVR;
} else if (strncmp(phy_type, "gsd", 3) == 0) {
nxgep->mac.portmode = PORT_1G_SERDES;
nxgep->statsp->mac_stats.xcvr_inuse = PCS_XCVR;
} else {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_get_xcvr_type: Unknown phy type [%c%c%c] in EEPROM",
phy_type[0], phy_type[1], phy_type[2]));
goto read_seeprom;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "nxge_get_xcvr_type: "
"Got phy type [0x%x] from VPD", nxgep->mac.portmode));
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "<== nxge_get_xcvr_type"));
return (status);
read_seeprom:
/*
* read the phy type from the SEEPROM - NCR registers
*/
status = nxge_espc_phy_type_get(nxgep);
if (status != NXGE_OK) {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"Failed to get phy type"));
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL, "EEPROM version "
"[%s] invalid...please update", nxgep->vpd_info.ver));
}
return (status);
}
/* Set up the PHY specific values. */
nxge_status_t
nxge_setup_xcvr_table(p_nxge_t nxgep)
{
nxge_status_t status = NXGE_OK;
uint32_t port_type;
uint8_t portn = NXGE_GET_PORT_NUM(nxgep->function_num);
uint32_t pcs_id = 0;
uint32_t pma_pmd_id = 0;
uint32_t phy_id = 0;
uint16_t chip_id = 0;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "==> nxge_setup_xcvr_table: port<%d>",
portn));
switch (nxgep->niu_type) {
case N2_NIU:
switch (nxgep->mac.portmode) {
case PORT_1G_FIBER:
case PORT_1G_SERDES:
nxgep->xcvr = nxge_n2_1G_table;
nxgep->xcvr_addr = portn;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "NIU 1G %s Xcvr",
(nxgep->mac.portmode == PORT_1G_FIBER) ? "Fiber" :
"Serdes"));
break;
case PORT_10G_FIBER:
case PORT_10G_COPPER:
case PORT_10G_SERDES:
nxgep->xcvr = nxge_n2_10G_table;
if (nxgep->nxge_hw_p->xcvr_addr[portn]) {
nxgep->xcvr_addr =
nxgep->nxge_hw_p->xcvr_addr[portn];
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "NIU 10G %s Xcvr",
(nxgep->mac.portmode == PORT_10G_FIBER) ? "Fiber" :
((nxgep->mac.portmode == PORT_10G_COPPER) ?
"Copper" : "Serdes")));
break;
case PORT_1G_TN1010:
nxgep->xcvr = nxge_n2_1G_tn1010_table;
nxgep->xcvr_addr = nxgep->nxge_hw_p->xcvr_addr[portn];
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"TN1010 Copper Xcvr in 1G"));
break;
case PORT_10G_TN1010:
nxgep->xcvr = nxge_n2_10G_tn1010_table;
nxgep->xcvr_addr = nxgep->nxge_hw_p->xcvr_addr[portn];
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"TN1010 Copper Xcvr in 10G"));
break;
case PORT_HSP_MODE:
nxgep->xcvr = nxge_n2_10G_table;
nxgep->xcvr.xcvr_inuse = HSP_XCVR;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "NIU 10G Hot "
"Swappable Xcvr (not present)"));
break;
default:
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"<== nxge_setup_xcvr_table: "
"Unable to determine NIU portmode"));
return (NXGE_ERROR);
}
break;
default:
if (nxgep->mac.portmode == 0) {
/*
* Would be the case for platforms like Maramba
* in which the phy type could not be got from conf
* file, OBP, VPD or Serial PROM.
*/
if (!NXGE_IS_VALID_NEPTUNE_TYPE(nxgep)) {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"<== nxge_setup_xcvr_table:"
" Invalid Neptune type [0x%x]",
nxgep->niu_type));
return (NXGE_ERROR);
}
port_type = nxgep->niu_type >>
(NXGE_PORT_TYPE_SHIFT * portn);
port_type = port_type & (NXGE_PORT_TYPE_MASK);
switch (port_type) {
case NXGE_PORT_1G_COPPER:
nxgep->mac.portmode = PORT_1G_COPPER;
break;
case NXGE_PORT_10G_COPPER:
nxgep->mac.portmode = PORT_10G_COPPER;
break;
case NXGE_PORT_1G_FIBRE:
nxgep->mac.portmode = PORT_1G_FIBER;
break;
case NXGE_PORT_10G_FIBRE:
nxgep->mac.portmode = PORT_10G_FIBER;
break;
case NXGE_PORT_1G_SERDES:
nxgep->mac.portmode = PORT_1G_SERDES;
break;
case NXGE_PORT_10G_SERDES:
nxgep->mac.portmode = PORT_10G_SERDES;
break;
/* Ports 2 and 3 of Alonso or ARTM */
case NXGE_PORT_1G_RGMII_FIBER:
nxgep->mac.portmode = PORT_1G_RGMII_FIBER;
break;
case NXGE_PORT_TN1010:
/*
* If this port uses the TN1010 copper
* PHY, then its speed is not known yet
* because nxge_scan_ports_phy could only
* figure out the vendor of the PHY but
* not its speed. nxge_set_tn1010_param
* will read the PHY speed and set
* portmode accordingly.
*/
if ((status = nxge_set_tn1010_param(nxgep))
!= NXGE_OK) {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_set_tn1010_param failed"));
return (status);
}
break;
default:
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"<== nxge_setup_xcvr_table: "
"Unknown port-type: 0x%x", port_type));
return (NXGE_ERROR);
}
}
/*
* Above switch has figured out nxge->mac.portmode, now set
* nxgep->xcvr (the table) and nxgep->xcvr_addr according
* to portmode.
*/
switch (nxgep->mac.portmode) {
case PORT_1G_COPPER:
case PORT_1G_RGMII_FIBER:
nxgep->xcvr = nxge_1G_copper_table;
nxgep->xcvr_addr = nxgep->nxge_hw_p->xcvr_addr[portn];
/*
* For Altas 4-1G copper, Xcvr port numbers are
* swapped with ethernet port number. This is
* designed for better signal integrity in
* routing. This is also the case for the
* on-board Neptune copper ports on the Maramba
* platform.
*/
switch (nxgep->platform_type) {
case P_NEPTUNE_ATLAS_4PORT:
case P_NEPTUNE_MARAMBA_P0:
case P_NEPTUNE_MARAMBA_P1:
switch (portn) {
case 0:
nxgep->xcvr_addr += 3;
break;
case 1:
nxgep->xcvr_addr += 1;
break;
case 2:
nxgep->xcvr_addr -= 1;
break;
case 3:
nxgep->xcvr_addr -= 3;
break;
default:
return (NXGE_ERROR);
}
break;
default:
break;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "1G %s Xcvr",
(nxgep->mac.portmode == PORT_1G_COPPER) ?
"Copper" : "RGMII Fiber"));
break;
case PORT_10G_COPPER:
nxgep->xcvr = nxge_10G_copper_table;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "10G Copper Xcvr"));
break;
case PORT_1G_TN1010:
nxgep->xcvr = nxge_1G_tn1010_table;
nxgep->xcvr_addr = nxgep->nxge_hw_p->xcvr_addr[portn];
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"1G TN1010 copper Xcvr"));
break;
case PORT_10G_TN1010:
nxgep->xcvr = nxge_10G_tn1010_table;
nxgep->xcvr_addr = nxgep->nxge_hw_p->xcvr_addr[portn];
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"10G TN1010 copper Xcvr"));
break;
case PORT_1G_FIBER:
case PORT_1G_SERDES:
nxgep->xcvr = nxge_1G_fiber_table;
nxgep->xcvr_addr = portn;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "1G %s Xcvr",
(nxgep->mac.portmode == PORT_1G_FIBER) ?
"Fiber" : "Serdes"));
break;
case PORT_10G_FIBER:
case PORT_10G_SERDES:
nxgep->xcvr = nxge_10G_fiber_table;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "10G xcvr "
"nxgep->nxge_hw_p->xcvr_addr[portn] = [%d] "
"nxgep->xcvr_addr = [%d]",
nxgep->nxge_hw_p->xcvr_addr[portn],
nxgep->xcvr_addr));
if (nxgep->nxge_hw_p->xcvr_addr[portn]) {
nxgep->xcvr_addr =
nxgep->nxge_hw_p->xcvr_addr[portn];
}
switch (nxgep->platform_type) {
case P_NEPTUNE_MARAMBA_P0:
case P_NEPTUNE_MARAMBA_P1:
/*
* Switch off LED for corresponding copper
* port
*/
nxge_bcm5464_link_led_off(nxgep);
break;
default:
break;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "10G %s Xcvr",
(nxgep->mac.portmode == PORT_10G_FIBER) ?
"Fiber" : "Serdes"));
break;
case PORT_HSP_MODE:
nxgep->xcvr = nxge_10G_fiber_table;
nxgep->xcvr.xcvr_inuse = HSP_XCVR;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "Neptune 10G Hot "
"Swappable Xcvr (not present)"));
break;
default:
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"Unknown port-type: 0x%x", port_type));
return (NXGE_ERROR);
}
}
if (nxgep->mac.portmode == PORT_10G_FIBER ||
nxgep->mac.portmode == PORT_10G_COPPER) {
uint32_t pma_pmd_id;
pma_pmd_id = nxge_get_cl45_pma_pmd_id(nxgep,
nxgep->xcvr_addr);
if ((pma_pmd_id & BCM_PHY_ID_MASK) == MARVELL_88X201X_PHY_ID) {
chip_id = MRVL88X201X_CHIP_ID;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_setup_xcvr_table: "
"Chip ID MARVELL [0x%x] for 10G xcvr", chip_id));
} else if ((pma_pmd_id & NLP2020_DEV_ID_MASK) ==
NLP2020_DEV_ID) {
chip_id = NLP2020_CHIP_ID;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_setup_xcvr_table: "
"Chip ID AEL2020 [0x%x] for 10G xcvr", chip_id));
} else if ((status = nxge_mdio_read(nxgep, nxgep->xcvr_addr,
BCM8704_PCS_DEV_ADDR, BCM8704_CHIP_ID_REG,
&chip_id)) == NXGE_OK) {
switch (chip_id) {
case BCM8704_CHIP_ID:
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_setup_xcvr_table: "
"Chip ID 8704 [0x%x] for 10G xcvr",
chip_id));
break;
case BCM8706_CHIP_ID:
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_setup_xcvr_table: "
"Chip ID 8706 [0x%x] for 10G xcvr",
chip_id));
break;
default:
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_setup_xcvr_table: "
"Unknown Chip ID [0x%x] for 10G xcvr",
chip_id));
break;
}
}
}
nxgep->statsp->mac_stats.xcvr_inuse = nxgep->xcvr.xcvr_inuse;
nxgep->statsp->mac_stats.xcvr_portn = nxgep->xcvr_addr;
nxgep->chip_id = chip_id;
/*
* Get the actual device ID value returned by MDIO read.
*/
nxgep->statsp->mac_stats.xcvr_id = 0;
pma_pmd_id = nxge_get_cl45_pma_pmd_id(nxgep, nxgep->xcvr_addr);
if (nxge_is_supported_phy(pma_pmd_id, CLAUSE_45_TYPE)) {
nxgep->statsp->mac_stats.xcvr_id = pma_pmd_id;
} else {
pcs_id = nxge_get_cl45_pcs_id(nxgep, nxgep->xcvr_addr);
if (nxge_is_supported_phy(pcs_id, CLAUSE_45_TYPE)) {
nxgep->statsp->mac_stats.xcvr_id = pcs_id;
} else {
phy_id = nxge_get_cl22_phy_id(nxgep,
nxgep->xcvr_addr);
if (nxge_is_supported_phy(phy_id, CLAUSE_22_TYPE)) {
nxgep->statsp->mac_stats.xcvr_id = phy_id;
}
}
}
nxgep->mac.linkchkmode = LINKCHK_TIMER;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "nxge_setup_xcvr_table: niu_type"
"[0x%x] platform type[0x%x] xcvr_addr[%d]", nxgep->niu_type,
nxgep->platform_type, nxgep->xcvr_addr));
return (status);
}
/* Initialize the entire MAC and physical layer */
nxge_status_t
nxge_mac_init(p_nxge_t nxgep)
{
uint8_t portn;
nxge_status_t status = NXGE_OK;
portn = NXGE_GET_PORT_NUM(nxgep->function_num);
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "==> nxge_mac_init: port<%d>", portn));
nxgep->mac.portnum = portn;
nxgep->mac.porttype = PORT_TYPE_XMAC;
if ((portn == BMAC_PORT_0) || (portn == BMAC_PORT_1))
nxgep->mac.porttype = PORT_TYPE_BMAC;
/* Initialize XIF to configure a network mode */
if ((status = nxge_xif_init(nxgep)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_pcs_init(nxgep)) != NXGE_OK) {
goto fail;
}
/* Initialize TX and RX MACs */
/*
* Always perform XIF init first, before TX and RX MAC init
*/
if ((status = nxge_tx_mac_reset(nxgep)) != NXGE_OK)
goto fail;
if ((status = nxge_tx_mac_init(nxgep)) != NXGE_OK)
goto fail;
if ((status = nxge_rx_mac_reset(nxgep)) != NXGE_OK)
goto fail;
if ((status = nxge_rx_mac_init(nxgep)) != NXGE_OK)
goto fail;
if ((status = nxge_tx_mac_enable(nxgep)) != NXGE_OK)
goto fail;
if (nxgep->nxge_mac_state == NXGE_MAC_STARTED) {
if ((status = nxge_rx_mac_enable(nxgep)) != NXGE_OK)
goto fail;
}
/* Initialize MAC control configuration */
if ((status = nxge_mac_ctrl_init(nxgep)) != NXGE_OK) {
goto fail;
}
nxgep->statsp->mac_stats.mac_mtu = nxgep->mac.maxframesize;
/* The Neptune Serdes needs to be reinitialized again */
if ((NXGE_IS_VALID_NEPTUNE_TYPE(nxgep)) &&
((nxgep->mac.portmode == PORT_1G_SERDES) ||
(nxgep->mac.portmode == PORT_1G_TN1010) ||
(nxgep->mac.portmode == PORT_1G_FIBER)) &&
((portn == 0) || (portn == 1))) {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_mac_init: reinit Neptune 1G Serdes "));
if ((status = nxge_1G_serdes_init(nxgep)) != NXGE_OK) {
goto fail;
}
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "<== nxge_mac_init: port<%d>", portn));
return (NXGE_OK);
fail:
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_mac_init: failed to initialize MAC port<%d>", portn));
return (status);
}
/* Initialize the Ethernet Link */
nxge_status_t
nxge_link_init(p_nxge_t nxgep)
{
nxge_status_t status = NXGE_OK;
nxge_port_mode_t portmode;
#ifdef NXGE_DEBUG
uint8_t portn;
portn = nxgep->mac.portnum;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "==> nxge_link_init: port<%d>", portn));
#endif
/* For Opus NEM, Serdes always needs to be initialized */
portmode = nxgep->mac.portmode;
/*
* Workaround to get link up in both NIU ports. Some portmodes require
* that the xcvr be initialized twice, the first time before calling
* nxge_serdes_init.
*/
if (nxgep->niu_type == N2_NIU && (portmode != PORT_10G_SERDES) &&
(portmode != PORT_10G_TN1010) &&
(portmode != PORT_1G_TN1010) &&
(portmode != PORT_1G_SERDES)) {
if ((status = nxge_xcvr_init(nxgep)) != NXGE_OK) {
goto fail;
}
}
NXGE_DELAY(200000);
/* Initialize internal serdes */
if ((status = nxge_serdes_init(nxgep)) != NXGE_OK)
goto fail;
NXGE_DELAY(200000);
if ((status = nxge_xcvr_init(nxgep)) != NXGE_OK)
goto fail;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "<== nxge_link_init: port<%d>", portn));
return (NXGE_OK);
fail:
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "nxge_link_init: ",
"failed to initialize Ethernet link on port<%d>", portn));
return (status);
}
/* Initialize the XIF sub-block within the MAC */
nxge_status_t
nxge_xif_init(p_nxge_t nxgep)
{
uint32_t xif_cfg = 0;
npi_attr_t ap;
uint8_t portn;
nxge_port_t portt;
nxge_port_mode_t portmode;
p_nxge_stats_t statsp;
npi_status_t rs = NPI_SUCCESS;
npi_handle_t handle;
portn = NXGE_GET_PORT_NUM(nxgep->function_num);
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "==> nxge_xif_init: port<%d>", portn));
handle = nxgep->npi_handle;
portmode = nxgep->mac.portmode;
portt = nxgep->mac.porttype;
statsp = nxgep->statsp;
if ((NXGE_IS_VALID_NEPTUNE_TYPE(nxgep)) &&
((nxgep->mac.portmode == PORT_1G_SERDES) ||
(nxgep->mac.portmode == PORT_1G_TN1010) ||
(nxgep->mac.portmode == PORT_1G_FIBER)) &&
((portn == 0) || (portn == 1))) {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_xcvr_init: set ATCA mode"));
npi_mac_mif_set_atca_mode(nxgep->npi_handle, B_TRUE);
}
if (portt == PORT_TYPE_XMAC) {
/* Setup XIF Configuration for XMAC */
if ((portmode == PORT_10G_FIBER) ||
(portmode == PORT_10G_COPPER) ||
(portmode == PORT_10G_TN1010) ||
(portmode == PORT_HSP_MODE) ||
(portmode == PORT_10G_SERDES))
xif_cfg |= CFG_XMAC_XIF_LFS;
/* Bypass PCS so that RGMII will be used */
if (portmode == PORT_1G_COPPER) {
xif_cfg |= CFG_XMAC_XIF_1G_PCS_BYPASS;
}
/* Set MAC Internal Loopback if necessary */
if (statsp->port_stats.lb_mode == nxge_lb_mac1000)
xif_cfg |= CFG_XMAC_XIF_LOOPBACK;
if (statsp->mac_stats.link_speed == 100)
xif_cfg |= CFG_XMAC_XIF_SEL_CLK_25MHZ;
xif_cfg |= CFG_XMAC_XIF_TX_OUTPUT;
if ((portmode == PORT_10G_FIBER) ||
(portmode == PORT_10G_COPPER) ||
(portmode == PORT_10G_TN1010) ||
(portmode == PORT_1G_TN1010) ||
(portmode == PORT_HSP_MODE) ||
(portmode == PORT_10G_SERDES)) {
/* Assume LED same for 1G and 10G */
if (statsp->mac_stats.link_up) {
xif_cfg |= CFG_XMAC_XIF_LED_POLARITY;
} else {
xif_cfg |= CFG_XMAC_XIF_LED_FORCE;
}
}
rs = npi_xmac_xif_config(handle, INIT, portn, xif_cfg);
if (rs != NPI_SUCCESS)
goto fail;
nxgep->mac.xif_config = xif_cfg;
/* Set Port Mode */
if ((portmode == PORT_10G_FIBER) ||
(portmode == PORT_10G_COPPER) ||
(portmode == PORT_10G_TN1010) ||
(portmode == PORT_HSP_MODE) ||
(portmode == PORT_10G_SERDES)) {
SET_MAC_ATTR1(handle, ap, portn, MAC_PORT_MODE,
MAC_XGMII_MODE, rs);
if (rs != NPI_SUCCESS)
goto fail;
if (statsp->mac_stats.link_up) {
if (nxge_10g_link_led_on(nxgep) != NXGE_OK)
goto fail;
} else {
if (nxge_10g_link_led_off(nxgep) != NXGE_OK)
goto fail;
}
} else if ((portmode == PORT_1G_FIBER) ||
(portmode == PORT_1G_COPPER) ||
(portmode == PORT_1G_SERDES) ||
(portmode == PORT_1G_TN1010) ||
(portmode == PORT_1G_RGMII_FIBER)) {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_xif_init: Port[%d] Mode[%d] Speed[%d]",
portn, portmode, statsp->mac_stats.link_speed));
if (statsp->mac_stats.link_speed == 1000) {
SET_MAC_ATTR1(handle, ap, portn, MAC_PORT_MODE,
MAC_GMII_MODE, rs);
} else {
SET_MAC_ATTR1(handle, ap, portn, MAC_PORT_MODE,
MAC_MII_MODE, rs);
}
if (rs != NPI_SUCCESS)
goto fail;
} else {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_xif_init: Unknown port mode (%d)"
" for port<%d>", portmode, portn));
goto fail;
}
/* Enable ATCA mode */
} else if (portt == PORT_TYPE_BMAC) {
/* Setup XIF Configuration for BMAC */
if ((portmode == PORT_1G_COPPER) ||
(portmode == PORT_1G_RGMII_FIBER)) {
if (statsp->mac_stats.link_speed == 100)
xif_cfg |= CFG_BMAC_XIF_SEL_CLK_25MHZ;
}
if (statsp->port_stats.lb_mode == nxge_lb_mac1000)
xif_cfg |= CFG_BMAC_XIF_LOOPBACK;
if (statsp->mac_stats.link_speed == 1000)
xif_cfg |= CFG_BMAC_XIF_GMII_MODE;
xif_cfg |= CFG_BMAC_XIF_TX_OUTPUT;
rs = npi_bmac_xif_config(handle, INIT, portn, xif_cfg);
if (rs != NPI_SUCCESS)
goto fail;
nxgep->mac.xif_config = xif_cfg;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "<== nxge_xif_init: port<%d>", portn));
return (NXGE_OK);
fail:
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_xif_init: Failed to initialize XIF port<%d>", portn));
return (NXGE_ERROR | rs);
}
/*
* Initialize the PCS sub-block in the MAC. Note that PCS does not
* support loopback like XPCS.
*/
nxge_status_t
nxge_pcs_init(p_nxge_t nxgep)
{
pcs_cfg_t pcs_cfg;
uint32_t val;
uint8_t portn;
nxge_port_mode_t portmode;
npi_handle_t handle;
p_nxge_stats_t statsp;
pcs_ctrl_t pcs_ctrl;
npi_status_t rs = NPI_SUCCESS;
uint8_t i;
handle = nxgep->npi_handle;
portmode = nxgep->mac.portmode;
portn = nxgep->mac.portnum;
statsp = nxgep->statsp;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "==> nxge_pcs_init: port<%d>", portn));
if (portmode == PORT_1G_FIBER ||
portmode == PORT_1G_TN1010 ||
portmode == PORT_1G_SERDES) {
if (portmode == PORT_1G_TN1010) {
/* Reset PCS multiple time in PORT_1G_TN1010 mode */
for (i = 0; i < 6; i ++) {
if ((rs = npi_mac_pcs_reset(handle, portn))
!= NPI_SUCCESS) {
goto fail;
}
}
} else {
if ((rs = npi_mac_pcs_reset(handle, portn))
!= NPI_SUCCESS)
goto fail;
}
/* Initialize port's PCS */
pcs_cfg.value = 0;
pcs_cfg.bits.w0.enable = 1;
pcs_cfg.bits.w0.mask = 1;
PCS_REG_WR(handle, portn, PCS_CONFIG_REG, pcs_cfg.value);
PCS_REG_WR(handle, portn, PCS_DATAPATH_MODE_REG, 0);
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_pcs_init: (1G) port<%d> write config 0x%llx",
portn, pcs_cfg.value));
if (portmode == PORT_1G_TN1010) {
/*
* Must disable PCS auto-negotiation when the the driver
* is driving the TN1010 based XAUI card Otherwise the
* autonegotiation between the PCS and the TN1010 PCS
* will never complete and the Neptune/NIU will not work
*/
pcs_ctrl.value = 0;
PCS_REG_WR(handle, portn, PCS_MII_CTRL_REG,
pcs_ctrl.value);
}
} else if (portmode == PORT_10G_FIBER ||
portmode == PORT_10G_COPPER ||
portmode == PORT_10G_TN1010 ||
portmode == PORT_HSP_MODE ||
portmode == PORT_10G_SERDES) {
/* Use internal XPCS, bypass 1G PCS */
XMAC_REG_RD(handle, portn, XMAC_CONFIG_REG, &val);
val &= ~XMAC_XIF_XPCS_BYPASS;
XMAC_REG_WR(handle, portn, XMAC_CONFIG_REG, val);
if ((rs = npi_xmac_xpcs_reset(handle, portn)) != NPI_SUCCESS)
goto fail;
/* Set XPCS Internal Loopback if necessary */
if ((rs = npi_xmac_xpcs_read(handle, portn,
XPCS_REG_CONTROL1, &val)) != NPI_SUCCESS)
goto fail;
if ((statsp->port_stats.lb_mode == nxge_lb_mac10g) ||
(statsp->port_stats.lb_mode == nxge_lb_mac1000))
val |= XPCS_CTRL1_LOOPBK;
else
val &= ~XPCS_CTRL1_LOOPBK;
if ((rs = npi_xmac_xpcs_write(handle, portn,
XPCS_REG_CONTROL1, val)) != NPI_SUCCESS)
goto fail;
/* Clear descw errors */
if ((rs = npi_xmac_xpcs_write(handle, portn,
XPCS_REG_DESCWERR_COUNTER, 0)) != NPI_SUCCESS)
goto fail;
/* Clear symbol errors */
if ((rs = npi_xmac_xpcs_read(handle, portn,
XPCS_REG_SYMBOL_ERR_L0_1_COUNTER, &val)) != NPI_SUCCESS)
goto fail;
if ((rs = npi_xmac_xpcs_read(handle, portn,
XPCS_REG_SYMBOL_ERR_L2_3_COUNTER, &val)) != NPI_SUCCESS)
goto fail;
} else if ((portmode == PORT_1G_COPPER) ||
(portmode == PORT_1G_RGMII_FIBER)) {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_pcs_init: (1G) copper port<%d>", portn));
if (portn < 4) {
PCS_REG_WR(handle, portn, PCS_DATAPATH_MODE_REG,
PCS_DATAPATH_MODE_MII);
}
if ((rs = npi_mac_pcs_reset(handle, portn)) != NPI_SUCCESS)
goto fail;
} else {
goto fail;
}
pass:
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "<== nxge_pcs_init: port<%d>", portn));
return (NXGE_OK);
fail:
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_pcs_init: Failed to initialize PCS port<%d>", portn));
return (NXGE_ERROR | rs);
}
/*
* Initialize the MAC CTRL sub-block within the MAC
* Only the receive-pause-cap is supported.
*/
nxge_status_t
nxge_mac_ctrl_init(p_nxge_t nxgep)
{
uint8_t portn;
nxge_port_t portt;
p_nxge_stats_t statsp;
npi_handle_t handle;
uint32_t val;
portn = NXGE_GET_PORT_NUM(nxgep->function_num);
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "==> nxge_mac_ctrl_init: port<%d>",
portn));
handle = nxgep->npi_handle;
portt = nxgep->mac.porttype;
statsp = nxgep->statsp;
if (portt == PORT_TYPE_XMAC) {
/* Reading the current XMAC Config Register for XMAC */
XMAC_REG_RD(handle, portn, XMAC_CONFIG_REG, &val);
/*
* Setup XMAC Configuration for XMAC
* XMAC only supports receive-pause
*/
if (statsp->mac_stats.adv_cap_asmpause) {
if (!statsp->mac_stats.adv_cap_pause) {
/*
* If adv_cap_asmpause is 1 and adv_cap_pause
* is 0, enable receive pause.
*/
val |= XMAC_RX_CFG_RX_PAUSE_EN;
} else {
/*
* If adv_cap_asmpause is 1 and adv_cap_pause
* is 1, disable receive pause. Send pause is
* not supported.
*/
val &= ~XMAC_RX_CFG_RX_PAUSE_EN;
}
} else {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_mac_ctrl_init: port<%d>: pause",
portn));
if (statsp->mac_stats.adv_cap_pause) {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_mac_ctrl_init: port<%d>: "
"enable pause", portn));
/*
* If adv_cap_asmpause is 0 and adv_cap_pause
* is 1, enable receive pause.
*/
val |= XMAC_RX_CFG_RX_PAUSE_EN;
} else {
/*
* If adv_cap_asmpause is 0 and adv_cap_pause
* is 0, disable receive pause. Send pause is
* not supported
*/
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_mac_ctrl_init: port<%d>: "
"disable pause", portn));
val &= ~XMAC_RX_CFG_RX_PAUSE_EN;
}
}
XMAC_REG_WR(handle, portn, XMAC_CONFIG_REG, val);
} else if (portt == PORT_TYPE_BMAC) {
/* Reading the current MAC CTRL Config Register for BMAC */
BMAC_REG_RD(handle, portn, MAC_CTRL_CONFIG_REG, &val);
/* Setup MAC CTRL Configuration for BMAC */
if (statsp->mac_stats.adv_cap_asmpause) {
if (statsp->mac_stats.adv_cap_pause) {
/*
* If adv_cap_asmpause is 1 and adv_cap_pause
* is 1, disable receive pause. Send pause
* is not supported
*/
val &= ~MAC_CTRL_CFG_RECV_PAUSE_EN;
} else {
/*
* If adv_cap_asmpause is 1 and adv_cap_pause
* is 0, enable receive pause and disable
* send pause.
*/
val |= MAC_CTRL_CFG_RECV_PAUSE_EN;
val &= ~MAC_CTRL_CFG_SEND_PAUSE_EN;
}
} else {
if (statsp->mac_stats.adv_cap_pause) {
/*
* If adv_cap_asmpause is 0 and adv_cap_pause
* is 1, enable receive pause. Send pause is
* not supported.
*/
val |= MAC_CTRL_CFG_RECV_PAUSE_EN;
} else {
/*
* If adv_cap_asmpause is 0 and adv_cap_pause
* is 0, pause capability is not available in
* either direction.
*/
val &= (~MAC_CTRL_CFG_SEND_PAUSE_EN &
~MAC_CTRL_CFG_RECV_PAUSE_EN);
}
}
BMAC_REG_WR(handle, portn, MAC_CTRL_CONFIG_REG, val);
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "<== nxge_mac_ctrl_init: port<%d>",
portn));
return (NXGE_OK);
}
/* Initialize the Internal Serdes */
nxge_status_t
nxge_serdes_init(p_nxge_t nxgep)
{
p_nxge_stats_t statsp;
#ifdef NXGE_DEBUG
uint8_t portn;
#endif
nxge_status_t status = NXGE_OK;
#ifdef NXGE_DEBUG
portn = nxgep->mac.portnum;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_serdes_init port<%d>", portn));
#endif
if (nxgep->xcvr.serdes_init) {
statsp = nxgep->statsp;
status = nxgep->xcvr.serdes_init(nxgep);
if (status != NXGE_OK)
goto fail;
statsp->mac_stats.serdes_inits++;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "<== nxge_serdes_init port<%d>",
portn));
return (NXGE_OK);
fail:
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_serdes_init: Failed to initialize serdes for port<%d>",
portn));
return (status);
}
/* Initialize the TI Hedwig Internal Serdes (N2-NIU only) */
static nxge_status_t
nxge_n2_serdes_init(p_nxge_t nxgep)
{
uint8_t portn;
int chan;
esr_ti_cfgpll_l_t pll_cfg_l;
esr_ti_cfgpll_l_t pll_sts_l;
esr_ti_cfgrx_l_t rx_cfg_l;
esr_ti_cfgrx_h_t rx_cfg_h;
esr_ti_cfgtx_l_t tx_cfg_l;
esr_ti_cfgtx_h_t tx_cfg_h;
#ifdef NXGE_DEBUG
esr_ti_testcfg_t cfg;
#endif
esr_ti_testcfg_t test_cfg;
nxge_status_t status = NXGE_OK;
portn = nxgep->mac.portnum;
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "==> nxge_n2_serdes_init port<%d>",
portn));
if (nxgep->niu_hw_type == NIU_HW_TYPE_RF) {
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: KT-NIU", portn));
return (nxge_n2_kt_serdes_init(nxgep));
}
tx_cfg_l.value = 0;
tx_cfg_h.value = 0;
rx_cfg_l.value = 0;
rx_cfg_h.value = 0;
pll_cfg_l.value = 0;
pll_sts_l.value = 0;
test_cfg.value = 0;
/*
* If the nxge driver has been plumbed without a link, then it will
* detect a link up when a cable connecting to an anto-negotiation
* partner is plugged into the port. Because the TN1010 PHY supports
* both 1G and 10G speeds, the driver must re-configure the
* Neptune/NIU according to the negotiated speed. nxge_n2_serdes_init
* is called at the post-link-up reconfiguration time. Here it calls
* nxge_set_tn1010_param to set portmode before re-initializing
* the serdes.
*/
if (nxgep->mac.portmode == PORT_1G_TN1010 ||
nxgep->mac.portmode == PORT_10G_TN1010) {
if (nxge_set_tn1010_param(nxgep) != NXGE_OK) {
goto fail;
}
}
if (nxgep->mac.portmode == PORT_10G_FIBER ||
nxgep->mac.portmode == PORT_10G_COPPER ||
nxgep->mac.portmode == PORT_10G_TN1010 ||
nxgep->mac.portmode == PORT_HSP_MODE ||
nxgep->mac.portmode == PORT_10G_SERDES) {
/* 0x0E01 */
tx_cfg_l.bits.entx = 1;
tx_cfg_l.bits.swing = CFGTX_SWING_1375MV;
/* 0x9101 */
rx_cfg_l.bits.enrx = 1;
rx_cfg_l.bits.term = CFGRX_TERM_0P8VDDT;
rx_cfg_l.bits.align = CFGRX_ALIGN_EN;
rx_cfg_l.bits.los = CFGRX_LOS_LOTHRES;
/* 0x0008 */
rx_cfg_h.bits.eq = CFGRX_EQ_ADAPTIVE_LP_ADAPTIVE_ZF;
/* Set loopback mode if necessary */
if (nxgep->statsp->port_stats.lb_mode == nxge_lb_serdes10g) {
tx_cfg_l.bits.entest = 1;
rx_cfg_l.bits.entest = 1;
test_cfg.bits.loopback = TESTCFG_INNER_CML_DIS_LOOPBACK;
if ((status = nxge_mdio_write(nxgep, portn,
ESR_N2_DEV_ADDR,
ESR_N2_TEST_CFG_REG, test_cfg.value)) != NXGE_OK)
goto fail;
}
/* Initialize PLL for 10G */
pll_cfg_l.bits.mpy = CFGPLL_MPY_10X;
pll_cfg_l.bits.enpll = 1;
pll_sts_l.bits.enpll = 1;
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_CFG_L_REG, pll_cfg_l.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_STS_L_REG, pll_sts_l.value)) != NXGE_OK)
goto fail;
#ifdef NXGE_DEBUG
nxge_mdio_read(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_CFG_L_REG, &cfg.value);
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: PLL cfg.l 0x%x (0x%x)",
portn, pll_cfg_l.value, cfg.value));
nxge_mdio_read(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_STS_L_REG, &cfg.value);
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: PLL sts.l 0x%x (0x%x)",
portn, pll_sts_l.value, cfg.value));
#endif
} else if (nxgep->mac.portmode == PORT_1G_FIBER ||
nxgep->mac.portmode == PORT_1G_TN1010 ||
nxgep->mac.portmode == PORT_1G_SERDES) {
/* 0x0E21 */
tx_cfg_l.bits.entx = 1;
tx_cfg_l.bits.rate = CFGTX_RATE_HALF;
tx_cfg_l.bits.swing = CFGTX_SWING_1375MV;
/* 0x9121 */
rx_cfg_l.bits.enrx = 1;
rx_cfg_l.bits.rate = CFGRX_RATE_HALF;
rx_cfg_l.bits.term = CFGRX_TERM_0P8VDDT;
rx_cfg_l.bits.align = CFGRX_ALIGN_EN;
rx_cfg_l.bits.los = CFGRX_LOS_LOTHRES;
if (portn == 0) {
/* 0x8 */
rx_cfg_h.bits.eq = CFGRX_EQ_ADAPTIVE_LP_ADAPTIVE_ZF;
}
/* Initialize PLL for 1G */
pll_cfg_l.bits.mpy = CFGPLL_MPY_8X;
pll_cfg_l.bits.enpll = 1;
pll_sts_l.bits.enpll = 1;
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_CFG_L_REG, pll_cfg_l.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_STS_L_REG, pll_sts_l.value)) != NXGE_OK)
goto fail;
#ifdef NXGE_DEBUG
nxge_mdio_read(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_CFG_L_REG, &cfg.value);
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: PLL cfg.l 0x%x (0x%x)",
portn, pll_cfg_l.value, cfg.value));
nxge_mdio_read(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_STS_L_REG, &cfg.value);
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: PLL sts.l 0x%x (0x%x)",
portn, pll_sts_l.value, cfg.value));
#endif
/* Set loopback mode if necessary */
if (nxgep->statsp->port_stats.lb_mode == nxge_lb_serdes1000) {
tx_cfg_l.bits.entest = 1;
rx_cfg_l.bits.entest = 1;
test_cfg.bits.loopback = TESTCFG_INNER_CML_DIS_LOOPBACK;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: loopback 0x%x",
portn, test_cfg.value));
if ((status = nxge_mdio_write(nxgep, portn,
ESR_N2_DEV_ADDR,
ESR_N2_TEST_CFG_REG, test_cfg.value)) != NXGE_OK) {
goto fail;
}
}
} else {
goto fail;
}
/* MIF_REG_WR(handle, MIF_MASK_REG, ~mask); */
NXGE_DELAY(20);
/* init TX channels */
for (chan = 0; chan < 4; chan++) {
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_TX_CFG_L_REG_ADDR(chan), tx_cfg_l.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_TX_CFG_H_REG_ADDR(chan), tx_cfg_h.value)) != NXGE_OK)
goto fail;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: chan %d tx_cfg_l 0x%x",
portn, chan, tx_cfg_l.value));
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: chan %d tx_cfg_h 0x%x",
portn, chan, tx_cfg_h.value));
}
/* init RX channels */
for (chan = 0; chan < 4; chan++) {
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_RX_CFG_L_REG_ADDR(chan), rx_cfg_l.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_RX_CFG_H_REG_ADDR(chan), rx_cfg_h.value)) != NXGE_OK)
goto fail;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: chan %d rx_cfg_l 0x%x",
portn, chan, rx_cfg_l.value));
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_serdes_init port<%d>: chan %d rx_cfg_h 0x%x",
portn, chan, rx_cfg_h.value));
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "<== nxge_n2_serdes_init port<%d>",
portn));
return (NXGE_OK);
fail:
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_n2_serdes_init: Failed to initialize N2 serdes for port<%d>",
portn));
return (status);
}
/* Initialize the TI Hedwig Internal Serdes (N2-KT-NIU only) */
static nxge_status_t
nxge_n2_kt_serdes_init(p_nxge_t nxgep)
{
uint8_t portn;
int chan, i;
k_esr_ti_cfgpll_l_t pll_cfg_l;
k_esr_ti_cfgrx_l_t rx_cfg_l;
k_esr_ti_cfgrx_h_t rx_cfg_h;
k_esr_ti_cfgtx_l_t tx_cfg_l;
k_esr_ti_cfgtx_h_t tx_cfg_h;
#ifdef NXGE_DEBUG
k_esr_ti_testcfg_t cfg;
#endif
k_esr_ti_testcfg_t test_cfg;
nxge_status_t status = NXGE_OK;
boolean_t mode_1g = B_FALSE;
uint64_t val;
npi_handle_t handle;
portn = nxgep->mac.portnum;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d>", portn));
handle = nxgep->npi_handle;
tx_cfg_l.value = 0;
tx_cfg_h.value = 0;
rx_cfg_l.value = 0;
rx_cfg_h.value = 0;
pll_cfg_l.value = 0;
test_cfg.value = 0;
/*
* The following setting assumes the reference clock frquency
* is 156.25 MHz.
*/
/*
* If the nxge driver has been plumbed without a link, then it will
* detect a link up when a cable connecting to an anto-negotiation
* partner is plugged into the port. Because the TN1010 PHY supports
* both 1G and 10G speeds, the driver must re-configure the
* Neptune/NIU according to the negotiated speed. nxge_n2_serdes_init
* is called at the post-link-up reconfiguration time. Here it calls
* nxge_set_tn1010_param to set portmode before re-initializing
* the serdes.
*/
if (nxgep->mac.portmode == PORT_1G_TN1010 ||
nxgep->mac.portmode == PORT_10G_TN1010) {
if (nxge_set_tn1010_param(nxgep) != NXGE_OK) {
goto fail;
}
}
if (nxgep->mac.portmode == PORT_10G_FIBER ||
nxgep->mac.portmode == PORT_10G_COPPER ||
nxgep->mac.portmode == PORT_10G_TN1010 ||
nxgep->mac.portmode == PORT_10G_SERDES) {
/* Take tunables from OBP if present, otherwise use defaults */
if (nxgep->srds_prop.prop_set & NXGE_SRDS_TXCFGL) {
tx_cfg_l.value = nxgep->srds_prop.tx_cfg_l;
} else {
tx_cfg_l.bits.entx = K_CFGTX_ENABLE_TX;
/* 0x1e21 */
tx_cfg_l.bits.swing = K_CFGTX_SWING_2000MV;
tx_cfg_l.bits.rate = K_CFGTX_RATE_HALF;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> tx_cfg_l 0x%x",
portn, tx_cfg_l.value));
if (nxgep->srds_prop.prop_set & NXGE_SRDS_TXCFGH) {
tx_cfg_h.value = nxgep->srds_prop.tx_cfg_h;
} else {
/* channel 0: enable syn. master */
/* 0x40 */
tx_cfg_h.bits.msync = K_CFGTX_ENABLE_MSYNC;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> tx_cfg_h 0x%x",
portn, tx_cfg_h.value));
if (nxgep->srds_prop.prop_set & NXGE_SRDS_RXCFGL) {
rx_cfg_l.value = nxgep->srds_prop.rx_cfg_l;
} else {
/* 0x4821 */
rx_cfg_l.bits.enrx = K_CFGRX_ENABLE_RX;
rx_cfg_l.bits.rate = K_CFGRX_RATE_HALF;
rx_cfg_l.bits.align = K_CFGRX_ALIGN_EN;
rx_cfg_l.bits.los = K_CFGRX_LOS_ENABLE;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> rx_cfg_l 0x%x",
portn, rx_cfg_l.value));
if (nxgep->srds_prop.prop_set & NXGE_SRDS_RXCFGH) {
rx_cfg_h.value = nxgep->srds_prop.rx_cfg_h;
} else {
/* 0x0008 */
rx_cfg_h.bits.eq = K_CFGRX_EQ_ADAPTIVE;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> rx_cfg_h 0x%x",
portn, rx_cfg_h.value));
if (nxgep->srds_prop.prop_set & NXGE_SRDS_PLLCFGL) {
pll_cfg_l.value = nxgep->srds_prop.pll_cfg_l;
} else {
/* 0xa1: Initialize PLL for 10G */
pll_cfg_l.bits.mpy = K_CFGPLL_MPY_20X;
pll_cfg_l.bits.enpll = K_CFGPLL_ENABLE_PLL;
}
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> pll_cfg_l 0x%x",
portn, pll_cfg_l.value));
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_CFG_L_REG, pll_cfg_l.value)) != NXGE_OK)
goto fail;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> pll_cfg_l 0x%x",
portn, pll_cfg_l.value));
/* Set loopback mode if necessary */
if (nxgep->statsp->port_stats.lb_mode == nxge_lb_serdes10g) {
tx_cfg_h.bits.loopback = K_CFGTX_INNER_CML_ENA_LOOPBACK;
rx_cfg_h.bits.loopback = K_CFGTX_INNER_CML_ENA_LOOPBACK;
rx_cfg_l.bits.los = 0;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d>: "
"loopback 0x%x", portn, tx_cfg_h.value));
}
#ifdef NXGE_DEBUG
nxge_mdio_read(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_CFG_L_REG, &cfg.value);
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"==> nxge_n2_kt_serdes_init port<%d>: "
"PLL cfg.l 0x%x (0x%x)",
portn, pll_cfg_l.value, cfg.value));
nxge_mdio_read(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_STS_L_REG, &cfg.value);
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"==> nxge_n2_kt_serdes_init port<%d>: (0x%x)",
portn, cfg.value));
#endif
} else if (nxgep->mac.portmode == PORT_1G_FIBER ||
nxgep->mac.portmode == PORT_1G_TN1010 ||
nxgep->mac.portmode == PORT_1G_SERDES) {
mode_1g = B_TRUE;
/* 0x1e41 */
tx_cfg_l.bits.entx = 1;
tx_cfg_l.bits.rate = K_CFGTX_RATE_HALF;
tx_cfg_l.bits.swing = K_CFGTX_SWING_2000MV;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> tx_cfg_l 0x%x",
portn, tx_cfg_l.value));
/* channel 0: enable syn. master */
tx_cfg_h.bits.msync = K_CFGTX_ENABLE_MSYNC;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> tx_cfg_h 0x%x",
portn, tx_cfg_h.value));
/* 0x4841 */
rx_cfg_l.bits.enrx = 1;
rx_cfg_l.bits.rate = K_CFGRX_RATE_HALF;
rx_cfg_l.bits.align = K_CFGRX_ALIGN_EN;
rx_cfg_l.bits.los = K_CFGRX_LOS_ENABLE;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> rx_cfg_l 0x%x",
portn, rx_cfg_l.value));
/* 0x0008 */
rx_cfg_h.bits.eq = K_CFGRX_EQ_ADAPTIVE_LF_365MHZ_ZF;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> tx_cfg_h 0x%x",
portn, rx_cfg_h.value));
/* 0xa1: Initialize PLL for 1G */
pll_cfg_l.bits.mpy = K_CFGPLL_MPY_20X;
pll_cfg_l.bits.enpll = K_CFGPLL_ENABLE_PLL;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d> pll_cfg_l 0x%x",
portn, pll_cfg_l.value));
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_CFG_L_REG, pll_cfg_l.value))
!= NXGE_OK)
goto fail;
#ifdef NXGE_DEBUG
nxge_mdio_read(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_CFG_L_REG, &cfg.value);
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"==> nxge_n2_serdes_init port<%d>: PLL cfg.l 0x%x (0x%x)",
portn, pll_cfg_l.value, cfg.value));
nxge_mdio_read(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_PLL_STS_L_REG, &cfg.value);
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"==> nxge_n2_kt_serdes_init port<%d>: (0x%x)",
portn, cfg.value));
#endif
/* Set loopback mode if necessary */
if (nxgep->statsp->port_stats.lb_mode == nxge_lb_serdes1000) {
tx_cfg_h.bits.loopback = TESTCFG_INNER_CML_DIS_LOOPBACK;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d>: "
"loopback 0x%x", portn, test_cfg.value));
if ((status = nxge_mdio_write(nxgep, portn,
ESR_N2_DEV_ADDR,
ESR_N2_TX_CFG_L_REG_ADDR(0),
tx_cfg_h.value)) != NXGE_OK) {
goto fail;
}
}
} else {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_n2_kt_serdes_init:port<%d> - "
"unsupported port mode %d",
portn, nxgep->mac.portmode));
goto fail;
}
NXGE_DELAY(20);
/* Clear the test register (offset 0x8004) */
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_TEST_CFG_REG, test_cfg.value)) != NXGE_OK) {
goto fail;
}
NXGE_DELAY(20);
/* init TX channels */
for (chan = 0; chan < 4; chan++) {
if (mode_1g)
tx_cfg_l.value = 0;
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_TX_CFG_L_REG_ADDR(chan), tx_cfg_l.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_TX_CFG_H_REG_ADDR(chan), tx_cfg_h.value)) != NXGE_OK)
goto fail;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d>: "
"chan %d tx_cfg_l 0x%x", portn, chan, tx_cfg_l.value));
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d>: "
"chan %d tx_cfg_h 0x%x", portn, chan, tx_cfg_h.value));
}
/* init RX channels */
/* 1G mode only write to the first channel */
for (chan = 0; chan < 4; chan++) {
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_RX_CFG_L_REG_ADDR(chan), rx_cfg_l.value))
!= NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn, ESR_N2_DEV_ADDR,
ESR_N2_RX_CFG_H_REG_ADDR(chan), rx_cfg_h.value))
!= NXGE_OK)
goto fail;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d>: "
"chan %d rx_cfg_l 0x%x", portn, chan, rx_cfg_l.value));
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_n2_kt_serdes_init port<%d>: "
"chan %d rx_cfg_h 0x%x", portn, chan, rx_cfg_h.value));
}
if (portn == 0) {
/* Wait for serdes to be ready */
for (i = 0; i < MAX_SERDES_RDY_RETRIES; i++) {
ESR_REG_RD(handle, ESR_INTERNAL_SIGNALS_REG, &val);
if ((val & ESR_SIG_P0_BITS_MASK) !=
(ESR_SIG_SERDES_RDY0_P0 | ESR_SIG_DETECT0_P0 |
ESR_SIG_XSERDES_RDY_P0 |
ESR_SIG_XDETECT_P0_CH3 |
ESR_SIG_XDETECT_P0_CH2 |
ESR_SIG_XDETECT_P0_CH1 |
ESR_SIG_XDETECT_P0_CH0))
NXGE_DELAY(SERDES_RDY_WT_INTERVAL);
else
break;
}
if (i == MAX_SERDES_RDY_RETRIES) {
/*
* RDY signal stays low may due to the absent of the
* external PHY, it is not an error condition.
* But still print the message for the debugging
* purpose when link stays down
*/
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_n2_kt_serdes_init: "
"Serdes/signal for port<%d> not ready", portn));
goto done;
}
} else if (portn == 1) {
/* Wait for serdes to be ready */
for (i = 0; i < MAX_SERDES_RDY_RETRIES; i++) {
ESR_REG_RD(handle, ESR_INTERNAL_SIGNALS_REG, &val);
if ((val & ESR_SIG_P1_BITS_MASK) !=
(ESR_SIG_SERDES_RDY0_P1 | ESR_SIG_DETECT0_P1 |
ESR_SIG_XSERDES_RDY_P1 |
ESR_SIG_XDETECT_P1_CH3 |
ESR_SIG_XDETECT_P1_CH2 |
ESR_SIG_XDETECT_P1_CH1 |
ESR_SIG_XDETECT_P1_CH0))
NXGE_DELAY(SERDES_RDY_WT_INTERVAL);
else
break;
}
if (i == MAX_SERDES_RDY_RETRIES) {
/*
* RDY signal stays low may due to the absent of the
* external PHY, it is not an error condition.
* But still print the message for the debugging
* purpose when link stays down
*/
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_n2_kt_serdes_init: "
"Serdes/signal for port<%d> not ready", portn));
goto done;
}
}
done:
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"<== nxge_n2_kt_serdes_init port<%d>", portn));
return (NXGE_OK);
fail:
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_n2_serdes_init: Failed to initialize N2 serdes for port<%d>",
portn));
return (status);
}
/* Initialize the Neptune Internal Serdes for 10G (Neptune only) */
static nxge_status_t
nxge_neptune_10G_serdes_init(p_nxge_t nxgep)
{
npi_handle_t handle;
uint8_t portn;
int chan, i;
sr_rx_tx_ctrl_l_t rx_tx_ctrl_l;
sr_rx_tx_ctrl_h_t rx_tx_ctrl_h;
sr_glue_ctrl0_l_t glue_ctrl0_l;
sr_glue_ctrl0_h_t glue_ctrl0_h;
uint64_t val;
uint16_t val16l;
uint16_t val16h;
nxge_status_t status = NXGE_OK;
portn = nxgep->mac.portnum;
if ((portn != 0) && (portn != 1))
return (NXGE_OK);
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_neptune_10G_serdes_init port<%d>", portn));
handle = nxgep->npi_handle;
switch (portn) {
case 0:
/* Reset Serdes */
ESR_REG_WR(handle, ESR_RESET_REG, ESR_RESET_0);
NXGE_DELAY(20);
ESR_REG_WR(handle, ESR_RESET_REG, 0x0);
NXGE_DELAY(2000);
/* Configure Serdes to 10G mode */
ESR_REG_WR(handle, ESR_0_PLL_CONFIG_REG,
ESR_PLL_CFG_10G_SERDES);
ESR_REG_WR(handle, ESR_0_CONTROL_REG,
ESR_CTL_EN_SYNCDET_0 | ESR_CTL_EN_SYNCDET_1 |
ESR_CTL_EN_SYNCDET_2 | ESR_CTL_EN_SYNCDET_3 |
(0x5 << ESR_CTL_OUT_EMPH_0_SHIFT) |
(0x5 << ESR_CTL_OUT_EMPH_1_SHIFT) |
(0x5 << ESR_CTL_OUT_EMPH_2_SHIFT) |
(0x5 << ESR_CTL_OUT_EMPH_3_SHIFT) |
(0x5 << ESR_CTL_OUT_EMPH_3_SHIFT) |
(0x1 << ESR_CTL_LOSADJ_0_SHIFT) |
(0x1 << ESR_CTL_LOSADJ_1_SHIFT) |
(0x1 << ESR_CTL_LOSADJ_2_SHIFT) |
(0x1 << ESR_CTL_LOSADJ_3_SHIFT));
/* Set Serdes0 Internal Loopback if necessary */
if (nxgep->statsp->port_stats.lb_mode == nxge_lb_serdes10g) {
ESR_REG_WR(handle,
ESR_0_TEST_CONFIG_REG,
ESR_PAD_LOOPBACK_CH3 |
ESR_PAD_LOOPBACK_CH2 |
ESR_PAD_LOOPBACK_CH1 |
ESR_PAD_LOOPBACK_CH0);
} else {
ESR_REG_WR(handle, ESR_0_TEST_CONFIG_REG, 0);
}
break;
case 1:
/* Reset Serdes */
ESR_REG_WR(handle, ESR_RESET_REG, ESR_RESET_1);
NXGE_DELAY(20);
ESR_REG_WR(handle, ESR_RESET_REG, 0x0);
NXGE_DELAY(2000);
/* Configure Serdes to 10G mode */
ESR_REG_WR(handle, ESR_1_PLL_CONFIG_REG,
ESR_PLL_CFG_10G_SERDES);
ESR_REG_WR(handle, ESR_1_CONTROL_REG,
ESR_CTL_EN_SYNCDET_0 | ESR_CTL_EN_SYNCDET_1 |
ESR_CTL_EN_SYNCDET_2 | ESR_CTL_EN_SYNCDET_3 |
(0x5 << ESR_CTL_OUT_EMPH_0_SHIFT) |
(0x5 << ESR_CTL_OUT_EMPH_1_SHIFT) |
(0x5 << ESR_CTL_OUT_EMPH_2_SHIFT) |
(0x5 << ESR_CTL_OUT_EMPH_3_SHIFT) |
(0x5 << ESR_CTL_OUT_EMPH_3_SHIFT) |
(0x1 << ESR_CTL_LOSADJ_0_SHIFT) |
(0x1 << ESR_CTL_LOSADJ_1_SHIFT) |
(0x1 << ESR_CTL_LOSADJ_2_SHIFT) |
(0x1 << ESR_CTL_LOSADJ_3_SHIFT));
/* Set Serdes1 Internal Loopback if necessary */
if (nxgep->statsp->port_stats.lb_mode == nxge_lb_serdes10g) {
ESR_REG_WR(handle, ESR_1_TEST_CONFIG_REG,
ESR_PAD_LOOPBACK_CH3 | ESR_PAD_LOOPBACK_CH2 |
ESR_PAD_LOOPBACK_CH1 | ESR_PAD_LOOPBACK_CH0);
} else {
ESR_REG_WR(handle, ESR_1_TEST_CONFIG_REG, 0);
}
break;
default:
/* Nothing to do here */
goto done;
}
/* init TX RX channels */
for (chan = 0; chan < 4; chan++) {
if ((status = nxge_mdio_read(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_CONTROL_L_ADDR(chan),
&rx_tx_ctrl_l.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_read(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_CONTROL_H_ADDR(chan),
&rx_tx_ctrl_h.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_read(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_GLUE_CONTROL0_L_ADDR(chan),
&glue_ctrl0_l.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_read(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_GLUE_CONTROL0_H_ADDR(chan),
&glue_ctrl0_h.value)) != NXGE_OK)
goto fail;
rx_tx_ctrl_l.bits.enstretch = 1;
rx_tx_ctrl_h.bits.vmuxlo = 2;
rx_tx_ctrl_h.bits.vpulselo = 2;
glue_ctrl0_l.bits.rxlosenable = 1;
glue_ctrl0_l.bits.samplerate = 0xF;
glue_ctrl0_l.bits.thresholdcount = 0xFF;
glue_ctrl0_h.bits.bitlocktime = BITLOCKTIME_300_CYCLES;
if ((status = nxge_mdio_write(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_CONTROL_L_ADDR(chan),
rx_tx_ctrl_l.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_CONTROL_H_ADDR(chan),
rx_tx_ctrl_h.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_GLUE_CONTROL0_L_ADDR(chan),
glue_ctrl0_l.value)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_GLUE_CONTROL0_H_ADDR(chan),
glue_ctrl0_h.value)) != NXGE_OK)
goto fail;
}
/* Apply Tx core reset */
if ((status = nxge_mdio_write(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_RESET_CONTROL_L_ADDR(),
(uint16_t)0)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_H_ADDR(), (uint16_t)0xffff)) !=
NXGE_OK)
goto fail;
NXGE_DELAY(200);
/* Apply Rx core reset */
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_L_ADDR(), (uint16_t)0xffff)) !=
NXGE_OK)
goto fail;
NXGE_DELAY(200);
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_H_ADDR(), (uint16_t)0)) != NXGE_OK)
goto fail;
NXGE_DELAY(200);
if ((status = nxge_mdio_read(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_RESET_CONTROL_L_ADDR(),
&val16l)) != NXGE_OK)
goto fail;
if ((status = nxge_mdio_read(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_H_ADDR(), &val16h)) != NXGE_OK)
goto fail;
if ((val16l != 0) || (val16h != 0)) {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"Failed to reset port<%d> XAUI Serdes "
"(val16l 0x%x val16h 0x%x)",
portn, val16l, val16h));
}
if (portn == 0) {
/* Wait for serdes to be ready */
for (i = 0; i < MAX_SERDES_RDY_RETRIES; i++) {
ESR_REG_RD(handle, ESR_INTERNAL_SIGNALS_REG, &val);
if ((val & ESR_SIG_P0_BITS_MASK) !=
(ESR_SIG_SERDES_RDY0_P0 | ESR_SIG_DETECT0_P0 |
ESR_SIG_XSERDES_RDY_P0 |
ESR_SIG_XDETECT_P0_CH3 |
ESR_SIG_XDETECT_P0_CH2 |
ESR_SIG_XDETECT_P0_CH1 |
ESR_SIG_XDETECT_P0_CH0))
NXGE_DELAY(SERDES_RDY_WT_INTERVAL);
else
break;
}
if (i == MAX_SERDES_RDY_RETRIES) {
/*
* RDY signal stays low may due to the absent of the
* external PHY, it is not an error condition. But still
* print the message for the debugging purpose when link
* stays down
*/
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_neptune_10G_serdes_init: "
"Serdes/signal for port<%d> not ready", portn));
goto done;
}
} else if (portn == 1) {
/* Wait for serdes to be ready */
for (i = 0; i < MAX_SERDES_RDY_RETRIES; i++) {
ESR_REG_RD(handle, ESR_INTERNAL_SIGNALS_REG, &val);
if ((val & ESR_SIG_P1_BITS_MASK) !=
(ESR_SIG_SERDES_RDY0_P1 | ESR_SIG_DETECT0_P1 |
ESR_SIG_XSERDES_RDY_P1 |
ESR_SIG_XDETECT_P1_CH3 |
ESR_SIG_XDETECT_P1_CH2 |
ESR_SIG_XDETECT_P1_CH1 |
ESR_SIG_XDETECT_P1_CH0))
NXGE_DELAY(SERDES_RDY_WT_INTERVAL);
else
break;
}
if (i == MAX_SERDES_RDY_RETRIES) {
/*
* RDY signal stays low may due to the absent of the
* external PHY, it is not an error condition. But still
* print the message for the debugging purpose when link
* stays down
*/
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_neptune_10G_serdes_init: "
"Serdes/signal for port<%d> not ready", portn));
goto done;
}
}
done:
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"<== nxge_neptune_10G_serdes_init port<%d>", portn));
return (NXGE_OK);
fail:
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_neptune_10G_serdes_init: "
"Failed to initialize Neptune serdes for port<%d>", portn));
return (status);
}
/* Initialize Neptune Internal Serdes for 1G (Neptune only) */
static nxge_status_t
nxge_1G_serdes_init(p_nxge_t nxgep)
{
npi_handle_t handle;
uint8_t portn;
int chan;
sr_rx_tx_ctrl_l_t rx_tx_ctrl_l;
sr_rx_tx_ctrl_h_t rx_tx_ctrl_h;
sr_glue_ctrl0_l_t glue_ctrl0_l;
sr_glue_ctrl0_h_t glue_ctrl0_h;
uint64_t val;
uint16_t val16l;
uint16_t val16h;
nxge_status_t status = NXGE_OK;
portn = nxgep->mac.portnum;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"==> nxge_1G_serdes_init port<%d>", portn));
handle = nxgep->npi_handle;
switch (portn) {
case 0:
/* Assert the reset register */
ESR_REG_RD(handle, ESR_RESET_REG, &val);
val |= ESR_RESET_0;
ESR_REG_WR(handle, ESR_RESET_REG, val);
/* Set the PLL register to 0x79 */
ESR_REG_WR(handle, ESR_0_PLL_CONFIG_REG,
ESR_PLL_CFG_1G_SERDES);
/* Set the control register to 0x249249f */
ESR_REG_WR(handle, ESR_0_CONTROL_REG, ESR_CTL_1G_SERDES);
/* Set Serdes0 Internal Loopback if necessary */
if (nxgep->statsp->port_stats.lb_mode == nxge_lb_serdes1000) {
/* Set pad loopback modes 0xaa */
ESR_REG_WR(handle, ESR_0_TEST_CONFIG_REG,
ESR_TSTCFG_LBTEST_PAD);
} else {
ESR_REG_WR(handle, ESR_0_TEST_CONFIG_REG, 0);
}
/* Deassert the reset register */
ESR_REG_RD(handle, ESR_RESET_REG, &val);
val &= ~ESR_RESET_0;
ESR_REG_WR(handle, ESR_RESET_REG, val);
break;
case 1:
/* Assert the reset register */
ESR_REG_RD(handle, ESR_RESET_REG, &val);
val |= ESR_RESET_1;
ESR_REG_WR(handle, ESR_RESET_REG, val);
/* Set PLL register to 0x79 */
ESR_REG_WR(handle, ESR_1_PLL_CONFIG_REG,
ESR_PLL_CFG_1G_SERDES);
/* Set the control register to 0x249249f */
ESR_REG_WR(handle, ESR_1_CONTROL_REG, ESR_CTL_1G_SERDES);
/* Set Serdes1 Internal Loopback if necessary */
if (nxgep->statsp->port_stats.lb_mode == nxge_lb_serdes1000) {
/* Set pad loopback mode 0xaa */
ESR_REG_WR(handle, ESR_1_TEST_CONFIG_REG,
ESR_TSTCFG_LBTEST_PAD);
} else {
ESR_REG_WR(handle, ESR_1_TEST_CONFIG_REG, 0);
}
/* Deassert the reset register */
ESR_REG_RD(handle, ESR_RESET_REG, &val);
val &= ~ESR_RESET_1;
ESR_REG_WR(handle, ESR_RESET_REG, val);
break;
default:
/* Nothing to do here */
goto done;
}
/* init TX RX channels */
for (chan = 0; chan < 4; chan++) {
if ((status = nxge_mdio_read(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_CONTROL_L_ADDR(chan),
&rx_tx_ctrl_l.value)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_read(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_CONTROL_H_ADDR(chan),
&rx_tx_ctrl_h.value)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_read(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_GLUE_CONTROL0_L_ADDR(chan),
&glue_ctrl0_l.value)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_read(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_GLUE_CONTROL0_H_ADDR(chan),
&glue_ctrl0_h.value)) != NXGE_OK) {
goto fail;
}
rx_tx_ctrl_l.bits.enstretch = 1;
rx_tx_ctrl_h.bits.vmuxlo = 2;
rx_tx_ctrl_h.bits.vpulselo = 2;
glue_ctrl0_l.bits.rxlosenable = 1;
glue_ctrl0_l.bits.samplerate = 0xF;
glue_ctrl0_l.bits.thresholdcount = 0xFF;
glue_ctrl0_h.bits.bitlocktime = BITLOCKTIME_300_CYCLES;
if ((status = nxge_mdio_write(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_CONTROL_L_ADDR(chan),
rx_tx_ctrl_l.value)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_write(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_RX_TX_CONTROL_H_ADDR(chan),
rx_tx_ctrl_h.value)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_write(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_GLUE_CONTROL0_L_ADDR(chan),
glue_ctrl0_l.value)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_write(nxgep, portn,
ESR_NEPTUNE_DEV_ADDR, ESR_NEP_GLUE_CONTROL0_H_ADDR(chan),
glue_ctrl0_h.value)) != NXGE_OK) {
goto fail;
}
}
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_POWER_CONTROL_L_ADDR(), 0xfff)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_POWER_CONTROL_H_ADDR(), 0xfff)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_TX_POWER_CONTROL_L_ADDR(), 0x70)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_TX_POWER_CONTROL_H_ADDR(), 0xfff)) != NXGE_OK) {
goto fail;
}
/* Apply Tx core reset */
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_L_ADDR(), (uint16_t)0)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_H_ADDR(), (uint16_t)0xffff)) !=
NXGE_OK) {
goto fail;
}
NXGE_DELAY(200);
/* Apply Rx core reset */
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_L_ADDR(), (uint16_t)0xffff)) !=
NXGE_OK) {
goto fail;
}
NXGE_DELAY(200);
if ((status = nxge_mdio_write(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_H_ADDR(), (uint16_t)0)) != NXGE_OK) {
goto fail;
}
NXGE_DELAY(200);
if ((status = nxge_mdio_read(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_L_ADDR(), &val16l)) != NXGE_OK) {
goto fail;
}
if ((status = nxge_mdio_read(nxgep, portn, ESR_NEPTUNE_DEV_ADDR,
ESR_NEP_RX_TX_RESET_CONTROL_H_ADDR(), &val16h)) != NXGE_OK) {
goto fail;
}
if ((val16l != 0) || (val16h != 0)) {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"Failed to reset port<%d> XAUI Serdes "
"(val16l 0x%x val16h 0x%x)", portn, val16l, val16h));
status = NXGE_ERROR;
goto fail;
}
NXGE_DELAY(200);
ESR_REG_RD(handle, ESR_INTERNAL_SIGNALS_REG, &val);
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"nxge_neptune_serdes_init: read internal signal reg port<%d> "
"val 0x%x", portn, val));
if (portn == 0) {
if ((val & ESR_SIG_P0_BITS_MASK_1G) !=
(ESR_SIG_SERDES_RDY0_P0 | ESR_SIG_DETECT0_P0)) {
/*
* RDY signal stays low may due to the absent of the
* external PHY, it is not an error condition. But still
* print the message for the debugging purpose when link
* stays down
*/
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_neptune_1G_serdes_init: "
"Serdes/signal for port<%d> not ready", portn));
goto done;
}
} else if (portn == 1) {
if ((val & ESR_SIG_P1_BITS_MASK_1G) !=
(ESR_SIG_SERDES_RDY0_P1 | ESR_SIG_DETECT0_P1)) {
/*
* RDY signal stays low may due to the absent of the
* external PHY, it is not an error condition. But still
* print the message for the debugging purpose when link
* stays down
*/
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_neptune_1G_serdes_init: "
"Serdes/signal for port<%d> not ready", portn));
goto done;
}
}
done:
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"<== nxge_1G_serdes_init port<%d>", portn));
return (NXGE_OK);
fail:
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_1G_serdes_init: "
"Failed to initialize Neptune serdes for port<%d>",
portn));
return (status);
}
#define NXGE_SET_PHY_TUNABLES(nxgep, phy_port, stat) \
{ \
int i; \
\
if (nxgep->phy_prop.cnt > 0) { \
for (i = 0; i < nxgep->phy_prop.cnt; i++) { \
if ((stat = nxge_mdio_write(nxgep, phy_port, \
nxgep->phy_prop.arr[i].dev, \
nxgep->phy_prop.arr[i].reg, \
nxgep->phy_prop.arr[i].val)) != NXGE_OK) { \
break; \
} \
NXGE_DEBUG_MSG((nxgep, MAC_CTL, \
"From OBP, write<dev.reg.val> = " \
"<0x%x.0x%x.0x%x>", \
nxgep->phy_prop.arr[i].dev, \
nxgep->phy_prop.arr[i].reg, \
nxgep->phy_prop.arr[i].val)); \
} \
} \
}
/* Initialize the BCM 8704 xcvr */
static nxge_status_t
nxge_BCM8704_xcvr_init(p_nxge_t nxgep)
{
uint16_t val;
#ifdef NXGE_DEBUG
uint8_t portn;
uint16_t val1;
#endif
uint8_t phy_port_addr;
pmd_tx_control_t tx_ctl;
control_t ctl;
phyxs_control_t phyxs_ctl;
pcs_control_t pcs_ctl;
uint32_t delay = 0;
optics_dcntr_t op_ctr;
nxge_status_t status = NXGE_OK;
#ifdef NXGE_DEBUG
portn = nxgep->mac.portnum;
#endif
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "==> nxge_BCM8704_xcvr_init: port<%d>",
portn));
phy_port_addr = nxgep->statsp->mac_stats.xcvr_portn;
/* Reset the transceiver */
if ((status = nxge_mdio_read(nxgep, phy_port_addr, BCM8704_PHYXS_ADDR,
BCM8704_PHYXS_CONTROL_REG, &phyxs_ctl.value)) != NXGE_OK)
goto fail;
phyxs_ctl.bits.reset = 1;
if ((status = nxge_mdio_write(nxgep, phy_port_addr, BCM8704_PHYXS_ADDR,
BCM8704_PHYXS_CONTROL_REG, phyxs_ctl.value)) != NXGE_OK)
goto fail;
do {
drv_usecwait(500);
if ((status = nxge_mdio_read(nxgep, phy_port_addr,
BCM8704_PHYXS_ADDR, BCM8704_PHYXS_CONTROL_REG,
&phyxs_ctl.value)) != NXGE_OK)
goto fail;
delay++;
} while ((phyxs_ctl.bits.reset) && (delay < 100));
if (delay == 100) {
NXGE_DEBUG_MSG((nxgep, MAC_CTL, "nxge_xcvr_init: "
"failed to reset Transceiver on port<%d>", portn));
status = NXGE_ERROR;
goto fail;
}
/* Set to 0x7FBF */
ctl.value = 0;
ctl.bits.res1 = 0x3F;
ctl.bits.optxon_lvl = 1;
ctl.bits.oprxflt_lvl = 1;
ctl.bits.optrxlos_lvl = 1;
ctl.bits.optxflt_lvl = 1;
ctl.bits.opprflt_lvl = 1;
ctl.bits.obtmpflt_lvl = 1;
ctl.bits.opbiasflt_lvl = 1;
ctl.bits.optxrst_lvl = 1;
if ((status = nxge_mdio_write(nxgep, phy_port_addr,
BCM8704_USER_DEV3_ADDR, BCM8704_USER_CONTROL_REG, ctl.value))
!= NXGE_OK)
goto fail;
/* Set to 0x164 */
tx_ctl.value = 0;
tx_ctl.bits.tsck_lpwren = 1;
tx_ctl.bits.tx_dac_txck = 0x2;
tx_ctl.bits.tx_dac_txd = 0x1;
tx_ctl.bits.xfp_clken = 1;
if ((status = nxge_mdio_write(nxgep, phy_port_addr,
BCM8704_USER_DEV3_ADDR, BCM8704_USER_PMD_TX_CONTROL_REG,
tx_ctl.value)) != NXGE_OK)
goto fail;
/*
* According to Broadcom's instruction, SW needs to read
* back these registers twice after written.
*/
if ((status = nxge_mdio_read(nxgep, phy_port_addr,
BCM8704_USER_DEV3_ADDR, BCM8704_USER_CONTROL_REG, &val))
!= NXGE_OK)
goto fail;
if ((status = nxge_mdio_read(nxgep, phy_port_addr,
BCM8704_USER_DEV3_ADDR, BCM8704_USER_CONTROL_REG, &val))
!= NXGE_OK)
goto fail;
if ((status = nxge_mdio_read(nxgep, phy_port_addr,
BCM8704_USER_DEV3_ADDR, BCM8704_USER_PMD_TX_CONTROL_REG, &val))
!= NXGE_OK)
goto fail;
if ((status = nxge_mdio_read(nxgep, phy_port_addr,
BCM8704_USER_DEV3_ADDR, BCM8704_USER_PMD_TX_CONTROL_REG, &val))
!= NXGE_OK)
goto fail;
/* Enable Tx and Rx LEDs to be driven by traffic */
if ((status = nxge_mdio_read(nxgep, phy_port_addr,
BCM8704_USER_DEV3_ADDR, BCM8704_USER_OPTICS_DIGITAL_CTRL_REG,
&op_ctr.value)) != NXGE_OK)
goto fail;
if (NXGE_IS_XAUI_PLATFORM(nxgep)) {
op_ctr.bits.gpio_sel = 0x1;
} else {
op_ctr.bits.gpio_sel = 0x3;
}
if ((status = nxge_mdio_write(nxgep, phy_port_addr,
BCM8704_USER_DEV3_ADDR, BCM8704_USER_OPTICS_DIGITAL_CTRL_REG,
op_ctr.value)) != NXGE_OK)
goto fail;
NXGE_DELAY(1000000);
/*
* Set XAUI link tunables from OBP if present.
*/
NXGE_SET_PHY_TUNABLES(nxgep, phy_port_addr, status);
if (status != NXGE_OK) {
NXGE_ERROR_MSG((nxgep, NXGE_ERR_CTL,
"nxge_BCM8704_xcvr_init: Failed setting PHY tunables"));
goto fail;
}
/* Set BCM8704 Internal Loopback mode if necessary */
if ((status = nxge_mdio_read(nxgep, phy_port_addr,
BCM8704_PCS_DEV_ADDR, BCM8704_PCS_CONTROL_REG, &pcs_ctl.value))
!= NXGE_OK)
goto fail;
if (nxgep->statsp->port_stats.lb_mode == nxge_lb_phy10g)
pcs_ctl.bits.loopback = 1;
else
pcs_ctl.bits.loopback = 0;
if ((status = nxge_mdio_write(nxgep, phy_port_addr,
BCM8704_PCS_DEV_ADDR, BCM8704_PCS_CONTROL_REG, pcs_ctl.value))
!= NXGE_OK)
goto fail;
status = nxge_mdio_read(nxgep, phy_port_addr, 0x1, 0xA, &val);
if (status != NXGE_OK)
goto fail;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"BCM8704 port<%d> Dev 1 Reg 0xA = 0x%x\n", portn, val));
status = nxge_mdio_read(nxgep, phy_port_addr, 0x3, 0x20, &val);
if (status != NXGE_OK)
goto fail;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"BCM8704 port<%d> Dev 3 Reg 0x20 = 0x%x\n", portn, val));
status = nxge_mdio_read(nxgep, phy_port_addr, 0x4, 0x18, &val);
if (status != NXGE_OK)
goto fail;
NXGE_DEBUG_MSG((nxgep, MAC_CTL,
"BCM8704 port<%d> Dev 4 Reg 0x18 = 0x%x\n", portn, val));