diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index e62882c68dd787a092d36ba1c73073dc1ddd37f4..ef976215b649330d0c6873b8185f3fefc34b1651 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -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 diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 1baa53ad8e10437422dbc4ca4c05110768065b4b..85d70c2dd3971131b873c89b2c12a2ede5fa32ae 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -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); }) @@ -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); @@ -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); @@ -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; @@ -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");