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
33#include <dev/mlx5/mlx5_fpga/trans.h>
34#include <dev/mlx5/mlx5_fpga/conn.h>
35
36enum mlx5_fpga_transaction_state {
37	TRANS_STATE_NONE,
38	TRANS_STATE_SEND,
39	TRANS_STATE_WAIT,
40	TRANS_STATE_COMPLETE,
41};
42
43struct mlx5_fpga_trans_priv {
44	const struct mlx5_fpga_transaction *user_trans;
45	u8 tid;
46	enum mlx5_fpga_transaction_state state;
47	u8 status;
48	u32 header[MLX5_ST_SZ_DW(fpga_shell_qp_packet)];
49	struct mlx5_fpga_dma_buf buf;
50	struct list_head list_item;
51};
52
53struct mlx5_fpga_trans_device_state {
54	spinlock_t lock; /* Protects all members of this struct */
55	struct list_head free_queue;
56	struct mlx5_fpga_trans_priv transactions[MLX5_FPGA_TID_COUNT];
57};
58
59static struct mlx5_fpga_trans_priv *find_tid(struct mlx5_fpga_device *fdev,
60					     u8 tid)
61{
62	if (tid >= MLX5_FPGA_TID_COUNT) {
63		mlx5_fpga_warn(fdev, "Unexpected transaction ID %u\n", tid);
64		return NULL;
65	}
66	return &fdev->trans->transactions[tid];
67}
68
69static struct mlx5_fpga_trans_priv *alloc_tid(struct mlx5_fpga_device *fdev)
70{
71	struct mlx5_fpga_trans_priv *ret;
72	unsigned long flags;
73
74	spin_lock_irqsave(&fdev->trans->lock, flags);
75
76	if (list_empty(&fdev->trans->free_queue)) {
77		mlx5_fpga_dbg(fdev, "No free transaction ID available\n");
78		ret = NULL;
79		goto out;
80	}
81
82	ret = list_first_entry(&fdev->trans->free_queue,
83			       struct mlx5_fpga_trans_priv, list_item);
84	list_del(&ret->list_item);
85
86	ret->state = TRANS_STATE_NONE;
87out:
88	spin_unlock_irqrestore(&fdev->trans->lock, flags);
89	return ret;
90}
91
92static void free_tid(struct mlx5_fpga_device *fdev,
93		     struct mlx5_fpga_trans_priv *trans_priv)
94{
95	unsigned long flags;
96
97	spin_lock_irqsave(&fdev->trans->lock, flags);
98	list_add_tail(&trans_priv->list_item, &fdev->trans->free_queue);
99	spin_unlock_irqrestore(&fdev->trans->lock, flags);
100}
101
102static void trans_complete(struct mlx5_fpga_device *fdev,
103			   struct mlx5_fpga_trans_priv *trans_priv, u8 status)
104{
105	const struct mlx5_fpga_transaction *user_trans;
106	unsigned long flags;
107
108	mlx5_fpga_dbg(fdev, "Transaction %u is complete with status %u\n",
109		      trans_priv->tid, status);
110
111	spin_lock_irqsave(&fdev->trans->lock, flags);
112	trans_priv->state = TRANS_STATE_COMPLETE;
113	trans_priv->status = status;
114	spin_unlock_irqrestore(&fdev->trans->lock, flags);
115
116	user_trans = trans_priv->user_trans;
117	free_tid(fdev, trans_priv);
118
119	if (user_trans->complete1)
120		user_trans->complete1(user_trans, status);
121}
122
123static void trans_send_complete(struct mlx5_fpga_conn *conn,
124				struct mlx5_fpga_device *fdev,
125				struct mlx5_fpga_dma_buf *buf, u8 status)
126{
127	unsigned long flags;
128	struct mlx5_fpga_trans_priv *trans_priv;
129
130	trans_priv = container_of(buf, struct mlx5_fpga_trans_priv, buf);
131	mlx5_fpga_dbg(fdev, "send complete tid %u. Status: %u\n",
132		      trans_priv->tid, status);
133	if (status) {
134		trans_complete(fdev, trans_priv, status);
135		return;
136	}
137
138	spin_lock_irqsave(&fdev->trans->lock, flags);
139	if (trans_priv->state == TRANS_STATE_SEND)
140		trans_priv->state = TRANS_STATE_WAIT;
141	spin_unlock_irqrestore(&fdev->trans->lock, flags);
142}
143
144static int trans_validate(struct mlx5_fpga_device *fdev, u64 addr, size_t size)
145{
146	if (size > MLX5_FPGA_TRANSACTION_MAX_SIZE) {
147		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at once. Max is %u\n",
148			       size, MLX5_FPGA_TRANSACTION_MAX_SIZE);
149		return -EINVAL;
150	}
151	if (size & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
152		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Must be full dwords\n",
153			       size);
154		return -EINVAL;
155	}
156	if (size < 1) {
157		mlx5_fpga_warn(fdev, "Cannot access %zu bytes. Empty transaction not allowed\n",
158			       size);
159		return -EINVAL;
160	}
161	if (addr & MLX5_FPGA_TRANSACTION_SEND_ALIGN_BITS) {
162		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at unaligned address %jx\n",
163			       size, (uintmax_t)addr);
164		return -EINVAL;
165	}
166	if ((addr >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS) !=
167	    ((addr + size - 1) >> MLX5_FPGA_TRANSACTION_SEND_PAGE_BITS)) {
168		mlx5_fpga_warn(fdev, "Cannot access %zu bytes at address %jx. Crosses page boundary\n",
169			       size, (uintmax_t)addr);
170		return -EINVAL;
171	}
172	if (addr < mlx5_fpga_ddr_base_get(fdev)) {
173		if (size != sizeof(u32)) {
174			mlx5_fpga_warn(fdev, "Cannot access %zu bytes at cr-space address %jx. Must access a single dword\n",
175				       size, (uintmax_t)addr);
176			return -EINVAL;
177		}
178	}
179	return 0;
180}
181
182int mlx5_fpga_trans_exec(const struct mlx5_fpga_transaction *trans)
183{
184	struct mlx5_fpga_conn *conn = trans->conn;
185	struct mlx5_fpga_trans_priv *trans_priv;
186	u32 *header;
187	int err;
188
189	if (!trans->complete1) {
190		mlx5_fpga_warn(conn->fdev, "Transaction must have a completion callback\n");
191		err = -EINVAL;
192		goto out;
193	}
194
195	err = trans_validate(conn->fdev, trans->addr, trans->size);
196	if (err)
197		goto out;
198
199	trans_priv = alloc_tid(conn->fdev);
200	if (!trans_priv) {
201		err = -EBUSY;
202		goto out;
203	}
204	trans_priv->user_trans = trans;
205	header = trans_priv->header;
206
207	memset(header, 0, sizeof(trans_priv->header));
208	memset(&trans_priv->buf, 0, sizeof(trans_priv->buf));
209	MLX5_SET(fpga_shell_qp_packet, header, type,
210		 (trans->direction == MLX5_FPGA_WRITE) ?
211		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE :
212		 MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ);
213	MLX5_SET(fpga_shell_qp_packet, header, tid, trans_priv->tid);
214	MLX5_SET(fpga_shell_qp_packet, header, len, trans->size);
215	MLX5_SET64(fpga_shell_qp_packet, header, address, trans->addr);
216
217	trans_priv->buf.sg[0].data = header;
218	trans_priv->buf.sg[0].size = sizeof(trans_priv->header);
219	if (trans->direction == MLX5_FPGA_WRITE) {
220		trans_priv->buf.sg[1].data = trans->data;
221		trans_priv->buf.sg[1].size = trans->size;
222	}
223
224	trans_priv->buf.complete = trans_send_complete;
225	trans_priv->state = TRANS_STATE_SEND;
226
227#ifdef NOT_YET
228	/* XXXKIB */
229	err = mlx5_fpga_conn_send(conn->fdev->shell_conn, &trans_priv->buf);
230#else
231	err = 0;
232#endif
233	if (err)
234		goto out_buf_tid;
235	goto out;
236
237out_buf_tid:
238	free_tid(conn->fdev, trans_priv);
239out:
240	return err;
241}
242
243void mlx5_fpga_trans_recv(void *cb_arg, struct mlx5_fpga_dma_buf *buf)
244{
245	struct mlx5_fpga_device *fdev = cb_arg;
246	struct mlx5_fpga_trans_priv *trans_priv;
247	size_t payload_len;
248	u8 status = 0;
249	u8 tid, type;
250
251	mlx5_fpga_dbg(fdev, "Rx QP message on core conn; %u bytes\n",
252		      buf->sg[0].size);
253
254	if (buf->sg[0].size < MLX5_ST_SZ_BYTES(fpga_shell_qp_packet)) {
255		mlx5_fpga_warn(fdev, "Short message %u bytes from device\n",
256			       buf->sg[0].size);
257		goto out;
258	}
259	payload_len = buf->sg[0].size - MLX5_ST_SZ_BYTES(fpga_shell_qp_packet);
260
261	tid = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, tid);
262	trans_priv = find_tid(fdev, tid);
263	if (!trans_priv)
264		goto out;
265
266	type = MLX5_GET(fpga_shell_qp_packet, buf->sg[0].data, type);
267	switch (type) {
268	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_READ_RESPONSE:
269		if (trans_priv->user_trans->direction != MLX5_FPGA_READ) {
270			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
271				       type, trans_priv->user_trans->direction);
272			status = -EIO;
273			goto complete;
274		}
275		if (payload_len != trans_priv->user_trans->size) {
276			mlx5_fpga_warn(fdev, "Incorrect transaction payload length %zu expected %zu\n",
277				       payload_len,
278				       trans_priv->user_trans->size);
279			goto complete;
280		}
281		memcpy(trans_priv->user_trans->data,
282		       MLX5_ADDR_OF(fpga_shell_qp_packet, buf->sg[0].data,
283				    data), payload_len);
284		break;
285	case MLX5_FPGA_SHELL_QP_PACKET_TYPE_DDR_WRITE_RESPONSE:
286		if (trans_priv->user_trans->direction != MLX5_FPGA_WRITE) {
287			mlx5_fpga_warn(fdev, "Wrong answer type %u to a %u transaction\n",
288				       type, trans_priv->user_trans->direction);
289			status = -EIO;
290			goto complete;
291		}
292		break;
293	default:
294		mlx5_fpga_warn(fdev, "Unexpected message type %u len %u from device\n",
295			       type, buf->sg[0].size);
296		status = -EIO;
297		goto complete;
298	}
299
300complete:
301	trans_complete(fdev, trans_priv, status);
302out:
303	return;
304}
305
306int mlx5_fpga_trans_device_init(struct mlx5_fpga_device *fdev)
307{
308	int ret = 0;
309	int tid;
310
311	fdev->trans = kzalloc(sizeof(*fdev->trans), GFP_KERNEL);
312	if (!fdev->trans) {
313		ret = -ENOMEM;
314		goto out;
315	}
316
317	INIT_LIST_HEAD(&fdev->trans->free_queue);
318	for (tid = 0; tid < ARRAY_SIZE(fdev->trans->transactions); tid++) {
319		fdev->trans->transactions[tid].tid = tid;
320		list_add_tail(&fdev->trans->transactions[tid].list_item,
321			      &fdev->trans->free_queue);
322	}
323
324	spin_lock_init(&fdev->trans->lock);
325
326out:
327	return ret;
328}
329
330void mlx5_fpga_trans_device_cleanup(struct mlx5_fpga_device *fdev)
331{
332	kfree(fdev->trans);
333}
334