smsc911x: add external phy detection overrides
authorSteve Glendinning <steve.glendinning@smsc.com>
Tue, 27 Jan 2009 06:51:11 +0000 (06:51 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 1 Feb 2009 08:37:21 +0000 (00:37 -0800)
On LAN9115/LAN9117/LAN9215/LAN9217, external phys are supported.  These
are usually indicated by a hardware strap which sets an "external PHY
detected" bit in the HW_CFG register.

In some cases it is desirable to override this hardware strap and force
use of either the internal phy or an external PHY.  This patch adds
SMSC911X_FORCE_INTERNAL_PHY and SMSC911X_FORCE_EXTERNAL_PHY flags so a
platform can indicate this preference via its platform_data.

Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com>
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Tested-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/smsc911x.c
include/linux/smsc911x.h

index 6aa2b16..27d2d7c 100644 (file)
@@ -368,48 +368,53 @@ out:
        return reg;
 }
 
-/* Autodetects and initialises external phy for SMSC9115 and SMSC9117 flavors.
- * If something goes wrong, returns -ENODEV to revert back to internal phy.
- * Performed at initialisation only, so interrupts are enabled */
-static int smsc911x_phy_initialise_external(struct smsc911x_data *pdata)
+/* Switch to external phy. Assumes tx and rx are stopped. */
+static void smsc911x_phy_enable_external(struct smsc911x_data *pdata)
 {
        unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG);
 
-       /* External phy is requested, supported, and detected */
-       if (hwcfg & HW_CFG_EXT_PHY_DET_) {
+       /* Disable phy clocks to the MAC */
+       hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+       hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+       smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+       udelay(10);     /* Enough time for clocks to stop */
 
-               /* Switch to external phy. Assuming tx and rx are stopped
-                * because smsc911x_phy_initialise is called before
-                * smsc911x_rx_initialise and tx_initialise. */
+       /* Switch to external phy */
+       hwcfg |= HW_CFG_EXT_PHY_EN_;
+       smsc911x_reg_write(pdata, HW_CFG, hwcfg);
 
-               /* Disable phy clocks to the MAC */
-               hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
-               hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
-               smsc911x_reg_write(pdata, HW_CFG, hwcfg);
-               udelay(10);     /* Enough time for clocks to stop */
+       /* Enable phy clocks to the MAC */
+       hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+       hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+       smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+       udelay(10);     /* Enough time for clocks to restart */
 
-               /* Switch to external phy */
-               hwcfg |= HW_CFG_EXT_PHY_EN_;
-               smsc911x_reg_write(pdata, HW_CFG, hwcfg);
-
-               /* Enable phy clocks to the MAC */
-               hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
-               hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
-               smsc911x_reg_write(pdata, HW_CFG, hwcfg);
-               udelay(10);     /* Enough time for clocks to restart */
+       hwcfg |= HW_CFG_SMI_SEL_;
+       smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+}
 
-               hwcfg |= HW_CFG_SMI_SEL_;
-               smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+/* Autodetects and enables external phy if present on supported chips.
+ * autodetection can be overridden by specifying SMSC911X_FORCE_INTERNAL_PHY
+ * or SMSC911X_FORCE_EXTERNAL_PHY in the platform_data flags. */
+static void smsc911x_phy_initialise_external(struct smsc911x_data *pdata)
+{
+       unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG);
 
-               SMSC_TRACE(HW, "Successfully switched to external PHY");
+       if (pdata->config.flags & SMSC911X_FORCE_INTERNAL_PHY) {
+               SMSC_TRACE(HW, "Forcing internal PHY");
+               pdata->using_extphy = 0;
+       } else if (pdata->config.flags & SMSC911X_FORCE_EXTERNAL_PHY) {
+               SMSC_TRACE(HW, "Forcing external PHY");
+               smsc911x_phy_enable_external(pdata);
+               pdata->using_extphy = 1;
+       } else if (hwcfg & HW_CFG_EXT_PHY_DET_) {
+               SMSC_TRACE(HW, "HW_CFG EXT_PHY_DET set, using external PHY");
+               smsc911x_phy_enable_external(pdata);
                pdata->using_extphy = 1;
        } else {
-               SMSC_WARNING(HW, "No external PHY detected, "
-                       "Using internal PHY instead.");
-               /* Use internal phy */
-               return -ENODEV;
+               SMSC_TRACE(HW, "HW_CFG EXT_PHY_DET clear, using internal PHY");
+               pdata->using_extphy = 0;
        }
-       return 0;
 }
 
 /* Fetches a tx status out of the status fifo */
@@ -825,22 +830,18 @@ static int __devinit smsc911x_mii_init(struct platform_device *pdev,
 
        pdata->mii_bus->parent = &pdev->dev;
 
-       pdata->using_extphy = 0;
-
        switch (pdata->idrev & 0xFFFF0000) {
        case 0x01170000:
        case 0x01150000:
        case 0x117A0000:
        case 0x115A0000:
                /* External PHY supported, try to autodetect */
-               if (smsc911x_phy_initialise_external(pdata) < 0) {
-                       SMSC_TRACE(HW, "No external PHY detected, "
-                               "using internal PHY");
-               }
+               smsc911x_phy_initialise_external(pdata);
                break;
        default:
                SMSC_TRACE(HW, "External PHY is not supported, "
                        "using internal PHY");
+               pdata->using_extphy = 0;
                break;
        }
 
index 1cbf031..170c76b 100644 (file)
@@ -43,5 +43,7 @@ struct smsc911x_platform_config {
 /* Constants for flags */
 #define SMSC911X_USE_16BIT                     (BIT(0))
 #define SMSC911X_USE_32BIT                     (BIT(1))
+#define SMSC911X_FORCE_INTERNAL_PHY            (BIT(2))
+#define SMSC911X_FORCE_EXTERNAL_PHY            (BIT(3))
 
 #endif /* __LINUX_SMSC911X_H__ */