| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * This file is part of wlcore |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2008-2010 Nokia Corporation |
|---|
| 5 | 6 | * Copyright (C) 2011-2013 Texas Instruments Inc. |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or |
|---|
| 8 | | - * modify it under the terms of the GNU General Public License |
|---|
| 9 | | - * version 2 as published by the Free Software Foundation. |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 12 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 14 | | - * General Public License for more details. |
|---|
| 15 | | - * |
|---|
| 16 | | - * You should have received a copy of the GNU General Public License |
|---|
| 17 | | - * along with this program; if not, write to the Free Software |
|---|
| 18 | | - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
|---|
| 19 | | - * 02110-1301 USA |
|---|
| 20 | | - * |
|---|
| 21 | 7 | */ |
|---|
| 22 | 8 | |
|---|
| 23 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 27 | 13 | #include <linux/interrupt.h> |
|---|
| 28 | 14 | #include <linux/irq.h> |
|---|
| 29 | 15 | #include <linux/pm_runtime.h> |
|---|
| 16 | +#include <linux/pm_wakeirq.h> |
|---|
| 30 | 17 | |
|---|
| 31 | 18 | #include "wlcore.h" |
|---|
| 32 | 19 | #include "debug.h" |
|---|
| .. | .. |
|---|
| 43 | 30 | #include "sysfs.h" |
|---|
| 44 | 31 | |
|---|
| 45 | 32 | #define WL1271_BOOT_RETRIES 3 |
|---|
| 46 | | -#define WL1271_SUSPEND_SLEEP 100 |
|---|
| 47 | 33 | #define WL1271_WAKEUP_TIMEOUT 500 |
|---|
| 48 | 34 | |
|---|
| 49 | 35 | static char *fwlog_param; |
|---|
| .. | .. |
|---|
| 496 | 482 | } |
|---|
| 497 | 483 | |
|---|
| 498 | 484 | /* update the host-chipset time offset */ |
|---|
| 499 | | - wl->time_offset = (ktime_get_boot_ns() >> 10) - |
|---|
| 485 | + wl->time_offset = (ktime_get_boottime_ns() >> 10) - |
|---|
| 500 | 486 | (s64)(status->fw_localtime); |
|---|
| 501 | 487 | |
|---|
| 502 | 488 | wl->fw_fast_lnk_map = status->link_fast_bitmap; |
|---|
| .. | .. |
|---|
| 534 | 520 | int ret = 0; |
|---|
| 535 | 521 | u32 intr; |
|---|
| 536 | 522 | int loopcount = WL1271_IRQ_MAX_LOOPS; |
|---|
| 523 | + bool run_tx_queue = true; |
|---|
| 537 | 524 | bool done = false; |
|---|
| 538 | 525 | unsigned int defer_count; |
|---|
| 539 | 526 | unsigned long flags; |
|---|
| .. | .. |
|---|
| 557 | 544 | } |
|---|
| 558 | 545 | |
|---|
| 559 | 546 | while (!done && loopcount--) { |
|---|
| 560 | | - /* |
|---|
| 561 | | - * In order to avoid a race with the hardirq, clear the flag |
|---|
| 562 | | - * before acknowledging the chip. |
|---|
| 563 | | - */ |
|---|
| 564 | | - clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); |
|---|
| 565 | 547 | smp_mb__after_atomic(); |
|---|
| 566 | 548 | |
|---|
| 567 | 549 | ret = wlcore_fw_status(wl, wl->fw_status); |
|---|
| 568 | 550 | if (ret < 0) |
|---|
| 569 | | - goto out; |
|---|
| 551 | + goto err_ret; |
|---|
| 570 | 552 | |
|---|
| 571 | 553 | wlcore_hw_tx_immediate_compl(wl); |
|---|
| 572 | 554 | |
|---|
| .. | .. |
|---|
| 583 | 565 | ret = -EIO; |
|---|
| 584 | 566 | |
|---|
| 585 | 567 | /* restarting the chip. ignore any other interrupt. */ |
|---|
| 586 | | - goto out; |
|---|
| 568 | + goto err_ret; |
|---|
| 587 | 569 | } |
|---|
| 588 | 570 | |
|---|
| 589 | 571 | if (unlikely(intr & WL1271_ACX_SW_INTR_WATCHDOG)) { |
|---|
| .. | .. |
|---|
| 593 | 575 | ret = -EIO; |
|---|
| 594 | 576 | |
|---|
| 595 | 577 | /* restarting the chip. ignore any other interrupt. */ |
|---|
| 596 | | - goto out; |
|---|
| 578 | + goto err_ret; |
|---|
| 597 | 579 | } |
|---|
| 598 | 580 | |
|---|
| 599 | 581 | if (likely(intr & WL1271_ACX_INTR_DATA)) { |
|---|
| .. | .. |
|---|
| 601 | 583 | |
|---|
| 602 | 584 | ret = wlcore_rx(wl, wl->fw_status); |
|---|
| 603 | 585 | if (ret < 0) |
|---|
| 604 | | - goto out; |
|---|
| 586 | + goto err_ret; |
|---|
| 605 | 587 | |
|---|
| 606 | 588 | /* Check if any tx blocks were freed */ |
|---|
| 607 | | - spin_lock_irqsave(&wl->wl_lock, flags); |
|---|
| 608 | | - if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && |
|---|
| 609 | | - wl1271_tx_total_queue_count(wl) > 0) { |
|---|
| 610 | | - spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 589 | + if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) { |
|---|
| 590 | + if (spin_trylock_irqsave(&wl->wl_lock, flags)) { |
|---|
| 591 | + if (!wl1271_tx_total_queue_count(wl)) |
|---|
| 592 | + run_tx_queue = false; |
|---|
| 593 | + spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 594 | + } |
|---|
| 595 | + |
|---|
| 611 | 596 | /* |
|---|
| 612 | 597 | * In order to avoid starvation of the TX path, |
|---|
| 613 | 598 | * call the work function directly. |
|---|
| 614 | 599 | */ |
|---|
| 615 | | - ret = wlcore_tx_work_locked(wl); |
|---|
| 616 | | - if (ret < 0) |
|---|
| 617 | | - goto out; |
|---|
| 618 | | - } else { |
|---|
| 619 | | - spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 600 | + if (run_tx_queue) { |
|---|
| 601 | + ret = wlcore_tx_work_locked(wl); |
|---|
| 602 | + if (ret < 0) |
|---|
| 603 | + goto err_ret; |
|---|
| 604 | + } |
|---|
| 620 | 605 | } |
|---|
| 621 | 606 | |
|---|
| 622 | 607 | /* check for tx results */ |
|---|
| 623 | 608 | ret = wlcore_hw_tx_delayed_compl(wl); |
|---|
| 624 | 609 | if (ret < 0) |
|---|
| 625 | | - goto out; |
|---|
| 610 | + goto err_ret; |
|---|
| 626 | 611 | |
|---|
| 627 | 612 | /* Make sure the deferred queues don't get too long */ |
|---|
| 628 | 613 | defer_count = skb_queue_len(&wl->deferred_tx_queue) + |
|---|
| .. | .. |
|---|
| 635 | 620 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); |
|---|
| 636 | 621 | ret = wl1271_event_handle(wl, 0); |
|---|
| 637 | 622 | if (ret < 0) |
|---|
| 638 | | - goto out; |
|---|
| 623 | + goto err_ret; |
|---|
| 639 | 624 | } |
|---|
| 640 | 625 | |
|---|
| 641 | 626 | if (intr & WL1271_ACX_INTR_EVENT_B) { |
|---|
| 642 | 627 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); |
|---|
| 643 | 628 | ret = wl1271_event_handle(wl, 1); |
|---|
| 644 | 629 | if (ret < 0) |
|---|
| 645 | | - goto out; |
|---|
| 630 | + goto err_ret; |
|---|
| 646 | 631 | } |
|---|
| 647 | 632 | |
|---|
| 648 | 633 | if (intr & WL1271_ACX_INTR_INIT_COMPLETE) |
|---|
| .. | .. |
|---|
| 653 | 638 | wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); |
|---|
| 654 | 639 | } |
|---|
| 655 | 640 | |
|---|
| 641 | +err_ret: |
|---|
| 656 | 642 | pm_runtime_mark_last_busy(wl->dev); |
|---|
| 657 | 643 | pm_runtime_put_autosuspend(wl->dev); |
|---|
| 658 | 644 | |
|---|
| .. | .. |
|---|
| 665 | 651 | int ret; |
|---|
| 666 | 652 | unsigned long flags; |
|---|
| 667 | 653 | struct wl1271 *wl = cookie; |
|---|
| 654 | + bool queue_tx_work = true; |
|---|
| 655 | + |
|---|
| 656 | + set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); |
|---|
| 668 | 657 | |
|---|
| 669 | 658 | /* complete the ELP completion */ |
|---|
| 670 | | - spin_lock_irqsave(&wl->wl_lock, flags); |
|---|
| 671 | | - set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); |
|---|
| 672 | | - if (wl->elp_compl) { |
|---|
| 673 | | - complete(wl->elp_compl); |
|---|
| 674 | | - wl->elp_compl = NULL; |
|---|
| 659 | + if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags)) { |
|---|
| 660 | + spin_lock_irqsave(&wl->wl_lock, flags); |
|---|
| 661 | + if (wl->elp_compl) |
|---|
| 662 | + complete(wl->elp_compl); |
|---|
| 663 | + spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 675 | 664 | } |
|---|
| 676 | 665 | |
|---|
| 677 | 666 | if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) { |
|---|
| 678 | 667 | /* don't enqueue a work right now. mark it as pending */ |
|---|
| 679 | 668 | set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags); |
|---|
| 680 | 669 | wl1271_debug(DEBUG_IRQ, "should not enqueue work"); |
|---|
| 670 | + spin_lock_irqsave(&wl->wl_lock, flags); |
|---|
| 681 | 671 | disable_irq_nosync(wl->irq); |
|---|
| 682 | 672 | pm_wakeup_event(wl->dev, 0); |
|---|
| 683 | 673 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 684 | | - return IRQ_HANDLED; |
|---|
| 674 | + goto out_handled; |
|---|
| 685 | 675 | } |
|---|
| 686 | | - spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 687 | 676 | |
|---|
| 688 | 677 | /* TX might be handled here, avoid redundant work */ |
|---|
| 689 | 678 | set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); |
|---|
| .. | .. |
|---|
| 695 | 684 | if (ret) |
|---|
| 696 | 685 | wl12xx_queue_recovery_work(wl); |
|---|
| 697 | 686 | |
|---|
| 698 | | - spin_lock_irqsave(&wl->wl_lock, flags); |
|---|
| 699 | | - /* In case TX was not handled here, queue TX work */ |
|---|
| 687 | + /* In case TX was not handled in wlcore_irq_locked(), queue TX work */ |
|---|
| 700 | 688 | clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags); |
|---|
| 701 | | - if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && |
|---|
| 702 | | - wl1271_tx_total_queue_count(wl) > 0) |
|---|
| 703 | | - ieee80211_queue_work(wl->hw, &wl->tx_work); |
|---|
| 704 | | - spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 689 | + if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) { |
|---|
| 690 | + if (spin_trylock_irqsave(&wl->wl_lock, flags)) { |
|---|
| 691 | + if (!wl1271_tx_total_queue_count(wl)) |
|---|
| 692 | + queue_tx_work = false; |
|---|
| 693 | + spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 694 | + } |
|---|
| 695 | + if (queue_tx_work) |
|---|
| 696 | + ieee80211_queue_work(wl->hw, &wl->tx_work); |
|---|
| 697 | + } |
|---|
| 705 | 698 | |
|---|
| 706 | 699 | mutex_unlock(&wl->mutex); |
|---|
| 700 | + |
|---|
| 701 | +out_handled: |
|---|
| 702 | + clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); |
|---|
| 707 | 703 | |
|---|
| 708 | 704 | return IRQ_HANDLED; |
|---|
| 709 | 705 | } |
|---|
| .. | .. |
|---|
| 1447 | 1443 | |
|---|
| 1448 | 1444 | field = &filter->fields[filter->num_fields]; |
|---|
| 1449 | 1445 | |
|---|
| 1450 | | - field->pattern = kzalloc(len, GFP_KERNEL); |
|---|
| 1446 | + field->pattern = kmemdup(pattern, len, GFP_KERNEL); |
|---|
| 1451 | 1447 | if (!field->pattern) { |
|---|
| 1452 | 1448 | wl1271_warning("Failed to allocate RX filter pattern"); |
|---|
| 1453 | 1449 | return -ENOMEM; |
|---|
| .. | .. |
|---|
| 1458 | 1454 | field->offset = cpu_to_le16(offset); |
|---|
| 1459 | 1455 | field->flags = flags; |
|---|
| 1460 | 1456 | field->len = len; |
|---|
| 1461 | | - memcpy(field->pattern, pattern, len); |
|---|
| 1462 | 1457 | |
|---|
| 1463 | 1458 | return 0; |
|---|
| 1464 | 1459 | } |
|---|
| .. | .. |
|---|
| 1760 | 1755 | |
|---|
| 1761 | 1756 | ret = wl1271_configure_suspend(wl, wlvif, wow); |
|---|
| 1762 | 1757 | if (ret < 0) { |
|---|
| 1763 | | - mutex_unlock(&wl->mutex); |
|---|
| 1764 | | - wl1271_warning("couldn't prepare device to suspend"); |
|---|
| 1765 | | - return ret; |
|---|
| 1758 | + goto out_sleep; |
|---|
| 1766 | 1759 | } |
|---|
| 1767 | 1760 | } |
|---|
| 1768 | 1761 | |
|---|
| .. | .. |
|---|
| 2712 | 2705 | |
|---|
| 2713 | 2706 | if (!wlcore_is_p2p_mgmt(wlvif)) { |
|---|
| 2714 | 2707 | ret = wl12xx_cmd_role_disable(wl, &wlvif->role_id); |
|---|
| 2715 | | - if (ret < 0) |
|---|
| 2708 | + if (ret < 0) { |
|---|
| 2709 | + pm_runtime_put_noidle(wl->dev); |
|---|
| 2716 | 2710 | goto deinit; |
|---|
| 2711 | + } |
|---|
| 2717 | 2712 | } else { |
|---|
| 2718 | 2713 | ret = wl12xx_cmd_role_disable(wl, &wlvif->dev_role_id); |
|---|
| 2719 | | - if (ret < 0) |
|---|
| 2714 | + if (ret < 0) { |
|---|
| 2715 | + pm_runtime_put_noidle(wl->dev); |
|---|
| 2720 | 2716 | goto deinit; |
|---|
| 2717 | + } |
|---|
| 2721 | 2718 | } |
|---|
| 2722 | 2719 | |
|---|
| 2723 | 2720 | pm_runtime_mark_last_busy(wl->dev); |
|---|
| .. | .. |
|---|
| 3274 | 3271 | static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, |
|---|
| 3275 | 3272 | u8 id, u8 key_type, u8 key_size, |
|---|
| 3276 | 3273 | const u8 *key, u8 hlid, u32 tx_seq_32, |
|---|
| 3277 | | - u16 tx_seq_16) |
|---|
| 3274 | + u16 tx_seq_16, bool is_pairwise) |
|---|
| 3278 | 3275 | { |
|---|
| 3279 | 3276 | struct wl1271_ap_key *ap_key; |
|---|
| 3280 | 3277 | int i; |
|---|
| .. | .. |
|---|
| 3312 | 3309 | ap_key->hlid = hlid; |
|---|
| 3313 | 3310 | ap_key->tx_seq_32 = tx_seq_32; |
|---|
| 3314 | 3311 | ap_key->tx_seq_16 = tx_seq_16; |
|---|
| 3312 | + ap_key->is_pairwise = is_pairwise; |
|---|
| 3315 | 3313 | |
|---|
| 3316 | 3314 | wlvif->ap.recorded_keys[i] = ap_key; |
|---|
| 3317 | 3315 | return 0; |
|---|
| .. | .. |
|---|
| 3347 | 3345 | key->id, key->key_type, |
|---|
| 3348 | 3346 | key->key_size, key->key, |
|---|
| 3349 | 3347 | hlid, key->tx_seq_32, |
|---|
| 3350 | | - key->tx_seq_16); |
|---|
| 3348 | + key->tx_seq_16, key->is_pairwise); |
|---|
| 3351 | 3349 | if (ret < 0) |
|---|
| 3352 | 3350 | goto out; |
|---|
| 3353 | 3351 | |
|---|
| .. | .. |
|---|
| 3370 | 3368 | static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, |
|---|
| 3371 | 3369 | u16 action, u8 id, u8 key_type, |
|---|
| 3372 | 3370 | u8 key_size, const u8 *key, u32 tx_seq_32, |
|---|
| 3373 | | - u16 tx_seq_16, struct ieee80211_sta *sta) |
|---|
| 3371 | + u16 tx_seq_16, struct ieee80211_sta *sta, |
|---|
| 3372 | + bool is_pairwise) |
|---|
| 3374 | 3373 | { |
|---|
| 3375 | 3374 | int ret; |
|---|
| 3376 | 3375 | bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); |
|---|
| .. | .. |
|---|
| 3397 | 3396 | ret = wl1271_record_ap_key(wl, wlvif, id, |
|---|
| 3398 | 3397 | key_type, key_size, |
|---|
| 3399 | 3398 | key, hlid, tx_seq_32, |
|---|
| 3400 | | - tx_seq_16); |
|---|
| 3399 | + tx_seq_16, is_pairwise); |
|---|
| 3401 | 3400 | } else { |
|---|
| 3402 | 3401 | ret = wl1271_cmd_set_ap_key(wl, wlvif, action, |
|---|
| 3403 | 3402 | id, key_type, key_size, |
|---|
| 3404 | 3403 | key, hlid, tx_seq_32, |
|---|
| 3405 | | - tx_seq_16); |
|---|
| 3404 | + tx_seq_16, is_pairwise); |
|---|
| 3406 | 3405 | } |
|---|
| 3407 | 3406 | |
|---|
| 3408 | 3407 | if (ret < 0) |
|---|
| .. | .. |
|---|
| 3502 | 3501 | u16 tx_seq_16 = 0; |
|---|
| 3503 | 3502 | u8 key_type; |
|---|
| 3504 | 3503 | u8 hlid; |
|---|
| 3504 | + bool is_pairwise; |
|---|
| 3505 | 3505 | |
|---|
| 3506 | 3506 | wl1271_debug(DEBUG_MAC80211, "mac80211 set key"); |
|---|
| 3507 | 3507 | |
|---|
| .. | .. |
|---|
| 3551 | 3551 | return -EOPNOTSUPP; |
|---|
| 3552 | 3552 | } |
|---|
| 3553 | 3553 | |
|---|
| 3554 | + is_pairwise = key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE; |
|---|
| 3555 | + |
|---|
| 3554 | 3556 | switch (cmd) { |
|---|
| 3555 | 3557 | case SET_KEY: |
|---|
| 3556 | 3558 | ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE, |
|---|
| 3557 | 3559 | key_conf->keyidx, key_type, |
|---|
| 3558 | 3560 | key_conf->keylen, key_conf->key, |
|---|
| 3559 | | - tx_seq_32, tx_seq_16, sta); |
|---|
| 3561 | + tx_seq_32, tx_seq_16, sta, is_pairwise); |
|---|
| 3560 | 3562 | if (ret < 0) { |
|---|
| 3561 | 3563 | wl1271_error("Could not add or replace key"); |
|---|
| 3562 | 3564 | return ret; |
|---|
| .. | .. |
|---|
| 3582 | 3584 | ret = wl1271_set_key(wl, wlvif, KEY_REMOVE, |
|---|
| 3583 | 3585 | key_conf->keyidx, key_type, |
|---|
| 3584 | 3586 | key_conf->keylen, key_conf->key, |
|---|
| 3585 | | - 0, 0, sta); |
|---|
| 3587 | + 0, 0, sta, is_pairwise); |
|---|
| 3586 | 3588 | if (ret < 0) { |
|---|
| 3587 | 3589 | wl1271_error("Could not remove key"); |
|---|
| 3588 | 3590 | return ret; |
|---|
| .. | .. |
|---|
| 5751 | 5753 | ieee80211_remain_on_channel_expired(wl->hw); |
|---|
| 5752 | 5754 | } |
|---|
| 5753 | 5755 | |
|---|
| 5754 | | -static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw) |
|---|
| 5756 | +static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw, |
|---|
| 5757 | + struct ieee80211_vif *vif) |
|---|
| 5755 | 5758 | { |
|---|
| 5756 | 5759 | struct wl1271 *wl = hw->priv; |
|---|
| 5757 | 5760 | |
|---|
| .. | .. |
|---|
| 6225 | 6228 | |
|---|
| 6226 | 6229 | ieee80211_hw_set(wl->hw, SUPPORT_FAST_XMIT); |
|---|
| 6227 | 6230 | ieee80211_hw_set(wl->hw, CHANCTX_STA_CSA); |
|---|
| 6231 | + ieee80211_hw_set(wl->hw, SUPPORTS_PER_STA_GTK); |
|---|
| 6228 | 6232 | ieee80211_hw_set(wl->hw, QUEUE_CONTROL); |
|---|
| 6229 | 6233 | ieee80211_hw_set(wl->hw, TX_AMPDU_SETUP_IN_HW); |
|---|
| 6230 | 6234 | ieee80211_hw_set(wl->hw, AMPDU_AGGREGATION); |
|---|
| .. | .. |
|---|
| 6269 | 6273 | |
|---|
| 6270 | 6274 | wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | |
|---|
| 6271 | 6275 | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | |
|---|
| 6272 | | - WIPHY_FLAG_HAS_CHANNEL_SWITCH; |
|---|
| 6276 | + WIPHY_FLAG_HAS_CHANNEL_SWITCH | |
|---|
| 6277 | + WIPHY_FLAG_IBSS_RSN; |
|---|
| 6273 | 6278 | |
|---|
| 6274 | 6279 | wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; |
|---|
| 6275 | 6280 | |
|---|
| .. | .. |
|---|
| 6619 | 6624 | } |
|---|
| 6620 | 6625 | |
|---|
| 6621 | 6626 | #ifdef CONFIG_PM |
|---|
| 6627 | + device_init_wakeup(wl->dev, true); |
|---|
| 6628 | + |
|---|
| 6622 | 6629 | ret = enable_irq_wake(wl->irq); |
|---|
| 6623 | 6630 | if (!ret) { |
|---|
| 6624 | 6631 | wl->irq_wake_enabled = true; |
|---|
| 6625 | | - device_init_wakeup(wl->dev, 1); |
|---|
| 6626 | 6632 | if (pdev_data->pwr_in_suspend) |
|---|
| 6627 | 6633 | wl->hw->wiphy->wowlan = &wlcore_wowlan_support; |
|---|
| 6634 | + } |
|---|
| 6635 | + |
|---|
| 6636 | + res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); |
|---|
| 6637 | + if (res) { |
|---|
| 6638 | + wl->wakeirq = res->start; |
|---|
| 6639 | + wl->wakeirq_flags = res->flags & IRQF_TRIGGER_MASK; |
|---|
| 6640 | + ret = dev_pm_set_dedicated_wake_irq(wl->dev, wl->wakeirq); |
|---|
| 6641 | + if (ret) |
|---|
| 6642 | + wl->wakeirq = -ENODEV; |
|---|
| 6643 | + } else { |
|---|
| 6644 | + wl->wakeirq = -ENODEV; |
|---|
| 6628 | 6645 | } |
|---|
| 6629 | 6646 | #endif |
|---|
| 6630 | 6647 | disable_irq(wl->irq); |
|---|
| .. | .. |
|---|
| 6653 | 6670 | wl1271_unregister_hw(wl); |
|---|
| 6654 | 6671 | |
|---|
| 6655 | 6672 | out_irq: |
|---|
| 6673 | + if (wl->wakeirq >= 0) |
|---|
| 6674 | + dev_pm_clear_wake_irq(wl->dev); |
|---|
| 6675 | + device_init_wakeup(wl->dev, false); |
|---|
| 6656 | 6676 | free_irq(wl->irq, wl); |
|---|
| 6657 | 6677 | |
|---|
| 6658 | 6678 | out_free_nvs: |
|---|
| .. | .. |
|---|
| 6703 | 6723 | unsigned long flags; |
|---|
| 6704 | 6724 | int ret; |
|---|
| 6705 | 6725 | unsigned long start_time = jiffies; |
|---|
| 6706 | | - bool pending = false; |
|---|
| 6707 | 6726 | bool recovery = false; |
|---|
| 6708 | 6727 | |
|---|
| 6709 | 6728 | /* Nothing to do if no ELP mode requested */ |
|---|
| .. | .. |
|---|
| 6713 | 6732 | wl1271_debug(DEBUG_PSM, "waking up chip from elp"); |
|---|
| 6714 | 6733 | |
|---|
| 6715 | 6734 | spin_lock_irqsave(&wl->wl_lock, flags); |
|---|
| 6716 | | - if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags)) |
|---|
| 6717 | | - pending = true; |
|---|
| 6718 | | - else |
|---|
| 6719 | | - wl->elp_compl = &compl; |
|---|
| 6735 | + wl->elp_compl = &compl; |
|---|
| 6720 | 6736 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 6721 | 6737 | |
|---|
| 6722 | 6738 | ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP); |
|---|
| 6723 | 6739 | if (ret < 0) { |
|---|
| 6724 | 6740 | recovery = true; |
|---|
| 6725 | | - goto err; |
|---|
| 6726 | | - } |
|---|
| 6727 | | - |
|---|
| 6728 | | - if (!pending) { |
|---|
| 6741 | + } else if (!test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags)) { |
|---|
| 6729 | 6742 | ret = wait_for_completion_timeout(&compl, |
|---|
| 6730 | 6743 | msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); |
|---|
| 6731 | 6744 | if (ret == 0) { |
|---|
| 6732 | 6745 | wl1271_warning("ELP wakeup timeout!"); |
|---|
| 6733 | | - |
|---|
| 6734 | | - /* Return no error for runtime PM for recovery */ |
|---|
| 6735 | | - ret = 0; |
|---|
| 6736 | 6746 | recovery = true; |
|---|
| 6737 | | - goto err; |
|---|
| 6738 | 6747 | } |
|---|
| 6739 | 6748 | } |
|---|
| 6740 | 6749 | |
|---|
| 6741 | | - clear_bit(WL1271_FLAG_IN_ELP, &wl->flags); |
|---|
| 6742 | | - |
|---|
| 6743 | | - wl1271_debug(DEBUG_PSM, "wakeup time: %u ms", |
|---|
| 6744 | | - jiffies_to_msecs(jiffies - start_time)); |
|---|
| 6745 | | - |
|---|
| 6746 | | - return 0; |
|---|
| 6747 | | - |
|---|
| 6748 | | -err: |
|---|
| 6749 | 6750 | spin_lock_irqsave(&wl->wl_lock, flags); |
|---|
| 6750 | 6751 | wl->elp_compl = NULL; |
|---|
| 6751 | 6752 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
|---|
| 6753 | + clear_bit(WL1271_FLAG_IN_ELP, &wl->flags); |
|---|
| 6752 | 6754 | |
|---|
| 6753 | 6755 | if (recovery) { |
|---|
| 6754 | 6756 | set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags); |
|---|
| 6755 | 6757 | wl12xx_queue_recovery_work(wl); |
|---|
| 6758 | + } else { |
|---|
| 6759 | + wl1271_debug(DEBUG_PSM, "wakeup time: %u ms", |
|---|
| 6760 | + jiffies_to_msecs(jiffies - start_time)); |
|---|
| 6756 | 6761 | } |
|---|
| 6757 | 6762 | |
|---|
| 6758 | | - return ret; |
|---|
| 6763 | + return 0; |
|---|
| 6759 | 6764 | } |
|---|
| 6760 | 6765 | |
|---|
| 6761 | 6766 | static const struct dev_pm_ops wlcore_pm_ops = { |
|---|
| .. | .. |
|---|
| 6817 | 6822 | if (!wl->initialized) |
|---|
| 6818 | 6823 | return 0; |
|---|
| 6819 | 6824 | |
|---|
| 6820 | | - if (wl->irq_wake_enabled) { |
|---|
| 6821 | | - device_init_wakeup(wl->dev, 0); |
|---|
| 6822 | | - disable_irq_wake(wl->irq); |
|---|
| 6825 | + if (wl->wakeirq >= 0) { |
|---|
| 6826 | + dev_pm_clear_wake_irq(wl->dev); |
|---|
| 6827 | + wl->wakeirq = -ENODEV; |
|---|
| 6823 | 6828 | } |
|---|
| 6829 | + |
|---|
| 6830 | + device_init_wakeup(wl->dev, false); |
|---|
| 6831 | + |
|---|
| 6832 | + if (wl->irq_wake_enabled) |
|---|
| 6833 | + disable_irq_wake(wl->irq); |
|---|
| 6834 | + |
|---|
| 6824 | 6835 | wl1271_unregister_hw(wl); |
|---|
| 6825 | 6836 | |
|---|
| 6826 | 6837 | pm_runtime_put_sync(wl->dev); |
|---|