Skip to content

Commit

Permalink
smsc75xx: 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.

Signed-off-by: Steve Glendinning <steve.glendinning@shawell.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Steve Glendinning authored and David S. Miller committed Nov 3, 2012
1 parent 4f99ad5 commit 899a391
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 33 deletions.
2 changes: 2 additions & 0 deletions drivers/net/usb/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ config USB_NET_DM9601
config USB_NET_SMSC75XX
tristate "SMSC LAN75XX based USB 2.0 gigabit 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
155 changes: 122 additions & 33 deletions drivers/net/usb/smsc75xx.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 @@ -52,7 +54,8 @@
#define USB_PRODUCT_ID_LAN7500 (0x7500)
#define USB_PRODUCT_ID_LAN7505 (0x7505)
#define RXW_PADDING 2
#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 Down Expand Up @@ -1143,6 +1146,36 @@ static void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf)
}
}

static u16 smsc_crc(const u8 *buffer, size_t len)
{
return bitrev16(crc16(0xFFFF, buffer, len));
}

static int smsc75xx_write_wuff(struct usbnet *dev, int filter, u32 wuf_cfg,
u32 wuf_mask1)
{
int cfg_base = WUF_CFGX + filter * 4;
int mask_base = WUF_MASKX + filter * 16;
int ret;

ret = smsc75xx_write_reg(dev, cfg_base, wuf_cfg);
check_warn_return(ret, "Error writing WUF_CFGX");

ret = smsc75xx_write_reg(dev, mask_base, wuf_mask1);
check_warn_return(ret, "Error writing WUF_MASKX");

ret = smsc75xx_write_reg(dev, mask_base + 4, 0);
check_warn_return(ret, "Error writing WUF_MASKX");

ret = smsc75xx_write_reg(dev, mask_base + 8, 0);
check_warn_return(ret, "Error writing WUF_MASKX");

ret = smsc75xx_write_reg(dev, mask_base + 12, 0);
check_warn_return(ret, "Error writing WUF_MASKX");

return 0;
}

static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
Expand Down Expand Up @@ -1187,42 +1220,107 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
return 0;
}

if (pdata->wolopts & WAKE_MAGIC) {
/* clear any pending magic packet status */
if (pdata->wolopts & (WAKE_MCAST | WAKE_ARP)) {
int i, filter = 0;

/* disable all filters */
for (i = 0; i < WUF_NUM; i++) {
ret = smsc75xx_write_reg(dev, WUF_CFGX + i * 4, 0);
check_warn_return(ret, "Error writing WUF_CFGX");
}

if (pdata->wolopts & WAKE_MCAST) {
const u8 mcast[] = {0x01, 0x00, 0x5E};
netdev_info(dev->net, "enabling multicast detection");

val = WUF_CFGX_EN | WUF_CFGX_ATYPE_MULTICAST
| smsc_crc(mcast, 3);
ret = smsc75xx_write_wuff(dev, filter++, val, 0x0007);
check_warn_return(ret, "Error writing wakeup filter");
}

if (pdata->wolopts & WAKE_ARP) {
const u8 arp[] = {0x08, 0x06};
netdev_info(dev->net, "enabling ARP detection");

val = WUF_CFGX_EN | WUF_CFGX_ATYPE_ALL | (0x0C << 16)
| smsc_crc(arp, 2);
ret = smsc75xx_write_wuff(dev, filter++, val, 0x0003);
check_warn_return(ret, "Error writing wakeup filter");
}

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

val |= WUCSR_WUFR;

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

netdev_info(dev->net, "enabling packet match detection");
ret = smsc75xx_read_reg(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR");

val |= WUCSR_WUEN;

ret = smsc75xx_write_reg(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR");
} else {
netdev_info(dev->net, "disabling packet match detection");
ret = smsc75xx_read_reg(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR");

val |= WUCSR_MPR;
val &= ~WUCSR_WUEN;

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

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

val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN);

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

if (pdata->wolopts & WAKE_MAGIC) {
netdev_info(dev->net, "enabling magic packet wakeup");
val |= WUCSR_MPEN;
} else {
netdev_info(dev->net, "disabling magic packet wakeup");
val &= ~WUCSR_MPEN;
ret = smsc75xx_read_reg(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR");

/* clear any pending magic packet status */
val |= WUCSR_MPR | WUCSR_MPEN;

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

ret = smsc75xx_write_reg(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR");
if (pdata->wolopts & WAKE_BCAST) {
netdev_info(dev->net, "enabling broadcast detection");
ret = smsc75xx_read_reg(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR");

/* enable wol wakeup source */
ret = smsc75xx_read_reg(dev, PMT_CTL, &val);
check_warn_return(ret, "Error reading PMT_CTL");
val |= WUCSR_BCAST_FR | WUCSR_BCST_EN;

val |= PMT_CTL_WOL_EN;
ret = smsc75xx_write_reg(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR");
}

ret = smsc75xx_write_reg(dev, PMT_CTL, val);
check_warn_return(ret, "Error writing PMT_CTL");
if (pdata->wolopts & WAKE_UCAST) {
netdev_info(dev->net, "enabling unicast detection");
ret = smsc75xx_read_reg(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR");

val |= WUCSR_WUFR | WUCSR_PFDA_EN;

/* enable receiver */
ret = smsc75xx_write_reg(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR");
}

/* enable receiver to enable frame reception */
ret = smsc75xx_read_reg(dev, MAC_RX, &val);
check_warn_return(ret, "Failed to read MAC_RX: %d", ret);

Expand All @@ -1237,22 +1335,12 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
ret = smsc75xx_read_reg(dev, PMT_CTL, &val);
check_warn_return(ret, "Error reading PMT_CTL");

val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST));
val |= PMT_CTL_SUS_MODE_0;

ret = smsc75xx_write_reg(dev, PMT_CTL, val);
check_warn_return(ret, "Error writing PMT_CTL");
val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST));
val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS;

/* clear wol status */
val &= ~PMT_CTL_WUPS;
val |= PMT_CTL_WUPS_WOL;
ret = smsc75xx_write_reg(dev, PMT_CTL, val);
check_warn_return(ret, "Error writing PMT_CTL");

/* read back PMT_CTL */
ret = smsc75xx_read_reg(dev, PMT_CTL, &val);
check_warn_return(ret, "Error reading PMT_CTL");

smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);

return 0;
Expand All @@ -1265,16 +1353,17 @@ static int smsc75xx_resume(struct usb_interface *intf)
int ret;
u32 val;

if (pdata->wolopts & WAKE_MAGIC) {
if (pdata->wolopts) {
netdev_info(dev->net, "resuming from SUSPEND0");

smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);

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

val &= ~WUCSR_MPEN;
val &= ~(WUCSR_WUEN | WUCSR_MPEN | WUCSR_PFDA_EN
| WUCSR_BCST_EN);

ret = smsc75xx_write_reg(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR");
Expand Down

0 comments on commit 899a391

Please sign in to comment.