diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 1b854bf07b09fec79f6abfa5b58f70b6ef91d1be..d7a1a58de7669b3bfefbf062ccb0fd8d98ec60b9 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -130,6 +130,9 @@ static int gfar_remove(struct platform_device *pdev); static void free_skb_resources(struct gfar_private *priv); static void gfar_set_multi(struct net_device *dev); static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr); +static void gfar_configure_serdes(struct net_device *dev); +extern int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id, int regnum, u16 value); +extern int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum); #ifdef CONFIG_GFAR_NAPI static int gfar_poll(struct net_device *dev, int *budget); #endif @@ -451,6 +454,9 @@ static int init_phy(struct net_device *dev) phydev = phy_connect(dev, phy_id, &adjust_link, 0, interface); + if (interface == PHY_INTERFACE_MODE_SGMII) + gfar_configure_serdes(dev); + if (IS_ERR(phydev)) { printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); return PTR_ERR(phydev); @@ -465,6 +471,27 @@ static int init_phy(struct net_device *dev) return 0; } +static void gfar_configure_serdes(struct net_device *dev) +{ + struct gfar_private *priv = netdev_priv(dev); + struct gfar_mii __iomem *regs = + (void __iomem *)&priv->regs->gfar_mii_regs; + + /* Initialise TBI i/f to communicate with serdes (lynx phy) */ + + /* Single clk mode, mii mode off(for aerdes communication) */ + gfar_local_mdio_write(regs, TBIPA_VALUE, MII_TBICON, TBICON_CLK_SELECT); + + /* Supported pause and full-duplex, no half-duplex */ + gfar_local_mdio_write(regs, TBIPA_VALUE, MII_ADVERTISE, + ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE | + ADVERTISE_1000XPSE_ASYM); + + /* ANEG enable, restart ANEG, full duplex mode, speed[1] set */ + gfar_local_mdio_write(regs, TBIPA_VALUE, MII_BMCR, BMCR_ANENABLE | + BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000); +} + static void init_registers(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 39e9e321fcbc1d2c2e4c0bd9fd294a7bccfea33c..d8e779c102faf3becd6c281cab68cee44f8ea9b6 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -136,6 +136,12 @@ extern const char gfar_driver_version[]; #define MIIMCFG_RESET 0x80000000 #define MIIMIND_BUSY 0x00000001 +/* TBI register addresses */ +#define MII_TBICON 0x11 + +/* TBICON register bit fields */ +#define TBICON_CLK_SELECT 0x0020 + /* MAC register bits */ #define MACCFG1_SOFT_RESET 0x80000000 #define MACCFG1_RESET_RX_MC 0x00080000 diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c index bcc6b82f4a33b6a9385ef66197a556fdf3b82149..5dd34a1a7b899217eaabdb8b4abdb8985695684c 100644 --- a/drivers/net/gianfar_mii.c +++ b/drivers/net/gianfar_mii.c @@ -43,13 +43,18 @@ #include "gianfar.h" #include "gianfar_mii.h" -/* Write value to the PHY at mii_id at register regnum, - * on the bus, waiting until the write is done before returning. - * All PHY configuration is done through the TSEC1 MIIM regs */ -int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) +/* + * Write value to the PHY at mii_id at register regnum, + * on the bus attached to the local interface, which may be different from the + * generic mdio bus (tied to a single interface), waiting until the write is + * done before returning. This is helpful in programming interfaces like + * the TBI which control interfaces like onchip SERDES and are always tied to + * the local mdio pins, which may not be the same as system mdio bus, used for + * controlling the external PHYs, for example. + */ +int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id, + int regnum, u16 value) { - struct gfar_mii __iomem *regs = (void __iomem *)bus->priv; - /* Set the PHY address and the register address we want to write */ gfar_write(®s->miimadd, (mii_id << 8) | regnum); @@ -63,12 +68,19 @@ int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) return 0; } -/* Read the bus for PHY at addr mii_id, register regnum, and - * return the value. Clears miimcom first. All PHY - * configuration has to be done through the TSEC1 MIIM regs */ -int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +/* + * Read the bus for PHY at addr mii_id, register regnum, and + * return the value. Clears miimcom first. All PHY operation + * done on the bus attached to the local interface, + * which may be different from the generic mdio bus + * This is helpful in programming interfaces like + * the TBI which, inturn, control interfaces like onchip SERDES + * and are always tied to the local mdio pins, which may not be the + * same as system mdio bus, used for controlling the external PHYs, for eg. + */ +int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum) + { - struct gfar_mii __iomem *regs = (void __iomem *)bus->priv; u16 value; /* Set the PHY address and the register address we want to read */ @@ -88,6 +100,27 @@ int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum) return value; } +/* Write value to the PHY at mii_id at register regnum, + * on the bus, waiting until the write is done before returning. + * All PHY configuration is done through the TSEC1 MIIM regs */ +int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) +{ + struct gfar_mii __iomem *regs = (void __iomem *)bus->priv; + + /* Write to the local MII regs */ + return(gfar_local_mdio_write(regs, mii_id, regnum, value)); +} + +/* Read the bus for PHY at addr mii_id, register regnum, and + * return the value. Clears miimcom first. All PHY + * configuration has to be done through the TSEC1 MIIM regs */ +int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct gfar_mii __iomem *regs = (void __iomem *)bus->priv; + + /* Read the local MII regs */ + return(gfar_local_mdio_read(regs, mii_id, regnum)); +} /* Reset the MIIM registers, and wait for the bus to free */ int gfar_mdio_reset(struct mii_bus *bus)