diff options
author | Steve Glendinning <steve.glendinning@shawell.net> | 2012-11-22 03:05:24 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-11-23 14:15:18 -0500 |
commit | e5e3af8348945b7c0b96e720c35309c79c24e98f (patch) | |
tree | ae237dd7282e990b4cbb0f59c3a52297ed58c6a3 /drivers/net/usb | |
parent | 9ebca5071c8679bba96981af6bc29370f1c2f0aa (diff) |
smsc95xx: support PHY wakeup source
This patch enables LAN9500 family devices to wake from suspend
on either link up or link down events
It also adds _nopm versions of mdio access functions, so we can
safely call them from suspend and resume functions
Signed-off-by: Steve Glendinning <steve.glendinning@shawell.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/smsc95xx.c | 164 | ||||
-rw-r--r-- | drivers/net/usb/smsc95xx.h | 17 |
2 files changed, 164 insertions, 17 deletions
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 3bacb4153f3d..e98ff8c77ec5 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c | |||
@@ -48,7 +48,7 @@ | |||
48 | #define SMSC95XX_INTERNAL_PHY_ID (1) | 48 | #define SMSC95XX_INTERNAL_PHY_ID (1) |
49 | #define SMSC95XX_TX_OVERHEAD (8) | 49 | #define SMSC95XX_TX_OVERHEAD (8) |
50 | #define SMSC95XX_TX_OVERHEAD_CSUM (12) | 50 | #define SMSC95XX_TX_OVERHEAD_CSUM (12) |
51 | #define SUPPORTED_WAKE (WAKE_UCAST | WAKE_BCAST | \ | 51 | #define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \ |
52 | WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) | 52 | WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) |
53 | 53 | ||
54 | #define FEATURE_8_WAKEUP_FILTERS (0x01) | 54 | #define FEATURE_8_WAKEUP_FILTERS (0x01) |
@@ -176,14 +176,15 @@ static int smsc95xx_clear_feature(struct usbnet *dev, u32 feature) | |||
176 | 176 | ||
177 | /* Loop until the read is completed with timeout | 177 | /* Loop until the read is completed with timeout |
178 | * called with phy_mutex held */ | 178 | * called with phy_mutex held */ |
179 | static int __must_check smsc95xx_phy_wait_not_busy(struct usbnet *dev) | 179 | static int __must_check __smsc95xx_phy_wait_not_busy(struct usbnet *dev, |
180 | int in_pm) | ||
180 | { | 181 | { |
181 | unsigned long start_time = jiffies; | 182 | unsigned long start_time = jiffies; |
182 | u32 val; | 183 | u32 val; |
183 | int ret; | 184 | int ret; |
184 | 185 | ||
185 | do { | 186 | do { |
186 | ret = smsc95xx_read_reg(dev, MII_ADDR, &val); | 187 | ret = __smsc95xx_read_reg(dev, MII_ADDR, &val, in_pm); |
187 | check_warn_return(ret, "Error reading MII_ACCESS"); | 188 | check_warn_return(ret, "Error reading MII_ACCESS"); |
188 | if (!(val & MII_BUSY_)) | 189 | if (!(val & MII_BUSY_)) |
189 | return 0; | 190 | return 0; |
@@ -192,7 +193,8 @@ static int __must_check smsc95xx_phy_wait_not_busy(struct usbnet *dev) | |||
192 | return -EIO; | 193 | return -EIO; |
193 | } | 194 | } |
194 | 195 | ||
195 | static int smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx) | 196 | static int __smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx, |
197 | int in_pm) | ||
196 | { | 198 | { |
197 | struct usbnet *dev = netdev_priv(netdev); | 199 | struct usbnet *dev = netdev_priv(netdev); |
198 | u32 val, addr; | 200 | u32 val, addr; |
@@ -201,20 +203,20 @@ static int smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx) | |||
201 | mutex_lock(&dev->phy_mutex); | 203 | mutex_lock(&dev->phy_mutex); |
202 | 204 | ||
203 | /* confirm MII not busy */ | 205 | /* confirm MII not busy */ |
204 | ret = smsc95xx_phy_wait_not_busy(dev); | 206 | ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); |
205 | check_warn_goto_done(ret, "MII is busy in smsc95xx_mdio_read"); | 207 | check_warn_goto_done(ret, "MII is busy in smsc95xx_mdio_read"); |
206 | 208 | ||
207 | /* set the address, index & direction (read from PHY) */ | 209 | /* set the address, index & direction (read from PHY) */ |
208 | phy_id &= dev->mii.phy_id_mask; | 210 | phy_id &= dev->mii.phy_id_mask; |
209 | idx &= dev->mii.reg_num_mask; | 211 | idx &= dev->mii.reg_num_mask; |
210 | addr = (phy_id << 11) | (idx << 6) | MII_READ_ | MII_BUSY_; | 212 | addr = (phy_id << 11) | (idx << 6) | MII_READ_ | MII_BUSY_; |
211 | ret = smsc95xx_write_reg(dev, MII_ADDR, addr); | 213 | ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); |
212 | check_warn_goto_done(ret, "Error writing MII_ADDR"); | 214 | check_warn_goto_done(ret, "Error writing MII_ADDR"); |
213 | 215 | ||
214 | ret = smsc95xx_phy_wait_not_busy(dev); | 216 | ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); |
215 | check_warn_goto_done(ret, "Timed out reading MII reg %02X", idx); | 217 | check_warn_goto_done(ret, "Timed out reading MII reg %02X", idx); |
216 | 218 | ||
217 | ret = smsc95xx_read_reg(dev, MII_DATA, &val); | 219 | ret = __smsc95xx_read_reg(dev, MII_DATA, &val, in_pm); |
218 | check_warn_goto_done(ret, "Error reading MII_DATA"); | 220 | check_warn_goto_done(ret, "Error reading MII_DATA"); |
219 | 221 | ||
220 | ret = (u16)(val & 0xFFFF); | 222 | ret = (u16)(val & 0xFFFF); |
@@ -224,8 +226,8 @@ done: | |||
224 | return ret; | 226 | return ret; |
225 | } | 227 | } |
226 | 228 | ||
227 | static void smsc95xx_mdio_write(struct net_device *netdev, int phy_id, int idx, | 229 | static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id, |
228 | int regval) | 230 | int idx, int regval, int in_pm) |
229 | { | 231 | { |
230 | struct usbnet *dev = netdev_priv(netdev); | 232 | struct usbnet *dev = netdev_priv(netdev); |
231 | u32 val, addr; | 233 | u32 val, addr; |
@@ -234,27 +236,50 @@ static void smsc95xx_mdio_write(struct net_device *netdev, int phy_id, int idx, | |||
234 | mutex_lock(&dev->phy_mutex); | 236 | mutex_lock(&dev->phy_mutex); |
235 | 237 | ||
236 | /* confirm MII not busy */ | 238 | /* confirm MII not busy */ |
237 | ret = smsc95xx_phy_wait_not_busy(dev); | 239 | ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); |
238 | check_warn_goto_done(ret, "MII is busy in smsc95xx_mdio_write"); | 240 | check_warn_goto_done(ret, "MII is busy in smsc95xx_mdio_write"); |
239 | 241 | ||
240 | val = regval; | 242 | val = regval; |
241 | ret = smsc95xx_write_reg(dev, MII_DATA, val); | 243 | ret = __smsc95xx_write_reg(dev, MII_DATA, val, in_pm); |
242 | check_warn_goto_done(ret, "Error writing MII_DATA"); | 244 | check_warn_goto_done(ret, "Error writing MII_DATA"); |
243 | 245 | ||
244 | /* set the address, index & direction (write to PHY) */ | 246 | /* set the address, index & direction (write to PHY) */ |
245 | phy_id &= dev->mii.phy_id_mask; | 247 | phy_id &= dev->mii.phy_id_mask; |
246 | idx &= dev->mii.reg_num_mask; | 248 | idx &= dev->mii.reg_num_mask; |
247 | addr = (phy_id << 11) | (idx << 6) | MII_WRITE_ | MII_BUSY_; | 249 | addr = (phy_id << 11) | (idx << 6) | MII_WRITE_ | MII_BUSY_; |
248 | ret = smsc95xx_write_reg(dev, MII_ADDR, addr); | 250 | ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); |
249 | check_warn_goto_done(ret, "Error writing MII_ADDR"); | 251 | check_warn_goto_done(ret, "Error writing MII_ADDR"); |
250 | 252 | ||
251 | ret = smsc95xx_phy_wait_not_busy(dev); | 253 | ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); |
252 | check_warn_goto_done(ret, "Timed out writing MII reg %02X", idx); | 254 | check_warn_goto_done(ret, "Timed out writing MII reg %02X", idx); |
253 | 255 | ||
254 | done: | 256 | done: |
255 | mutex_unlock(&dev->phy_mutex); | 257 | mutex_unlock(&dev->phy_mutex); |
256 | } | 258 | } |
257 | 259 | ||
260 | static int smsc95xx_mdio_read_nopm(struct net_device *netdev, int phy_id, | ||
261 | int idx) | ||
262 | { | ||
263 | return __smsc95xx_mdio_read(netdev, phy_id, idx, 1); | ||
264 | } | ||
265 | |||
266 | static void smsc95xx_mdio_write_nopm(struct net_device *netdev, int phy_id, | ||
267 | int idx, int regval) | ||
268 | { | ||
269 | __smsc95xx_mdio_write(netdev, phy_id, idx, regval, 1); | ||
270 | } | ||
271 | |||
272 | static int smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx) | ||
273 | { | ||
274 | return __smsc95xx_mdio_read(netdev, phy_id, idx, 0); | ||
275 | } | ||
276 | |||
277 | static void smsc95xx_mdio_write(struct net_device *netdev, int phy_id, int idx, | ||
278 | int regval) | ||
279 | { | ||
280 | __smsc95xx_mdio_write(netdev, phy_id, idx, regval, 0); | ||
281 | } | ||
282 | |||
258 | static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev) | 283 | static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev) |
259 | { | 284 | { |
260 | unsigned long start_time = jiffies; | 285 | unsigned long start_time = jiffies; |
@@ -1068,18 +1093,61 @@ static u16 smsc_crc(const u8 *buffer, size_t len, int filter) | |||
1068 | return bitrev16(crc16(0xFFFF, buffer, len)) << ((filter % 2) * 16); | 1093 | return bitrev16(crc16(0xFFFF, buffer, len)) << ((filter % 2) * 16); |
1069 | } | 1094 | } |
1070 | 1095 | ||
1096 | static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) | ||
1097 | { | ||
1098 | struct mii_if_info *mii = &dev->mii; | ||
1099 | int ret; | ||
1100 | |||
1101 | netdev_dbg(dev->net, "enabling PHY wakeup interrupts"); | ||
1102 | |||
1103 | /* read to clear */ | ||
1104 | ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); | ||
1105 | check_warn_return(ret, "Error reading PHY_INT_SRC"); | ||
1106 | |||
1107 | /* enable interrupt source */ | ||
1108 | ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); | ||
1109 | check_warn_return(ret, "Error reading PHY_INT_MASK"); | ||
1110 | |||
1111 | ret |= mask; | ||
1112 | |||
1113 | smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret); | ||
1114 | |||
1115 | return 0; | ||
1116 | } | ||
1117 | |||
1118 | static int smsc95xx_link_ok_nopm(struct usbnet *dev) | ||
1119 | { | ||
1120 | struct mii_if_info *mii = &dev->mii; | ||
1121 | int ret; | ||
1122 | |||
1123 | /* first, a dummy read, needed to latch some MII phys */ | ||
1124 | ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); | ||
1125 | check_warn_return(ret, "Error reading MII_BMSR"); | ||
1126 | |||
1127 | ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); | ||
1128 | check_warn_return(ret, "Error reading MII_BMSR"); | ||
1129 | |||
1130 | return !!(ret & BMSR_LSTATUS); | ||
1131 | } | ||
1132 | |||
1071 | static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) | 1133 | static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) |
1072 | { | 1134 | { |
1073 | struct usbnet *dev = usb_get_intfdata(intf); | 1135 | struct usbnet *dev = usb_get_intfdata(intf); |
1074 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | 1136 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); |
1137 | u32 val, link_up; | ||
1075 | int ret; | 1138 | int ret; |
1076 | u32 val; | ||
1077 | 1139 | ||
1078 | ret = usbnet_suspend(intf, message); | 1140 | ret = usbnet_suspend(intf, message); |
1079 | check_warn_return(ret, "usbnet_suspend error"); | 1141 | check_warn_return(ret, "usbnet_suspend error"); |
1080 | 1142 | ||
1081 | /* if no wol options set, enter lowest power SUSPEND2 mode */ | 1143 | /* determine if link is up using only _nopm functions */ |
1082 | if (!(pdata->wolopts & SUPPORTED_WAKE)) { | 1144 | link_up = smsc95xx_link_ok_nopm(dev); |
1145 | |||
1146 | /* if no wol options set, or if link is down and we're not waking on | ||
1147 | * PHY activity, enter lowest power SUSPEND2 mode | ||
1148 | */ | ||
1149 | if (!(pdata->wolopts & SUPPORTED_WAKE) || | ||
1150 | !(link_up || (pdata->wolopts & WAKE_PHY))) { | ||
1083 | netdev_info(dev->net, "entering SUSPEND2 mode"); | 1151 | netdev_info(dev->net, "entering SUSPEND2 mode"); |
1084 | 1152 | ||
1085 | /* disable energy detect (link up) & wake up events */ | 1153 | /* disable energy detect (link up) & wake up events */ |
@@ -1112,6 +1180,59 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) | |||
1112 | return 0; | 1180 | return 0; |
1113 | } | 1181 | } |
1114 | 1182 | ||
1183 | if (pdata->wolopts & WAKE_PHY) { | ||
1184 | ret = smsc95xx_enable_phy_wakeup_interrupts(dev, | ||
1185 | (PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_LINK_DOWN_)); | ||
1186 | check_warn_return(ret, "error enabling PHY wakeup ints"); | ||
1187 | |||
1188 | /* if link is down then configure EDPD and enter SUSPEND1, | ||
1189 | * otherwise enter SUSPEND0 below | ||
1190 | */ | ||
1191 | if (!link_up) { | ||
1192 | struct mii_if_info *mii = &dev->mii; | ||
1193 | netdev_info(dev->net, "entering SUSPEND1 mode"); | ||
1194 | |||
1195 | /* reconfigure link pulse detection timing for | ||
1196 | * compatibility with non-standard link partners | ||
1197 | */ | ||
1198 | if (pdata->features & FEATURE_PHY_NLP_CROSSOVER) | ||
1199 | smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, | ||
1200 | PHY_EDPD_CONFIG, | ||
1201 | PHY_EDPD_CONFIG_DEFAULT); | ||
1202 | |||
1203 | /* enable energy detect power-down mode */ | ||
1204 | ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, | ||
1205 | PHY_MODE_CTRL_STS); | ||
1206 | check_warn_return(ret, "Error reading PHY_MODE_CTRL_STS"); | ||
1207 | |||
1208 | ret |= MODE_CTRL_STS_EDPWRDOWN_; | ||
1209 | |||
1210 | smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, | ||
1211 | PHY_MODE_CTRL_STS, ret); | ||
1212 | |||
1213 | /* enter SUSPEND1 mode */ | ||
1214 | ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); | ||
1215 | check_warn_return(ret, "Error reading PM_CTRL"); | ||
1216 | |||
1217 | val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); | ||
1218 | val |= PM_CTL_SUS_MODE_1; | ||
1219 | |||
1220 | ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); | ||
1221 | check_warn_return(ret, "Error writing PM_CTRL"); | ||
1222 | |||
1223 | /* clear wol status, enable energy detection */ | ||
1224 | val &= ~PM_CTL_WUPS_; | ||
1225 | val |= (PM_CTL_WUPS_ED_ | PM_CTL_ED_EN_); | ||
1226 | |||
1227 | ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); | ||
1228 | check_warn_return(ret, "Error writing PM_CTRL"); | ||
1229 | |||
1230 | smsc95xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); | ||
1231 | |||
1232 | return 0; | ||
1233 | } | ||
1234 | } | ||
1235 | |||
1115 | if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { | 1236 | if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { |
1116 | u32 *filter_mask = kzalloc(32, GFP_KERNEL); | 1237 | u32 *filter_mask = kzalloc(32, GFP_KERNEL); |
1117 | u32 command[2]; | 1238 | u32 command[2]; |
@@ -1250,6 +1371,10 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) | |||
1250 | 1371 | ||
1251 | val |= PM_CTL_WOL_EN_; | 1372 | val |= PM_CTL_WOL_EN_; |
1252 | 1373 | ||
1374 | /* phy energy detect wakeup source */ | ||
1375 | if (pdata->wolopts & WAKE_PHY) | ||
1376 | val |= PM_CTL_ED_EN_; | ||
1377 | |||
1253 | ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); | 1378 | ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); |
1254 | check_warn_return(ret, "Error writing PM_CTRL"); | 1379 | check_warn_return(ret, "Error writing PM_CTRL"); |
1255 | 1380 | ||
@@ -1271,6 +1396,11 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) | |||
1271 | /* clear wol status */ | 1396 | /* clear wol status */ |
1272 | val &= ~PM_CTL_WUPS_; | 1397 | val &= ~PM_CTL_WUPS_; |
1273 | val |= PM_CTL_WUPS_WOL_; | 1398 | val |= PM_CTL_WUPS_WOL_; |
1399 | |||
1400 | /* enable energy detection */ | ||
1401 | if (pdata->wolopts & WAKE_PHY) | ||
1402 | val |= PM_CTL_WUPS_ED_; | ||
1403 | |||
1274 | ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); | 1404 | ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); |
1275 | check_warn_return(ret, "Error writing PM_CTRL"); | 1405 | check_warn_return(ret, "Error writing PM_CTRL"); |
1276 | 1406 | ||
diff --git a/drivers/net/usb/smsc95xx.h b/drivers/net/usb/smsc95xx.h index 99f04a251a4e..f360ee372554 100644 --- a/drivers/net/usb/smsc95xx.h +++ b/drivers/net/usb/smsc95xx.h | |||
@@ -226,6 +226,23 @@ | |||
226 | 226 | ||
227 | /* Vendor-specific PHY Definitions */ | 227 | /* Vendor-specific PHY Definitions */ |
228 | 228 | ||
229 | /* EDPD NLP / crossover time configuration (LAN9500A only) */ | ||
230 | #define PHY_EDPD_CONFIG (16) | ||
231 | #define PHY_EDPD_CONFIG_TX_NLP_EN_ ((u16)0x8000) | ||
232 | #define PHY_EDPD_CONFIG_TX_NLP_1000_ ((u16)0x0000) | ||
233 | #define PHY_EDPD_CONFIG_TX_NLP_768_ ((u16)0x2000) | ||
234 | #define PHY_EDPD_CONFIG_TX_NLP_512_ ((u16)0x4000) | ||
235 | #define PHY_EDPD_CONFIG_TX_NLP_256_ ((u16)0x6000) | ||
236 | #define PHY_EDPD_CONFIG_RX_1_NLP_ ((u16)0x1000) | ||
237 | #define PHY_EDPD_CONFIG_RX_NLP_64_ ((u16)0x0000) | ||
238 | #define PHY_EDPD_CONFIG_RX_NLP_256_ ((u16)0x0400) | ||
239 | #define PHY_EDPD_CONFIG_RX_NLP_512_ ((u16)0x0800) | ||
240 | #define PHY_EDPD_CONFIG_RX_NLP_1000_ ((u16)0x0C00) | ||
241 | #define PHY_EDPD_CONFIG_EXT_CROSSOVER_ ((u16)0x0001) | ||
242 | #define PHY_EDPD_CONFIG_DEFAULT (PHY_EDPD_CONFIG_TX_NLP_EN_ | \ | ||
243 | PHY_EDPD_CONFIG_TX_NLP_768_ | \ | ||
244 | PHY_EDPD_CONFIG_RX_1_NLP_) | ||
245 | |||
229 | /* Mode Control/Status Register */ | 246 | /* Mode Control/Status Register */ |
230 | #define PHY_MODE_CTRL_STS (17) | 247 | #define PHY_MODE_CTRL_STS (17) |
231 | #define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000) | 248 | #define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000) |