JOYCON_CTLR_STATE_INIT,
        JOYCON_CTLR_STATE_READ,
        JOYCON_CTLR_STATE_REMOVED,
+       JOYCON_CTLR_STATE_SUSPENDED,
 };
 
 /* Controller type received as part of device info */
 
 static int nintendo_hid_resume(struct hid_device *hdev)
 {
-       int ret = joycon_init(hdev);
+       struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
+       int ret;
+
+       hid_dbg(hdev, "resume\n");
+       if (!joycon_using_usb(ctlr)) {
+               hid_dbg(hdev, "no-op resume for bt ctlr\n");
+               ctlr->ctlr_state = JOYCON_CTLR_STATE_READ;
+               return 0;
+       }
 
+       ret = joycon_init(hdev);
        if (ret)
-               hid_err(hdev, "Failed to restore controller after resume");
+               hid_err(hdev,
+                       "Failed to restore controller after resume: %d\n",
+                       ret);
+       else
+               ctlr->ctlr_state = JOYCON_CTLR_STATE_READ;
 
        return ret;
 }
 
+static int nintendo_hid_suspend(struct hid_device *hdev, pm_message_t message)
+{
+       struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
+
+       hid_dbg(hdev, "suspend: %d\n", message.event);
+       /*
+        * Avoid any blocking loops in suspend/resume transitions.
+        *
+        * joycon_enforce_subcmd_rate() can result in repeated retries if for
+        * whatever reason the controller stops providing input reports.
+        *
+        * This has been observed with bluetooth controllers which lose
+        * connectivity prior to suspend (but not long enough to result in
+        * complete disconnection).
+        */
+       ctlr->ctlr_state = JOYCON_CTLR_STATE_SUSPENDED;
+       return 0;
+}
+
 #endif
 
 static const struct hid_device_id nintendo_hid_devices[] = {
 
 #ifdef CONFIG_PM
        .resume         = nintendo_hid_resume,
+       .suspend        = nintendo_hid_suspend,
 #endif
 };
 static int __init nintendo_init(void)