|
35 | 35 | #include <linux/phy.h> |
36 | 36 | #include <linux/timer.h> |
37 | 37 | #include <linux/workqueue.h> |
| 38 | +#include <linux/mdio.h> |
38 | 39 |
|
39 | 40 | #include <linux/atomic.h> |
40 | 41 | #include <asm/io.h> |
@@ -967,3 +968,283 @@ void phy_state_machine(struct work_struct *work) |
967 | 968 |
|
968 | 969 | schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ); |
969 | 970 | } |
| 971 | + |
| 972 | +static 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 | + */ |
| 1000 | +static 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 | + */ |
| 1029 | +static 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 | + |
| 1038 | +static 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 | + |
| 1058 | +static 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 | + |
| 1078 | +static 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 | + */ |
| 1108 | +int 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 | + |
| 1176 | +eee_exit: |
| 1177 | + return ret; |
| 1178 | +} |
| 1179 | +EXPORT_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 | + */ |
| 1188 | +int 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 | +} |
| 1194 | +EXPORT_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 | + */ |
| 1204 | +int 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 | +} |
| 1231 | +EXPORT_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 | + */ |
| 1240 | +int 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 | +} |
| 1250 | +EXPORT_SYMBOL(phy_ethtool_set_eee); |
0 commit comments