Extend support for resetting ring pairs on the device to VFs. Such reset happens by sending a request to the PF over the PFVF protocol. This patch defines two new PFVF messages and adds the PFVF logic for handling the request on PF, triggering the reset, and VFs, accepting the 'success'/'error' response. This feature is GEN4 specific. This patch is based on earlier work done by Zelin Deng. Signed-off-by: Marco Chiappero <marco.chiappero@intel.com> Reviewed-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com> Reviewed-by: Fiona Trahe <fiona.trahe@intel.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
368 lines
10 KiB
C
368 lines
10 KiB
C
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
|
|
/* Copyright(c) 2015 - 2021 Intel Corporation */
|
|
#include <linux/bitfield.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/minmax.h>
|
|
#include <linux/types.h>
|
|
#include "adf_accel_devices.h"
|
|
#include "adf_common_drv.h"
|
|
#include "adf_pfvf_msg.h"
|
|
#include "adf_pfvf_utils.h"
|
|
#include "adf_pfvf_vf_msg.h"
|
|
#include "adf_pfvf_vf_proto.h"
|
|
|
|
#define ADF_PFVF_MSG_COLLISION_DETECT_DELAY 10
|
|
#define ADF_PFVF_MSG_ACK_DELAY 2
|
|
#define ADF_PFVF_MSG_ACK_MAX_RETRY 100
|
|
|
|
/* How often to retry if there is no response */
|
|
#define ADF_PFVF_MSG_RESP_RETRIES 5
|
|
#define ADF_PFVF_MSG_RESP_TIMEOUT (ADF_PFVF_MSG_ACK_DELAY * \
|
|
ADF_PFVF_MSG_ACK_MAX_RETRY + \
|
|
ADF_PFVF_MSG_COLLISION_DETECT_DELAY)
|
|
|
|
/**
|
|
* adf_send_vf2pf_msg() - send VF to PF message
|
|
* @accel_dev: Pointer to acceleration device
|
|
* @msg: Message to send
|
|
*
|
|
* This function allows the VF to send a message to the PF.
|
|
*
|
|
* Return: 0 on success, error code otherwise.
|
|
*/
|
|
int adf_send_vf2pf_msg(struct adf_accel_dev *accel_dev, struct pfvf_message msg)
|
|
{
|
|
struct adf_pfvf_ops *pfvf_ops = GET_PFVF_OPS(accel_dev);
|
|
u32 pfvf_offset = pfvf_ops->get_vf2pf_offset(0);
|
|
|
|
return pfvf_ops->send_msg(accel_dev, msg, pfvf_offset,
|
|
&accel_dev->vf.vf2pf_lock);
|
|
}
|
|
|
|
/**
|
|
* adf_recv_pf2vf_msg() - receive a PF to VF message
|
|
* @accel_dev: Pointer to acceleration device
|
|
*
|
|
* This function allows the VF to receive a message from the PF.
|
|
*
|
|
* Return: a valid message on success, zero otherwise.
|
|
*/
|
|
static struct pfvf_message adf_recv_pf2vf_msg(struct adf_accel_dev *accel_dev)
|
|
{
|
|
struct adf_pfvf_ops *pfvf_ops = GET_PFVF_OPS(accel_dev);
|
|
u32 pfvf_offset = pfvf_ops->get_pf2vf_offset(0);
|
|
|
|
return pfvf_ops->recv_msg(accel_dev, pfvf_offset, accel_dev->vf.pf_compat_ver);
|
|
}
|
|
|
|
/**
|
|
* adf_send_vf2pf_req() - send VF2PF request message
|
|
* @accel_dev: Pointer to acceleration device.
|
|
* @msg: Request message to send
|
|
* @resp: Returned PF response
|
|
*
|
|
* This function sends a message that requires a response from the VF to the PF
|
|
* and waits for a reply.
|
|
*
|
|
* Return: 0 on success, error code otherwise.
|
|
*/
|
|
int adf_send_vf2pf_req(struct adf_accel_dev *accel_dev, struct pfvf_message msg,
|
|
struct pfvf_message *resp)
|
|
{
|
|
unsigned long timeout = msecs_to_jiffies(ADF_PFVF_MSG_RESP_TIMEOUT);
|
|
unsigned int retries = ADF_PFVF_MSG_RESP_RETRIES;
|
|
int ret;
|
|
|
|
reinit_completion(&accel_dev->vf.msg_received);
|
|
|
|
/* Send request from VF to PF */
|
|
do {
|
|
ret = adf_send_vf2pf_msg(accel_dev, msg);
|
|
if (ret) {
|
|
dev_err(&GET_DEV(accel_dev),
|
|
"Failed to send request msg to PF\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Wait for response, if it times out retry */
|
|
ret = wait_for_completion_timeout(&accel_dev->vf.msg_received,
|
|
timeout);
|
|
if (ret) {
|
|
if (likely(resp))
|
|
*resp = accel_dev->vf.response;
|
|
|
|
/* Once copied, set to an invalid value */
|
|
accel_dev->vf.response.type = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
dev_err(&GET_DEV(accel_dev), "PFVF response message timeout\n");
|
|
} while (--retries);
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int adf_vf2pf_blkmsg_data_req(struct adf_accel_dev *accel_dev, bool crc,
|
|
u8 *type, u8 *data)
|
|
{
|
|
struct pfvf_message req = { 0 };
|
|
struct pfvf_message resp = { 0 };
|
|
u8 blk_type;
|
|
u8 blk_byte;
|
|
u8 msg_type;
|
|
u8 max_data;
|
|
int err;
|
|
|
|
/* Convert the block type to {small, medium, large} size category */
|
|
if (*type <= ADF_VF2PF_SMALL_BLOCK_TYPE_MAX) {
|
|
msg_type = ADF_VF2PF_MSGTYPE_SMALL_BLOCK_REQ;
|
|
blk_type = FIELD_PREP(ADF_VF2PF_SMALL_BLOCK_TYPE_MASK, *type);
|
|
blk_byte = FIELD_PREP(ADF_VF2PF_SMALL_BLOCK_BYTE_MASK, *data);
|
|
max_data = ADF_VF2PF_SMALL_BLOCK_BYTE_MAX;
|
|
} else if (*type <= ADF_VF2PF_MEDIUM_BLOCK_TYPE_MAX) {
|
|
msg_type = ADF_VF2PF_MSGTYPE_MEDIUM_BLOCK_REQ;
|
|
blk_type = FIELD_PREP(ADF_VF2PF_MEDIUM_BLOCK_TYPE_MASK,
|
|
*type - ADF_VF2PF_SMALL_BLOCK_TYPE_MAX);
|
|
blk_byte = FIELD_PREP(ADF_VF2PF_MEDIUM_BLOCK_BYTE_MASK, *data);
|
|
max_data = ADF_VF2PF_MEDIUM_BLOCK_BYTE_MAX;
|
|
} else if (*type <= ADF_VF2PF_LARGE_BLOCK_TYPE_MAX) {
|
|
msg_type = ADF_VF2PF_MSGTYPE_LARGE_BLOCK_REQ;
|
|
blk_type = FIELD_PREP(ADF_VF2PF_LARGE_BLOCK_TYPE_MASK,
|
|
*type - ADF_VF2PF_MEDIUM_BLOCK_TYPE_MAX);
|
|
blk_byte = FIELD_PREP(ADF_VF2PF_LARGE_BLOCK_BYTE_MASK, *data);
|
|
max_data = ADF_VF2PF_LARGE_BLOCK_BYTE_MAX;
|
|
} else {
|
|
dev_err(&GET_DEV(accel_dev), "Invalid message type %u\n", *type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Sanity check */
|
|
if (*data > max_data) {
|
|
dev_err(&GET_DEV(accel_dev),
|
|
"Invalid byte %s %u for message type %u\n",
|
|
crc ? "count" : "index", *data, *type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Build the block message */
|
|
req.type = msg_type;
|
|
req.data = blk_type | blk_byte | FIELD_PREP(ADF_VF2PF_BLOCK_CRC_REQ_MASK, crc);
|
|
|
|
err = adf_send_vf2pf_req(accel_dev, req, &resp);
|
|
if (err)
|
|
return err;
|
|
|
|
*type = FIELD_GET(ADF_PF2VF_BLKMSG_RESP_TYPE_MASK, resp.data);
|
|
*data = FIELD_GET(ADF_PF2VF_BLKMSG_RESP_DATA_MASK, resp.data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adf_vf2pf_blkmsg_get_byte(struct adf_accel_dev *accel_dev, u8 type,
|
|
u8 index, u8 *data)
|
|
{
|
|
int ret;
|
|
|
|
ret = adf_vf2pf_blkmsg_data_req(accel_dev, false, &type, &index);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (unlikely(type != ADF_PF2VF_BLKMSG_RESP_TYPE_DATA)) {
|
|
dev_err(&GET_DEV(accel_dev),
|
|
"Unexpected BLKMSG response type %u, byte 0x%x\n",
|
|
type, index);
|
|
return -EFAULT;
|
|
}
|
|
|
|
*data = index;
|
|
return 0;
|
|
}
|
|
|
|
static int adf_vf2pf_blkmsg_get_crc(struct adf_accel_dev *accel_dev, u8 type,
|
|
u8 bytes, u8 *crc)
|
|
{
|
|
int ret;
|
|
|
|
/* The count of bytes refers to a length, however shift it to a 0-based
|
|
* count to avoid overflows. Thus, a request for 0 bytes is technically
|
|
* valid.
|
|
*/
|
|
--bytes;
|
|
|
|
ret = adf_vf2pf_blkmsg_data_req(accel_dev, true, &type, &bytes);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (unlikely(type != ADF_PF2VF_BLKMSG_RESP_TYPE_CRC)) {
|
|
dev_err(&GET_DEV(accel_dev),
|
|
"Unexpected CRC BLKMSG response type %u, crc 0x%x\n",
|
|
type, bytes);
|
|
return -EFAULT;
|
|
}
|
|
|
|
*crc = bytes;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* adf_send_vf2pf_blkmsg_req() - retrieve block message
|
|
* @accel_dev: Pointer to acceleration VF device.
|
|
* @type: The block message type, see adf_pfvf_msg.h for allowed values
|
|
* @buffer: input buffer where to place the received data
|
|
* @buffer_len: buffer length as input, the amount of written bytes on output
|
|
*
|
|
* Request a message of type 'type' over the block message transport.
|
|
* This function will send the required amount block message requests and
|
|
* return the overall content back to the caller through the provided buffer.
|
|
* The buffer should be large enough to contain the requested message type,
|
|
* otherwise the response will be truncated.
|
|
*
|
|
* Return: 0 on success, error code otherwise.
|
|
*/
|
|
int adf_send_vf2pf_blkmsg_req(struct adf_accel_dev *accel_dev, u8 type,
|
|
u8 *buffer, unsigned int *buffer_len)
|
|
{
|
|
unsigned int index;
|
|
unsigned int msg_len;
|
|
int ret;
|
|
u8 remote_crc;
|
|
u8 local_crc;
|
|
|
|
if (unlikely(type > ADF_VF2PF_LARGE_BLOCK_TYPE_MAX)) {
|
|
dev_err(&GET_DEV(accel_dev), "Invalid block message type %d\n",
|
|
type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unlikely(*buffer_len < ADF_PFVF_BLKMSG_HEADER_SIZE)) {
|
|
dev_err(&GET_DEV(accel_dev),
|
|
"Buffer size too small for a block message\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = adf_vf2pf_blkmsg_get_byte(accel_dev, type,
|
|
ADF_PFVF_BLKMSG_VER_BYTE,
|
|
&buffer[ADF_PFVF_BLKMSG_VER_BYTE]);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
if (unlikely(!buffer[ADF_PFVF_BLKMSG_VER_BYTE])) {
|
|
dev_err(&GET_DEV(accel_dev),
|
|
"Invalid version 0 received for block request %u", type);
|
|
return -EFAULT;
|
|
}
|
|
|
|
ret = adf_vf2pf_blkmsg_get_byte(accel_dev, type,
|
|
ADF_PFVF_BLKMSG_LEN_BYTE,
|
|
&buffer[ADF_PFVF_BLKMSG_LEN_BYTE]);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
if (unlikely(!buffer[ADF_PFVF_BLKMSG_LEN_BYTE])) {
|
|
dev_err(&GET_DEV(accel_dev),
|
|
"Invalid size 0 received for block request %u", type);
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* We need to pick the minimum since there is no way to request a
|
|
* specific version. As a consequence any scenario is possible:
|
|
* - PF has a newer (longer) version which doesn't fit in the buffer
|
|
* - VF expects a newer (longer) version, so we must not ask for
|
|
* bytes in excess
|
|
* - PF and VF share the same version, no problem
|
|
*/
|
|
msg_len = ADF_PFVF_BLKMSG_HEADER_SIZE + buffer[ADF_PFVF_BLKMSG_LEN_BYTE];
|
|
msg_len = min(*buffer_len, msg_len);
|
|
|
|
/* Get the payload */
|
|
for (index = ADF_PFVF_BLKMSG_HEADER_SIZE; index < msg_len; index++) {
|
|
ret = adf_vf2pf_blkmsg_get_byte(accel_dev, type, index,
|
|
&buffer[index]);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
}
|
|
|
|
ret = adf_vf2pf_blkmsg_get_crc(accel_dev, type, msg_len, &remote_crc);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
local_crc = adf_pfvf_calc_blkmsg_crc(buffer, msg_len);
|
|
if (unlikely(local_crc != remote_crc)) {
|
|
dev_err(&GET_DEV(accel_dev),
|
|
"CRC error on msg type %d. Local %02X, remote %02X\n",
|
|
type, local_crc, remote_crc);
|
|
return -EIO;
|
|
}
|
|
|
|
*buffer_len = msg_len;
|
|
return 0;
|
|
}
|
|
|
|
static bool adf_handle_pf2vf_msg(struct adf_accel_dev *accel_dev,
|
|
struct pfvf_message msg)
|
|
{
|
|
switch (msg.type) {
|
|
case ADF_PF2VF_MSGTYPE_RESTARTING:
|
|
dev_dbg(&GET_DEV(accel_dev), "Restarting message received from PF\n");
|
|
|
|
adf_pf2vf_handle_pf_restarting(accel_dev);
|
|
return false;
|
|
case ADF_PF2VF_MSGTYPE_VERSION_RESP:
|
|
case ADF_PF2VF_MSGTYPE_BLKMSG_RESP:
|
|
case ADF_PF2VF_MSGTYPE_RP_RESET_RESP:
|
|
dev_dbg(&GET_DEV(accel_dev),
|
|
"Response Message received from PF (type 0x%.4x, data 0x%.4x)\n",
|
|
msg.type, msg.data);
|
|
accel_dev->vf.response = msg;
|
|
complete(&accel_dev->vf.msg_received);
|
|
return true;
|
|
default:
|
|
dev_err(&GET_DEV(accel_dev),
|
|
"Unknown message from PF (type 0x%.4x, data: 0x%.4x)\n",
|
|
msg.type, msg.data);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool adf_recv_and_handle_pf2vf_msg(struct adf_accel_dev *accel_dev)
|
|
{
|
|
struct pfvf_message msg;
|
|
|
|
msg = adf_recv_pf2vf_msg(accel_dev);
|
|
if (msg.type) /* Invalid or no message */
|
|
return adf_handle_pf2vf_msg(accel_dev, msg);
|
|
|
|
/* No replies for PF->VF messages at present */
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* adf_enable_vf2pf_comms() - Function enables communication from vf to pf
|
|
*
|
|
* @accel_dev: Pointer to acceleration device virtual function.
|
|
*
|
|
* Return: 0 on success, error code otherwise.
|
|
*/
|
|
int adf_enable_vf2pf_comms(struct adf_accel_dev *accel_dev)
|
|
{
|
|
int ret;
|
|
|
|
adf_pfvf_crc_init();
|
|
adf_enable_pf2vf_interrupts(accel_dev);
|
|
|
|
ret = adf_vf2pf_request_version(accel_dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = adf_vf2pf_get_capabilities(accel_dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = adf_vf2pf_get_ring_to_svc(accel_dev);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(adf_enable_vf2pf_comms);
|