// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2024 Intel Corporation */
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
#include <asm/errno.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "adf_gen4_hw_data.h"
#include "adf_gen4_pfvf.h"
#include "adf_pfvf_utils.h"
#include "adf_mstate_mgr.h"
#include "adf_gen4_vf_mig.h"
#define ADF_GEN4_VF_MSTATE_SIZE 4096
#define ADF_GEN4_PFVF_RSP_TIMEOUT_US 5000
static int adf_gen4_vfmig_save_setup(struct qat_mig_dev *mdev);
static int adf_gen4_vfmig_load_setup(struct qat_mig_dev *mdev, int len);
static int adf_gen4_vfmig_init_device(struct qat_mig_dev *mdev)
{
u8 *state;
state = kmalloc(ADF_GEN4_VF_MSTATE_SIZE, GFP_KERNEL);
if (!state)
return -ENOMEM;
mdev->state = state;
mdev->state_size = ADF_GEN4_VF_MSTATE_SIZE;
mdev->setup_size = 0;
mdev->remote_setup_size = 0;
return 0;
}
static void adf_gen4_vfmig_cleanup_device(struct qat_mig_dev *mdev)
{
kfree(mdev->state);
mdev->state = NULL;
}
static void adf_gen4_vfmig_reset_device(struct qat_mig_dev *mdev)
{
mdev->setup_size = 0;
mdev->remote_setup_size = 0;
}
static int adf_gen4_vfmig_open_device(struct qat_mig_dev *mdev)
{
struct adf_accel_dev *accel_dev = mdev->parent_accel_dev;
struct adf_accel_vf_info *vf_info;
struct adf_gen4_vfmig *vfmig;
vf_info = &accel_dev->pf.vf_info[mdev->vf_id];
vfmig = kzalloc(sizeof(*vfmig), GFP_KERNEL);
if (!vfmig)
return -ENOMEM;
vfmig->mstate_mgr = adf_mstate_mgr_new(mdev->state, mdev->state_size);
if (!vfmig->mstate_mgr) {
kfree(vfmig);
return -ENOMEM;
}
vf_info->mig_priv = vfmig;
mdev->setup_size = 0;
mdev->remote_setup_size = 0;
return 0;
}
static void adf_gen4_vfmig_close_device(struct qat_mig_dev *mdev)
{
struct adf_accel_dev *accel_dev = mdev->parent_accel_dev;
struct adf_accel_vf_info *vf_info;
struct adf_gen4_vfmig *vfmig;
vf_info = &accel_dev->pf.vf_info[mdev->vf_id];
if (vf_info->mig_priv) {
vfmig = vf_info->mig_priv;
adf_mstate_mgr_destroy(vfmig->mstate_mgr);
kfree(vfmig);
vf_info->mig_priv = NULL;
}
}
static int adf_gen4_vfmig_suspend_device(struct qat_mig_dev *mdev)
{
struct adf_accel_dev *accel_dev = mdev->parent_accel_dev;
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
struct adf_accel_vf_info *vf_info;
struct adf_gen4_vfmig *vf_mig;
u32 vf_nr = mdev->vf_id;
int ret, i;
vf_info = &accel_dev->pf.vf_info[vf_nr];
vf_mig = vf_info->mig_priv;
/* Stop all inflight jobs */
for (i = 0; i < hw_data->num_banks_per_vf; i++) {
u32 pf_bank_nr = i + vf_nr * hw_data->num_banks_per_vf;
ret = adf_gen4_bank_drain_start(accel_dev, pf_bank_nr,
ADF_RPRESET_POLL_TIMEOUT_US);
if (ret) {
dev_err(&GET_DEV(accel_dev),
"Failed to drain bank %d for vf_nr %d\n", i,
vf_nr);
return ret;
}
vf_mig->bank_stopped[i] = true;
adf_gen4_bank_quiesce_coal_timer(accel_dev, pf_bank_nr,
ADF_COALESCED_POLL_TIMEOUT_US);
}
return 0;
}
static int adf_gen4_vfmig_resume_device(struct qat_mig_dev *mdev)
{
struct adf_accel_dev *accel_dev = mdev->parent_accel_dev;
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
struct adf_accel_vf_info *vf_info;
struct adf_gen4_vfmig *vf_mig;
u32 vf_nr = mdev->vf_id;
int i;
vf_info = &accel_dev->pf.vf_info[vf_nr];
vf_mig = vf_info->mig_priv;
for (i = 0; i < hw_data->num_banks_per_vf; i++) {
u32 pf_bank_nr = i + vf_nr * hw_data->num_banks_per_vf;
if (vf_mig->bank_stopped[i]) {
adf_gen4_bank_drain_finish(accel_dev, pf_bank_nr);
vf_mig->bank_stopped[i] = false;
}
}
return 0;
}
struct adf_vf_bank_info {
struct adf_accel_dev *accel_dev;
u32 vf_nr;
u32 bank_nr;
};
struct mig_user_sla {
enum adf_base_services srv;
u64 rp_mask;
u32 cir;
u32 pir;
};
static int adf_mstate_sla_check(struct adf_mstate_mgr *sub_mgr, u8 *src_buf,
u32 src_size, void *opaque)
{
struct adf_mstate_vreginfo _sinfo = { src_buf, src_size };
struct adf_mstate_vreginfo *sinfo = &_sinfo, *dinfo = opaque;
u32 src_sla_cnt = sinfo->size / sizeof(struct mig_user_sla);
u32 dst_sla_cnt = dinfo->size / sizeof(struct mig_user_sla);
struct mig_user_sla *src_slas = sinfo->addr;
struct mig_user_sla *dst_slas = dinfo->addr;
int i, j;
for (