aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb
diff options
context:
space:
mode:
authorSteve Glendinning <steve.glendinning@shawell.net>2012-09-27 20:07:12 -0400
committerDavid S. Miller <davem@davemloft.net>2012-09-28 18:34:58 -0400
commite0e474a83c18f134e42c4845e19d0ef0751f43f0 (patch)
treec62d08a0812b975954f3e519b56a0aad4aa0792e /drivers/net/usb
parentb5a044758672b6dc775fdab007337b47a54576f4 (diff)
smsc95xx: add wol magic packet support
This patch enables wake from system suspend on magic packet. Patch updated to replace BUG_ON with WARN_ON_ONCE and return. 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.c185
-rw-r--r--drivers/net/usb/smsc95xx.h5
2 files changed, 182 insertions, 8 deletions
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index ba0360fb857f..7479a5761d0d 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -46,6 +46,7 @@
46#define SMSC95XX_INTERNAL_PHY_ID (1) 46#define SMSC95XX_INTERNAL_PHY_ID (1)
47#define SMSC95XX_TX_OVERHEAD (8) 47#define SMSC95XX_TX_OVERHEAD (8)
48#define SMSC95XX_TX_OVERHEAD_CSUM (12) 48#define SMSC95XX_TX_OVERHEAD_CSUM (12)
49#define SUPPORTED_WAKE (WAKE_MAGIC)
49 50
50#define check_warn(ret, fmt, args...) \ 51#define check_warn(ret, fmt, args...) \
51 ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) 52 ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
@@ -60,6 +61,7 @@ struct smsc95xx_priv {
60 u32 mac_cr; 61 u32 mac_cr;
61 u32 hash_hi; 62 u32 hash_hi;
62 u32 hash_lo; 63 u32 hash_lo;
64 u32 wolopts;
63 spinlock_t mac_cr_lock; 65 spinlock_t mac_cr_lock;
64}; 66};
65 67
@@ -125,6 +127,30 @@ static int __must_check smsc95xx_write_reg(struct usbnet *dev, u32 index,
125 return ret; 127 return ret;
126} 128}
127 129
130static int smsc95xx_set_feature(struct usbnet *dev, u32 feature)
131{
132 if (WARN_ON_ONCE(!dev))
133 return -EINVAL;
134
135 cpu_to_le32s(&feature);
136
137 return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
138 USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0,
139 USB_CTRL_SET_TIMEOUT);
140}
141
142static int smsc95xx_clear_feature(struct usbnet *dev, u32 feature)
143{
144 if (WARN_ON_ONCE(!dev))
145 return -EINVAL;
146
147 cpu_to_le32s(&feature);
148
149 return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
150 USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0,
151 USB_CTRL_SET_TIMEOUT);
152}
153
128/* Loop until the read is completed with timeout 154/* Loop until the read is completed with timeout
129 * called with phy_mutex held */ 155 * called with phy_mutex held */
130static int __must_check smsc95xx_phy_wait_not_busy(struct usbnet *dev) 156static int __must_check smsc95xx_phy_wait_not_busy(struct usbnet *dev)
@@ -636,6 +662,26 @@ smsc95xx_ethtool_getregs(struct net_device *netdev, struct ethtool_regs *regs,
636 } 662 }
637} 663}
638 664
665static void smsc95xx_ethtool_get_wol(struct net_device *net,
666 struct ethtool_wolinfo *wolinfo)
667{
668 struct usbnet *dev = netdev_priv(net);
669 struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
670
671 wolinfo->supported = SUPPORTED_WAKE;
672 wolinfo->wolopts = pdata->wolopts;
673}
674
675static int smsc95xx_ethtool_set_wol(struct net_device *net,
676 struct ethtool_wolinfo *wolinfo)
677{
678 struct usbnet *dev = netdev_priv(net);
679 struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
680
681 pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE;
682 return 0;
683}
684
639static const struct ethtool_ops smsc95xx_ethtool_ops = { 685static const struct ethtool_ops smsc95xx_ethtool_ops = {
640 .get_link = usbnet_get_link, 686 .get_link = usbnet_get_link,
641 .nway_reset = usbnet_nway_reset, 687 .nway_reset = usbnet_nway_reset,
@@ -649,6 +695,8 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = {
649 .set_eeprom = smsc95xx_ethtool_set_eeprom, 695 .set_eeprom = smsc95xx_ethtool_set_eeprom,
650 .get_regs_len = smsc95xx_ethtool_getregslen, 696 .get_regs_len = smsc95xx_ethtool_getregslen,
651 .get_regs = smsc95xx_ethtool_getregs, 697 .get_regs = smsc95xx_ethtool_getregs,
698 .get_wol = smsc95xx_ethtool_get_wol,
699 .set_wol = smsc95xx_ethtool_set_wol,
652}; 700};
653 701
654static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) 702static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
@@ -1021,26 +1069,147 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
1021static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) 1069static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
1022{ 1070{
1023 struct usbnet *dev = usb_get_intfdata(intf); 1071 struct usbnet *dev = usb_get_intfdata(intf);
1072 struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
1024 int ret; 1073 int ret;
1025 u32 val; 1074 u32 val;
1026 1075
1027 if (WARN_ON_ONCE(!dev))
1028 return -EINVAL;
1029
1030 ret = usbnet_suspend(intf, message); 1076 ret = usbnet_suspend(intf, message);
1031 check_warn_return(ret, "usbnet_suspend error"); 1077 check_warn_return(ret, "usbnet_suspend error");
1032 1078
1033 netdev_info(dev->net, "entering SUSPEND2 mode"); 1079 /* if no wol options set, enter lowest power SUSPEND2 mode */
1080 if (!(pdata->wolopts & SUPPORTED_WAKE)) {
1081 netdev_info(dev->net, "entering SUSPEND2 mode");
1082
1083 /* disable energy detect (link up) & wake up events */
1084 ret = smsc95xx_read_reg(dev, WUCSR, &val);
1085 check_warn_return(ret, "Error reading WUCSR");
1086
1087 val &= ~(WUCSR_MPEN_ | WUCSR_WAKE_EN_);
1088
1089 ret = smsc95xx_write_reg(dev, WUCSR, val);
1090 check_warn_return(ret, "Error writing WUCSR");
1091
1092 ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
1093 check_warn_return(ret, "Error reading PM_CTRL");
1094
1095 val &= ~(PM_CTL_ED_EN_ | PM_CTL_WOL_EN_);
1096
1097 ret = smsc95xx_write_reg(dev, PM_CTRL, val);
1098 check_warn_return(ret, "Error writing PM_CTRL");
1099
1100 /* enter suspend2 mode */
1101 ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
1102 check_warn_return(ret, "Error reading PM_CTRL");
1103
1104 val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_);
1105 val |= PM_CTL_SUS_MODE_2;
1106
1107 ret = smsc95xx_write_reg(dev, PM_CTRL, val);
1108 check_warn_return(ret, "Error writing PM_CTRL");
1109
1110 return 0;
1111 }
1112
1113 if (pdata->wolopts & WAKE_MAGIC) {
1114 /* clear any pending magic packet status */
1115 ret = smsc95xx_read_reg(dev, WUCSR, &val);
1116 check_warn_return(ret, "Error reading WUCSR");
1117
1118 val |= WUCSR_MPR_;
1119
1120 ret = smsc95xx_write_reg(dev, WUCSR, val);
1121 check_warn_return(ret, "Error writing WUCSR");
1122 }
1123
1124 /* enable/disable magic packup wake */
1125 ret = smsc95xx_read_reg(dev, WUCSR, &val);
1126 check_warn_return(ret, "Error reading WUCSR");
1127
1128 if (pdata->wolopts & WAKE_MAGIC) {
1129 netdev_info(dev->net, "enabling magic packet wakeup");
1130 val |= WUCSR_MPEN_;
1131 } else {
1132 netdev_info(dev->net, "disabling magic packet wakeup");
1133 val &= ~WUCSR_MPEN_;
1134 }
1135
1136 ret = smsc95xx_write_reg(dev, WUCSR, val);
1137 check_warn_return(ret, "Error writing WUCSR");
1138
1139 /* enable wol wakeup source */
1140 ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
1141 check_warn_return(ret, "Error reading PM_CTRL");
1142
1143 val |= PM_CTL_WOL_EN_;
1144
1145 ret = smsc95xx_write_reg(dev, PM_CTRL, val);
1146 check_warn_return(ret, "Error writing PM_CTRL");
1147
1148 /* enable receiver */
1149 smsc95xx_start_rx_path(dev);
1150
1151 /* some wol options are enabled, so enter SUSPEND0 */
1152 netdev_info(dev->net, "entering SUSPEND0 mode");
1034 1153
1035 ret = smsc95xx_read_reg(dev, PM_CTRL, &val); 1154 ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
1036 check_warn_return(ret, "Error reading PM_CTRL"); 1155 check_warn_return(ret, "Error reading PM_CTRL");
1037 1156
1038 val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); 1157 val &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
1039 val |= PM_CTL_SUS_MODE_2; 1158 val |= PM_CTL_SUS_MODE_0;
1040 1159
1041 ret = smsc95xx_write_reg(dev, PM_CTRL, val); 1160 ret = smsc95xx_write_reg(dev, PM_CTRL, val);
1042 check_warn_return(ret, "Error writing PM_CTRL"); 1161 check_warn_return(ret, "Error writing PM_CTRL");
1043 1162
1163 /* clear wol status */
1164 val &= ~PM_CTL_WUPS_;
1165 val |= PM_CTL_WUPS_WOL_;
1166 ret = smsc95xx_write_reg(dev, PM_CTRL, val);
1167 check_warn_return(ret, "Error writing PM_CTRL");
1168
1169 /* read back PM_CTRL */
1170 ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
1171 check_warn_return(ret, "Error reading PM_CTRL");
1172
1173 smsc95xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
1174
1175 return 0;
1176}
1177
1178static int smsc95xx_resume(struct usb_interface *intf)
1179{
1180 struct usbnet *dev = usb_get_intfdata(intf);
1181 struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
1182 int ret;
1183 u32 val;
1184
1185 BUG_ON(!dev);
1186
1187 if (pdata->wolopts & WAKE_MAGIC) {
1188 smsc95xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
1189
1190 /* Disable magic packup wake */
1191 ret = smsc95xx_read_reg(dev, WUCSR, &val);
1192 check_warn_return(ret, "Error reading WUCSR");
1193
1194 val &= ~WUCSR_MPEN_;
1195
1196 ret = smsc95xx_write_reg(dev, WUCSR, val);
1197 check_warn_return(ret, "Error writing WUCSR");
1198
1199 /* clear wake-up status */
1200 ret = smsc95xx_read_reg(dev, PM_CTRL, &val);
1201 check_warn_return(ret, "Error reading PM_CTRL");
1202
1203 val &= ~PM_CTL_WOL_EN_;
1204 val |= PM_CTL_WUPS_;
1205
1206 ret = smsc95xx_write_reg(dev, PM_CTRL, val);
1207 check_warn_return(ret, "Error writing PM_CTRL");
1208 }
1209
1210 return usbnet_resume(intf);
1211 check_warn_return(ret, "usbnet_resume error");
1212
1044 return 0; 1213 return 0;
1045} 1214}
1046 1215
@@ -1307,8 +1476,8 @@ static struct usb_driver smsc95xx_driver = {
1307 .id_table = products, 1476 .id_table = products,
1308 .probe = usbnet_probe, 1477 .probe = usbnet_probe,
1309 .suspend = smsc95xx_suspend, 1478 .suspend = smsc95xx_suspend,
1310 .resume = usbnet_resume, 1479 .resume = smsc95xx_resume,
1311 .reset_resume = usbnet_resume, 1480 .reset_resume = smsc95xx_resume,
1312 .disconnect = usbnet_disconnect, 1481 .disconnect = usbnet_disconnect,
1313 .disable_hub_initiated_lpm = 1, 1482 .disable_hub_initiated_lpm = 1,
1314}; 1483};
diff --git a/drivers/net/usb/smsc95xx.h b/drivers/net/usb/smsc95xx.h
index 89ad9259d613..2ff9815aa27c 100644
--- a/drivers/net/usb/smsc95xx.h
+++ b/drivers/net/usb/smsc95xx.h
@@ -205,6 +205,11 @@
205#define WUFF (0x128) 205#define WUFF (0x128)
206 206
207#define WUCSR (0x12C) 207#define WUCSR (0x12C)
208#define WUCSR_GUE_ (0x00000200)
209#define WUCSR_WUFR_ (0x00000040)
210#define WUCSR_MPR_ (0x00000020)
211#define WUCSR_WAKE_EN_ (0x00000004)
212#define WUCSR_MPEN_ (0x00000002)
208 213
209#define COE_CR (0x130) 214#define COE_CR (0x130)
210#define Tx_COE_EN_ (0x00010000) 215#define Tx_COE_EN_ (0x00010000)