2020-08-13 12:04:24 +03:00
|
|
|
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
2022-04-01 14:53:08 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
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
|
|
|
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
2022-04-01 14:53:08 +03:00
|
|
|
*/
|
2020-08-13 12:04:24 +03:00
|
|
|
|
|
|
|
#include <linux/msi.h>
|
|
|
|
#include <linux/pci.h>
|
2023-10-18 11:37:06 +03:00
|
|
|
#include <linux/firmware.h>
|
2021-12-14 17:39:43 +02:00
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_address.h>
|
|
|
|
#include <linux/ioport.h>
|
2020-08-13 12:04:24 +03:00
|
|
|
|
|
|
|
#include "core.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "mhi.h"
|
2021-02-16 09:16:08 +02:00
|
|
|
#include "pci.h"
|
2022-04-01 14:53:08 +03:00
|
|
|
#include "pcic.h"
|
2020-08-13 12:04:24 +03:00
|
|
|
|
2023-03-29 19:20:38 +03:00
|
|
|
#define MHI_TIMEOUT_DEFAULT_MS 20000
|
2022-02-14 19:53:16 +02:00
|
|
|
#define RDDM_DUMP_SIZE 0x420000
|
2020-08-13 12:04:24 +03:00
|
|
|
|
2021-02-16 09:16:08 +02:00
|
|
|
static struct mhi_channel_config ath11k_mhi_channels_qca6390[] = {
|
2020-08-13 12:04:24 +03:00
|
|
|
{
|
|
|
|
.num = 20,
|
|
|
|
.name = "IPCR",
|
|
|
|
.num_elements = 64,
|
|
|
|
.event_ring = 1,
|
|
|
|
.dir = DMA_TO_DEVICE,
|
|
|
|
.ee_mask = 0x4,
|
|
|
|
.pollcfg = 0,
|
|
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
|
|
.lpm_notify = false,
|
|
|
|
.offload_channel = false,
|
|
|
|
.doorbell_mode_switch = false,
|
|
|
|
.auto_queue = false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.num = 21,
|
|
|
|
.name = "IPCR",
|
|
|
|
.num_elements = 64,
|
|
|
|
.event_ring = 1,
|
|
|
|
.dir = DMA_FROM_DEVICE,
|
|
|
|
.ee_mask = 0x4,
|
|
|
|
.pollcfg = 0,
|
|
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
|
|
.lpm_notify = false,
|
|
|
|
.offload_channel = false,
|
|
|
|
.doorbell_mode_switch = false,
|
|
|
|
.auto_queue = true,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-02-16 09:16:08 +02:00
|
|
|
static struct mhi_event_config ath11k_mhi_events_qca6390[] = {
|
2020-08-13 12:04:24 +03:00
|
|
|
{
|
|
|
|
.num_elements = 32,
|
|
|
|
.irq_moderation_ms = 0,
|
|
|
|
.irq = 1,
|
|
|
|
.mode = MHI_DB_BRST_DISABLE,
|
|
|
|
.data_type = MHI_ER_CTRL,
|
|
|
|
.hardware_event = false,
|
|
|
|
.client_managed = false,
|
|
|
|
.offload_channel = false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.num_elements = 256,
|
|
|
|
.irq_moderation_ms = 1,
|
|
|
|
.irq = 2,
|
|
|
|
.mode = MHI_DB_BRST_DISABLE,
|
|
|
|
.priority = 1,
|
|
|
|
.hardware_event = false,
|
|
|
|
.client_managed = false,
|
|
|
|
.offload_channel = false,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-02-16 09:16:08 +02:00
|
|
|
static struct mhi_controller_config ath11k_mhi_config_qca6390 = {
|
2020-08-13 12:04:24 +03:00
|
|
|
.max_channels = 128,
|
|
|
|
.timeout_ms = 2000,
|
|
|
|
.use_bounce_buf = false,
|
wifi: ath11k: decrease MHI channel buffer length to 8KB
Currently buf_len field of ath11k_mhi_config_qca6390 is assigned
with 0, making MHI use a default size, 64KB, to allocate channel
buffers. This is likely to fail in some scenarios where system
memory is highly fragmented and memory compaction or reclaim is
not allowed.
There is a fail report which is caused by it:
kworker/u32:45: page allocation failure: order:4, mode:0x40c00(GFP_NOIO|__GFP_COMP), nodemask=(null),cpuset=/,mems_allowed=0
CPU: 0 PID: 19318 Comm: kworker/u32:45 Not tainted 6.8.0-rc3-1.gae4495f-default #1 openSUSE Tumbleweed (unreleased) 493b6d5b382c603654d7a81fc3c144d59a1dfceb
Workqueue: events_unbound async_run_entry_fn
Call Trace:
<TASK>
dump_stack_lvl+0x47/0x60
warn_alloc+0x13a/0x1b0
? srso_alias_return_thunk+0x5/0xfbef5
? __alloc_pages_direct_compact+0xab/0x210
__alloc_pages_slowpath.constprop.0+0xd3e/0xda0
__alloc_pages+0x32d/0x350
? mhi_prepare_channel+0x127/0x2d0 [mhi 40df44e07c05479f7a6e7b90fba9f0e0031a7814]
__kmalloc_large_node+0x72/0x110
__kmalloc+0x37c/0x480
? mhi_map_single_no_bb+0x77/0xf0 [mhi 40df44e07c05479f7a6e7b90fba9f0e0031a7814]
? mhi_prepare_channel+0x127/0x2d0 [mhi 40df44e07c05479f7a6e7b90fba9f0e0031a7814]
mhi_prepare_channel+0x127/0x2d0 [mhi 40df44e07c05479f7a6e7b90fba9f0e0031a7814]
__mhi_prepare_for_transfer+0x44/0x80 [mhi 40df44e07c05479f7a6e7b90fba9f0e0031a7814]
? __pfx_____mhi_prepare_for_transfer+0x10/0x10 [mhi 40df44e07c05479f7a6e7b90fba9f0e0031a7814]
device_for_each_child+0x5c/0xa0
? __pfx_pci_pm_resume+0x10/0x10
ath11k_core_resume+0x65/0x100 [ath11k a5094e22d7223135c40d93c8f5321cf09fd85e4e]
? srso_alias_return_thunk+0x5/0xfbef5
ath11k_pci_pm_resume+0x32/0x60 [ath11k_pci 830b7bfc3ea80ebef32e563cafe2cb55e9cc73ec]
? srso_alias_return_thunk+0x5/0xfbef5
dpm_run_callback+0x8c/0x1e0
device_resume+0x104/0x340
? __pfx_dpm_watchdog_handler+0x10/0x10
async_resume+0x1d/0x30
async_run_entry_fn+0x32/0x120
process_one_work+0x168/0x330
worker_thread+0x2f5/0x410
? __pfx_worker_thread+0x10/0x10
kthread+0xe8/0x120
? __pfx_kthread+0x10/0x10
ret_from_fork+0x34/0x50
? __pfx_kthread+0x10/0x10
ret_from_fork_asm+0x1b/0x30
</TASK>
Actually those buffers are used only by QMI target -> host communication.
And for WCN6855 and QCA6390, the largest packet size for that is less
than 6KB. So change buf_len field to 8KB, which results in order 1
allocation if page size is 4KB. In this way, we can at least save some
memory, and as well as decrease the possibility of allocation failure
in those scenarios.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30
Reported-by: Vlastimil Babka <vbabka@suse.cz>
Closes: https://lore.kernel.org/ath11k/96481a45-3547-4d23-ad34-3a8f1d90c1cd@suse.cz/
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/20240223053111.29170-1-quic_bqiang@quicinc.com
2024-02-23 13:31:11 +08:00
|
|
|
.buf_len = 8192,
|
2021-02-16 09:16:08 +02:00
|
|
|
.num_channels = ARRAY_SIZE(ath11k_mhi_channels_qca6390),
|
|
|
|
.ch_cfg = ath11k_mhi_channels_qca6390,
|
|
|
|
.num_events = ARRAY_SIZE(ath11k_mhi_events_qca6390),
|
|
|
|
.event_cfg = ath11k_mhi_events_qca6390,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct mhi_channel_config ath11k_mhi_channels_qcn9074[] = {
|
|
|
|
{
|
|
|
|
.num = 20,
|
|
|
|
.name = "IPCR",
|
|
|
|
.num_elements = 32,
|
|
|
|
.event_ring = 1,
|
|
|
|
.dir = DMA_TO_DEVICE,
|
|
|
|
.ee_mask = 0x14,
|
|
|
|
.pollcfg = 0,
|
|
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
|
|
.lpm_notify = false,
|
|
|
|
.offload_channel = false,
|
|
|
|
.doorbell_mode_switch = false,
|
|
|
|
.auto_queue = false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.num = 21,
|
|
|
|
.name = "IPCR",
|
|
|
|
.num_elements = 32,
|
|
|
|
.event_ring = 1,
|
|
|
|
.dir = DMA_FROM_DEVICE,
|
|
|
|
.ee_mask = 0x14,
|
|
|
|
.pollcfg = 0,
|
|
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
|
|
.lpm_notify = false,
|
|
|
|
.offload_channel = false,
|
|
|
|
.doorbell_mode_switch = false,
|
|
|
|
.auto_queue = true,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct mhi_event_config ath11k_mhi_events_qcn9074[] = {
|
|
|
|
{
|
|
|
|
.num_elements = 32,
|
|
|
|
.irq_moderation_ms = 0,
|
|
|
|
.irq = 1,
|
|
|
|
.data_type = MHI_ER_CTRL,
|
|
|
|
.mode = MHI_DB_BRST_DISABLE,
|
|
|
|
.hardware_event = false,
|
|
|
|
.client_managed = false,
|
|
|
|
.offload_channel = false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.num_elements = 256,
|
|
|
|
.irq_moderation_ms = 1,
|
|
|
|
.irq = 2,
|
|
|
|
.mode = MHI_DB_BRST_DISABLE,
|
|
|
|
.priority = 1,
|
|
|
|
.hardware_event = false,
|
|
|
|
.client_managed = false,
|
|
|
|
.offload_channel = false,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct mhi_controller_config ath11k_mhi_config_qcn9074 = {
|
|
|
|
.max_channels = 30,
|
|
|
|
.timeout_ms = 10000,
|
|
|
|
.use_bounce_buf = false,
|
|
|
|
.buf_len = 0,
|
|
|
|
.num_channels = ARRAY_SIZE(ath11k_mhi_channels_qcn9074),
|
|
|
|
.ch_cfg = ath11k_mhi_channels_qcn9074,
|
|
|
|
.num_events = ARRAY_SIZE(ath11k_mhi_events_qcn9074),
|
|
|
|
.event_cfg = ath11k_mhi_events_qcn9074,
|
2020-08-13 12:04:24 +03:00
|
|
|
};
|
|
|
|
|
2020-08-17 13:31:55 +03:00
|
|
|
void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
val = ath11k_pcic_read32(ab, MHISTATUS);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
2023-06-09 17:24:34 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "mhistatus 0x%x\n", val);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
|
|
|
/* Observed on QCA6390 that after SOC_GLOBAL_RESET, MHISTATUS
|
|
|
|
* has SYSERR bit set and thus need to set MHICTRL_RESET
|
|
|
|
* to clear SYSERR.
|
|
|
|
*/
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, MHICTRL, MHICTRL_RESET_MASK);
|
2020-08-17 13:31:55 +03:00
|
|
|
|
|
|
|
mdelay(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_mhi_reset_txvecdb(struct ath11k_base *ab)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, PCIE_TXVECDB, 0);
|
2020-08-17 13:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_mhi_reset_txvecstatus(struct ath11k_base *ab)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, PCIE_TXVECSTATUS, 0);
|
2020-08-17 13:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_mhi_reset_rxvecdb(struct ath11k_base *ab)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, PCIE_RXVECDB, 0);
|
2020-08-17 13:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_mhi_reset_rxvecstatus(struct ath11k_base *ab)
|
|
|
|
{
|
2022-04-01 14:53:08 +03:00
|
|
|
ath11k_pcic_write32(ab, PCIE_RXVECSTATUS, 0);
|
2020-08-17 13:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ath11k_mhi_clear_vector(struct ath11k_base *ab)
|
|
|
|
{
|
|
|
|
ath11k_mhi_reset_txvecdb(ab);
|
|
|
|
ath11k_mhi_reset_txvecstatus(ab);
|
|
|
|
ath11k_mhi_reset_rxvecdb(ab);
|
|
|
|
ath11k_mhi_reset_rxvecstatus(ab);
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:24 +03:00
|
|
|
static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
|
|
|
u32 user_base_data, base_vector;
|
|
|
|
int ret, num_vectors, i;
|
|
|
|
int *irq;
|
2021-11-19 15:36:26 +02:00
|
|
|
unsigned int msi_data;
|
2020-08-13 12:04:24 +03:00
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
ret = ath11k_pcic_get_user_msi_assignment(ab, "MHI", &num_vectors,
|
2022-04-01 14:53:08 +03:00
|
|
|
&user_base_data, &base_vector);
|
2020-08-13 12:04:24 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2023-06-09 17:24:34 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "num_vectors %d base_vector %d\n",
|
2020-08-13 12:04:24 +03:00
|
|
|
num_vectors, base_vector);
|
|
|
|
|
|
|
|
irq = kcalloc(num_vectors, sizeof(int), GFP_KERNEL);
|
|
|
|
if (!irq)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2021-11-19 15:36:26 +02:00
|
|
|
for (i = 0; i < num_vectors; i++) {
|
|
|
|
msi_data = base_vector;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
|
2021-11-19 15:36:26 +02:00
|
|
|
msi_data += i;
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
irq[i] = ath11k_pci_get_msi_irq(ab, msi_data);
|
2021-11-19 15:36:26 +02:00
|
|
|
}
|
2020-08-13 12:04:24 +03:00
|
|
|
|
|
|
|
ab_pci->mhi_ctrl->irq = irq;
|
|
|
|
ab_pci->mhi_ctrl->nr_irqs = num_vectors;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath11k_mhi_op_runtime_get(struct mhi_controller *mhi_cntrl)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_mhi_op_runtime_put(struct mhi_controller *mhi_cntrl)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
ath11k: add support for device recovery for QCA6390/WCN6855
Currently ath11k has device recovery logic, it is introduced by this
patch "ath11k: Add support for subsystem recovery" which is upstream
by https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/commit/?h=ath11k-bringup&id=3a7b4838b6f6f234239f263ef3dc02e612a083ad.
The patch is for AHB devices such as IPQ8074, it has remote proc module
which is used to download the firmware and boots the processor which
firmware is running on. If firmware crashed, remote proc module will
detect it and download and boot firmware again. Below command will
trigger a firmware crash, and then user can test feature of device
recovery.
Test command:
echo assert > /sys/kernel/debug/ath11k/qca6390\ hw2.0/simulate_fw_crash
echo assert > /sys/kernel/debug/ath11k/wcn6855\ hw2.0/simulate_fw_crash
Unfortunately, QCA6390 is PCIe bus, it does not have the remote proc
module, it use mhi module to communicate between firmware and ath11k.
So ath11k does not support device recovery for QCA6390 currently.
This patch is to add the extra logic which is different for QCA6390.
When firmware crashed, MHI_CB_EE_RDDM event will be indicate by
firmware and then ath11k_mhi_op_status_cb which is the callback of
mhi_controller will receive the MHI_CB_EE_RDDM event, then ath11k
will start to do recovery process, ath11k_core_reset() calls
ath11k_hif_power_down()/ath11k_hif_power_up(), then the mhi/ath11k
will start to download and boot firmware. There are some logic to
avoid deadloop recovery and two simultaneous recovery operations.
And because it has muti-radios for the soc, so it add some logic
in ath11k_mac_op_reconfig_complete() to make sure all radios has
reconfig complete and then complete the device recovery.
Also it add workqueue_aux, because ab->workqueue is used when receive
ATH11K_QMI_EVENT_FW_READY in recovery process(queue_work(ab->workqueue,
&ab->restart_work)), and ath11k_core_reset will wait for max
ATH11K_RESET_TIMEOUT_HZ for the previous restart_work finished, if
ath11k_core_reset also queued in ab->workqueue, then it will delay
restart_work of previous recovery and lead previous recovery fail.
ath11k recovery success for QCA6390/WCN6855 after apply this patch.
Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2
Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220228064606.8981-2-quic_wgong@quicinc.com
2022-02-28 01:46:03 -05:00
|
|
|
static char *ath11k_mhi_op_callback_to_str(enum mhi_callback reason)
|
|
|
|
{
|
|
|
|
switch (reason) {
|
|
|
|
case MHI_CB_IDLE:
|
|
|
|
return "MHI_CB_IDLE";
|
|
|
|
case MHI_CB_PENDING_DATA:
|
|
|
|
return "MHI_CB_PENDING_DATA";
|
|
|
|
case MHI_CB_LPM_ENTER:
|
|
|
|
return "MHI_CB_LPM_ENTER";
|
|
|
|
case MHI_CB_LPM_EXIT:
|
|
|
|
return "MHI_CB_LPM_EXIT";
|
|
|
|
case MHI_CB_EE_RDDM:
|
|
|
|
return "MHI_CB_EE_RDDM";
|
|
|
|
case MHI_CB_EE_MISSION_MODE:
|
|
|
|
return "MHI_CB_EE_MISSION_MODE";
|
|
|
|
case MHI_CB_SYS_ERROR:
|
|
|
|
return "MHI_CB_SYS_ERROR";
|
|
|
|
case MHI_CB_FATAL_ERROR:
|
|
|
|
return "MHI_CB_FATAL_ERROR";
|
|
|
|
case MHI_CB_BW_REQ:
|
|
|
|
return "MHI_CB_BW_REQ";
|
|
|
|
default:
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-08-13 12:04:24 +03:00
|
|
|
static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
|
|
|
|
enum mhi_callback cb)
|
|
|
|
{
|
2020-12-10 16:05:19 +02:00
|
|
|
struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev);
|
|
|
|
|
2023-06-09 17:24:28 +03:00
|
|
|
ath11k_dbg(ab, ATH11K_DBG_BOOT, "notify status reason %s\n",
|
ath11k: add support for device recovery for QCA6390/WCN6855
Currently ath11k has device recovery logic, it is introduced by this
patch "ath11k: Add support for subsystem recovery" which is upstream
by https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/commit/?h=ath11k-bringup&id=3a7b4838b6f6f234239f263ef3dc02e612a083ad.
The patch is for AHB devices such as IPQ8074, it has remote proc module
which is used to download the firmware and boots the processor which
firmware is running on. If firmware crashed, remote proc module will
detect it and download and boot firmware again. Below command will
trigger a firmware crash, and then user can test feature of device
recovery.
Test command:
echo assert > /sys/kernel/debug/ath11k/qca6390\ hw2.0/simulate_fw_crash
echo assert > /sys/kernel/debug/ath11k/wcn6855\ hw2.0/simulate_fw_crash
Unfortunately, QCA6390 is PCIe bus, it does not have the remote proc
module, it use mhi module to communicate between firmware and ath11k.
So ath11k does not support device recovery for QCA6390 currently.
This patch is to add the extra logic which is different for QCA6390.
When firmware crashed, MHI_CB_EE_RDDM event will be indicate by
firmware and then ath11k_mhi_op_status_cb which is the callback of
mhi_controller will receive the MHI_CB_EE_RDDM event, then ath11k
will start to do recovery process, ath11k_core_reset() calls
ath11k_hif_power_down()/ath11k_hif_power_up(), then the mhi/ath11k
will start to download and boot firmware. There are some logic to
avoid deadloop recovery and two simultaneous recovery operations.
And because it has muti-radios for the soc, so it add some logic
in ath11k_mac_op_reconfig_complete() to make sure all radios has
reconfig complete and then complete the device recovery.
Also it add workqueue_aux, because ab->workqueue is used when receive
ATH11K_QMI_EVENT_FW_READY in recovery process(queue_work(ab->workqueue,
&ab->restart_work)), and ath11k_core_reset will wait for max
ATH11K_RESET_TIMEOUT_HZ for the previous restart_work finished, if
ath11k_core_reset also queued in ab->workqueue, then it will delay
restart_work of previous recovery and lead previous recovery fail.
ath11k recovery success for QCA6390/WCN6855 after apply this patch.
Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2
Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220228064606.8981-2-quic_wgong@quicinc.com
2022-02-28 01:46:03 -05:00
|
|
|
ath11k_mhi_op_callback_to_str(cb));
|
|
|
|
|
2020-12-10 16:05:19 +02:00
|
|
|
switch (cb) {
|
|
|
|
case MHI_CB_SYS_ERROR:
|
|
|
|
ath11k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n");
|
|
|
|
break;
|
ath11k: add support for device recovery for QCA6390/WCN6855
Currently ath11k has device recovery logic, it is introduced by this
patch "ath11k: Add support for subsystem recovery" which is upstream
by https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/commit/?h=ath11k-bringup&id=3a7b4838b6f6f234239f263ef3dc02e612a083ad.
The patch is for AHB devices such as IPQ8074, it has remote proc module
which is used to download the firmware and boots the processor which
firmware is running on. If firmware crashed, remote proc module will
detect it and download and boot firmware again. Below command will
trigger a firmware crash, and then user can test feature of device
recovery.
Test command:
echo assert > /sys/kernel/debug/ath11k/qca6390\ hw2.0/simulate_fw_crash
echo assert > /sys/kernel/debug/ath11k/wcn6855\ hw2.0/simulate_fw_crash
Unfortunately, QCA6390 is PCIe bus, it does not have the remote proc
module, it use mhi module to communicate between firmware and ath11k.
So ath11k does not support device recovery for QCA6390 currently.
This patch is to add the extra logic which is different for QCA6390.
When firmware crashed, MHI_CB_EE_RDDM event will be indicate by
firmware and then ath11k_mhi_op_status_cb which is the callback of
mhi_controller will receive the MHI_CB_EE_RDDM event, then ath11k
will start to do recovery process, ath11k_core_reset() calls
ath11k_hif_power_down()/ath11k_hif_power_up(), then the mhi/ath11k
will start to download and boot firmware. There are some logic to
avoid deadloop recovery and two simultaneous recovery operations.
And because it has muti-radios for the soc, so it add some logic
in ath11k_mac_op_reconfig_complete() to make sure all radios has
reconfig complete and then complete the device recovery.
Also it add workqueue_aux, because ab->workqueue is used when receive
ATH11K_QMI_EVENT_FW_READY in recovery process(queue_work(ab->workqueue,
&ab->restart_work)), and ath11k_core_reset will wait for max
ATH11K_RESET_TIMEOUT_HZ for the previous restart_work finished, if
ath11k_core_reset also queued in ab->workqueue, then it will delay
restart_work of previous recovery and lead previous recovery fail.
ath11k recovery success for QCA6390/WCN6855 after apply this patch.
Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2
Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220228064606.8981-2-quic_wgong@quicinc.com
2022-02-28 01:46:03 -05:00
|
|
|
case MHI_CB_EE_RDDM:
|
2023-08-26 08:42:42 +03:00
|
|
|
ath11k_warn(ab, "firmware crashed: MHI_CB_EE_RDDM\n");
|
ath11k: add support for device recovery for QCA6390/WCN6855
Currently ath11k has device recovery logic, it is introduced by this
patch "ath11k: Add support for subsystem recovery" which is upstream
by https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/commit/?h=ath11k-bringup&id=3a7b4838b6f6f234239f263ef3dc02e612a083ad.
The patch is for AHB devices such as IPQ8074, it has remote proc module
which is used to download the firmware and boots the processor which
firmware is running on. If firmware crashed, remote proc module will
detect it and download and boot firmware again. Below command will
trigger a firmware crash, and then user can test feature of device
recovery.
Test command:
echo assert > /sys/kernel/debug/ath11k/qca6390\ hw2.0/simulate_fw_crash
echo assert > /sys/kernel/debug/ath11k/wcn6855\ hw2.0/simulate_fw_crash
Unfortunately, QCA6390 is PCIe bus, it does not have the remote proc
module, it use mhi module to communicate between firmware and ath11k.
So ath11k does not support device recovery for QCA6390 currently.
This patch is to add the extra logic which is different for QCA6390.
When firmware crashed, MHI_CB_EE_RDDM event will be indicate by
firmware and then ath11k_mhi_op_status_cb which is the callback of
mhi_controller will receive the MHI_CB_EE_RDDM event, then ath11k
will start to do recovery process, ath11k_core_reset() calls
ath11k_hif_power_down()/ath11k_hif_power_up(), then the mhi/ath11k
will start to download and boot firmware. There are some logic to
avoid deadloop recovery and two simultaneous recovery operations.
And because it has muti-radios for the soc, so it add some logic
in ath11k_mac_op_reconfig_complete() to make sure all radios has
reconfig complete and then complete the device recovery.
Also it add workqueue_aux, because ab->workqueue is used when receive
ATH11K_QMI_EVENT_FW_READY in recovery process(queue_work(ab->workqueue,
&ab->restart_work)), and ath11k_core_reset will wait for max
ATH11K_RESET_TIMEOUT_HZ for the previous restart_work finished, if
ath11k_core_reset also queued in ab->workqueue, then it will delay
restart_work of previous recovery and lead previous recovery fail.
ath11k recovery success for QCA6390/WCN6855 after apply this patch.
Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03003-QCAHSPSWPL_V1_V2_SILICONZ_LITE-2
Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220228064606.8981-2-quic_wgong@quicinc.com
2022-02-28 01:46:03 -05:00
|
|
|
if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)))
|
|
|
|
queue_work(ab->workqueue_aux, &ab->reset_work);
|
|
|
|
break;
|
2020-12-10 16:05:19 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2020-08-13 12:04:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ath11k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl,
|
|
|
|
void __iomem *addr,
|
|
|
|
u32 *out)
|
|
|
|
{
|
|
|
|
*out = readl(addr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath11k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl,
|
|
|
|
void __iomem *addr,
|
|
|
|
u32 val)
|
|
|
|
{
|
|
|
|
writel(val, addr);
|
|
|
|
}
|
|
|
|
|
2021-12-14 17:39:43 +02:00
|
|
|
static int ath11k_mhi_read_addr_from_dt(struct mhi_controller *mhi_ctrl)
|
|
|
|
{
|
|
|
|
struct device_node *np;
|
|
|
|
struct resource res;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
np = of_find_node_by_type(NULL, "memory");
|
|
|
|
if (!np)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
ret = of_address_to_resource(np, 0, &res);
|
2022-01-10 16:24:13 +02:00
|
|
|
of_node_put(np);
|
2021-12-14 17:39:43 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
mhi_ctrl->iova_start = res.start + 0x1000000;
|
|
|
|
mhi_ctrl->iova_stop = res.end;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:24 +03:00
|
|
|
int ath11k_mhi_register(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
|
|
|
struct mhi_controller *mhi_ctrl;
|
2021-02-16 09:16:08 +02:00
|
|
|
struct mhi_controller_config *ath11k_mhi_config;
|
2020-08-13 12:04:24 +03:00
|
|
|
int ret;
|
|
|
|
|
2020-11-17 09:33:56 -08:00
|
|
|
mhi_ctrl = mhi_alloc_controller();
|
2020-08-13 12:04:24 +03:00
|
|
|
if (!mhi_ctrl)
|
2020-08-26 14:34:17 +03:00
|
|
|
return -ENOMEM;
|
2020-08-13 12:04:24 +03:00
|
|
|
|
|
|
|
ab_pci->mhi_ctrl = mhi_ctrl;
|
|
|
|
mhi_ctrl->cntrl_dev = ab->dev;
|
|
|
|
mhi_ctrl->regs = ab->mem;
|
2021-08-02 10:42:51 +05:30
|
|
|
mhi_ctrl->reg_len = ab->mem_len;
|
2020-08-13 12:04:24 +03:00
|
|
|
|
2023-10-18 11:37:06 +03:00
|
|
|
if (ab->fw.amss_data && ab->fw.amss_len > 0) {
|
|
|
|
/* use MHI firmware file from firmware-N.bin */
|
|
|
|
mhi_ctrl->fw_data = ab->fw.amss_data;
|
|
|
|
mhi_ctrl->fw_sz = ab->fw.amss_len;
|
|
|
|
} else {
|
|
|
|
/* use the old separate mhi.bin MHI firmware file */
|
|
|
|
ath11k_core_create_firmware_path(ab, ATH11K_AMSS_FILE,
|
|
|
|
ab_pci->amss_path,
|
|
|
|
sizeof(ab_pci->amss_path));
|
|
|
|
mhi_ctrl->fw_image = ab_pci->amss_path;
|
|
|
|
}
|
|
|
|
|
2020-08-13 12:04:24 +03:00
|
|
|
ret = ath11k_mhi_get_msi(ab_pci);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to get msi for mhi\n");
|
2022-09-07 15:37:04 +08:00
|
|
|
goto free_controller;
|
2020-08-13 12:04:24 +03:00
|
|
|
}
|
|
|
|
|
2022-04-01 14:53:08 +03:00
|
|
|
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
|
2021-11-19 15:36:26 +02:00
|
|
|
mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING;
|
|
|
|
|
2021-12-14 17:39:43 +02:00
|
|
|
if (test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) {
|
|
|
|
ret = ath11k_mhi_read_addr_from_dt(mhi_ctrl);
|
|
|
|
if (ret < 0)
|
2022-09-07 15:37:04 +08:00
|
|
|
goto free_controller;
|
2021-12-14 17:39:43 +02:00
|
|
|
} else {
|
|
|
|
mhi_ctrl->iova_start = 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
|
|
|
mhi_ctrl->iova_stop = ab_pci->dma_mask;
|
2021-12-14 17:39:43 +02:00
|
|
|
}
|
|
|
|
|
2022-02-14 19:53:16 +02:00
|
|
|
mhi_ctrl->rddm_size = RDDM_DUMP_SIZE;
|
2020-08-13 12:04:24 +03:00
|
|
|
mhi_ctrl->sbl_size = SZ_512K;
|
|
|
|
mhi_ctrl->seg_len = SZ_512K;
|
|
|
|
mhi_ctrl->fbc_download = true;
|
|
|
|
mhi_ctrl->runtime_get = ath11k_mhi_op_runtime_get;
|
|
|
|
mhi_ctrl->runtime_put = ath11k_mhi_op_runtime_put;
|
|
|
|
mhi_ctrl->status_cb = ath11k_mhi_op_status_cb;
|
|
|
|
mhi_ctrl->read_reg = ath11k_mhi_op_read_reg;
|
|
|
|
mhi_ctrl->write_reg = ath11k_mhi_op_write_reg;
|
|
|
|
|
2021-04-08 10:27:10 +05:30
|
|
|
switch (ab->hw_rev) {
|
|
|
|
case ATH11K_HW_QCN9074_HW10:
|
2021-02-16 09:16:08 +02:00
|
|
|
ath11k_mhi_config = &ath11k_mhi_config_qcn9074;
|
2021-04-08 10:27:10 +05:30
|
|
|
break;
|
|
|
|
case ATH11K_HW_QCA6390_HW20:
|
2021-05-31 17:41:28 +03:00
|
|
|
case ATH11K_HW_WCN6855_HW20:
|
2021-11-29 10:56:12 +08:00
|
|
|
case ATH11K_HW_WCN6855_HW21:
|
2024-01-09 10:13:36 +08:00
|
|
|
case ATH11K_HW_QCA2066_HW21:
|
2021-04-08 10:27:10 +05:30
|
|
|
ath11k_mhi_config = &ath11k_mhi_config_qca6390;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ath11k_err(ab, "failed assign mhi_config for unknown hw rev %d\n",
|
|
|
|
ab->hw_rev);
|
2022-09-07 15:37:04 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto free_controller;
|
2021-04-08 10:27:10 +05:30
|
|
|
}
|
2021-02-16 09:16:08 +02:00
|
|
|
|
|
|
|
ret = mhi_register_controller(mhi_ctrl, ath11k_mhi_config);
|
2020-08-13 12:04:24 +03:00
|
|
|
if (ret) {
|
|
|
|
ath11k_err(ab, "failed to register to mhi bus, err = %d\n", ret);
|
2022-09-07 15:37:04 +08:00
|
|
|
goto free_controller;
|
2020-08-13 12:04:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2022-09-07 15:37:04 +08:00
|
|
|
|
|
|
|
free_controller:
|
|
|
|
mhi_free_controller(mhi_ctrl);
|
|
|
|
ab_pci->mhi_ctrl = NULL;
|
|
|
|
return ret;
|
2020-08-13 12:04:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ath11k_mhi_unregister(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
|
|
|
struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl;
|
|
|
|
|
|
|
|
mhi_unregister_controller(mhi_ctrl);
|
|
|
|
kfree(mhi_ctrl->irq);
|
2020-11-17 09:33:56 -08:00
|
|
|
mhi_free_controller(mhi_ctrl);
|
2020-08-13 12:04:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int ath11k_mhi_start(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
2022-04-05 11:26:44 +03:00
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
2020-08-13 12:04:24 +03:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ab_pci->mhi_ctrl->timeout_ms = MHI_TIMEOUT_DEFAULT_MS;
|
|
|
|
|
2022-04-01 20:30:40 +03:00
|
|
|
ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl);
|
2022-04-05 11:26:44 +03:00
|
|
|
if (ret) {
|
|
|
|
ath11k_warn(ab, "failed to prepare mhi: %d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2020-08-13 12:04:24 +03:00
|
|
|
|
2022-04-01 20:30:40 +03:00
|
|
|
ret = mhi_sync_power_up(ab_pci->mhi_ctrl);
|
2022-04-05 11:26:44 +03:00
|
|
|
if (ret) {
|
|
|
|
ath11k_warn(ab, "failed to power up mhi: %d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2020-08-13 12:04:24 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath11k_mhi_stop(struct ath11k_pci *ab_pci)
|
|
|
|
{
|
2022-04-01 20:30:40 +03:00
|
|
|
mhi_power_down(ab_pci->mhi_ctrl, true);
|
|
|
|
mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);
|
2020-08-13 12:04:24 +03:00
|
|
|
}
|
|
|
|
|
2022-04-05 11:26:39 +03:00
|
|
|
int ath11k_mhi_suspend(struct ath11k_pci *ab_pci)
|
2020-12-11 19:35:41 +02:00
|
|
|
{
|
2022-04-05 11:26:39 +03:00
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = mhi_pm_suspend(ab_pci->mhi_ctrl);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_warn(ab, "failed to suspend mhi: %d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-12-11 19:35:41 +02:00
|
|
|
}
|
|
|
|
|
2022-04-05 11:26:39 +03:00
|
|
|
int ath11k_mhi_resume(struct ath11k_pci *ab_pci)
|
2020-12-11 19:35:41 +02:00
|
|
|
{
|
2022-04-05 11:26:39 +03:00
|
|
|
struct ath11k_base *ab = ab_pci->ab;
|
|
|
|
int ret;
|
|
|
|
|
2022-04-01 20:30:40 +03:00
|
|
|
/* Do force MHI resume as some devices like QCA6390, WCN6855
|
|
|
|
* are not in M3 state but they are functional. So just ignore
|
|
|
|
* the MHI state while resuming.
|
|
|
|
*/
|
2022-04-05 11:26:39 +03:00
|
|
|
ret = mhi_pm_resume_force(ab_pci->mhi_ctrl);
|
|
|
|
if (ret) {
|
|
|
|
ath11k_warn(ab, "failed to resume mhi: %d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-12-11 19:35:41 +02:00
|
|
|
}
|