2020-08-13 12:04:20 +03:00
|
|
|
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
|
2025-01-23 16:49:48 +08:00
|
|
|
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
2020-08-13 12:04:20 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
2020-08-13 12:04:22 +03:00
|
|
|
#include <linux/msi.h>
|
2020-08-13 12:04:20 +03:00
|
|
|
#include <linux/pci.h>
|
2021-12-14 17:39:43 +02:00
|
|
|
#include <linux/of.h>
|
2024-08-13 09:30:28 +08:00
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/vmalloc.h>
|
2020-08-13 12:04:20 +03:00
|
|
|
|
2020-08-13 12:04:21 +03:00
|
|
|
#include "pci.h"
|
2020-08-13 12:04:20 +03:00
|
|
|
#include "core.h"
|
2020-08-13 12:04:24 +03:00
|
|
|
#include "hif.h"
|
|
|
|
#include "mhi.h"
|
2020-08-13 12:04:20 +03:00
|
|
|
#include "debug.h"
|
2022-04-01 14:53:08 +03:00
|
|
|
#include "pcic.h"
|
2023-07-26 19:40:31 +05:30
|
|
|
#include "qmi.h"
|
2020-08-13 12:04:20 +03:00
|
|
|
|
2020-08-13 12:04:21 +03:00
|
|
|
#define ATH11K_PCI_BAR_NUM 0
|
wifi: ath11k: enable 36 bit mask for stream DMA
Currently 32 bit DMA mask is used, telling kernel to get us an DMA
address under 4GB when mapping a buffer. This results in a very high
CPU overhead in the case where IOMMU is disabled and more than 4GB
system memory is installed. The reason is, with more than 4GB memory
installed, kernel is likely to allocate a buffer whose physical
address is above 4GB. While with IOMMU disabled, kernel has to involve
SWIOTLB to map/unmap that buffer, which consumes lots of CPU cycles.
We did hit an issue caused by the reason mentioned above: in a system
that disables IOMMU and gets 8GB memory installed, a total of 40.5%
CPU usage is observed in throughput test. CPU profiling shows nearly
60% of CPU cycles are consumed by SWIOTLB.
By enabling 36 bit DMA mask, we can bypass SWIOTLB for any buffer
whose physical address is below 64GB. There are two types of DMA mask
within struct device, named dma_mask and coherent_dma_mask. Here we
only enable 36 bit for dma_mask, because firmware crashes if
coherent_dma_mask is also enabled, due to some unknown hardware
limitations. This is acceptable because coherent_dma_mask is used for
mapping a consistent DMA buffer, which generally does not happen in
a hot path.
With this change, the total CPU usage mentioned in above issue drops
to 18.9%.
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
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://msgid.link/20240123015201.28939-1-quic_bqiang@quicinc.com
2024-01-23 09:52:01 +08:00
|
|
|
#define ATH11K_PCI_DMA_MASK 36
|
|
|
|
#define ATH11K_PCI_COHERENT_DMA_MASK 32
|
2020-08-13 12:04:21 +03:00
|
|
|
|
2020-09-29 20:15:32 +03:00
|
|
|
#define TCSR_SOC_HW_VERSION 0x0224
|
2021-11-29 10:56:12 +08:00
|
|
|
#define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(11, 8)
|
2020-09-29 20:15:32 +03:00
|
|
|
#define TCSR_SOC_HW_VERSION_MINOR_MASK GENMASK(7, 0)
|
|
|
|
|
2020-08-13 12:04:20 +03:00
|
|
|
#define QCA6390_DEVICE_ID 0x1101
|
2021-02-16 09:16:25 +02:00
|
|
|
#define QCN9074_DEVICE_ID 0x1104
|
2021-05-31 17:41:28 +03:00
|
|
|
#define WCN6855_DEVICE_ID 0x1103
|
2020-08-13 12:04:20 +03:00
|
|
|
|
2024-01-09 10:13:36 +08:00
|
|
|
#define TCSR_SOC_HW_SUB_VER 0x1910010
|
|
|
|
|
2020-08-13 12:04:20 +03:00
|
|
|
static const struct pci_device_id ath11k_pci_id_table[] = {
|
|
|
|
{ PCI_VDEVICE(QCOM, QCA6390_DEVICE_ID) },
|
2021-05-31 17:41:28 +03:00
|
|
|
{ PCI_VDEVICE(QCOM, WCN6855_DEVICE_ID) },
|
2021-06-17 11:29:40 +03:00
|
|
|
{ PCI_VDEVICE(QCOM, QCN9074_DEVICE_ID) },
|
2025-07-20 08:13:38 -07:00
|
|
|
{}
|
2020-08-13 12:04:20 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table);
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
static int ath11k_pci_bus_wake_up(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
|
|
|
|
|
|
|
return mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pci_bus_release(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
|
|
|
|
|
|
|
mhi_device_put(ab_pci->mhi_ctrl->mhi_dev);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
static u32 ath11k_pci_get_window_start(struct ath11k_base *ab, u32 offset)
|
|
|
|
{
|
|
|
|
if (!ab->hw_params.static_window_map)
|
|
|
|
return ATH11K_PCI_WINDOW_START;
|
|
|
|
|
|
|
|
if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < ATH11K_PCI_WINDOW_RANGE_MASK)
|
|
|
|
/* if offset lies within DP register range, use 3rd window */
|
|
|
|
return 3 * ATH11K_PCI_WINDOW_START;
|
|
|
|
else if ((offset ^ HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab)) <
|
|
|
|
ATH11K_PCI_WINDOW_RANGE_MASK)
|
|
|
|
/* if offset lies within CE register range, use 2nd window */
|
|
|
|
return 2 * ATH11K_PCI_WINDOW_START;
|
|
|
|
else
|
|
|
|
return ATH11K_PCI_WINDOW_START;
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
static inline void ath11k_pci_select_window(struct ath11k_pci *ab_pci, u32 offset)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
|
|
|
|
|
|
|
u32 window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, offset);
|
|
|
|
|
|
|
|
lockdep_assert_held(&ab_pci->window_lock);
|
|
|
|
|
|
|
|
if (window != ab_pci->register_window) {
|
|
|
|
iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window,
|
|
|
|
ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
|
|
|
|
ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
|
|
|
|
ab_pci->register_window = window;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ath11k_pci_window_write32(struct ath11k_base *ab, u32 offset, u32 value)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
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 window_start;
|
|
|
|
|
|
|
|
window_start = ath11k_pci_get_window_start(ab, offset);
|
2022-04-01 14:53:08 +03:00
|
|
|
|
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
|
|
|
if (window_start == ATH11K_PCI_WINDOW_START) {
|
|
|
|
spin_lock_bh(&ab_pci->window_lock);
|
|
|
|
ath11k_pci_select_window(ab_pci, offset);
|
|
|
|
iowrite32(value, ab->mem + window_start +
|
|
|
|
(offset & ATH11K_PCI_WINDOW_RANGE_MASK));
|
|
|
|
spin_unlock_bh(&ab_pci->window_lock);
|
|
|
|
} else {
|
|
|
|
iowrite32(value, ab->mem + window_start +
|
|
|
|
(offset & ATH11K_PCI_WINDOW_RANGE_MASK));
|
|
|
|
}
|
2022-04-01 14:53:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static u32 ath11k_pci_window_read32(struct ath11k_base *ab, u32 offset)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
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 window_start, val;
|
2022-04-01 14:53:08 +03:00
|
|
|
|
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
|
|
|
window_start = ath11k_pci_get_window_start(ab, offset);
|
|
|
|
|
|
|
|
if (window_start == ATH11K_PCI_WINDOW_START) {
|
|
|
|
spin_lock_bh(&ab_pci->window_lock);
|
|
|
|
ath11k_pci_select_window(ab_pci, offset);
|
|
|
|
val = ioread32(ab->mem + window_start +
|
|
|
|
(offset & ATH11K_PCI_WINDOW_RANGE_MASK));
|
|
|
|
spin_unlock_bh(&ab_pci->window_lock);
|
|
|
|
} else {
|
|
|
|
val = ioread32(ab->mem + window_start +
|
|
|
|
(offset & ATH11K_PCI_WINDOW_RANGE_MASK));
|
|
|
|
}
|
2022-04-01 14:53:08 +03:00
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ath11k_pci_get_msi_irq(struct ath11k_base *ab, unsigned int vector)
|
|
|
|
{
|
|
|
|
struct pci_dev *pci_dev = to_pci_dev(ab->dev);
|
|
|
|
|
|
|
|
return pci_irq_vector(pci_dev, vector);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct ath11k_pci_ops ath11k_pci_ops_qca6390 = {
|
|
|
|
.wakeup = ath11k_pci_bus_wake_up,
|
|
|
|
.release = ath11k_pci_bus_release,
|
|
|
|
.get_msi_irq = ath11k_pci_get_msi_irq,
|
|
|
|
.window_write32 = ath11k_pci_window_write32,
|
|
|
|
.window_read32 = ath11k_pci_window_read32,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct ath11k_pci_ops ath11k_pci_ops_qcn9074 = {
|
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
|
|
|
.wakeup = NULL,
|
|
|
|
.release = NULL,
|
2022-04-01 14:53:08 +03:00
|
|
|
.get_msi_irq = ath11k_pci_get_msi_irq,
|
|
|
|
.window_write32 = ath11k_pci_window_write32,
|
|
|
|
.window_read32 = ath11k_pci_window_read32,
|
|
|
|
};
|
|
|
|
|
2021-11-19 15:36:26 +02:00
|
|
|
static const struct ath11k_msi_config msi_config_one_msi = {
|
|
|
|
.total_vectors = 1,
|
|
|
|
.total_users = 4,
|
|
|
|
.users = (struct ath11k_msi_user[]) {
|
|
|
|
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
|
|
|
|
{ .name = "CE", .num_vectors = 1, .base_vector = 0 },
|
|
|
|
{ .name = "WAKE", .num_vectors = 1, .base_vector = 0 },
|
|
|
|
{ .name = "DP", .num_vectors = 1, .base_vector = 0 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-02-16 09:16:17 +02:00
|
|
|
static inline void ath11k_pci_select_static_window(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
u32 umac_window;
|
|
|
|
u32 ce_window;
|
2021-02-16 09:16:17 +02:00
|
|
|
u32 window;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
umac_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET);
|
|
|
|
ce_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE);
|
2021-02-16 09:16:17 +02:00
|
|
|
window = (umac_window << 12) | (ce_window << 6);
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window,
|
|
|
|
ab_pci->ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
|
2021-02-16 09:16:17 +02:00
|
|
|
}
|
|
|
|
|
2020-08-17 13:31:55 +03:00
|
|
|
static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
u32 val, delay;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
|
|
|
val |= PCIE_SOC_GLOBAL_RESET_V;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
|
|
|
/* TODO: exact time to sleep is uncertain */
|
|
|
|
delay = 10;
|
|
|
|
mdelay(delay);
|
|
|
|
|
|
|
|
/* Need to toggle V bit back otherwise stuck in reset status */
|
|
|
|
val &= ~PCIE_SOC_GLOBAL_RESET_V;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
|
|
|
mdelay(delay);
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET);
|
2020-08-17 13:31:55 +03:00
|
|
|
if (val == 0xffffffff)
|
|
|
|
ath11k_warn(ab, "link down error during global reset\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* read cookie */
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, PCIE_Q6_COOKIE_ADDR);
|
2023-06-09 17:24:34 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "pcie_q6_cookie_addr 0x%x\n", val);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, WLAON_WARM_SW_ENTRY);
|
2023-06-09 17:24:34 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "wlaon_warm_sw_entry 0x%x\n", val);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
|
|
|
/* TODO: exact time to sleep is uncertain */
|
|
|
|
mdelay(10);
|
|
|
|
|
|
|
|
/* write 0 to WLAON_WARM_SW_ENTRY to prevent Q6 from
|
|
|
|
* continuing warm path and entering dead loop.
|
|
|
|
*/
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, WLAON_WARM_SW_ENTRY, 0);
|
2020-08-17 13:31:55 +03:00
|
|
|
mdelay(10);
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, WLAON_WARM_SW_ENTRY);
|
2023-06-09 17:24:34 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "wlaon_warm_sw_entry 0x%x\n", val);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
|
|
|
/* A read clear register. clear the register to prevent
|
|
|
|
* Q6 from entering wrong code path.
|
|
|
|
*/
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, WLAON_SOC_RESET_CAUSE_REG);
|
2023-06-09 17:24:34 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "soc reset cause %d\n", val);
|
2020-08-17 13:31:55 +03:00
|
|
|
}
|
|
|
|
|
2020-12-10 16:05:22 +02:00
|
|
|
static int ath11k_pci_set_link_reg(struct ath11k_base *ab,
|
|
|
|
u32 offset, u32 value, u32 mask)
|
|
|
|
{
|
|
|
|
u32 v;
|
|
|
|
int i;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
v = ath11k_pcic_read32(ab, offset);
|
2020-12-10 16:05:22 +02:00
|
|
|
if ((v & mask) == value)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < 10; i++) {
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, offset, (v & ~mask) | value);
|
2020-12-10 16:05:22 +02:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
v = ath11k_pcic_read32(ab, offset);
|
2020-12-10 16:05:22 +02:00
|
|
|
if ((v & mask) == value)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
mdelay(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
ath11k_warn(ab, "failed to set pcie link register 0x%08x: 0x%08x != 0x%08x\n",
|
|
|
|
offset, v & mask, value);
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ath11k_pci_set_link_reg(ab,
|
2021-02-16 09:16:22 +02:00
|
|
|
PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(ab),
|
2020-12-10 16:05:22 +02:00
|
|
|
PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL,
|
|
|
|
PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK);
|
2020-12-16 11:32:12 +03:00
|
|
|
if (ret) {
|
2020-12-10 16:05:22 +02:00
|
|
|
ath11k_warn(ab, "failed to set sysclk: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ath11k_pci_set_link_reg(ab,
|
2021-02-16 09:16:22 +02:00
|
|
|
PCIE_PCS_OSC_DTCT_CONFIG1_REG(ab),
|
|
|
|
PCIE_PCS_OSC_DTCT_CONFIG1_VAL,
|
|
|
|
PCIE_PCS_OSC_DTCT_CONFIG_MSK);
|
2020-12-16 11:32:12 +03:00
|
|
|
if (ret) {
|
2020-12-10 16:05:22 +02:00
|
|
|
ath11k_warn(ab, "failed to set dtct config1 error: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ath11k_pci_set_link_reg(ab,
|
2021-02-16 09:16:22 +02:00
|
|
|
PCIE_PCS_OSC_DTCT_CONFIG2_REG(ab),
|
|
|
|
PCIE_PCS_OSC_DTCT_CONFIG2_VAL,
|
|
|
|
PCIE_PCS_OSC_DTCT_CONFIG_MSK);
|
2020-12-16 11:32:12 +03:00
|
|
|
if (ret) {
|
2020-12-10 16:05:22 +02:00
|
|
|
ath11k_warn(ab, "failed to set dtct config2: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ath11k_pci_set_link_reg(ab,
|
2021-02-16 09:16:22 +02:00
|
|
|
PCIE_PCS_OSC_DTCT_CONFIG4_REG(ab),
|
|
|
|
PCIE_PCS_OSC_DTCT_CONFIG4_VAL,
|
|
|
|
PCIE_PCS_OSC_DTCT_CONFIG_MSK);
|
2020-12-16 11:32:12 +03:00
|
|
|
if (ret) {
|
2020-12-10 16:05:22 +02:00
|
|
|
ath11k_warn(ab, "failed to set dtct config4: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-10 16:05:21 +02:00
|
|
|
static void ath11k_pci_enable_ltssm(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
int i;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, PCIE_PCIE_PARF_LTSSM);
|
2020-12-10 16:05:21 +02:00
|
|
|
|
|
|
|
/* PCIE link seems very unstable after the Hot Reset*/
|
|
|
|
for (i = 0; val != PARM_LTSSM_VALUE && i < 5; i++) {
|
|
|
|
if (val == 0xffffffff)
|
|
|
|
mdelay(5);
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, PCIE_PCIE_PARF_LTSSM, PARM_LTSSM_VALUE);
|
|
|
|
val = ath11k_pcic_read32(ab, PCIE_PCIE_PARF_LTSSM);
|
2020-12-10 16:05:21 +02:00
|
|
|
}
|
|
|
|
|
2023-06-09 17:24:28 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "ltssm 0x%x\n", val);
|
2020-12-10 16:05:21 +02:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, GCC_GCC_PCIE_HOT_RST);
|
2020-12-16 20:24:11 +02:00
|
|
|
val |= GCC_GCC_PCIE_HOT_RST_VAL;
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, GCC_GCC_PCIE_HOT_RST, val);
|
|
|
|
val = ath11k_pcic_read32(ab, GCC_GCC_PCIE_HOT_RST);
|
2020-12-10 16:05:21 +02:00
|
|
|
|
2023-06-09 17:24:28 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "pcie_hot_rst 0x%x\n", val);
|
2020-12-10 16:05:21 +02:00
|
|
|
|
|
|
|
mdelay(5);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pci_clear_all_intrs(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
/* This is a WAR for PCIE Hotreset.
|
|
|
|
* When target receive Hotreset, but will set the interrupt.
|
|
|
|
* So when download SBL again, SBL will open Interrupt and
|
|
|
|
* receive it, and crash immediately.
|
|
|
|
*/
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, PCIE_PCIE_INT_ALL_CLEAR, PCIE_INT_CLEAR_ALL);
|
2020-12-10 16:05:21 +02:00
|
|
|
}
|
|
|
|
|
2020-12-10 16:05:23 +02:00
|
|
|
static void ath11k_pci_set_wlaon_pwr_ctrl(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, WLAON_QFPROM_PWR_CTRL_REG);
|
2020-12-10 16:05:23 +02:00
|
|
|
val &= ~QFPROM_PWR_CTRL_VDD4BLOW_MASK;
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, WLAON_QFPROM_PWR_CTRL_REG, val);
|
2020-12-10 16:05:23 +02:00
|
|
|
}
|
|
|
|
|
2020-08-17 13:31:55 +03:00
|
|
|
static void ath11k_pci_force_wake(struct ath11k_base *ab)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1);
|
2020-08-17 13:31:55 +03:00
|
|
|
mdelay(5);
|
|
|
|
}
|
|
|
|
|
2020-12-10 16:05:21 +02:00
|
|
|
static void ath11k_pci_sw_reset(struct ath11k_base *ab, bool power_on)
|
2020-08-17 13:31:55 +03:00
|
|
|
{
|
2021-09-28 14:00:44 +03:00
|
|
|
mdelay(100);
|
|
|
|
|
2020-12-10 16:05:21 +02:00
|
|
|
if (power_on) {
|
|
|
|
ath11k_pci_enable_ltssm(ab);
|
|
|
|
ath11k_pci_clear_all_intrs(ab);
|
2020-12-10 16:05:23 +02:00
|
|
|
ath11k_pci_set_wlaon_pwr_ctrl(ab);
|
2021-05-31 17:41:28 +03:00
|
|
|
if (ab->hw_params.fix_l1ss)
|
|
|
|
ath11k_pci_fix_l1ss(ab);
|
2020-12-10 16:05:21 +02:00
|
|
|
}
|
|
|
|
|
2020-08-17 13:31:55 +03:00
|
|
|
ath11k_mhi_clear_vector(ab);
|
2021-09-28 14:00:44 +03:00
|
|
|
ath11k_pci_clear_dbg_registers(ab);
|
2020-08-17 13:31:55 +03:00
|
|
|
ath11k_pci_soc_global_reset(ab);
|
|
|
|
ath11k_mhi_set_mhictrl_reset(ab);
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:25 +03:00
|
|
|
static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg;
|
|
|
|
|
2020-09-08 07:55:35 +00:00
|
|
|
cfg->tgt_ce = ab->hw_params.target_ce_config;
|
|
|
|
cfg->tgt_ce_len = ab->hw_params.target_ce_count;
|
2020-08-13 12:04:25 +03:00
|
|
|
|
2020-09-08 07:55:35 +00:00
|
|
|
cfg->svc_to_ce_map = ab->hw_params.svc_to_ce_map;
|
|
|
|
cfg->svc_to_ce_map_len = ab->hw_params.svc_to_ce_map_len;
|
2021-02-16 09:15:48 +02:00
|
|
|
ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id;
|
2020-10-01 12:34:44 +03:00
|
|
|
|
|
|
|
ath11k_ce_get_shadow_config(ab, &cfg->shadow_reg_v2,
|
|
|
|
&cfg->shadow_reg_v2_len);
|
2020-08-13 12:04:25 +03:00
|
|
|
}
|
|
|
|
|
2021-10-11 09:33:08 +03:00
|
|
|
static void ath11k_pci_msi_config(struct ath11k_pci *ab_pci, bool enable)
|
|
|
|
{
|
|
|
|
struct pci_dev *dev = ab_pci->pdev;
|
|
|
|
u16 control;
|
|
|
|
|
|
|
|
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
|
|
|
|
|
|
|
|
if (enable)
|
|
|
|
control |= PCI_MSI_FLAGS_ENABLE;
|
|
|
|
else
|
|
|
|
control &= ~PCI_MSI_FLAGS_ENABLE;
|
|
|
|
|
|
|
|
pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pci_msi_enable(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
|
|
|
ath11k_pci_msi_config(ab_pci, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pci_msi_disable(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
|
|
|
ath11k_pci_msi_config(ab_pci, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath11k_pci_alloc_msi(struct ath11k_pci *ab_pci)
|
2020-08-13 12:04:22 +03:00
|
|
|
{
|
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
2022-04-01 14:53:08 +03:00
|
|
|
const struct ath11k_msi_config *msi_config = ab->pci.msi.config;
|
|
|
|
struct pci_dev *pci_dev = ab_pci->pdev;
|
2020-08-13 12:04:22 +03:00
|
|
|
struct msi_desc *msi_desc;
|
|
|
|
int num_vectors;
|
|
|
|
int ret;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
num_vectors = pci_alloc_irq_vectors(pci_dev,
|
2021-02-16 09:15:35 +02:00
|
|
|
msi_config->total_vectors,
|
|
|
|
msi_config->total_vectors,
|
2020-08-13 12:04:22 +03:00
|
|
|
PCI_IRQ_MSI);
|
2021-11-19 15:36:26 +02:00
|
|
|
if (num_vectors == msi_config->total_vectors) {
|
2022-04-01 14:53:08 +03:00
|
|
|
set_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags);
|
2021-11-19 15:36:26 +02:00
|
|
|
} else {
|
|
|
|
num_vectors = pci_alloc_irq_vectors(ab_pci->pdev,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
PCI_IRQ_MSI);
|
|
|
|
if (num_vectors < 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto reset_msi_config;
|
|
|
|
}
|
2022-04-01 14:53:08 +03:00
|
|
|
clear_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags);
|
2022-04-01 14:53:08 +03:00
|
|
|
ab->pci.msi.config = &msi_config_one_msi;
|
2023-06-09 17:24:34 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "request one msi vector\n");
|
2020-08-13 12:04:22 +03:00
|
|
|
}
|
2021-11-19 15:36:26 +02:00
|
|
|
ath11k_info(ab, "MSI vectors: %d\n", num_vectors);
|
|
|
|
|
2021-10-11 09:33:08 +03:00
|
|
|
ath11k_pci_msi_disable(ab_pci);
|
2020-08-13 12:04:22 +03:00
|
|
|
|
|
|
|
msi_desc = irq_get_msi_desc(ab_pci->pdev->irq);
|
|
|
|
if (!msi_desc) {
|
|
|
|
ath11k_err(ab, "msi_desc is NULL!\n");
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto free_msi_vector;
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ab->pci.msi.ep_base_data = msi_desc->msg.data;
|
|
|
|
|
|
|
|
pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO,
|
|
|
|
&ab->pci.msi.addr_lo);
|
|
|
|
|
|
|
|
if (msi_desc->pci.msi_attrib.is_64) {
|
|
|
|
pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI,
|
|
|
|
&ab->pci.msi.addr_hi);
|
|
|
|
} else {
|
|
|
|
ab->pci.msi.addr_hi = 0;
|
|
|
|
}
|
2020-08-13 12:04:22 +03:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "msi base data is %d\n", ab->pci.msi.ep_base_data);
|
2020-08-13 12:04:22 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_msi_vector:
|
|
|
|
pci_free_irq_vectors(ab_pci->pdev);
|
|
|
|
|
2021-11-19 15:36:26 +02:00
|
|
|
reset_msi_config:
|
2020-08-13 12:04:22 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-10-11 09:33:08 +03:00
|
|
|
static void ath11k_pci_free_msi(struct ath11k_pci *ab_pci)
|
2020-08-13 12:04:22 +03:00
|
|
|
{
|
|
|
|
pci_free_irq_vectors(ab_pci->pdev);
|
|
|
|
}
|
|
|
|
|
2021-11-19 15:36:26 +02:00
|
|
|
static int ath11k_pci_config_msi_data(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
|
|
|
struct msi_desc *msi_desc;
|
|
|
|
|
|
|
|
msi_desc = irq_get_msi_desc(ab_pci->pdev->irq);
|
|
|
|
if (!msi_desc) {
|
|
|
|
ath11k_err(ab_pci->ab, "msi_desc is NULL!\n");
|
|
|
|
pci_free_irq_vectors(ab_pci->pdev);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ab_pci->ab->pci.msi.ep_base_data = msi_desc->msg.data;
|
2021-11-19 15:36:26 +02:00
|
|
|
|
2023-06-09 17:24:28 +03:00
|
|
|
ath11k_dbg(ab_pci->ab, ATH11K_DBG_PCI, "after request_irq msi_ep_base_data %d\n",
|
2022-04-01 14:53:08 +03:00
|
|
|
ab_pci->ab->pci.msi.ep_base_data);
|
2021-11-19 15:36:26 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:21 +03:00
|
|
|
static int ath11k_pci_claim(struct ath11k_pci *ab_pci, struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
|
|
|
u16 device_id;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id);
|
|
|
|
if (device_id != ab_pci->dev_id) {
|
|
|
|
ath11k_err(ab, "pci device id mismatch: 0x%x 0x%x\n",
|
|
|
|
device_id, ab_pci->dev_id);
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pci_assign_resource(pdev, ATH11K_PCI_BAR_NUM);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to assign pci resource: %d\n", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pci_enable_device(pdev);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to enable pci device: %d\n", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pci_request_region(pdev, ATH11K_PCI_BAR_NUM, "ath11k_pci");
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to request pci region: %d\n", ret);
|
|
|
|
goto disable_device;
|
|
|
|
}
|
|
|
|
|
wifi: ath11k: enable 36 bit mask for stream DMA
Currently 32 bit DMA mask is used, telling kernel to get us an DMA
address under 4GB when mapping a buffer. This results in a very high
CPU overhead in the case where IOMMU is disabled and more than 4GB
system memory is installed. The reason is, with more than 4GB memory
installed, kernel is likely to allocate a buffer whose physical
address is above 4GB. While with IOMMU disabled, kernel has to involve
SWIOTLB to map/unmap that buffer, which consumes lots of CPU cycles.
We did hit an issue caused by the reason mentioned above: in a system
that disables IOMMU and gets 8GB memory installed, a total of 40.5%
CPU usage is observed in throughput test. CPU profiling shows nearly
60% of CPU cycles are consumed by SWIOTLB.
By enabling 36 bit DMA mask, we can bypass SWIOTLB for any buffer
whose physical address is below 64GB. There are two types of DMA mask
within struct device, named dma_mask and coherent_dma_mask. Here we
only enable 36 bit for dma_mask, because firmware crashes if
coherent_dma_mask is also enabled, due to some unknown hardware
limitations. This is acceptable because coherent_dma_mask is used for
mapping a consistent DMA buffer, which generally does not happen in
a hot path.
With this change, the total CPU usage mentioned in above issue drops
to 18.9%.
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
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://msgid.link/20240123015201.28939-1-quic_bqiang@quicinc.com
2024-01-23 09:52:01 +08:00
|
|
|
ret = dma_set_mask(&pdev->dev,
|
|
|
|
DMA_BIT_MASK(ATH11K_PCI_DMA_MASK));
|
2020-08-13 12:04:21 +03:00
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to set pci dma mask to %d: %d\n",
|
|
|
|
ATH11K_PCI_DMA_MASK, ret);
|
|
|
|
goto release_region;
|
|
|
|
}
|
|
|
|
|
wifi: ath11k: enable 36 bit mask for stream DMA
Currently 32 bit DMA mask is used, telling kernel to get us an DMA
address under 4GB when mapping a buffer. This results in a very high
CPU overhead in the case where IOMMU is disabled and more than 4GB
system memory is installed. The reason is, with more than 4GB memory
installed, kernel is likely to allocate a buffer whose physical
address is above 4GB. While with IOMMU disabled, kernel has to involve
SWIOTLB to map/unmap that buffer, which consumes lots of CPU cycles.
We did hit an issue caused by the reason mentioned above: in a system
that disables IOMMU and gets 8GB memory installed, a total of 40.5%
CPU usage is observed in throughput test. CPU profiling shows nearly
60% of CPU cycles are consumed by SWIOTLB.
By enabling 36 bit DMA mask, we can bypass SWIOTLB for any buffer
whose physical address is below 64GB. There are two types of DMA mask
within struct device, named dma_mask and coherent_dma_mask. Here we
only enable 36 bit for dma_mask, because firmware crashes if
coherent_dma_mask is also enabled, due to some unknown hardware
limitations. This is acceptable because coherent_dma_mask is used for
mapping a consistent DMA buffer, which generally does not happen in
a hot path.
With this change, the total CPU usage mentioned in above issue drops
to 18.9%.
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
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://msgid.link/20240123015201.28939-1-quic_bqiang@quicinc.com
2024-01-23 09:52:01 +08:00
|
|
|
ab_pci->dma_mask = DMA_BIT_MASK(ATH11K_PCI_DMA_MASK);
|
|
|
|
|
|
|
|
ret = dma_set_coherent_mask(&pdev->dev,
|
|
|
|
DMA_BIT_MASK(ATH11K_PCI_COHERENT_DMA_MASK));
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to set pci coherent dma mask to %d: %d\n",
|
|
|
|
ATH11K_PCI_COHERENT_DMA_MASK, ret);
|
|
|
|
goto release_region;
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:21 +03:00
|
|
|
pci_set_master(pdev);
|
|
|
|
|
|
|
|
ab->mem_len = pci_resource_len(pdev, ATH11K_PCI_BAR_NUM);
|
|
|
|
ab->mem = pci_iomap(pdev, ATH11K_PCI_BAR_NUM, 0);
|
|
|
|
if (!ab->mem) {
|
|
|
|
ath11k_err(ab, "failed to map pci bar %d\n", ATH11K_PCI_BAR_NUM);
|
|
|
|
ret = -EIO;
|
2023-03-23 19:26:09 +08:00
|
|
|
goto release_region;
|
2020-08-13 12:04:21 +03:00
|
|
|
}
|
|
|
|
|
2022-12-02 23:37:14 +02:00
|
|
|
ab->mem_ce = ab->mem;
|
|
|
|
|
2023-06-09 17:24:38 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_BOOT, "pci_mem 0x%p\n", ab->mem);
|
2020-08-13 12:04:21 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
release_region:
|
|
|
|
pci_release_region(pdev, ATH11K_PCI_BAR_NUM);
|
|
|
|
disable_device:
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pci_free_region(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
|
|
|
struct pci_dev *pci_dev = ab_pci->pdev;
|
|
|
|
|
|
|
|
pci_iounmap(pci_dev, ab->mem);
|
|
|
|
ab->mem = NULL;
|
|
|
|
pci_release_region(pci_dev, ATH11K_PCI_BAR_NUM);
|
|
|
|
if (pci_is_enabled(pci_dev))
|
|
|
|
pci_disable_device(pci_dev);
|
|
|
|
}
|
|
|
|
|
2020-12-17 17:22:10 +02:00
|
|
|
static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
|
|
|
|
|
|
|
pcie_capability_read_word(ab_pci->pdev, PCI_EXP_LNKCTL,
|
|
|
|
&ab_pci->link_ctl);
|
|
|
|
|
2023-06-09 17:24:28 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "link_ctl 0x%04x L0s %d L1 %d\n",
|
2020-12-17 17:22:10 +02:00
|
|
|
ab_pci->link_ctl,
|
|
|
|
u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L0S),
|
|
|
|
u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L1));
|
|
|
|
|
|
|
|
/* disable L0s and L1 */
|
2023-07-17 15:05:00 +03:00
|
|
|
pcie_capability_clear_word(ab_pci->pdev, PCI_EXP_LNKCTL,
|
|
|
|
PCI_EXP_LNKCTL_ASPMC);
|
2020-12-17 17:22:10 +02:00
|
|
|
|
|
|
|
set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags);
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
|
|
|
if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags))
|
2023-07-17 15:05:00 +03:00
|
|
|
pcie_capability_clear_and_set_word(ab_pci->pdev, PCI_EXP_LNKCTL,
|
|
|
|
PCI_EXP_LNKCTL_ASPMC,
|
|
|
|
ab_pci->link_ctl &
|
|
|
|
PCI_EXP_LNKCTL_ASPMC);
|
2022-04-01 14:53:08 +03:00
|
|
|
}
|
|
|
|
|
2024-08-13 09:30:28 +08:00
|
|
|
#ifdef CONFIG_DEV_COREDUMP
|
|
|
|
static int ath11k_pci_coredump_calculate_size(struct ath11k_base *ab, u32 *dump_seg_sz)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
|
|
|
struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl;
|
|
|
|
struct image_info *rddm_img, *fw_img;
|
|
|
|
struct ath11k_tlv_dump_data *dump_tlv;
|
|
|
|
enum ath11k_fw_crash_dump_type mem_type;
|
|
|
|
u32 len = 0, rddm_tlv_sz = 0, paging_tlv_sz = 0;
|
|
|
|
struct ath11k_dump_file_data *file_data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
rddm_img = mhi_ctrl->rddm_image;
|
|
|
|
if (!rddm_img) {
|
|
|
|
ath11k_err(ab, "No RDDM dump found\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
fw_img = mhi_ctrl->fbc_image;
|
|
|
|
|
|
|
|
for (i = 0; i < fw_img->entries ; i++) {
|
|
|
|
if (!fw_img->mhi_buf[i].buf)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
paging_tlv_sz += fw_img->mhi_buf[i].len;
|
|
|
|
}
|
|
|
|
dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA] = paging_tlv_sz;
|
|
|
|
|
|
|
|
for (i = 0; i < rddm_img->entries; i++) {
|
|
|
|
if (!rddm_img->mhi_buf[i].buf)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
rddm_tlv_sz += rddm_img->mhi_buf[i].len;
|
|
|
|
}
|
|
|
|
dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA] = rddm_tlv_sz;
|
|
|
|
|
|
|
|
for (i = 0; i < ab->qmi.mem_seg_count; i++) {
|
|
|
|
mem_type = ath11k_coredump_get_dump_type(ab->qmi.target_mem[i].type);
|
|
|
|
|
|
|
|
if (mem_type == FW_CRASH_DUMP_NONE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (mem_type == FW_CRASH_DUMP_TYPE_MAX) {
|
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI,
|
|
|
|
"target mem region type %d not supported",
|
|
|
|
ab->qmi.target_mem[i].type);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ab->qmi.target_mem[i].anyaddr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dump_seg_sz[mem_type] += ab->qmi.target_mem[i].size;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < FW_CRASH_DUMP_TYPE_MAX; i++) {
|
|
|
|
if (!dump_seg_sz[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
len += sizeof(*dump_tlv) + dump_seg_sz[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len)
|
|
|
|
len += sizeof(*file_data);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pci_coredump_download(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
|
|
|
struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl;
|
|
|
|
struct image_info *rddm_img, *fw_img;
|
|
|
|
struct timespec64 timestamp;
|
|
|
|
int i, len, mem_idx;
|
|
|
|
enum ath11k_fw_crash_dump_type mem_type;
|
|
|
|
struct ath11k_dump_file_data *file_data;
|
|
|
|
struct ath11k_tlv_dump_data *dump_tlv;
|
|
|
|
size_t hdr_len = sizeof(*file_data);
|
|
|
|
void *buf;
|
2025-07-20 08:13:38 -07:00
|
|
|
u32 dump_seg_sz[FW_CRASH_DUMP_TYPE_MAX] = {};
|
2024-08-13 09:30:28 +08:00
|
|
|
|
|
|
|
ath11k_mhi_coredump(mhi_ctrl, false);
|
|
|
|
|
|
|
|
len = ath11k_pci_coredump_calculate_size(ab, dump_seg_sz);
|
|
|
|
if (!len) {
|
|
|
|
ath11k_warn(ab, "No crash dump data found for devcoredump");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rddm_img = mhi_ctrl->rddm_image;
|
|
|
|
fw_img = mhi_ctrl->fbc_image;
|
|
|
|
|
|
|
|
/* dev_coredumpv() requires vmalloc data */
|
|
|
|
buf = vzalloc(len);
|
|
|
|
if (!buf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ab->dump_data = buf;
|
|
|
|
ab->ath11k_coredump_len = len;
|
|
|
|
file_data = ab->dump_data;
|
|
|
|
strscpy(file_data->df_magic, "ATH11K-FW-DUMP", sizeof(file_data->df_magic));
|
|
|
|
file_data->len = cpu_to_le32(len);
|
|
|
|
file_data->version = cpu_to_le32(ATH11K_FW_CRASH_DUMP_V2);
|
|
|
|
file_data->chip_id = cpu_to_le32(ab_pci->dev_id);
|
|
|
|
file_data->qrtr_id = cpu_to_le32(ab_pci->ab->qmi.service_ins_id);
|
|
|
|
file_data->bus_id = cpu_to_le32(pci_domain_nr(ab_pci->pdev->bus));
|
|
|
|
guid_gen(&file_data->guid);
|
|
|
|
ktime_get_real_ts64(×tamp);
|
|
|
|
file_data->tv_sec = cpu_to_le64(timestamp.tv_sec);
|
|
|
|
file_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec);
|
|
|
|
buf += hdr_len;
|
|
|
|
dump_tlv = buf;
|
|
|
|
dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_PAGING_DATA);
|
|
|
|
dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA]);
|
|
|
|
buf += COREDUMP_TLV_HDR_SIZE;
|
|
|
|
|
|
|
|
/* append all segments together as they are all part of a single contiguous
|
|
|
|
* block of memory
|
|
|
|
*/
|
|
|
|
for (i = 0; i < fw_img->entries ; i++) {
|
|
|
|
if (!fw_img->mhi_buf[i].buf)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
memcpy_fromio(buf, (void const __iomem *)fw_img->mhi_buf[i].buf,
|
|
|
|
fw_img->mhi_buf[i].len);
|
|
|
|
buf += fw_img->mhi_buf[i].len;
|
|
|
|
}
|
|
|
|
|
|
|
|
dump_tlv = buf;
|
|
|
|
dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_RDDM_DATA);
|
|
|
|
dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA]);
|
|
|
|
buf += COREDUMP_TLV_HDR_SIZE;
|
|
|
|
|
|
|
|
/* append all segments together as they are all part of a single contiguous
|
|
|
|
* block of memory
|
|
|
|
*/
|
|
|
|
for (i = 0; i < rddm_img->entries; i++) {
|
|
|
|
if (!rddm_img->mhi_buf[i].buf)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
memcpy_fromio(buf, (void const __iomem *)rddm_img->mhi_buf[i].buf,
|
|
|
|
rddm_img->mhi_buf[i].len);
|
|
|
|
buf += rddm_img->mhi_buf[i].len;
|
|
|
|
}
|
|
|
|
|
|
|
|
mem_idx = FW_CRASH_DUMP_REMOTE_MEM_DATA;
|
|
|
|
for (; mem_idx < FW_CRASH_DUMP_TYPE_MAX; mem_idx++) {
|
|
|
|
if (mem_idx == FW_CRASH_DUMP_NONE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (i = 0; i < ab->qmi.mem_seg_count; i++) {
|
|
|
|
mem_type = ath11k_coredump_get_dump_type
|
|
|
|
(ab->qmi.target_mem[i].type);
|
|
|
|
|
|
|
|
if (mem_type != mem_idx)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!ab->qmi.target_mem[i].anyaddr) {
|
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI,
|
|
|
|
"Skipping mem region type %d",
|
|
|
|
ab->qmi.target_mem[i].type);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
dump_tlv = buf;
|
|
|
|
dump_tlv->type = cpu_to_le32(mem_idx);
|
|
|
|
dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[mem_idx]);
|
|
|
|
buf += COREDUMP_TLV_HDR_SIZE;
|
|
|
|
|
|
|
|
memcpy_fromio(buf, ab->qmi.target_mem[i].iaddr,
|
|
|
|
ab->qmi.target_mem[i].size);
|
|
|
|
|
|
|
|
buf += ab->qmi.target_mem[i].size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
queue_work(ab->workqueue, &ab->dump_work);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-08-13 12:04:24 +03:00
|
|
|
static int ath11k_pci_power_up(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
|
|
|
int ret;
|
|
|
|
|
2020-10-01 12:34:43 +03:00
|
|
|
ab_pci->register_window = 0;
|
2022-04-01 14:53:08 +03:00
|
|
|
clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);
|
2020-12-10 16:05:21 +02:00
|
|
|
ath11k_pci_sw_reset(ab_pci->ab, true);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
2020-12-17 17:22:10 +02:00
|
|
|
/* Disable ASPM during firmware download due to problems switching
|
|
|
|
* to AMSS state.
|
|
|
|
*/
|
|
|
|
ath11k_pci_aspm_disable(ab_pci);
|
|
|
|
|
2021-10-11 09:33:08 +03:00
|
|
|
ath11k_pci_msi_enable(ab_pci);
|
|
|
|
|
2020-08-13 12:04:24 +03:00
|
|
|
ret = ath11k_mhi_start(ab_pci);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to start mhi: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-04-29 22:34:55 +05:30
|
|
|
if (ab->hw_params.static_window_map)
|
2021-02-16 09:16:17 +02:00
|
|
|
ath11k_pci_select_static_window(ab_pci);
|
|
|
|
|
2020-08-13 12:04:24 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-03-28 13:32:27 +08:00
|
|
|
static void ath11k_pci_power_down(struct ath11k_base *ab, bool is_suspend)
|
2020-08-13 12:04:24 +03:00
|
|
|
{
|
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
|
|
|
|
2020-12-17 17:22:10 +02:00
|
|
|
/* restore aspm in case firmware bootup fails */
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pci_aspm_restore(ab_pci);
|
2020-12-17 17:22:10 +02:00
|
|
|
|
2020-12-10 16:05:21 +02:00
|
|
|
ath11k_pci_force_wake(ab_pci->ab);
|
2021-10-11 09:33:08 +03:00
|
|
|
|
|
|
|
ath11k_pci_msi_disable(ab_pci);
|
|
|
|
|
2025-03-28 13:32:27 +08:00
|
|
|
ath11k_mhi_stop(ab_pci, is_suspend);
|
2022-04-01 14:53:08 +03:00
|
|
|
clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);
|
2020-12-10 16:05:21 +02:00
|
|
|
ath11k_pci_sw_reset(ab_pci->ab, false);
|
2020-08-13 12:04:24 +03:00
|
|
|
}
|
|
|
|
|
2020-12-11 19:35:42 +02:00
|
|
|
static int ath11k_pci_hif_suspend(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ar_pci = ath11k_pci_priv(ab);
|
|
|
|
|
2022-04-05 11:26:39 +03:00
|
|
|
return ath11k_mhi_suspend(ar_pci);
|
2020-12-11 19:35:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ath11k_pci_hif_resume(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ar_pci = ath11k_pci_priv(ab);
|
|
|
|
|
2022-04-05 11:26:39 +03:00
|
|
|
return ath11k_mhi_resume(ar_pci);
|
2020-12-11 19:35:42 +02:00
|
|
|
}
|
|
|
|
|
2020-12-11 19:35:49 +02:00
|
|
|
static void ath11k_pci_hif_ce_irq_enable(struct ath11k_base *ab)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_ce_irqs_enable(ab);
|
2020-12-11 19:35:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pci_hif_ce_irq_disable(struct ath11k_base *ab)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_ce_irq_disable_sync(ab);
|
2020-08-13 12:04:25 +03:00
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
static int ath11k_pci_start(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
|
|
|
|
|
|
|
/* TODO: for now don't restore ASPM in case of single MSI
|
|
|
|
* vector as MHI register reading in M2 causes system hang.
|
|
|
|
*/
|
|
|
|
if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
|
|
|
|
ath11k_pci_aspm_restore(ab_pci);
|
|
|
|
else
|
|
|
|
ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n");
|
|
|
|
|
|
|
|
ath11k_pcic_start(ab);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:25 +03:00
|
|
|
static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
|
2022-04-01 14:53:08 +03:00
|
|
|
.start = ath11k_pci_start,
|
2022-04-01 14:53:08 +03:00
|
|
|
.stop = ath11k_pcic_stop,
|
|
|
|
.read32 = ath11k_pcic_read32,
|
|
|
|
.write32 = ath11k_pcic_write32,
|
2022-09-07 11:31:13 +03:00
|
|
|
.read = ath11k_pcic_read,
|
2020-08-13 12:04:24 +03:00
|
|
|
.power_down = ath11k_pci_power_down,
|
|
|
|
.power_up = ath11k_pci_power_up,
|
2020-12-11 19:35:42 +02:00
|
|
|
.suspend = ath11k_pci_hif_suspend,
|
|
|
|
.resume = ath11k_pci_hif_resume,
|
2022-04-01 14:53:08 +03:00
|
|
|
.irq_enable = ath11k_pcic_ext_irq_enable,
|
|
|
|
.irq_disable = ath11k_pcic_ext_irq_disable,
|
|
|
|
.get_msi_address = ath11k_pcic_get_msi_address,
|
2022-04-01 14:53:08 +03:00
|
|
|
.get_user_msi_vector = ath11k_pcic_get_user_msi_assignment,
|
2022-04-01 14:53:08 +03:00
|
|
|
.map_service_to_pipe = ath11k_pcic_map_service_to_pipe,
|
2020-12-11 19:35:49 +02:00
|
|
|
.ce_irq_enable = ath11k_pci_hif_ce_irq_enable,
|
|
|
|
.ce_irq_disable = ath11k_pci_hif_ce_irq_disable,
|
2022-04-01 14:53:08 +03:00
|
|
|
.get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx,
|
2024-08-13 09:30:28 +08:00
|
|
|
#ifdef CONFIG_DEV_COREDUMP
|
|
|
|
.coredump_download = ath11k_pci_coredump_download,
|
|
|
|
#endif
|
2020-08-13 12:04:24 +03:00
|
|
|
};
|
|
|
|
|
2021-05-31 17:41:28 +03:00
|
|
|
static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor)
|
|
|
|
{
|
|
|
|
u32 soc_hw_version;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
soc_hw_version = ath11k_pcic_read32(ab, TCSR_SOC_HW_VERSION);
|
2021-05-31 17:41:28 +03:00
|
|
|
*major = FIELD_GET(TCSR_SOC_HW_VERSION_MAJOR_MASK,
|
|
|
|
soc_hw_version);
|
|
|
|
*minor = FIELD_GET(TCSR_SOC_HW_VERSION_MINOR_MASK,
|
|
|
|
soc_hw_version);
|
|
|
|
|
2023-06-09 17:24:28 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "tcsr_soc_hw_version major %d minor %d\n",
|
2021-05-31 17:41:28 +03:00
|
|
|
*major, *minor);
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
static int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci,
|
|
|
|
const struct cpumask *m)
|
|
|
|
{
|
|
|
|
if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab_pci->ab->dev_flags))
|
|
|
|
return 0;
|
|
|
|
|
2025-02-25 11:04:47 +05:30
|
|
|
return irq_set_affinity_and_hint(ab_pci->pdev->irq, m);
|
2022-04-01 14:53:08 +03:00
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:20 +03:00
|
|
|
static int ath11k_pci_probe(struct pci_dev *pdev,
|
|
|
|
const struct pci_device_id *pci_dev)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab;
|
2020-08-13 12:04:21 +03:00
|
|
|
struct ath11k_pci *ab_pci;
|
2025-04-08 14:02:11 -05:00
|
|
|
u32 soc_hw_version_major, soc_hw_version_minor;
|
2020-08-13 12:04:21 +03:00
|
|
|
int ret;
|
2024-01-09 10:13:36 +08:00
|
|
|
u32 sub_version;
|
2020-08-13 12:04:20 +03:00
|
|
|
|
2022-04-29 22:34:55 +05:30
|
|
|
ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI);
|
|
|
|
|
2020-08-13 12:04:20 +03:00
|
|
|
if (!ab) {
|
|
|
|
dev_err(&pdev->dev, "failed to allocate ath11k base\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
ab->dev = &pdev->dev;
|
|
|
|
pci_set_drvdata(pdev, ab);
|
2020-08-13 12:04:21 +03:00
|
|
|
ab_pci = ath11k_pci_priv(ab);
|
|
|
|
ab_pci->dev_id = pci_dev->device;
|
|
|
|
ab_pci->ab = ab;
|
2020-08-13 12:04:22 +03:00
|
|
|
ab_pci->pdev = pdev;
|
2020-08-13 12:04:25 +03:00
|
|
|
ab->hif.ops = &ath11k_pci_hif_ops;
|
2023-05-26 12:41:07 +03:00
|
|
|
ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL;
|
2020-08-13 12:04:21 +03:00
|
|
|
pci_set_drvdata(pdev, ab);
|
2020-08-14 10:10:23 +03:00
|
|
|
spin_lock_init(&ab_pci->window_lock);
|
2020-08-13 12:04:21 +03:00
|
|
|
|
2021-12-14 17:39:43 +02:00
|
|
|
/* Set fixed_mem_region to true for platforms support reserved memory
|
|
|
|
* from DT. If memory is reserved from DT for FW, ath11k driver need not
|
|
|
|
* allocate memory.
|
|
|
|
*/
|
2025-04-08 14:02:11 -05:00
|
|
|
if (of_property_present(ab->dev->of_node, "memory-region"))
|
2021-12-14 17:39:43 +02:00
|
|
|
set_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags);
|
|
|
|
|
2020-08-13 12:04:21 +03:00
|
|
|
ret = ath11k_pci_claim(ab_pci, pdev);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to claim device: %d\n", ret);
|
|
|
|
goto err_free_core;
|
|
|
|
}
|
2020-08-13 12:04:20 +03:00
|
|
|
|
ath11k: add string type to search board data in board-2.bin for WCN6855
Currently ath11k only support string type with bus, chip id and board id
such as "bus=ahb,qmi-chip-id=1,qmi-board-id=4" for ahb bus chip and
"bus=pci,qmi-chip-id=0,qmi-board-id=255" for PCIe bus chip in
board-2.bin. For WCN6855, it is not enough to distinguish all different
chips.
This is to add a new string type which include bus, chip id, board id,
vendor, device, subsystem-vendor and subsystem-device for WCN6855.
ath11k will first load board-2.bin and search in it for the board data
with the above parameters, if matched one board data, then download it
to firmware, if not matched any one, then ath11k will download the file
board.bin to firmware.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1
Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20211111065340.20187-1-quic_wgong@quicinc.com
2021-11-15 11:29:55 +02:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_BOOT, "pci probe %04x:%04x %04x:%04x\n",
|
|
|
|
pdev->vendor, pdev->device,
|
|
|
|
pdev->subsystem_vendor, pdev->subsystem_device);
|
|
|
|
|
|
|
|
ab->id.vendor = pdev->vendor;
|
|
|
|
ab->id.device = pdev->device;
|
|
|
|
ab->id.subsystem_vendor = pdev->subsystem_vendor;
|
|
|
|
ab->id.subsystem_device = pdev->subsystem_device;
|
|
|
|
|
2020-09-29 20:15:32 +03:00
|
|
|
switch (pci_dev->device) {
|
|
|
|
case QCA6390_DEVICE_ID:
|
2024-01-09 10:13:35 +08:00
|
|
|
ret = ath11k_pcic_register_pci_ops(ab, &ath11k_pci_ops_qca6390);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to register PCI ops: %d\n", ret);
|
|
|
|
goto err_pci_free_region;
|
|
|
|
}
|
|
|
|
|
2021-05-31 17:41:28 +03:00
|
|
|
ath11k_pci_read_hw_version(ab, &soc_hw_version_major,
|
|
|
|
&soc_hw_version_minor);
|
2020-09-29 20:15:32 +03:00
|
|
|
switch (soc_hw_version_major) {
|
|
|
|
case 2:
|
|
|
|
ab->hw_rev = ATH11K_HW_QCA6390_HW20;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_err(&pdev->dev, "Unsupported QCA6390 SOC hardware version: %d %d\n",
|
|
|
|
soc_hw_version_major, soc_hw_version_minor);
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
goto err_pci_free_region;
|
|
|
|
}
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2021-02-16 09:16:25 +02:00
|
|
|
break;
|
|
|
|
case QCN9074_DEVICE_ID:
|
2024-01-09 10:13:35 +08:00
|
|
|
ret = ath11k_pcic_register_pci_ops(ab, &ath11k_pci_ops_qcn9074);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to register PCI ops: %d\n", ret);
|
|
|
|
goto err_pci_free_region;
|
|
|
|
}
|
2021-02-16 09:16:25 +02:00
|
|
|
ab->hw_rev = ATH11K_HW_QCN9074_HW10;
|
2020-09-29 20:15:32 +03:00
|
|
|
break;
|
2021-05-31 17:41:28 +03:00
|
|
|
case WCN6855_DEVICE_ID:
|
2024-01-09 10:13:35 +08:00
|
|
|
ret = ath11k_pcic_register_pci_ops(ab, &ath11k_pci_ops_qca6390);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to register PCI ops: %d\n", ret);
|
|
|
|
goto err_pci_free_region;
|
|
|
|
}
|
ath11k: add string type to search board data in board-2.bin for WCN6855
Currently ath11k only support string type with bus, chip id and board id
such as "bus=ahb,qmi-chip-id=1,qmi-board-id=4" for ahb bus chip and
"bus=pci,qmi-chip-id=0,qmi-board-id=255" for PCIe bus chip in
board-2.bin. For WCN6855, it is not enough to distinguish all different
chips.
This is to add a new string type which include bus, chip id, board id,
vendor, device, subsystem-vendor and subsystem-device for WCN6855.
ath11k will first load board-2.bin and search in it for the board data
with the above parameters, if matched one board data, then download it
to firmware, if not matched any one, then ath11k will download the file
board.bin to firmware.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1
Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20211111065340.20187-1-quic_wgong@quicinc.com
2021-11-15 11:29:55 +02:00
|
|
|
ab->id.bdf_search = ATH11K_BDF_SEARCH_BUS_AND_BOARD;
|
2021-05-31 17:41:28 +03:00
|
|
|
ath11k_pci_read_hw_version(ab, &soc_hw_version_major,
|
|
|
|
&soc_hw_version_minor);
|
|
|
|
switch (soc_hw_version_major) {
|
|
|
|
case 2:
|
2021-11-29 10:56:12 +08:00
|
|
|
switch (soc_hw_version_minor) {
|
|
|
|
case 0x00:
|
|
|
|
case 0x01:
|
|
|
|
ab->hw_rev = ATH11K_HW_WCN6855_HW20;
|
|
|
|
break;
|
|
|
|
case 0x10:
|
|
|
|
case 0x11:
|
2024-01-09 10:13:36 +08:00
|
|
|
sub_version = ath11k_pcic_read32(ab, TCSR_SOC_HW_SUB_VER);
|
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "sub_version 0x%x\n",
|
|
|
|
sub_version);
|
|
|
|
switch (sub_version) {
|
|
|
|
case 0x1019A0E1:
|
|
|
|
case 0x1019B0E1:
|
|
|
|
case 0x1019C0E1:
|
|
|
|
case 0x1019D0E1:
|
|
|
|
ab->hw_rev = ATH11K_HW_QCA2066_HW21;
|
|
|
|
break;
|
2024-10-31 08:05:41 +08:00
|
|
|
case 0x001e60e1:
|
|
|
|
ab->hw_rev = ATH11K_HW_QCA6698AQ_HW21;
|
|
|
|
break;
|
2024-01-09 10:13:36 +08:00
|
|
|
default:
|
|
|
|
ab->hw_rev = ATH11K_HW_WCN6855_HW21;
|
|
|
|
}
|
2021-11-29 10:56:12 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto unsupported_wcn6855_soc;
|
|
|
|
}
|
2021-05-31 17:41:28 +03:00
|
|
|
break;
|
|
|
|
default:
|
2021-11-29 10:56:12 +08:00
|
|
|
unsupported_wcn6855_soc:
|
2021-05-31 17:41:28 +03:00
|
|
|
dev_err(&pdev->dev, "Unsupported WCN6855 SOC hardware version: %d %d\n",
|
|
|
|
soc_hw_version_major, soc_hw_version_minor);
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
goto err_pci_free_region;
|
|
|
|
}
|
2022-04-01 14:53:08 +03:00
|
|
|
|
2021-05-31 17:41:28 +03:00
|
|
|
break;
|
2020-09-29 20:15:32 +03:00
|
|
|
default:
|
|
|
|
dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n",
|
|
|
|
pci_dev->device);
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
goto err_pci_free_region;
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ret = ath11k_pcic_init_msi_config(ab);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to init msi config: %d\n", ret);
|
|
|
|
goto err_pci_free_region;
|
|
|
|
}
|
|
|
|
|
2021-10-11 09:33:08 +03:00
|
|
|
ret = ath11k_pci_alloc_msi(ab_pci);
|
2020-08-13 12:04:22 +03:00
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to enable msi: %d\n", ret);
|
|
|
|
goto err_pci_free_region;
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:23 +03:00
|
|
|
ret = ath11k_core_pre_init(ab);
|
|
|
|
if (ret)
|
|
|
|
goto err_pci_disable_msi;
|
|
|
|
|
wifi: ath11k: fix boot failure with one MSI vector
Commit 5b32b6dd96633 ("ath11k: Remove core PCI references from
PCI common code") breaks with one MSI vector because it moves
affinity setting after IRQ request, see below log:
[ 1417.278835] ath11k_pci 0000:02:00.0: failed to receive control response completion, polling..
[ 1418.302829] ath11k_pci 0000:02:00.0: Service connect timeout
[ 1418.302833] ath11k_pci 0000:02:00.0: failed to connect to HTT: -110
[ 1418.303669] ath11k_pci 0000:02:00.0: failed to start core: -110
The detail is, if do affinity request after IRQ activated,
which is done in request_irq(), kernel caches that request and
returns success directly. Later when a subsequent MHI interrupt is
fired, kernel will do the real affinity setting work, as a result,
changs the MSI vector. However at that time host has configured
old vector to hardware, so host never receives CE or DP interrupts.
Fix it by setting affinity before registering MHI controller
where host is, for the first time, doing IRQ request.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-01160-QCAMSLSWPLZ-1
Fixes: 5b32b6dd9663 ("ath11k: Remove core PCI references from PCI common code")
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/20230907015606.16297-1-quic_bqiang@quicinc.com
2023-09-07 09:56:06 +08:00
|
|
|
ret = ath11k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0));
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to set irq affinity %d\n", ret);
|
|
|
|
goto err_pci_disable_msi;
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:24 +03:00
|
|
|
ret = ath11k_mhi_register(ab_pci);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to register mhi: %d\n", ret);
|
wifi: ath11k: fix boot failure with one MSI vector
Commit 5b32b6dd96633 ("ath11k: Remove core PCI references from
PCI common code") breaks with one MSI vector because it moves
affinity setting after IRQ request, see below log:
[ 1417.278835] ath11k_pci 0000:02:00.0: failed to receive control response completion, polling..
[ 1418.302829] ath11k_pci 0000:02:00.0: Service connect timeout
[ 1418.302833] ath11k_pci 0000:02:00.0: failed to connect to HTT: -110
[ 1418.303669] ath11k_pci 0000:02:00.0: failed to start core: -110
The detail is, if do affinity request after IRQ activated,
which is done in request_irq(), kernel caches that request and
returns success directly. Later when a subsequent MHI interrupt is
fired, kernel will do the real affinity setting work, as a result,
changs the MSI vector. However at that time host has configured
old vector to hardware, so host never receives CE or DP interrupts.
Fix it by setting affinity before registering MHI controller
where host is, for the first time, doing IRQ request.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-01160-QCAMSLSWPLZ-1
Fixes: 5b32b6dd9663 ("ath11k: Remove core PCI references from PCI common code")
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/20230907015606.16297-1-quic_bqiang@quicinc.com
2023-09-07 09:56:06 +08:00
|
|
|
goto err_irq_affinity_cleanup;
|
2020-08-13 12:04:24 +03:00
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:25 +03:00
|
|
|
ret = ath11k_hal_srng_init(ab);
|
|
|
|
if (ret)
|
|
|
|
goto err_mhi_unregister;
|
|
|
|
|
|
|
|
ret = ath11k_ce_alloc_pipes(ab);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret);
|
|
|
|
goto err_hal_srng_deinit;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath11k_pci_init_qmi_ce_config(ab);
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ret = ath11k_pcic_config_irq(ab);
|
2020-08-13 12:04:25 +03:00
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to config irq: %d\n", ret);
|
|
|
|
goto err_ce_free;
|
|
|
|
}
|
|
|
|
|
2021-11-19 15:36:26 +02:00
|
|
|
/* kernel may allocate a dummy vector before request_irq and
|
|
|
|
* then allocate a real vector when request_irq is called.
|
|
|
|
* So get msi_data here again to avoid spurious interrupt
|
|
|
|
* as msi_data will configured to srngs.
|
|
|
|
*/
|
|
|
|
ret = ath11k_pci_config_msi_data(ab_pci);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to config msi_data: %d\n", ret);
|
wifi: ath11k: fix boot failure with one MSI vector
Commit 5b32b6dd96633 ("ath11k: Remove core PCI references from
PCI common code") breaks with one MSI vector because it moves
affinity setting after IRQ request, see below log:
[ 1417.278835] ath11k_pci 0000:02:00.0: failed to receive control response completion, polling..
[ 1418.302829] ath11k_pci 0000:02:00.0: Service connect timeout
[ 1418.302833] ath11k_pci 0000:02:00.0: failed to connect to HTT: -110
[ 1418.303669] ath11k_pci 0000:02:00.0: failed to start core: -110
The detail is, if do affinity request after IRQ activated,
which is done in request_irq(), kernel caches that request and
returns success directly. Later when a subsequent MHI interrupt is
fired, kernel will do the real affinity setting work, as a result,
changs the MSI vector. However at that time host has configured
old vector to hardware, so host never receives CE or DP interrupts.
Fix it by setting affinity before registering MHI controller
where host is, for the first time, doing IRQ request.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-01160-QCAMSLSWPLZ-1
Fixes: 5b32b6dd9663 ("ath11k: Remove core PCI references from PCI common code")
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/20230907015606.16297-1-quic_bqiang@quicinc.com
2023-09-07 09:56:06 +08:00
|
|
|
goto err_free_irq;
|
2021-11-19 15:36:26 +02:00
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:25 +03:00
|
|
|
ret = ath11k_core_init(ab);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to init core: %d\n", ret);
|
wifi: ath11k: fix boot failure with one MSI vector
Commit 5b32b6dd96633 ("ath11k: Remove core PCI references from
PCI common code") breaks with one MSI vector because it moves
affinity setting after IRQ request, see below log:
[ 1417.278835] ath11k_pci 0000:02:00.0: failed to receive control response completion, polling..
[ 1418.302829] ath11k_pci 0000:02:00.0: Service connect timeout
[ 1418.302833] ath11k_pci 0000:02:00.0: failed to connect to HTT: -110
[ 1418.303669] ath11k_pci 0000:02:00.0: failed to start core: -110
The detail is, if do affinity request after IRQ activated,
which is done in request_irq(), kernel caches that request and
returns success directly. Later when a subsequent MHI interrupt is
fired, kernel will do the real affinity setting work, as a result,
changs the MSI vector. However at that time host has configured
old vector to hardware, so host never receives CE or DP interrupts.
Fix it by setting affinity before registering MHI controller
where host is, for the first time, doing IRQ request.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-01160-QCAMSLSWPLZ-1
Fixes: 5b32b6dd9663 ("ath11k: Remove core PCI references from PCI common code")
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/20230907015606.16297-1-quic_bqiang@quicinc.com
2023-09-07 09:56:06 +08:00
|
|
|
goto err_free_irq;
|
2020-08-13 12:04:25 +03:00
|
|
|
}
|
2023-07-26 19:40:31 +05:30
|
|
|
ath11k_qmi_fwreset_from_cold_boot(ab);
|
2020-08-13 12:04:20 +03:00
|
|
|
return 0;
|
2020-08-13 12:04:21 +03:00
|
|
|
|
2020-08-13 12:04:25 +03:00
|
|
|
err_free_irq:
|
2025-02-25 11:04:45 +05:30
|
|
|
/* __free_irq() expects the caller to have cleared the affinity hint */
|
|
|
|
ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_free_irq(ab);
|
2020-08-13 12:04:25 +03:00
|
|
|
|
|
|
|
err_ce_free:
|
|
|
|
ath11k_ce_free_pipes(ab);
|
|
|
|
|
|
|
|
err_hal_srng_deinit:
|
|
|
|
ath11k_hal_srng_deinit(ab);
|
|
|
|
|
|
|
|
err_mhi_unregister:
|
|
|
|
ath11k_mhi_unregister(ab_pci);
|
|
|
|
|
wifi: ath11k: fix boot failure with one MSI vector
Commit 5b32b6dd96633 ("ath11k: Remove core PCI references from
PCI common code") breaks with one MSI vector because it moves
affinity setting after IRQ request, see below log:
[ 1417.278835] ath11k_pci 0000:02:00.0: failed to receive control response completion, polling..
[ 1418.302829] ath11k_pci 0000:02:00.0: Service connect timeout
[ 1418.302833] ath11k_pci 0000:02:00.0: failed to connect to HTT: -110
[ 1418.303669] ath11k_pci 0000:02:00.0: failed to start core: -110
The detail is, if do affinity request after IRQ activated,
which is done in request_irq(), kernel caches that request and
returns success directly. Later when a subsequent MHI interrupt is
fired, kernel will do the real affinity setting work, as a result,
changs the MSI vector. However at that time host has configured
old vector to hardware, so host never receives CE or DP interrupts.
Fix it by setting affinity before registering MHI controller
where host is, for the first time, doing IRQ request.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23
Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-01160-QCAMSLSWPLZ-1
Fixes: 5b32b6dd9663 ("ath11k: Remove core PCI references from PCI common code")
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/20230907015606.16297-1-quic_bqiang@quicinc.com
2023-09-07 09:56:06 +08:00
|
|
|
err_irq_affinity_cleanup:
|
|
|
|
ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
|
|
|
|
|
2020-08-13 12:04:23 +03:00
|
|
|
err_pci_disable_msi:
|
2021-10-11 09:33:08 +03:00
|
|
|
ath11k_pci_free_msi(ab_pci);
|
2020-08-13 12:04:23 +03:00
|
|
|
|
2020-08-13 12:04:22 +03:00
|
|
|
err_pci_free_region:
|
|
|
|
ath11k_pci_free_region(ab_pci);
|
|
|
|
|
2020-08-13 12:04:21 +03:00
|
|
|
err_free_core:
|
|
|
|
ath11k_core_free(ab);
|
2020-08-13 12:04:22 +03:00
|
|
|
|
2020-08-13 12:04:21 +03:00
|
|
|
return ret;
|
2020-08-13 12:04:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_pci_remove(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = pci_get_drvdata(pdev);
|
2020-08-13 12:04:21 +03:00
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
2020-08-13 12:04:20 +03:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
|
2021-11-19 15:36:26 +02:00
|
|
|
|
2020-12-08 09:52:55 +02:00
|
|
|
if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
|
2025-03-28 13:32:27 +08:00
|
|
|
ath11k_pci_power_down(ab, false);
|
2020-12-08 09:52:55 +02:00
|
|
|
ath11k_debugfs_soc_destroy(ab);
|
|
|
|
ath11k_qmi_deinit_service(ab);
|
2025-03-28 13:32:28 +08:00
|
|
|
ath11k_core_pm_notifier_unregister(ab);
|
2020-12-08 09:52:55 +02:00
|
|
|
goto qmi_fail;
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:20 +03:00
|
|
|
set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags);
|
2020-09-30 13:51:10 +03:00
|
|
|
|
2024-08-13 09:30:28 +08:00
|
|
|
cancel_work_sync(&ab->reset_work);
|
|
|
|
cancel_work_sync(&ab->dump_work);
|
2020-09-30 13:51:10 +03:00
|
|
|
ath11k_core_deinit(ab);
|
|
|
|
|
2020-12-08 09:52:55 +02:00
|
|
|
qmi_fail:
|
2025-01-23 16:49:48 +08:00
|
|
|
ath11k_fw_destroy(ab);
|
2020-08-13 12:04:24 +03:00
|
|
|
ath11k_mhi_unregister(ab_pci);
|
2020-09-30 13:51:10 +03:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_free_irq(ab);
|
2021-10-11 09:33:08 +03:00
|
|
|
ath11k_pci_free_msi(ab_pci);
|
2020-08-13 12:04:21 +03:00
|
|
|
ath11k_pci_free_region(ab_pci);
|
2020-09-30 13:51:10 +03:00
|
|
|
|
|
|
|
ath11k_hal_srng_deinit(ab);
|
|
|
|
ath11k_ce_free_pipes(ab);
|
2020-08-13 12:04:20 +03:00
|
|
|
ath11k_core_free(ab);
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:24 +03:00
|
|
|
static void ath11k_pci_shutdown(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = pci_get_drvdata(pdev);
|
2022-05-23 16:32:58 +02:00
|
|
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
2020-08-13 12:04:24 +03:00
|
|
|
|
2022-05-23 16:32:58 +02:00
|
|
|
ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
|
2025-03-28 13:32:27 +08:00
|
|
|
ath11k_pci_power_down(ab, false);
|
2020-08-13 12:04:24 +03:00
|
|
|
}
|
|
|
|
|
2020-12-11 19:35:50 +02:00
|
|
|
static __maybe_unused int ath11k_pci_pm_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = dev_get_drvdata(dev);
|
|
|
|
int ret;
|
|
|
|
|
2022-01-27 11:01:16 +02:00
|
|
|
if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
|
|
|
|
ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot skipping pci suspend as qmi is not initialised\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-11 19:35:50 +02:00
|
|
|
ret = ath11k_core_suspend(ab);
|
|
|
|
if (ret)
|
|
|
|
ath11k_warn(ab, "failed to suspend core: %d\n", ret);
|
|
|
|
|
2023-02-01 12:32:01 -06:00
|
|
|
return 0;
|
2020-12-11 19:35:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static __maybe_unused int ath11k_pci_pm_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = dev_get_drvdata(dev);
|
|
|
|
int ret;
|
|
|
|
|
2022-01-27 11:01:16 +02:00
|
|
|
if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
|
|
|
|
ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot skipping pci resume as qmi is not initialised\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-11 19:35:50 +02:00
|
|
|
ret = ath11k_core_resume(ab);
|
|
|
|
if (ret)
|
|
|
|
ath11k_warn(ab, "failed to resume core: %d\n", ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2025-03-28 13:32:27 +08:00
|
|
|
static __maybe_unused int ath11k_pci_pm_suspend_late(struct device *dev)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = dev_get_drvdata(dev);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ath11k_core_suspend_late(ab);
|
|
|
|
if (ret)
|
|
|
|
ath11k_warn(ab, "failed to late suspend core: %d\n", ret);
|
|
|
|
|
|
|
|
/* Similar to ath11k_pci_pm_suspend(), we return success here
|
|
|
|
* even error happens, to allow system suspend/hibernation survive.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static __maybe_unused int ath11k_pci_pm_resume_early(struct device *dev)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = dev_get_drvdata(dev);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ath11k_core_resume_early(ab);
|
|
|
|
if (ret)
|
|
|
|
ath11k_warn(ab, "failed to early resume core: %d\n", ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dev_pm_ops __maybe_unused ath11k_pci_pm_ops = {
|
|
|
|
SET_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend,
|
|
|
|
ath11k_pci_pm_resume)
|
|
|
|
SET_LATE_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend_late,
|
|
|
|
ath11k_pci_pm_resume_early)
|
|
|
|
};
|
2020-12-11 19:35:50 +02:00
|
|
|
|
2020-08-13 12:04:20 +03:00
|
|
|
static struct pci_driver ath11k_pci_driver = {
|
|
|
|
.name = "ath11k_pci",
|
|
|
|
.id_table = ath11k_pci_id_table,
|
|
|
|
.probe = ath11k_pci_probe,
|
|
|
|
.remove = ath11k_pci_remove,
|
2020-08-13 12:04:24 +03:00
|
|
|
.shutdown = ath11k_pci_shutdown,
|
2020-12-11 19:35:50 +02:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
.driver.pm = &ath11k_pci_pm_ops,
|
|
|
|
#endif
|
2020-08-13 12:04:20 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static int ath11k_pci_init(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = pci_register_driver(&ath11k_pci_driver);
|
|
|
|
if (ret)
|
|
|
|
pr_err("failed to register ath11k pci driver: %d\n",
|
|
|
|
ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
module_init(ath11k_pci_init);
|
|
|
|
|
|
|
|
static void ath11k_pci_exit(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&ath11k_pci_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_exit(ath11k_pci_exit);
|
|
|
|
|
2023-08-08 20:44:48 -04:00
|
|
|
MODULE_DESCRIPTION("Driver support for Qualcomm Technologies PCIe 802.11ax WLAN devices");
|
2020-08-13 12:04:20 +03:00
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
2020-12-07 18:16:55 +02:00
|
|
|
|
2023-03-30 16:37:18 +02:00
|
|
|
/* firmware files */
|
|
|
|
MODULE_FIRMWARE(ATH11K_FW_DIR "/QCA6390/hw2.0/*");
|
|
|
|
MODULE_FIRMWARE(ATH11K_FW_DIR "/QCN9074/hw1.0/*");
|
|
|
|
MODULE_FIRMWARE(ATH11K_FW_DIR "/WCN6855/hw2.0/*");
|
|
|
|
MODULE_FIRMWARE(ATH11K_FW_DIR "/WCN6855/hw2.1/*");
|