* (c) 2004-2005, Eugene Surovegin <ebs@ebshome.net>
*
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/ocp.h>
+#include "ibm_emac_core.h"
#include "ibm_emac_phy.h"
static inline int phy_read(struct mii_phy *phy, int reg)
phy->mdio_write(phy->dev, phy->address, reg, val);
}
-int mii_reset_phy(struct mii_phy *phy)
+/*
+ * polls MII_BMCR until BMCR_RESET bit clears or operation times out.
+ *
+ * returns:
+ * >= 0 => success, value in BMCR returned to caller
+ * -EBUSY => failure, RESET bit never cleared
+ * otherwise => failure, lower level PHY read failed
+ */
+static int mii_spin_reset_complete(struct mii_phy *phy)
{
int val;
int limit = 10000;
+ while (limit--) {
+ val = phy_read(phy, MII_BMCR);
+ if (val >= 0 && !(val & BMCR_RESET))
+ return val; /* success */
+ udelay(10);
+ }
+ if (val & BMCR_RESET)
+ val = -EBUSY;
+
+ if (net_ratelimit())
+ printk(KERN_ERR "emac%d: PHY reset timeout (%d)\n",
+ ((struct ocp_enet_private *)phy->dev->priv)->def->index,
+ val);
+ return val;
+}
+
+int mii_reset_phy(struct mii_phy *phy)
+{
+ int val;
+
val = phy_read(phy, MII_BMCR);
val &= ~BMCR_ISOLATE;
val |= BMCR_RESET;
udelay(300);
- while (limit--) {
- val = phy_read(phy, MII_BMCR);
- if (val >= 0 && (val & BMCR_RESET) == 0)
- break;
- udelay(10);
- }
- if ((val & BMCR_ISOLATE) && limit > 0)
+ val = mii_spin_reset_complete(phy);
+ if (val >= 0 && (val & BMCR_ISOLATE))
phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
- return limit <= 0;
+ return val < 0;
}
static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
}
/* Start/Restart aneg */
- ctl = phy_read(phy, MII_BMCR);
- ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ /* on some PHYs (e.g. National DP83843) a write to MII_ADVERTISE
+ * causes BMCR_RESET to be set on the next read of MII_BMCR, which
+ * if not checked for causes the PHY to be reset below */
+ ctl = mii_spin_reset_complete(phy);
+ if (ctl < 0)
+ return ctl;
+
+ ctl |= BMCR_ANENABLE | BMCR_ANRESTART;
phy_write(phy, MII_BMCR, ctl);
return 0;
phy->duplex = fd;
phy->pause = phy->asym_pause = 0;
+ /* First reset the PHY */
+ mii_reset_phy(phy);
+
ctl = phy_read(phy, MII_BMCR);
if (ctl < 0)
return ctl;
- ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_ANENABLE);
-
- /* First reset the PHY */
- phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
+ ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_ANENABLE | BMCR_SPEED1000);
/* Select speed & duplex */
switch (speed) {
};
/* CIS8201 */
+#define MII_CIS8201_10BTCSR 0x16
+#define TENBTCSR_ECHO_DISABLE 0x2000
#define MII_CIS8201_EPCR 0x17
#define EPCR_MODE_MASK 0x3000
#define EPCR_GMII_MODE 0x0000
#define EPCR_RGMII_MODE 0x1000
#define EPCR_TBI_MODE 0x2000
#define EPCR_RTBI_MODE 0x3000
+#define MII_CIS8201_ACSR 0x1c
+#define ACSR_PIN_PRIO_SELECT 0x0004
static int cis8201_init(struct mii_phy *phy)
{
}
phy_write(phy, MII_CIS8201_EPCR, epcr);
+
+ /* MII regs override strap pins */
+ phy_write(phy, MII_CIS8201_ACSR,
+ phy_read(phy, MII_CIS8201_ACSR) | ACSR_PIN_PRIO_SELECT);
+
+ /* Disable TX_EN -> CRS echo mode, otherwise 10/HDX doesn't work */
+ phy_write(phy, MII_CIS8201_10BTCSR,
+ phy_read(phy, MII_CIS8201_10BTCSR) | TENBTCSR_ECHO_DISABLE);
return 0;
}
{
struct mii_phy_def *def;
int i;
- u32 id;
+ int id;
phy->autoneg = AUTONEG_DISABLE;
phy->advertising = 0;
/* Read ID and find matching entry */
id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2);
+ if (id < 0)
+ return -ENODEV;
for (i = 0; (def = mii_phy_table[i]) != NULL; i++)
if ((id & def->phy_id_mask) == def->phy_id)
break;