/*
- * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 #include <linux/pci.h>
 #include <linux/moduleparam.h>
 #include <linux/interrupt.h>
-
+#include <linux/suspend.h>
 #include "wil6210.h"
 
 static bool use_msi = true;
 module_param(use_msi, bool, S_IRUGO);
 MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
 
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
+static int wil6210_pm_notify(struct notifier_block *notify_block,
+                            unsigned long mode, void *unused);
+#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM */
+
 static
 void wil_set_capabilities(struct wil6210_priv *wil)
 {
                goto bus_disable;
        }
 
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
+       wil->pm_notify.notifier_call = wil6210_pm_notify;
+       rc = register_pm_notifier(&wil->pm_notify);
+       if (rc)
+               /* Do not fail the driver initialization, as suspend can
+                * be prevented in a later phase if needed
+                */
+               wil_err(wil, "register_pm_notifier failed: %d\n", rc);
+#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM */
+
        wil6210_debugfs_init(wil);
 
 
 
        wil_dbg_misc(wil, "%s()\n", __func__);
 
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
+       unregister_pm_notifier(&wil->pm_notify);
+#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM */
+
        wil6210_debugfs_remove(wil);
        wil_if_remove(wil);
        wil_if_pcie_disable(wil);
        return rc;
 }
 
+static int wil6210_pm_notify(struct notifier_block *notify_block,
+                            unsigned long mode, void *unused)
+{
+       struct wil6210_priv *wil = container_of(
+               notify_block, struct wil6210_priv, pm_notify);
+       int rc = 0;
+       enum wil_platform_event evt;
+
+       wil_dbg_pm(wil, "%s: mode (%ld)\n", __func__, mode);
+
+       switch (mode) {
+       case PM_HIBERNATION_PREPARE:
+       case PM_SUSPEND_PREPARE:
+       case PM_RESTORE_PREPARE:
+               rc = wil_can_suspend(wil, false);
+               if (rc)
+                       break;
+               evt = WIL_PLATFORM_EVT_PRE_SUSPEND;
+               if (wil->platform_ops.notify)
+                       rc = wil->platform_ops.notify(wil->platform_handle,
+                                                     evt);
+               break;
+       case PM_POST_SUSPEND:
+       case PM_POST_HIBERNATION:
+       case PM_POST_RESTORE:
+               evt = WIL_PLATFORM_EVT_POST_SUSPEND;
+               if (wil->platform_ops.notify)
+                       rc = wil->platform_ops.notify(wil->platform_handle,
+                                                     evt);
+               break;
+       default:
+               wil_dbg_pm(wil, "unhandled notify mode %ld\n", mode);
+               break;
+       }
+
+       wil_dbg_pm(wil, "notification mode %ld: rc (%d)\n", mode, rc);
+       return rc;
+}
+
 static int wil6210_pm_suspend(struct device *dev)
 {
        return wil6210_suspend(dev, false);
 
 /*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014,2016 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
        wil_dbg_pm(wil, "%s(%s)\n", __func__,
                   is_runtime ? "runtime" : "system");
 
+       if (!netif_running(wil_to_ndev(wil))) {
+               /* can always sleep when down */
+               wil_dbg_pm(wil, "Interface is down\n");
+               goto out;
+       }
+       if (test_bit(wil_status_resetting, wil->status)) {
+               wil_dbg_pm(wil, "Delay suspend when resetting\n");
+               rc = -EBUSY;
+               goto out;
+       }
+       if (wil->recovery_state != fw_recovery_idle) {
+               wil_dbg_pm(wil, "Delay suspend during recovery\n");
+               rc = -EBUSY;
+               goto out;
+       }
+
+       /* interface is running */
        switch (wdev->iftype) {
        case NL80211_IFTYPE_MONITOR:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
+               if (test_bit(wil_status_fwconnecting, wil->status)) {
+                       wil_dbg_pm(wil, "Delay suspend when connecting\n");
+                       rc = -EBUSY;
+                       goto out;
+               }
                break;
        /* AP-like interface - can't suspend */
        default:
                break;
        }
 
+out:
        wil_dbg_pm(wil, "%s(%s) => %s (%d)\n", __func__,
                   is_runtime ? "runtime" : "system", rc ? "No" : "Yes", rc);
 
 
 /*
- * Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2016 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
        WIL_PLATFORM_EVT_FW_CRASH = 0,
        WIL_PLATFORM_EVT_PRE_RESET = 1,
        WIL_PLATFORM_EVT_FW_RDY = 2,
+       WIL_PLATFORM_EVT_PRE_SUSPEND = 3,
+       WIL_PLATFORM_EVT_POST_SUSPEND = 4,
 };
 
 /**