* [PATCH v2] accel/qaic: Add support for AI200 virtual functions
@ 2026-08-31 13:50 Youssef Samir
2026-08-31 17:09 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Youssef Samir @ 2026-08-31 13:50 UTC (permalink / raw)
To: jeff.hugo, carl.vanderlip, troy.hanson, zachary.mckevitt
Cc: ogabbay, lizhi.hou, karol.wachowski, linux-arm-msm, dri-devel,
Sashiko, Troy Hanson
From: Zack McKevitt <zmckevit@qti.qualcomm.com>
Register the qaic_pci_sriov_configure() callback to allow VFs to be
created through AIC200's sysfs numvfs entry. Additionally, define
a new MHI family for the 5 channels supported by AIC200 VFs: NNC,
Telemetry, IPCR, Loopback and SSR.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://lore.kernel.org/all/20260814165907.C76841F00A3A@smtp.kernel.org/
Co-developed-by: Troy Hanson <thanson@qti.qualcomm.com>
Signed-off-by: Troy Hanson <thanson@qti.qualcomm.com>
Signed-off-by: Zack McKevitt <zmckevit@qti.qualcomm.com>
Signed-off-by: Youssef Samir <youssef.abdulrahman@oss.qualcomm.com>
---
Changes in v2:
- Add the missing AIC200VF PCI ID to qaic_ids
- Handle teardown of VF in qaic_pci_sriov_configure() and qaic_pci_remove()
- Take dev_lock in qaic_pci_sriov_configure() before reading qdev->dev_state
- Link to v1: https://lore.kernel.org/all/20260814164125.3652876-1-youssef.abdulrahman@oss.qualcomm.com/
---
drivers/accel/qaic/mhi_controller.c | 192 +++++++++++++++++++++++++++-
drivers/accel/qaic/qaic.h | 1 +
drivers/accel/qaic/qaic_drv.c | 47 +++++++
3 files changed, 238 insertions(+), 2 deletions(-)
diff --git a/drivers/accel/qaic/mhi_controller.c b/drivers/accel/qaic/mhi_controller.c
index 40e6d262ef21..c720b70b1e0f 100644
--- a/drivers/accel/qaic/mhi_controller.c
+++ b/drivers/accel/qaic/mhi_controller.c
@@ -23,6 +23,7 @@ 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",
+ [FAMILY_AIC200_VF] = NULL,
};
static const struct mhi_channel_config aic100_channels[] = {
@@ -691,6 +692,159 @@ static const struct mhi_channel_config aic200_channels[] = {
},
};
+static const struct mhi_channel_config aic200vf_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_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_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 = "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,
@@ -721,6 +875,21 @@ static struct mhi_event_config aic200_events[] = {
},
};
+static struct mhi_event_config aic200vf_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,
@@ -744,6 +913,17 @@ static struct mhi_controller_config mhi_cntrl_configs[] = {
.use_bounce_buf = false,
.m2_no_db = false,
},
+ [FAMILY_AIC200_VF] = {
+ .max_channels = 128,
+ .timeout_ms = 0, /* controlled by mhi_timeout */
+ .buf_len = 0,
+ .num_channels = ARRAY_SIZE(aic200vf_channels),
+ .ch_cfg = aic200vf_channels,
+ .num_events = ARRAY_SIZE(aic200vf_events),
+ .event_cfg = aic200vf_events,
+ .use_bounce_buf = false,
+ .m2_no_db = false,
+ },
};
static int mhi_read_reg(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 *out)
@@ -864,11 +1044,19 @@ struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, voi
mhi_cntrl->fw_image = fw_image_paths[family];
- if (family == FAMILY_AIC200) {
+ switch (family) {
+ case FAMILY_AIC200:
mhi_cntrl->name = "AIC200";
mhi_cntrl->seg_len = SZ_512K;
- } else {
+ break;
+ case FAMILY_AIC200_VF:
+ mhi_cntrl->name = "AIC200_VF";
+ break;
+ case FAMILY_AIC100:
mhi_cntrl->name = "AIC100";
+ break;
+ default:
+ mhi_cntrl->name = NULL;
}
/* use latest configured timeout */
diff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h
index 02ca99000e2a..889fa51da7da 100644
--- a/drivers/accel/qaic/qaic.h
+++ b/drivers/accel/qaic/qaic.h
@@ -35,6 +35,7 @@
enum aic_families {
FAMILY_AIC100,
FAMILY_AIC200,
+ FAMILY_AIC200_VF,
FAMILY_MAX,
};
diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
index c1bb6db78d12..9e1e184efe0d 100644
--- a/drivers/accel/qaic/qaic_drv.c
+++ b/drivers/accel/qaic/qaic_drv.c
@@ -40,6 +40,7 @@ MODULE_IMPORT_NS("DMA_BUF");
#define PCI_DEVICE_ID_QCOM_AIC080 0xa080
#define PCI_DEVICE_ID_QCOM_AIC100 0xa100
#define PCI_DEVICE_ID_QCOM_AIC200 0xa110
+#define PCI_DEVICE_ID_QCOM_AIC200VF 0xa111
#define QAIC_NAME "qaic"
#define QAIC_DESC "Qualcomm Cloud AI Accelerators"
#define CNTL_MAJOR 5
@@ -78,6 +79,13 @@ static const struct qaic_device_config aic200_config = {
.dbc_bar_idx = 2,
};
+static const struct qaic_device_config aic200vf_config = {
+ .family = FAMILY_AIC200_VF,
+ .bar_mask = BIT(0) | BIT(1) | BIT(2) | BIT(4),
+ .mhi_bar_idx = 1,
+ .dbc_bar_idx = 2,
+};
+
bool datapath_polling;
module_param(datapath_polling, bool, 0400);
MODULE_PARM_DESC(datapath_polling, "Operate the datapath in polling mode");
@@ -610,6 +618,8 @@ static void qaic_pci_remove(struct pci_dev *pdev)
{
struct qaic_device *qdev = pci_get_drvdata(pdev);
+ pci_disable_sriov(pdev);
+
if (!qdev)
return;
@@ -666,6 +676,7 @@ static const struct pci_device_id qaic_ids[] = {
{ PCI_DEVICE_DATA(QCOM, AIC080, (kernel_ulong_t)&aic080_config), },
{ PCI_DEVICE_DATA(QCOM, AIC100, (kernel_ulong_t)&aic100_config), },
{ PCI_DEVICE_DATA(QCOM, AIC200, (kernel_ulong_t)&aic200_config), },
+ { PCI_DEVICE_DATA(QCOM, AIC200VF, (kernel_ulong_t)&aic200vf_config), },
{ }
};
MODULE_DEVICE_TABLE(pci, qaic_ids);
@@ -762,6 +773,41 @@ static const struct dev_pm_ops qaic_pm_ops = {
SYSTEM_SLEEP_PM_OPS(qaic_pm_suspend, qaic_pm_resume)
};
+static int qaic_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+ struct qaic_device *qdev = pci_get_drvdata(pdev);
+ int rcu_id;
+ int ret;
+
+ if (num_vfs == 0) {
+ pci_disable_sriov(pdev);
+ return 0;
+ }
+
+ rcu_id = srcu_read_lock(&qdev->dev_lock);
+ /* Qaic device must be online to process VF bringup */
+ if (qdev->dev_state == QAIC_OFFLINE) {
+ ret = -ENODEV;
+ goto unlock_dev_srcu;
+ }
+ if (qdev->dev_state == QAIC_BOOT) {
+ ret = -EBUSY;
+ goto unlock_dev_srcu;
+ }
+
+ ret = pci_enable_sriov(pdev, num_vfs);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to enable SR-IOV: %d (requested %d VFs, max %d)\n",
+ ret, num_vfs, pci_sriov_get_totalvfs(pdev));
+ goto unlock_dev_srcu;
+ }
+
+ dev_dbg(&pdev->dev, "Successfully enabled %d VFs\n", num_vfs);
+unlock_dev_srcu:
+ srcu_read_unlock(&qdev->dev_lock, rcu_id);
+ return ret ?: num_vfs;
+}
+
static struct pci_driver qaic_pci_driver = {
.name = QAIC_NAME,
.id_table = qaic_ids,
@@ -769,6 +815,7 @@ static struct pci_driver qaic_pci_driver = {
.remove = qaic_pci_remove,
.shutdown = qaic_pci_shutdown,
.err_handler = &qaic_pci_err_handler,
+ .sriov_configure = qaic_pci_sriov_configure,
.driver = {
.pm = pm_sleep_ptr(&qaic_pm_ops),
},
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v2] accel/qaic: Add support for AI200 virtual functions
2026-08-31 13:50 [PATCH v2] accel/qaic: Add support for AI200 virtual functions Youssef Samir
@ 2026-08-31 17:09 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-08-31 17:09 UTC (permalink / raw)
To: Youssef Samir; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] System crash on VF suspend due to unconditional NULL pointer dereference of qdev->mqts_ch.
--
commit 136267f1b302748ae2c7ab124a5490ff5bbea19a
Author: Zack McKevitt <zmckevit@qti.qualcomm.com>
accel/qaic: Add support for AI200 virtual functions
This patch registers the qaic_pci_sriov_configure() callback to allow
virtual functions to be created through the AIC200's sysfs numvfs entry.
It also defines a new MHI family for the channels supported by AIC200 VFs.
Link: https://lore.kernel.org/all/20260814165907.C76841F00A3A@smtp.kernel.org/
> diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
> index 0acf6cdd44f81..6427105ebdf75 100644
> --- a/drivers/accel/qaic/qaic_drv.c
> +++ b/drivers/accel/qaic/qaic_drv.c
[ ... ]
> @@ -768,6 +814,7 @@ static struct pci_driver qaic_pci_driver = {
> .remove = qaic_pci_remove,
> .shutdown = qaic_pci_shutdown,
> .err_handler = &qaic_pci_err_handler,
> + .sriov_configure = qaic_pci_sriov_configure,
> .driver = {
> .pm = pm_sleep_ptr(&qaic_pm_ops),
[Severity: High]
This is a pre-existing issue, but since this patch introduces virtual
functions that can be suspended, will this cause a system crash on VF suspend?
When the system suspends a QAIC virtual function, qaic_pm_suspend() is called
via the qaic_pm_ops struct. This function unconditionally calls into the
timesync code:
drivers/accel/qaic/qaic_drv.c:qaic_pm_suspend() {
...
qaic_mqts_ch_stop_timer(qdev->mqts_ch);
...
}
However, since VFs do not support or initialize the QAIC_TIMESYNC_PERIODIC
channel, qdev->mqts_ch is always NULL for them. This leads to an unconditional
NULL pointer dereference when stopping the timer:
drivers/accel/qaic/qaic_timesync.c:qaic_mqts_ch_stop_timer() {
struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
...
}
Because dev_get_drvdata accesses dev->driver_data, passing the evaluated
address of the NULL pointer's dev struct will trigger a kernel panic. Should
qaic_pm_suspend() or qaic_mqts_ch_stop_timer() check for a NULL channel
before attempting to stop the timer?
> },
> };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831135008.1418190-1-youssef.abdulrahman@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-31 17:09 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 13:50 [PATCH v2] accel/qaic: Add support for AI200 virtual functions Youssef Samir
2026-08-31 17:09 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox