1/*-
2 * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses.  You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 *     Redistribution and use in source and binary forms, with or
11 *     without modification, are permitted provided that the following
12 *     conditions are met:
13 *
14 *      - Redistributions of source code must retain the above
15 *        copyright notice, this list of conditions and the following
16 *        disclaimer.
17 *
18 *      - Redistributions in binary form must reproduce the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer in the documentation and/or other materials
21 *        provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 *
32 * $FreeBSD$
33 */
34
35#include <dev/mlx5/mlx5_fpga/trans.h>
36#include <dev/mlx5/mlx5_fpga/conn.h>
37
38enum mlx5_fpga_transaction_state {
39	TRANS_STATE_NONE,
40	TRANS_STATE_SEND,
41	TRANS_STATE_WAIT,
42	TRANS_STATE_COMPLETE,
43};
44
45struct mlx5_fpga_trans_priv {
46	const struct mlx5_fpga_transaction *user_trans;
47	u8 tid;
48	enum mlx5_fpga_transaction_state state;
49	u8 status;
50	u32 header[MLX5_ST_SZ_DW(fpga_shell_qp_packet)];
51	struct mlx5_fpga_dma_buf buf;
52	struct list_head list_item;
53};
54
55struct mlx5_fpga_trans_device_state {
56	spinlock_t lock; /* Protects all members of this struct */
57	struct list_head free_queue;
58	struct mlx5_fpga_trans_priv transactions[MLX5_FPGA_TID_COUNT];
59};
60
61static struct mlx5_fpga_trans_priv *find_tid(struct mlx5_fpga_device *fdev,
62					     u8 tid)
63{
64	if (tid >= MLX5_FPGA_TID_COUNT) {
65		mlx5_fpga_warn(fdev, "Unexpected transaction ID %u\n", tid);
66		return NULL;
67	}
68	return &fdev->trans->transactions[tid];
69}
70
71static struct mlx5_fpga_trans_priv *alloc_tid(struct mlx5_fpga_device *fdev)
72{
73	struct mlx5_fpga_trans_priv *ret;
74	unsigned long flags;
75
76	spin_lock_irqsave(&fdev->trans->lock, flags);
77
78	if (list_empty(&fdev->trans->free_queue)) {
79		mlx5_fpga_dbg(fdev, "No free transaction ID available\n");
80		ret = NULL;
81		goto out;
82	}
83
84	ret = list_first_entry(&fdev->trans->free_queue,
85			       struct mlx5_fpga_trans_priv, list_item);
86	list_del(&ret->list_item);
87
88	ret->state = TRANS_STATE_NONE;
89out:
90	spin_unlock_irqrestore(&fdev->trans->lock, flags);
91	return ret;
92}
93
94static void free_tid(struct mlx5_fpga_device *fdev,
95		     struct mlx5_fpga_trans_priv *trans_priv)
96{
97	unsigned long flags;
98
99	spin_lock_irqsave(&fdev->trans->lock, flags);
100	list_add_tail(&trans_priv->list_item, &fdev->trans->free_queue);
101	spin_unlock_irqrestore(&fdev->trans->lock, flags);
102}
103
104static void trans_complete(struct mlx5_fpga_device *fdev,
105			   struct mlx5_fpga_trans_priv *trans_priv, u8 status)
106{
107	const struct mlx5_fpga_transaction *user_trans;
108	unsigned long flags;
109
110	mlx5_fpga_dbg(fdev, "Transaction %u is complete with status %u\n",
111		      trans_priv->tid, status);
112
113	spin_lock_irqsave(&fdev->trans->lock, flags);
114	trans_priv->state = TRANS_STATE_COMPLETE;
115	trans_priv->status = status;
116	spin_unlock_irqrestore(&fdev->trans->lock, flags);
117
118	user_trans = trans_priv->user_trans;
119	free_tid(fdev, trans_priv);
120
121	if (user_trans->complete1)
122		user_trans->complete1(user_trans, status);
123}
124
125static void trans_send_complete(struct mlx5_fpga_conn *conn,
126				struct mlx5_fpga_device *fdev,
127				struct mlx5_fpga_dma_buf *buf, u8 status)
128{
129	unsigned long flags;
130	struct mlx5_fpga_trans_priv *trans_priv;
131
132	trans_priv = container_of(buf, struct mlx5_fpga_trans_priv, buf);
133	mlx5_fpga_dbg(fdev, "send complete tid %u. Status: %u\n",
134		      trans_priv->tid, status);
135	if (status) {
136		trans_complete(fdev, trans_priv, status);
137		return;
138	}
139
140	spin_lock_irqsave(&fdev->trans->lock, flags);
141	if (trans_priv->state == TRANS_STATE_SEND)
142		trans_priv->state = TRANS_STATE_WAIT;
143	spin_unlock_irqrestore(&fdev->trans->lock, flags);
144}
145
146static int trans_validate(struct mlx5_fpga_device *fdev, u64 addr, size_t size)
147{
148	if (size > MLX5_FPGA_TRANSACTION_MAX_SIZE) {
149		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at once. Max is %u\n",
150			       size, MLX5_FPGA_TRANSACTION_MAX_SIZE);
151		return -EINVAL;
152	}
153	if (size & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
154		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Must be full dwords\n",
155			       size);
156		return -EINVAL;
157	}
158	if (size < 1) {
159		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Empty transaction not allowed\n",
160			       size);
161		return -EINVAL;
162	}
163	if (addr & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
164		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at unaligned address %jx\n",
165			       size, (uintmax_t)addr);
166		return -EINVAL;
167	}
168	if ((addr >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS) !=
169	    ((addr + size - 1) >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS)) {
170		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at address %jx. Crosses page boundary\n",
171			       size, (uintmax_t)addr);
172		return -EINVAL;
173	}
174	if (addr < mlx5_fpga_ddr_base_get(fdev)) {
175		if (size != sizeof(u32)) {
176			mlx5_fpga_warn(fdev, "Cannot access %zu bytes at cr-space address %jx. Must access a single dword\n",
177				       size, (uintmax_t)addr);
178			return -EINVAL;
179		}
180	}
181	return 0;
182}
183
184int mlx5_fpga_trans_exec(const struct mlx5_fpga_transaction *trans)
185{
186	struct mlx5_fpga_conn *conn = trans->conn;
187	struct mlx5_fpga_trans_priv *trans_priv;
188	u32 *header;
189	int err;
190
191	if (!trans->complete1) {
192		mlx5_fpga_warn(conn->fdev, "Transaction must have a completion callback\n");
193		err = -EINVAL;
194		goto out;
195	}
196
197	err = trans_validate(conn->fdev, trans->addr, trans->size);
198	if (err)
199		goto out;
200
201	trans_priv = alloc_tid(conn->fdev);
202	if (!trans_priv) {
203		err = -EBUSY;
204		goto out;
205	}
206	trans_priv->user_trans = trans;
207	header = trans_priv->header;
208
209	memset(header, 0, sizeof(trans_priv->header));
210	memset(&trans_priv->buf, 0, sizeof(trans_priv->buf));
211	MLX5_SET(fpga_shell_qp_packet, header, type,
212		 (trans->direction == MLX5_FPGA_WRITE) ?
213		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE :
214		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ);
215	MLX5_SET(fpga_shell_qp_packet, header, tid, trans_priv->tid);
216	MLX5_SET(fpga_shell_qp_packet, header, len, trans->size);
217	MLX5_SET64(fpga_shell_qp_packet, header, address, trans->addr);
218
219	trans_priv->buf.sg[0].data = header;
220	trans_priv->buf.sg[0].size = sizeof(trans_priv->header);
221	if (trans->direction == MLX5_FPGA_WRITE) {
222		trans_priv->buf.sg[1].data = trans->data;
223		trans_priv->buf.sg[1].size = trans->size;
224	}
225
226	trans_priv->buf.complete = trans_send_complete;
227	trans_priv->state = TRANS_STATE_SEND;
228
229#ifdef NOT_YET
230	/* XXXKIB */
231	err = mlx5_fpga_conn_send(conn->fdev->shell_conn, &trans_priv->buf);
232#else
233	err = 0;
234#endif
235	if (err)
236		goto out_buf_tid;
237	goto out;
238
239out_buf_tid:
240	free_tid(conn->fdev, trans_priv);
241out:
242	return err;
243}
244
245void mlx5_fpga_trans_recv(void *cb_arg, struct mlx5_fpga_dma_buf *buf)
246{
247	struct mlx5_fpga_device *fdev = cb_arg;
248	struct mlx5_fpga_trans_priv *trans_priv;
249	size_t payload_len;
250	u8 status = 0;
251	u8 tid, type;
252
253	mlx5_fpga_dbg(fdev, "Rx QP message on core conn; %u bytes\n",
254		      buf->sg[0].size);
255
256	if (buf->sg[0].size < MLX5_ST_SZ_BYTES(fpga_shell_qp_packet)) {
257		mlx5_fpga_warn(fdev, "Short message %u bytes from device\n",
258			       buf->sg[0].size);
259		goto out;
260	}
261	payload_len = buf->sg[0].size - MLX5_ST_SZ_BYTES(fpga_shell_qp_packet);
262
263	tid = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, tid);
264	trans_priv = find_tid(fdev, tid);
265	if (!trans_priv)
266		goto out;
267
268	type = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, type);
269	switch (type) {
270	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ_RESPONSE:
271		if (trans_priv->user_trans->direction != MLX5_FPGA_READ) {
272			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
273				       type, trans_priv->user_trans->direction);
274			status = -EIO;
275			goto complete;
276		}
277		if (payload_len != trans_priv->user_trans->size) {
278			mlx5_fpga_warn(fdev, "Incorrect transaction payload length %zu expected %zu\n",
279				       payload_len,
280				       trans_priv->user_trans->size);
281			goto complete;
282		}
283		memcpy(trans_priv->user_trans->data,
284		       MLX5_ADDR_OF(fpga_shell_qp_packet, buf->sg[0].data,
285				    data), payload_len);
286		break;
287	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE_RESPONSE:
288		if (trans_priv->user_trans->direction != MLX5_FPGA_WRITE) {
289			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
290				       type, trans_priv->user_trans->direction);
291			status = -EIO;
292			goto complete;
293		}
294		break;
295	default:
296		mlx5_fpga_warn(fdev, "Unexpected message type %u len %u from device\n",
297			       type, buf->sg[0].size);
298		status = -EIO;
299		goto complete;
300	}
301
302complete:
303	trans_complete(fdev, trans_priv, status);
304out:
305	return;
306}
307
308int mlx5_fpga_trans_device_init(struct mlx5_fpga_device *fdev)
309{
310	int ret = 0;
311	int tid;
312
313	fdev->trans = kzalloc(sizeof(*fdev->trans), GFP_KERNEL);
314	if (!fdev->trans) {
315		ret = -ENOMEM;
316		goto out;
317	}
318
319	INIT_LIST_HEAD(&fdev->trans->free_queue);
320	for (tid = 0; tid < ARRAY_SIZE(fdev->trans->transactions); tid++) {
321		fdev->trans->transactions[tid].tid = tid;
322		list_add_tail(&fdev->trans->transactions[tid].list_item,
323			      &fdev->trans->free_queue);
324	}
325
326	spin_lock_init(&fdev->trans->lock);
327
328out:
329	return ret;
330}
331
332void mlx5_fpga_trans_device_cleanup(struct mlx5_fpga_device *fdev)
333{
334	kfree(fdev->trans);
335}
336