Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
  2/* Copyright(c) 2021 Intel Corporation */
  3#include <linux/delay.h>
  4#include <linux/iopoll.h>
  5#include <linux/mutex.h>
  6#include <linux/types.h>
  7#include "adf_accel_devices.h"
  8#include "adf_common_drv.h"
  9#include "adf_gen2_pfvf.h"
 10#include "adf_pfvf_msg.h"
 11#include "adf_pfvf_pf_proto.h"
 12#include "adf_pfvf_vf_proto.h"
 13#include "adf_pfvf_utils.h"
 14
 15 /* VF2PF interrupts */
 16#define ADF_GEN2_VF_MSK			0xFFFF
 17#define ADF_GEN2_ERR_REG_VF2PF(vf_src)	(((vf_src) & 0x01FFFE00) >> 9)
 18#define ADF_GEN2_ERR_MSK_VF2PF(vf_mask)	(((vf_mask) & ADF_GEN2_VF_MSK) << 9)
 19
 20#define ADF_GEN2_PF_PF2VF_OFFSET(i)	(0x3A000 + 0x280 + ((i) * 0x04))
 21#define ADF_GEN2_VF_PF2VF_OFFSET	0x200
 22
 23#define ADF_GEN2_CSR_IN_USE		0x6AC2
 24#define ADF_GEN2_CSR_IN_USE_MASK	0xFFFE
 25
 26enum gen2_csr_pos {
 27	ADF_GEN2_CSR_PF2VF_OFFSET	=  0,
 28	ADF_GEN2_CSR_VF2PF_OFFSET	= 16,
 29};
 30
 31#define ADF_PFVF_GEN2_MSGTYPE_SHIFT	2
 32#define ADF_PFVF_GEN2_MSGTYPE_MASK	0x0F
 33#define ADF_PFVF_GEN2_MSGDATA_SHIFT	6
 34#define ADF_PFVF_GEN2_MSGDATA_MASK	0x3FF
 35
 36static const struct pfvf_csr_format csr_gen2_fmt = {
 37	{ ADF_PFVF_GEN2_MSGTYPE_SHIFT, ADF_PFVF_GEN2_MSGTYPE_MASK },
 38	{ ADF_PFVF_GEN2_MSGDATA_SHIFT, ADF_PFVF_GEN2_MSGDATA_MASK },
 39};
 40
 41#define ADF_PFVF_MSG_RETRY_DELAY	5
 42#define ADF_PFVF_MSG_MAX_RETRIES	3
 43
 44static u32 adf_gen2_pf_get_pfvf_offset(u32 i)
 45{
 46	return ADF_GEN2_PF_PF2VF_OFFSET(i);
 47}
 48
 49static u32 adf_gen2_vf_get_pfvf_offset(u32 i)
 50{
 51	return ADF_GEN2_VF_PF2VF_OFFSET;
 52}
 53
 54static void adf_gen2_enable_vf2pf_interrupts(void __iomem *pmisc_addr, u32 vf_mask)
 55{
 56	/* Enable VF2PF Messaging Ints - VFs 0 through 15 per vf_mask[15:0] */
 57	if (vf_mask & ADF_GEN2_VF_MSK) {
 58		u32 val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRMSK3)
 59			  & ~ADF_GEN2_ERR_MSK_VF2PF(vf_mask);
 60		ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, val);
 61	}
 62}
 63
 64static void adf_gen2_disable_all_vf2pf_interrupts(void __iomem *pmisc_addr)
 65{
 66	/* Disable VF2PF interrupts for VFs 0 through 15 per vf_mask[15:0] */
 67	u32 val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRMSK3)
 68		  | ADF_GEN2_ERR_MSK_VF2PF(ADF_GEN2_VF_MSK);
 69	ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, val);
 70}
 71
 72static u32 adf_gen2_disable_pending_vf2pf_interrupts(void __iomem *pmisc_addr)
 73{
 74	u32 sources, disabled, pending;
 75	u32 errsou3, errmsk3;
 76
 77	/* Get the interrupt sources triggered by VFs */
 78	errsou3 = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRSOU3);
 79	sources = ADF_GEN2_ERR_REG_VF2PF(errsou3);
 80
 81	if (!sources)
 82		return 0;
 83
 84	/* Get the already disabled interrupts */
 85	errmsk3 = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRMSK3);
 86	disabled = ADF_GEN2_ERR_REG_VF2PF(errmsk3);
 87
 88	pending = sources & ~disabled;
 89	if (!pending)
 90		return 0;
 91
 92	/* Due to HW limitations, when disabling the interrupts, we can't
 93	 * just disable the requested sources, as this would lead to missed
 94	 * interrupts if ERRSOU3 changes just before writing to ERRMSK3.
 95	 * To work around it, disable all and re-enable only the sources that
 96	 * are not in vf_mask and were not already disabled. Re-enabling will
 97	 * trigger a new interrupt for the sources that have changed in the
 98	 * meantime, if any.
 99	 */
100	errmsk3 |= ADF_GEN2_ERR_MSK_VF2PF(ADF_GEN2_VF_MSK);
101	ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, errmsk3);
102
103	errmsk3 &= ADF_GEN2_ERR_MSK_VF2PF(sources | disabled);
 
 
104	ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, errmsk3);
105
106	/* Return the sources of the (new) interrupt(s) */
107	return pending;
108}
109
110static u32 gen2_csr_get_int_bit(enum gen2_csr_pos offset)
111{
112	return ADF_PFVF_INT << offset;
113}
114
115static u32 gen2_csr_msg_to_position(u32 csr_msg, enum gen2_csr_pos offset)
116{
117	return (csr_msg & 0xFFFF) << offset;
118}
119
120static u32 gen2_csr_msg_from_position(u32 csr_val, enum gen2_csr_pos offset)
121{
122	return (csr_val >> offset) & 0xFFFF;
123}
124
125static bool gen2_csr_is_in_use(u32 msg, enum gen2_csr_pos offset)
126{
127	return ((msg >> offset) & ADF_GEN2_CSR_IN_USE_MASK) == ADF_GEN2_CSR_IN_USE;
128}
129
130static void gen2_csr_clear_in_use(u32 *msg, enum gen2_csr_pos offset)
131{
132	*msg &= ~(ADF_GEN2_CSR_IN_USE_MASK << offset);
133}
134
135static void gen2_csr_set_in_use(u32 *msg, enum gen2_csr_pos offset)
136{
137	*msg |= (ADF_GEN2_CSR_IN_USE << offset);
138}
139
140static bool is_legacy_user_pfvf_message(u32 msg)
141{
142	return !(msg & ADF_PFVF_MSGORIGIN_SYSTEM);
143}
144
145static bool is_pf2vf_notification(u8 msg_type)
146{
147	switch (msg_type) {
148	case ADF_PF2VF_MSGTYPE_RESTARTING:
149		return true;
150	default:
151		return false;
152	}
153}
154
155static bool is_vf2pf_notification(u8 msg_type)
156{
157	switch (msg_type) {
158	case ADF_VF2PF_MSGTYPE_INIT:
159	case ADF_VF2PF_MSGTYPE_SHUTDOWN:
160		return true;
161	default:
162		return false;
163	}
164}
165
166struct pfvf_gen2_params {
167	u32 pfvf_offset;
168	struct mutex *csr_lock; /* lock preventing concurrent access of CSR */
169	enum gen2_csr_pos local_offset;
170	enum gen2_csr_pos remote_offset;
171	bool (*is_notification_message)(u8 msg_type);
172	u8 compat_ver;
173};
174
175static int adf_gen2_pfvf_send(struct adf_accel_dev *accel_dev,
176			      struct pfvf_message msg,
177			      struct pfvf_gen2_params *params)
178{
179	void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
180	enum gen2_csr_pos remote_offset = params->remote_offset;
181	enum gen2_csr_pos local_offset = params->local_offset;
182	unsigned int retries = ADF_PFVF_MSG_MAX_RETRIES;
183	struct mutex *lock = params->csr_lock;
184	u32 pfvf_offset = params->pfvf_offset;
185	u32 int_bit;
186	u32 csr_val;
187	u32 csr_msg;
188	int ret;
189
190	/* Gen2 messages, both PF->VF and VF->PF, are all 16 bits long. This
191	 * allows us to build and read messages as if they where all 0 based.
192	 * However, send and receive are in a single shared 32 bits register,
193	 * so we need to shift and/or mask the message half before decoding
194	 * it and after encoding it. Which one to shift depends on the
195	 * direction.
196	 */
197
198	int_bit = gen2_csr_get_int_bit(local_offset);
199
200	csr_msg = adf_pfvf_csr_msg_of(accel_dev, msg, &csr_gen2_fmt);
201	if (unlikely(!csr_msg))
202		return -EINVAL;
203
204	/* Prepare for CSR format, shifting the wire message in place and
205	 * setting the in use pattern
206	 */
207	csr_msg = gen2_csr_msg_to_position(csr_msg, local_offset);
208	gen2_csr_set_in_use(&csr_msg, remote_offset);
209
210	mutex_lock(lock);
211
212start:
213	/* Check if the PFVF CSR is in use by remote function */
214	csr_val = ADF_CSR_RD(pmisc_addr, pfvf_offset);
215	if (gen2_csr_is_in_use(csr_val, local_offset)) {
216		dev_dbg(&GET_DEV(accel_dev),
217			"PFVF CSR in use by remote function\n");
218		goto retry;
219	}
220
221	/* Attempt to get ownership of the PFVF CSR */
222	ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_msg | int_bit);
223
224	/* Wait for confirmation from remote func it received the message */
225	ret = read_poll_timeout(ADF_CSR_RD, csr_val, !(csr_val & int_bit),
226				ADF_PFVF_MSG_ACK_DELAY_US,
227				ADF_PFVF_MSG_ACK_MAX_DELAY_US,
228				true, pmisc_addr, pfvf_offset);
229	if (unlikely(ret < 0)) {
230		dev_dbg(&GET_DEV(accel_dev), "ACK not received from remote\n");
231		csr_val &= ~int_bit;
232	}
233
234	/* For fire-and-forget notifications, the receiver does not clear
235	 * the in-use pattern. This is used to detect collisions.
236	 */
237	if (params->is_notification_message(msg.type) && csr_val != csr_msg) {
238		/* Collision must have overwritten the message */
239		dev_err(&GET_DEV(accel_dev),
240			"Collision on notification - PFVF CSR overwritten by remote function\n");
241		goto retry;
242	}
243
244	/* If the far side did not clear the in-use pattern it is either
245	 * 1) Notification - message left intact to detect collision
246	 * 2) Older protocol (compatibility version < 3) on the far side
247	 *    where the sender is responsible for clearing the in-use
248	 *    pattern after the received has acknowledged receipt.
249	 * In either case, clear the in-use pattern now.
250	 */
251	if (gen2_csr_is_in_use(csr_val, remote_offset)) {
252		gen2_csr_clear_in_use(&csr_val, remote_offset);
253		ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val);
254	}
255
256out:
257	mutex_unlock(lock);
258	return ret;
259
260retry:
261	if (--retries) {
262		msleep(ADF_PFVF_MSG_RETRY_DELAY);
263		goto start;
264	} else {
265		ret = -EBUSY;
266		goto out;
267	}
268}
269
270static struct pfvf_message adf_gen2_pfvf_recv(struct adf_accel_dev *accel_dev,
271					      struct pfvf_gen2_params *params)
272{
273	void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
274	enum gen2_csr_pos remote_offset = params->remote_offset;
275	enum gen2_csr_pos local_offset = params->local_offset;
276	u32 pfvf_offset = params->pfvf_offset;
277	struct pfvf_message msg = { 0 };
278	u32 int_bit;
279	u32 csr_val;
280	u16 csr_msg;
281
282	int_bit = gen2_csr_get_int_bit(local_offset);
283
284	/* Read message */
285	csr_val = ADF_CSR_RD(pmisc_addr, pfvf_offset);
286	if (!(csr_val & int_bit)) {
287		dev_info(&GET_DEV(accel_dev),
288			 "Spurious PFVF interrupt, msg 0x%.8x. Ignored\n", csr_val);
289		return msg;
290	}
291
292	/* Extract the message from the CSR */
293	csr_msg = gen2_csr_msg_from_position(csr_val, local_offset);
294
295	/* Ignore legacy non-system (non-kernel) messages */
296	if (unlikely(is_legacy_user_pfvf_message(csr_msg))) {
297		dev_dbg(&GET_DEV(accel_dev),
298			"Ignored non-system message (0x%.8x);\n", csr_val);
299		/* Because this must be a legacy message, the far side
300		 * must clear the in-use pattern, so don't do it.
301		 */
302		return msg;
303	}
304
305	/* Return the pfvf_message format */
306	msg = adf_pfvf_message_of(accel_dev, csr_msg, &csr_gen2_fmt);
307
308	/* The in-use pattern is not cleared for notifications (so that
309	 * it can be used for collision detection) or older implementations
310	 */
311	if (params->compat_ver >= ADF_PFVF_COMPAT_FAST_ACK &&
312	    !params->is_notification_message(msg.type))
313		gen2_csr_clear_in_use(&csr_val, remote_offset);
314
315	/* To ACK, clear the INT bit */
316	csr_val &= ~int_bit;
317	ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val);
318
319	return msg;
320}
321
322static int adf_gen2_pf2vf_send(struct adf_accel_dev *accel_dev, struct pfvf_message msg,
323			       u32 pfvf_offset, struct mutex *csr_lock)
324{
325	struct pfvf_gen2_params params = {
326		.csr_lock = csr_lock,
327		.pfvf_offset = pfvf_offset,
328		.local_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
329		.remote_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
330		.is_notification_message = is_pf2vf_notification,
331	};
332
333	return adf_gen2_pfvf_send(accel_dev, msg, &params);
334}
335
336static int adf_gen2_vf2pf_send(struct adf_accel_dev *accel_dev, struct pfvf_message msg,
337			       u32 pfvf_offset, struct mutex *csr_lock)
338{
339	struct pfvf_gen2_params params = {
340		.csr_lock = csr_lock,
341		.pfvf_offset = pfvf_offset,
342		.local_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
343		.remote_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
344		.is_notification_message = is_vf2pf_notification,
345	};
346
347	return adf_gen2_pfvf_send(accel_dev, msg, &params);
348}
349
350static struct pfvf_message adf_gen2_pf2vf_recv(struct adf_accel_dev *accel_dev,
351					       u32 pfvf_offset, u8 compat_ver)
352{
353	struct pfvf_gen2_params params = {
354		.pfvf_offset = pfvf_offset,
355		.local_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
356		.remote_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
357		.is_notification_message = is_pf2vf_notification,
358		.compat_ver = compat_ver,
359	};
360
361	return adf_gen2_pfvf_recv(accel_dev, &params);
362}
363
364static struct pfvf_message adf_gen2_vf2pf_recv(struct adf_accel_dev *accel_dev,
365					       u32 pfvf_offset, u8 compat_ver)
366{
367	struct pfvf_gen2_params params = {
368		.pfvf_offset = pfvf_offset,
369		.local_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
370		.remote_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
371		.is_notification_message = is_vf2pf_notification,
372		.compat_ver = compat_ver,
373	};
374
375	return adf_gen2_pfvf_recv(accel_dev, &params);
376}
377
378void adf_gen2_init_pf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
379{
380	pfvf_ops->enable_comms = adf_enable_pf2vf_comms;
381	pfvf_ops->get_pf2vf_offset = adf_gen2_pf_get_pfvf_offset;
382	pfvf_ops->get_vf2pf_offset = adf_gen2_pf_get_pfvf_offset;
383	pfvf_ops->enable_vf2pf_interrupts = adf_gen2_enable_vf2pf_interrupts;
384	pfvf_ops->disable_all_vf2pf_interrupts = adf_gen2_disable_all_vf2pf_interrupts;
385	pfvf_ops->disable_pending_vf2pf_interrupts = adf_gen2_disable_pending_vf2pf_interrupts;
386	pfvf_ops->send_msg = adf_gen2_pf2vf_send;
387	pfvf_ops->recv_msg = adf_gen2_vf2pf_recv;
388}
389EXPORT_SYMBOL_GPL(adf_gen2_init_pf_pfvf_ops);
390
391void adf_gen2_init_vf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
392{
393	pfvf_ops->enable_comms = adf_enable_vf2pf_comms;
394	pfvf_ops->get_pf2vf_offset = adf_gen2_vf_get_pfvf_offset;
395	pfvf_ops->get_vf2pf_offset = adf_gen2_vf_get_pfvf_offset;
396	pfvf_ops->send_msg = adf_gen2_vf2pf_send;
397	pfvf_ops->recv_msg = adf_gen2_pf2vf_recv;
398}
399EXPORT_SYMBOL_GPL(adf_gen2_init_vf_pfvf_ops);
v6.13.7
  1// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
  2/* Copyright(c) 2021 Intel Corporation */
  3#include <linux/delay.h>
  4#include <linux/iopoll.h>
  5#include <linux/mutex.h>
  6#include <linux/types.h>
  7#include "adf_accel_devices.h"
  8#include "adf_common_drv.h"
  9#include "adf_gen2_pfvf.h"
 10#include "adf_pfvf_msg.h"
 11#include "adf_pfvf_pf_proto.h"
 12#include "adf_pfvf_vf_proto.h"
 13#include "adf_pfvf_utils.h"
 14
 15 /* VF2PF interrupts */
 16#define ADF_GEN2_VF_MSK			0xFFFF
 17#define ADF_GEN2_ERR_REG_VF2PF(vf_src)	(((vf_src) & 0x01FFFE00) >> 9)
 18#define ADF_GEN2_ERR_MSK_VF2PF(vf_mask)	(((vf_mask) & ADF_GEN2_VF_MSK) << 9)
 19
 20#define ADF_GEN2_PF_PF2VF_OFFSET(i)	(0x3A000 + 0x280 + ((i) * 0x04))
 21#define ADF_GEN2_VF_PF2VF_OFFSET	0x200
 22
 23#define ADF_GEN2_CSR_IN_USE		0x6AC2
 24#define ADF_GEN2_CSR_IN_USE_MASK	0xFFFE
 25
 26enum gen2_csr_pos {
 27	ADF_GEN2_CSR_PF2VF_OFFSET	=  0,
 28	ADF_GEN2_CSR_VF2PF_OFFSET	= 16,
 29};
 30
 31#define ADF_PFVF_GEN2_MSGTYPE_SHIFT	2
 32#define ADF_PFVF_GEN2_MSGTYPE_MASK	0x0F
 33#define ADF_PFVF_GEN2_MSGDATA_SHIFT	6
 34#define ADF_PFVF_GEN2_MSGDATA_MASK	0x3FF
 35
 36static const struct pfvf_csr_format csr_gen2_fmt = {
 37	{ ADF_PFVF_GEN2_MSGTYPE_SHIFT, ADF_PFVF_GEN2_MSGTYPE_MASK },
 38	{ ADF_PFVF_GEN2_MSGDATA_SHIFT, ADF_PFVF_GEN2_MSGDATA_MASK },
 39};
 40
 41#define ADF_PFVF_MSG_RETRY_DELAY	5
 42#define ADF_PFVF_MSG_MAX_RETRIES	3
 43
 44static u32 adf_gen2_pf_get_pfvf_offset(u32 i)
 45{
 46	return ADF_GEN2_PF_PF2VF_OFFSET(i);
 47}
 48
 49static u32 adf_gen2_vf_get_pfvf_offset(u32 i)
 50{
 51	return ADF_GEN2_VF_PF2VF_OFFSET;
 52}
 53
 54static void adf_gen2_enable_vf2pf_interrupts(void __iomem *pmisc_addr, u32 vf_mask)
 55{
 56	/* Enable VF2PF Messaging Ints - VFs 0 through 15 per vf_mask[15:0] */
 57	if (vf_mask & ADF_GEN2_VF_MSK) {
 58		u32 val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRMSK3)
 59			  & ~ADF_GEN2_ERR_MSK_VF2PF(vf_mask);
 60		ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, val);
 61	}
 62}
 63
 64static void adf_gen2_disable_all_vf2pf_interrupts(void __iomem *pmisc_addr)
 65{
 66	/* Disable VF2PF interrupts for VFs 0 through 15 per vf_mask[15:0] */
 67	u32 val = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRMSK3)
 68		  | ADF_GEN2_ERR_MSK_VF2PF(ADF_GEN2_VF_MSK);
 69	ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, val);
 70}
 71
 72static u32 adf_gen2_disable_pending_vf2pf_interrupts(void __iomem *pmisc_addr)
 73{
 74	u32 sources, disabled, pending;
 75	u32 errsou3, errmsk3;
 76
 77	/* Get the interrupt sources triggered by VFs */
 78	errsou3 = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRSOU3);
 79	sources = ADF_GEN2_ERR_REG_VF2PF(errsou3);
 80
 81	if (!sources)
 82		return 0;
 83
 84	/* Get the already disabled interrupts */
 85	errmsk3 = ADF_CSR_RD(pmisc_addr, ADF_GEN2_ERRMSK3);
 86	disabled = ADF_GEN2_ERR_REG_VF2PF(errmsk3);
 87
 88	pending = sources & ~disabled;
 89	if (!pending)
 90		return 0;
 91
 92	/* Due to HW limitations, when disabling the interrupts, we can't
 93	 * just disable the requested sources, as this would lead to missed
 94	 * interrupts if ERRSOU3 changes just before writing to ERRMSK3.
 95	 * To work around it, disable all and re-enable only the sources that
 96	 * are not in vf_mask and were not already disabled. Re-enabling will
 97	 * trigger a new interrupt for the sources that have changed in the
 98	 * meantime, if any.
 99	 */
100	errmsk3 |= ADF_GEN2_ERR_MSK_VF2PF(ADF_GEN2_VF_MSK);
101	ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, errmsk3);
102
103	/* Update only section of errmsk3 related to VF2PF */
104	errmsk3 &= ~ADF_GEN2_ERR_MSK_VF2PF(ADF_GEN2_VF_MSK);
105	errmsk3 |= ADF_GEN2_ERR_MSK_VF2PF(sources | disabled);
106	ADF_CSR_WR(pmisc_addr, ADF_GEN2_ERRMSK3, errmsk3);
107
108	/* Return the sources of the (new) interrupt(s) */
109	return pending;
110}
111
112static u32 gen2_csr_get_int_bit(enum gen2_csr_pos offset)
113{
114	return ADF_PFVF_INT << offset;
115}
116
117static u32 gen2_csr_msg_to_position(u32 csr_msg, enum gen2_csr_pos offset)
118{
119	return (csr_msg & 0xFFFF) << offset;
120}
121
122static u32 gen2_csr_msg_from_position(u32 csr_val, enum gen2_csr_pos offset)
123{
124	return (csr_val >> offset) & 0xFFFF;
125}
126
127static bool gen2_csr_is_in_use(u32 msg, enum gen2_csr_pos offset)
128{
129	return ((msg >> offset) & ADF_GEN2_CSR_IN_USE_MASK) == ADF_GEN2_CSR_IN_USE;
130}
131
132static void gen2_csr_clear_in_use(u32 *msg, enum gen2_csr_pos offset)
133{
134	*msg &= ~(ADF_GEN2_CSR_IN_USE_MASK << offset);
135}
136
137static void gen2_csr_set_in_use(u32 *msg, enum gen2_csr_pos offset)
138{
139	*msg |= (ADF_GEN2_CSR_IN_USE << offset);
140}
141
142static bool is_legacy_user_pfvf_message(u32 msg)
143{
144	return !(msg & ADF_PFVF_MSGORIGIN_SYSTEM);
145}
146
147static bool is_pf2vf_notification(u8 msg_type)
148{
149	switch (msg_type) {
150	case ADF_PF2VF_MSGTYPE_RESTARTING:
151		return true;
152	default:
153		return false;
154	}
155}
156
157static bool is_vf2pf_notification(u8 msg_type)
158{
159	switch (msg_type) {
160	case ADF_VF2PF_MSGTYPE_INIT:
161	case ADF_VF2PF_MSGTYPE_SHUTDOWN:
162		return true;
163	default:
164		return false;
165	}
166}
167
168struct pfvf_gen2_params {
169	u32 pfvf_offset;
170	struct mutex *csr_lock; /* lock preventing concurrent access of CSR */
171	enum gen2_csr_pos local_offset;
172	enum gen2_csr_pos remote_offset;
173	bool (*is_notification_message)(u8 msg_type);
174	u8 compat_ver;
175};
176
177static int adf_gen2_pfvf_send(struct adf_accel_dev *accel_dev,
178			      struct pfvf_message msg,
179			      struct pfvf_gen2_params *params)
180{
181	void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
182	enum gen2_csr_pos remote_offset = params->remote_offset;
183	enum gen2_csr_pos local_offset = params->local_offset;
184	unsigned int retries = ADF_PFVF_MSG_MAX_RETRIES;
185	struct mutex *lock = params->csr_lock;
186	u32 pfvf_offset = params->pfvf_offset;
187	u32 int_bit;
188	u32 csr_val;
189	u32 csr_msg;
190	int ret;
191
192	/* Gen2 messages, both PF->VF and VF->PF, are all 16 bits long. This
193	 * allows us to build and read messages as if they where all 0 based.
194	 * However, send and receive are in a single shared 32 bits register,
195	 * so we need to shift and/or mask the message half before decoding
196	 * it and after encoding it. Which one to shift depends on the
197	 * direction.
198	 */
199
200	int_bit = gen2_csr_get_int_bit(local_offset);
201
202	csr_msg = adf_pfvf_csr_msg_of(accel_dev, msg, &csr_gen2_fmt);
203	if (unlikely(!csr_msg))
204		return -EINVAL;
205
206	/* Prepare for CSR format, shifting the wire message in place and
207	 * setting the in use pattern
208	 */
209	csr_msg = gen2_csr_msg_to_position(csr_msg, local_offset);
210	gen2_csr_set_in_use(&csr_msg, remote_offset);
211
212	mutex_lock(lock);
213
214start:
215	/* Check if the PFVF CSR is in use by remote function */
216	csr_val = ADF_CSR_RD(pmisc_addr, pfvf_offset);
217	if (gen2_csr_is_in_use(csr_val, local_offset)) {
218		dev_dbg(&GET_DEV(accel_dev),
219			"PFVF CSR in use by remote function\n");
220		goto retry;
221	}
222
223	/* Attempt to get ownership of the PFVF CSR */
224	ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_msg | int_bit);
225
226	/* Wait for confirmation from remote func it received the message */
227	ret = read_poll_timeout(ADF_CSR_RD, csr_val, !(csr_val & int_bit),
228				ADF_PFVF_MSG_ACK_DELAY_US,
229				ADF_PFVF_MSG_ACK_MAX_DELAY_US,
230				true, pmisc_addr, pfvf_offset);
231	if (unlikely(ret < 0)) {
232		dev_dbg(&GET_DEV(accel_dev), "ACK not received from remote\n");
233		csr_val &= ~int_bit;
234	}
235
236	/* For fire-and-forget notifications, the receiver does not clear
237	 * the in-use pattern. This is used to detect collisions.
238	 */
239	if (params->is_notification_message(msg.type) && csr_val != csr_msg) {
240		/* Collision must have overwritten the message */
241		dev_err(&GET_DEV(accel_dev),
242			"Collision on notification - PFVF CSR overwritten by remote function\n");
243		goto retry;
244	}
245
246	/* If the far side did not clear the in-use pattern it is either
247	 * 1) Notification - message left intact to detect collision
248	 * 2) Older protocol (compatibility version < 3) on the far side
249	 *    where the sender is responsible for clearing the in-use
250	 *    pattern after the received has acknowledged receipt.
251	 * In either case, clear the in-use pattern now.
252	 */
253	if (gen2_csr_is_in_use(csr_val, remote_offset)) {
254		gen2_csr_clear_in_use(&csr_val, remote_offset);
255		ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val);
256	}
257
258out:
259	mutex_unlock(lock);
260	return ret;
261
262retry:
263	if (--retries) {
264		msleep(ADF_PFVF_MSG_RETRY_DELAY);
265		goto start;
266	} else {
267		ret = -EBUSY;
268		goto out;
269	}
270}
271
272static struct pfvf_message adf_gen2_pfvf_recv(struct adf_accel_dev *accel_dev,
273					      struct pfvf_gen2_params *params)
274{
275	void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
276	enum gen2_csr_pos remote_offset = params->remote_offset;
277	enum gen2_csr_pos local_offset = params->local_offset;
278	u32 pfvf_offset = params->pfvf_offset;
279	struct pfvf_message msg = { 0 };
280	u32 int_bit;
281	u32 csr_val;
282	u16 csr_msg;
283
284	int_bit = gen2_csr_get_int_bit(local_offset);
285
286	/* Read message */
287	csr_val = ADF_CSR_RD(pmisc_addr, pfvf_offset);
288	if (!(csr_val & int_bit)) {
289		dev_info(&GET_DEV(accel_dev),
290			 "Spurious PFVF interrupt, msg 0x%.8x. Ignored\n", csr_val);
291		return msg;
292	}
293
294	/* Extract the message from the CSR */
295	csr_msg = gen2_csr_msg_from_position(csr_val, local_offset);
296
297	/* Ignore legacy non-system (non-kernel) messages */
298	if (unlikely(is_legacy_user_pfvf_message(csr_msg))) {
299		dev_dbg(&GET_DEV(accel_dev),
300			"Ignored non-system message (0x%.8x);\n", csr_val);
301		/* Because this must be a legacy message, the far side
302		 * must clear the in-use pattern, so don't do it.
303		 */
304		return msg;
305	}
306
307	/* Return the pfvf_message format */
308	msg = adf_pfvf_message_of(accel_dev, csr_msg, &csr_gen2_fmt);
309
310	/* The in-use pattern is not cleared for notifications (so that
311	 * it can be used for collision detection) or older implementations
312	 */
313	if (params->compat_ver >= ADF_PFVF_COMPAT_FAST_ACK &&
314	    !params->is_notification_message(msg.type))
315		gen2_csr_clear_in_use(&csr_val, remote_offset);
316
317	/* To ACK, clear the INT bit */
318	csr_val &= ~int_bit;
319	ADF_CSR_WR(pmisc_addr, pfvf_offset, csr_val);
320
321	return msg;
322}
323
324static int adf_gen2_pf2vf_send(struct adf_accel_dev *accel_dev, struct pfvf_message msg,
325			       u32 pfvf_offset, struct mutex *csr_lock)
326{
327	struct pfvf_gen2_params params = {
328		.csr_lock = csr_lock,
329		.pfvf_offset = pfvf_offset,
330		.local_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
331		.remote_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
332		.is_notification_message = is_pf2vf_notification,
333	};
334
335	return adf_gen2_pfvf_send(accel_dev, msg, &params);
336}
337
338static int adf_gen2_vf2pf_send(struct adf_accel_dev *accel_dev, struct pfvf_message msg,
339			       u32 pfvf_offset, struct mutex *csr_lock)
340{
341	struct pfvf_gen2_params params = {
342		.csr_lock = csr_lock,
343		.pfvf_offset = pfvf_offset,
344		.local_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
345		.remote_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
346		.is_notification_message = is_vf2pf_notification,
347	};
348
349	return adf_gen2_pfvf_send(accel_dev, msg, &params);
350}
351
352static struct pfvf_message adf_gen2_pf2vf_recv(struct adf_accel_dev *accel_dev,
353					       u32 pfvf_offset, u8 compat_ver)
354{
355	struct pfvf_gen2_params params = {
356		.pfvf_offset = pfvf_offset,
357		.local_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
358		.remote_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
359		.is_notification_message = is_pf2vf_notification,
360		.compat_ver = compat_ver,
361	};
362
363	return adf_gen2_pfvf_recv(accel_dev, &params);
364}
365
366static struct pfvf_message adf_gen2_vf2pf_recv(struct adf_accel_dev *accel_dev,
367					       u32 pfvf_offset, u8 compat_ver)
368{
369	struct pfvf_gen2_params params = {
370		.pfvf_offset = pfvf_offset,
371		.local_offset = ADF_GEN2_CSR_VF2PF_OFFSET,
372		.remote_offset = ADF_GEN2_CSR_PF2VF_OFFSET,
373		.is_notification_message = is_vf2pf_notification,
374		.compat_ver = compat_ver,
375	};
376
377	return adf_gen2_pfvf_recv(accel_dev, &params);
378}
379
380void adf_gen2_init_pf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
381{
382	pfvf_ops->enable_comms = adf_enable_pf2vf_comms;
383	pfvf_ops->get_pf2vf_offset = adf_gen2_pf_get_pfvf_offset;
384	pfvf_ops->get_vf2pf_offset = adf_gen2_pf_get_pfvf_offset;
385	pfvf_ops->enable_vf2pf_interrupts = adf_gen2_enable_vf2pf_interrupts;
386	pfvf_ops->disable_all_vf2pf_interrupts = adf_gen2_disable_all_vf2pf_interrupts;
387	pfvf_ops->disable_pending_vf2pf_interrupts = adf_gen2_disable_pending_vf2pf_interrupts;
388	pfvf_ops->send_msg = adf_gen2_pf2vf_send;
389	pfvf_ops->recv_msg = adf_gen2_vf2pf_recv;
390}
391EXPORT_SYMBOL_GPL(adf_gen2_init_pf_pfvf_ops);
392
393void adf_gen2_init_vf_pfvf_ops(struct adf_pfvf_ops *pfvf_ops)
394{
395	pfvf_ops->enable_comms = adf_enable_vf2pf_comms;
396	pfvf_ops->get_pf2vf_offset = adf_gen2_vf_get_pfvf_offset;
397	pfvf_ops->get_vf2pf_offset = adf_gen2_vf_get_pfvf_offset;
398	pfvf_ops->send_msg = adf_gen2_vf2pf_send;
399	pfvf_ops->recv_msg = adf_gen2_pf2vf_recv;
400}
401EXPORT_SYMBOL_GPL(adf_gen2_init_vf_pfvf_ops);