smsc75xx: add wol support for more frame types
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>
This commit is contained in:
parent
4f99ad5129
commit
899a391b75
|
@ -248,6 +248,8 @@ config USB_NET_DM9601
|
||||||
config USB_NET_SMSC75XX
|
config USB_NET_SMSC75XX
|
||||||
tristate "SMSC LAN75XX based USB 2.0 gigabit ethernet devices"
|
tristate "SMSC LAN75XX based USB 2.0 gigabit ethernet devices"
|
||||||
depends on USB_USBNET
|
depends on USB_USBNET
|
||||||
|
select BITREVERSE
|
||||||
|
select CRC16
|
||||||
select CRC32
|
select CRC32
|
||||||
help
|
help
|
||||||
This option adds support for SMSC LAN95XX based USB 2.0
|
This option adds support for SMSC LAN95XX based USB 2.0
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
#include <linux/ethtool.h>
|
#include <linux/ethtool.h>
|
||||||
#include <linux/mii.h>
|
#include <linux/mii.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
#include <linux/bitrev.h>
|
||||||
|
#include <linux/crc16.h>
|
||||||
#include <linux/crc32.h>
|
#include <linux/crc32.h>
|
||||||
#include <linux/usb/usbnet.h>
|
#include <linux/usb/usbnet.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -52,7 +54,8 @@
|
||||||
#define USB_PRODUCT_ID_LAN7500 (0x7500)
|
#define USB_PRODUCT_ID_LAN7500 (0x7500)
|
||||||
#define USB_PRODUCT_ID_LAN7505 (0x7505)
|
#define USB_PRODUCT_ID_LAN7505 (0x7505)
|
||||||
#define RXW_PADDING 2
|
#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...) \
|
#define check_warn(ret, fmt, args...) \
|
||||||
({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
|
({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
|
||||||
|
@ -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)
|
static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
|
||||||
{
|
{
|
||||||
struct usbnet *dev = usb_get_intfdata(intf);
|
struct usbnet *dev = usb_get_intfdata(intf);
|
||||||
|
@ -1187,42 +1220,107 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->wolopts & WAKE_MAGIC) {
|
if (pdata->wolopts & (WAKE_MCAST | WAKE_ARP)) {
|
||||||
/* clear any pending magic packet status */
|
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);
|
ret = smsc75xx_read_reg(dev, WUCSR, &val);
|
||||||
check_warn_return(ret, "Error reading WUCSR");
|
check_warn_return(ret, "Error reading WUCSR");
|
||||||
|
|
||||||
val |= WUCSR_MPR;
|
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_WUEN;
|
||||||
|
|
||||||
ret = smsc75xx_write_reg(dev, WUCSR, val);
|
ret = smsc75xx_write_reg(dev, WUCSR, val);
|
||||||
check_warn_return(ret, "Error writing WUCSR");
|
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);
|
ret = smsc75xx_read_reg(dev, WUCSR, &val);
|
||||||
check_warn_return(ret, "Error reading WUCSR");
|
check_warn_return(ret, "Error reading WUCSR");
|
||||||
|
|
||||||
if (pdata->wolopts & WAKE_MAGIC) {
|
val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN);
|
||||||
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_write_reg(dev, WUCSR, val);
|
ret = smsc75xx_write_reg(dev, WUCSR, val);
|
||||||
check_warn_return(ret, "Error writing WUCSR");
|
check_warn_return(ret, "Error writing WUCSR");
|
||||||
|
|
||||||
/* enable wol wakeup source */
|
if (pdata->wolopts & WAKE_MAGIC) {
|
||||||
ret = smsc75xx_read_reg(dev, PMT_CTL, &val);
|
netdev_info(dev->net, "enabling magic packet wakeup");
|
||||||
check_warn_return(ret, "Error reading PMT_CTL");
|
ret = smsc75xx_read_reg(dev, WUCSR, &val);
|
||||||
|
check_warn_return(ret, "Error reading WUCSR");
|
||||||
|
|
||||||
val |= PMT_CTL_WOL_EN;
|
/* clear any pending magic packet status */
|
||||||
|
val |= WUCSR_MPR | WUCSR_MPEN;
|
||||||
|
|
||||||
ret = smsc75xx_write_reg(dev, PMT_CTL, val);
|
ret = smsc75xx_write_reg(dev, WUCSR, val);
|
||||||
check_warn_return(ret, "Error writing PMT_CTL");
|
check_warn_return(ret, "Error writing WUCSR");
|
||||||
|
}
|
||||||
|
|
||||||
/* enable receiver */
|
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");
|
||||||
|
|
||||||
|
val |= WUCSR_BCAST_FR | WUCSR_BCST_EN;
|
||||||
|
|
||||||
|
ret = smsc75xx_write_reg(dev, WUCSR, val);
|
||||||
|
check_warn_return(ret, "Error writing WUCSR");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
ret = smsc75xx_read_reg(dev, MAC_RX, &val);
|
||||||
check_warn_return(ret, "Failed to read MAC_RX: %d", ret);
|
check_warn_return(ret, "Failed to read MAC_RX: %d", ret);
|
||||||
|
|
||||||
|
@ -1237,22 +1335,12 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
|
||||||
ret = smsc75xx_read_reg(dev, PMT_CTL, &val);
|
ret = smsc75xx_read_reg(dev, PMT_CTL, &val);
|
||||||
check_warn_return(ret, "Error reading PMT_CTL");
|
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 | PMT_CTL_PHY_RST));
|
||||||
val |= PMT_CTL_SUS_MODE_0;
|
val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS;
|
||||||
|
|
||||||
ret = smsc75xx_write_reg(dev, PMT_CTL, val);
|
ret = smsc75xx_write_reg(dev, PMT_CTL, val);
|
||||||
check_warn_return(ret, "Error writing PMT_CTL");
|
check_warn_return(ret, "Error writing PMT_CTL");
|
||||||
|
|
||||||
/* 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);
|
smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1265,16 +1353,17 @@ static int smsc75xx_resume(struct usb_interface *intf)
|
||||||
int ret;
|
int ret;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
if (pdata->wolopts & WAKE_MAGIC) {
|
if (pdata->wolopts) {
|
||||||
netdev_info(dev->net, "resuming from SUSPEND0");
|
netdev_info(dev->net, "resuming from SUSPEND0");
|
||||||
|
|
||||||
smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
|
smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
|
||||||
|
|
||||||
/* Disable magic packup wake */
|
/* Disable wakeup sources */
|
||||||
ret = smsc75xx_read_reg(dev, WUCSR, &val);
|
ret = smsc75xx_read_reg(dev, WUCSR, &val);
|
||||||
check_warn_return(ret, "Error reading WUCSR");
|
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);
|
ret = smsc75xx_write_reg(dev, WUCSR, val);
|
||||||
check_warn_return(ret, "Error writing WUCSR");
|
check_warn_return(ret, "Error writing WUCSR");
|
||||||
|
|
Loading…
Reference in New Issue