aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Glendinning <steve.glendinning@shawell.net>2012-10-25 23:43:56 -0400
committerDavid S. Miller <davem@davemloft.net>2012-10-31 13:32:36 -0400
commitbbd9f9ee69242f23c6063f0102bbb98f5bd23521 (patch)
treeb75904a46c0ea31a1e1863375536012ae580a0fa
parent9ecd1c3d6cff05fcfd11517341cc22f61651ec3e (diff)
smsc95xx: add wol support for more frame types
This patch adds support for wol wakeup on unicast, broadcast, multicast and arp frames. The wakeup filter code isn't pretty, but it works. Signed-off-by: Steve Glendinning <steve.glendinning@shawell.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/usb/Kconfig2
-rw-r--r--drivers/net/usb/smsc95xx.c127
-rw-r--r--drivers/net/usb/smsc95xx.h5
3 files changed, 128 insertions, 6 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 2ab8043e1e2..e62882c68dd 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -256,6 +256,8 @@ config USB_NET_SMSC75XX
256config USB_NET_SMSC95XX 256config USB_NET_SMSC95XX
257 tristate "SMSC LAN95XX based USB 2.0 10/100 ethernet devices" 257 tristate "SMSC LAN95XX based USB 2.0 10/100 ethernet devices"
258 depends on USB_USBNET 258 depends on USB_USBNET
259 select BITREVERSE
260 select CRC16
259 select CRC32 261 select CRC32
260 help 262 help
261 This option adds support for SMSC LAN95XX based USB 2.0 263 This option adds support for SMSC LAN95XX based USB 2.0
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 1730f753d06..46cd784467d 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -26,6 +26,8 @@
26#include <linux/ethtool.h> 26#include <linux/ethtool.h>
27#include <linux/mii.h> 27#include <linux/mii.h>
28#include <linux/usb.h> 28#include <linux/usb.h>
29#include <linux/bitrev.h>
30#include <linux/crc16.h>
29#include <linux/crc32.h> 31#include <linux/crc32.h>
30#include <linux/usb/usbnet.h> 32#include <linux/usb/usbnet.h>
31#include <linux/slab.h> 33#include <linux/slab.h>
@@ -46,7 +48,8 @@
46#define SMSC95XX_INTERNAL_PHY_ID (1) 48#define SMSC95XX_INTERNAL_PHY_ID (1)
47#define SMSC95XX_TX_OVERHEAD (8) 49#define SMSC95XX_TX_OVERHEAD (8)
48#define SMSC95XX_TX_OVERHEAD_CSUM (12) 50#define SMSC95XX_TX_OVERHEAD_CSUM (12)
49#define SUPPORTED_WAKE (WAKE_MAGIC) 51#define SUPPORTED_WAKE (WAKE_UCAST | WAKE_BCAST | \
52 WAKE_MCAST | WAKE_ARP | WAKE_MAGIC)
50 53
51#define check_warn(ret, fmt, args...) \ 54#define check_warn(ret, fmt, args...) \
52 ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) 55 ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
@@ -63,6 +66,7 @@ struct smsc95xx_priv {
63 u32 hash_lo; 66 u32 hash_lo;
64 u32 wolopts; 67 u32 wolopts;
65 spinlock_t mac_cr_lock; 68 spinlock_t mac_cr_lock;
69 int wuff_filter_count;
66}; 70};
67 71
68static bool turbo_mode = true; 72static bool turbo_mode = true;
@@ -956,6 +960,7 @@ static const struct net_device_ops smsc95xx_netdev_ops = {
956static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) 960static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
957{ 961{
958 struct smsc95xx_priv *pdata = NULL; 962 struct smsc95xx_priv *pdata = NULL;
963 u32 val;
959 int ret; 964 int ret;
960 965
961 printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); 966 printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n");
@@ -986,6 +991,15 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
986 /* Init all registers */ 991 /* Init all registers */
987 ret = smsc95xx_reset(dev); 992 ret = smsc95xx_reset(dev);
988 993
994 /* detect device revision as different features may be available */
995 ret = smsc95xx_read_reg(dev, ID_REV, &val);
996 check_warn_return(ret, "Failed to read ID_REV: %d\n", ret);
997 val >>= 16;
998 if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9512_))
999 pdata->wuff_filter_count = LAN9500A_WUFF_NUM;
1000 else
1001 pdata->wuff_filter_count = LAN9500_WUFF_NUM;
1002
989 dev->net->netdev_ops = &smsc95xx_netdev_ops; 1003 dev->net->netdev_ops = &smsc95xx_netdev_ops;
990 dev->net->ethtool_ops = &smsc95xx_ethtool_ops; 1004 dev->net->ethtool_ops = &smsc95xx_ethtool_ops;
991 dev->net->flags |= IFF_MULTICAST; 1005 dev->net->flags |= IFF_MULTICAST;
@@ -1005,6 +1019,11 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
1005 } 1019 }
1006} 1020}
1007 1021
1022static u16 smsc_crc(const u8 *buffer, size_t len, int filter)
1023{
1024 return bitrev16(crc16(0xFFFF, buffer, len)) << ((filter % 2) * 16);
1025}
1026
1008static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) 1027static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
1009{ 1028{
1010 struct usbnet *dev = usb_get_intfdata(intf); 1029 struct usbnet *dev = usb_get_intfdata(intf);
@@ -1049,6 +1068,94 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
1049 return 0; 1068 return 0;
1050 } 1069 }
1051 1070
1071 if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) {
1072 u32 *filter_mask = kzalloc(32, GFP_KERNEL);
1073 u32 *command = kzalloc(2, GFP_KERNEL);
1074 u32 *offset = kzalloc(2, GFP_KERNEL);
1075 u32 *crc = kzalloc(4, GFP_KERNEL);
1076 int i, filter = 0;
1077
1078 if (pdata->wolopts & WAKE_BCAST) {
1079 const u8 bcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1080 netdev_info(dev->net, "enabling broadcast detection");
1081 filter_mask[filter * 4] = 0x003F;
1082 filter_mask[filter * 4 + 1] = 0x00;
1083 filter_mask[filter * 4 + 2] = 0x00;
1084 filter_mask[filter * 4 + 3] = 0x00;
1085 command[filter/4] |= 0x05UL << ((filter % 4) * 8);
1086 offset[filter/4] |= 0x00 << ((filter % 4) * 8);
1087 crc[filter/2] |= smsc_crc(bcast, 6, filter);
1088 filter++;
1089 }
1090
1091 if (pdata->wolopts & WAKE_MCAST) {
1092 const u8 mcast[] = {0x01, 0x00, 0x5E};
1093 netdev_info(dev->net, "enabling multicast detection");
1094 filter_mask[filter * 4] = 0x0007;
1095 filter_mask[filter * 4 + 1] = 0x00;
1096 filter_mask[filter * 4 + 2] = 0x00;
1097 filter_mask[filter * 4 + 3] = 0x00;
1098 command[filter/4] |= 0x09UL << ((filter % 4) * 8);
1099 offset[filter/4] |= 0x00 << ((filter % 4) * 8);
1100 crc[filter/2] |= smsc_crc(mcast, 3, filter);
1101 filter++;
1102 }
1103
1104 if (pdata->wolopts & WAKE_ARP) {
1105 const u8 arp[] = {0x08, 0x06};
1106 netdev_info(dev->net, "enabling ARP detection");
1107 filter_mask[filter * 4] = 0x0003;
1108 filter_mask[filter * 4 + 1] = 0x00;
1109 filter_mask[filter * 4 + 2] = 0x00;
1110 filter_mask[filter * 4 + 3] = 0x00;
1111 command[filter/4] |= 0x05UL << ((filter % 4) * 8);
1112 offset[filter/4] |= 0x0C << ((filter % 4) * 8);
1113 crc[filter/2] |= smsc_crc(arp, 2, filter);
1114 filter++;
1115 }
1116
1117 if (pdata->wolopts & WAKE_UCAST) {
1118 netdev_info(dev->net, "enabling unicast detection");
1119 filter_mask[filter * 4] = 0x003F;
1120 filter_mask[filter * 4 + 1] = 0x00;
1121 filter_mask[filter * 4 + 2] = 0x00;
1122 filter_mask[filter * 4 + 3] = 0x00;
1123 command[filter/4] |= 0x01UL << ((filter % 4) * 8);
1124 offset[filter/4] |= 0x00 << ((filter % 4) * 8);
1125 crc[filter/2] |= smsc_crc(dev->net->dev_addr, ETH_ALEN, filter);
1126 filter++;
1127 }
1128
1129 for (i = 0; i < (pdata->wuff_filter_count * 4); i++) {
1130 ret = smsc95xx_write_reg(dev, WUFF, filter_mask[i]);
1131 check_warn_return(ret, "Error writing WUFF");
1132 }
1133
1134 for (i = 0; i < (pdata->wuff_filter_count / 4); i++) {
1135 ret = smsc95xx_write_reg(dev, WUFF, command[i]);
1136 check_warn_return(ret, "Error writing WUFF");
1137 }
1138
1139 for (i = 0; i < (pdata->wuff_filter_count / 4); i++) {
1140 ret = smsc95xx_write_reg(dev, WUFF, offset[i]);
1141 check_warn_return(ret, "Error writing WUFF");
1142 }
1143
1144 for (i = 0; i < (pdata->wuff_filter_count / 2); i++) {
1145 ret = smsc95xx_write_reg(dev, WUFF, crc[i]);
1146 check_warn_return(ret, "Error writing WUFF");
1147 }
1148
1149 /* clear any pending pattern match packet status */
1150 ret = smsc95xx_read_reg(dev, WUCSR, &val);
1151 check_warn_return(ret, "Error reading WUCSR");
1152
1153 val |= WUCSR_WUFR_;
1154
1155 ret = smsc95xx_write_reg(dev, WUCSR, val);
1156 check_warn_return(ret, "Error writing WUCSR");
1157 }
1158
1052 if (pdata->wolopts & WAKE_MAGIC) { 1159 if (pdata->wolopts & WAKE_MAGIC) {
1053 /* clear any pending magic packet status */ 1160 /* clear any pending magic packet status */
1054 ret = smsc95xx_read_reg(dev, WUCSR, &val); 1161 ret = smsc95xx_read_reg(dev, WUCSR, &val);
@@ -1060,10 +1167,18 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
1060 check_warn_return(ret, "Error writing WUCSR"); 1167 check_warn_return(ret, "Error writing WUCSR");
1061 } 1168 }
1062 1169
1063 /* enable/disable magic packup wake */ 1170 /* enable/disable wakeup sources */
1064 ret = smsc95xx_read_reg(dev, WUCSR, &val); 1171 ret = smsc95xx_read_reg(dev, WUCSR, &val);
1065 check_warn_return(ret, "Error reading WUCSR"); 1172 check_warn_return(ret, "Error reading WUCSR");
1066 1173
1174 if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) {
1175 netdev_info(dev->net, "enabling pattern match wakeup");
1176 val |= WUCSR_WAKE_EN_;
1177 } else {
1178 netdev_info(dev->net, "disabling pattern match wakeup");
1179 val &= ~WUCSR_WAKE_EN_;
1180 }
1181
1067 if (pdata->wolopts & WAKE_MAGIC) { 1182 if (pdata->wolopts & WAKE_MAGIC) {
1068 netdev_info(dev->net, "enabling magic packet wakeup"); 1183 netdev_info(dev->net, "enabling magic packet wakeup");
1069 val |= WUCSR_MPEN_; 1184 val |= WUCSR_MPEN_;
@@ -1084,7 +1199,7 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
1084 ret = smsc95xx_write_reg(dev, PM_CTRL, val); 1199 ret = smsc95xx_write_reg(dev, PM_CTRL, val);
1085 check_warn_return(ret, "Error writing PM_CTRL"); 1200 check_warn_return(ret, "Error writing PM_CTRL");
1086 1201
1087 /* enable receiver */ 1202 /* enable receiver to enable frame reception */
1088 smsc95xx_start_rx_path(dev); 1203 smsc95xx_start_rx_path(dev);
1089 1204
1090 /* some wol options are enabled, so enter SUSPEND0 */ 1205 /* some wol options are enabled, so enter SUSPEND0 */
@@ -1123,14 +1238,14 @@ static int smsc95xx_resume(struct usb_interface *intf)
1123 1238
1124 BUG_ON(!dev); 1239 BUG_ON(!dev);
1125 1240
1126 if (pdata->wolopts & WAKE_MAGIC) { 1241 if (pdata->wolopts) {
1127 smsc95xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP); 1242 smsc95xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
1128 1243
1129 /* Disable magic packup wake */ 1244 /* clear wake-up sources */
1130 ret = smsc95xx_read_reg(dev, WUCSR, &val); 1245 ret = smsc95xx_read_reg(dev, WUCSR, &val);
1131 check_warn_return(ret, "Error reading WUCSR"); 1246 check_warn_return(ret, "Error reading WUCSR");
1132 1247
1133 val &= ~WUCSR_MPEN_; 1248 val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_);
1134 1249
1135 ret = smsc95xx_write_reg(dev, WUCSR, val); 1250 ret = smsc95xx_write_reg(dev, WUCSR, val);
1136 check_warn_return(ret, "Error writing WUCSR"); 1251 check_warn_return(ret, "Error writing WUCSR");
diff --git a/drivers/net/usb/smsc95xx.h b/drivers/net/usb/smsc95xx.h
index 2ff9815aa27..1f862693dd7 100644
--- a/drivers/net/usb/smsc95xx.h
+++ b/drivers/net/usb/smsc95xx.h
@@ -53,6 +53,8 @@
53#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000) 53#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000)
54#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) 54#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF)
55#define ID_REV_CHIP_ID_9500_ (0x9500) 55#define ID_REV_CHIP_ID_9500_ (0x9500)
56#define ID_REV_CHIP_ID_9500A_ (0x9E00)
57#define ID_REV_CHIP_ID_9512_ (0xEC00)
56 58
57#define INT_STS (0x08) 59#define INT_STS (0x08)
58#define INT_STS_TX_STOP_ (0x00020000) 60#define INT_STS_TX_STOP_ (0x00020000)
@@ -203,8 +205,11 @@
203#define VLAN2 (0x124) 205#define VLAN2 (0x124)
204 206
205#define WUFF (0x128) 207#define WUFF (0x128)
208#define LAN9500_WUFF_NUM (4)
209#define LAN9500A_WUFF_NUM (8)
206 210
207#define WUCSR (0x12C) 211#define WUCSR (0x12C)
212#define WUCSR_WFF_PTR_RST_ (0x80000000)
208#define WUCSR_GUE_ (0x00000200) 213#define WUCSR_GUE_ (0x00000200)
209#define WUCSR_WUFR_ (0x00000040) 214#define WUCSR_WUFR_ (0x00000040)
210#define WUCSR_MPR_ (0x00000020) 215#define WUCSR_MPR_ (0x00000020)