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