[PATCH v3 3/8] accel/qaic: Add MHI controller

Jeffrey Hugo quic_jhugo at quicinc.com
Mon Mar 13 17:45:34 UTC 2023


On 3/13/2023 7:39 AM, Jacek Lawrynowicz wrote:
> Hi,
> 
> On 06.03.2023 22:33, Jeffrey Hugo wrote:
>> An AIC100 device contains a MHI interface with a number of different
>> channels for controlling different aspects of the device. The MHI
>> controller works with the MHI bus to enable and drive that interface.
>>
>> AIC100 uses the BHI protocol in PBL to load SBL. The MHI controller
>> expects the SBL to be located at /lib/firmware/qcom/aic100/sbl.bin and
>> expects the MHI bus to manage the process of loading and sending SBL to
>> the device.
>>
>> Signed-off-by: Jeffrey Hugo <quic_jhugo at quicinc.com>
>> Reviewed-by: Carl Vanderlip <quic_carlv at quicinc.com>
>> Reviewed-by: Pranjal Ramajor Asha Kanojiya <quic_pkanojiy at quicinc.com>
>> ---
>>   drivers/accel/qaic/mhi_controller.c | 563 ++++++++++++++++++++++++++++++++++++
>>   drivers/accel/qaic/mhi_controller.h |  16 +
>>   2 files changed, 579 insertions(+)
>>   create mode 100644 drivers/accel/qaic/mhi_controller.c
>>   create mode 100644 drivers/accel/qaic/mhi_controller.h
>>
>> diff --git a/drivers/accel/qaic/mhi_controller.c b/drivers/accel/qaic/mhi_controller.c
>> new file mode 100644
>> index 0000000..f16dbb7
>> --- /dev/null
>> +++ b/drivers/accel/qaic/mhi_controller.c
>> @@ -0,0 +1,563 @@
>> +// 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 = 2000; /* 2 sec default */
>> +module_param(mhi_timeout, uint, 0600);
> 
> Consider documenting the param with MODULE_PARM_DESC() and adding _ms postfix to
> indicate that time units it is using.

Will do.

> 
>> +
>> +static 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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,
>> +		.auto_queue = 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 | MHI_CH_EE_AMSS,
>> +		.pollcfg = 0,
>> +		.doorbell = MHI_DB_BRST_DISABLE,
>> +		.lpm_notify = false,
>> +		.offload_channel = false,
>> +		.doorbell_mode_switch = false,
>> +		.auto_queue = false,
>> +		.wake_capable = false,
>> +	},
>> +	{
>> +		.num = 21,
>> +		.name = "QAIC_TIMESYNC",
>> +		.num_elements = 32,
>> +		.local_elements = 0,
>> +		.event_ring = 0,
>> +		.dir = DMA_FROM_DEVICE,
>> +		.ee_mask = MHI_CH_EE_SBL | MHI_CH_EE_AMSS,
>> +		.pollcfg = 0,
>> +		.doorbell = MHI_DB_BRST_DISABLE,
>> +		.lpm_notify = false,
>> +		.offload_channel = false,
>> +		.doorbell_mode_switch = false,
>> +		.auto_queue = 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_controller_config aic100_config = {
>> +	.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,
>> +};
>> +
>> +static int mhi_read_reg(struct mhi_controller *mhi_cntl, void __iomem *addr, u32 *out)
>> +{
>> +	u32 tmp = readl_relaxed(addr);
>> +
>> +	if (tmp == U32_MAX)
>> +		return -EIO;
>> +
>> +	*out = tmp;
>> +
>> +	return 0;
>> +}
>> +
>> +static void mhi_write_reg(struct mhi_controller *mhi_cntl, void __iomem *addr, u32 val)
>> +{
>> +	writel_relaxed(val, addr);
>> +}
>> +
>> +static int mhi_runtime_get(struct mhi_controller *mhi_cntl)
>> +{
>> +	return 0;
>> +}
>> +
>> +static void mhi_runtime_put(struct mhi_controller *mhi_cntl)
>> +{
>> +}
>> +
>> +static void mhi_status_cb(struct mhi_controller *mhi_cntl, enum mhi_callback reason)
>> +{
>> +	struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_cntl->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 && !qdev->in_reset)
> 
> Looks like qdev->in_reset should be protected by qdev->dev_lock.

Will check.

> 
>> +		qaic_dev_reset_clean_local_state(qdev, true);
>> +}
>> +
>> +static int mhi_reset_and_async_power_up(struct mhi_controller *mhi_cntl)
>> +{
>> +	char time_sec = 1;
>> +	int current_ee;
>> +	int ret;
>> +
>> +	/* Reset the device to bring the device in PBL EE */
>> +	mhi_soc_reset(mhi_cntl);
>> +
>> +	/*
>> +	 * Keep checking the execution environment(EE) after every 1 second
>> +	 * interval.
>> +	 */
>> +	do {
>> +		msleep(1000)> +		current_ee = mhi_get_exec_env(mhi_cntl);
>> +	} 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_cntl);
>> +	else
>> +		ret = -EIO;
>> +
>> +	return ret;
>> +}
>> +
>> +struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, void __iomem *mhi_bar,
>> +						    int mhi_irq)
>> +{
>> +	struct mhi_controller *mhi_cntl;
>> +	int ret;
>> +
>> +	mhi_cntl = devm_kzalloc(&pci_dev->dev, sizeof(*mhi_cntl), GFP_KERNEL);
>> +	if (!mhi_cntl)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	mhi_cntl->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_cntl->iova_start = 0;
>> +	mhi_cntl->iova_stop = PHYS_ADDR_MAX - 1;
>> +	mhi_cntl->status_cb = mhi_status_cb;
>> +	mhi_cntl->runtime_get = mhi_runtime_get;
>> +	mhi_cntl->runtime_put = mhi_runtime_put;
>> +	mhi_cntl->read_reg = mhi_read_reg;
>> +	mhi_cntl->write_reg = mhi_write_reg;
>> +	mhi_cntl->regs = mhi_bar;
>> +	mhi_cntl->reg_len = SZ_4K;
>> +	mhi_cntl->nr_irqs = 1;
>> +	mhi_cntl->irq = devm_kmalloc(&pci_dev->dev, sizeof(*mhi_cntl->irq), GFP_KERNEL);
>> +
>> +	if (!mhi_cntl->irq)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	mhi_cntl->irq[0] = mhi_irq;
>> +	mhi_cntl->fw_image = "qcom/aic100/sbl.bin";
>> +
>> +	/* use latest configured timeout */
>> +	aic100_config.timeout_ms = mhi_timeout;
>> +	ret = mhi_register_controller(mhi_cntl, &aic100_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_cntl);
>> +	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_cntl);
>> +	/*
>> +	 * 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_cntl)) {
>> +		pci_err(pci_dev, "Device is not expected to be SBL EE. SOC resetting the device to put it in PBL EE and again trying mhi async power up. Error %d\n",

Will have a look.


More information about the dri-devel mailing list