1341572Sslavash/*-
2341572Sslavash * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
3341572Sslavash *
4341572Sslavash * This software is available to you under a choice of one of two
5341572Sslavash * licenses.  You may choose to be licensed under the terms of the GNU
6341572Sslavash * General Public License (GPL) Version 2, available from the file
7341572Sslavash * COPYING in the main directory of this source tree, or the
8341572Sslavash * OpenIB.org BSD license below:
9341572Sslavash *
10341572Sslavash *     Redistribution and use in source and binary forms, with or
11341572Sslavash *     without modification, are permitted provided that the following
12341572Sslavash *     conditions are met:
13341572Sslavash *
14341572Sslavash *      - Redistributions of source code must retain the above
15341572Sslavash *        copyright notice, this list of conditions and the following
16341572Sslavash *        disclaimer.
17341572Sslavash *
18341572Sslavash *      - Redistributions in binary form must reproduce the above
19341572Sslavash *        copyright notice, this list of conditions and the following
20341572Sslavash *        disclaimer in the documentation and/or other materials
21341572Sslavash *        provided with the distribution.
22341572Sslavash *
23341572Sslavash * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24341572Sslavash * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25341572Sslavash * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26341572Sslavash * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27341572Sslavash * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28341572Sslavash * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29341572Sslavash * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30341572Sslavash * SOFTWARE.
31341572Sslavash *
32341572Sslavash * $FreeBSD: stable/11/sys/dev/mlx5/mlx5_fpga/mlx5fpga_trans.c 341572 2018-12-05 14:11:20Z slavash $
33341572Sslavash */
34341572Sslavash
35341572Sslavash#include <dev/mlx5/mlx5_fpga/trans.h>
36341572Sslavash#include <dev/mlx5/mlx5_fpga/conn.h>
37341572Sslavash
38341572Sslavashenum mlx5_fpga_transaction_state {
39341572Sslavash	TRANS_STATE_NONE,
40341572Sslavash	TRANS_STATE_SEND,
41341572Sslavash	TRANS_STATE_WAIT,
42341572Sslavash	TRANS_STATE_COMPLETE,
43341572Sslavash};
44341572Sslavash
45341572Sslavashstruct mlx5_fpga_trans_priv {
46341572Sslavash	const struct mlx5_fpga_transaction *user_trans;
47341572Sslavash	u8 tid;
48341572Sslavash	enum mlx5_fpga_transaction_state state;
49341572Sslavash	u8 status;
50341572Sslavash	u32 header[MLX5_ST_SZ_DW(fpga_shell_qp_packet)];
51341572Sslavash	struct mlx5_fpga_dma_buf buf;
52341572Sslavash	struct list_head list_item;
53341572Sslavash};
54341572Sslavash
55341572Sslavashstruct mlx5_fpga_trans_device_state {
56341572Sslavash	spinlock_t lock; /* Protects all members of this struct */
57341572Sslavash	struct list_head free_queue;
58341572Sslavash	struct mlx5_fpga_trans_priv transactions[MLX5_FPGA_TID_COUNT];
59341572Sslavash};
60341572Sslavash
61341572Sslavashstatic struct mlx5_fpga_trans_priv *find_tid(struct mlx5_fpga_device *fdev,
62341572Sslavash					     u8 tid)
63341572Sslavash{
64341572Sslavash	if (tid >= MLX5_FPGA_TID_COUNT) {
65341572Sslavash		mlx5_fpga_warn(fdev, "Unexpected transaction ID %u\n", tid);
66341572Sslavash		return NULL;
67341572Sslavash	}
68341572Sslavash	return &fdev->trans->transactions[tid];
69341572Sslavash}
70341572Sslavash
71341572Sslavashstatic struct mlx5_fpga_trans_priv *alloc_tid(struct mlx5_fpga_device *fdev)
72341572Sslavash{
73341572Sslavash	struct mlx5_fpga_trans_priv *ret;
74341572Sslavash	unsigned long flags;
75341572Sslavash
76341572Sslavash	spin_lock_irqsave(&fdev->trans->lock, flags);
77341572Sslavash
78341572Sslavash	if (list_empty(&fdev->trans->free_queue)) {
79341572Sslavash		mlx5_fpga_dbg(fdev, "No free transaction ID available\n");
80341572Sslavash		ret = NULL;
81341572Sslavash		goto out;
82341572Sslavash	}
83341572Sslavash
84341572Sslavash	ret = list_first_entry(&fdev->trans->free_queue,
85341572Sslavash			       struct mlx5_fpga_trans_priv, list_item);
86341572Sslavash	list_del(&ret->list_item);
87341572Sslavash
88341572Sslavash	ret->state = TRANS_STATE_NONE;
89341572Sslavashout:
90341572Sslavash	spin_unlock_irqrestore(&fdev->trans->lock, flags);
91341572Sslavash	return ret;
92341572Sslavash}
93341572Sslavash
94341572Sslavashstatic void free_tid(struct mlx5_fpga_device *fdev,
95341572Sslavash		     struct mlx5_fpga_trans_priv *trans_priv)
96341572Sslavash{
97341572Sslavash	unsigned long flags;
98341572Sslavash
99341572Sslavash	spin_lock_irqsave(&fdev->trans->lock, flags);
100341572Sslavash	list_add_tail(&trans_priv->list_item, &fdev->trans->free_queue);
101341572Sslavash	spin_unlock_irqrestore(&fdev->trans->lock, flags);
102341572Sslavash}
103341572Sslavash
104341572Sslavashstatic void trans_complete(struct mlx5_fpga_device *fdev,
105341572Sslavash			   struct mlx5_fpga_trans_priv *trans_priv, u8 status)
106341572Sslavash{
107341572Sslavash	const struct mlx5_fpga_transaction *user_trans;
108341572Sslavash	unsigned long flags;
109341572Sslavash
110341572Sslavash	mlx5_fpga_dbg(fdev, "Transaction %u is complete with status %u\n",
111341572Sslavash		      trans_priv->tid, status);
112341572Sslavash
113341572Sslavash	spin_lock_irqsave(&fdev->trans->lock, flags);
114341572Sslavash	trans_priv->state = TRANS_STATE_COMPLETE;
115341572Sslavash	trans_priv->status = status;
116341572Sslavash	spin_unlock_irqrestore(&fdev->trans->lock, flags);
117341572Sslavash
118341572Sslavash	user_trans = trans_priv->user_trans;
119341572Sslavash	free_tid(fdev, trans_priv);
120341572Sslavash
121341572Sslavash	if (user_trans->complete1)
122341572Sslavash		user_trans->complete1(user_trans, status);
123341572Sslavash}
124341572Sslavash
125341572Sslavashstatic void trans_send_complete(struct mlx5_fpga_conn *conn,
126341572Sslavash				struct mlx5_fpga_device *fdev,
127341572Sslavash				struct mlx5_fpga_dma_buf *buf, u8 status)
128341572Sslavash{
129341572Sslavash	unsigned long flags;
130341572Sslavash	struct mlx5_fpga_trans_priv *trans_priv;
131341572Sslavash
132341572Sslavash	trans_priv = container_of(buf, struct mlx5_fpga_trans_priv, buf);
133341572Sslavash	mlx5_fpga_dbg(fdev, "send complete tid %u. Status: %u\n",
134341572Sslavash		      trans_priv->tid, status);
135341572Sslavash	if (status) {
136341572Sslavash		trans_complete(fdev, trans_priv, status);
137341572Sslavash		return;
138341572Sslavash	}
139341572Sslavash
140341572Sslavash	spin_lock_irqsave(&fdev->trans->lock, flags);
141341572Sslavash	if (trans_priv->state == TRANS_STATE_SEND)
142341572Sslavash		trans_priv->state = TRANS_STATE_WAIT;
143341572Sslavash	spin_unlock_irqrestore(&fdev->trans->lock, flags);
144341572Sslavash}
145341572Sslavash
146341572Sslavashstatic int trans_validate(struct mlx5_fpga_device *fdev, u64 addr, size_t size)
147341572Sslavash{
148341572Sslavash	if (size > MLX5_FPGA_TRANSACTION_MAX_SIZE) {
149341572Sslavash		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at once. Max is %u\n",
150341572Sslavash			       size, MLX5_FPGA_TRANSACTION_MAX_SIZE);
151341572Sslavash		return -EINVAL;
152341572Sslavash	}
153341572Sslavash	if (size & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
154341572Sslavash		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Must be full dwords\n",
155341572Sslavash			       size);
156341572Sslavash		return -EINVAL;
157341572Sslavash	}
158341572Sslavash	if (size < 1) {
159341572Sslavash		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Empty transaction not allowed\n",
160341572Sslavash			       size);
161341572Sslavash		return -EINVAL;
162341572Sslavash	}
163341572Sslavash	if (addr & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
164341572Sslavash		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at unaligned address %jx\n",
165341572Sslavash			       size, (uintmax_t)addr);
166341572Sslavash		return -EINVAL;
167341572Sslavash	}
168341572Sslavash	if ((addr >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS) !=
169341572Sslavash	    ((addr + size - 1) >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS)) {
170341572Sslavash		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at address %jx. Crosses page boundary\n",
171341572Sslavash			       size, (uintmax_t)addr);
172341572Sslavash		return -EINVAL;
173341572Sslavash	}
174341572Sslavash	if (addr < mlx5_fpga_ddr_base_get(fdev)) {
175341572Sslavash		if (size != sizeof(u32)) {
176341572Sslavash			mlx5_fpga_warn(fdev, "Cannot access %zu bytes at cr-space address %jx. Must access a single dword\n",
177341572Sslavash				       size, (uintmax_t)addr);
178341572Sslavash			return -EINVAL;
179341572Sslavash		}
180341572Sslavash	}
181341572Sslavash	return 0;
182341572Sslavash}
183341572Sslavash
184341572Sslavashint mlx5_fpga_trans_exec(const struct mlx5_fpga_transaction *trans)
185341572Sslavash{
186341572Sslavash	struct mlx5_fpga_conn *conn = trans->conn;
187341572Sslavash	struct mlx5_fpga_trans_priv *trans_priv;
188341572Sslavash	u32 *header;
189341572Sslavash	int err;
190341572Sslavash
191341572Sslavash	if (!trans->complete1) {
192341572Sslavash		mlx5_fpga_warn(conn->fdev, "Transaction must have a completion callback\n");
193341572Sslavash		err = -EINVAL;
194341572Sslavash		goto out;
195341572Sslavash	}
196341572Sslavash
197341572Sslavash	err = trans_validate(conn->fdev, trans->addr, trans->size);
198341572Sslavash	if (err)
199341572Sslavash		goto out;
200341572Sslavash
201341572Sslavash	trans_priv = alloc_tid(conn->fdev);
202341572Sslavash	if (!trans_priv) {
203341572Sslavash		err = -EBUSY;
204341572Sslavash		goto out;
205341572Sslavash	}
206341572Sslavash	trans_priv->user_trans = trans;
207341572Sslavash	header = trans_priv->header;
208341572Sslavash
209341572Sslavash	memset(header, 0, sizeof(trans_priv->header));
210341572Sslavash	memset(&trans_priv->buf, 0, sizeof(trans_priv->buf));
211341572Sslavash	MLX5_SET(fpga_shell_qp_packet, header, type,
212341572Sslavash		 (trans->direction == MLX5_FPGA_WRITE) ?
213341572Sslavash		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE :
214341572Sslavash		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ);
215341572Sslavash	MLX5_SET(fpga_shell_qp_packet, header, tid, trans_priv->tid);
216341572Sslavash	MLX5_SET(fpga_shell_qp_packet, header, len, trans->size);
217341572Sslavash	MLX5_SET64(fpga_shell_qp_packet, header, address, trans->addr);
218341572Sslavash
219341572Sslavash	trans_priv->buf.sg[0].data = header;
220341572Sslavash	trans_priv->buf.sg[0].size = sizeof(trans_priv->header);
221341572Sslavash	if (trans->direction == MLX5_FPGA_WRITE) {
222341572Sslavash		trans_priv->buf.sg[1].data = trans->data;
223341572Sslavash		trans_priv->buf.sg[1].size = trans->size;
224341572Sslavash	}
225341572Sslavash
226341572Sslavash	trans_priv->buf.complete = trans_send_complete;
227341572Sslavash	trans_priv->state = TRANS_STATE_SEND;
228341572Sslavash
229341572Sslavash#ifdef NOT_YET
230341572Sslavash	/* XXXKIB */
231341572Sslavash	err = mlx5_fpga_conn_send(conn->fdev->shell_conn, &trans_priv->buf);
232341572Sslavash#else
233341572Sslavash	err = 0;
234341572Sslavash#endif
235341572Sslavash	if (err)
236341572Sslavash		goto out_buf_tid;
237341572Sslavash	goto out;
238341572Sslavash
239341572Sslavashout_buf_tid:
240341572Sslavash	free_tid(conn->fdev, trans_priv);
241341572Sslavashout:
242341572Sslavash	return err;
243341572Sslavash}
244341572Sslavash
245341572Sslavashvoid mlx5_fpga_trans_recv(void *cb_arg, struct mlx5_fpga_dma_buf *buf)
246341572Sslavash{
247341572Sslavash	struct mlx5_fpga_device *fdev = cb_arg;
248341572Sslavash	struct mlx5_fpga_trans_priv *trans_priv;
249341572Sslavash	size_t payload_len;
250341572Sslavash	u8 status = 0;
251341572Sslavash	u8 tid, type;
252341572Sslavash
253341572Sslavash	mlx5_fpga_dbg(fdev, "Rx QP message on core conn; %u bytes\n",
254341572Sslavash		      buf->sg[0].size);
255341572Sslavash
256341572Sslavash	if (buf->sg[0].size < MLX5_ST_SZ_BYTES(fpga_shell_qp_packet)) {
257341572Sslavash		mlx5_fpga_warn(fdev, "Short message %u bytes from device\n",
258341572Sslavash			       buf->sg[0].size);
259341572Sslavash		goto out;
260341572Sslavash	}
261341572Sslavash	payload_len = buf->sg[0].size - MLX5_ST_SZ_BYTES(fpga_shell_qp_packet);
262341572Sslavash
263341572Sslavash	tid = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, tid);
264341572Sslavash	trans_priv = find_tid(fdev, tid);
265341572Sslavash	if (!trans_priv)
266341572Sslavash		goto out;
267341572Sslavash
268341572Sslavash	type = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, type);
269341572Sslavash	switch (type) {
270341572Sslavash	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ_RESPONSE:
271341572Sslavash		if (trans_priv->user_trans->direction != MLX5_FPGA_READ) {
272341572Sslavash			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
273341572Sslavash				       type, trans_priv->user_trans->direction);
274341572Sslavash			status = -EIO;
275341572Sslavash			goto complete;
276341572Sslavash		}
277341572Sslavash		if (payload_len != trans_priv->user_trans->size) {
278341572Sslavash			mlx5_fpga_warn(fdev, "Incorrect transaction payload length %zu expected %zu\n",
279341572Sslavash				       payload_len,
280341572Sslavash				       trans_priv->user_trans->size);
281341572Sslavash			goto complete;
282341572Sslavash		}
283341572Sslavash		memcpy(trans_priv->user_trans->data,
284341572Sslavash		       MLX5_ADDR_OF(fpga_shell_qp_packet, buf->sg[0].data,
285341572Sslavash				    data), payload_len);
286341572Sslavash		break;
287341572Sslavash	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE_RESPONSE:
288341572Sslavash		if (trans_priv->user_trans->direction != MLX5_FPGA_WRITE) {
289341572Sslavash			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
290341572Sslavash				       type, trans_priv->user_trans->direction);
291341572Sslavash			status = -EIO;
292341572Sslavash			goto complete;
293341572Sslavash		}
294341572Sslavash		break;
295341572Sslavash	default:
296341572Sslavash		mlx5_fpga_warn(fdev, "Unexpected message type %u len %u from device\n",
297341572Sslavash			       type, buf->sg[0].size);
298341572Sslavash		status = -EIO;
299341572Sslavash		goto complete;
300341572Sslavash	}
301341572Sslavash
302341572Sslavashcomplete:
303341572Sslavash	trans_complete(fdev, trans_priv, status);
304341572Sslavashout:
305341572Sslavash	return;
306341572Sslavash}
307341572Sslavash
308341572Sslavashint mlx5_fpga_trans_device_init(struct mlx5_fpga_device *fdev)
309341572Sslavash{
310341572Sslavash	int ret = 0;
311341572Sslavash	int tid;
312341572Sslavash
313341572Sslavash	fdev->trans = kzalloc(sizeof(*fdev->trans), GFP_KERNEL);
314341572Sslavash	if (!fdev->trans) {
315341572Sslavash		ret = -ENOMEM;
316341572Sslavash		goto out;
317341572Sslavash	}
318341572Sslavash
319341572Sslavash	INIT_LIST_HEAD(&fdev->trans->free_queue);
320341572Sslavash	for (tid = 0; tid < ARRAY_SIZE(fdev->trans->transactions); tid++) {
321341572Sslavash		fdev->trans->transactions[tid].tid = tid;
322341572Sslavash		list_add_tail(&fdev->trans->transactions[tid].list_item,
323341572Sslavash			      &fdev->trans->free_queue);
324341572Sslavash	}
325341572Sslavash
326341572Sslavash	spin_lock_init(&fdev->trans->lock);
327341572Sslavash
328341572Sslavashout:
329341572Sslavash	return ret;
330341572Sslavash}
331341572Sslavash
332341572Sslavashvoid mlx5_fpga_trans_device_cleanup(struct mlx5_fpga_device *fdev)
333341572Sslavash{
334341572Sslavash	kfree(fdev->trans);
335341572Sslavash}
336