linux/drivers/net/wireless/ath/wil6210/pm.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
   3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
   4 *
   5 * Permission to use, copy, modify, and/or distribute this software for any
   6 * purpose with or without fee is hereby granted, provided that the above
   7 * copyright notice and this permission notice appear in all copies.
   8 *
   9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16 */
  17
  18#include "wil6210.h"
  19#include <linux/jiffies.h>
  20#include <linux/pm_runtime.h>
  21
  22#define WIL6210_AUTOSUSPEND_DELAY_MS (1000)
  23
  24static void wil_pm_wake_connected_net_queues(struct wil6210_priv *wil)
  25{
  26        int i;
  27
  28        mutex_lock(&wil->vif_mutex);
  29        for (i = 0; i < wil->max_vifs; i++) {
  30                struct wil6210_vif *vif = wil->vifs[i];
  31
  32                if (vif && test_bit(wil_vif_fwconnected, vif->status))
  33                        wil_update_net_queues_bh(wil, vif, NULL, false);
  34        }
  35        mutex_unlock(&wil->vif_mutex);
  36}
  37
  38static void wil_pm_stop_all_net_queues(struct wil6210_priv *wil)
  39{
  40        int i;
  41
  42        mutex_lock(&wil->vif_mutex);
  43        for (i = 0; i < wil->max_vifs; i++) {
  44                struct wil6210_vif *vif = wil->vifs[i];
  45
  46                if (vif)
  47                        wil_update_net_queues_bh(wil, vif, NULL, true);
  48        }
  49        mutex_unlock(&wil->vif_mutex);
  50}
  51
  52static bool
  53wil_can_suspend_vif(struct wil6210_priv *wil, struct wil6210_vif *vif,
  54                    bool is_runtime)
  55{
  56        struct wireless_dev *wdev = vif_to_wdev(vif);
  57
  58        switch (wdev->iftype) {
  59        case NL80211_IFTYPE_MONITOR:
  60                wil_dbg_pm(wil, "Sniffer\n");
  61                return false;
  62
  63        /* for STA-like interface, don't runtime suspend */
  64        case NL80211_IFTYPE_STATION:
  65        case NL80211_IFTYPE_P2P_CLIENT:
  66                if (test_bit(wil_vif_fwconnecting, vif->status)) {
  67                        wil_dbg_pm(wil, "Delay suspend when connecting\n");
  68                        return false;
  69                }
  70                if (is_runtime) {
  71                        wil_dbg_pm(wil, "STA-like interface\n");
  72                        return false;
  73                }
  74                break;
  75        /* AP-like interface - can't suspend */
  76        default:
  77                wil_dbg_pm(wil, "AP-like interface\n");
  78                return false;
  79        }
  80
  81        return true;
  82}
  83
  84int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
  85{
  86        int rc = 0, i;
  87        bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
  88                                 wil->fw_capabilities);
  89        bool active_ifaces;
  90
  91        wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system");
  92
  93        if (wmi_only || debug_fw) {
  94                wil_dbg_pm(wil, "Deny any suspend - %s mode\n",
  95                           wmi_only ? "wmi_only" : "debug_fw");
  96                rc = -EBUSY;
  97                goto out;
  98        }
  99        if (is_runtime && !wil->platform_ops.suspend) {
 100                rc = -EBUSY;
 101                goto out;
 102        }
 103
 104        mutex_lock(&wil->vif_mutex);
 105        active_ifaces = wil_has_active_ifaces(wil, true, false);
 106        mutex_unlock(&wil->vif_mutex);
 107
 108        if (!active_ifaces) {
 109                /* can always sleep when down */
 110                wil_dbg_pm(wil, "Interface is down\n");
 111                goto out;
 112        }
 113        if (test_bit(wil_status_resetting, wil->status)) {
 114                wil_dbg_pm(wil, "Delay suspend when resetting\n");
 115                rc = -EBUSY;
 116                goto out;
 117        }
 118        if (wil->recovery_state != fw_recovery_idle) {
 119                wil_dbg_pm(wil, "Delay suspend during recovery\n");
 120                rc = -EBUSY;
 121                goto out;
 122        }
 123
 124        /* interface is running */
 125        mutex_lock(&wil->vif_mutex);
 126        for (i = 0; i < wil->max_vifs; i++) {
 127                struct wil6210_vif *vif = wil->vifs[i];
 128
 129                if (!vif)
 130                        continue;
 131                if (!wil_can_suspend_vif(wil, vif, is_runtime)) {
 132                        rc = -EBUSY;
 133                        mutex_unlock(&wil->vif_mutex);
 134                        goto out;
 135                }
 136        }
 137        mutex_unlock(&wil->vif_mutex);
 138
 139out:
 140        wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n",
 141                   is_runtime ? "runtime" : "system", rc ? "No" : "Yes", rc);
 142
 143        if (rc)
 144                wil->suspend_stats.rejected_by_host++;
 145
 146        return rc;
 147}
 148
 149static int wil_resume_keep_radio_on(struct wil6210_priv *wil)
 150{
 151        int rc = 0;
 152
 153        /* wil_status_resuming will be cleared when getting
 154         * WMI_TRAFFIC_RESUME_EVENTID
 155         */
 156        set_bit(wil_status_resuming, wil->status);
 157        clear_bit(wil_status_suspended, wil->status);
 158        wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
 159        wil_unmask_irq(wil);
 160
 161        wil6210_bus_request(wil, wil->bus_request_kbps_pre_suspend);
 162
 163        /* Send WMI resume request to the device */
 164        rc = wmi_resume(wil);
 165        if (rc) {
 166                wil_err(wil, "device failed to resume (%d)\n", rc);
 167                if (no_fw_recovery)
 168                        goto out;
 169                rc = wil_down(wil);
 170                if (rc) {
 171                        wil_err(wil, "wil_down failed (%d)\n", rc);
 172                        goto out;
 173                }
 174                rc = wil_up(wil);
 175                if (rc) {
 176                        wil_err(wil, "wil_up failed (%d)\n", rc);
 177                        goto out;
 178                }
 179        }
 180
 181        /* Wake all queues */
 182        wil_pm_wake_connected_net_queues(wil);
 183
 184out:
 185        if (rc)
 186                set_bit(wil_status_suspended, wil->status);
 187        return rc;
 188}
 189
 190static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
 191{
 192        int rc = 0;
 193        unsigned long start, data_comp_to;
 194
 195        wil_dbg_pm(wil, "suspend keep radio on\n");
 196
 197        /* Prevent handling of new tx and wmi commands */
 198        set_bit(wil_status_suspending, wil->status);
 199        if (test_bit(wil_status_collecting_dumps, wil->status)) {
 200                /* Device collects crash dump, cancel the suspend */
 201                wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
 202                clear_bit(wil_status_suspending, wil->status);
 203                wil->suspend_stats.rejected_by_host++;
 204                return -EBUSY;
 205        }
 206        wil_pm_stop_all_net_queues(wil);
 207
 208        if (!wil_is_tx_idle(wil)) {
 209                wil_dbg_pm(wil, "Pending TX data, reject suspend\n");
 210                wil->suspend_stats.rejected_by_host++;
 211                goto reject_suspend;
 212        }
 213
 214        if (!wil_is_rx_idle(wil)) {
 215                wil_dbg_pm(wil, "Pending RX data, reject suspend\n");
 216                wil->suspend_stats.rejected_by_host++;
 217                goto reject_suspend;
 218        }
 219
 220        if (!wil_is_wmi_idle(wil)) {
 221                wil_dbg_pm(wil, "Pending WMI events, reject suspend\n");
 222                wil->suspend_stats.rejected_by_host++;
 223                goto reject_suspend;
 224        }
 225
 226        /* Send WMI suspend request to the device */
 227        rc = wmi_suspend(wil);
 228        if (rc) {
 229                wil_dbg_pm(wil, "wmi_suspend failed, reject suspend (%d)\n",
 230                           rc);
 231                goto reject_suspend;
 232        }
 233
 234        /* Wait for completion of the pending RX packets */
 235        start = jiffies;
 236        data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS);
 237        if (test_bit(wil_status_napi_en, wil->status)) {
 238                while (!wil_is_rx_idle(wil)) {
 239                        if (time_after(jiffies, data_comp_to)) {
 240                                if (wil_is_rx_idle(wil))
 241                                        break;
 242                                wil_err(wil,
 243                                        "TO waiting for idle RX, suspend failed\n");
 244                                wil->suspend_stats.r_on.failed_suspends++;
 245                                goto resume_after_fail;
 246                        }
 247                        wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n");
 248                        napi_synchronize(&wil->napi_rx);
 249                        msleep(20);
 250                }
 251        }
 252
 253        /* In case of pending WMI events, reject the suspend
 254         * and resume the device.
 255         * This can happen if the device sent the WMI events before
 256         * approving the suspend.
 257         */
 258        if (!wil_is_wmi_idle(wil)) {
 259                wil_err(wil, "suspend failed due to pending WMI events\n");
 260                wil->suspend_stats.r_on.failed_suspends++;
 261                goto resume_after_fail;
 262        }
 263
 264        wil_mask_irq(wil);
 265
 266        /* Disable device reset on PERST */
 267        wil_s(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
 268
 269        if (wil->platform_ops.suspend) {
 270                rc = wil->platform_ops.suspend(wil->platform_handle, true);
 271                if (rc) {
 272                        wil_err(wil, "platform device failed to suspend (%d)\n",
 273                                rc);
 274                        wil->suspend_stats.r_on.failed_suspends++;
 275                        wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
 276                        wil_unmask_irq(wil);
 277                        goto resume_after_fail;
 278                }
 279        }
 280
 281        /* Save the current bus request to return to the same in resume */
 282        wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps;
 283        wil6210_bus_request(wil, 0);
 284
 285        set_bit(wil_status_suspended, wil->status);
 286        clear_bit(wil_status_suspending, wil->status);
 287
 288        return rc;
 289
 290resume_after_fail:
 291        set_bit(wil_status_resuming, wil->status);
 292        clear_bit(wil_status_suspending, wil->status);
 293        rc = wmi_resume(wil);
 294        /* if resume succeeded, reject the suspend */
 295        if (!rc) {
 296                rc = -EBUSY;
 297                wil_pm_wake_connected_net_queues(wil);
 298        }
 299        return rc;
 300
 301reject_suspend:
 302        clear_bit(wil_status_suspending, wil->status);
 303        wil_pm_wake_connected_net_queues(wil);
 304        return -EBUSY;
 305}
 306
 307static int wil_suspend_radio_off(struct wil6210_priv *wil)
 308{
 309        int rc = 0;
 310        bool active_ifaces;
 311
 312        wil_dbg_pm(wil, "suspend radio off\n");
 313
 314        set_bit(wil_status_suspending, wil->status);
 315        if (test_bit(wil_status_collecting_dumps, wil->status)) {
 316                /* Device collects crash dump, cancel the suspend */
 317                wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
 318                clear_bit(wil_status_suspending, wil->status);
 319                wil->suspend_stats.rejected_by_host++;
 320                return -EBUSY;
 321        }
 322
 323        /* if netif up, hardware is alive, shut it down */
 324        mutex_lock(&wil->vif_mutex);
 325        active_ifaces = wil_has_active_ifaces(wil, true, false);
 326        mutex_unlock(&wil->vif_mutex);
 327
 328        if (active_ifaces) {
 329                rc = wil_down(wil);
 330                if (rc) {
 331                        wil_err(wil, "wil_down : %d\n", rc);
 332                        wil->suspend_stats.r_off.failed_suspends++;
 333                        goto out;
 334                }
 335        }
 336
 337        /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */
 338        wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n");
 339        wil_disable_irq(wil);
 340
 341        if (wil->platform_ops.suspend) {
 342                rc = wil->platform_ops.suspend(wil->platform_handle, false);
 343                if (rc) {
 344                        wil_enable_irq(wil);
 345                        wil->suspend_stats.r_off.failed_suspends++;
 346                        goto out;
 347                }
 348        }
 349
 350        set_bit(wil_status_suspended, wil->status);
 351
 352out:
 353        clear_bit(wil_status_suspending, wil->status);
 354        wil_dbg_pm(wil, "suspend radio off: %d\n", rc);
 355
 356        return rc;
 357}
 358
 359static int wil_resume_radio_off(struct wil6210_priv *wil)
 360{
 361        int rc = 0;
 362        bool active_ifaces;
 363
 364        wil_dbg_pm(wil, "Enabling PCIe IRQ\n");
 365        wil_enable_irq(wil);
 366        /* if any netif up, bring hardware up
 367         * During open(), IFF_UP set after actual device method
 368         * invocation. This prevent recursive call to wil_up()
 369         * wil_status_suspended will be cleared in wil_reset
 370         */
 371        mutex_lock(&wil->vif_mutex);
 372        active_ifaces = wil_has_active_ifaces(wil, true, false);
 373        mutex_unlock(&wil->vif_mutex);
 374        if (active_ifaces)
 375                rc = wil_up(wil);
 376        else
 377                clear_bit(wil_status_suspended, wil->status);
 378
 379        return rc;
 380}
 381
 382int wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
 383{
 384        int rc = 0;
 385
 386        wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
 387
 388        if (test_bit(wil_status_suspended, wil->status)) {
 389                wil_dbg_pm(wil, "trying to suspend while suspended\n");
 390                return 0;
 391        }
 392
 393        if (!keep_radio_on)
 394                rc = wil_suspend_radio_off(wil);
 395        else
 396                rc = wil_suspend_keep_radio_on(wil);
 397
 398        wil_dbg_pm(wil, "suspend: %s => %d\n",
 399                   is_runtime ? "runtime" : "system", rc);
 400
 401        return rc;
 402}
 403
 404int wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
 405{
 406        int rc = 0;
 407
 408        wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
 409
 410        if (wil->platform_ops.resume) {
 411                rc = wil->platform_ops.resume(wil->platform_handle,
 412                                              keep_radio_on);
 413                if (rc) {
 414                        wil_err(wil, "platform_ops.resume : %d\n", rc);
 415                        goto out;
 416                }
 417        }
 418
 419        if (keep_radio_on)
 420                rc = wil_resume_keep_radio_on(wil);
 421        else
 422                rc = wil_resume_radio_off(wil);
 423
 424out:
 425        wil_dbg_pm(wil, "resume: %s => %d\n", is_runtime ? "runtime" : "system",
 426                   rc);
 427        return rc;
 428}
 429
 430void wil_pm_runtime_allow(struct wil6210_priv *wil)
 431{
 432        struct device *dev = wil_to_dev(wil);
 433
 434        pm_runtime_put_noidle(dev);
 435        pm_runtime_set_autosuspend_delay(dev, WIL6210_AUTOSUSPEND_DELAY_MS);
 436        pm_runtime_use_autosuspend(dev);
 437        pm_runtime_allow(dev);
 438}
 439
 440void wil_pm_runtime_forbid(struct wil6210_priv *wil)
 441{
 442        struct device *dev = wil_to_dev(wil);
 443
 444        pm_runtime_forbid(dev);
 445        pm_runtime_get_noresume(dev);
 446}
 447
 448int wil_pm_runtime_get(struct wil6210_priv *wil)
 449{
 450        int rc;
 451        struct device *dev = wil_to_dev(wil);
 452
 453        rc = pm_runtime_get_sync(dev);
 454        if (rc < 0) {
 455                wil_err(wil, "pm_runtime_get_sync() failed, rc = %d\n", rc);
 456                pm_runtime_put_noidle(dev);
 457                return rc;
 458        }
 459
 460        return 0;
 461}
 462
 463void wil_pm_runtime_put(struct wil6210_priv *wil)
 464{
 465        struct device *dev = wil_to_dev(wil);
 466
 467        pm_runtime_mark_last_busy(dev);
 468        pm_runtime_put_autosuspend(dev);
 469}
 470