+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\n");
+
+ ret = smsc75xx_write_reg(dev, mask_base, wuf_mask1);
+ check_warn_return(ret, "Error writing WUF_MASKX\n");
+
+ ret = smsc75xx_write_reg(dev, mask_base + 4, 0);
+ check_warn_return(ret, "Error writing WUF_MASKX\n");
+
+ ret = smsc75xx_write_reg(dev, mask_base + 8, 0);
+ check_warn_return(ret, "Error writing WUF_MASKX\n");
+
+ ret = smsc75xx_write_reg(dev, mask_base + 12, 0);
+ check_warn_return(ret, "Error writing WUF_MASKX\n");
+
+ return 0;
+}
+
+static int smsc75xx_enter_suspend0(struct usbnet *dev)
+{
+ u32 val;
+ int ret;
+
+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
+ check_warn_return(ret, "Error reading PMT_CTL\n");
+
+ val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST));
+ val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS;
+
+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
+ check_warn_return(ret, "Error writing PMT_CTL\n");
+
+ smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
+
+ return 0;
+}
+
+static int smsc75xx_enter_suspend1(struct usbnet *dev)
+{
+ u32 val;
+ int ret;
+
+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
+ check_warn_return(ret, "Error reading PMT_CTL\n");
+
+ val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST);
+ val |= PMT_CTL_SUS_MODE_1;
+
+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
+ check_warn_return(ret, "Error writing PMT_CTL\n");
+
+ /* clear wol status, enable energy detection */
+ val &= ~PMT_CTL_WUPS;
+ val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN);
+
+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
+ check_warn_return(ret, "Error writing PMT_CTL\n");
+
+ smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
+
+ return 0;
+}
+
+static int smsc75xx_enter_suspend2(struct usbnet *dev)
+{
+ u32 val;
+ int ret;
+
+ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
+ check_warn_return(ret, "Error reading PMT_CTL\n");
+
+ val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST);
+ val |= PMT_CTL_SUS_MODE_2;
+
+ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
+ check_warn_return(ret, "Error writing PMT_CTL\n");
+
+ return 0;
+}
+
+static int smsc75xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask)
+{
+ struct mii_if_info *mii = &dev->mii;
+ int ret;
+
+ netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n");
+
+ /* read to clear */
+ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC);
+ check_warn_return(ret, "Error reading PHY_INT_SRC\n");
+
+ /* enable interrupt source */
+ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK);
+ check_warn_return(ret, "Error reading PHY_INT_MASK\n");
+
+ ret |= mask;
+
+ smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret);
+
+ return 0;
+}
+
+static int smsc75xx_link_ok_nopm(struct usbnet *dev)
+{
+ struct mii_if_info *mii = &dev->mii;
+ int ret;
+
+ /* first, a dummy read, needed to latch some MII phys */
+ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR);
+ check_warn_return(ret, "Error reading MII_BMSR\n");
+
+ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR);
+ check_warn_return(ret, "Error reading MII_BMSR\n");
+
+ return !!(ret & BMSR_LSTATUS);
+}
+