1
0
Fork 0

bnx2x: Add new PHY 54616s

Signed-off-by: Yaniv Rosner <yanivr@broadcom.com>
Signed-off-by: Vladislav Zolotarov <vladz@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
Signed-off-by: David S. Miller <davem@conan.davemloft.net>
hifive-unleashed-5.1
Yaniv Rosner 2011-06-14 01:34:17 +00:00 committed by David S. Miller
parent 3c9ada227c
commit 6583e33bae
2 changed files with 471 additions and 8 deletions

View File

@ -134,6 +134,9 @@
#define LINK_10GXFD LINK_STATUS_SPEED_AND_DUPLEX_10GXFD
#define LINK_20GTFD LINK_STATUS_SPEED_AND_DUPLEX_20GTFD
#define LINK_20GXFD LINK_STATUS_SPEED_AND_DUPLEX_20GXFD
/* */
#define SFP_EEPROM_CON_TYPE_ADDR 0x2
#define SFP_EEPROM_CON_TYPE_VAL_LC 0x7
@ -2059,6 +2062,83 @@ static u32 bnx2x_get_emac_base(struct bnx2x *bp,
}
/******************************************************************/
/* CL22 access functions */
/******************************************************************/
static int bnx2x_cl22_write(struct bnx2x *bp,
struct bnx2x_phy *phy,
u16 reg, u16 val)
{
u32 tmp, mode;
u8 i;
int rc = 0;
/* Switch to CL22 */
mode = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE);
REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE,
mode & ~EMAC_MDIO_MODE_CLAUSE_45);
/* address */
tmp = ((phy->addr << 21) | (reg << 16) | val |
EMAC_MDIO_COMM_COMMAND_WRITE_22 |
EMAC_MDIO_COMM_START_BUSY);
REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp);
for (i = 0; i < 50; i++) {
udelay(10);
tmp = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM);
if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) {
udelay(5);
break;
}
}
if (tmp & EMAC_MDIO_COMM_START_BUSY) {
DP(NETIF_MSG_LINK, "write phy register failed\n");
rc = -EFAULT;
}
REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, mode);
return rc;
}
static int bnx2x_cl22_read(struct bnx2x *bp,
struct bnx2x_phy *phy,
u16 reg, u16 *ret_val)
{
u32 val, mode;
u16 i;
int rc = 0;
/* Switch to CL22 */
mode = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE);
REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE,
mode & ~EMAC_MDIO_MODE_CLAUSE_45);
/* address */
val = ((phy->addr << 21) | (reg << 16) |
EMAC_MDIO_COMM_COMMAND_READ_22 |
EMAC_MDIO_COMM_START_BUSY);
REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val);
for (i = 0; i < 50; i++) {
udelay(10);
val = REG_RD(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM);
if (!(val & EMAC_MDIO_COMM_START_BUSY)) {
*ret_val = (u16)(val & EMAC_MDIO_COMM_DATA);
udelay(5);
break;
}
}
if (val & EMAC_MDIO_COMM_START_BUSY) {
DP(NETIF_MSG_LINK, "read phy register failed\n");
*ret_val = 0;
rc = -EFAULT;
}
REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, mode);
return rc;
}
/******************************************************************/
/* CL45 access functions */
/******************************************************************/
@ -2649,12 +2729,19 @@ static u8 bnx2x_ext_phy_resolve_fc(struct bnx2x_phy *phy,
vars->flow_ctrl = params->req_fc_auto_adv;
else if (vars->link_status & LINK_STATUS_AUTO_NEGOTIATE_COMPLETE) {
ret = 1;
bnx2x_cl45_read(bp, phy,
MDIO_AN_DEVAD,
MDIO_AN_REG_ADV_PAUSE, &ld_pause);
bnx2x_cl45_read(bp, phy,
MDIO_AN_DEVAD,
MDIO_AN_REG_LP_AUTO_NEG, &lp_pause);
if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54616) {
bnx2x_cl22_read(bp, phy,
0x4, &ld_pause);
bnx2x_cl22_read(bp, phy,
0x5, &lp_pause);
} else {
bnx2x_cl45_read(bp, phy,
MDIO_AN_DEVAD,
MDIO_AN_REG_ADV_PAUSE, &ld_pause);
bnx2x_cl45_read(bp, phy,
MDIO_AN_DEVAD,
MDIO_AN_REG_LP_AUTO_NEG, &lp_pause);
}
pause_result = (ld_pause &
MDIO_AN_REG_ADV_PAUSE_MASK) >> 8;
pause_result |= (lp_pause &
@ -4653,8 +4740,13 @@ static u16 bnx2x_wait_reset_complete(struct bnx2x *bp,
u16 cnt, ctrl;
/* Wait for soft reset to get cleared up to 1 sec */
for (cnt = 0; cnt < 1000; cnt++) {
bnx2x_cl45_read(bp, phy,
MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, &ctrl);
if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54616)
bnx2x_cl22_read(bp, phy,
MDIO_PMA_REG_CTRL, &ctrl);
else
bnx2x_cl45_read(bp, phy,
MDIO_PMA_DEVAD,
MDIO_PMA_REG_CTRL, &ctrl);
if (!(ctrl & (1<<15)))
break;
msleep(1);
@ -8807,6 +8899,329 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy,
break;
}
}
/******************************************************************/
/* 54616S PHY SECTION */
/******************************************************************/
static int bnx2x_54616s_config_init(struct bnx2x_phy *phy,
struct link_params *params,
struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
u8 port;
u16 autoneg_val, an_1000_val, an_10_100_val, fc_val, temp;
u32 cfg_pin;
DP(NETIF_MSG_LINK, "54616S cfg init\n");
usleep_range(1000, 1000);
/* This works with E3 only, no need to check the chip
before determining the port. */
port = params->port;
cfg_pin = (REG_RD(bp, params->shmem_base +
offsetof(struct shmem_region,
dev_info.port_hw_config[port].e3_cmn_pin_cfg)) &
PORT_HW_CFG_E3_PHY_RESET_MASK) >>
PORT_HW_CFG_E3_PHY_RESET_SHIFT;
/* Drive pin high to bring the GPHY out of reset. */
bnx2x_set_cfg_pin(bp, cfg_pin, 1);
/* wait for GPHY to reset */
msleep(50);
/* reset phy */
bnx2x_cl22_write(bp, phy,
MDIO_PMA_REG_CTRL, 0x8000);
bnx2x_wait_reset_complete(bp, phy, params);
/*wait for GPHY to reset */
msleep(50);
/* Configure LED4: set to INTR (0x6). */
/* Accessing shadow register 0xe. */
bnx2x_cl22_write(bp, phy,
MDIO_REG_GPHY_SHADOW,
MDIO_REG_GPHY_SHADOW_LED_SEL2);
bnx2x_cl22_read(bp, phy,
MDIO_REG_GPHY_SHADOW,
&temp);
temp &= ~(0xf << 4);
temp |= (0x6 << 4);
bnx2x_cl22_write(bp, phy,
MDIO_REG_GPHY_SHADOW,
MDIO_REG_GPHY_SHADOW_WR_ENA | temp);
/* Configure INTR based on link status change. */
bnx2x_cl22_write(bp, phy,
MDIO_REG_INTR_MASK,
~MDIO_REG_INTR_MASK_LINK_STATUS);
/* Flip the signal detect polarity (set 0x1c.0x1e[8]). */
bnx2x_cl22_write(bp, phy,
MDIO_REG_GPHY_SHADOW,
MDIO_REG_GPHY_SHADOW_AUTO_DET_MED);
bnx2x_cl22_read(bp, phy,
MDIO_REG_GPHY_SHADOW,
&temp);
temp |= MDIO_REG_GPHY_SHADOW_INVERT_FIB_SD;
bnx2x_cl22_write(bp, phy,
MDIO_REG_GPHY_SHADOW,
MDIO_REG_GPHY_SHADOW_WR_ENA | temp);
/* Set up fc */
/* Please refer to Table 28B-3 of 802.3ab-1999 spec. */
bnx2x_calc_ieee_aneg_adv(phy, params, &vars->ieee_fc);
fc_val = 0;
if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) ==
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC)
fc_val |= MDIO_AN_REG_ADV_PAUSE_ASYMMETRIC;
if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) ==
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH)
fc_val |= MDIO_AN_REG_ADV_PAUSE_PAUSE;
/* read all advertisement */
bnx2x_cl22_read(bp, phy,
0x09,
&an_1000_val);
bnx2x_cl22_read(bp, phy,
0x04,
&an_10_100_val);
bnx2x_cl22_read(bp, phy,
MDIO_PMA_REG_CTRL,
&autoneg_val);
/* Disable forced speed */
autoneg_val &= ~((1<<6) | (1<<8) | (1<<9) | (1<<12) | (1<<13));
an_10_100_val &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8) | (1<<10) |
(1<<11));
if (((phy->req_line_speed == SPEED_AUTO_NEG) &&
(phy->speed_cap_mask &
PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) ||
(phy->req_line_speed == SPEED_1000)) {
an_1000_val |= (1<<8);
autoneg_val |= (1<<9 | 1<<12);
if (phy->req_duplex == DUPLEX_FULL)
an_1000_val |= (1<<9);
DP(NETIF_MSG_LINK, "Advertising 1G\n");
} else
an_1000_val &= ~((1<<8) | (1<<9));
bnx2x_cl22_write(bp, phy,
0x09,
an_1000_val);
bnx2x_cl22_read(bp, phy,
0x09,
&an_1000_val);
/* set 100 speed advertisement */
if (((phy->req_line_speed == SPEED_AUTO_NEG) &&
(phy->speed_cap_mask &
(PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL |
PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF)))) {
an_10_100_val |= (1<<7);
/* Enable autoneg and restart autoneg for legacy speeds */
autoneg_val |= (1<<9 | 1<<12);
if (phy->req_duplex == DUPLEX_FULL)
an_10_100_val |= (1<<8);
DP(NETIF_MSG_LINK, "Advertising 100M\n");
}
/* set 10 speed advertisement */
if (((phy->req_line_speed == SPEED_AUTO_NEG) &&
(phy->speed_cap_mask &
(PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL |
PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF)))) {
an_10_100_val |= (1<<5);
autoneg_val |= (1<<9 | 1<<12);
if (phy->req_duplex == DUPLEX_FULL)
an_10_100_val |= (1<<6);
DP(NETIF_MSG_LINK, "Advertising 10M\n");
}
/* Only 10/100 are allowed to work in FORCE mode */
if (phy->req_line_speed == SPEED_100) {
autoneg_val |= (1<<13);
/* Enabled AUTO-MDIX when autoneg is disabled */
bnx2x_cl22_write(bp, phy,
0x18,
(1<<15 | 1<<9 | 7<<0));
DP(NETIF_MSG_LINK, "Setting 100M force\n");
}
if (phy->req_line_speed == SPEED_10) {
/* Enabled AUTO-MDIX when autoneg is disabled */
bnx2x_cl22_write(bp, phy,
0x18,
(1<<15 | 1<<9 | 7<<0));
DP(NETIF_MSG_LINK, "Setting 10M force\n");
}
bnx2x_cl22_write(bp, phy,
0x04,
an_10_100_val | fc_val);
if (phy->req_duplex == DUPLEX_FULL)
autoneg_val |= (1<<8);
bnx2x_cl22_write(bp, phy,
MDIO_PMA_REG_CTRL, autoneg_val);
return 0;
}
static void bnx2x_54616s_set_link_led(struct bnx2x_phy *phy,
struct link_params *params, u8 mode)
{
struct bnx2x *bp = params->bp;
DP(NETIF_MSG_LINK, "54616S set link led (mode=%x)\n", mode);
switch (mode) {
case LED_MODE_FRONT_PANEL_OFF:
case LED_MODE_OFF:
case LED_MODE_OPER:
case LED_MODE_ON:
default:
break;
}
return;
}
static void bnx2x_54616s_link_reset(struct bnx2x_phy *phy,
struct link_params *params)
{
struct bnx2x *bp = params->bp;
u32 cfg_pin;
u8 port;
/* This works with E3 only, no need to check the chip
before determining the port. */
port = params->port;
cfg_pin = (REG_RD(bp, params->shmem_base +
offsetof(struct shmem_region,
dev_info.port_hw_config[port].e3_cmn_pin_cfg)) &
PORT_HW_CFG_E3_PHY_RESET_MASK) >>
PORT_HW_CFG_E3_PHY_RESET_SHIFT;
/* Drive pin low to put GPHY in reset. */
bnx2x_set_cfg_pin(bp, cfg_pin, 0);
}
static u8 bnx2x_54616s_read_status(struct bnx2x_phy *phy,
struct link_params *params,
struct link_vars *vars)
{
struct bnx2x *bp = params->bp;
u16 val;
u8 link_up = 0;
u16 legacy_status, legacy_speed;
/* Get speed operation status */
bnx2x_cl22_read(bp, phy,
0x19,
&legacy_status);
DP(NETIF_MSG_LINK, "54616S read_status: 0x%x\n", legacy_status);
/* Read status to clear the PHY interrupt. */
bnx2x_cl22_read(bp, phy,
MDIO_REG_INTR_STATUS,
&val);
link_up = ((legacy_status & (1<<2)) == (1<<2));
if (link_up) {
legacy_speed = (legacy_status & (7<<8));
if (legacy_speed == (7<<8)) {
vars->line_speed = SPEED_1000;
vars->duplex = DUPLEX_FULL;
} else if (legacy_speed == (6<<8)) {
vars->line_speed = SPEED_1000;
vars->duplex = DUPLEX_HALF;
} else if (legacy_speed == (5<<8)) {
vars->line_speed = SPEED_100;
vars->duplex = DUPLEX_FULL;
}
/* Omitting 100Base-T4 for now */
else if (legacy_speed == (3<<8)) {
vars->line_speed = SPEED_100;
vars->duplex = DUPLEX_HALF;
} else if (legacy_speed == (2<<8)) {
vars->line_speed = SPEED_10;
vars->duplex = DUPLEX_FULL;
} else if (legacy_speed == (1<<8)) {
vars->line_speed = SPEED_10;
vars->duplex = DUPLEX_HALF;
} else /* Should not happen */
vars->line_speed = 0;
DP(NETIF_MSG_LINK, "Link is up in %dMbps,"
" is_duplex_full= %d\n", vars->line_speed,
(vars->duplex == DUPLEX_FULL));
/* Check legacy speed AN resolution */
bnx2x_cl22_read(bp, phy,
0x01,
&val);
if (val & (1<<5))
vars->link_status |=
LINK_STATUS_AUTO_NEGOTIATE_COMPLETE;
bnx2x_cl22_read(bp, phy,
0x06,
&val);
if ((val & (1<<0)) == 0)
vars->link_status |=
LINK_STATUS_PARALLEL_DETECTION_USED;
DP(NETIF_MSG_LINK, "BCM54616S: link speed is %d\n",
vars->line_speed);
bnx2x_ext_phy_resolve_fc(phy, params, vars);
}
return link_up;
}
static void bnx2x_54616s_config_loopback(struct bnx2x_phy *phy,
struct link_params *params)
{
struct bnx2x *bp = params->bp;
u16 val;
u32 umac_base = params->port ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
DP(NETIF_MSG_LINK, "2PMA/PMD ext_phy_loopback: 54616s\n");
/* Enable master/slave manual mmode and set to master */
/* mii write 9 [bits set 11 12] */
bnx2x_cl22_write(bp, phy, 0x09, 3<<11);
/* forced 1G and disable autoneg */
/* set val [mii read 0] */
/* set val [expr $val & [bits clear 6 12 13]] */
/* set val [expr $val | [bits set 6 8]] */
/* mii write 0 $val */
bnx2x_cl22_read(bp, phy, 0x00, &val);
val &= ~((1<<6) | (1<<12) | (1<<13));
val |= (1<<6) | (1<<8);
bnx2x_cl22_write(bp, phy, 0x00, val);
/* Set external loopback and Tx using 6dB coding */
/* mii write 0x18 7 */
/* set val [mii read 0x18] */
/* mii write 0x18 [expr $val | [bits set 10 15]] */
bnx2x_cl22_write(bp, phy, 0x18, 7);
bnx2x_cl22_read(bp, phy, 0x18, &val);
bnx2x_cl22_write(bp, phy, 0x18, val | (1<<10) | (1<<15));
/* This register opens the gate for the UMAC despite its name */
REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + params->port*4, 1);
/*
* Maximum Frame Length (RW). Defines a 14-Bit maximum frame
* length used by the MAC receive logic to check frames.
*/
REG_WR(bp, umac_base + UMAC_REG_MAXFR, 0x2710);
}
/******************************************************************/
/* SFX7101 PHY SECTION */
/******************************************************************/
@ -9390,6 +9805,39 @@ static struct bnx2x_phy phy_84833 = {
.phy_specific_func = (phy_specific_func_t)NULL
};
static struct bnx2x_phy phy_54616s = {
.type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54616,
.addr = 0xff,
.def_md_devad = 0,
.flags = FLAGS_INIT_XGXS_FIRST,
.rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
.mdio_ctrl = 0,
.supported = (SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Full |
SUPPORTED_TP |
SUPPORTED_Autoneg |
SUPPORTED_Pause |
SUPPORTED_Asym_Pause),
.media_type = ETH_PHY_BASE_T,
.ver_addr = 0,
.req_flow_ctrl = 0,
.req_line_speed = 0,
.speed_cap_mask = 0,
/* req_duplex = */0,
/* rsrv = */0,
.config_init = (config_init_t)bnx2x_54616s_config_init,
.read_status = (read_status_t)bnx2x_54616s_read_status,
.link_reset = (link_reset_t)bnx2x_54616s_link_reset,
.config_loopback = (config_loopback_t)bnx2x_54616s_config_loopback,
.format_fw_ver = (format_fw_ver_t)NULL,
.hw_reset = (hw_reset_t)NULL,
.set_link_led = (set_link_led_t)bnx2x_54616s_set_link_led,
.phy_specific_func = (phy_specific_func_t)NULL
};
/*****************************************************************/
/* */
/* Populate the phy according. Main function: bnx2x_populate_phy */
@ -9630,6 +10078,9 @@ static int bnx2x_populate_ext_phy(struct bnx2x *bp,
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833:
*phy = phy_84833;
break;
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54616:
*phy = phy_54616s;
break;
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101:
*phy = phy_7101;
break;

View File

@ -5420,7 +5420,9 @@
#define EMAC_LED_OVERRIDE (1L<<0)
#define EMAC_LED_TRAFFIC (1L<<6)
#define EMAC_MDIO_COMM_COMMAND_ADDRESS (0L<<26)
#define EMAC_MDIO_COMM_COMMAND_READ_22 (2L<<26)
#define EMAC_MDIO_COMM_COMMAND_READ_45 (3L<<26)
#define EMAC_MDIO_COMM_COMMAND_WRITE_22 (1L<<26)
#define EMAC_MDIO_COMM_COMMAND_WRITE_45 (1L<<26)
#define EMAC_MDIO_COMM_DATA (0xffffL<<0)
#define EMAC_MDIO_COMM_START_BUSY (1L<<29)
@ -6737,6 +6739,16 @@ Theotherbitsarereservedandshouldbezero*/
#define DIGITAL5_ACTUAL_SPEED_TX_MASK 0x003f
/* 54616s */
#define MDIO_REG_INTR_STATUS 0x1a
#define MDIO_REG_INTR_MASK 0x1b
#define MDIO_REG_INTR_MASK_LINK_STATUS (0x1 << 1)
#define MDIO_REG_GPHY_SHADOW 0x1c
#define MDIO_REG_GPHY_SHADOW_LED_SEL2 (0x0e << 10)
#define MDIO_REG_GPHY_SHADOW_WR_ENA (0x1 << 15)
#define MDIO_REG_GPHY_SHADOW_AUTO_DET_MED (0x1e << 10)
#define MDIO_REG_GPHY_SHADOW_INVERT_FIB_SD (0x1 << 8)
#define IGU_FUNC_BASE 0x0400
#define IGU_ADDR_MSIX 0x0000