162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright(c) 2023 Advanced Micro Devices, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/io.h>
562306a36Sopenharmony_ci#include <linux/types.h>
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/pds/pds_common.h>
962306a36Sopenharmony_ci#include <linux/pds/pds_core_if.h>
1062306a36Sopenharmony_ci#include <linux/pds/pds_adminq.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "vfio_dev.h"
1362306a36Sopenharmony_ci#include "cmds.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define SUSPEND_TIMEOUT_S		5
1662306a36Sopenharmony_ci#define SUSPEND_CHECK_INTERVAL_MS	1
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int pds_vfio_client_adminq_cmd(struct pds_vfio_pci_device *pds_vfio,
1962306a36Sopenharmony_ci				      union pds_core_adminq_cmd *req,
2062306a36Sopenharmony_ci				      union pds_core_adminq_comp *resp,
2162306a36Sopenharmony_ci				      bool fast_poll)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio);
2462306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {};
2562306a36Sopenharmony_ci	struct pdsc *pdsc;
2662306a36Sopenharmony_ci	int err;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	/* Wrap the client request */
2962306a36Sopenharmony_ci	cmd.client_request.opcode = PDS_AQ_CMD_CLIENT_CMD;
3062306a36Sopenharmony_ci	cmd.client_request.client_id = cpu_to_le16(pds_vfio->client_id);
3162306a36Sopenharmony_ci	memcpy(cmd.client_request.client_cmd, req,
3262306a36Sopenharmony_ci	       sizeof(cmd.client_request.client_cmd));
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	pdsc = pdsc_get_pf_struct(pdev);
3562306a36Sopenharmony_ci	if (IS_ERR(pdsc))
3662306a36Sopenharmony_ci		return PTR_ERR(pdsc);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	err = pdsc_adminq_post(pdsc, &cmd, resp, fast_poll);
3962306a36Sopenharmony_ci	if (err && err != -EAGAIN)
4062306a36Sopenharmony_ci		dev_err(pds_vfio_to_dev(pds_vfio),
4162306a36Sopenharmony_ci			"client admin cmd failed: %pe\n", ERR_PTR(err));
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return err;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciint pds_vfio_register_client_cmd(struct pds_vfio_pci_device *pds_vfio)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio);
4962306a36Sopenharmony_ci	char devname[PDS_DEVNAME_LEN];
5062306a36Sopenharmony_ci	struct pdsc *pdsc;
5162306a36Sopenharmony_ci	int ci;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	snprintf(devname, sizeof(devname), "%s.%d-%u", PDS_VFIO_LM_DEV_NAME,
5462306a36Sopenharmony_ci		 pci_domain_nr(pdev->bus),
5562306a36Sopenharmony_ci		 PCI_DEVID(pdev->bus->number, pdev->devfn));
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	pdsc = pdsc_get_pf_struct(pdev);
5862306a36Sopenharmony_ci	if (IS_ERR(pdsc))
5962306a36Sopenharmony_ci		return PTR_ERR(pdsc);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	ci = pds_client_register(pdsc, devname);
6262306a36Sopenharmony_ci	if (ci < 0)
6362306a36Sopenharmony_ci		return ci;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	pds_vfio->client_id = ci;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_civoid pds_vfio_unregister_client_cmd(struct pds_vfio_pci_device *pds_vfio)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio);
7362306a36Sopenharmony_ci	struct pdsc *pdsc;
7462306a36Sopenharmony_ci	int err;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	pdsc = pdsc_get_pf_struct(pdev);
7762306a36Sopenharmony_ci	if (IS_ERR(pdsc))
7862306a36Sopenharmony_ci		return;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	err = pds_client_unregister(pdsc, pds_vfio->client_id);
8162306a36Sopenharmony_ci	if (err)
8262306a36Sopenharmony_ci		dev_err(&pdev->dev, "unregister from DSC failed: %pe\n",
8362306a36Sopenharmony_ci			ERR_PTR(err));
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	pds_vfio->client_id = 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int
8962306a36Sopenharmony_cipds_vfio_suspend_wait_device_cmd(struct pds_vfio_pci_device *pds_vfio, u8 type)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
9262306a36Sopenharmony_ci		.lm_suspend_status = {
9362306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_SUSPEND_STATUS,
9462306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
9562306a36Sopenharmony_ci			.type = type,
9662306a36Sopenharmony_ci		},
9762306a36Sopenharmony_ci	};
9862306a36Sopenharmony_ci	struct device *dev = pds_vfio_to_dev(pds_vfio);
9962306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
10062306a36Sopenharmony_ci	unsigned long time_limit;
10162306a36Sopenharmony_ci	unsigned long time_start;
10262306a36Sopenharmony_ci	unsigned long time_done;
10362306a36Sopenharmony_ci	int err;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	time_start = jiffies;
10662306a36Sopenharmony_ci	time_limit = time_start + HZ * SUSPEND_TIMEOUT_S;
10762306a36Sopenharmony_ci	do {
10862306a36Sopenharmony_ci		err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, true);
10962306a36Sopenharmony_ci		if (err != -EAGAIN)
11062306a36Sopenharmony_ci			break;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		msleep(SUSPEND_CHECK_INTERVAL_MS);
11362306a36Sopenharmony_ci	} while (time_before(jiffies, time_limit));
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	time_done = jiffies;
11662306a36Sopenharmony_ci	dev_dbg(dev, "%s: vf%u: Suspend comp received in %d msecs\n", __func__,
11762306a36Sopenharmony_ci		pds_vfio->vf_id, jiffies_to_msecs(time_done - time_start));
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* Check the results */
12062306a36Sopenharmony_ci	if (time_after_eq(time_done, time_limit)) {
12162306a36Sopenharmony_ci		dev_err(dev, "%s: vf%u: Suspend comp timeout\n", __func__,
12262306a36Sopenharmony_ci			pds_vfio->vf_id);
12362306a36Sopenharmony_ci		err = -ETIMEDOUT;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return err;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciint pds_vfio_suspend_device_cmd(struct pds_vfio_pci_device *pds_vfio, u8 type)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
13262306a36Sopenharmony_ci		.lm_suspend = {
13362306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_SUSPEND,
13462306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
13562306a36Sopenharmony_ci			.type = type,
13662306a36Sopenharmony_ci		},
13762306a36Sopenharmony_ci	};
13862306a36Sopenharmony_ci	struct device *dev = pds_vfio_to_dev(pds_vfio);
13962306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
14062306a36Sopenharmony_ci	int err;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	dev_dbg(dev, "vf%u: Suspend device\n", pds_vfio->vf_id);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/*
14562306a36Sopenharmony_ci	 * The initial suspend request to the firmware starts the device suspend
14662306a36Sopenharmony_ci	 * operation and the firmware returns success if it's started
14762306a36Sopenharmony_ci	 * successfully.
14862306a36Sopenharmony_ci	 */
14962306a36Sopenharmony_ci	err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, true);
15062306a36Sopenharmony_ci	if (err) {
15162306a36Sopenharmony_ci		dev_err(dev, "vf%u: Suspend failed: %pe\n", pds_vfio->vf_id,
15262306a36Sopenharmony_ci			ERR_PTR(err));
15362306a36Sopenharmony_ci		return err;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * The subsequent suspend status request(s) check if the firmware has
15862306a36Sopenharmony_ci	 * completed the device suspend process.
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	return pds_vfio_suspend_wait_device_cmd(pds_vfio, type);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciint pds_vfio_resume_device_cmd(struct pds_vfio_pci_device *pds_vfio, u8 type)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
16662306a36Sopenharmony_ci		.lm_resume = {
16762306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_RESUME,
16862306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
16962306a36Sopenharmony_ci			.type = type,
17062306a36Sopenharmony_ci		},
17162306a36Sopenharmony_ci	};
17262306a36Sopenharmony_ci	struct device *dev = pds_vfio_to_dev(pds_vfio);
17362306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	dev_dbg(dev, "vf%u: Resume device\n", pds_vfio->vf_id);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, true);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ciint pds_vfio_get_lm_state_size_cmd(struct pds_vfio_pci_device *pds_vfio, u64 *size)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
18362306a36Sopenharmony_ci		.lm_state_size = {
18462306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_STATE_SIZE,
18562306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
18662306a36Sopenharmony_ci		},
18762306a36Sopenharmony_ci	};
18862306a36Sopenharmony_ci	struct device *dev = pds_vfio_to_dev(pds_vfio);
18962306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
19062306a36Sopenharmony_ci	int err;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	dev_dbg(dev, "vf%u: Get migration status\n", pds_vfio->vf_id);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false);
19562306a36Sopenharmony_ci	if (err)
19662306a36Sopenharmony_ci		return err;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	*size = le64_to_cpu(comp.lm_state_size.size);
19962306a36Sopenharmony_ci	return 0;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int pds_vfio_dma_map_lm_file(struct device *dev,
20362306a36Sopenharmony_ci				    enum dma_data_direction dir,
20462306a36Sopenharmony_ci				    struct pds_vfio_lm_file *lm_file)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct pds_lm_sg_elem *sgl, *sge;
20762306a36Sopenharmony_ci	struct scatterlist *sg;
20862306a36Sopenharmony_ci	dma_addr_t sgl_addr;
20962306a36Sopenharmony_ci	size_t sgl_size;
21062306a36Sopenharmony_ci	int err;
21162306a36Sopenharmony_ci	int i;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (!lm_file)
21462306a36Sopenharmony_ci		return -EINVAL;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* dma map file pages */
21762306a36Sopenharmony_ci	err = dma_map_sgtable(dev, &lm_file->sg_table, dir, 0);
21862306a36Sopenharmony_ci	if (err)
21962306a36Sopenharmony_ci		return err;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	lm_file->num_sge = lm_file->sg_table.nents;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* alloc sgl */
22462306a36Sopenharmony_ci	sgl_size = lm_file->num_sge * sizeof(struct pds_lm_sg_elem);
22562306a36Sopenharmony_ci	sgl = kzalloc(sgl_size, GFP_KERNEL);
22662306a36Sopenharmony_ci	if (!sgl) {
22762306a36Sopenharmony_ci		err = -ENOMEM;
22862306a36Sopenharmony_ci		goto out_unmap_sgtable;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* fill sgl */
23262306a36Sopenharmony_ci	sge = sgl;
23362306a36Sopenharmony_ci	for_each_sgtable_dma_sg(&lm_file->sg_table, sg, i) {
23462306a36Sopenharmony_ci		sge->addr = cpu_to_le64(sg_dma_address(sg));
23562306a36Sopenharmony_ci		sge->len = cpu_to_le32(sg_dma_len(sg));
23662306a36Sopenharmony_ci		dev_dbg(dev, "addr = %llx, len = %u\n", sge->addr, sge->len);
23762306a36Sopenharmony_ci		sge++;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	sgl_addr = dma_map_single(dev, sgl, sgl_size, DMA_TO_DEVICE);
24162306a36Sopenharmony_ci	if (dma_mapping_error(dev, sgl_addr)) {
24262306a36Sopenharmony_ci		err = -EIO;
24362306a36Sopenharmony_ci		goto out_free_sgl;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	lm_file->sgl = sgl;
24762306a36Sopenharmony_ci	lm_file->sgl_addr = sgl_addr;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ciout_free_sgl:
25262306a36Sopenharmony_ci	kfree(sgl);
25362306a36Sopenharmony_ciout_unmap_sgtable:
25462306a36Sopenharmony_ci	lm_file->num_sge = 0;
25562306a36Sopenharmony_ci	dma_unmap_sgtable(dev, &lm_file->sg_table, dir, 0);
25662306a36Sopenharmony_ci	return err;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void pds_vfio_dma_unmap_lm_file(struct device *dev,
26062306a36Sopenharmony_ci				       enum dma_data_direction dir,
26162306a36Sopenharmony_ci				       struct pds_vfio_lm_file *lm_file)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	if (!lm_file)
26462306a36Sopenharmony_ci		return;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* free sgl */
26762306a36Sopenharmony_ci	if (lm_file->sgl) {
26862306a36Sopenharmony_ci		dma_unmap_single(dev, lm_file->sgl_addr,
26962306a36Sopenharmony_ci				 lm_file->num_sge * sizeof(*lm_file->sgl),
27062306a36Sopenharmony_ci				 DMA_TO_DEVICE);
27162306a36Sopenharmony_ci		kfree(lm_file->sgl);
27262306a36Sopenharmony_ci		lm_file->sgl = NULL;
27362306a36Sopenharmony_ci		lm_file->sgl_addr = DMA_MAPPING_ERROR;
27462306a36Sopenharmony_ci		lm_file->num_sge = 0;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* dma unmap file pages */
27862306a36Sopenharmony_ci	dma_unmap_sgtable(dev, &lm_file->sg_table, dir, 0);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ciint pds_vfio_get_lm_state_cmd(struct pds_vfio_pci_device *pds_vfio)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
28462306a36Sopenharmony_ci		.lm_save = {
28562306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_SAVE,
28662306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
28762306a36Sopenharmony_ci		},
28862306a36Sopenharmony_ci	};
28962306a36Sopenharmony_ci	struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio);
29062306a36Sopenharmony_ci	struct device *pdsc_dev = &pci_physfn(pdev)->dev;
29162306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
29262306a36Sopenharmony_ci	struct pds_vfio_lm_file *lm_file;
29362306a36Sopenharmony_ci	int err;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "vf%u: Get migration state\n", pds_vfio->vf_id);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	lm_file = pds_vfio->save_file;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	err = pds_vfio_dma_map_lm_file(pdsc_dev, DMA_FROM_DEVICE, lm_file);
30062306a36Sopenharmony_ci	if (err) {
30162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to map save migration file: %pe\n",
30262306a36Sopenharmony_ci			ERR_PTR(err));
30362306a36Sopenharmony_ci		return err;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	cmd.lm_save.sgl_addr = cpu_to_le64(lm_file->sgl_addr);
30762306a36Sopenharmony_ci	cmd.lm_save.num_sge = cpu_to_le32(lm_file->num_sge);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false);
31062306a36Sopenharmony_ci	if (err)
31162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get migration state: %pe\n",
31262306a36Sopenharmony_ci			ERR_PTR(err));
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	pds_vfio_dma_unmap_lm_file(pdsc_dev, DMA_FROM_DEVICE, lm_file);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return err;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ciint pds_vfio_set_lm_state_cmd(struct pds_vfio_pci_device *pds_vfio)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
32262306a36Sopenharmony_ci		.lm_restore = {
32362306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_RESTORE,
32462306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
32562306a36Sopenharmony_ci		},
32662306a36Sopenharmony_ci	};
32762306a36Sopenharmony_ci	struct pci_dev *pdev = pds_vfio_to_pci_dev(pds_vfio);
32862306a36Sopenharmony_ci	struct device *pdsc_dev = &pci_physfn(pdev)->dev;
32962306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
33062306a36Sopenharmony_ci	struct pds_vfio_lm_file *lm_file;
33162306a36Sopenharmony_ci	int err;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "vf%u: Set migration state\n", pds_vfio->vf_id);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	lm_file = pds_vfio->restore_file;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	err = pds_vfio_dma_map_lm_file(pdsc_dev, DMA_TO_DEVICE, lm_file);
33862306a36Sopenharmony_ci	if (err) {
33962306a36Sopenharmony_ci		dev_err(&pdev->dev,
34062306a36Sopenharmony_ci			"failed to map restore migration file: %pe\n",
34162306a36Sopenharmony_ci			ERR_PTR(err));
34262306a36Sopenharmony_ci		return err;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	cmd.lm_restore.sgl_addr = cpu_to_le64(lm_file->sgl_addr);
34662306a36Sopenharmony_ci	cmd.lm_restore.num_sge = cpu_to_le32(lm_file->num_sge);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false);
34962306a36Sopenharmony_ci	if (err)
35062306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to set migration state: %pe\n",
35162306a36Sopenharmony_ci			ERR_PTR(err));
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	pds_vfio_dma_unmap_lm_file(pdsc_dev, DMA_TO_DEVICE, lm_file);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return err;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_civoid pds_vfio_send_host_vf_lm_status_cmd(struct pds_vfio_pci_device *pds_vfio,
35962306a36Sopenharmony_ci					 enum pds_lm_host_vf_status vf_status)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
36262306a36Sopenharmony_ci		.lm_host_vf_status = {
36362306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_HOST_VF_STATUS,
36462306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
36562306a36Sopenharmony_ci			.status = vf_status,
36662306a36Sopenharmony_ci		},
36762306a36Sopenharmony_ci	};
36862306a36Sopenharmony_ci	struct device *dev = pds_vfio_to_dev(pds_vfio);
36962306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
37062306a36Sopenharmony_ci	int err;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	dev_dbg(dev, "vf%u: Set host VF LM status: %u", pds_vfio->vf_id,
37362306a36Sopenharmony_ci		vf_status);
37462306a36Sopenharmony_ci	if (vf_status != PDS_LM_STA_IN_PROGRESS &&
37562306a36Sopenharmony_ci	    vf_status != PDS_LM_STA_NONE) {
37662306a36Sopenharmony_ci		dev_warn(dev, "Invalid host VF migration status, %d\n",
37762306a36Sopenharmony_ci			 vf_status);
37862306a36Sopenharmony_ci		return;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false);
38262306a36Sopenharmony_ci	if (err)
38362306a36Sopenharmony_ci		dev_warn(dev, "failed to send host VF migration status: %pe\n",
38462306a36Sopenharmony_ci			 ERR_PTR(err));
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ciint pds_vfio_dirty_status_cmd(struct pds_vfio_pci_device *pds_vfio,
38862306a36Sopenharmony_ci			      u64 regions_dma, u8 *max_regions, u8 *num_regions)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
39162306a36Sopenharmony_ci		.lm_dirty_status = {
39262306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_DIRTY_STATUS,
39362306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
39462306a36Sopenharmony_ci		},
39562306a36Sopenharmony_ci	};
39662306a36Sopenharmony_ci	struct device *dev = pds_vfio_to_dev(pds_vfio);
39762306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
39862306a36Sopenharmony_ci	int err;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	dev_dbg(dev, "vf%u: Dirty status\n", pds_vfio->vf_id);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	cmd.lm_dirty_status.regions_dma = cpu_to_le64(regions_dma);
40362306a36Sopenharmony_ci	cmd.lm_dirty_status.max_regions = *max_regions;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false);
40662306a36Sopenharmony_ci	if (err) {
40762306a36Sopenharmony_ci		dev_err(dev, "failed to get dirty status: %pe\n", ERR_PTR(err));
40862306a36Sopenharmony_ci		return err;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/* only support seq_ack approach for now */
41262306a36Sopenharmony_ci	if (!(le32_to_cpu(comp.lm_dirty_status.bmp_type_mask) &
41362306a36Sopenharmony_ci	      BIT(PDS_LM_DIRTY_BMP_TYPE_SEQ_ACK))) {
41462306a36Sopenharmony_ci		dev_err(dev, "Dirty bitmap tracking SEQ_ACK not supported\n");
41562306a36Sopenharmony_ci		return -EOPNOTSUPP;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	*num_regions = comp.lm_dirty_status.num_regions;
41962306a36Sopenharmony_ci	*max_regions = comp.lm_dirty_status.max_regions;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	dev_dbg(dev,
42262306a36Sopenharmony_ci		"Page Tracking Status command successful, max_regions: %d, num_regions: %d, bmp_type: %s\n",
42362306a36Sopenharmony_ci		*max_regions, *num_regions, "PDS_LM_DIRTY_BMP_TYPE_SEQ_ACK");
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return 0;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ciint pds_vfio_dirty_enable_cmd(struct pds_vfio_pci_device *pds_vfio,
42962306a36Sopenharmony_ci			      u64 regions_dma, u8 num_regions)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
43262306a36Sopenharmony_ci		.lm_dirty_enable = {
43362306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_DIRTY_ENABLE,
43462306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
43562306a36Sopenharmony_ci			.regions_dma = cpu_to_le64(regions_dma),
43662306a36Sopenharmony_ci			.bmp_type = PDS_LM_DIRTY_BMP_TYPE_SEQ_ACK,
43762306a36Sopenharmony_ci			.num_regions = num_regions,
43862306a36Sopenharmony_ci		},
43962306a36Sopenharmony_ci	};
44062306a36Sopenharmony_ci	struct device *dev = pds_vfio_to_dev(pds_vfio);
44162306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
44262306a36Sopenharmony_ci	int err;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false);
44562306a36Sopenharmony_ci	if (err) {
44662306a36Sopenharmony_ci		dev_err(dev, "failed dirty tracking enable: %pe\n",
44762306a36Sopenharmony_ci			ERR_PTR(err));
44862306a36Sopenharmony_ci		return err;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return 0;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ciint pds_vfio_dirty_disable_cmd(struct pds_vfio_pci_device *pds_vfio)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
45762306a36Sopenharmony_ci		.lm_dirty_disable = {
45862306a36Sopenharmony_ci			.opcode = PDS_LM_CMD_DIRTY_DISABLE,
45962306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
46062306a36Sopenharmony_ci		},
46162306a36Sopenharmony_ci	};
46262306a36Sopenharmony_ci	struct device *dev = pds_vfio_to_dev(pds_vfio);
46362306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
46462306a36Sopenharmony_ci	int err;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false);
46762306a36Sopenharmony_ci	if (err || comp.lm_dirty_status.num_regions != 0) {
46862306a36Sopenharmony_ci		/* in case num_regions is still non-zero after disable */
46962306a36Sopenharmony_ci		err = err ? err : -EIO;
47062306a36Sopenharmony_ci		dev_err(dev,
47162306a36Sopenharmony_ci			"failed dirty tracking disable: %pe, num_regions %d\n",
47262306a36Sopenharmony_ci			ERR_PTR(err), comp.lm_dirty_status.num_regions);
47362306a36Sopenharmony_ci		return err;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return 0;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ciint pds_vfio_dirty_seq_ack_cmd(struct pds_vfio_pci_device *pds_vfio,
48062306a36Sopenharmony_ci			       u64 sgl_dma, u16 num_sge, u32 offset,
48162306a36Sopenharmony_ci			       u32 total_len, bool read_seq)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	const char *cmd_type_str = read_seq ? "read_seq" : "write_ack";
48462306a36Sopenharmony_ci	union pds_core_adminq_cmd cmd = {
48562306a36Sopenharmony_ci		.lm_dirty_seq_ack = {
48662306a36Sopenharmony_ci			.vf_id = cpu_to_le16(pds_vfio->vf_id),
48762306a36Sopenharmony_ci			.len_bytes = cpu_to_le32(total_len),
48862306a36Sopenharmony_ci			.off_bytes = cpu_to_le32(offset),
48962306a36Sopenharmony_ci			.sgl_addr = cpu_to_le64(sgl_dma),
49062306a36Sopenharmony_ci			.num_sge = cpu_to_le16(num_sge),
49162306a36Sopenharmony_ci		},
49262306a36Sopenharmony_ci	};
49362306a36Sopenharmony_ci	struct device *dev = pds_vfio_to_dev(pds_vfio);
49462306a36Sopenharmony_ci	union pds_core_adminq_comp comp = {};
49562306a36Sopenharmony_ci	int err;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (read_seq)
49862306a36Sopenharmony_ci		cmd.lm_dirty_seq_ack.opcode = PDS_LM_CMD_DIRTY_READ_SEQ;
49962306a36Sopenharmony_ci	else
50062306a36Sopenharmony_ci		cmd.lm_dirty_seq_ack.opcode = PDS_LM_CMD_DIRTY_WRITE_ACK;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	err = pds_vfio_client_adminq_cmd(pds_vfio, &cmd, &comp, false);
50362306a36Sopenharmony_ci	if (err) {
50462306a36Sopenharmony_ci		dev_err(dev, "failed cmd Page Tracking %s: %pe\n", cmd_type_str,
50562306a36Sopenharmony_ci			ERR_PTR(err));
50662306a36Sopenharmony_ci		return err;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return 0;
51062306a36Sopenharmony_ci}
511