diff options
author | Steve Glendinning <steve.glendinning@smsc.com> | 2009-01-27 01:51:11 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-02-01 03:37:21 -0500 |
commit | d23f028a4ddce8b783c212bfe911d1d307ff3617 (patch) | |
tree | 95166b84758afba8fdbbf4ce2fdb4dd3b5a4ccac | |
parent | e81259b4f7c69a71d92216ba551731fb7027bcbe (diff) |
smsc911x: add external phy detection overrides
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>
-rw-r--r-- | drivers/net/smsc911x.c | 75 | ||||
-rw-r--r-- | include/linux/smsc911x.h | 2 |
2 files changed, 40 insertions, 37 deletions
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index 6aa2b165de6..27d2d7c4551 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c | |||
@@ -368,48 +368,53 @@ out: | |||
368 | return reg; | 368 | return reg; |
369 | } | 369 | } |
370 | 370 | ||
371 | /* Autodetects and initialises external phy for SMSC9115 and SMSC9117 flavors. | 371 | /* Switch to external phy. Assumes tx and rx are stopped. */ |
372 | * If something goes wrong, returns -ENODEV to revert back to internal phy. | 372 | static void smsc911x_phy_enable_external(struct smsc911x_data *pdata) |
373 | * Performed at initialisation only, so interrupts are enabled */ | ||
374 | static int smsc911x_phy_initialise_external(struct smsc911x_data *pdata) | ||
375 | { | 373 | { |
376 | unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG); | 374 | unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG); |
377 | 375 | ||
378 | /* External phy is requested, supported, and detected */ | 376 | /* Disable phy clocks to the MAC */ |
379 | if (hwcfg & HW_CFG_EXT_PHY_DET_) { | 377 | hwcfg &= (~HW_CFG_PHY_CLK_SEL_); |
378 | hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_; | ||
379 | smsc911x_reg_write(pdata, HW_CFG, hwcfg); | ||
380 | udelay(10); /* Enough time for clocks to stop */ | ||
380 | 381 | ||
381 | /* Switch to external phy. Assuming tx and rx are stopped | 382 | /* Switch to external phy */ |
382 | * because smsc911x_phy_initialise is called before | 383 | hwcfg |= HW_CFG_EXT_PHY_EN_; |
383 | * smsc911x_rx_initialise and tx_initialise. */ | 384 | smsc911x_reg_write(pdata, HW_CFG, hwcfg); |
384 | 385 | ||
385 | /* Disable phy clocks to the MAC */ | 386 | /* Enable phy clocks to the MAC */ |
386 | hwcfg &= (~HW_CFG_PHY_CLK_SEL_); | 387 | hwcfg &= (~HW_CFG_PHY_CLK_SEL_); |
387 | hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_; | 388 | hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_; |
388 | smsc911x_reg_write(pdata, HW_CFG, hwcfg); | 389 | smsc911x_reg_write(pdata, HW_CFG, hwcfg); |
389 | udelay(10); /* Enough time for clocks to stop */ | 390 | udelay(10); /* Enough time for clocks to restart */ |
390 | 391 | ||
391 | /* Switch to external phy */ | 392 | hwcfg |= HW_CFG_SMI_SEL_; |
392 | hwcfg |= HW_CFG_EXT_PHY_EN_; | 393 | smsc911x_reg_write(pdata, HW_CFG, hwcfg); |
393 | smsc911x_reg_write(pdata, HW_CFG, hwcfg); | 394 | } |
394 | |||
395 | /* Enable phy clocks to the MAC */ | ||
396 | hwcfg &= (~HW_CFG_PHY_CLK_SEL_); | ||
397 | hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_; | ||
398 | smsc911x_reg_write(pdata, HW_CFG, hwcfg); | ||
399 | udelay(10); /* Enough time for clocks to restart */ | ||
400 | 395 | ||
401 | hwcfg |= HW_CFG_SMI_SEL_; | 396 | /* Autodetects and enables external phy if present on supported chips. |
402 | smsc911x_reg_write(pdata, HW_CFG, hwcfg); | 397 | * autodetection can be overridden by specifying SMSC911X_FORCE_INTERNAL_PHY |
398 | * or SMSC911X_FORCE_EXTERNAL_PHY in the platform_data flags. */ | ||
399 | static void smsc911x_phy_initialise_external(struct smsc911x_data *pdata) | ||
400 | { | ||
401 | unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG); | ||
403 | 402 | ||
404 | SMSC_TRACE(HW, "Successfully switched to external PHY"); | 403 | if (pdata->config.flags & SMSC911X_FORCE_INTERNAL_PHY) { |
404 | SMSC_TRACE(HW, "Forcing internal PHY"); | ||
405 | pdata->using_extphy = 0; | ||
406 | } else if (pdata->config.flags & SMSC911X_FORCE_EXTERNAL_PHY) { | ||
407 | SMSC_TRACE(HW, "Forcing external PHY"); | ||
408 | smsc911x_phy_enable_external(pdata); | ||
409 | pdata->using_extphy = 1; | ||
410 | } else if (hwcfg & HW_CFG_EXT_PHY_DET_) { | ||
411 | SMSC_TRACE(HW, "HW_CFG EXT_PHY_DET set, using external PHY"); | ||
412 | smsc911x_phy_enable_external(pdata); | ||
405 | pdata->using_extphy = 1; | 413 | pdata->using_extphy = 1; |
406 | } else { | 414 | } else { |
407 | SMSC_WARNING(HW, "No external PHY detected, " | 415 | SMSC_TRACE(HW, "HW_CFG EXT_PHY_DET clear, using internal PHY"); |
408 | "Using internal PHY instead."); | 416 | pdata->using_extphy = 0; |
409 | /* Use internal phy */ | ||
410 | return -ENODEV; | ||
411 | } | 417 | } |
412 | return 0; | ||
413 | } | 418 | } |
414 | 419 | ||
415 | /* Fetches a tx status out of the status fifo */ | 420 | /* Fetches a tx status out of the status fifo */ |
@@ -825,22 +830,18 @@ static int __devinit smsc911x_mii_init(struct platform_device *pdev, | |||
825 | 830 | ||
826 | pdata->mii_bus->parent = &pdev->dev; | 831 | pdata->mii_bus->parent = &pdev->dev; |
827 | 832 | ||
828 | pdata->using_extphy = 0; | ||
829 | |||
830 | switch (pdata->idrev & 0xFFFF0000) { | 833 | switch (pdata->idrev & 0xFFFF0000) { |
831 | case 0x01170000: | 834 | case 0x01170000: |
832 | case 0x01150000: | 835 | case 0x01150000: |
833 | case 0x117A0000: | 836 | case 0x117A0000: |
834 | case 0x115A0000: | 837 | case 0x115A0000: |
835 | /* External PHY supported, try to autodetect */ | 838 | /* External PHY supported, try to autodetect */ |
836 | if (smsc911x_phy_initialise_external(pdata) < 0) { | 839 | smsc911x_phy_initialise_external(pdata); |
837 | SMSC_TRACE(HW, "No external PHY detected, " | ||
838 | "using internal PHY"); | ||
839 | } | ||
840 | break; | 840 | break; |
841 | default: | 841 | default: |
842 | SMSC_TRACE(HW, "External PHY is not supported, " | 842 | SMSC_TRACE(HW, "External PHY is not supported, " |
843 | "using internal PHY"); | 843 | "using internal PHY"); |
844 | pdata->using_extphy = 0; | ||
844 | break; | 845 | break; |
845 | } | 846 | } |
846 | 847 | ||
diff --git a/include/linux/smsc911x.h b/include/linux/smsc911x.h index 1cbf0313add..170c76b8f7a 100644 --- a/include/linux/smsc911x.h +++ b/include/linux/smsc911x.h | |||
@@ -43,5 +43,7 @@ struct smsc911x_platform_config { | |||
43 | /* Constants for flags */ | 43 | /* Constants for flags */ |
44 | #define SMSC911X_USE_16BIT (BIT(0)) | 44 | #define SMSC911X_USE_16BIT (BIT(0)) |
45 | #define SMSC911X_USE_32BIT (BIT(1)) | 45 | #define SMSC911X_USE_32BIT (BIT(1)) |
46 | #define SMSC911X_FORCE_INTERNAL_PHY (BIT(2)) | ||
47 | #define SMSC911X_FORCE_EXTERNAL_PHY (BIT(3)) | ||
46 | 48 | ||
47 | #endif /* __LINUX_SMSC911X_H__ */ | 49 | #endif /* __LINUX_SMSC911X_H__ */ |