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