aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/crypto/qat/qat_common/adf_pfvf_vf_proto.c
blob: 1015155b637493fb81c9720b72b1324873020457 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
// 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);