#define SUPPORTED_WAKE                 (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \
                                         WAKE_MCAST | WAKE_ARP | WAKE_MAGIC)
 
+#define SUSPEND_SUSPEND0               (0x01)
+#define SUSPEND_SUSPEND1               (0x02)
+#define SUSPEND_SUSPEND2               (0x04)
+#define SUSPEND_SUSPEND3               (0x08)
+#define SUSPEND_REMOTEWAKE             (0x10)
+#define SUSPEND_ALLMODES               (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
+                                        SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
+
 #define check_warn(ret, fmt, args...) \
        ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
 
        struct mutex dataport_mutex;
        spinlock_t rfe_ctl_lock;
        struct work_struct set_multicast;
+       u8 suspend_flags;
 };
 
 struct usb_context {
 
 static int smsc75xx_enter_suspend0(struct usbnet *dev)
 {
+       struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
        u32 val;
        int ret;
 
 
        smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
 
+       pdata->suspend_flags |= SUSPEND_SUSPEND0 | SUSPEND_REMOTEWAKE;
+
        return 0;
 }
 
 static int smsc75xx_enter_suspend1(struct usbnet *dev)
 {
+       struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
        u32 val;
        int ret;
 
 
        smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
 
+       pdata->suspend_flags |= SUSPEND_SUSPEND1 | SUSPEND_REMOTEWAKE;
+
        return 0;
 }
 
 static int smsc75xx_enter_suspend2(struct usbnet *dev)
 {
+       struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
        u32 val;
        int ret;
 
        ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
        check_warn_return(ret, "Error writing PMT_CTL\n");
 
+       pdata->suspend_flags |= SUSPEND_SUSPEND2;
+
+       return 0;
+}
+
+static int smsc75xx_enter_suspend3(struct usbnet *dev)
+{
+       struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
+       u32 val;
+       int ret;
+
+       ret = smsc75xx_read_reg_nopm(dev, FCT_RX_CTL, &val);
+       check_warn_return(ret, "Error reading FCT_RX_CTL\n");
+
+       if (val & FCT_RX_CTL_RXUSED) {
+               netdev_dbg(dev->net, "rx fifo not empty in autosuspend\n");
+               return -EBUSY;
+       }
+
+       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_3 | PMT_CTL_RES_CLR_WKP_EN;
+
+       ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
+       check_warn_return(ret, "Error writing PMT_CTL\n");
+
+       /* clear wol status */
+       val &= ~PMT_CTL_WUPS;
+       val |= PMT_CTL_WUPS_WOL;
+
+       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);
+
+       pdata->suspend_flags |= SUSPEND_SUSPEND3 | SUSPEND_REMOTEWAKE;
+
        return 0;
 }
 
        return !!(ret & BMSR_LSTATUS);
 }
 
+static int smsc75xx_autosuspend(struct usbnet *dev, u32 link_up)
+{
+       int ret;
+
+       if (!netif_running(dev->net)) {
+               /* interface is ifconfig down so fully power down hw */
+               netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n");
+               return smsc75xx_enter_suspend2(dev);
+       }
+
+       if (!link_up) {
+               /* link is down so enter EDPD mode */
+               netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n");
+
+               /* enable PHY wakeup events for if cable is attached */
+               ret = smsc75xx_enable_phy_wakeup_interrupts(dev,
+                       PHY_INT_MASK_ANEG_COMP);
+               check_warn_return(ret, "error enabling PHY wakeup ints\n");
+
+               netdev_info(dev->net, "entering SUSPEND1 mode\n");
+               return smsc75xx_enter_suspend1(dev);
+       }
+
+       /* enable PHY wakeup events so we remote wakeup if cable is pulled */
+       ret = smsc75xx_enable_phy_wakeup_interrupts(dev,
+               PHY_INT_MASK_LINK_DOWN);
+       check_warn_return(ret, "error enabling PHY wakeup ints\n");
+
+       netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n");
+       return smsc75xx_enter_suspend3(dev);
+}
+
 static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct usbnet *dev = usb_get_intfdata(intf);
        ret = usbnet_suspend(intf, message);
        check_warn_goto_done(ret, "usbnet_suspend error\n");
 
+       if (pdata->suspend_flags) {
+               netdev_warn(dev->net, "error during last resume\n");
+               pdata->suspend_flags = 0;
+       }
+
        /* determine if link is up using only _nopm functions */
        link_up = smsc75xx_link_ok_nopm(dev);
 
+       if (message.event == PM_EVENT_AUTO_SUSPEND) {
+               ret = smsc75xx_autosuspend(dev, link_up);
+               goto done;
+       }
+
+       /* if we get this far we're not autosuspending */
        /* if no wol options set, or if link is down and we're not waking on
         * PHY activity, enter lowest power SUSPEND2 mode
         */
 {
        struct usbnet *dev = usb_get_intfdata(intf);
        struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
+       u8 suspend_flags = pdata->suspend_flags;
        int ret;
        u32 val;
 
-       if (pdata->wolopts) {
-               netdev_info(dev->net, "resuming from SUSPEND0\n");
+       netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags);
 
-               smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
+       /* do this first to ensure it's cleared even in error case */
+       pdata->suspend_flags = 0;
+
+       if (suspend_flags & SUSPEND_REMOTEWAKE) {
+               ret = smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
+               check_warn_return(ret, "Error disabling remote wakeup\n");
+       }
 
+       if (suspend_flags & SUSPEND_ALLMODES) {
                /* Disable wakeup sources */
                ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
                check_warn_return(ret, "Error reading WUCSR\n");
 
                ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
                check_warn_return(ret, "Error writing PMT_CTL\n");
-       } else {
+       }
+
+       if (suspend_flags & SUSPEND_SUSPEND2) {
                netdev_info(dev->net, "resuming from SUSPEND2\n");
 
                ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
        return skb;
 }
 
+static int smsc75xx_manage_power(struct usbnet *dev, int on)
+{
+       dev->intf->needs_remote_wakeup = on;
+       return 0;
+}
+
 static const struct driver_info smsc75xx_info = {
        .description    = "smsc75xx USB 2.0 Gigabit Ethernet",
        .bind           = smsc75xx_bind,
        .rx_fixup       = smsc75xx_rx_fixup,
        .tx_fixup       = smsc75xx_tx_fixup,
        .status         = smsc75xx_status,
+       .manage_power   = smsc75xx_manage_power,
        .flags          = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR,
 };
 
        .reset_resume   = smsc75xx_resume,
        .disconnect     = usbnet_disconnect,
        .disable_hub_initiated_lpm = 1,
+       .supports_autosuspend = 1,
 };
 
 module_usb_driver(smsc75xx_driver);