aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGiuseppe CAVALLARO <peppe.cavallaro@st.com>2012-06-27 17:14:38 -0400
committerDavid S. Miller <davem@davemloft.net>2012-07-01 06:34:50 -0400
commita59a4d1921664da63d801ba477950114c71c88c9 (patch)
treea1b5557c8a70aa35bb98822fabae543dc15ae62a
parentd765955d2ae0b88781a0db3a5bacfe4241925e09 (diff)
phy: add the EEE support and the way to access to the MMD registers.
This patch adds the support for the Energy-Efficient Ethernet (EEE) to the Physical Abstraction Layer. To support the EEE we have to access to the MMD registers 3.20 and 7.60/61. So two new functions have been added to read/write the MMD registers (clause 45). An Ethernet driver (I tested the stmmac) can invoke the phy_init_eee to properly check if the EEE is supported by the PHYs and it can also set the clock stop enable bit in the 3.0 register. The phy_get_eee_err can be used for reporting the number of time where the PHY failed to complete its normal wake sequence. In the end, this patch also adds the EEE ethtool support implementing: o phy_ethtool_set_eee o phy_ethtool_get_eee v1: initial patch v2: fixed some errors especially on naming convention v3: renamed again the mmd read/write functions thank to Ben's feedback v4: moved file to phy.c and added the ethtool support. v5: fixed phy_adv_to_eee, phy_eee_to_supported, phy_eee_to_adv return values according to ethtool API (thanks to Ben's feedback). Renamed some macros to avoid too long names. v6: fixed kernel-doc comments to be properly parsed. Fixed the phy_init_eee function: we need to check which link mode was autonegotiated and then the corresponding bits in 7.60 and 7.61 registers. v7: reviewed the way to get the negotiated settings. v8: fixed a problem in the phy_init_eee return value erroneously added when included the phy_read_status call. v9: do not remove the MDIO_AN_EEE_ADV_100TX and MDIO_AN_EEE_ADV_1000T and fixed the eee_{cap,lp,adv} declaration as "int" instead of u16. Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com> Reviewed-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/phy/phy.c281
-rw-r--r--include/linux/mdio.h28
-rw-r--r--include/linux/mii.h9
-rw-r--r--include/linux/phy.h5
4 files changed, 319 insertions, 4 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 2e1c23731ded..7ca2ff97c368 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -35,6 +35,7 @@
35#include <linux/phy.h> 35#include <linux/phy.h>
36#include <linux/timer.h> 36#include <linux/timer.h>
37#include <linux/workqueue.h> 37#include <linux/workqueue.h>
38#include <linux/mdio.h>
38 39
39#include <linux/atomic.h> 40#include <linux/atomic.h>
40#include <asm/io.h> 41#include <asm/io.h>
@@ -967,3 +968,283 @@ void phy_state_machine(struct work_struct *work)
967 968
968 schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ); 969 schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ);
969} 970}
971
972static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
973 int addr)
974{
975 /* Write the desired MMD Devad */
976 bus->write(bus, addr, MII_MMD_CTRL, devad);
977
978 /* Write the desired MMD register address */
979 bus->write(bus, addr, MII_MMD_DATA, prtad);
980
981 /* Select the Function : DATA with no post increment */
982 bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
983}
984
985/**
986 * phy_read_mmd_indirect - reads data from the MMD registers
987 * @bus: the target MII bus
988 * @prtad: MMD Address
989 * @devad: MMD DEVAD
990 * @addr: PHY address on the MII bus
991 *
992 * Description: it reads data from the MMD registers (clause 22 to access to
993 * clause 45) of the specified phy address.
994 * To read these register we have:
995 * 1) Write reg 13 // DEVAD
996 * 2) Write reg 14 // MMD Address
997 * 3) Write reg 13 // MMD Data Command for MMD DEVAD
998 * 3) Read reg 14 // Read MMD data
999 */
1000static int phy_read_mmd_indirect(struct mii_bus *bus, int prtad, int devad,
1001 int addr)
1002{
1003 u32 ret;
1004
1005 mmd_phy_indirect(bus, prtad, devad, addr);
1006
1007 /* Read the content of the MMD's selected register */
1008 ret = bus->read(bus, addr, MII_MMD_DATA);
1009
1010 return ret;
1011}
1012
1013/**
1014 * phy_write_mmd_indirect - writes data to the MMD registers
1015 * @bus: the target MII bus
1016 * @prtad: MMD Address
1017 * @devad: MMD DEVAD
1018 * @addr: PHY address on the MII bus
1019 * @data: data to write in the MMD register
1020 *
1021 * Description: Write data from the MMD registers of the specified
1022 * phy address.
1023 * To write these register we have:
1024 * 1) Write reg 13 // DEVAD
1025 * 2) Write reg 14 // MMD Address
1026 * 3) Write reg 13 // MMD Data Command for MMD DEVAD
1027 * 3) Write reg 14 // Write MMD data
1028 */
1029static void phy_write_mmd_indirect(struct mii_bus *bus, int prtad, int devad,
1030 int addr, u32 data)
1031{
1032 mmd_phy_indirect(bus, prtad, devad, addr);
1033
1034 /* Write the data into MMD's selected register */
1035 bus->write(bus, addr, MII_MMD_DATA, data);
1036}
1037
1038static u32 phy_eee_to_adv(u16 eee_adv)
1039{
1040 u32 adv = 0;
1041
1042 if (eee_adv & MDIO_EEE_100TX)
1043 adv |= ADVERTISED_100baseT_Full;
1044 if (eee_adv & MDIO_EEE_1000T)
1045 adv |= ADVERTISED_1000baseT_Full;
1046 if (eee_adv & MDIO_EEE_10GT)
1047 adv |= ADVERTISED_10000baseT_Full;
1048 if (eee_adv & MDIO_EEE_1000KX)
1049 adv |= ADVERTISED_1000baseKX_Full;
1050 if (eee_adv & MDIO_EEE_10GKX4)
1051 adv |= ADVERTISED_10000baseKX4_Full;
1052 if (eee_adv & MDIO_EEE_10GKR)
1053 adv |= ADVERTISED_10000baseKR_Full;
1054
1055 return adv;
1056}
1057
1058static u32 phy_eee_to_supported(u16 eee_caported)
1059{
1060 u32 supported = 0;
1061
1062 if (eee_caported & MDIO_EEE_100TX)
1063 supported |= SUPPORTED_100baseT_Full;
1064 if (eee_caported & MDIO_EEE_1000T)
1065 supported |= SUPPORTED_1000baseT_Full;
1066 if (eee_caported & MDIO_EEE_10GT)
1067 supported |= SUPPORTED_10000baseT_Full;
1068 if (eee_caported & MDIO_EEE_1000KX)
1069 supported |= SUPPORTED_1000baseKX_Full;
1070 if (eee_caported & MDIO_EEE_10GKX4)
1071 supported |= SUPPORTED_10000baseKX4_Full;
1072 if (eee_caported & MDIO_EEE_10GKR)
1073 supported |= SUPPORTED_10000baseKR_Full;
1074
1075 return supported;
1076}
1077
1078static u16 phy_adv_to_eee(u32 adv)
1079{
1080 u16 reg = 0;
1081
1082 if (adv & ADVERTISED_100baseT_Full)
1083 reg |= MDIO_EEE_100TX;
1084 if (adv & ADVERTISED_1000baseT_Full)
1085 reg |= MDIO_EEE_1000T;
1086 if (adv & ADVERTISED_10000baseT_Full)
1087 reg |= MDIO_EEE_10GT;
1088 if (adv & ADVERTISED_1000baseKX_Full)
1089 reg |= MDIO_EEE_1000KX;
1090 if (adv & ADVERTISED_10000baseKX4_Full)
1091 reg |= MDIO_EEE_10GKX4;
1092 if (adv & ADVERTISED_10000baseKR_Full)
1093 reg |= MDIO_EEE_10GKR;
1094
1095 return reg;
1096}
1097
1098/**
1099 * phy_init_eee - init and check the EEE feature
1100 * @phydev: target phy_device struct
1101 * @clk_stop_enable: PHY may stop the clock during LPI
1102 *
1103 * Description: it checks if the Energy-Efficient Ethernet (EEE)
1104 * is supported by looking at the MMD registers 3.20 and 7.60/61
1105 * and it programs the MMD register 3.0 setting the "Clock stop enable"
1106 * bit if required.
1107 */
1108int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
1109{
1110 int ret = -EPROTONOSUPPORT;
1111
1112 /* According to 802.3az,the EEE is supported only in full duplex-mode.
1113 * Also EEE feature is active when core is operating with MII, GMII
1114 * or RGMII.
1115 */
1116 if ((phydev->duplex == DUPLEX_FULL) &&
1117 ((phydev->interface == PHY_INTERFACE_MODE_MII) ||
1118 (phydev->interface == PHY_INTERFACE_MODE_GMII) ||
1119 (phydev->interface == PHY_INTERFACE_MODE_RGMII))) {
1120 int eee_lp, eee_cap, eee_adv;
1121 u32 lp, cap, adv;
1122 int idx, status;
1123
1124 /* Read phy status to properly get the right settings */
1125 status = phy_read_status(phydev);
1126 if (status)
1127 return status;
1128
1129 /* First check if the EEE ability is supported */
1130 eee_cap = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE,
1131 MDIO_MMD_PCS, phydev->addr);
1132 if (eee_cap < 0)
1133 return eee_cap;
1134
1135 cap = phy_eee_to_supported(eee_cap);
1136 if (!cap)
1137 goto eee_exit;
1138
1139 /* Check which link settings negotiated and verify it in
1140 * the EEE advertising registers.
1141 */
1142 eee_lp = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE,
1143 MDIO_MMD_AN, phydev->addr);
1144 if (eee_lp < 0)
1145 return eee_lp;
1146
1147 eee_adv = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV,
1148 MDIO_MMD_AN, phydev->addr);
1149 if (eee_adv < 0)
1150 return eee_adv;
1151
1152 adv = phy_eee_to_adv(eee_adv);
1153 lp = phy_eee_to_adv(eee_lp);
1154 idx = phy_find_setting(phydev->speed, phydev->duplex);
1155 if ((lp & adv & settings[idx].setting))
1156 goto eee_exit;
1157
1158 if (clk_stop_enable) {
1159 /* Configure the PHY to stop receiving xMII
1160 * clock while it is signaling LPI.
1161 */
1162 int val = phy_read_mmd_indirect(phydev->bus, MDIO_CTRL1,
1163 MDIO_MMD_PCS,
1164 phydev->addr);
1165 if (val < 0)
1166 return val;
1167
1168 val |= MDIO_PCS_CTRL1_CLKSTOP_EN;
1169 phy_write_mmd_indirect(phydev->bus, MDIO_CTRL1,
1170 MDIO_MMD_PCS, phydev->addr, val);
1171 }
1172
1173 ret = 0; /* EEE supported */
1174 }
1175
1176eee_exit:
1177 return ret;
1178}
1179EXPORT_SYMBOL(phy_init_eee);
1180
1181/**
1182 * phy_get_eee_err - report the EEE wake error count
1183 * @phydev: target phy_device struct
1184 *
1185 * Description: it is to report the number of time where the PHY
1186 * failed to complete its normal wake sequence.
1187 */
1188int phy_get_eee_err(struct phy_device *phydev)
1189{
1190 return phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_WK_ERR,
1191 MDIO_MMD_PCS, phydev->addr);
1192
1193}
1194EXPORT_SYMBOL(phy_get_eee_err);
1195
1196/**
1197 * phy_ethtool_get_eee - get EEE supported and status
1198 * @phydev: target phy_device struct
1199 * @data: ethtool_eee data
1200 *
1201 * Description: it reportes the Supported/Advertisement/LP Advertisement
1202 * capabilities.
1203 */
1204int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
1205{
1206 int val;
1207
1208 /* Get Supported EEE */
1209 val = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE,
1210 MDIO_MMD_PCS, phydev->addr);
1211 if (val < 0)
1212 return val;
1213 data->supported = phy_eee_to_supported(val);
1214
1215 /* Get advertisement EEE */
1216 val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV,
1217 MDIO_MMD_AN, phydev->addr);
1218 if (val < 0)
1219 return val;
1220 data->advertised = phy_eee_to_adv(val);
1221
1222 /* Get LP advertisement EEE */
1223 val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE,
1224 MDIO_MMD_AN, phydev->addr);
1225 if (val < 0)
1226 return val;
1227 data->lp_advertised = phy_eee_to_adv(val);
1228
1229 return 0;
1230}
1231EXPORT_SYMBOL(phy_ethtool_get_eee);
1232
1233/**
1234 * phy_ethtool_set_eee - set EEE supported and status
1235 * @phydev: target phy_device struct
1236 * @data: ethtool_eee data
1237 *
1238 * Description: it is to program the Advertisement EEE register.
1239 */
1240int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
1241{
1242 int val;
1243
1244 val = phy_adv_to_eee(data->advertised);
1245 phy_write_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, MDIO_MMD_AN,
1246 phydev->addr, val);
1247
1248 return 0;
1249}
1250EXPORT_SYMBOL(phy_ethtool_set_eee);
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index dfb947959ec9..7cccafe50e7b 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -43,7 +43,11 @@
43#define MDIO_PKGID2 15 43#define MDIO_PKGID2 15
44#define MDIO_AN_ADVERTISE 16 /* AN advertising (base page) */ 44#define MDIO_AN_ADVERTISE 16 /* AN advertising (base page) */
45#define MDIO_AN_LPA 19 /* AN LP abilities (base page) */ 45#define MDIO_AN_LPA 19 /* AN LP abilities (base page) */
46#define MDIO_PCS_EEE_ABLE 20 /* EEE Capability register */
47#define MDIO_PCS_EEE_WK_ERR 22 /* EEE wake error counter */
46#define MDIO_PHYXS_LNSTAT 24 /* PHY XGXS lane state */ 48#define MDIO_PHYXS_LNSTAT 24 /* PHY XGXS lane state */
49#define MDIO_AN_EEE_ADV 60 /* EEE advertisement */
50#define MDIO_AN_EEE_LPABLE 61 /* EEE link partner ability */
47 51
48/* Media-dependent registers. */ 52/* Media-dependent registers. */
49#define MDIO_PMA_10GBT_SWAPPOL 130 /* 10GBASE-T pair swap & polarity */ 53#define MDIO_PMA_10GBT_SWAPPOL 130 /* 10GBASE-T pair swap & polarity */
@@ -56,7 +60,6 @@
56#define MDIO_PCS_10GBRT_STAT2 33 /* 10GBASE-R/-T PCS status 2 */ 60#define MDIO_PCS_10GBRT_STAT2 33 /* 10GBASE-R/-T PCS status 2 */
57#define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */ 61#define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */
58#define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */ 62#define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */
59#define MDIO_AN_EEE_ADV 60 /* EEE advertisement */
60 63
61/* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */ 64/* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */
62#define MDIO_PMA_LASI_RXCTRL 0x9000 /* RX_ALARM control */ 65#define MDIO_PMA_LASI_RXCTRL 0x9000 /* RX_ALARM control */
@@ -82,6 +85,7 @@
82#define MDIO_AN_CTRL1_RESTART BMCR_ANRESTART 85#define MDIO_AN_CTRL1_RESTART BMCR_ANRESTART
83#define MDIO_AN_CTRL1_ENABLE BMCR_ANENABLE 86#define MDIO_AN_CTRL1_ENABLE BMCR_ANENABLE
84#define MDIO_AN_CTRL1_XNP 0x2000 /* Enable extended next page */ 87#define MDIO_AN_CTRL1_XNP 0x2000 /* Enable extended next page */
88#define MDIO_PCS_CTRL1_CLKSTOP_EN 0x400 /* Stop the clock during LPI */
85 89
86/* 10 Gb/s */ 90/* 10 Gb/s */
87#define MDIO_CTRL1_SPEED10G (MDIO_CTRL1_SPEEDSELEXT | 0x00) 91#define MDIO_CTRL1_SPEED10G (MDIO_CTRL1_SPEEDSELEXT | 0x00)
@@ -237,9 +241,25 @@
237#define MDIO_AN_10GBT_STAT_MS 0x4000 /* Master/slave config */ 241#define MDIO_AN_10GBT_STAT_MS 0x4000 /* Master/slave config */
238#define MDIO_AN_10GBT_STAT_MSFLT 0x8000 /* Master/slave config fault */ 242#define MDIO_AN_10GBT_STAT_MSFLT 0x8000 /* Master/slave config fault */
239 243
240/* AN EEE Advertisement register. */ 244/* EEE Supported/Advertisement/LP Advertisement registers.
241#define MDIO_AN_EEE_ADV_100TX 0x0002 /* Advertise 100TX EEE cap */ 245 *
242#define MDIO_AN_EEE_ADV_1000T 0x0004 /* Advertise 1000T EEE cap */ 246 * EEE capability Register (3.20), Advertisement (7.60) and
247 * Link partner ability (7.61) registers have and can use the same identical
248 * bit masks.
249 */
250#define MDIO_AN_EEE_ADV_100TX 0x0002 /* Advertise 100TX EEE cap */
251#define MDIO_AN_EEE_ADV_1000T 0x0004 /* Advertise 1000T EEE cap */
252/* Note: the two defines above can be potentially used by the user-land
253 * and cannot remove them now.
254 * So, we define the new generic MDIO_EEE_100TX and MDIO_EEE_1000T macros
255 * using the previous ones (that can be considered obsolete).
256 */
257#define MDIO_EEE_100TX MDIO_AN_EEE_ADV_100TX /* 100TX EEE cap */
258#define MDIO_EEE_1000T MDIO_AN_EEE_ADV_1000T /* 1000T EEE cap */
259#define MDIO_EEE_10GT 0x0008 /* 10GT EEE cap */
260#define MDIO_EEE_1000KX 0x0010 /* 1000KX EEE cap */
261#define MDIO_EEE_10GKX4 0x0020 /* 10G KX4 EEE cap */
262#define MDIO_EEE_10GKR 0x0040 /* 10G KR EEE cap */
243 263
244/* LASI RX_ALARM control/status registers. */ 264/* LASI RX_ALARM control/status registers. */
245#define MDIO_PMA_LASI_RX_PHYXSLFLT 0x0001 /* PHY XS RX local fault */ 265#define MDIO_PMA_LASI_RX_PHYXSLFLT 0x0001 /* PHY XS RX local fault */
diff --git a/include/linux/mii.h b/include/linux/mii.h
index 2783eca629a0..8ef3a7a11592 100644
--- a/include/linux/mii.h
+++ b/include/linux/mii.h
@@ -21,6 +21,8 @@
21#define MII_EXPANSION 0x06 /* Expansion register */ 21#define MII_EXPANSION 0x06 /* Expansion register */
22#define MII_CTRL1000 0x09 /* 1000BASE-T control */ 22#define MII_CTRL1000 0x09 /* 1000BASE-T control */
23#define MII_STAT1000 0x0a /* 1000BASE-T status */ 23#define MII_STAT1000 0x0a /* 1000BASE-T status */
24#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */
25#define MII_MMD_DATA 0x0e /* MMD Access Data Register */
24#define MII_ESTATUS 0x0f /* Extended Status */ 26#define MII_ESTATUS 0x0f /* Extended Status */
25#define MII_DCOUNTER 0x12 /* Disconnect counter */ 27#define MII_DCOUNTER 0x12 /* Disconnect counter */
26#define MII_FCSCOUNTER 0x13 /* False carrier counter */ 28#define MII_FCSCOUNTER 0x13 /* False carrier counter */
@@ -141,6 +143,13 @@
141#define FLOW_CTRL_TX 0x01 143#define FLOW_CTRL_TX 0x01
142#define FLOW_CTRL_RX 0x02 144#define FLOW_CTRL_RX 0x02
143 145
146/* MMD Access Control register fields */
147#define MII_MMD_CTRL_DEVAD_MASK 0x1f /* Mask MMD DEVAD*/
148#define MII_MMD_CTRL_ADDR 0x0000 /* Address */
149#define MII_MMD_CTRL_NOINCR 0x4000 /* no post increment */
150#define MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */
151#define MII_MMD_CTRL_INCR_ON_WT 0xC000 /* post increment on writes only */
152
144/* This structure is used in all SIOCxMIIxxx ioctl calls */ 153/* This structure is used in all SIOCxMIIxxx ioctl calls */
145struct mii_ioctl_data { 154struct mii_ioctl_data {
146 __u16 phy_id; 155 __u16 phy_id;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 7eac80a2557b..c35299e4da8e 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -554,6 +554,11 @@ int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
554 int (*run)(struct phy_device *)); 554 int (*run)(struct phy_device *));
555int phy_scan_fixups(struct phy_device *phydev); 555int phy_scan_fixups(struct phy_device *phydev);
556 556
557int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable);
558int phy_get_eee_err(struct phy_device *phydev);
559int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data);
560int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data);
561
557int __init mdio_bus_init(void); 562int __init mdio_bus_init(void);
558void mdio_bus_exit(void); 563void mdio_bus_exit(void);
559 564