提交 64f6b64d 编写于 作者: S Stephen Hemminger 提交者: Jeff Garzik

[PATCH] skge: fiber support

Add support for older fiber versions of the SysKonnect board. These chipsets
use an internal PHY so they require special handling. The older sk98lin
driver already supported these
Signed-off-by: NStephen Hemminger <shemminger@osdl.org>
Signed-off-by: NJeff Garzik <jeff@garzik.org>
上级 46798c89
......@@ -58,6 +58,7 @@
#define TX_WATCHDOG (5 * HZ)
#define NAPI_WEIGHT 64
#define BLINK_MS 250
#define LINK_HZ (HZ/2)
MODULE_DESCRIPTION("SysKonnect Gigabit Ethernet driver");
MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
......@@ -605,7 +606,12 @@ static void skge_led(struct skge_port *skge, enum led_mode mode)
if (hw->chip_id == CHIP_ID_GENESIS) {
switch (mode) {
case LED_MODE_OFF:
if (hw->phy_type == SK_PHY_BCOM)
xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_OFF);
else {
skge_write32(hw, SK_REG(port, TX_LED_VAL), 0);
skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_T_OFF);
}
skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
skge_write32(hw, SK_REG(port, RX_LED_VAL), 0);
skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_T_OFF);
......@@ -625,8 +631,14 @@ static void skge_led(struct skge_port *skge, enum led_mode mode)
skge_write32(hw, SK_REG(port, RX_LED_VAL), 100);
skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_START);
if (hw->phy_type == SK_PHY_BCOM)
xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_ON);
break;
else {
skge_write8(hw, SK_REG(port, TX_LED_TST), LED_T_ON);
skge_write32(hw, SK_REG(port, TX_LED_VAL), 100);
skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_START);
}
}
} else {
switch (mode) {
......@@ -879,6 +891,9 @@ static int __xm_phy_read(struct skge_hw *hw, int port, u16 reg, u16 *val)
xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr);
*val = xm_read16(hw, port, XM_PHY_DATA);
if (hw->phy_type == SK_PHY_XMAC)
goto ready;
for (i = 0; i < PHY_RETRIES; i++) {
if (xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_RDY)
goto ready;
......@@ -965,6 +980,7 @@ static void genesis_reset(struct skge_hw *hw, int port)
xm_write16(hw, port, XM_RX_CMD, 0); /* reset RX CMD Reg */
/* disable Broadcom PHY IRQ */
if (hw->phy_type == SK_PHY_BCOM)
xm_write16(hw, port, PHY_BCOM_INT_MASK, 0xffff);
xm_outhash(hw, port, XM_HSM, zero);
......@@ -1000,18 +1016,24 @@ static void bcom_check_link(struct skge_hw *hw, int port)
if (netif_carrier_ok(dev))
skge_link_down(skge);
} else {
if (skge->autoneg == AUTONEG_ENABLE &&
(status & PHY_ST_AN_OVER)) {
u16 lpa = xm_phy_read(hw, port, PHY_BCOM_AUNE_LP);
u16 aux = xm_phy_read(hw, port, PHY_BCOM_AUX_STAT);
return;
}
if (skge->autoneg == AUTONEG_ENABLE) {
u16 lpa, aux;
if (!(status & PHY_ST_AN_OVER))
return;
lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
if (lpa & PHY_B_AN_RF) {
printk(KERN_NOTICE PFX "%s: remote fault\n",
dev->name);
return;
}
aux = xm_phy_read(hw, port, PHY_BCOM_AUX_STAT);
/* Check Duplex mismatch */
switch (aux & PHY_B_AS_AN_RES_MSK) {
case PHY_B_RES_1000FD:
......@@ -1041,19 +1063,17 @@ static void bcom_check_link(struct skge_hw *hw, int port)
default:
skge->flow_control = FLOW_MODE_NONE;
}
skge->speed = SPEED_1000;
}
if (!netif_carrier_ok(dev))
genesis_link_up(skge);
}
}
/* Broadcom 5400 only supports giagabit! SysKonnect did not put an additional
* Phy on for 100 or 10Mbit operation
*/
static void bcom_phy_init(struct skge_port *skge, int jumbo)
static void bcom_phy_init(struct skge_port *skge)
{
struct skge_hw *hw = skge->hw;
int port = skge->port;
......@@ -1144,7 +1164,7 @@ static void bcom_phy_init(struct skge_port *skge, int jumbo)
phy_pause_map[skge->flow_control] | PHY_AN_CSMA);
/* Handle Jumbo frames */
if (jumbo) {
if (hw->dev[port]->mtu > ETH_DATA_LEN) {
xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL,
PHY_B_AC_TX_TST | PHY_B_AC_LONG_PACK);
......@@ -1157,8 +1177,154 @@ static void bcom_phy_init(struct skge_port *skge, int jumbo)
/* Use link status change interrupt */
xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
}
bcom_check_link(hw, port);
static void xm_phy_init(struct skge_port *skge)
{
struct skge_hw *hw = skge->hw;
int port = skge->port;
u16 ctrl = 0;
if (skge->autoneg == AUTONEG_ENABLE) {
if (skge->advertising & ADVERTISED_1000baseT_Half)
ctrl |= PHY_X_AN_HD;
if (skge->advertising & ADVERTISED_1000baseT_Full)
ctrl |= PHY_X_AN_FD;
switch(skge->flow_control) {
case FLOW_MODE_NONE:
ctrl |= PHY_X_P_NO_PAUSE;
break;
case FLOW_MODE_LOC_SEND:
ctrl |= PHY_X_P_ASYM_MD;
break;
case FLOW_MODE_SYMMETRIC:
ctrl |= PHY_X_P_BOTH_MD;
break;
}
xm_phy_write(hw, port, PHY_XMAC_AUNE_ADV, ctrl);
/* Restart Auto-negotiation */
ctrl = PHY_CT_ANE | PHY_CT_RE_CFG;
} else {
/* Set DuplexMode in Config register */
if (skge->duplex == DUPLEX_FULL)
ctrl |= PHY_CT_DUP_MD;
/*
* Do NOT enable Auto-negotiation here. This would hold
* the link down because no IDLEs are transmitted
*/
}
xm_phy_write(hw, port, PHY_XMAC_CTRL, ctrl);
/* Poll PHY for status changes */
schedule_delayed_work(&skge->link_thread, LINK_HZ);
}
static void xm_check_link(struct net_device *dev)
{
struct skge_port *skge = netdev_priv(dev);
struct skge_hw *hw = skge->hw;
int port = skge->port;
u16 status;
/* read twice because of latch */
(void) xm_phy_read(hw, port, PHY_XMAC_STAT);
status = xm_phy_read(hw, port, PHY_XMAC_STAT);
if ((status & PHY_ST_LSYNC) == 0) {
u16 cmd = xm_read16(hw, port, XM_MMU_CMD);
cmd &= ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX);
xm_write16(hw, port, XM_MMU_CMD, cmd);
/* dummy read to ensure writing */
(void) xm_read16(hw, port, XM_MMU_CMD);
if (netif_carrier_ok(dev))
skge_link_down(skge);
return;
}
if (skge->autoneg == AUTONEG_ENABLE) {
u16 lpa, res;
if (!(status & PHY_ST_AN_OVER))
return;
lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
if (lpa & PHY_B_AN_RF) {
printk(KERN_NOTICE PFX "%s: remote fault\n",
dev->name);
return;
}
res = xm_phy_read(hw, port, PHY_XMAC_RES_ABI);
/* Check Duplex mismatch */
switch (res & (PHY_X_RS_HD | PHY_X_RS_FD)) {
case PHY_X_RS_FD:
skge->duplex = DUPLEX_FULL;
break;
case PHY_X_RS_HD:
skge->duplex = DUPLEX_HALF;
break;
default:
printk(KERN_NOTICE PFX "%s: duplex mismatch\n",
dev->name);
return;
}
/* We are using IEEE 802.3z/D5.0 Table 37-4 */
if (lpa & PHY_X_P_SYM_MD)
skge->flow_control = FLOW_MODE_SYMMETRIC;
else if ((lpa & PHY_X_RS_PAUSE) == PHY_X_P_ASYM_MD)
skge->flow_control = FLOW_MODE_REM_SEND;
else if ((lpa & PHY_X_RS_PAUSE) == PHY_X_P_BOTH_MD)
skge->flow_control = FLOW_MODE_LOC_SEND;
else
skge->flow_control = FLOW_MODE_NONE;
skge->speed = SPEED_1000;
}
if (!netif_carrier_ok(dev))
genesis_link_up(skge);
}
/* Poll to check for link coming up.
* Since internal PHY is wired to a level triggered pin, can't
* get an interrupt when carrier is detected.
*/
static void xm_link_timer(void *arg)
{
struct net_device *dev = arg;
struct skge_port *skge = netdev_priv(arg);
struct skge_hw *hw = skge->hw;
int port = skge->port;
if (!netif_running(dev))
return;
if (netif_carrier_ok(dev)) {
xm_read16(hw, port, XM_ISRC);
if (!(xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS))
goto nochange;
} else {
if (xm_read32(hw, port, XM_GP_PORT) & XM_GP_INP_ASS)
goto nochange;
xm_read16(hw, port, XM_ISRC);
if (xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS)
goto nochange;
}
mutex_lock(&hw->phy_mutex);
xm_check_link(dev);
mutex_unlock(&hw->phy_mutex);
nochange:
schedule_delayed_work(&skge->link_thread, LINK_HZ);
}
static void genesis_mac_init(struct skge_hw *hw, int port)
......@@ -1189,6 +1355,7 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
* namely for the 1000baseTX cards that use the XMAC's
* GMII mode.
*/
if (hw->phy_type != SK_PHY_XMAC) {
/* Take external Phy out of reset */
r = skge_read32(hw, B2_GP_IO);
if (port == 0)
......@@ -1198,11 +1365,19 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
skge_write32(hw, B2_GP_IO, r);
/* Enable GMII interface */
xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD);
}
bcom_phy_init(skge, jumbo);
switch(hw->phy_type) {
case SK_PHY_XMAC:
xm_phy_init(skge);
break;
case SK_PHY_BCOM:
bcom_phy_init(skge);
bcom_check_link(hw, port);
}
/* Set Station Address */
xm_outaddr(hw, port, XM_SA, dev->dev_addr);
......@@ -1335,6 +1510,7 @@ static void genesis_stop(struct skge_port *skge)
skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_SET_MAC_RST);
/* For external PHYs there must be special handling */
if (hw->phy_type != SK_PHY_XMAC) {
reg = skge_read32(hw, B2_GP_IO);
if (port == 0) {
reg |= GP_DIR_0;
......@@ -1345,6 +1521,7 @@ static void genesis_stop(struct skge_port *skge)
}
skge_write32(hw, B2_GP_IO, reg);
skge_read32(hw, B2_GP_IO);
}
xm_write16(hw, port, XM_MMU_CMD,
xm_read16(hw, port, XM_MMU_CMD)
......@@ -1406,7 +1583,7 @@ static void genesis_link_up(struct skge_port *skge)
struct skge_hw *hw = skge->hw;
int port = skge->port;
u16 cmd;
u32 mode, msk;
u32 mode;
cmd = xm_read16(hw, port, XM_MMU_CMD);
......@@ -1454,27 +1631,24 @@ static void genesis_link_up(struct skge_port *skge)
}
xm_write32(hw, port, XM_MODE, mode);
msk = XM_DEF_MSK;
/* disable GP0 interrupt bit for external Phy */
msk |= XM_IS_INP_ASS;
xm_write16(hw, port, XM_IMSK, msk);
xm_write16(hw, port, XM_IMSK, XM_DEF_MSK);
xm_read16(hw, port, XM_ISRC);
/* get MMU Command Reg. */
cmd = xm_read16(hw, port, XM_MMU_CMD);
if (skge->duplex == DUPLEX_FULL)
if (hw->phy_type != SK_PHY_XMAC && skge->duplex == DUPLEX_FULL)
cmd |= XM_MMU_GMII_FD;
/*
* Workaround BCOM Errata (#10523) for all BCom Phys
* Enable Power Management after link up
*/
if (hw->phy_type == SK_PHY_BCOM) {
xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL,
xm_phy_read(hw, port, PHY_BCOM_AUX_CTRL)
& ~PHY_B_AC_DIS_PM);
xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
}
/* enable Rx/Tx */
xm_write16(hw, port, XM_MMU_CMD,
......@@ -2240,6 +2414,8 @@ static int skge_down(struct net_device *dev)
printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
netif_stop_queue(dev);
if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC)
cancel_rearming_delayed_work(&skge->link_thread);
skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
if (hw->chip_id == CHIP_ID_GENESIS)
......@@ -2862,7 +3038,7 @@ static void skge_extirq(void *arg)
if (netif_running(dev)) {
if (hw->chip_id != CHIP_ID_GENESIS)
yukon_phy_intr(skge);
else
else if (hw->phy_type == SK_PHY_BCOM)
bcom_phy_intr(skge);
}
}
......@@ -3014,7 +3190,7 @@ static int skge_reset(struct skge_hw *hw)
{
u32 reg;
u16 ctst, pci_status;
u8 t8, mac_cfg, pmd_type, phy_type;
u8 t8, mac_cfg, pmd_type;
int i;
ctst = skge_read16(hw, B0_CTST);
......@@ -3038,19 +3214,22 @@ static int skge_reset(struct skge_hw *hw)
ctst & (CS_CLK_RUN_HOT|CS_CLK_RUN_RST|CS_CLK_RUN_ENA));
hw->chip_id = skge_read8(hw, B2_CHIP_ID);
phy_type = skge_read8(hw, B2_E_1) & 0xf;
hw->phy_type = skge_read8(hw, B2_E_1) & 0xf;
pmd_type = skge_read8(hw, B2_PMD_TYP);
hw->copper = (pmd_type == 'T' || pmd_type == '1');
switch (hw->chip_id) {
case CHIP_ID_GENESIS:
switch (phy_type) {
switch (hw->phy_type) {
case SK_PHY_XMAC:
hw->phy_addr = PHY_ADDR_XMAC;
break;
case SK_PHY_BCOM:
hw->phy_addr = PHY_ADDR_BCOM;
break;
default:
printk(KERN_ERR PFX "%s: unsupported phy type 0x%x\n",
pci_name(hw->pdev), phy_type);
pci_name(hw->pdev), hw->phy_type);
return -EOPNOTSUPP;
}
break;
......@@ -3058,7 +3237,7 @@ static int skge_reset(struct skge_hw *hw)
case CHIP_ID_YUKON:
case CHIP_ID_YUKON_LITE:
case CHIP_ID_YUKON_LP:
if (phy_type < SK_PHY_MARV_COPPER && pmd_type != 'S')
if (hw->phy_type < SK_PHY_MARV_COPPER && pmd_type != 'S')
hw->copper = 1;
hw->phy_addr = PHY_ADDR_MARV;
......@@ -3089,10 +3268,13 @@ static int skge_reset(struct skge_hw *hw)
else
hw->ram_size = t8 * 4096;
hw->intr_mask = IS_HW_ERR | IS_EXT_REG | IS_PORT_1;
hw->intr_mask = IS_HW_ERR | IS_PORT_1;
if (hw->ports > 1)
hw->intr_mask |= IS_PORT_2;
if (!(hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC))
hw->intr_mask |= IS_EXT_REG;
if (hw->chip_id == CHIP_ID_GENESIS)
genesis_init(hw);
else {
......@@ -3226,6 +3408,9 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
skge->port = port;
/* Only used for Genesis XMAC */
INIT_WORK(&skge->link_thread, xm_link_timer, dev);
if (hw->chip_id != CHIP_ID_GENESIS) {
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
skge->rx_csum = 1;
......
......@@ -1097,13 +1097,36 @@ enum {
/* Pause Bits (PHY_X_AN_PAUSE and PHY_X_RS_PAUSE) encoding */
enum {
PHY_X_P_NO_PAUSE = 0<<7,/* Bit 8..7: no Pause Mode */
PHY_X_P_NO_PAUSE= 0<<7,/* Bit 8..7: no Pause Mode */
PHY_X_P_SYM_MD = 1<<7, /* Bit 8..7: symmetric Pause Mode */
PHY_X_P_ASYM_MD = 2<<7,/* Bit 8..7: asymmetric Pause Mode */
PHY_X_P_BOTH_MD = 3<<7,/* Bit 8..7: both Pause Mode */
};
/***** PHY_XMAC_EXT_STAT 16 bit r/w Extended Status Register *****/
enum {
PHY_X_EX_FD = 1<<15, /* Bit 15: Device Supports Full Duplex */
PHY_X_EX_HD = 1<<14, /* Bit 14: Device Supports Half Duplex */
};
/***** PHY_XMAC_RES_ABI 16 bit r/o PHY Resolved Ability *****/
enum {
PHY_X_RS_PAUSE = 3<<7, /* Bit 8..7: selected Pause Mode */
PHY_X_RS_HD = 1<<6, /* Bit 6: Half Duplex Mode selected */
PHY_X_RS_FD = 1<<5, /* Bit 5: Full Duplex Mode selected */
PHY_X_RS_ABLMIS = 1<<4, /* Bit 4: duplex or pause cap mismatch */
PHY_X_RS_PAUMIS = 1<<3, /* Bit 3: pause capability mismatch */
};
/* Remote Fault Bits (PHY_X_AN_RFB) encoding */
enum {
X_RFB_OK = 0<<12,/* Bit 13..12 No errors, Link OK */
X_RFB_LF = 1<<12,/* Bit 13..12 Link Failure */
X_RFB_OFF = 2<<12,/* Bit 13..12 Offline */
X_RFB_AN_ERR = 3<<12,/* Bit 13..12 Auto-Negotiation Error */
};
/* Broadcom-Specific */
/***** PHY_BCOM_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/
enum {
......@@ -2172,9 +2195,7 @@ enum {
XM_IS_RX_COMP = 1<<0, /* Bit 0: Frame Rx Complete */
};
#define XM_DEF_MSK (~(XM_IS_INP_ASS | XM_IS_LIPA_RC | XM_IS_RX_PAGE | \
XM_IS_AND | XM_IS_RXC_OV | XM_IS_TXC_OV | \
XM_IS_RXF_OV | XM_IS_TXF_UR))
#define XM_DEF_MSK (~(XM_IS_RXC_OV | XM_IS_TXC_OV | XM_IS_RXF_OV | XM_IS_TXF_UR))
/* XM_HW_CFG 16 bit r/w Hardware Config Register */
......@@ -2396,6 +2417,7 @@ struct skge_hw {
u8 chip_rev;
u8 copper;
u8 ports;
u8 phy_type;
u32 ram_size;
u32 ram_offset;
......@@ -2422,6 +2444,7 @@ struct skge_port {
struct net_device_stats net_stats;
struct work_struct link_thread;
u8 rx_csum;
u8 blink_on;
u8 flow_control;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册