mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 23:03:57 -04:00
MHI stack offers the 'auto_queue' feature, which allows the MHI stack to auto queue the buffers for the RX path (DL channel). Though this feature simplifies the client driver design, it introduces race between the client drivers and the MHI stack. For instance, with auto_queue, the 'dl_callback' for the DL channel may get called before the client driver is fully probed. This means, by the time the dl_callback gets called, the client driver's structures might not be initialized, leading to NULL ptr dereference. Currently, the drivers have to workaround this issue by initializing the internal structures before calling mhi_prepare_for_transfer_autoqueue(). But even so, there is a chance that the client driver's internal code path may call the MHI queue APIs before mhi_prepare_for_transfer_autoqueue() is called, leading to similar NULL ptr dereference. This issue has been reported on the Qcom X1E80100 CRD machines affecting boot. So to properly fix all these races, drop the MHI 'auto_queue' feature altogether and let the client driver (QRTR) manage the RX buffers manually. In the QRTR driver, queue the RX buffers based on the ring length during probe and recycle the buffers in 'dl_callback' once they are consumed. This also warrants removing the setting of 'auto_queue' flag from controller drivers. Currently, this 'auto_queue' feature is only enabled for IPCR DL channel. So only the QRTR client driver requires the modification. Fixes:227fee5fc9("bus: mhi: core: Add an API for auto queueing buffers for DL channel") Fixes:68a838b84e("net: qrtr: start MHI channel after endpoit creation") Reported-by: Johan Hovold <johan@kernel.org> Closes: https://lore.kernel.org/linux-arm-msm/ZyTtVdkCCES0lkl4@hovoldconsulting.com Suggested-by: Chris Lew <quic_clew@quicinc.com> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com> Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com> Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com> Acked-by: Jeff Johnson <jjohnson@kernel.org> # drivers/net/wireless/ath/... Acked-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com> Acked-by: Paolo Abeni <pabeni@redhat.com> Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20251218-qrtr-fix-v2-1-c7499bfcfbe0@oss.qualcomm.com
933 lines
21 KiB
C
933 lines
21 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
|
|
/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/err.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/mhi.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/sizes.h>
|
|
|
|
#include "mhi_controller.h"
|
|
#include "qaic.h"
|
|
|
|
#define MAX_RESET_TIME_SEC 25
|
|
|
|
static unsigned int mhi_timeout_ms = 2000; /* 2 sec default */
|
|
module_param(mhi_timeout_ms, uint, 0600);
|
|
MODULE_PARM_DESC(mhi_timeout_ms, "MHI controller timeout value");
|
|
|
|
static const char *fw_image_paths[FAMILY_MAX] = {
|
|
[FAMILY_AIC100] = "qcom/aic100/sbl.bin",
|
|
[FAMILY_AIC200] = "qcom/aic200/sbl.bin",
|
|
};
|
|
|
|
static const struct mhi_channel_config aic100_channels[] = {
|
|
{
|
|
.name = "QAIC_LOOPBACK",
|
|
.num = 0,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_LOOPBACK",
|
|
.num = 1,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_SAHARA",
|
|
.num = 2,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_SAHARA",
|
|
.num = 3,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_DIAG",
|
|
.num = 4,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_DIAG",
|
|
.num = 5,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_SSR",
|
|
.num = 6,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_SSR",
|
|
.num = 7,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_QDSS",
|
|
.num = 8,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_QDSS",
|
|
.num = 9,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_CONTROL",
|
|
.num = 10,
|
|
.num_elements = 128,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_CONTROL",
|
|
.num = 11,
|
|
.num_elements = 128,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_LOGGING",
|
|
.num = 12,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_LOGGING",
|
|
.num = 13,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_STATUS",
|
|
.num = 14,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_STATUS",
|
|
.num = 15,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TELEMETRY",
|
|
.num = 16,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TELEMETRY",
|
|
.num = 17,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_DEBUG",
|
|
.num = 18,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_DEBUG",
|
|
.num = 19,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TIMESYNC",
|
|
.num = 20,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TIMESYNC",
|
|
.num = 21,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TIMESYNC_PERIODIC",
|
|
.num = 22,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TIMESYNC_PERIODIC",
|
|
.num = 23,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "IPCR",
|
|
.num = 24,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "IPCR",
|
|
.num = 25,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
};
|
|
|
|
static const struct mhi_channel_config aic200_channels[] = {
|
|
{
|
|
.name = "QAIC_LOOPBACK",
|
|
.num = 0,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_LOOPBACK",
|
|
.num = 1,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_SAHARA",
|
|
.num = 2,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_SAHARA",
|
|
.num = 3,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_SSR",
|
|
.num = 6,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_SSR",
|
|
.num = 7,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_CONTROL",
|
|
.num = 10,
|
|
.num_elements = 128,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_CONTROL",
|
|
.num = 11,
|
|
.num_elements = 128,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_LOGGING",
|
|
.num = 12,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_LOGGING",
|
|
.num = 13,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_SBL,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_STATUS",
|
|
.num = 14,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_STATUS",
|
|
.num = 15,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TELEMETRY",
|
|
.num = 16,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TELEMETRY",
|
|
.num = 17,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TIMESYNC_PERIODIC",
|
|
.num = 22,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "QAIC_TIMESYNC_PERIODIC",
|
|
.num = 23,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "IPCR",
|
|
.num = 24,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_TO_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
{
|
|
.name = "IPCR",
|
|
.num = 25,
|
|
.num_elements = 32,
|
|
.local_elements = 0,
|
|
.event_ring = 0,
|
|
.dir = DMA_FROM_DEVICE,
|
|
.ee_mask = MHI_CH_EE_AMSS,
|
|
.pollcfg = 0,
|
|
.doorbell = MHI_DB_BRST_DISABLE,
|
|
.lpm_notify = false,
|
|
.offload_channel = false,
|
|
.doorbell_mode_switch = false,
|
|
.wake_capable = false,
|
|
},
|
|
};
|
|
|
|
static struct mhi_event_config aic100_events[] = {
|
|
{
|
|
.num_elements = 32,
|
|
.irq_moderation_ms = 0,
|
|
.irq = 0,
|
|
.channel = U32_MAX,
|
|
.priority = 1,
|
|
.mode = MHI_DB_BRST_DISABLE,
|
|
.data_type = MHI_ER_CTRL,
|
|
.hardware_event = false,
|
|
.client_managed = false,
|
|
.offload_channel = false,
|
|
},
|
|
};
|
|
|
|
static struct mhi_event_config aic200_events[] = {
|
|
{
|
|
.num_elements = 32,
|
|
.irq_moderation_ms = 0,
|
|
.irq = 0,
|
|
.channel = U32_MAX,
|
|
.priority = 1,
|
|
.mode = MHI_DB_BRST_DISABLE,
|
|
.data_type = MHI_ER_CTRL,
|
|
.hardware_event = false,
|
|
.client_managed = false,
|
|
.offload_channel = false,
|
|
},
|
|
};
|
|
|
|
static struct mhi_controller_config mhi_cntrl_configs[] = {
|
|
[FAMILY_AIC100] = {
|
|
.max_channels = 128,
|
|
.timeout_ms = 0, /* controlled by mhi_timeout */
|
|
.buf_len = 0,
|
|
.num_channels = ARRAY_SIZE(aic100_channels),
|
|
.ch_cfg = aic100_channels,
|
|
.num_events = ARRAY_SIZE(aic100_events),
|
|
.event_cfg = aic100_events,
|
|
.use_bounce_buf = false,
|
|
.m2_no_db = false,
|
|
},
|
|
[FAMILY_AIC200] = {
|
|
.max_channels = 128,
|
|
.timeout_ms = 0, /* controlled by mhi_timeout */
|
|
.buf_len = 0,
|
|
.num_channels = ARRAY_SIZE(aic200_channels),
|
|
.ch_cfg = aic200_channels,
|
|
.num_events = ARRAY_SIZE(aic200_events),
|
|
.event_cfg = aic200_events,
|
|
.use_bounce_buf = false,
|
|
.m2_no_db = false,
|
|
},
|
|
};
|
|
|
|
static int mhi_read_reg(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 *out)
|
|
{
|
|
u32 tmp;
|
|
|
|
/*
|
|
* SOC_HW_VERSION quirk
|
|
* The SOC_HW_VERSION register (offset 0x224) is not reliable and
|
|
* may contain uninitialized values, including 0xFFFFFFFF. This could
|
|
* cause a false positive link down error. Instead, intercept any
|
|
* reads and provide the correct value of the register.
|
|
*/
|
|
if (addr - mhi_cntrl->regs == 0x224) {
|
|
*out = 0x60110200;
|
|
return 0;
|
|
}
|
|
|
|
tmp = readl_relaxed(addr);
|
|
if (tmp == U32_MAX)
|
|
return -EIO;
|
|
|
|
*out = tmp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 val)
|
|
{
|
|
writel_relaxed(val, addr);
|
|
}
|
|
|
|
static int mhi_runtime_get(struct mhi_controller *mhi_cntrl)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void mhi_runtime_put(struct mhi_controller *mhi_cntrl)
|
|
{
|
|
}
|
|
|
|
static void mhi_status_cb(struct mhi_controller *mhi_cntrl, enum mhi_callback reason)
|
|
{
|
|
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_cntrl->cntrl_dev));
|
|
|
|
/* this event occurs in atomic context */
|
|
if (reason == MHI_CB_FATAL_ERROR)
|
|
pci_err(qdev->pdev, "Fatal error received from device. Attempting to recover\n");
|
|
/* this event occurs in non-atomic context */
|
|
if (reason == MHI_CB_SYS_ERROR)
|
|
qaic_dev_reset_clean_local_state(qdev);
|
|
}
|
|
|
|
static int mhi_reset_and_async_power_up(struct mhi_controller *mhi_cntrl)
|
|
{
|
|
u8 time_sec = 1;
|
|
int current_ee;
|
|
int ret;
|
|
|
|
/* Reset the device to bring the device in PBL EE */
|
|
mhi_soc_reset(mhi_cntrl);
|
|
|
|
/*
|
|
* Keep checking the execution environment(EE) after every 1 second
|
|
* interval.
|
|
*/
|
|
do {
|
|
msleep(1000);
|
|
current_ee = mhi_get_exec_env(mhi_cntrl);
|
|
} while (current_ee != MHI_EE_PBL && time_sec++ <= MAX_RESET_TIME_SEC);
|
|
|
|
/* If the device is in PBL EE retry power up */
|
|
if (current_ee == MHI_EE_PBL)
|
|
ret = mhi_async_power_up(mhi_cntrl);
|
|
else
|
|
ret = -EIO;
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, void __iomem *mhi_bar,
|
|
int mhi_irq, bool shared_msi, int family)
|
|
{
|
|
struct mhi_controller_config mhi_config = mhi_cntrl_configs[family];
|
|
struct mhi_controller *mhi_cntrl;
|
|
int ret;
|
|
|
|
mhi_cntrl = devm_kzalloc(&pci_dev->dev, sizeof(*mhi_cntrl), GFP_KERNEL);
|
|
if (!mhi_cntrl)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
mhi_cntrl->cntrl_dev = &pci_dev->dev;
|
|
|
|
/*
|
|
* Covers the entire possible physical ram region. Remote side is
|
|
* going to calculate a size of this range, so subtract 1 to prevent
|
|
* rollover.
|
|
*/
|
|
mhi_cntrl->iova_start = 0;
|
|
mhi_cntrl->iova_stop = PHYS_ADDR_MAX - 1;
|
|
mhi_cntrl->status_cb = mhi_status_cb;
|
|
mhi_cntrl->runtime_get = mhi_runtime_get;
|
|
mhi_cntrl->runtime_put = mhi_runtime_put;
|
|
mhi_cntrl->read_reg = mhi_read_reg;
|
|
mhi_cntrl->write_reg = mhi_write_reg;
|
|
mhi_cntrl->regs = mhi_bar;
|
|
mhi_cntrl->reg_len = SZ_4K;
|
|
mhi_cntrl->nr_irqs = 1;
|
|
mhi_cntrl->irq = devm_kmalloc(&pci_dev->dev, sizeof(*mhi_cntrl->irq), GFP_KERNEL);
|
|
|
|
if (!mhi_cntrl->irq)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
mhi_cntrl->irq[0] = mhi_irq;
|
|
|
|
if (shared_msi) /* MSI shared with data path, no IRQF_NO_SUSPEND */
|
|
mhi_cntrl->irq_flags = IRQF_SHARED;
|
|
|
|
mhi_cntrl->fw_image = fw_image_paths[family];
|
|
|
|
if (family == FAMILY_AIC200) {
|
|
mhi_cntrl->name = "AIC200";
|
|
mhi_cntrl->seg_len = SZ_512K;
|
|
} else {
|
|
mhi_cntrl->name = "AIC100";
|
|
}
|
|
|
|
/* use latest configured timeout */
|
|
mhi_config.timeout_ms = mhi_timeout_ms;
|
|
ret = mhi_register_controller(mhi_cntrl, &mhi_config);
|
|
if (ret) {
|
|
pci_err(pci_dev, "mhi_register_controller failed %d\n", ret);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
ret = mhi_prepare_for_power_up(mhi_cntrl);
|
|
if (ret) {
|
|
pci_err(pci_dev, "mhi_prepare_for_power_up failed %d\n", ret);
|
|
goto prepare_power_up_fail;
|
|
}
|
|
|
|
ret = mhi_async_power_up(mhi_cntrl);
|
|
/*
|
|
* If EIO is returned it is possible that device is in SBL EE, which is
|
|
* undesired. SOC reset the device and try to power up again.
|
|
*/
|
|
if (ret == -EIO && MHI_EE_SBL == mhi_get_exec_env(mhi_cntrl)) {
|
|
pci_err(pci_dev, "Found device in SBL at MHI init. Attempting a reset.\n");
|
|
ret = mhi_reset_and_async_power_up(mhi_cntrl);
|
|
}
|
|
|
|
if (ret) {
|
|
pci_err(pci_dev, "mhi_async_power_up failed %d\n", ret);
|
|
goto power_up_fail;
|
|
}
|
|
|
|
return mhi_cntrl;
|
|
|
|
power_up_fail:
|
|
mhi_unprepare_after_power_down(mhi_cntrl);
|
|
prepare_power_up_fail:
|
|
mhi_unregister_controller(mhi_cntrl);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
void qaic_mhi_free_controller(struct mhi_controller *mhi_cntrl, bool link_up)
|
|
{
|
|
mhi_power_down(mhi_cntrl, link_up);
|
|
mhi_unprepare_after_power_down(mhi_cntrl);
|
|
mhi_unregister_controller(mhi_cntrl);
|
|
}
|
|
|
|
void qaic_mhi_start_reset(struct mhi_controller *mhi_cntrl)
|
|
{
|
|
mhi_power_down(mhi_cntrl, true);
|
|
}
|
|
|
|
void qaic_mhi_reset_done(struct mhi_controller *mhi_cntrl)
|
|
{
|
|
struct pci_dev *pci_dev = container_of(mhi_cntrl->cntrl_dev, struct pci_dev, dev);
|
|
int ret;
|
|
|
|
ret = mhi_async_power_up(mhi_cntrl);
|
|
if (ret)
|
|
pci_err(pci_dev, "mhi_async_power_up failed after reset %d\n", ret);
|
|
}
|