Most AMD pcnet chips support up to 32 external PHYs. This patch
introduces basic PHY selection/switching support, by adding two
new module parameters:
-maxphy: how many PHYs the card supports
-usephy: which phy to use instead of eeprom default
Maxphy is necessary in order to check the range of usephy and may
be overriden inside the module.
If only maxphy is present I've implemented an algorithm which checks
the link state on all PHYs and uses the one that has a link.
I tested this extensively on our 2700/01 FTX cards and works. I have
added a maxphy override for those cards to the driver as well.
The only drawback here is that I wasn't able to figure out how to
dynamically switch the PHY, so the whole switching process is done in
pcnet32_probe1 instead of open or when the link state changes. This
means that once the driver is loaded the PHY which was connected must
be used and no change is possible without physically resetting
(power off/on) the card.
Patch applies to 2.6.16-rc3 and depends on patch nr. 1 to completely
support AT 2700/01 FTX cards.
Signed-off-by: Philippe Seewer <[email protected]>
---
pcnet32.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 105 insertions(+), 2 deletions(-)
diff -uprN -X linux-2.6.16-rc3-vanilla/Documentation/dontdiff linux-2.6.16-rc3-vanilla/drivers/net/pcnet32.c linux-2.6.16-rc3/drivers/net/pcnet32.c
--- linux-2.6.16-rc3-vanilla/drivers/net/pcnet32.c 2006-02-17 16:38:44.000000000 +0100
+++ linux-2.6.16-rc3/drivers/net/pcnet32.c 2006-02-17 16:40:47.000000000 +0100
@@ -22,7 +22,7 @@
*************************************************************************/
#define DRV_NAME "pcnet32"
-#define DRV_VERSION "1.31d"
+#define DRV_VERSION "1.32"
#define DRV_RELDATE "17.Feb.2006"
#define PFX DRV_NAME ": "
@@ -140,6 +140,10 @@ static int options[MAX_UNITS];
static int full_duplex[MAX_UNITS];
static int homepna[MAX_UNITS];
+/* Options to switch PHY */
+static int maxphy[MAX_UNITS];
+static int usephy[MAX_UNITS];
+
/*
* Theory of Operation
*
@@ -267,6 +271,8 @@ static int homepna[MAX_UNITS];
* See Bugzilla 2669 and 4551.
* v1.31d 17 Nov 2006 Philippe Seewer Extended AT 2700/01 FX support
* to support FTX variants as well.
+ * v1.32 17 Nov 2006 Philippe Seewer Basic PHY switching support on
+ * module load.
*/
@@ -386,6 +392,8 @@ struct pcnet32_private {
struct timer_list watchdog_timer;
struct timer_list blink_timer;
u32 msg_enable; /* debug message level */
+ int maxphy; /* max PHYs supported by chip */
+ int usephy; /* which PHY to use */
};
static void pcnet32_probe_vlbus(void);
@@ -417,6 +425,7 @@ static void pcnet32_get_regs(struct net_
static void pcnet32_purge_tx_ring(struct net_device *dev);
static int pcnet32_alloc_ring(struct net_device *dev, char *name);
static void pcnet32_free_ring(struct net_device *dev);
+static void pcnet32_switch_phy(struct net_device *dev, int phy);
enum pci_flags_bit {
@@ -1336,6 +1345,20 @@ pcnet32_probe1(unsigned long ioaddr, int
lp->mii_if.mdio_read = mdio_read;
lp->mii_if.mdio_write = mdio_write;
+ lp->maxphy = 1;
+ if ((cards_found >= MAX_UNITS) || (maxphy[cards_found]))
+ lp->maxphy = maxphy[cards_found];
+
+ lp->usephy = 0;
+ if ((cards_found >= MAX_UNITS) || (usephy[cards_found]))
+ lp->usephy = usephy[cards_found];
+
+ if (lp->usephy > lp->maxphy) {
+ printk(KERN_ERR PFX " usephy paramater out of range! ignoring!\n");
+ lp->usephy = 0;
+ }
+
+
if (fdx && !(lp->options & PCNET32_PORT_ASEL) &&
((cards_found>=MAX_UNITS) || full_duplex[cards_found]))
lp->options |= PCNET32_PORT_FD;
@@ -1358,6 +1381,15 @@ pcnet32_probe1(unsigned long ioaddr, int
&& dev->dev_addr[2] == 0x75)
lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI;
+ /*
+ * Test for specific cards to set maxphy count
+ */
+ if (lp->pci_dev->subsystem_vendor == PCI_VENDOR_ID_AT &&
+ (lp->pci_dev->subsystem_device == PCI_SUBDEVICE_ID_AT_2700FTX ||
+ lp->pci_dev->subsystem_device == PCI_SUBDEVICE_ID_AT_2701FTX)) {
+ lp->maxphy = 2;
+ }
+
lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */
lp->init_block.tlen_rlen = le16_to_cpu(lp->tx_len_bits | lp->rx_len_bits);
for (i = 0; i < 6; i++)
@@ -1403,9 +1435,38 @@ pcnet32_probe1(unsigned long ioaddr, int
}
/* Set the mii phy_id so that we can query the link state */
- if (lp->mii)
+ if (lp->mii) {
lp->mii_if.phy_id = ((lp->a.read_bcr (ioaddr, 33)) >> 5) & 0x1f;
+ /* Multi PHY? */
+ if (lp->maxphy > 1) {
+ /* Autoswitch? */
+ if (lp->usephy == 0) {
+ /* backup */
+ lp->usephy = lp->mii_if.phy_id;
+
+ /* give preference to eeprom default if no link */
+ media = lp->mii_if.phy_id;
+
+ /* check link */
+ for (i = 1; i <= lp->maxphy; i++) {
+ lp->mii_if.phy_id = i;
+ if (mii_check_media(&lp->mii_if, 0, 1)) {
+ media = i;
+ /* give preference to eeprom default if link found */
+ if (i == lp->usephy)
+ break;
+ }
+ }
+
+ lp->mii_if.phy_id = media;
+ } else {
+ lp->mii_if.phy_id = lp->usephy;
+ }
+ pcnet32_switch_phy(dev, lp->mii_if.phy_id);
+ }
+ }
+
/*
* Override options:
* Allied Telesyn AT 2700/2701 FX are 100Mbit only and do not
@@ -1573,6 +1634,32 @@ static void pcnet32_free_ring(struct net
}
}
+static void pcnet32_switch_phy(struct net_device *dev, int phy)
+{
+ struct pcnet32_private *lp = dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ int i;
+
+ /* Isolate all unused phy's */
+ for (i = 1; i < 3; i++) {
+ if (i != phy)
+ mdio_write(dev, 1, MII_BMCR, BMCR_ISOLATE);
+ }
+
+ /* Make sure used phy is not isolated */
+ mdio_write(dev, phy, MII_BMCR,
+ mdio_read(dev, phy, MII_BMCR) & ~BMCR_ISOLATE);
+
+ /* Rearead mii regs */
+ for (i = 0; i < 5; i++) {
+ mdio_read(dev, phy, i);
+ mdio_read(dev, phy, i);
+ }
+
+ /* Store PHY */
+ lp->a.write_bcr(ioaddr, 33, ((phy & 0x1f) << 5) | MII_BMCR);
+}
+
static int
pcnet32_open(struct net_device *dev)
@@ -1654,6 +1741,16 @@ pcnet32_open(struct net_device *dev)
if (lp->options & PCNET32_PORT_100)
val |= 0x08;
lp->a.write_bcr (ioaddr, 32, val);
+ /*
+ * AT 2700 FTX seems broken somehow, when disabling the
+ * DANAS the mii registers for the fiber poort need to be
+ * set.
+ */
+ if (lp->pci_dev->subsystem_vendor == PCI_VENDOR_ID_AT &&
+ lp->pci_dev->subsystem_device == PCI_SUBDEVICE_ID_AT_2700FTX &&
+ lp->mii_if.phy_id == 1) {
+ mdio_write(dev, lp->mii_if.phy_id, MII_BMCR, 0x2100);
+ }
} else {
if (lp->options & PCNET32_PORT_ASEL) {
lp->a.write_bcr(ioaddr, 32,
@@ -2505,6 +2602,12 @@ MODULE_PARM_DESC(full_duplex, DRV_NAME "
module_param_array(homepna, int, NULL, 0);
MODULE_PARM_DESC(homepna, DRV_NAME " mode for 79C978 cards (1 for HomePNA, 0 for Ethernet, default Ethernet");
+/* Module parameters for PHY selection support */
+module_param_array(maxphy, int, NULL, 0);
+MODULE_PARM_DESC(maxphy, DRV_NAME " max PHYs the card supports");
+module_param_array(usephy, int, NULL, 0);
+MODULE_PARM_DESC(usephy, DRV_NAME " use specified PHY port instead of default");
+
MODULE_AUTHOR("Thomas Bogendoerfer");
MODULE_DESCRIPTION("Driver for PCnet32 and PCnetPCI based ethercards");
MODULE_LICENSE("GPL");
Seewer Philippe wrote:
> Most AMD pcnet chips support up to 32 external PHYs. This patch
> introduces basic PHY selection/switching support, by adding two
> new module parameters:
> -maxphy: how many PHYs the card supports
> -usephy: which phy to use instead of eeprom default
>
> Maxphy is necessary in order to check the range of usephy and may
> be overriden inside the module.
It seems a bit pointless for the range check of a user-supplied value to
be driven by another user-supplied value.
> If only maxphy is present I've implemented an algorithm which checks
> the link state on all PHYs and uses the one that has a link.
Knowing how many PHYs to scan is potentially useful, but how about
determining that at runtime? Missing PHYs should be detectable with a
timeout or similar. Too risky?
--Adam
Adam Kropelin wrote:
> Seewer Philippe wrote:
>
>>Most AMD pcnet chips support up to 32 external PHYs. This patch
>>introduces basic PHY selection/switching support, by adding two
>>new module parameters:
>>-maxphy: how many PHYs the card supports
>>-usephy: which phy to use instead of eeprom default
>>
>>Maxphy is necessary in order to check the range of usephy and may
>>be overriden inside the module.
>
>
> It seems a bit pointless for the range check of a user-supplied value to
> be driven by another user-supplied value.
I just want to make sure and there's the possibility of supplying only maxphy
and let the autoswitch algorithm decide...
>
>
>>If only maxphy is present I've implemented an algorithm which checks
>>the link state on all PHYs and uses the one that has a link.
>
>
> Knowing how many PHYs to scan is potentially useful, but how about
> determining that at runtime? Missing PHYs should be detectable with a
> timeout or similar. Too risky?
Actually its possible to query them with mii and all non-present phy's
should "return" 0xffff. I wanted my changes to have no impact on pcnet
cards with only one phy, thats why.
>
> --Adam
>