diff options
author | Andreas Mohr <andim2@users.sourceforge.net> | 2009-06-10 05:55:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-06-11 05:32:39 -0400 |
commit | 720017623ab294b66c8a95d7bc5ccf75a581ebe4 (patch) | |
tree | b5603217826267eaff7022dca689169b29bf8d36 /drivers | |
parent | e5241c448f94feee40b2a285c8bf55d066420073 (diff) |
e100: add non-MII PHY support
Restore support for cards with MII-lacking PHYs as compared to removed
pre-2.6.29 eepro100 driver: use full low-level MII I/O access abstraction
by providing clean PHY-specific mdio_ctrl() functions for either standard
MII-compliant cards, slightly special ones or non-MII PHY ones.
We now have another netdev_priv member for mdio_ctrl(), thus we have some
array indirection, but we save some additional opcodes for specific
phy_82552_v handling in the common case.
[akpm@linux-foundation.org: cleanups]
Signed-off-by: Andreas Mohr <andi@lisas.de>
Cc: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Cc: Jesse Brandeburg <jesse.brandeburg@intel.com>
Cc: Bruce Allan <bruce.w.allan@intel.com>
Cc: PJ Waskiewicz <peter.p.waskiewicz.jr@intel.com>
Cc: John Ronciak <john.ronciak@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/e100.c | 188 |
1 files changed, 162 insertions, 26 deletions
diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 0c37dd97fa09..119dc5300f9d 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c | |||
@@ -143,6 +143,8 @@ | |||
143 | * FIXES: | 143 | * FIXES: |
144 | * 2005/12/02 - Michael O'Donnell <Michael.ODonnell at stratus dot com> | 144 | * 2005/12/02 - Michael O'Donnell <Michael.ODonnell at stratus dot com> |
145 | * - Stratus87247: protect MDI control register manipulations | 145 | * - Stratus87247: protect MDI control register manipulations |
146 | * 2009/06/01 - Andreas Mohr <andi at lisas dot de> | ||
147 | * - add clean lowlevel I/O emulation for cards with MII-lacking PHYs | ||
146 | */ | 148 | */ |
147 | 149 | ||
148 | #include <linux/module.h> | 150 | #include <linux/module.h> |
@@ -372,6 +374,7 @@ enum eeprom_op { | |||
372 | 374 | ||
373 | enum eeprom_offsets { | 375 | enum eeprom_offsets { |
374 | eeprom_cnfg_mdix = 0x03, | 376 | eeprom_cnfg_mdix = 0x03, |
377 | eeprom_phy_iface = 0x06, | ||
375 | eeprom_id = 0x0A, | 378 | eeprom_id = 0x0A, |
376 | eeprom_config_asf = 0x0D, | 379 | eeprom_config_asf = 0x0D, |
377 | eeprom_smbus_addr = 0x90, | 380 | eeprom_smbus_addr = 0x90, |
@@ -381,6 +384,18 @@ enum eeprom_cnfg_mdix { | |||
381 | eeprom_mdix_enabled = 0x0080, | 384 | eeprom_mdix_enabled = 0x0080, |
382 | }; | 385 | }; |
383 | 386 | ||
387 | enum eeprom_phy_iface { | ||
388 | NoSuchPhy = 0, | ||
389 | I82553AB, | ||
390 | I82553C, | ||
391 | I82503, | ||
392 | DP83840, | ||
393 | S80C240, | ||
394 | S80C24, | ||
395 | I82555, | ||
396 | DP83840A = 10, | ||
397 | }; | ||
398 | |||
384 | enum eeprom_id { | 399 | enum eeprom_id { |
385 | eeprom_id_wol = 0x0020, | 400 | eeprom_id_wol = 0x0020, |
386 | }; | 401 | }; |
@@ -545,6 +560,7 @@ struct nic { | |||
545 | u32 msg_enable ____cacheline_aligned; | 560 | u32 msg_enable ____cacheline_aligned; |
546 | struct net_device *netdev; | 561 | struct net_device *netdev; |
547 | struct pci_dev *pdev; | 562 | struct pci_dev *pdev; |
563 | u16 (*mdio_ctrl)(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data); | ||
548 | 564 | ||
549 | struct rx *rxs ____cacheline_aligned; | 565 | struct rx *rxs ____cacheline_aligned; |
550 | struct rx *rx_to_use; | 566 | struct rx *rx_to_use; |
@@ -899,7 +915,21 @@ err_unlock: | |||
899 | return err; | 915 | return err; |
900 | } | 916 | } |
901 | 917 | ||
902 | static u16 mdio_ctrl(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data) | 918 | static int mdio_read(struct net_device *netdev, int addr, int reg) |
919 | { | ||
920 | struct nic *nic = netdev_priv(netdev); | ||
921 | return nic->mdio_ctrl(nic, addr, mdi_read, reg, 0); | ||
922 | } | ||
923 | |||
924 | static void mdio_write(struct net_device *netdev, int addr, int reg, int data) | ||
925 | { | ||
926 | struct nic *nic = netdev_priv(netdev); | ||
927 | |||
928 | nic->mdio_ctrl(nic, addr, mdi_write, reg, data); | ||
929 | } | ||
930 | |||
931 | /* the standard mdio_ctrl() function for usual MII-compliant hardware */ | ||
932 | static u16 mdio_ctrl_hw(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data) | ||
903 | { | 933 | { |
904 | u32 data_out = 0; | 934 | u32 data_out = 0; |
905 | unsigned int i; | 935 | unsigned int i; |
@@ -938,30 +968,83 @@ static u16 mdio_ctrl(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data) | |||
938 | return (u16)data_out; | 968 | return (u16)data_out; |
939 | } | 969 | } |
940 | 970 | ||
941 | static int mdio_read(struct net_device *netdev, int addr, int reg) | 971 | /* slightly tweaked mdio_ctrl() function for phy_82552_v specifics */ |
942 | { | 972 | static u16 mdio_ctrl_phy_82552_v(struct nic *nic, |
943 | return mdio_ctrl(netdev_priv(netdev), addr, mdi_read, reg, 0); | 973 | u32 addr, |
974 | u32 dir, | ||
975 | u32 reg, | ||
976 | u16 data) | ||
977 | { | ||
978 | if ((reg == MII_BMCR) && (dir == mdi_write)) { | ||
979 | if (data & (BMCR_ANRESTART | BMCR_ANENABLE)) { | ||
980 | u16 advert = mdio_read(nic->netdev, nic->mii.phy_id, | ||
981 | MII_ADVERTISE); | ||
982 | |||
983 | /* | ||
984 | * Workaround Si issue where sometimes the part will not | ||
985 | * autoneg to 100Mbps even when advertised. | ||
986 | */ | ||
987 | if (advert & ADVERTISE_100FULL) | ||
988 | data |= BMCR_SPEED100 | BMCR_FULLDPLX; | ||
989 | else if (advert & ADVERTISE_100HALF) | ||
990 | data |= BMCR_SPEED100; | ||
991 | } | ||
992 | } | ||
993 | return mdio_ctrl_hw(nic, addr, dir, reg, data); | ||
944 | } | 994 | } |
945 | 995 | ||
946 | static void mdio_write(struct net_device *netdev, int addr, int reg, int data) | 996 | /* Fully software-emulated mdio_ctrl() function for cards without |
947 | { | 997 | * MII-compliant PHYs. |
948 | struct nic *nic = netdev_priv(netdev); | 998 | * For now, this is mainly geared towards 80c24 support; in case of further |
949 | 999 | * requirements for other types (i82503, ...?) either extend this mechanism | |
950 | if ((nic->phy == phy_82552_v) && (reg == MII_BMCR) && | 1000 | * or split it, whichever is cleaner. |
951 | (data & (BMCR_ANRESTART | BMCR_ANENABLE))) { | 1001 | */ |
952 | u16 advert = mdio_read(netdev, nic->mii.phy_id, MII_ADVERTISE); | 1002 | static u16 mdio_ctrl_phy_mii_emulated(struct nic *nic, |
953 | 1003 | u32 addr, | |
954 | /* | 1004 | u32 dir, |
955 | * Workaround Si issue where sometimes the part will not | 1005 | u32 reg, |
956 | * autoneg to 100Mbps even when advertised. | 1006 | u16 data) |
957 | */ | 1007 | { |
958 | if (advert & ADVERTISE_100FULL) | 1008 | /* might need to allocate a netdev_priv'ed register array eventually |
959 | data |= BMCR_SPEED100 | BMCR_FULLDPLX; | 1009 | * to be able to record state changes, but for now |
960 | else if (advert & ADVERTISE_100HALF) | 1010 | * some fully hardcoded register handling ought to be ok I guess. */ |
961 | data |= BMCR_SPEED100; | 1011 | |
1012 | if (dir == mdi_read) { | ||
1013 | switch (reg) { | ||
1014 | case MII_BMCR: | ||
1015 | /* Auto-negotiation, right? */ | ||
1016 | return BMCR_ANENABLE | | ||
1017 | BMCR_FULLDPLX; | ||
1018 | case MII_BMSR: | ||
1019 | return BMSR_LSTATUS /* for mii_link_ok() */ | | ||
1020 | BMSR_ANEGCAPABLE | | ||
1021 | BMSR_10FULL; | ||
1022 | case MII_ADVERTISE: | ||
1023 | /* 80c24 is a "combo card" PHY, right? */ | ||
1024 | return ADVERTISE_10HALF | | ||
1025 | ADVERTISE_10FULL; | ||
1026 | default: | ||
1027 | DPRINTK(HW, DEBUG, | ||
1028 | "%s:addr=%d, reg=%d, data=0x%04X: unimplemented emulation!\n", | ||
1029 | dir == mdi_read ? "READ" : "WRITE", addr, reg, data); | ||
1030 | return 0xFFFF; | ||
1031 | } | ||
1032 | } else { | ||
1033 | switch (reg) { | ||
1034 | default: | ||
1035 | DPRINTK(HW, DEBUG, | ||
1036 | "%s:addr=%d, reg=%d, data=0x%04X: unimplemented emulation!\n", | ||
1037 | dir == mdi_read ? "READ" : "WRITE", addr, reg, data); | ||
1038 | return 0xFFFF; | ||
1039 | } | ||
962 | } | 1040 | } |
963 | 1041 | } | |
964 | mdio_ctrl(netdev_priv(netdev), addr, mdi_write, reg, data); | 1042 | static inline int e100_phy_supports_mii(struct nic *nic) |
1043 | { | ||
1044 | /* for now, just check it by comparing whether we | ||
1045 | are using MII software emulation. | ||
1046 | */ | ||
1047 | return (nic->mdio_ctrl != mdio_ctrl_phy_mii_emulated); | ||
965 | } | 1048 | } |
966 | 1049 | ||
967 | static void e100_get_defaults(struct nic *nic) | 1050 | static void e100_get_defaults(struct nic *nic) |
@@ -1013,7 +1096,8 @@ static void e100_configure(struct nic *nic, struct cb *cb, struct sk_buff *skb) | |||
1013 | config->standard_stat_counter = 0x1; /* 1=standard, 0=extended */ | 1096 | config->standard_stat_counter = 0x1; /* 1=standard, 0=extended */ |
1014 | config->rx_discard_short_frames = 0x1; /* 1=discard, 0=pass */ | 1097 | config->rx_discard_short_frames = 0x1; /* 1=discard, 0=pass */ |
1015 | config->tx_underrun_retry = 0x3; /* # of underrun retries */ | 1098 | config->tx_underrun_retry = 0x3; /* # of underrun retries */ |
1016 | config->mii_mode = 0x1; /* 1=MII mode, 0=503 mode */ | 1099 | if (e100_phy_supports_mii(nic)) |
1100 | config->mii_mode = 1; /* 1=MII mode, 0=i82503 mode */ | ||
1017 | config->pad10 = 0x6; | 1101 | config->pad10 = 0x6; |
1018 | config->no_source_addr_insertion = 0x1; /* 1=no, 0=yes */ | 1102 | config->no_source_addr_insertion = 0x1; /* 1=no, 0=yes */ |
1019 | config->preamble_length = 0x2; /* 0=1, 1=3, 2=7, 3=15 bytes */ | 1103 | config->preamble_length = 0x2; /* 0=1, 1=3, 2=7, 3=15 bytes */ |
@@ -1270,6 +1354,42 @@ static void e100_dump(struct nic *nic, struct cb *cb, struct sk_buff *skb) | |||
1270 | offsetof(struct mem, dump_buf)); | 1354 | offsetof(struct mem, dump_buf)); |
1271 | } | 1355 | } |
1272 | 1356 | ||
1357 | static int e100_phy_check_without_mii(struct nic *nic) | ||
1358 | { | ||
1359 | u8 phy_type; | ||
1360 | int without_mii; | ||
1361 | |||
1362 | phy_type = (nic->eeprom[eeprom_phy_iface] >> 8) & 0x0f; | ||
1363 | |||
1364 | switch (phy_type) { | ||
1365 | case NoSuchPhy: /* Non-MII PHY; UNTESTED! */ | ||
1366 | case I82503: /* Non-MII PHY; UNTESTED! */ | ||
1367 | case S80C24: /* Non-MII PHY; tested and working */ | ||
1368 | /* paragraph from the FreeBSD driver, "FXP_PHY_80C24": | ||
1369 | * The Seeq 80c24 AutoDUPLEX(tm) Ethernet Interface Adapter | ||
1370 | * doesn't have a programming interface of any sort. The | ||
1371 | * media is sensed automatically based on how the link partner | ||
1372 | * is configured. This is, in essence, manual configuration. | ||
1373 | */ | ||
1374 | DPRINTK(PROBE, INFO, | ||
1375 | "found MII-less i82503 or 80c24 or other PHY\n"); | ||
1376 | |||
1377 | nic->mdio_ctrl = mdio_ctrl_phy_mii_emulated; | ||
1378 | nic->mii.phy_id = 0; /* is this ok for an MII-less PHY? */ | ||
1379 | |||
1380 | /* these might be needed for certain MII-less cards... | ||
1381 | * nic->flags |= ich; | ||
1382 | * nic->flags |= ich_10h_workaround; */ | ||
1383 | |||
1384 | without_mii = 1; | ||
1385 | break; | ||
1386 | default: | ||
1387 | without_mii = 0; | ||
1388 | break; | ||
1389 | } | ||
1390 | return without_mii; | ||
1391 | } | ||
1392 | |||
1273 | #define NCONFIG_AUTO_SWITCH 0x0080 | 1393 | #define NCONFIG_AUTO_SWITCH 0x0080 |
1274 | #define MII_NSC_CONG MII_RESV1 | 1394 | #define MII_NSC_CONG MII_RESV1 |
1275 | #define NSC_CONG_ENABLE 0x0100 | 1395 | #define NSC_CONG_ENABLE 0x0100 |
@@ -1290,9 +1410,21 @@ static int e100_phy_init(struct nic *nic) | |||
1290 | if (!((bmcr == 0xFFFF) || ((stat == 0) && (bmcr == 0)))) | 1410 | if (!((bmcr == 0xFFFF) || ((stat == 0) && (bmcr == 0)))) |
1291 | break; | 1411 | break; |
1292 | } | 1412 | } |
1293 | DPRINTK(HW, DEBUG, "phy_addr = %d\n", nic->mii.phy_id); | 1413 | if (addr == 32) { |
1294 | if (addr == 32) | 1414 | /* uhoh, no PHY detected: check whether we seem to be some |
1295 | return -EAGAIN; | 1415 | * weird, rare variant which is *known* to not have any MII. |
1416 | * But do this AFTER MII checking only, since this does | ||
1417 | * lookup of EEPROM values which may easily be unreliable. */ | ||
1418 | if (e100_phy_check_without_mii(nic)) | ||
1419 | return 0; /* simply return and hope for the best */ | ||
1420 | else { | ||
1421 | /* for unknown cases log a fatal error */ | ||
1422 | DPRINTK(HW, ERR, | ||
1423 | "Failed to locate any known PHY, aborting.\n"); | ||
1424 | return -EAGAIN; | ||
1425 | } | ||
1426 | } else | ||
1427 | DPRINTK(HW, DEBUG, "phy_addr = %d\n", nic->mii.phy_id); | ||
1296 | 1428 | ||
1297 | /* Isolate all the PHY ids */ | 1429 | /* Isolate all the PHY ids */ |
1298 | for (addr = 0; addr < 32; addr++) | 1430 | for (addr = 0; addr < 32; addr++) |
@@ -1320,6 +1452,9 @@ static int e100_phy_init(struct nic *nic) | |||
1320 | if (nic->phy == phy_82552_v) { | 1452 | if (nic->phy == phy_82552_v) { |
1321 | u16 advert = mdio_read(netdev, nic->mii.phy_id, MII_ADVERTISE); | 1453 | u16 advert = mdio_read(netdev, nic->mii.phy_id, MII_ADVERTISE); |
1322 | 1454 | ||
1455 | /* assign special tweaked mdio_ctrl() function */ | ||
1456 | nic->mdio_ctrl = mdio_ctrl_phy_82552_v; | ||
1457 | |||
1323 | /* Workaround Si not advertising flow-control during autoneg */ | 1458 | /* Workaround Si not advertising flow-control during autoneg */ |
1324 | advert |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; | 1459 | advert |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; |
1325 | mdio_write(netdev, nic->mii.phy_id, MII_ADVERTISE, advert); | 1460 | mdio_write(netdev, nic->mii.phy_id, MII_ADVERTISE, advert); |
@@ -2585,6 +2720,7 @@ static int __devinit e100_probe(struct pci_dev *pdev, | |||
2585 | nic->netdev = netdev; | 2720 | nic->netdev = netdev; |
2586 | nic->pdev = pdev; | 2721 | nic->pdev = pdev; |
2587 | nic->msg_enable = (1 << debug) - 1; | 2722 | nic->msg_enable = (1 << debug) - 1; |
2723 | nic->mdio_ctrl = mdio_ctrl_hw; | ||
2588 | pci_set_drvdata(pdev, netdev); | 2724 | pci_set_drvdata(pdev, netdev); |
2589 | 2725 | ||
2590 | if ((err = pci_enable_device(pdev))) { | 2726 | if ((err = pci_enable_device(pdev))) { |