Skip to content

Commit

Permalink
smsc95xx: add wol support for more frame types
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
steveglen authored and davem330 committed Oct 31, 2012
1 parent 9ecd1c3 commit bbd9f9e
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 6 deletions.
2 changes: 2 additions & 0 deletions drivers/net/usb/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ config USB_NET_SMSC75XX
config USB_NET_SMSC95XX
tristate "SMSC LAN95XX based USB 2.0 10/100 ethernet devices"
depends on USB_USBNET
select BITREVERSE
select CRC16
select CRC32
help
This option adds support for SMSC LAN95XX based USB 2.0
Expand Down
127 changes: 121 additions & 6 deletions drivers/net/usb/smsc95xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/usb.h>
#include <linux/bitrev.h>
#include <linux/crc16.h>
#include <linux/crc32.h>
#include <linux/usb/usbnet.h>
#include <linux/slab.h>
Expand All @@ -46,7 +48,8 @@
#define SMSC95XX_INTERNAL_PHY_ID (1)
#define SMSC95XX_TX_OVERHEAD (8)
#define SMSC95XX_TX_OVERHEAD_CSUM (12)
#define SUPPORTED_WAKE (WAKE_MAGIC)
#define SUPPORTED_WAKE (WAKE_UCAST | WAKE_BCAST | \
WAKE_MCAST | WAKE_ARP | WAKE_MAGIC)

#define check_warn(ret, fmt, args...) \
({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
Expand All @@ -63,6 +66,7 @@ struct smsc95xx_priv {
u32 hash_lo;
u32 wolopts;
spinlock_t mac_cr_lock;
int wuff_filter_count;
};

static bool turbo_mode = true;
Expand Down Expand Up @@ -956,6 +960,7 @@ static const struct net_device_ops smsc95xx_netdev_ops = {
static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
{
struct smsc95xx_priv *pdata = NULL;
u32 val;
int ret;

printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n");
Expand Down Expand Up @@ -986,6 +991,15 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
/* Init all registers */
ret = smsc95xx_reset(dev);

/* detect device revision as different features may be available */
ret = smsc95xx_read_reg(dev, ID_REV, &val);
check_warn_return(ret, "Failed to read ID_REV: %d\n", ret);
val >>= 16;
if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9512_))
pdata->wuff_filter_count = LAN9500A_WUFF_NUM;
else
pdata->wuff_filter_count = LAN9500_WUFF_NUM;

dev->net->netdev_ops = &smsc95xx_netdev_ops;
dev->net->ethtool_ops = &smsc95xx_ethtool_ops;
dev->net->flags |= IFF_MULTICAST;
Expand All @@ -1005,6 +1019,11 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
}
}

static u16 smsc_crc(const u8 *buffer, size_t len, int filter)
{
return bitrev16(crc16(0xFFFF, buffer, len)) << ((filter % 2) * 16);
}

static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
Expand Down Expand Up @@ -1049,6 +1068,94 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
return 0;
}

if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) {
u32 *filter_mask = kzalloc(32, GFP_KERNEL);
u32 *command = kzalloc(2, GFP_KERNEL);
u32 *offset = kzalloc(2, GFP_KERNEL);
u32 *crc = kzalloc(4, GFP_KERNEL);
int i, filter = 0;

if (pdata->wolopts & WAKE_BCAST) {
const u8 bcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
netdev_info(dev->net, "enabling broadcast detection");
filter_mask[filter * 4] = 0x003F;
filter_mask[filter * 4 + 1] = 0x00;
filter_mask[filter * 4 + 2] = 0x00;
filter_mask[filter * 4 + 3] = 0x00;
command[filter/4] |= 0x05UL << ((filter % 4) * 8);
offset[filter/4] |= 0x00 << ((filter % 4) * 8);
crc[filter/2] |= smsc_crc(bcast, 6, filter);
filter++;
}

if (pdata->wolopts & WAKE_MCAST) {
const u8 mcast[] = {0x01, 0x00, 0x5E};
netdev_info(dev->net, "enabling multicast detection");
filter_mask[filter * 4] = 0x0007;
filter_mask[filter * 4 + 1] = 0x00;
filter_mask[filter * 4 + 2] = 0x00;
filter_mask[filter * 4 + 3] = 0x00;
command[filter/4] |= 0x09UL << ((filter % 4) * 8);
offset[filter/4] |= 0x00 << ((filter % 4) * 8);
crc[filter/2] |= smsc_crc(mcast, 3, filter);
filter++;
}

if (pdata->wolopts & WAKE_ARP) {
const u8 arp[] = {0x08, 0x06};
netdev_info(dev->net, "enabling ARP detection");
filter_mask[filter * 4] = 0x0003;
filter_mask[filter * 4 + 1] = 0x00;
filter_mask[filter * 4 + 2] = 0x00;
filter_mask[filter * 4 + 3] = 0x00;
command[filter/4] |= 0x05UL << ((filter % 4) * 8);
offset[filter/4] |= 0x0C << ((filter % 4) * 8);
crc[filter/2] |= smsc_crc(arp, 2, filter);
filter++;
}

if (pdata->wolopts & WAKE_UCAST) {
netdev_info(dev->net, "enabling unicast detection");
filter_mask[filter * 4] = 0x003F;
filter_mask[filter * 4 + 1] = 0x00;
filter_mask[filter * 4 + 2] = 0x00;
filter_mask[filter * 4 + 3] = 0x00;
command[filter/4] |= 0x01UL << ((filter % 4) * 8);
offset[filter/4] |= 0x00 << ((filter % 4) * 8);
crc[filter/2] |= smsc_crc(dev->net->dev_addr, ETH_ALEN, filter);
filter++;
}

for (i = 0; i < (pdata->wuff_filter_count * 4); i++) {
ret = smsc95xx_write_reg(dev, WUFF, filter_mask[i]);
check_warn_return(ret, "Error writing WUFF");
}

for (i = 0; i < (pdata->wuff_filter_count / 4); i++) {
ret = smsc95xx_write_reg(dev, WUFF, command[i]);
check_warn_return(ret, "Error writing WUFF");
}

for (i = 0; i < (pdata->wuff_filter_count / 4); i++) {
ret = smsc95xx_write_reg(dev, WUFF, offset[i]);
check_warn_return(ret, "Error writing WUFF");
}

for (i = 0; i < (pdata->wuff_filter_count / 2); i++) {
ret = smsc95xx_write_reg(dev, WUFF, crc[i]);
check_warn_return(ret, "Error writing WUFF");
}

/* clear any pending pattern match packet status */
ret = smsc95xx_read_reg(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR");

val |= WUCSR_WUFR_;

ret = smsc95xx_write_reg(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR");
}

if (pdata->wolopts & WAKE_MAGIC) {
/* clear any pending magic packet status */
ret = smsc95xx_read_reg(dev, WUCSR, &val);
Expand All @@ -1060,10 +1167,18 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
check_warn_return(ret, "Error writing WUCSR");
}

/* enable/disable magic packup wake */
/* enable/disable wakeup sources */
ret = smsc95xx_read_reg(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR");

if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) {
netdev_info(dev->net, "enabling pattern match wakeup");
val |= WUCSR_WAKE_EN_;
} else {
netdev_info(dev->net, "disabling pattern match wakeup");
val &= ~WUCSR_WAKE_EN_;
}

if (pdata->wolopts & WAKE_MAGIC) {
netdev_info(dev->net, "enabling magic packet wakeup");
val |= WUCSR_MPEN_;
Expand All @@ -1084,7 +1199,7 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
ret = smsc95xx_write_reg(dev, PM_CTRL, val);
check_warn_return(ret, "Error writing PM_CTRL");

/* enable receiver */
/* enable receiver to enable frame reception */
smsc95xx_start_rx_path(dev);

/* some wol options are enabled, so enter SUSPEND0 */
Expand Down Expand Up @@ -1123,14 +1238,14 @@ static int smsc95xx_resume(struct usb_interface *intf)

BUG_ON(!dev);

if (pdata->wolopts & WAKE_MAGIC) {
if (pdata->wolopts) {
smsc95xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);

/* Disable magic packup wake */
/* clear wake-up sources */
ret = smsc95xx_read_reg(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR");

val &= ~WUCSR_MPEN_;
val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_);

ret = smsc95xx_write_reg(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR");
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/usb/smsc95xx.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000)
#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF)
#define ID_REV_CHIP_ID_9500_ (0x9500)
#define ID_REV_CHIP_ID_9500A_ (0x9E00)
#define ID_REV_CHIP_ID_9512_ (0xEC00)

#define INT_STS (0x08)
#define INT_STS_TX_STOP_ (0x00020000)
Expand Down Expand Up @@ -203,8 +205,11 @@
#define VLAN2 (0x124)

#define WUFF (0x128)
#define LAN9500_WUFF_NUM (4)
#define LAN9500A_WUFF_NUM (8)

#define WUCSR (0x12C)
#define WUCSR_WFF_PTR_RST_ (0x80000000)
#define WUCSR_GUE_ (0x00000200)
#define WUCSR_WUFR_ (0x00000040)
#define WUCSR_MPR_ (0x00000020)
Expand Down

0 comments on commit bbd9f9e

Please sign in to comment.