linux/drivers/net/wireless/ath/wil6210/pm.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
   3 * Copyright (c) 2018-2019, 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 < GET_MAX_VIFS(wil); 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 < GET_MAX_VIFS(wil); 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 < GET_MAX_VIFS(wil); 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 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        rc = down_write_trylock(&wil->mem_lock);
 199        if (!rc) {
 200                wil_err(wil,
 201                        "device is busy. down_write_trylock failed, returned (0x%x)\n",
 202                        rc);
 203                wil->suspend_stats.rejected_by_host++;
 204                return -EBUSY;
 205        }
 206
 207        set_bit(wil_status_suspending, wil->status);
 208        up_write(&wil->mem_lock);
 209
 210        wil_pm_stop_all_net_queues(wil);
 211
 212        if (!wil_is_tx_idle(wil)) {
 213                wil_dbg_pm(wil, "Pending TX data, reject suspend\n");
 214                wil->suspend_stats.rejected_by_host++;
 215                goto reject_suspend;
 216        }
 217
 218        if (!wil->txrx_ops.is_rx_idle(wil)) {
 219                wil_dbg_pm(wil, "Pending RX data, reject suspend\n");
 220                wil->suspend_stats.rejected_by_host++;
 221                goto reject_suspend;
 222        }
 223
 224        if (!wil_is_wmi_idle(wil)) {
 225                wil_dbg_pm(wil, "Pending WMI events, reject suspend\n");
 226                wil->suspend_stats.rejected_by_host++;
 227                goto reject_suspend;
 228        }
 229
 230        /* Send WMI suspend request to the device */
 231        rc = wmi_suspend(wil);
 232        if (rc) {
 233                wil_dbg_pm(wil, "wmi_suspend failed, reject suspend (%d)\n",
 234                           rc);
 235                goto reject_suspend;
 236        }
 237
 238        /* Wait for completion of the pending RX packets */
 239        data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS);
 240        if (test_bit(wil_status_napi_en, wil->status)) {
 241                while (!wil->txrx_ops.is_rx_idle(wil)) {
 242                        if (time_after(jiffies, data_comp_to)) {
 243                                if (wil->txrx_ops.is_rx_idle(wil))
 244                                        break;
 245                                wil_err(wil,
 246                                        "TO waiting for idle RX, suspend failed\n");
 247                                wil->suspend_stats.r_on.failed_suspends++;
 248                                goto resume_after_fail;
 249                        }
 250                        wil_dbg_ratelimited(wil, "rx vring is not empty -> NAPI\n");
 251                        napi_synchronize(&wil->napi_rx);
 252                        msleep(20);
 253                }
 254        }
 255
 256        /* In case of pending WMI events, reject the suspend
 257         * and resume the device.
 258         * This can happen if the device sent the WMI events before
 259         * approving the suspend.
 260         */
 261        if (!wil_is_wmi_idle(wil)) {
 262                wil_err(wil, "suspend failed due to pending WMI events\n");
 263                wil->suspend_stats.r_on.failed_suspends++;
 264                goto resume_after_fail;
 265        }
 266
 267        wil_mask_irq(wil);
 268
 269        /* Disable device reset on PERST */
 270        wil_s(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
 271
 272        if (wil->platform_ops.suspend) {
 273                rc = wil->platform_ops.suspend(wil->platform_handle, true);
 274                if (rc) {
 275                        wil_err(wil, "platform device failed to suspend (%d)\n",
 276                                rc);
 277                        wil->suspend_stats.r_on.failed_suspends++;
 278                        wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
 279                        wil_unmask_irq(wil);
 280                        goto resume_after_fail;
 281                }
 282        }
 283
 284        /* Save the current bus request to return to the same in resume */
 285        wil->bus_request_kbps_pre_suspend = wil->bus_request_kbps;
 286        wil6210_bus_request(wil, 0);
 287
 288        set_bit(wil_status_suspended, wil->status);
 289        clear_bit(wil_status_suspending, wil->status);
 290
 291        return rc;
 292
 293resume_after_fail:
 294        set_bit(wil_status_resuming, wil->status);
 295        clear_bit(wil_status_suspending, wil->status);
 296        rc = wmi_resume(wil);
 297        /* if resume succeeded, reject the suspend */
 298        if (!rc) {
 299                rc = -EBUSY;
 300                wil_pm_wake_connected_net_queues(wil);
 301        }
 302        return rc;
 303
 304reject_suspend:
 305        clear_bit(wil_status_suspending, wil->status);
 306        wil_pm_wake_connected_net_queues(wil);
 307        return -EBUSY;
 308}
 309
 310static int wil_suspend_radio_off(struct wil6210_priv *wil)
 311{
 312        int rc = 0;
 313        bool active_ifaces;
 314
 315        wil_dbg_pm(wil, "suspend radio off\n");
 316
 317        rc = down_write_trylock(&wil->mem_lock);
 318        if (!rc) {
 319                wil_err(wil,
 320                        "device is busy. down_write_trylock failed, returned (0x%x)\n",
 321                        rc);
 322                wil->suspend_stats.rejected_by_host++;
 323                return -EBUSY;
 324        }
 325
 326        set_bit(wil_status_suspending, wil->status);
 327        up_write(&wil->mem_lock);
 328
 329        /* if netif up, hardware is alive, shut it down */
 330        mutex_lock(&wil->vif_mutex);
 331        active_ifaces = wil_has_active_ifaces(wil, true, false);
 332        mutex_unlock(&wil->vif_mutex);
 333
 334        if (active_ifaces) {
 335                rc = wil_down(wil);
 336                if (rc) {
 337                        wil_err(wil, "wil_down : %d\n", rc);
 338                        wil->suspend_stats.r_off.failed_suspends++;
 339                        goto out;
 340                }
 341        }
 342
 343        /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */
 344        wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n");
 345        wil_disable_irq(wil);
 346
 347        if (wil->platform_ops.suspend) {
 348                rc = wil->platform_ops.suspend(wil->platform_handle, false);
 349                if (rc) {
 350                        wil_enable_irq(wil);
 351                        wil->suspend_stats.r_off.failed_suspends++;
 352                        goto out;
 353                }
 354        }
 355
 356        set_bit(wil_status_suspended, wil->status);
 357
 358out:
 359        clear_bit(wil_status_suspending, wil->status);
 360        wil_dbg_pm(wil, "suspend radio off: %d\n", rc);
 361
 362        return rc;
 363}
 364
 365static int wil_resume_radio_off(struct wil6210_priv *wil)
 366{
 367        int rc = 0;
 368        bool active_ifaces;
 369
 370        wil_dbg_pm(wil, "Enabling PCIe IRQ\n");
 371        wil_enable_irq(wil);
 372        /* if any netif up, bring hardware up
 373         * During open(), IFF_UP set after actual device method
 374         * invocation. This prevent recursive call to wil_up()
 375         * wil_status_suspended will be cleared in wil_reset
 376         */
 377        mutex_lock(&wil->vif_mutex);
 378        active_ifaces = wil_has_active_ifaces(wil, true, false);
 379        mutex_unlock(&wil->vif_mutex);
 380        if (active_ifaces)
 381                rc = wil_up(wil);
 382        else
 383                clear_bit(wil_status_suspended, wil->status);
 384
 385        return rc;
 386}
 387
 388int wil_suspend(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
 389{
 390        int rc = 0;
 391
 392        wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system");
 393
 394        if (test_bit(wil_status_suspended, wil->status)) {
 395                wil_dbg_pm(wil, "trying to suspend while suspended\n");
 396                return 0;
 397        }
 398
 399        if (!keep_radio_on)
 400                rc = wil_suspend_radio_off(wil);
 401        else
 402                rc = wil_suspend_keep_radio_on(wil);
 403
 404        wil_dbg_pm(wil, "suspend: %s => %d\n",
 405                   is_runtime ? "runtime" : "system", rc);
 406
 407        return rc;
 408}
 409
 410int wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on)
 411{
 412        int rc = 0;
 413
 414        wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");
 415
 416        if (wil->platform_ops.resume) {
 417                rc = wil->platform_ops.resume(wil->platform_handle,
 418                                              keep_radio_on);
 419                if (rc) {
 420                        wil_err(wil, "platform_ops.resume : %d\n", rc);
 421                        goto out;
 422                }
 423        }
 424
 425        if (keep_radio_on)
 426                rc = wil_resume_keep_radio_on(wil);
 427        else
 428                rc = wil_resume_radio_off(wil);
 429
 430out:
 431        wil_dbg_pm(wil, "resume: %s => %d\n", is_runtime ? "runtime" : "system",
 432                   rc);
 433        return rc;
 434}
 435
 436void wil_pm_runtime_allow(struct wil6210_priv *wil)
 437{
 438        struct device *dev = wil_to_dev(wil);
 439
 440        pm_runtime_put_noidle(dev);
 441        pm_runtime_set_autosuspend_delay(dev, WIL6210_AUTOSUSPEND_DELAY_MS);
 442        pm_runtime_use_autosuspend(dev);
 443        pm_runtime_allow(dev);
 444}
 445
 446void wil_pm_runtime_forbid(struct wil6210_priv *wil)
 447{
 448        struct device *dev = wil_to_dev(wil);
 449
 450        pm_runtime_forbid(dev);
 451        pm_runtime_get_noresume(dev);
 452}
 453
 454int wil_pm_runtime_get(struct wil6210_priv *wil)
 455{
 456        int rc;
 457        struct device *dev = wil_to_dev(wil);
 458
 459        rc = pm_runtime_get_sync(dev);
 460        if (rc < 0) {
 461                wil_err(wil, "pm_runtime_get_sync() failed, rc = %d\n", rc);
 462                pm_runtime_put_noidle(dev);
 463                return rc;
 464        }
 465
 466        return 0;
 467}
 468
 469void wil_pm_runtime_put(struct wil6210_priv *wil)
 470{
 471        struct device *dev = wil_to_dev(wil);
 472
 473        pm_runtime_mark_last_busy(dev);
 474        pm_runtime_put_autosuspend(dev);
 475}
 476