2022-04-01 14:53:08 +03:00
|
|
|
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
|
2024-10-31 08:05:41 +08:00
|
|
|
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
wifi: ath11k: Add missing include of export.h
Commit a934a57a42f6 ("scripts/misc-check: check missing #include
<linux/export.h> when W=1") introduced a new check that is producing
the following warnings:
drivers/net/wireless/ath/ath11k/ce.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/coredump.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/debug.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/debugfs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/dp.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/fw.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/hal.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/pcic.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/qmi.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/trace.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
Add the missing #include to satisfy the check.
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Link: https://patch.msgid.link/20250611-ath-unused-export-v1-4-c36819df7e7b@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
2025-06-11 09:13:57 -07:00
|
|
|
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
2022-04-01 14:53:08 +03:00
|
|
|
*/
|
|
|
|
|
wifi: ath11k: Add missing include of export.h
Commit a934a57a42f6 ("scripts/misc-check: check missing #include
<linux/export.h> when W=1") introduced a new check that is producing
the following warnings:
drivers/net/wireless/ath/ath11k/ce.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/coredump.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/debug.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/debugfs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/dp.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/fw.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/hal.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/pcic.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/qmi.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/net/wireless/ath/ath11k/trace.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
Add the missing #include to satisfy the check.
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Link: https://patch.msgid.link/20250611-ath-unused-export-v1-4-c36819df7e7b@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
2025-06-11 09:13:57 -07:00
|
|
|
#include <linux/export.h>
|
2022-04-01 14:53:08 +03:00
|
|
|
#include "core.h"
|
|
|
|
#include "pcic.h"
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
static const char *irq_name[ATH11K_IRQ_NUM_MAX] = {
|
|
|
|
"bhi",
|
|
|
|
"mhi-er0",
|
|
|
|
"mhi-er1",
|
|
|
|
"ce0",
|
|
|
|
"ce1",
|
|
|
|
"ce2",
|
|
|
|
"ce3",
|
|
|
|
"ce4",
|
|
|
|
"ce5",
|
|
|
|
"ce6",
|
|
|
|
"ce7",
|
|
|
|
"ce8",
|
|
|
|
"ce9",
|
|
|
|
"ce10",
|
|
|
|
"ce11",
|
|
|
|
"host2wbm-desc-feed",
|
|
|
|
"host2reo-re-injection",
|
|
|
|
"host2reo-command",
|
|
|
|
"host2rxdma-monitor-ring3",
|
|
|
|
"host2rxdma-monitor-ring2",
|
|
|
|
"host2rxdma-monitor-ring1",
|
|
|
|
"reo2ost-exception",
|
|
|
|
"wbm2host-rx-release",
|
|
|
|
"reo2host-status",
|
|
|
|
"reo2host-destination-ring4",
|
|
|
|
"reo2host-destination-ring3",
|
|
|
|
"reo2host-destination-ring2",
|
|
|
|
"reo2host-destination-ring1",
|
|
|
|
"rxdma2host-monitor-destination-mac3",
|
|
|
|
"rxdma2host-monitor-destination-mac2",
|
|
|
|
"rxdma2host-monitor-destination-mac1",
|
|
|
|
"ppdu-end-interrupts-mac3",
|
|
|
|
"ppdu-end-interrupts-mac2",
|
|
|
|
"ppdu-end-interrupts-mac1",
|
|
|
|
"rxdma2host-monitor-status-ring-mac3",
|
|
|
|
"rxdma2host-monitor-status-ring-mac2",
|
|
|
|
"rxdma2host-monitor-status-ring-mac1",
|
|
|
|
"host2rxdma-host-buf-ring-mac3",
|
|
|
|
"host2rxdma-host-buf-ring-mac2",
|
|
|
|
"host2rxdma-host-buf-ring-mac1",
|
|
|
|
"rxdma2host-destination-ring-mac3",
|
|
|
|
"rxdma2host-destination-ring-mac2",
|
|
|
|
"rxdma2host-destination-ring-mac1",
|
|
|
|
"host2tcl-input-ring4",
|
|
|
|
"host2tcl-input-ring3",
|
|
|
|
"host2tcl-input-ring2",
|
|
|
|
"host2tcl-input-ring1",
|
|
|
|
"wbm2host-tx-completions-ring3",
|
|
|
|
"wbm2host-tx-completions-ring2",
|
|
|
|
"wbm2host-tx-completions-ring1",
|
|
|
|
"tcl2host-status-ring",
|
|
|
|
};
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
static const struct ath11k_msi_config ath11k_msi_config[] = {
|
|
|
|
{
|
|
|
|
.total_vectors = 32,
|
|
|
|
.total_users = 4,
|
|
|
|
.users = (struct ath11k_msi_user[]) {
|
|
|
|
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
|
|
|
|
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
|
|
|
|
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
|
|
|
|
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
|
|
|
|
},
|
|
|
|
.hw_rev = ATH11K_HW_QCA6390_HW20,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.total_vectors = 16,
|
|
|
|
.total_users = 3,
|
|
|
|
.users = (struct ath11k_msi_user[]) {
|
|
|
|
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
|
|
|
|
{ .name = "CE", .num_vectors = 5, .base_vector = 3 },
|
|
|
|
{ .name = "DP", .num_vectors = 8, .base_vector = 8 },
|
|
|
|
},
|
|
|
|
.hw_rev = ATH11K_HW_QCN9074_HW10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.total_vectors = 32,
|
|
|
|
.total_users = 4,
|
|
|
|
.users = (struct ath11k_msi_user[]) {
|
|
|
|
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
|
|
|
|
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
|
|
|
|
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
|
|
|
|
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
|
|
|
|
},
|
|
|
|
.hw_rev = ATH11K_HW_WCN6855_HW20,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.total_vectors = 32,
|
|
|
|
.total_users = 4,
|
|
|
|
.users = (struct ath11k_msi_user[]) {
|
|
|
|
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
|
|
|
|
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
|
|
|
|
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
|
|
|
|
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
|
|
|
|
},
|
|
|
|
.hw_rev = ATH11K_HW_WCN6855_HW21,
|
|
|
|
},
|
2022-04-29 22:35:02 +05:30
|
|
|
{
|
|
|
|
.total_vectors = 28,
|
|
|
|
.total_users = 2,
|
|
|
|
.users = (struct ath11k_msi_user[]) {
|
|
|
|
{ .name = "CE", .num_vectors = 10, .base_vector = 0 },
|
|
|
|
{ .name = "DP", .num_vectors = 18, .base_vector = 10 },
|
|
|
|
},
|
|
|
|
.hw_rev = ATH11K_HW_WCN6750_HW10,
|
|
|
|
},
|
2024-01-09 10:13:36 +08:00
|
|
|
{
|
|
|
|
.total_vectors = 32,
|
|
|
|
.total_users = 4,
|
|
|
|
.users = (struct ath11k_msi_user[]) {
|
|
|
|
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
|
|
|
|
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
|
|
|
|
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
|
|
|
|
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
|
|
|
|
},
|
|
|
|
.hw_rev = ATH11K_HW_QCA2066_HW21,
|
|
|
|
},
|
2024-10-31 08:05:41 +08:00
|
|
|
{
|
|
|
|
.total_vectors = 32,
|
|
|
|
.total_users = 4,
|
|
|
|
.users = (struct ath11k_msi_user[]) {
|
|
|
|
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
|
|
|
|
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
|
|
|
|
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
|
|
|
|
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
|
|
|
|
},
|
|
|
|
.hw_rev = ATH11K_HW_QCA6698AQ_HW21,
|
|
|
|
},
|
2022-04-01 14:53:08 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
int ath11k_pcic_init_msi_config(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
const struct ath11k_msi_config *msi_config;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ath11k_msi_config); i++) {
|
|
|
|
msi_config = &ath11k_msi_config[i];
|
|
|
|
|
|
|
|
if (msi_config->hw_rev == ab->hw_rev)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == ARRAY_SIZE(ath11k_msi_config)) {
|
|
|
|
ath11k_err(ab, "failed to fetch msi config, unsupported hw version: 0x%x\n",
|
|
|
|
ab->hw_rev);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ab->pci.msi.config = msi_config;
|
2022-04-01 14:53:08 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ath11k_pcic_init_msi_config);
|
|
|
|
|
2022-09-07 11:31:13 +03:00
|
|
|
static void __ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)
|
|
|
|
{
|
|
|
|
if (offset < ATH11K_PCI_WINDOW_START)
|
|
|
|
iowrite32(value, ab->mem + offset);
|
|
|
|
else
|
|
|
|
ab->pci.ops->window_write32(ab, offset, value);
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
2022-09-07 11:31:13 +03:00
|
|
|
bool wakeup_required;
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
/* for offset beyond BAR + 4K - 32, may
|
2022-04-01 14:53:08 +03:00
|
|
|
* need to wakeup the device to access.
|
2022-04-01 14:53:08 +03:00
|
|
|
*/
|
2022-09-07 11:31:13 +03:00
|
|
|
wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
|
|
|
|
offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;
|
|
|
|
if (wakeup_required && ab->pci.ops->wakeup)
|
2022-04-01 14:53:08 +03:00
|
|
|
ret = ab->pci.ops->wakeup(ab);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2022-09-07 11:31:13 +03:00
|
|
|
__ath11k_pcic_write32(ab, offset, value);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2022-09-07 11:31:13 +03:00
|
|
|
if (wakeup_required && !ret && ab->pci.ops->release)
|
2022-04-01 14:53:08 +03:00
|
|
|
ab->pci.ops->release(ab);
|
2022-04-01 14:53:08 +03:00
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_write32);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2022-09-07 11:31:13 +03:00
|
|
|
static u32 __ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
if (offset < ATH11K_PCI_WINDOW_START)
|
|
|
|
val = ioread32(ab->mem + offset);
|
|
|
|
else
|
|
|
|
val = ab->pci.ops->window_read32(ab, offset);
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
wifi: ath11k: Fix register write failure on QCN9074
Commit 56c8ccf331bd ("ath11k: Add register access logic for WCN6750")
regressed QCN9074. With the above mentioned commit, writes are failing
for some registers on QCN9074 although the device seems to work
normally.
ath11k_pci 0000:03:00.0: failed to set pcie link register0x01e0e0a8: 0xffffffff != 0x00000010
ath11k_pci 0000:03:00.0: failed to set sysclk: -110
PCIe devices in ath11k (QCA6390, WCN6855, QCN9074, WCN6750) use window
concept for register accesses. There are two schemes, dynamic & static
window.
In dynamic window scheme, a single window(region in the BAR) is mapped
either to CE or DP register windows at any give time. QCA6390 & WCN6855
follow this scheme for register accesses.
In static window scheme, CE & DP register windows are statically mapped
to separate regions with in the BAR so that there is no switching of
register windows between CE & DP register accesses. QCN9074 & WCN6750
follow this scheme although the window start offsets are different for
QCN9074 & WCN6750.
QCN9074 uses 3rd & 2nd window for DP & CE register accesses respectively
whereas WCN6750 uses 1st & 2nd window for DP & CE. In QCN9074, along with
2nd & 3rd windows, 1st window is also used for certain configurations
which commit 56c8ccf331bd ("ath11k: Add register access logic for WCN6750")
did not account for and hence the regression.
Fix this by going back to the original way of accessing the registers on
QCN9074. Since this diverges from WCN6750 way of accessing registers, it
is required to register window_read32/window_write32() pci_ops for WCN6750.
We can also get rid of dp_window_idx & ce_window_idx members in hw_params,
so remove them.
Also add a new API ath11k_pcic_register_pci_ops() for registering pci_ops
to the ath11k core. This API checks for mandatory pci_ops() and reports
error if those are missing. Also initialize unused pci_ops to NULL.
Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.6.0.1-00861-QCAHKSWPL_SILICONZ-1
Fixes: 56c8ccf331bd ("ath11k: Add register access logic for WCN6750")
Reported-by: Maxime Bizon <mbizon@freebox.fr>
Tested-by: Maxime Bizon <mbizon@freebox.fr>
Signed-off-by: Manikanta Pubbisetty <quic_mpubbise@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220608062954.27792-1-quic_mpubbise@quicinc.com
2022-07-25 20:49:10 +03:00
|
|
|
u32 val;
|
2022-09-07 11:31:13 +03:00
|
|
|
bool wakeup_required;
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
/* for offset beyond BAR + 4K - 32, may
|
2022-04-01 14:53:08 +03:00
|
|
|
* need to wakeup the device to access.
|
2022-04-01 14:53:08 +03:00
|
|
|
*/
|
2022-09-07 11:31:13 +03:00
|
|
|
wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
|
|
|
|
offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;
|
|
|
|
if (wakeup_required && ab->pci.ops->wakeup)
|
2022-04-01 14:53:08 +03:00
|
|
|
ret = ab->pci.ops->wakeup(ab);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2022-09-07 11:31:13 +03:00
|
|
|
val = __ath11k_pcic_read32(ab, offset);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2022-09-07 11:31:13 +03:00
|
|
|
if (wakeup_required && !ret && ab->pci.ops->release)
|
2022-04-01 14:53:08 +03:00
|
|
|
ab->pci.ops->release(ab);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_read32);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2022-09-07 11:31:13 +03:00
|
|
|
int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
bool wakeup_required;
|
|
|
|
u32 *data = buf;
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
/* for offset beyond BAR + 4K - 32, may
|
|
|
|
* need to wakeup the device to access.
|
|
|
|
*/
|
|
|
|
wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
|
|
|
|
end >= ATH11K_PCI_ACCESS_ALWAYS_OFF;
|
|
|
|
if (wakeup_required && ab->pci.ops->wakeup) {
|
|
|
|
ret = ab->pci.ops->wakeup(ab);
|
|
|
|
if (ret) {
|
2022-09-28 09:51:40 +08:00
|
|
|
ath11k_warn(ab,
|
|
|
|
"wakeup failed, data may be invalid: %d",
|
|
|
|
ret);
|
|
|
|
/* Even though wakeup() failed, continue processing rather
|
|
|
|
* than returning because some parts of the data may still
|
|
|
|
* be valid and useful in some cases, e.g. could give us
|
|
|
|
* some clues on firmware crash.
|
|
|
|
* Mislead due to invalid data could be avoided because we
|
|
|
|
* are aware of the wakeup failure.
|
|
|
|
*/
|
2022-09-07 11:31:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = start; i < end + 1; i += 4)
|
|
|
|
*data++ = __ath11k_pcic_read32(ab, i);
|
|
|
|
|
|
|
|
if (wakeup_required && ab->pci.ops->release)
|
|
|
|
ab->pci.ops->release(ab);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ath11k_pcic_read);
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
|
|
|
|
u32 *msi_addr_hi)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
*msi_addr_lo = ab->pci.msi.addr_lo;
|
|
|
|
*msi_addr_hi = ab->pci.msi.addr_hi;
|
2022-04-01 14:53:08 +03:00
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_get_msi_address);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,
|
2022-04-01 14:53:08 +03:00
|
|
|
int *num_vectors, u32 *user_base_data,
|
|
|
|
u32 *base_vector)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
const struct ath11k_msi_config *msi_config = ab->pci.msi.config;
|
2022-04-01 14:53:08 +03:00
|
|
|
int idx;
|
|
|
|
|
|
|
|
for (idx = 0; idx < msi_config->total_users; idx++) {
|
|
|
|
if (strcmp(user_name, msi_config->users[idx].name) == 0) {
|
|
|
|
*num_vectors = msi_config->users[idx].num_vectors;
|
|
|
|
*base_vector = msi_config->users[idx].base_vector;
|
2022-04-01 14:53:08 +03:00
|
|
|
*user_base_data = *base_vector + ab->pci.msi.ep_base_data;
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI,
|
2023-06-09 17:24:34 +03:00
|
|
|
"msi assignment %s num_vectors %d user_base_data %u base_vector %u\n",
|
2022-04-01 14:53:08 +03:00
|
|
|
user_name, *num_vectors, *user_base_data,
|
|
|
|
*base_vector);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name);
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_get_user_msi_assignment);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx)
|
|
|
|
{
|
|
|
|
u32 i, msi_data_idx;
|
|
|
|
|
|
|
|
for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {
|
|
|
|
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ce_id == i)
|
|
|
|
break;
|
|
|
|
|
|
|
|
msi_data_idx++;
|
|
|
|
}
|
|
|
|
*msi_idx = msi_data_idx;
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_get_ce_msi_idx);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
|
|
|
|
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
|
|
|
|
|
|
|
|
for (j = 0; j < irq_grp->num_irq; j++)
|
|
|
|
free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
|
|
|
|
|
|
|
|
netif_napi_del(&irq_grp->napi);
|
2024-04-22 05:39:03 -07:00
|
|
|
free_netdev(irq_grp->napi_ndev);
|
2022-04-01 14:53:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath11k_pcic_free_irq(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i, irq_idx;
|
|
|
|
|
|
|
|
for (i = 0; i < ab->hw_params.ce_count; i++) {
|
|
|
|
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
|
|
|
|
continue;
|
|
|
|
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
|
|
|
|
free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
ath11k_pcic_free_ext_irq(ab);
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_free_irq);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)
|
|
|
|
{
|
|
|
|
u32 irq_idx;
|
|
|
|
|
|
|
|
/* In case of one MSI vector, we handle irq enable/disable in a
|
|
|
|
* uniform way since we only have one irq
|
|
|
|
*/
|
2022-04-01 14:53:08 +03:00
|
|
|
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
|
2022-04-01 14:53:08 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
|
|
|
|
enable_irq(ab->irq_num[irq_idx]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pcic_ce_irq_disable(struct ath11k_base *ab, u16 ce_id)
|
|
|
|
{
|
|
|
|
u32 irq_idx;
|
|
|
|
|
|
|
|
/* In case of one MSI vector, we handle irq enable/disable in a
|
|
|
|
* uniform way since we only have one irq
|
|
|
|
*/
|
2022-04-01 14:53:08 +03:00
|
|
|
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
|
2022-04-01 14:53:08 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
|
|
|
|
disable_irq_nosync(ab->irq_num[irq_idx]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pcic_ce_irqs_disable(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);
|
|
|
|
|
|
|
|
for (i = 0; i < ab->hw_params.ce_count; i++) {
|
|
|
|
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
|
|
|
|
continue;
|
|
|
|
ath11k_pcic_ce_irq_disable(ab, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pcic_sync_ce_irqs(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int irq_idx;
|
|
|
|
|
|
|
|
for (i = 0; i < ab->hw_params.ce_count; i++) {
|
|
|
|
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
|
|
|
|
synchronize_irq(ab->irq_num[irq_idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pcic_ce_tasklet(struct tasklet_struct *t)
|
|
|
|
{
|
|
|
|
struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq);
|
|
|
|
int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
|
|
|
|
|
|
|
|
ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);
|
|
|
|
|
|
|
|
enable_irq(ce_pipe->ab->irq_num[irq_idx]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t ath11k_pcic_ce_interrupt_handler(int irq, void *arg)
|
|
|
|
{
|
|
|
|
struct ath11k_ce_pipe *ce_pipe = arg;
|
|
|
|
struct ath11k_base *ab = ce_pipe->ab;
|
|
|
|
int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
|
|
|
|
|
|
|
|
if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags))
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
|
|
|
|
/* last interrupt received for this CE */
|
|
|
|
ce_pipe->timestamp = jiffies;
|
|
|
|
|
|
|
|
disable_irq_nosync(ab->irq_num[irq_idx]);
|
|
|
|
|
|
|
|
tasklet_schedule(&ce_pipe->intr_tq);
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pcic_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
struct ath11k_base *ab = irq_grp->ab;
|
2022-04-01 14:53:08 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* In case of one MSI vector, we handle irq enable/disable
|
|
|
|
* in a uniform way since we only have one irq
|
|
|
|
*/
|
2022-04-01 14:53:08 +03:00
|
|
|
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
|
2022-04-01 14:53:08 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < irq_grp->num_irq; i++)
|
|
|
|
disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
|
|
|
|
}
|
|
|
|
|
2023-10-14 08:56:50 +05:30
|
|
|
static void __ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)
|
2022-04-01 14:53:08 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2023-10-14 08:56:50 +05:30
|
|
|
clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
|
2023-10-14 08:56:50 +05:30
|
|
|
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
ath11k_pcic_ext_grp_disable(irq_grp);
|
|
|
|
|
|
|
|
if (irq_grp->napi_enabled) {
|
|
|
|
napi_synchronize(&irq_grp->napi);
|
|
|
|
napi_disable(&irq_grp->napi);
|
|
|
|
irq_grp->napi_enabled = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pcic_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
struct ath11k_base *ab = irq_grp->ab;
|
2022-04-01 14:53:08 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* In case of one MSI vector, we handle irq enable/disable in a
|
|
|
|
* uniform way since we only have one irq
|
|
|
|
*/
|
2022-04-01 14:53:08 +03:00
|
|
|
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
|
2022-04-01 14:53:08 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < irq_grp->num_irq; i++)
|
|
|
|
enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
|
|
|
|
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
|
|
|
|
|
|
|
|
if (!irq_grp->napi_enabled) {
|
|
|
|
napi_enable(&irq_grp->napi);
|
|
|
|
irq_grp->napi_enabled = true;
|
|
|
|
}
|
|
|
|
ath11k_pcic_ext_grp_enable(irq_grp);
|
|
|
|
}
|
wifi: ath11k: fix race due to setting ATH11K_FLAG_EXT_IRQ_ENABLED too early
We are seeing below error randomly in the case where only
one MSI vector is configured:
kernel: ath11k_pci 0000:03:00.0: wmi command 16387 timeout
The reason is, currently, in ath11k_pcic_ext_irq_enable(),
ATH11K_FLAG_EXT_IRQ_ENABLED is set before NAPI is enabled.
This results in a race condition: after
ATH11K_FLAG_EXT_IRQ_ENABLED is set but before NAPI enabled,
CE interrupt breaks in. Since IRQ is shared by CE and data
path, ath11k_pcic_ext_interrupt_handler() is also called
where we call disable_irq_nosync() to disable IRQ. Then
napi_schedule() is called but it does nothing because NAPI
is not enabled at that time, meaning
ath11k_pcic_ext_grp_napi_poll() will never run, so we have
no chance to call enable_irq() to enable IRQ back. Finally
we get above error.
Fix it by setting ATH11K_FLAG_EXT_IRQ_ENABLED after all
NAPI and IRQ work are done. With the fix, we are sure that
by the time ATH11K_FLAG_EXT_IRQ_ENABLED is set, NAPI is
enabled.
Note that the fix above also introduce some side effects:
if ath11k_pcic_ext_interrupt_handler() breaks in after NAPI
enabled but before ATH11K_FLAG_EXT_IRQ_ENABLED set, nothing
will be done by the handler this time, the work will be
postponed till the next time the IRQ fires.
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20231117003919.26218-1-quic_bqiang@quicinc.com
2023-11-17 08:39:19 +08:00
|
|
|
|
|
|
|
set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
|
2022-04-01 14:53:08 +03:00
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
static void ath11k_pcic_sync_ext_irqs(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i, j, irq_idx;
|
|
|
|
|
|
|
|
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
|
|
|
|
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
|
|
|
|
|
|
|
|
for (j = 0; j < irq_grp->num_irq; j++) {
|
|
|
|
irq_idx = irq_grp->irqs[j];
|
|
|
|
synchronize_irq(ab->irq_num[irq_idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
__ath11k_pcic_ext_irq_disable(ab);
|
|
|
|
ath11k_pcic_sync_ext_irqs(ab);
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_ext_irq_disable);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
static int ath11k_pcic_ext_grp_napi_poll(struct napi_struct *napi, int budget)
|
|
|
|
{
|
|
|
|
struct ath11k_ext_irq_grp *irq_grp = container_of(napi,
|
|
|
|
struct ath11k_ext_irq_grp,
|
|
|
|
napi);
|
|
|
|
struct ath11k_base *ab = irq_grp->ab;
|
|
|
|
int work_done;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
work_done = ath11k_dp_service_srng(ab, irq_grp, budget);
|
|
|
|
if (work_done < budget) {
|
|
|
|
napi_complete_done(napi, work_done);
|
|
|
|
for (i = 0; i < irq_grp->num_irq; i++)
|
|
|
|
enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (work_done > budget)
|
|
|
|
work_done = budget;
|
|
|
|
|
|
|
|
return work_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t ath11k_pcic_ext_interrupt_handler(int irq, void *arg)
|
|
|
|
{
|
|
|
|
struct ath11k_ext_irq_grp *irq_grp = arg;
|
|
|
|
struct ath11k_base *ab = irq_grp->ab;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags))
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
|
2023-06-09 17:24:34 +03:00
|
|
|
ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq %d\n", irq);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
/* last interrupt received for this group */
|
|
|
|
irq_grp->timestamp = jiffies;
|
|
|
|
|
|
|
|
for (i = 0; i < irq_grp->num_irq; i++)
|
|
|
|
disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
|
|
|
|
|
|
|
|
napi_schedule(&irq_grp->napi);
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
static int
|
|
|
|
ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector)
|
|
|
|
{
|
|
|
|
return ab->pci.ops->get_msi_irq(ab, vector);
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
|
|
|
|
{
|
2024-04-22 05:39:03 -07:00
|
|
|
int i, j, n, ret, num_vectors = 0;
|
2022-04-01 14:53:08 +03:00
|
|
|
u32 user_base_data = 0, base_vector = 0;
|
2024-05-16 11:30:41 +03:00
|
|
|
struct ath11k_ext_irq_grp *irq_grp;
|
2022-04-01 14:53:08 +03:00
|
|
|
unsigned long irq_flags;
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors,
|
2022-04-01 14:53:08 +03:00
|
|
|
&user_base_data,
|
|
|
|
&base_vector);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
irq_flags = IRQF_SHARED;
|
|
|
|
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
|
|
|
|
irq_flags |= IRQF_NOBALANCING;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
|
2024-05-16 11:30:41 +03:00
|
|
|
irq_grp = &ab->ext_irq_grp[i];
|
2022-04-01 14:53:08 +03:00
|
|
|
u32 num_irq = 0;
|
|
|
|
|
|
|
|
irq_grp->ab = ab;
|
|
|
|
irq_grp->grp_id = i;
|
2024-04-22 05:39:03 -07:00
|
|
|
irq_grp->napi_ndev = alloc_netdev_dummy(0);
|
2024-05-16 11:30:41 +03:00
|
|
|
if (!irq_grp->napi_ndev) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail_allocate;
|
|
|
|
}
|
2024-04-22 05:39:03 -07:00
|
|
|
|
|
|
|
netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,
|
2022-09-27 06:27:53 -07:00
|
|
|
ath11k_pcic_ext_grp_napi_poll);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
if (ab->hw_params.ring_mask->tx[i] ||
|
|
|
|
ab->hw_params.ring_mask->rx[i] ||
|
|
|
|
ab->hw_params.ring_mask->rx_err[i] ||
|
|
|
|
ab->hw_params.ring_mask->rx_wbm_rel[i] ||
|
|
|
|
ab->hw_params.ring_mask->reo_status[i] ||
|
|
|
|
ab->hw_params.ring_mask->rxdma2host[i] ||
|
|
|
|
ab->hw_params.ring_mask->host2rxdma[i] ||
|
|
|
|
ab->hw_params.ring_mask->rx_mon_status[i]) {
|
|
|
|
num_irq = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
irq_grp->num_irq = num_irq;
|
|
|
|
irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i;
|
|
|
|
|
|
|
|
for (j = 0; j < irq_grp->num_irq; j++) {
|
|
|
|
int irq_idx = irq_grp->irqs[j];
|
|
|
|
int vector = (i % num_vectors) + base_vector;
|
2022-04-01 14:53:08 +03:00
|
|
|
int irq = ath11k_pcic_get_msi_irq(ab, vector);
|
|
|
|
|
2024-04-22 05:39:03 -07:00
|
|
|
if (irq < 0) {
|
2024-05-16 11:30:41 +03:00
|
|
|
ret = irq;
|
|
|
|
goto fail_irq;
|
2024-04-22 05:39:03 -07:00
|
|
|
}
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
ab->irq_num[irq_idx] = irq;
|
|
|
|
|
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI,
|
2023-06-09 17:24:34 +03:00
|
|
|
"irq %d group %d\n", irq, i);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
|
|
|
|
ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler,
|
2022-04-01 14:53:08 +03:00
|
|
|
irq_flags, "DP_EXT_IRQ", irq_grp);
|
2022-04-01 14:53:08 +03:00
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed request irq %d: %d\n",
|
|
|
|
vector, ret);
|
2024-04-22 05:39:03 -07:00
|
|
|
for (n = 0; n <= i; n++) {
|
|
|
|
irq_grp = &ab->ext_irq_grp[n];
|
|
|
|
free_netdev(irq_grp->napi_ndev);
|
|
|
|
}
|
2022-04-01 14:53:08 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ath11k_pcic_ext_grp_disable(irq_grp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2024-05-16 11:30:41 +03:00
|
|
|
fail_irq:
|
|
|
|
/* i ->napi_ndev was properly allocated. Free it also */
|
|
|
|
i += 1;
|
|
|
|
fail_allocate:
|
|
|
|
for (n = 0; n < i; n++) {
|
|
|
|
irq_grp = &ab->ext_irq_grp[n];
|
|
|
|
free_netdev(irq_grp->napi_ndev);
|
|
|
|
}
|
|
|
|
return ret;
|
2022-04-01 14:53:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int ath11k_pcic_config_irq(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
struct ath11k_ce_pipe *ce_pipe;
|
|
|
|
u32 msi_data_start;
|
|
|
|
u32 msi_data_count, msi_data_idx;
|
|
|
|
u32 msi_irq_start;
|
|
|
|
unsigned int msi_data;
|
|
|
|
int irq, i, ret, irq_idx;
|
2022-04-01 14:53:08 +03:00
|
|
|
unsigned long irq_flags;
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ret = ath11k_pcic_get_user_msi_assignment(ab, "CE", &msi_data_count,
|
2022-04-01 14:53:08 +03:00
|
|
|
&msi_data_start, &msi_irq_start);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
irq_flags = IRQF_SHARED;
|
|
|
|
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
|
|
|
|
irq_flags |= IRQF_NOBALANCING;
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
/* Configure CE irqs */
|
|
|
|
for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {
|
|
|
|
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
msi_data = (msi_data_idx % msi_data_count) + msi_irq_start;
|
2022-04-01 14:53:08 +03:00
|
|
|
irq = ath11k_pcic_get_msi_irq(ab, msi_data);
|
|
|
|
if (irq < 0)
|
|
|
|
return irq;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ce_pipe = &ab->ce.ce_pipe[i];
|
|
|
|
|
|
|
|
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
|
|
|
|
|
|
|
|
tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet);
|
|
|
|
|
|
|
|
ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler,
|
2022-04-01 14:53:08 +03:00
|
|
|
irq_flags, irq_name[irq_idx], ce_pipe);
|
2022-04-01 14:53:08 +03:00
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to request irq %d: %d\n",
|
|
|
|
irq_idx, ret);
|
2022-04-01 14:53:08 +03:00
|
|
|
return ret;
|
2022-04-01 14:53:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ab->irq_num[irq_idx] = irq;
|
|
|
|
msi_data_idx++;
|
|
|
|
|
|
|
|
ath11k_pcic_ce_irq_disable(ab, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ath11k_pcic_ext_irq_config(ab);
|
|
|
|
if (ret)
|
2022-04-01 14:53:08 +03:00
|
|
|
return ret;
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_config_irq);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);
|
|
|
|
|
|
|
|
for (i = 0; i < ab->hw_params.ce_count; i++) {
|
|
|
|
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
|
|
|
|
continue;
|
|
|
|
ath11k_pcic_ce_irq_enable(ab, i);
|
|
|
|
}
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_ce_irqs_enable);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
static void ath11k_pcic_kill_tasklets(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ab->hw_params.ce_count; i++) {
|
|
|
|
struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];
|
|
|
|
|
|
|
|
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
tasklet_kill(&ce_pipe->intr_tq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
ath11k_pcic_ce_irqs_disable(ab);
|
|
|
|
ath11k_pcic_sync_ce_irqs(ab);
|
|
|
|
ath11k_pcic_kill_tasklets(ab);
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_ce_irq_disable_sync);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
void ath11k_pcic_stop(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
ath11k_pcic_ce_irq_disable_sync(ab);
|
|
|
|
ath11k_ce_cleanup_pipes(ab);
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_stop);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
int ath11k_pcic_start(struct ath11k_base *ab)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
set_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
ath11k_pcic_ce_irqs_enable(ab);
|
|
|
|
ath11k_ce_rx_post_buf(ab);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_start);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
|
|
|
|
u8 *ul_pipe, u8 *dl_pipe)
|
|
|
|
{
|
|
|
|
const struct service_to_pipe *entry;
|
|
|
|
bool ul_set = false, dl_set = false;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) {
|
|
|
|
entry = &ab->hw_params.svc_to_ce_map[i];
|
|
|
|
|
|
|
|
if (__le32_to_cpu(entry->service_id) != service_id)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (__le32_to_cpu(entry->pipedir)) {
|
|
|
|
case PIPEDIR_NONE:
|
|
|
|
break;
|
|
|
|
case PIPEDIR_IN:
|
|
|
|
WARN_ON(dl_set);
|
|
|
|
*dl_pipe = __le32_to_cpu(entry->pipenum);
|
|
|
|
dl_set = true;
|
|
|
|
break;
|
|
|
|
case PIPEDIR_OUT:
|
|
|
|
WARN_ON(ul_set);
|
|
|
|
*ul_pipe = __le32_to_cpu(entry->pipenum);
|
|
|
|
ul_set = true;
|
|
|
|
break;
|
|
|
|
case PIPEDIR_INOUT:
|
|
|
|
WARN_ON(dl_set);
|
|
|
|
WARN_ON(ul_set);
|
|
|
|
*dl_pipe = __le32_to_cpu(entry->pipenum);
|
|
|
|
*ul_pipe = __le32_to_cpu(entry->pipenum);
|
|
|
|
dl_set = true;
|
|
|
|
ul_set = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WARN_ON(!ul_set || !dl_set))
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2022-04-29 22:35:02 +05:30
|
|
|
EXPORT_SYMBOL(ath11k_pcic_map_service_to_pipe);
|
wifi: ath11k: Fix register write failure on QCN9074
Commit 56c8ccf331bd ("ath11k: Add register access logic for WCN6750")
regressed QCN9074. With the above mentioned commit, writes are failing
for some registers on QCN9074 although the device seems to work
normally.
ath11k_pci 0000:03:00.0: failed to set pcie link register0x01e0e0a8: 0xffffffff != 0x00000010
ath11k_pci 0000:03:00.0: failed to set sysclk: -110
PCIe devices in ath11k (QCA6390, WCN6855, QCN9074, WCN6750) use window
concept for register accesses. There are two schemes, dynamic & static
window.
In dynamic window scheme, a single window(region in the BAR) is mapped
either to CE or DP register windows at any give time. QCA6390 & WCN6855
follow this scheme for register accesses.
In static window scheme, CE & DP register windows are statically mapped
to separate regions with in the BAR so that there is no switching of
register windows between CE & DP register accesses. QCN9074 & WCN6750
follow this scheme although the window start offsets are different for
QCN9074 & WCN6750.
QCN9074 uses 3rd & 2nd window for DP & CE register accesses respectively
whereas WCN6750 uses 1st & 2nd window for DP & CE. In QCN9074, along with
2nd & 3rd windows, 1st window is also used for certain configurations
which commit 56c8ccf331bd ("ath11k: Add register access logic for WCN6750")
did not account for and hence the regression.
Fix this by going back to the original way of accessing the registers on
QCN9074. Since this diverges from WCN6750 way of accessing registers, it
is required to register window_read32/window_write32() pci_ops for WCN6750.
We can also get rid of dp_window_idx & ce_window_idx members in hw_params,
so remove them.
Also add a new API ath11k_pcic_register_pci_ops() for registering pci_ops
to the ath11k core. This API checks for mandatory pci_ops() and reports
error if those are missing. Also initialize unused pci_ops to NULL.
Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.6.0.1-00861-QCAHKSWPL_SILICONZ-1
Fixes: 56c8ccf331bd ("ath11k: Add register access logic for WCN6750")
Reported-by: Maxime Bizon <mbizon@freebox.fr>
Tested-by: Maxime Bizon <mbizon@freebox.fr>
Signed-off-by: Manikanta Pubbisetty <quic_mpubbise@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220608062954.27792-1-quic_mpubbise@quicinc.com
2022-07-25 20:49:10 +03:00
|
|
|
|
|
|
|
int ath11k_pcic_register_pci_ops(struct ath11k_base *ab,
|
|
|
|
const struct ath11k_pci_ops *pci_ops)
|
|
|
|
{
|
|
|
|
if (!pci_ops)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Return error if mandatory pci_ops callbacks are missing */
|
|
|
|
if (!pci_ops->get_msi_irq || !pci_ops->window_write32 ||
|
|
|
|
!pci_ops->window_read32)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ab->pci.ops = pci_ops;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ath11k_pcic_register_pci_ops);
|
2022-09-19 15:47:14 +03:00
|
|
|
|
|
|
|
void ath11k_pci_enable_ce_irqs_except_wake_irq(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ab->hw_params.ce_count; i++) {
|
|
|
|
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||
|
|
|
|
i == ATH11K_PCI_CE_WAKE_IRQ)
|
|
|
|
continue;
|
|
|
|
ath11k_pcic_ce_irq_enable(ab, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ath11k_pci_enable_ce_irqs_except_wake_irq);
|
|
|
|
|
|
|
|
void ath11k_pci_disable_ce_irqs_except_wake_irq(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int irq_idx;
|
|
|
|
struct ath11k_ce_pipe *ce_pipe;
|
|
|
|
|
|
|
|
for (i = 0; i < ab->hw_params.ce_count; i++) {
|
|
|
|
ce_pipe = &ab->ce.ce_pipe[i];
|
|
|
|
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
|
|
|
|
|
|
|
|
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||
|
|
|
|
i == ATH11K_PCI_CE_WAKE_IRQ)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
disable_irq_nosync(ab->irq_num[irq_idx]);
|
|
|
|
synchronize_irq(ab->irq_num[irq_idx]);
|
|
|
|
tasklet_kill(&ce_pipe->intr_tq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ath11k_pci_disable_ce_irqs_except_wake_irq);
|