Fungible cards have a number of different PCI functions and thus different drivers, all of which use a common method to initialize and interact with the device. This commit adds a library module that collects these common mechanisms. They mainly deal with device initialization, setting up and destroying queues, and operating an admin queue. A subset of the FW interface is also included here.
Unified · Split
| @@ -0,0 +1,5 @@ | |||
| 1 | # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) | ||
| 2 | |||
| 3 | obj-$(CONFIG_FUN_CORE) += funcore.o | ||
| 4 | |||
| 5 | funcore-y := fun_dev.o fun_queue.o | ||
| @@ -0,0 +1,843 @@ | |||
| 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) | ||
| 2 | |||
| 3 | #include <linux/aer.h> | ||
| 4 | #include <linux/bitmap.h> | ||
| 5 | #include <linux/delay.h> | ||
| 6 | #include <linux/interrupt.h> | ||
| 7 | #include <linux/io.h> | ||
| 8 | #include <linux/io-64-nonatomic-lo-hi.h> | ||
| 9 | #include <linux/mm.h> | ||
| 10 | #include <linux/module.h> | ||
| 11 | #include <linux/nvme.h> | ||
| 12 | #include <linux/pci.h> | ||
| 13 | #include <linux/wait.h> | ||
| 14 | #include <linux/sched/signal.h> | ||
| 15 | |||
| 16 | #include "fun_queue.h" | ||
| 17 | #include "fun_dev.h" | ||
| 18 | |||
| 19 | #define FUN_ADMIN_CMD_TO_MS 3000 | ||
| 20 | |||
| 21 | enum { | ||
| 22 | AQA_ASQS_SHIFT = 0, | ||
| 23 | AQA_ACQS_SHIFT = 16, | ||
| 24 | AQA_MIN_QUEUE_SIZE = 2, | ||
| 25 | AQA_MAX_QUEUE_SIZE = 4096 | ||
| 26 | }; | ||
| 27 | |||
| 28 | /* context for admin commands */ | ||
| 29 | struct fun_cmd_ctx { | ||
| 30 | fun_admin_callback_t cb; /* callback to invoke on completion */ | ||
| 31 | void *cb_data; /* user data provided to callback */ | ||
| 32 | int cpu; /* CPU where the cmd's tag was allocated */ | ||
| 33 | }; | ||
| 34 | |||
| 35 | /* Context for synchronous admin commands. */ | ||
| 36 | struct fun_sync_cmd_ctx { | ||
| 37 | struct completion compl; | ||
| 38 | u8 *rsp_buf; /* caller provided response buffer */ | ||
| 39 | unsigned int rsp_len; /* response buffer size */ | ||
| 40 | u8 rsp_status; /* command response status */ | ||
| 41 | }; | ||
| 42 | |||
| 43 | /* Wait for the CSTS.RDY bit to match @enabled. */ | ||
| 44 | static int fun_wait_ready(struct fun_dev *fdev, bool enabled) | ||
| 45 | { | ||
| 46 | unsigned int cap_to = NVME_CAP_TIMEOUT(fdev->cap_reg); | ||
| 47 | u32 bit = enabled ? NVME_CSTS_RDY : 0; | ||
| 48 | unsigned long deadline; | ||
| 49 | |||
| 50 | deadline = ((cap_to + 1) * HZ / 2) + jiffies; /* CAP.TO is in 500ms */ | ||
| 51 | |||
| 52 | for (;;) { | ||
| 53 | u32 csts = readl(fdev->bar + NVME_REG_CSTS); | ||
| 54 | |||
| 55 | if (csts == ~0) { | ||
| 56 | dev_err(fdev->dev, "CSTS register read %#x\n", csts); | ||
| 57 | return -EIO; | ||
| 58 | } | ||
| 59 | |||
| 60 | if ((csts & NVME_CSTS_RDY) == bit) | ||
| 61 | return 0; | ||
| 62 | |||
| 63 | if (time_is_before_jiffies(deadline)) | ||
| 64 | break; | ||
| 65 | |||
| 66 | msleep(100); | ||
| 67 | } | ||
| 68 | |||
| 69 | dev_err(fdev->dev, | ||
| 70 | "Timed out waiting for device to indicate RDY %u; aborting %s\n", | ||
| 71 | enabled, enabled ? "initialization" : "reset"); | ||
| 72 | return -ETIMEDOUT; | ||
| 73 | } | ||
| 74 | |||
| 75 | /* Check CSTS and return an error if it is unreadable or has unexpected | ||
| 76 | * RDY value. | ||
| 77 | */ | ||
| 78 | static int fun_check_csts_rdy(struct fun_dev *fdev, unsigned int expected_rdy) | ||
| 79 | { | ||
| 80 | u32 csts = readl(fdev->bar + NVME_REG_CSTS); | ||
| 81 | u32 actual_rdy = csts & NVME_CSTS_RDY; | ||
| 82 | |||
| 83 | if (csts == ~0) { | ||
| 84 | dev_err(fdev->dev, "CSTS register read %#x\n", csts); | ||
| 85 | return -EIO; | ||
| 86 | } | ||
| 87 | if (actual_rdy != expected_rdy) { | ||
| 88 | dev_err(fdev->dev, "Unexpected CSTS RDY %u\n", actual_rdy); | ||
| 89 | return -EINVAL; | ||
| 90 | } | ||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | /* Check that CSTS RDY has the expected value. Then write a new value to the CC | ||
| 95 | * register and wait for CSTS RDY to match the new CC ENABLE state. | ||
| 96 | */ | ||
| 97 | static int fun_update_cc_enable(struct fun_dev *fdev, unsigned int initial_rdy) | ||
| 98 | { | ||
| 99 | int rc = fun_check_csts_rdy(fdev, initial_rdy); | ||
| 100 | |||
| 101 | if (rc) | ||
| 102 | return rc; | ||
| 103 | writel(fdev->cc_reg, fdev->bar + NVME_REG_CC); | ||
| 104 | return fun_wait_ready(fdev, !!(fdev->cc_reg & NVME_CC_ENABLE)); | ||
| 105 | } | ||
| 106 | |||
| 107 | static int fun_disable_ctrl(struct fun_dev *fdev) | ||
| 108 | { | ||
| 109 | fdev->cc_reg &= ~(NVME_CC_SHN_MASK | NVME_CC_ENABLE); | ||
| 110 | return fun_update_cc_enable(fdev, 1); | ||
| 111 | } | ||
| 112 | |||
| 113 | static int fun_enable_ctrl(struct fun_dev *fdev, u32 admin_cqesz_log2, | ||
| 114 | u32 admin_sqesz_log2) | ||
| 115 | { | ||
| 116 | fdev->cc_reg = (admin_cqesz_log2 << NVME_CC_IOCQES_SHIFT) | | ||
| 117 | (admin_sqesz_log2 << NVME_CC_IOSQES_SHIFT) | | ||
| 118 | ((PAGE_SHIFT - 12) << NVME_CC_MPS_SHIFT) | | ||
| 119 | NVME_CC_ENABLE; | ||
| 120 | |||
| 121 | return fun_update_cc_enable(fdev, 0); | ||
| 122 | } | ||
| 123 | |||
| 124 | static int fun_map_bars(struct fun_dev *fdev, const char *name) | ||
| 125 | { | ||
| 126 | struct pci_dev *pdev = to_pci_dev(fdev->dev); | ||
| 127 | int err; | ||
| 128 | |||
| 129 | err = pci_request_mem_regions(pdev, name); | ||
| 130 | if (err) { | ||
| 131 | dev_err(&pdev->dev, | ||
| 132 | "Couldn't get PCI memory resources, err %d\n", err); | ||
| 133 | return err; | ||
| 134 | } | ||
| 135 | |||
| 136 | fdev->bar = pci_ioremap_bar(pdev, 0); | ||
| 137 | if (!fdev->bar) { | ||
| 138 | dev_err(&pdev->dev, "Couldn't map BAR 0\n"); | ||
| 139 | pci_release_mem_regions(pdev); | ||
| 140 | return -ENOMEM; | ||
| 141 | } | ||
| 142 | |||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | static void fun_unmap_bars(struct fun_dev *fdev) | ||
| 147 | { | ||
| 148 | struct pci_dev *pdev = to_pci_dev(fdev->dev); | ||
| 149 | |||
| 150 | if (fdev->bar) { | ||
| 151 | iounmap(fdev->bar); | ||
| 152 | fdev->bar = NULL; | ||
| 153 | pci_release_mem_regions(pdev); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | static int fun_set_dma_masks(struct device *dev) | ||
| 158 | { | ||
| 159 | int err; | ||
| 160 | |||
| 161 | err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); | ||
| 162 | if (err) | ||
| 163 | dev_err(dev, "DMA mask configuration failed, err %d\n", err); | ||
| 164 | return err; | ||
| 165 | } | ||
| 166 | |||
| 167 | static irqreturn_t fun_admin_irq(int irq, void *data) | ||
| 168 | { | ||
| 169 | struct fun_queue *funq = data; | ||
| 170 | |||
| 171 | return fun_process_cq(funq, 0) ? IRQ_HANDLED : IRQ_NONE; | ||
| 172 | } | ||
| 173 | |||
| 174 | static void fun_complete_admin_cmd(struct fun_queue *funq, void *data, | ||
| 175 | void *entry, const struct fun_cqe_info *info) | ||
| 176 | { | ||
| 177 | const struct fun_admin_rsp_common *rsp_common = entry; | ||
| 178 | struct fun_dev *fdev = funq->fdev; | ||
| 179 | struct fun_cmd_ctx *cmd_ctx; | ||
| 180 | int cpu; | ||
| 181 | u16 cid; | ||
| 182 | |||
| 183 | if (info->sqhd == cpu_to_be16(0xffff)) { | ||
| 184 | dev_dbg(fdev->dev, "adminq event"); | ||
| 185 | if (fdev->adminq_cb) | ||
| 186 | fdev->adminq_cb(fdev, entry); | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | |||
| 190 | cid = be16_to_cpu(rsp_common->cid); | ||
| 191 | dev_dbg(fdev->dev, "admin CQE cid %u, op %u, ret %u\n", cid, | ||
| 192 | rsp_common->op, rsp_common->ret); | ||
| 193 | |||
| 194 | cmd_ctx = &fdev->cmd_ctx[cid]; | ||
| 195 | if (cmd_ctx->cpu < 0) { | ||
| 196 | dev_err(fdev->dev, | ||
| 197 | "admin CQE with CID=%u, op=%u does not match a pending command\n", | ||
| 198 | cid, rsp_common->op); | ||
| 199 | return; | ||
| 200 | } | ||
| 201 | |||
| 202 | if (cmd_ctx->cb) | ||
| 203 | cmd_ctx->cb(fdev, entry, xchg(&cmd_ctx->cb_data, NULL)); | ||
| 204 | |||
| 205 | cpu = cmd_ctx->cpu; | ||
| 206 | cmd_ctx->cpu = -1; | ||
| 207 | sbitmap_queue_clear(&fdev->admin_sbq, cid, cpu); | ||
| 208 | } | ||
| 209 | |||
| 210 | static int fun_init_cmd_ctx(struct fun_dev *fdev, unsigned int ntags) | ||
| 211 | { | ||
| 212 | unsigned int i; | ||
| 213 | |||
| 214 | fdev->cmd_ctx = kvcalloc(ntags, sizeof(*fdev->cmd_ctx), GFP_KERNEL); | ||
| 215 | if (!fdev->cmd_ctx) | ||
| 216 | return -ENOMEM; | ||
| 217 | |||
| 218 | for (i = 0; i < ntags; i++) | ||
| 219 | fdev->cmd_ctx[i].cpu = -1; | ||
| 220 | |||
| 221 | return 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | /* Allocate and enable an admin queue and assign it the first IRQ vector. */ | ||
| 225 | static int fun_enable_admin_queue(struct fun_dev *fdev, | ||
| 226 | const struct fun_dev_params *areq) | ||
| 227 | { | ||
| 228 | struct fun_queue_alloc_req qreq = { | ||
| 229 | .cqe_size_log2 = areq->cqe_size_log2, | ||
| 230 | .sqe_size_log2 = areq->sqe_size_log2, | ||
| 231 | .cq_depth = areq->cq_depth, | ||
| 232 | .sq_depth = areq->sq_depth, | ||
| 233 | .rq_depth = areq->rq_depth, | ||
| 234 | }; | ||
| 235 | unsigned int ntags = areq->sq_depth - 1; | ||
| 236 | struct fun_queue *funq; | ||
| 237 | int rc; | ||
| 238 | |||
| 239 | if (fdev->admin_q) | ||
| 240 | return -EEXIST; | ||
| 241 | |||
| 242 | if (areq->sq_depth < AQA_MIN_QUEUE_SIZE || | ||
| 243 | areq->sq_depth > AQA_MAX_QUEUE_SIZE || | ||
| 244 | areq->cq_depth < AQA_MIN_QUEUE_SIZE || | ||
| 245 | areq->cq_depth > AQA_MAX_QUEUE_SIZE) | ||
| 246 | return -EINVAL; | ||
| 247 | |||
| 248 | fdev->admin_q = fun_alloc_queue(fdev, 0, &qreq); | ||
| 249 | if (!fdev->admin_q) | ||
| 250 | return -ENOMEM; | ||
| 251 | |||
| 252 | rc = fun_init_cmd_ctx(fdev, ntags); | ||
| 253 | if (rc) | ||
| 254 | goto free_q; | ||
| 255 | |||
| 256 | rc = sbitmap_queue_init_node(&fdev->admin_sbq, ntags, -1, false, | ||
| 257 | GFP_KERNEL, dev_to_node(fdev->dev)); | ||
| 258 | if (rc) | ||
| 259 | goto free_cmd_ctx; | ||
| 260 | |||
| 261 | funq = fdev->admin_q; | ||
| 262 | funq->cq_vector = 0; | ||
| 263 | rc = fun_request_irq(funq, dev_name(fdev->dev), fun_admin_irq, funq); | ||
| 264 | if (rc) | ||
| 265 | goto free_sbq; | ||
| 266 | |||
| 267 | fun_set_cq_callback(funq, fun_complete_admin_cmd, NULL); | ||
| 268 | fdev->adminq_cb = areq->event_cb; | ||
| 269 | |||
| 270 | writel((funq->sq_depth - 1) << AQA_ASQS_SHIFT | | ||
| 271 | (funq->cq_depth - 1) << AQA_ACQS_SHIFT, | ||
| 272 | fdev->bar + NVME_REG_AQA); | ||
| 273 | |||
| 274 | writeq(funq->sq_dma_addr, fdev->bar + NVME_REG_ASQ); | ||
| 275 | writeq(funq->cq_dma_addr, fdev->bar + NVME_REG_ACQ); | ||
| 276 | |||
| 277 | rc = fun_enable_ctrl(fdev, areq->cqe_size_log2, areq->sqe_size_log2); | ||
| 278 | if (rc) | ||
| 279 | goto free_irq; | ||
| 280 | |||
| 281 | if (areq->rq_depth) { | ||
| 282 | rc = fun_create_rq(funq); | ||
| 283 | if (rc) | ||
| 284 | goto disable_ctrl; | ||
| 285 | |||
| 286 | funq_rq_post(funq); | ||
| 287 | } | ||
| 288 | |||
| 289 | return 0; | ||
| 290 | |||
| 291 | disable_ctrl: | ||
| 292 | fun_disable_ctrl(fdev); | ||
| 293 | free_irq: | ||
| 294 | fun_free_irq(funq); | ||
| 295 | free_sbq: | ||
| 296 | sbitmap_queue_free(&fdev->admin_sbq); | ||
| 297 | free_cmd_ctx: | ||
| 298 | kvfree(fdev->cmd_ctx); | ||
| 299 | fdev->cmd_ctx = NULL; | ||
| 300 | free_q: | ||
| 301 | fun_free_queue(fdev->admin_q); | ||
| 302 | fdev->admin_q = NULL; | ||
| 303 | return rc; | ||
| 304 | } | ||
| 305 | |||
| 306 | static void fun_disable_admin_queue(struct fun_dev *fdev) | ||
| 307 | { | ||
| 308 | struct fun_queue *admq = fdev->admin_q; | ||
| 309 | |||
| 310 | if (!admq) | ||
| 311 | return; | ||
| 312 | |||
| 313 | fun_disable_ctrl(fdev); | ||
| 314 | |||
| 315 | fun_free_irq(admq); | ||
| 316 | __fun_process_cq(admq, 0); | ||
| 317 | |||
| 318 | sbitmap_queue_free(&fdev->admin_sbq); | ||
| 319 | |||
| 320 | kvfree(fdev->cmd_ctx); | ||
| 321 | fdev->cmd_ctx = NULL; | ||
| 322 | |||
| 323 | fun_free_queue(admq); | ||
| 324 | fdev->admin_q = NULL; | ||
| 325 | } | ||
| 326 | |||
| 327 | /* Return %true if the admin queue has stopped servicing commands as can be | ||
| 328 | * detected through registers. This isn't exhaustive and may provide false | ||
| 329 | * negatives. | ||
| 330 | */ | ||
| 331 | static bool fun_adminq_stopped(struct fun_dev *fdev) | ||
| 332 | { | ||
| 333 | u32 csts = readl(fdev->bar + NVME_REG_CSTS); | ||
| 334 | |||
| 335 | return (csts & (NVME_CSTS_CFS | NVME_CSTS_RDY)) != NVME_CSTS_RDY; | ||
| 336 | } | ||
| 337 | |||
| 338 | static int fun_wait_for_tag(struct fun_dev *fdev, int *cpup) | ||
| 339 | { | ||
| 340 | struct sbitmap_queue *sbq = &fdev->admin_sbq; | ||
| 341 | struct sbq_wait_state *ws = &sbq->ws[0]; | ||
| 342 | DEFINE_SBQ_WAIT(wait); | ||
| 343 | int tag; | ||
| 344 | |||
| 345 | for (;;) { | ||
| 346 | sbitmap_prepare_to_wait(sbq, ws, &wait, TASK_UNINTERRUPTIBLE); | ||
| 347 | if (fdev->suppress_cmds) { | ||
| 348 | tag = -ESHUTDOWN; | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | tag = sbitmap_queue_get(sbq, cpup); | ||
| 352 | if (tag >= 0) | ||
| 353 | break; | ||
| 354 | schedule(); | ||
| 355 | } | ||
| 356 | |||
| 357 | sbitmap_finish_wait(sbq, ws, &wait); | ||
| 358 | return tag; | ||
| 359 | } | ||
| 360 | |||
| 361 | /* Submit an asynchronous admin command. Caller is responsible for implementing | ||
| 362 | * any waiting or timeout. Upon command completion the callback @cb is called. | ||
| 363 | */ | ||
| 364 | int fun_submit_admin_cmd(struct fun_dev *fdev, struct fun_admin_req_common *cmd, | ||
| 365 | fun_admin_callback_t cb, void *cb_data, bool wait_ok) | ||
| 366 | { | ||
| 367 | struct fun_queue *funq = fdev->admin_q; | ||
| 368 | unsigned int cmdsize = cmd->len8 * 8; | ||
| 369 | struct fun_cmd_ctx *cmd_ctx; | ||
| 370 | int tag, cpu, rc = 0; | ||
| 371 | |||
| 372 | if (WARN_ON(cmdsize > (1 << funq->sqe_size_log2))) | ||
| 373 | return -EMSGSIZE; | ||
| 374 | |||
| 375 | tag = sbitmap_queue_get(&fdev->admin_sbq, &cpu); | ||
| 376 | if (tag < 0) { | ||
| 377 | if (!wait_ok) | ||
| 378 | return -EAGAIN; | ||
| 379 | tag = fun_wait_for_tag(fdev, &cpu); | ||
| 380 | if (tag < 0) | ||
| 381 | return tag; | ||
| 382 | } | ||
| 383 | |||
| 384 | cmd->cid = cpu_to_be16(tag); | ||
| 385 | |||
| 386 | cmd_ctx = &fdev->cmd_ctx[tag]; | ||
| 387 | cmd_ctx->cb = cb; | ||
| 388 | cmd_ctx->cb_data = cb_data; | ||
| 389 | |||
| 390 | spin_lock(&funq->sq_lock); | ||
| 391 | |||
| 392 | if (unlikely(fdev->suppress_cmds)) { | ||
| 393 | rc = -ESHUTDOWN; | ||
| 394 | sbitmap_queue_clear(&fdev->admin_sbq, tag, cpu); | ||
| 395 | } else { | ||
| 396 | cmd_ctx->cpu = cpu; | ||
| 397 | memcpy(fun_sqe_at(funq, funq->sq_tail), cmd, cmdsize); | ||
| 398 | |||
| 399 | dev_dbg(fdev->dev, "admin cmd @ %u: %8ph\n", funq->sq_tail, | ||
| 400 | cmd); | ||
| 401 | |||
| 402 | if (++funq->sq_tail == funq->sq_depth) | ||
| 403 | funq->sq_tail = 0; | ||
| 404 | writel(funq->sq_tail, funq->sq_db); | ||
| 405 | } | ||
| 406 | spin_unlock(&funq->sq_lock); | ||
| 407 | return rc; | ||
| 408 | } | ||
| 409 | |||
| 410 | /* Abandon a pending admin command by clearing the issuer's callback data. | ||
| 411 | * Failure indicates that the command either has already completed or its | ||
| 412 | * completion is racing with this call. | ||
| 413 | */ | ||
| 414 | static bool fun_abandon_admin_cmd(struct fun_dev *fd, | ||
| 415 | const struct fun_admin_req_common *cmd, | ||
| 416 | void *cb_data) | ||
| 417 | { | ||
| 418 | u16 cid = be16_to_cpu(cmd->cid); | ||
| 419 | struct fun_cmd_ctx *cmd_ctx = &fd->cmd_ctx[cid]; | ||
| 420 | |||
| 421 | return cmpxchg(&cmd_ctx->cb_data, cb_data, NULL) == cb_data; | ||
| 422 | } | ||
| 423 | |||
| 424 | /* Stop submission of new admin commands and wake up any processes waiting for | ||
| 425 | * tags. Already submitted commands are left to complete or time out. | ||
| 426 | */ | ||
| 427 | static void fun_admin_stop(struct fun_dev *fdev) | ||
| 428 | { | ||
| 429 | spin_lock(&fdev->admin_q->sq_lock); | ||
| 430 | fdev->suppress_cmds = true; | ||
| 431 | spin_unlock(&fdev->admin_q->sq_lock); | ||
| 432 | sbitmap_queue_wake_all(&fdev->admin_sbq); | ||
| 433 | } | ||
| 434 | |||
| 435 | /* The callback for synchronous execution of admin commands. It copies the | ||
| 436 | * command response to the caller's buffer and signals completion. | ||
| 437 | */ | ||
| 438 | static void fun_admin_cmd_sync_cb(struct fun_dev *fd, void *rsp, void *cb_data) | ||
| 439 | { | ||
| 440 | const struct fun_admin_rsp_common *rsp_common = rsp; | ||
| 441 | struct fun_sync_cmd_ctx *ctx = cb_data; | ||
| 442 | |||
| 443 | if (!ctx) | ||
| 444 | return; /* command issuer timed out and left */ | ||
| 445 | if (ctx->rsp_buf) { | ||
| 446 | unsigned int rsp_len = rsp_common->len8 * 8; | ||
| 447 | |||
| 448 | if (unlikely(rsp_len > ctx->rsp_len)) { | ||
| 449 | dev_err(fd->dev, | ||
| 450 | "response for op %u is %uB > response buffer %uB\n", | ||
| 451 | rsp_common->op, rsp_len, ctx->rsp_len); | ||
| 452 | rsp_len = ctx->rsp_len; | ||
| 453 | } | ||
| 454 | memcpy(ctx->rsp_buf, rsp, rsp_len); | ||
| 455 | } | ||
| 456 | ctx->rsp_status = rsp_common->ret; | ||
| 457 | complete(&ctx->compl); | ||
| 458 | } | ||
| 459 | |||
| 460 | /* Submit a synchronous admin command. */ | ||
| 461 | int fun_submit_admin_sync_cmd(struct fun_dev *fdev, | ||
| 462 | struct fun_admin_req_common *cmd, void *rsp, | ||
| 463 | size_t rspsize, unsigned int timeout) | ||
| 464 | { | ||
| 465 | struct fun_sync_cmd_ctx ctx = { | ||
| 466 | .compl = COMPLETION_INITIALIZER_ONSTACK(ctx.compl), | ||
| 467 | .rsp_buf = rsp, | ||
| 468 | .rsp_len = rspsize, | ||
| 469 | }; | ||
| 470 | unsigned int cmdlen = cmd->len8 * 8; | ||
| 471 | unsigned long jiffies_left; | ||
| 472 | int ret; | ||
| 473 | |||
| 474 | ret = fun_submit_admin_cmd(fdev, cmd, fun_admin_cmd_sync_cb, &ctx, | ||
| 475 | true); | ||
| 476 | if (ret) | ||
| 477 | return ret; | ||
| 478 | |||
| 479 | if (!timeout) | ||
| 480 | timeout = FUN_ADMIN_CMD_TO_MS; | ||
| 481 | |||
| 482 | jiffies_left = wait_for_completion_timeout(&ctx.compl, | ||
| 483 | msecs_to_jiffies(timeout)); | ||
| 484 | if (!jiffies_left) { | ||
| 485 | /* The command timed out. Attempt to cancel it so we can return. | ||
| 486 | * But if the command is in the process of completing we'll | ||
| 487 | * wait for it. | ||
| 488 | */ | ||
| 489 | if (fun_abandon_admin_cmd(fdev, cmd, &ctx)) { | ||
| 490 | dev_err(fdev->dev, "admin command timed out: %*ph\n", | ||
| 491 | cmdlen, cmd); | ||
| 492 | fun_admin_stop(fdev); | ||
| 493 | /* see if the timeout was due to a queue failure */ | ||
| 494 | if (fun_adminq_stopped(fdev)) | ||
| 495 | dev_err(fdev->dev, | ||
| 496 | "device does not accept admin commands\n"); | ||
| 497 | |||
| 498 | return -ETIMEDOUT; | ||
| 499 | } | ||
| 500 | wait_for_completion(&ctx.compl); | ||
| 501 | } | ||
| 502 | |||
| 503 | if (ctx.rsp_status) { | ||
| 504 | dev_err(fdev->dev, "admin command failed, err %d: %*ph\n", | ||
| 505 | ctx.rsp_status, cmdlen, cmd); | ||
| 506 | } | ||
| 507 | |||
| 508 | return -ctx.rsp_status; | ||
| 509 | } | ||
| 510 | EXPORT_SYMBOL_GPL(fun_submit_admin_sync_cmd); | ||
| 511 | |||
| 512 | /* Return the number of device resources of the requested type. */ | ||
| 513 | int fun_get_res_count(struct fun_dev *fdev, enum fun_admin_op res) | ||
| 514 | { | ||
| 515 | union { | ||
| 516 | struct fun_admin_res_count_req req; | ||
| 517 | struct fun_admin_res_count_rsp rsp; | ||
| 518 | } cmd; | ||
| 519 | int rc; | ||
| 520 | |||
| 521 | cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(res, sizeof(cmd.req)); | ||
| 522 | cmd.req.count = FUN_ADMIN_SIMPLE_SUBOP_INIT(FUN_ADMIN_SUBOP_RES_COUNT, | ||
| 523 | 0, 0); | ||
| 524 | |||
| 525 | rc = fun_submit_admin_sync_cmd(fdev, &cmd.req.common, &cmd.rsp, | ||
| 526 | sizeof(cmd), 0); | ||
| 527 | return rc ? rc : be32_to_cpu(cmd.rsp.count.data); | ||
| 528 | } | ||
| 529 | EXPORT_SYMBOL_GPL(fun_get_res_count); | ||
| 530 | |||
| 531 | /* Request that the instance of resource @res with the given id be deleted. */ | ||
| 532 | int fun_res_destroy(struct fun_dev *fdev, enum fun_admin_op res, | ||
| 533 | unsigned int flags, u32 id) | ||
| 534 | { | ||
| 535 | struct fun_admin_generic_destroy_req req = { | ||
| 536 | .common = FUN_ADMIN_REQ_COMMON_INIT2(res, sizeof(req)), | ||
| 537 | .destroy = FUN_ADMIN_SIMPLE_SUBOP_INIT(FUN_ADMIN_SUBOP_DESTROY, | ||
| 538 | flags, id) | ||
| 539 | }; | ||
| 540 | |||
| 541 | return fun_submit_admin_sync_cmd(fdev, &req.common, NULL, 0, 0); | ||
| 542 | } | ||
| 543 | EXPORT_SYMBOL_GPL(fun_res_destroy); | ||
| 544 | |||
| 545 | /* Bind two entities of the given types and IDs. */ | ||
| 546 | int fun_bind(struct fun_dev *fdev, enum fun_admin_bind_type type0, | ||
| 547 | unsigned int id0, enum fun_admin_bind_type type1, | ||
| 548 | unsigned int id1) | ||
| 549 | { | ||
| 550 | struct { | ||
| 551 | struct fun_admin_bind_req req; | ||
| 552 | struct fun_admin_bind_entry entry[2]; | ||
| 553 | } cmd = { | ||
| 554 | .req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, | ||
| 555 | sizeof(cmd)), | ||
| 556 | .entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0), | ||
| 557 | .entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1), | ||
| 558 | }; | ||
| 559 | |||
| 560 | return fun_submit_admin_sync_cmd(fdev, &cmd.req.common, NULL, 0, 0); | ||
| 561 | } | ||
| 562 | EXPORT_SYMBOL_GPL(fun_bind); | ||
| 563 | |||
| 564 | static int fun_get_dev_limits(struct fun_dev *fdev) | ||
| 565 | { | ||
| 566 | struct pci_dev *pdev = to_pci_dev(fdev->dev); | ||
| 567 | unsigned int cq_count, sq_count, num_dbs; | ||
| 568 | int rc; | ||
| 569 | |||
| 570 | rc = fun_get_res_count(fdev, FUN_ADMIN_OP_EPCQ); | ||
| 571 | if (rc < 0) | ||
| 572 | return rc; | ||
| 573 | cq_count = rc; | ||
| 574 | |||
| 575 | rc = fun_get_res_count(fdev, FUN_ADMIN_OP_EPSQ); | ||
| 576 | if (rc < 0) | ||
| 577 | return rc; | ||
| 578 | sq_count = rc; | ||
| 579 | |||
| 580 | /* The admin queue consumes 1 CQ and at least 1 SQ. To be usable the | ||
| 581 | * device must provide additional queues. | ||
| 582 | */ | ||
| 583 | if (cq_count < 2 || sq_count < 2 + !!fdev->admin_q->rq_depth) | ||
| 584 | return -EINVAL; | ||
| 585 | |||
| 586 | /* Calculate the max QID based on SQ/CQ/doorbell counts. | ||
| 587 | * SQ/CQ doorbells alternate. | ||
| 588 | */ | ||
| 589 | num_dbs = (pci_resource_len(pdev, 0) - NVME_REG_DBS) / | ||
| 590 | (fdev->db_stride * 4); | ||
| 591 | fdev->max_qid = min3(cq_count, sq_count, num_dbs / 2) - 1; | ||
| 592 | fdev->kern_end_qid = fdev->max_qid + 1; | ||
| 593 | return 0; | ||
| 594 | } | ||
| 595 | |||
| 596 | /* Allocate all MSI-X vectors available on a function and at least @min_vecs. */ | ||
| 597 | static int fun_alloc_irqs(struct pci_dev *pdev, unsigned int min_vecs) | ||
| 598 | { | ||
| 599 | int vecs, num_msix = pci_msix_vec_count(pdev); | ||
| 600 | |||
| 601 | if (num_msix < 0) | ||
| 602 | return num_msix; | ||
| 603 | if (min_vecs > num_msix) | ||
| 604 | return -ERANGE; | ||
| 605 | |||
| 606 | vecs = pci_alloc_irq_vectors(pdev, min_vecs, num_msix, PCI_IRQ_MSIX); | ||
| 607 | if (vecs > 0) { | ||
| 608 | dev_info(&pdev->dev, | ||
| 609 | "Allocated %d IRQ vectors of %d requested\n", | ||
| 610 | vecs, num_msix); | ||
| 611 | } else { | ||
| 612 | dev_err(&pdev->dev, | ||
| 613 | "Unable to allocate at least %u IRQ vectors\n", | ||
| 614 | min_vecs); | ||
| 615 | } | ||
| 616 | return vecs; | ||
| 617 | } | ||
| 618 | |||
| 619 | /* Allocate and initialize the IRQ manager state. */ | ||
| 620 | static int fun_alloc_irq_mgr(struct fun_dev *fdev) | ||
| 621 | { | ||
| 622 | fdev->irq_map = bitmap_zalloc(fdev->num_irqs, GFP_KERNEL); | ||
| 623 | if (!fdev->irq_map) | ||
| 624 | return -ENOMEM; | ||
| 625 | |||
| 626 | spin_lock_init(&fdev->irqmgr_lock); | ||
| 627 | /* mark IRQ 0 allocated, it is used by the admin queue */ | ||
| 628 | __set_bit(0, fdev->irq_map); | ||
| 629 | fdev->irqs_avail = fdev->num_irqs - 1; | ||
| 630 | return 0; | ||
| 631 | } | ||
| 632 | |||
| 633 | /* Reserve @nirqs of the currently available IRQs and return their indices. */ | ||
| 634 | int fun_reserve_irqs(struct fun_dev *fdev, unsigned int nirqs, u16 *irq_indices) | ||
| 635 | { | ||
| 636 | unsigned int b, n = 0; | ||
| 637 | int err = -ENOSPC; | ||
| 638 | |||
| 639 | if (!nirqs) | ||
| 640 | return 0; | ||
| 641 | |||
| 642 | spin_lock(&fdev->irqmgr_lock); | ||
| 643 | if (nirqs > fdev->irqs_avail) | ||
| 644 | goto unlock; | ||
| 645 | |||
| 646 | for_each_clear_bit(b, fdev->irq_map, fdev->num_irqs) { | ||
| 647 | __set_bit(b, fdev->irq_map); | ||
| 648 | irq_indices[n++] = b; | ||
| 649 | if (n >= nirqs) | ||
| 650 | break; | ||
| 651 | } | ||
| 652 | |||
| 653 | WARN_ON(n < nirqs); | ||
| 654 | fdev->irqs_avail -= n; | ||
| 655 | err = n; | ||
| 656 | unlock: | ||
| 657 | spin_unlock(&fdev->irqmgr_lock); | ||
| 658 | return err; | ||
| 659 | } | ||
| 660 | EXPORT_SYMBOL(fun_reserve_irqs); | ||
| 661 | |||
| 662 | /* Release @nirqs previously allocated IRQS with the supplied indices. */ | ||
| 663 | void fun_release_irqs(struct fun_dev *fdev, unsigned int nirqs, | ||
| 664 | u16 *irq_indices) | ||
| 665 | { | ||
| 666 | unsigned int i; | ||
| 667 | |||
| 668 | spin_lock(&fdev->irqmgr_lock); | ||
| 669 | for (i = 0; i < nirqs; i++) | ||
| 670 | __clear_bit(irq_indices[i], fdev->irq_map); | ||
| 671 | fdev->irqs_avail += nirqs; | ||
| 672 | spin_unlock(&fdev->irqmgr_lock); | ||
| 673 | } | ||
| 674 | EXPORT_SYMBOL(fun_release_irqs); | ||
| 675 | |||
| 676 | static void fun_serv_handler(struct work_struct *work) | ||
| 677 | { | ||
| 678 | struct fun_dev *fd = container_of(work, struct fun_dev, service_task); | ||
| 679 | |||
| 680 | if (test_bit(FUN_SERV_DISABLED, &fd->service_flags)) | ||
| 681 | return; | ||
| 682 | if (fd->serv_cb) | ||
| 683 | fd->serv_cb(fd); | ||
| 684 | } | ||
| 685 | |||
| 686 | void fun_serv_stop(struct fun_dev *fd) | ||
| 687 | { | ||
| 688 | set_bit(FUN_SERV_DISABLED, &fd->service_flags); | ||
| 689 | cancel_work_sync(&fd->service_task); | ||
| 690 | } | ||
| 691 | EXPORT_SYMBOL_GPL(fun_serv_stop); | ||
| 692 | |||
| 693 | void fun_serv_restart(struct fun_dev *fd) | ||
| 694 | { | ||
| 695 | clear_bit(FUN_SERV_DISABLED, &fd->service_flags); | ||
| 696 | if (fd->service_flags) | ||
| 697 | schedule_work(&fd->service_task); | ||
| 698 | } | ||
| 699 | EXPORT_SYMBOL_GPL(fun_serv_restart); | ||
| 700 | |||
| 701 | void fun_serv_sched(struct fun_dev *fd) | ||
| 702 | { | ||
| 703 | if (!test_bit(FUN_SERV_DISABLED, &fd->service_flags)) | ||
| 704 | schedule_work(&fd->service_task); | ||
| 705 | } | ||
| 706 | EXPORT_SYMBOL_GPL(fun_serv_sched); | ||
| 707 | |||
| 708 | /* Check and try to get the device into a proper state for initialization, | ||
| 709 | * i.e., CSTS.RDY = CC.EN = 0. | ||
| 710 | */ | ||
| 711 | static int sanitize_dev(struct fun_dev *fdev) | ||
| 712 | { | ||
| 713 | int rc; | ||
| 714 | |||
| 715 | fdev->cap_reg = readq(fdev->bar + NVME_REG_CAP); | ||
| 716 | fdev->cc_reg = readl(fdev->bar + NVME_REG_CC); | ||
| 717 | |||
| 718 | /* First get RDY to agree with the current EN. Give RDY the opportunity | ||
| 719 | * to complete a potential recent EN change. | ||
| 720 | */ | ||
| 721 | rc = fun_wait_ready(fdev, fdev->cc_reg & NVME_CC_ENABLE); | ||
| 722 | if (rc) | ||
| 723 | return rc; | ||
| 724 | |||
| 725 | /* Next, reset the device if EN is currently 1. */ | ||
| 726 | if (fdev->cc_reg & NVME_CC_ENABLE) | ||
| 727 | rc = fun_disable_ctrl(fdev); | ||
| 728 | |||
| 729 | return rc; | ||
| 730 | } | ||
| 731 | |||
| 732 | /* Undo the device initialization of fun_dev_enable(). */ | ||
| 733 | void fun_dev_disable(struct fun_dev *fdev) | ||
| 734 | { | ||
| 735 | struct pci_dev *pdev = to_pci_dev(fdev->dev); | ||
| 736 | |||
| 737 | pci_set_drvdata(pdev, NULL); | ||
| 738 | |||
| 739 | if (fdev->fw_handle != FUN_HCI_ID_INVALID) { | ||
| 740 | fun_res_destroy(fdev, FUN_ADMIN_OP_SWUPGRADE, 0, | ||
| 741 | fdev->fw_handle); | ||
| 742 | fdev->fw_handle = FUN_HCI_ID_INVALID; | ||
| 743 | } | ||
| 744 | |||
| 745 | fun_disable_admin_queue(fdev); | ||
| 746 | |||
| 747 | bitmap_free(fdev->irq_map); | ||
| 748 | pci_free_irq_vectors(pdev); | ||
| 749 | |||
| 750 | pci_clear_master(pdev); | ||
| 751 | pci_disable_pcie_error_reporting(pdev); | ||
| 752 | pci_disable_device(pdev); | ||
| 753 | |||
| 754 | fun_unmap_bars(fdev); | ||
| 755 | } | ||
| 756 | EXPORT_SYMBOL(fun_dev_disable); | ||
| 757 | |||
| 758 | /* Perform basic initialization of a device, including | ||
| 759 | * - PCI config space setup and BAR0 mapping | ||
| 760 | * - interrupt management initialization | ||
| 761 | * - 1 admin queue setup | ||
| 762 | * - determination of some device limits, such as number of queues. | ||
| 763 | */ | ||
| 764 | int fun_dev_enable(struct fun_dev *fdev, struct pci_dev *pdev, | ||
| 765 | const struct fun_dev_params *areq, const char *name) | ||
| 766 | { | ||
| 767 | int rc; | ||
| 768 | |||
| 769 | fdev->dev = &pdev->dev; | ||
| 770 | rc = fun_map_bars(fdev, name); | ||
| 771 | if (rc) | ||
| 772 | return rc; | ||
| 773 | |||
| 774 | rc = fun_set_dma_masks(fdev->dev); | ||
| 775 | if (rc) | ||
| 776 | goto unmap; | ||
| 777 | |||
| 778 | rc = pci_enable_device_mem(pdev); | ||
| 779 | if (rc) { | ||
| 780 | dev_err(&pdev->dev, "Couldn't enable device, err %d\n", rc); | ||
| 781 | goto unmap; | ||
| 782 | } | ||
| 783 | |||
| 784 | pci_enable_pcie_error_reporting(pdev); | ||
| 785 | |||
| 786 | rc = sanitize_dev(fdev); | ||
| 787 | if (rc) | ||
| 788 | goto disable_dev; | ||
| 789 | |||
| 790 | fdev->fw_handle = FUN_HCI_ID_INVALID; | ||
| 791 | fdev->q_depth = NVME_CAP_MQES(fdev->cap_reg) + 1; | ||
| 792 | fdev->db_stride = 1 << NVME_CAP_STRIDE(fdev->cap_reg); | ||
| 793 | fdev->dbs = fdev->bar + NVME_REG_DBS; | ||
| 794 | |||
| 795 | INIT_WORK(&fdev->service_task, fun_serv_handler); | ||
| 796 | fdev->service_flags = FUN_SERV_DISABLED; | ||
| 797 | fdev->serv_cb = areq->serv_cb; | ||
| 798 | |||
| 799 | rc = fun_alloc_irqs(pdev, areq->min_msix + 1); /* +1 for admin CQ */ | ||
| 800 | if (rc < 0) | ||
| 801 | goto disable_dev; | ||
| 802 | fdev->num_irqs = rc; | ||
| 803 | |||
| 804 | rc = fun_alloc_irq_mgr(fdev); | ||
| 805 | if (rc) | ||
| 806 | goto free_irqs; | ||
| 807 | |||
| 808 | pci_set_master(pdev); | ||
| 809 | rc = fun_enable_admin_queue(fdev, areq); | ||
| 810 | if (rc) | ||
| 811 | goto free_irq_mgr; | ||
| 812 | |||
| 813 | rc = fun_get_dev_limits(fdev); | ||
| 814 | if (rc < 0) | ||
| 815 | goto disable_admin; | ||
| 816 | |||
| 817 | pci_save_state(pdev); | ||
| 818 | pci_set_drvdata(pdev, fdev); | ||
| 819 | pcie_print_link_status(pdev); | ||
| 820 | dev_dbg(fdev->dev, "q_depth %u, db_stride %u, max qid %d kern_end_qid %d\n", | ||
| 821 | fdev->q_depth, fdev->db_stride, fdev->max_qid, | ||
| 822 | fdev->kern_end_qid); | ||
| 823 | return 0; | ||
| 824 | |||
| 825 | disable_admin: | ||
| 826 | fun_disable_admin_queue(fdev); | ||
| 827 | free_irq_mgr: | ||
| 828 | pci_clear_master(pdev); | ||
| 829 | bitmap_free(fdev->irq_map); | ||
| 830 | free_irqs: | ||
| 831 | pci_free_irq_vectors(pdev); | ||
| 832 | disable_dev: | ||
| 833 | pci_disable_pcie_error_reporting(pdev); | ||
| 834 | pci_disable_device(pdev); | ||
| 835 | unmap: | ||
| 836 | fun_unmap_bars(fdev); | ||
| 837 | return rc; | ||
| 838 | } | ||
| 839 | EXPORT_SYMBOL(fun_dev_enable); | ||
| 840 | |||
| 841 | MODULE_AUTHOR("Dimitris Michailidis <dmichail@fungible.com>"); | ||
| 842 | MODULE_DESCRIPTION("Core services driver for Fungible devices"); | ||
| 843 | MODULE_LICENSE("Dual BSD/GPL"); | ||
| @@ -0,0 +1,150 @@ | |||
| 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ | ||
| 2 | |||
| 3 | #ifndef _FUNDEV_H | ||
| 4 | #define _FUNDEV_H | ||
| 5 | |||
| 6 | #include <linux/sbitmap.h> | ||
| 7 | #include <linux/spinlock_types.h> | ||
| 8 | #include <linux/workqueue.h> | ||
| 9 | #include "fun_hci.h" | ||
| 10 | |||
| 11 | struct pci_dev; | ||
| 12 | struct fun_dev; | ||
| 13 | struct fun_queue; | ||
| 14 | struct fun_cmd_ctx; | ||
| 15 | struct fun_queue_alloc_req; | ||
| 16 | |||
| 17 | /* doorbell fields */ | ||
| 18 | enum { | ||
| 19 | FUN_DB_QIDX_S = 0, | ||
| 20 | FUN_DB_INTCOAL_ENTRIES_S = 16, | ||
| 21 | FUN_DB_INTCOAL_ENTRIES_M = 0x7f, | ||
| 22 | FUN_DB_INTCOAL_USEC_S = 23, | ||
| 23 | FUN_DB_INTCOAL_USEC_M = 0x7f, | ||
| 24 | FUN_DB_IRQ_S = 30, | ||
| 25 | FUN_DB_IRQ_F = 1 << FUN_DB_IRQ_S, | ||
| 26 | FUN_DB_IRQ_ARM_S = 31, | ||
| 27 | FUN_DB_IRQ_ARM_F = 1U << FUN_DB_IRQ_ARM_S | ||
| 28 | }; | ||
| 29 | |||
| 30 | /* Callback for asynchronous admin commands. | ||
| 31 | * Invoked on reception of command response. | ||
| 32 | */ | ||
| 33 | typedef void (*fun_admin_callback_t)(struct fun_dev *fdev, void *rsp, | ||
| 34 | void *cb_data); | ||
| 35 | |||
| 36 | /* Callback for events/notifications received by an admin queue. */ | ||
| 37 | typedef void (*fun_admin_event_cb)(struct fun_dev *fdev, void *cqe); | ||
| 38 | |||
| 39 | /* Callback for pending work handled by the service task. */ | ||
| 40 | typedef void (*fun_serv_cb)(struct fun_dev *fd); | ||
| 41 | |||
| 42 | /* service task flags */ | ||
| 43 | enum { | ||
| 44 | FUN_SERV_DISABLED, /* service task is disabled */ | ||
| 45 | FUN_SERV_FIRST_AVAIL | ||
| 46 | }; | ||
| 47 | |||
| 48 | /* Driver state associated with a PCI function. */ | ||
| 49 | struct fun_dev { | ||
| 50 | struct device *dev; | ||
| 51 | |||
| 52 | void __iomem *bar; /* start of BAR0 mapping */ | ||
| 53 | u32 __iomem *dbs; /* start of doorbells in BAR0 mapping */ | ||
| 54 | |||
| 55 | /* admin queue */ | ||
| 56 | struct fun_queue *admin_q; | ||
| 57 | struct sbitmap_queue admin_sbq; | ||
| 58 | struct fun_cmd_ctx *cmd_ctx; | ||
| 59 | fun_admin_event_cb adminq_cb; | ||
| 60 | bool suppress_cmds; /* if set don't write commands to SQ */ | ||
| 61 | |||
| 62 | /* address increment between consecutive doorbells, in 4B units */ | ||
| 63 | unsigned int db_stride; | ||
| 64 | |||
| 65 | /* SW versions of device registers */ | ||
| 66 | u32 cc_reg; /* CC register */ | ||
| 67 | u64 cap_reg; /* CAPability register */ | ||
| 68 | |||
| 69 | unsigned int q_depth; /* max queue depth supported by device */ | ||
| 70 | unsigned int max_qid; /* = #queues - 1, separately for SQs and CQs */ | ||
| 71 | unsigned int kern_end_qid; /* last qid in the kernel range + 1 */ | ||
| 72 | |||
| 73 | unsigned int fw_handle; | ||
| 74 | |||
| 75 | /* IRQ manager */ | ||
| 76 | unsigned int num_irqs; | ||
| 77 | unsigned int irqs_avail; | ||
| 78 | spinlock_t irqmgr_lock; | ||
| 79 | unsigned long *irq_map; | ||
| 80 | |||
| 81 | /* The service task handles work that needs a process context */ | ||
| 82 | struct work_struct service_task; | ||
| 83 | unsigned long service_flags; | ||
| 84 | fun_serv_cb serv_cb; | ||
| 85 | }; | ||
| 86 | |||
| 87 | struct fun_dev_params { | ||
| 88 | u8 cqe_size_log2; /* admin q CQE size */ | ||
| 89 | u8 sqe_size_log2; /* admin q SQE size */ | ||
| 90 | |||
| 91 | /* admin q depths */ | ||
| 92 | u16 cq_depth; | ||
| 93 | u16 sq_depth; | ||
| 94 | u16 rq_depth; | ||
| 95 | |||
| 96 | u16 min_msix; /* min vectors needed by requesting driver */ | ||
| 97 | |||
| 98 | fun_admin_event_cb event_cb; | ||
| 99 | fun_serv_cb serv_cb; | ||
| 100 | }; | ||
| 101 | |||
| 102 | /* Return the BAR address of a doorbell. */ | ||
| 103 | static inline u32 __iomem *fun_db_addr(const struct fun_dev *fdev, | ||
| 104 | unsigned int db_index) | ||
| 105 | { | ||
| 106 | return &fdev->dbs[db_index * fdev->db_stride]; | ||
| 107 | } | ||
| 108 | |||
| 109 | /* Return the BAR address of an SQ doorbell. SQ and CQ DBs alternate, | ||
| 110 | * SQs have even DB indices. | ||
| 111 | */ | ||
| 112 | static inline u32 __iomem *fun_sq_db_addr(const struct fun_dev *fdev, | ||
| 113 | unsigned int sqid) | ||
| 114 | { | ||
| 115 | return fun_db_addr(fdev, sqid * 2); | ||
| 116 | } | ||
| 117 | |||
| 118 | static inline u32 __iomem *fun_cq_db_addr(const struct fun_dev *fdev, | ||
| 119 | unsigned int cqid) | ||
| 120 | { | ||
| 121 | return fun_db_addr(fdev, cqid * 2 + 1); | ||
| 122 | } | ||
| 123 | |||
| 124 | int fun_get_res_count(struct fun_dev *fdev, enum fun_admin_op res); | ||
| 125 | int fun_res_destroy(struct fun_dev *fdev, enum fun_admin_op res, | ||
| 126 | unsigned int flags, u32 id); | ||
| 127 | int fun_bind(struct fun_dev *fdev, enum fun_admin_bind_type type0, | ||
| 128 | unsigned int id0, enum fun_admin_bind_type type1, | ||
| 129 | unsigned int id1); | ||
| 130 | |||
| 131 | int fun_submit_admin_cmd(struct fun_dev *fdev, struct fun_admin_req_common *cmd, | ||
| 132 | fun_admin_callback_t cb, void *cb_data, bool wait_ok); | ||
| 133 | int fun_submit_admin_sync_cmd(struct fun_dev *fdev, | ||
| 134 | struct fun_admin_req_common *cmd, void *rsp, | ||
| 135 | size_t rspsize, unsigned int timeout); | ||
| 136 | |||
| 137 | int fun_dev_enable(struct fun_dev *fdev, struct pci_dev *pdev, | ||
| 138 | const struct fun_dev_params *areq, const char *name); | ||
| 139 | void fun_dev_disable(struct fun_dev *fdev); | ||
| 140 | |||
| 141 | int fun_reserve_irqs(struct fun_dev *fdev, unsigned int nirqs, | ||
| 142 | u16 *irq_indices); | ||
| 143 | void fun_release_irqs(struct fun_dev *fdev, unsigned int nirqs, | ||
| 144 | u16 *irq_indices); | ||
| 145 | |||
| 146 | void fun_serv_stop(struct fun_dev *fd); | ||
| 147 | void fun_serv_restart(struct fun_dev *fd); | ||
| 148 | void fun_serv_sched(struct fun_dev *fd); | ||
| 149 | |||
| 150 | #endif /* _FUNDEV_H */ | ||
| @@ -0,0 +1,1202 @@ | |||
| 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ | ||
| 2 | |||
| 3 | #ifndef __FUN_HCI_H | ||
| 4 | #define __FUN_HCI_H | ||
| 5 | |||
| 6 | enum { | ||
| 7 | FUN_HCI_ID_INVALID = 0xffffffff, | ||
| 8 | }; | ||
| 9 | |||
| 10 | enum fun_admin_op { | ||
| 11 | FUN_ADMIN_OP_BIND = 0x1, | ||
| 12 | FUN_ADMIN_OP_EPCQ = 0x11, | ||
| 13 | FUN_ADMIN_OP_EPSQ = 0x12, | ||
| 14 | FUN_ADMIN_OP_PORT = 0x13, | ||
| 15 | FUN_ADMIN_OP_ETH = 0x14, | ||
| 16 | FUN_ADMIN_OP_VI = 0x15, | ||
| 17 | FUN_ADMIN_OP_SWUPGRADE = 0x1f, | ||
| 18 | FUN_ADMIN_OP_RSS = 0x21, | ||
| 19 | FUN_ADMIN_OP_ADI = 0x25, | ||
| 20 | FUN_ADMIN_OP_KTLS = 0x26, | ||
| 21 | }; | ||
| 22 | |||
| 23 | enum { | ||
| 24 | FUN_REQ_COMMON_FLAG_RSP = 0x1, | ||
| 25 | FUN_REQ_COMMON_FLAG_HEAD_WB = 0x2, | ||
| 26 | FUN_REQ_COMMON_FLAG_INT = 0x4, | ||
| 27 | FUN_REQ_COMMON_FLAG_CQE_IN_RQBUF = 0x8, | ||
| 28 | }; | ||
| 29 | |||
| 30 | struct fun_admin_req_common { | ||
| 31 | __u8 op; | ||
| 32 | __u8 len8; | ||
| 33 | __be16 flags; | ||
| 34 | __u8 suboff8; | ||
| 35 | __u8 rsvd0; | ||
| 36 | __be16 cid; | ||
| 37 | }; | ||
| 38 | |||
| 39 | #define FUN_ADMIN_REQ_COMMON_INIT(_op, _len8, _flags, _suboff8, _cid) \ | ||
| 40 | (struct fun_admin_req_common) { \ | ||
| 41 | .op = (_op), .len8 = (_len8), .flags = cpu_to_be16(_flags), \ | ||
| 42 | .suboff8 = (_suboff8), .cid = cpu_to_be16(_cid), \ | ||
| 43 | } | ||
| 44 | |||
| 45 | #define FUN_ADMIN_REQ_COMMON_INIT2(_op, _len) \ | ||
| 46 | (struct fun_admin_req_common) { \ | ||
| 47 | .op = (_op), .len8 = (_len) / 8, \ | ||
| 48 | } | ||
| 49 | |||
| 50 | struct fun_admin_rsp_common { | ||
| 51 | __u8 op; | ||
| 52 | __u8 len8; | ||
| 53 | __be16 flags; | ||
| 54 | __u8 suboff8; | ||
| 55 | __u8 ret; | ||
| 56 | __be16 cid; | ||
| 57 | }; | ||
| 58 | |||
| 59 | struct fun_admin_write48_req { | ||
| 60 | __be64 key_to_data; | ||
| 61 | }; | ||
| 62 | |||
| 63 | #define FUN_ADMIN_WRITE48_REQ_KEY_S 56U | ||
| 64 | #define FUN_ADMIN_WRITE48_REQ_KEY_M 0xff | ||
| 65 | #define FUN_ADMIN_WRITE48_REQ_KEY_P_NOSWAP(x) \ | ||
| 66 | (((__u64)x) << FUN_ADMIN_WRITE48_REQ_KEY_S) | ||
| 67 | |||
| 68 | #define FUN_ADMIN_WRITE48_REQ_DATA_S 0U | ||
| 69 | #define FUN_ADMIN_WRITE48_REQ_DATA_M 0xffffffffffff | ||
| 70 | #define FUN_ADMIN_WRITE48_REQ_DATA_P_NOSWAP(x) \ | ||
| 71 | (((__u64)x) << FUN_ADMIN_WRITE48_REQ_DATA_S) | ||
| 72 | |||
| 73 | #define FUN_ADMIN_WRITE48_REQ_INIT(key, data) \ | ||
| 74 | (struct fun_admin_write48_req) { \ | ||
| 75 | .key_to_data = cpu_to_be64( \ | ||
| 76 | FUN_ADMIN_WRITE48_REQ_KEY_P_NOSWAP(key) | \ | ||
| 77 | FUN_ADMIN_WRITE48_REQ_DATA_P_NOSWAP(data)), \ | ||
| 78 | } | ||
| 79 | |||
| 80 | struct fun_admin_write48_rsp { | ||
| 81 | __be64 key_to_data; | ||
| 82 | }; | ||
| 83 | |||
| 84 | struct fun_admin_read48_req { | ||
| 85 | __be64 key_pack; | ||
| 86 | }; | ||
| 87 | |||
| 88 | #define FUN_ADMIN_READ48_REQ_KEY_S 56U | ||
| 89 | #define FUN_ADMIN_READ48_REQ_KEY_M 0xff | ||
| 90 | #define FUN_ADMIN_READ48_REQ_KEY_P_NOSWAP(x) \ | ||
| 91 | (((__u64)x) << FUN_ADMIN_READ48_REQ_KEY_S) | ||
| 92 | |||
| 93 | #define FUN_ADMIN_READ48_REQ_INIT(key) \ | ||
| 94 | (struct fun_admin_read48_req) { \ | ||
| 95 | .key_pack = \ | ||
| 96 | cpu_to_be64(FUN_ADMIN_READ48_REQ_KEY_P_NOSWAP(key)), \ | ||
| 97 | } | ||
| 98 | |||
| 99 | struct fun_admin_read48_rsp { | ||
| 100 | __be64 key_to_data; | ||
| 101 | }; | ||
| 102 | |||
| 103 | #define FUN_ADMIN_READ48_RSP_KEY_S 56U | ||
| 104 | #define FUN_ADMIN_READ48_RSP_KEY_M 0xff | ||
| 105 | #define FUN_ADMIN_READ48_RSP_KEY_G(x) \ | ||
| 106 | ((be64_to_cpu(x) >> FUN_ADMIN_READ48_RSP_KEY_S) & \ | ||
| 107 | FUN_ADMIN_READ48_RSP_KEY_M) | ||
| 108 | |||
| 109 | #define FUN_ADMIN_READ48_RSP_RET_S 48U | ||
| 110 | #define FUN_ADMIN_READ48_RSP_RET_M 0xff | ||
| 111 | #define FUN_ADMIN_READ48_RSP_RET_G(x) \ | ||
| 112 | ((be64_to_cpu(x) >> FUN_ADMIN_READ48_RSP_RET_S) & \ | ||
| 113 | FUN_ADMIN_READ48_RSP_RET_M) | ||
| 114 | |||
| 115 | #define FUN_ADMIN_READ48_RSP_DATA_S 0U | ||
| 116 | #define FUN_ADMIN_READ48_RSP_DATA_M 0xffffffffffff | ||
| 117 | #define FUN_ADMIN_READ48_RSP_DATA_G(x) \ | ||
| 118 | ((be64_to_cpu(x) >> FUN_ADMIN_READ48_RSP_DATA_S) & \ | ||
| 119 | FUN_ADMIN_READ48_RSP_DATA_M) | ||
| 120 | |||
| 121 | enum fun_admin_bind_type { | ||
| 122 | FUN_ADMIN_BIND_TYPE_EPCQ = 0x1, | ||
| 123 | FUN_ADMIN_BIND_TYPE_EPSQ = 0x2, | ||
| 124 | FUN_ADMIN_BIND_TYPE_PORT = 0x3, | ||
| 125 | FUN_ADMIN_BIND_TYPE_RSS = 0x4, | ||
| 126 | FUN_ADMIN_BIND_TYPE_VI = 0x5, | ||
| 127 | FUN_ADMIN_BIND_TYPE_ETH = 0x6, | ||
| 128 | }; | ||
| 129 | |||
| 130 | struct fun_admin_bind_entry { | ||
| 131 | __u8 type; | ||
| 132 | __u8 rsvd0[3]; | ||
| 133 | __be32 id; | ||
| 134 | }; | ||
| 135 | |||
| 136 | #define FUN_ADMIN_BIND_ENTRY_INIT(_type, _id) \ | ||
| 137 | (struct fun_admin_bind_entry) { \ | ||
| 138 | .type = (_type), .id = cpu_to_be32(_id), \ | ||
| 139 | } | ||
| 140 | |||
| 141 | struct fun_admin_bind_req { | ||
| 142 | struct fun_admin_req_common common; | ||
| 143 | struct fun_admin_bind_entry entry[]; | ||
| 144 | }; | ||
| 145 | |||
| 146 | struct fun_admin_bind_rsp { | ||
| 147 | struct fun_admin_rsp_common bind_rsp_common; | ||
| 148 | }; | ||
| 149 | |||
| 150 | struct fun_admin_simple_subop { | ||
| 151 | __u8 subop; | ||
| 152 | __u8 rsvd0; | ||
| 153 | __be16 flags; | ||
| 154 | __be32 data; | ||
| 155 | }; | ||
| 156 | |||
| 157 | #define FUN_ADMIN_SIMPLE_SUBOP_INIT(_subop, _flags, _data) \ | ||
| 158 | (struct fun_admin_simple_subop) { \ | ||
| 159 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 160 | .data = cpu_to_be32(_data), \ | ||
| 161 | } | ||
| 162 | |||
| 163 | enum fun_admin_subop { | ||
| 164 | FUN_ADMIN_SUBOP_CREATE = 0x10, | ||
| 165 | FUN_ADMIN_SUBOP_DESTROY = 0x11, | ||
| 166 | FUN_ADMIN_SUBOP_MODIFY = 0x12, | ||
| 167 | FUN_ADMIN_SUBOP_RES_COUNT = 0x14, | ||
| 168 | FUN_ADMIN_SUBOP_READ = 0x15, | ||
| 169 | FUN_ADMIN_SUBOP_WRITE = 0x16, | ||
| 170 | FUN_ADMIN_SUBOP_NOTIFY = 0x17, | ||
| 171 | }; | ||
| 172 | |||
| 173 | enum { | ||
| 174 | FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR = 0x1, | ||
| 175 | }; | ||
| 176 | |||
| 177 | struct fun_admin_generic_destroy_req { | ||
| 178 | struct fun_admin_req_common common; | ||
| 179 | struct fun_admin_simple_subop destroy; | ||
| 180 | }; | ||
| 181 | |||
| 182 | struct fun_admin_generic_create_rsp { | ||
| 183 | struct fun_admin_rsp_common common; | ||
| 184 | |||
| 185 | __u8 subop; | ||
| 186 | __u8 rsvd0; | ||
| 187 | __be16 flags; | ||
| 188 | __be32 id; | ||
| 189 | }; | ||
| 190 | |||
| 191 | struct fun_admin_res_count_req { | ||
| 192 | struct fun_admin_req_common common; | ||
| 193 | struct fun_admin_simple_subop count; | ||
| 194 | }; | ||
| 195 | |||
| 196 | struct fun_admin_res_count_rsp { | ||
| 197 | struct fun_admin_rsp_common common; | ||
| 198 | struct fun_admin_simple_subop count; | ||
| 199 | }; | ||
| 200 | |||
| 201 | enum { | ||
| 202 | FUN_ADMIN_EPCQ_CREATE_FLAG_INT_EPCQ = 0x2, | ||
| 203 | FUN_ADMIN_EPCQ_CREATE_FLAG_ENTRY_WR_TPH = 0x4, | ||
| 204 | FUN_ADMIN_EPCQ_CREATE_FLAG_SL_WR_TPH = 0x8, | ||
| 205 | FUN_ADMIN_EPCQ_CREATE_FLAG_RQ = 0x80, | ||
| 206 | FUN_ADMIN_EPCQ_CREATE_FLAG_INT_IQ = 0x100, | ||
| 207 | FUN_ADMIN_EPCQ_CREATE_FLAG_INT_NOARM = 0x200, | ||
| 208 | FUN_ADMIN_EPCQ_CREATE_FLAG_DROP_ON_OVERFLOW = 0x400, | ||
| 209 | }; | ||
| 210 | |||
| 211 | struct fun_admin_epcq_req { | ||
| 212 | struct fun_admin_req_common common; | ||
| 213 | union epcq_req_subop { | ||
| 214 | struct fun_admin_epcq_create_req { | ||
| 215 | __u8 subop; | ||
| 216 | __u8 rsvd0; | ||
| 217 | __be16 flags; | ||
| 218 | __be32 id; | ||
| 219 | |||
| 220 | __be32 epsqid; | ||
| 221 | __u8 rsvd1; | ||
| 222 | __u8 entry_size_log2; | ||
| 223 | __be16 nentries; | ||
| 224 | |||
| 225 | __be64 address; | ||
| 226 | |||
| 227 | __be16 tailroom; /* per packet tailroom in bytes */ | ||
| 228 | __u8 headroom; /* per packet headroom in 2B units */ | ||
| 229 | __u8 intcoal_kbytes; | ||
| 230 | __u8 intcoal_holdoff_nentries; | ||
| 231 | __u8 intcoal_holdoff_usecs; | ||
| 232 | __be16 intid; | ||
| 233 | |||
| 234 | __be32 scan_start_id; | ||
| 235 | __be32 scan_end_id; | ||
| 236 | |||
| 237 | __be16 tph_cpuid; | ||
| 238 | __u8 rsvd3[6]; | ||
| 239 | } create; | ||
| 240 | |||
| 241 | struct fun_admin_epcq_modify_req { | ||
| 242 | __u8 subop; | ||
| 243 | __u8 rsvd0; | ||
| 244 | __be16 flags; | ||
| 245 | __be32 id; | ||
| 246 | |||
| 247 | __be16 headroom; /* headroom in bytes */ | ||
| 248 | __u8 rsvd1[6]; | ||
| 249 | } modify; | ||
| 250 | } u; | ||
| 251 | }; | ||
| 252 | |||
| 253 | #define FUN_ADMIN_EPCQ_CREATE_REQ_INIT( \ | ||
| 254 | _subop, _flags, _id, _epsqid, _entry_size_log2, _nentries, _address, \ | ||
| 255 | _tailroom, _headroom, _intcoal_kbytes, _intcoal_holdoff_nentries, \ | ||
| 256 | _intcoal_holdoff_usecs, _intid, _scan_start_id, _scan_end_id, \ | ||
| 257 | _tph_cpuid) \ | ||
| 258 | (struct fun_admin_epcq_create_req) { \ | ||
| 259 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 260 | .id = cpu_to_be32(_id), .epsqid = cpu_to_be32(_epsqid), \ | ||
| 261 | .entry_size_log2 = _entry_size_log2, \ | ||
| 262 | .nentries = cpu_to_be16(_nentries), \ | ||
| 263 | .address = cpu_to_be64(_address), \ | ||
| 264 | .tailroom = cpu_to_be16(_tailroom), .headroom = _headroom, \ | ||
| 265 | .intcoal_kbytes = _intcoal_kbytes, \ | ||
| 266 | .intcoal_holdoff_nentries = _intcoal_holdoff_nentries, \ | ||
| 267 | .intcoal_holdoff_usecs = _intcoal_holdoff_usecs, \ | ||
| 268 | .intid = cpu_to_be16(_intid), \ | ||
| 269 | .scan_start_id = cpu_to_be32(_scan_start_id), \ | ||
| 270 | .scan_end_id = cpu_to_be32(_scan_end_id), \ | ||
| 271 | .tph_cpuid = cpu_to_be16(_tph_cpuid), \ | ||
| 272 | } | ||
| 273 | |||
| 274 | #define FUN_ADMIN_EPCQ_MODIFY_REQ_INIT(_subop, _flags, _id, _headroom) \ | ||
| 275 | (struct fun_admin_epcq_modify_req) { \ | ||
| 276 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 277 | .id = cpu_to_be32(_id), .headroom = cpu_to_be16(_headroom), \ | ||
| 278 | } | ||
| 279 | |||
| 280 | enum { | ||
| 281 | FUN_ADMIN_EPSQ_CREATE_FLAG_INT_EPSQ = 0x2, | ||
| 282 | FUN_ADMIN_EPSQ_CREATE_FLAG_ENTRY_RD_TPH = 0x4, | ||
| 283 | FUN_ADMIN_EPSQ_CREATE_FLAG_GL_RD_TPH = 0x8, | ||
| 284 | FUN_ADMIN_EPSQ_CREATE_FLAG_HEAD_WB_ADDRESS = 0x10, | ||
| 285 | FUN_ADMIN_EPSQ_CREATE_FLAG_HEAD_WB_ADDRESS_TPH = 0x20, | ||
| 286 | FUN_ADMIN_EPSQ_CREATE_FLAG_HEAD_WB_EPCQ = 0x40, | ||
| 287 | FUN_ADMIN_EPSQ_CREATE_FLAG_RQ = 0x80, | ||
| 288 | FUN_ADMIN_EPSQ_CREATE_FLAG_INT_IQ = 0x100, | ||
| 289 | FUN_ADMIN_EPSQ_CREATE_FLAG_NO_CMPL = 0x200, | ||
| 290 | }; | ||
| 291 | |||
| 292 | struct fun_admin_epsq_req { | ||
| 293 | struct fun_admin_req_common common; | ||
| 294 | |||
| 295 | union epsq_req_subop { | ||
| 296 | struct fun_admin_epsq_create_req { | ||
| 297 | __u8 subop; | ||
| 298 | __u8 rsvd0; | ||
| 299 | __be16 flags; | ||
| 300 | __be32 id; | ||
| 301 | |||
| 302 | __be32 epcqid; | ||
| 303 | __u8 rsvd1; | ||
| 304 | __u8 entry_size_log2; | ||
| 305 | __be16 nentries; | ||
| 306 | |||
| 307 | __be64 address; /* DMA address of epsq */ | ||
| 308 | |||
| 309 | __u8 rsvd2[3]; | ||
| 310 | __u8 intcoal_kbytes; | ||
| 311 | __u8 intcoal_holdoff_nentries; | ||
| 312 | __u8 intcoal_holdoff_usecs; | ||
| 313 | __be16 intid; | ||
| 314 | |||
| 315 | __be32 scan_start_id; | ||
| 316 | __be32 scan_end_id; | ||
| 317 | |||
| 318 | __u8 rsvd3[4]; | ||
| 319 | __be16 tph_cpuid; | ||
| 320 | __u8 buf_size_log2; /* log2 of RQ buffer size */ | ||
| 321 | __u8 head_wb_size_log2; /* log2 of head write back size */ | ||
| 322 | |||
| 323 | __be64 head_wb_address; /* DMA address for head writeback */ | ||
| 324 | } create; | ||
| 325 | } u; | ||
| 326 | }; | ||
| 327 | |||
| 328 | #define FUN_ADMIN_EPSQ_CREATE_REQ_INIT( \ | ||
| 329 | _subop, _flags, _id, _epcqid, _entry_size_log2, _nentries, _address, \ | ||
| 330 | _intcoal_kbytes, _intcoal_holdoff_nentries, _intcoal_holdoff_usecs, \ | ||
| 331 | _intid, _scan_start_id, _scan_end_id, _tph_cpuid, _buf_size_log2, \ | ||
| 332 | _head_wb_size_log2, _head_wb_address) \ | ||
| 333 | (struct fun_admin_epsq_create_req) { \ | ||
| 334 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 335 | .id = cpu_to_be32(_id), .epcqid = cpu_to_be32(_epcqid), \ | ||
| 336 | .entry_size_log2 = _entry_size_log2, \ | ||
| 337 | .nentries = cpu_to_be16(_nentries), \ | ||
| 338 | .address = cpu_to_be64(_address), \ | ||
| 339 | .intcoal_kbytes = _intcoal_kbytes, \ | ||
| 340 | .intcoal_holdoff_nentries = _intcoal_holdoff_nentries, \ | ||
| 341 | .intcoal_holdoff_usecs = _intcoal_holdoff_usecs, \ | ||
| 342 | .intid = cpu_to_be16(_intid), \ | ||
| 343 | .scan_start_id = cpu_to_be32(_scan_start_id), \ | ||
| 344 | .scan_end_id = cpu_to_be32(_scan_end_id), \ | ||
| 345 | .tph_cpuid = cpu_to_be16(_tph_cpuid), \ | ||
| 346 | .buf_size_log2 = _buf_size_log2, \ | ||
| 347 | .head_wb_size_log2 = _head_wb_size_log2, \ | ||
| 348 | .head_wb_address = cpu_to_be64(_head_wb_address), \ | ||
| 349 | } | ||
| 350 | |||
| 351 | enum { | ||
| 352 | FUN_PORT_CAP_OFFLOADS = 0x1, | ||
| 353 | FUN_PORT_CAP_STATS = 0x2, | ||
| 354 | FUN_PORT_CAP_LOOPBACK = 0x4, | ||
| 355 | FUN_PORT_CAP_VPORT = 0x8, | ||
| 356 | FUN_PORT_CAP_TX_PAUSE = 0x10, | ||
| 357 | FUN_PORT_CAP_RX_PAUSE = 0x20, | ||
| 358 | FUN_PORT_CAP_AUTONEG = 0x40, | ||
| 359 | FUN_PORT_CAP_RSS = 0x80, | ||
| 360 | FUN_PORT_CAP_VLAN_OFFLOADS = 0x100, | ||
| 361 | FUN_PORT_CAP_ENCAP_OFFLOADS = 0x200, | ||
| 362 | FUN_PORT_CAP_1000_X = 0x1000, | ||
| 363 | FUN_PORT_CAP_10G_R = 0x2000, | ||
| 364 | FUN_PORT_CAP_40G_R4 = 0x4000, | ||
| 365 | FUN_PORT_CAP_25G_R = 0x8000, | ||
| 366 | FUN_PORT_CAP_50G_R2 = 0x10000, | ||
| 367 | FUN_PORT_CAP_50G_R = 0x20000, | ||
| 368 | FUN_PORT_CAP_100G_R4 = 0x40000, | ||
| 369 | FUN_PORT_CAP_100G_R2 = 0x80000, | ||
| 370 | FUN_PORT_CAP_200G_R4 = 0x100000, | ||
| 371 | FUN_PORT_CAP_FEC_NONE = 0x10000000, | ||
| 372 | FUN_PORT_CAP_FEC_FC = 0x20000000, | ||
| 373 | FUN_PORT_CAP_FEC_RS = 0x40000000, | ||
| 374 | }; | ||
| 375 | |||
| 376 | enum fun_port_brkout_mode { | ||
| 377 | FUN_PORT_BRKMODE_NA = 0x0, | ||
| 378 | FUN_PORT_BRKMODE_NONE = 0x1, | ||
| 379 | FUN_PORT_BRKMODE_2X = 0x2, | ||
| 380 | FUN_PORT_BRKMODE_4X = 0x3, | ||
| 381 | }; | ||
| 382 | |||
| 383 | enum { | ||
| 384 | FUN_PORT_SPEED_AUTO = 0x0, | ||
| 385 | FUN_PORT_SPEED_10M = 0x1, | ||
| 386 | FUN_PORT_SPEED_100M = 0x2, | ||
| 387 | FUN_PORT_SPEED_1G = 0x4, | ||
| 388 | FUN_PORT_SPEED_10G = 0x8, | ||
| 389 | FUN_PORT_SPEED_25G = 0x10, | ||
| 390 | FUN_PORT_SPEED_40G = 0x20, | ||
| 391 | FUN_PORT_SPEED_50G = 0x40, | ||
| 392 | FUN_PORT_SPEED_100G = 0x80, | ||
| 393 | FUN_PORT_SPEED_200G = 0x100, | ||
| 394 | }; | ||
| 395 | |||
| 396 | enum fun_port_duplex_mode { | ||
| 397 | FUN_PORT_FULL_DUPLEX = 0x0, | ||
| 398 | FUN_PORT_HALF_DUPLEX = 0x1, | ||
| 399 | }; | ||
| 400 | |||
| 401 | enum { | ||
| 402 | FUN_PORT_FEC_NA = 0x0, | ||
| 403 | FUN_PORT_FEC_OFF = 0x1, | ||
| 404 | FUN_PORT_FEC_RS = 0x2, | ||
| 405 | FUN_PORT_FEC_FC = 0x4, | ||
| 406 | FUN_PORT_FEC_AUTO = 0x8, | ||
| 407 | }; | ||
| 408 | |||
| 409 | enum fun_port_link_status { | ||
| 410 | FUN_PORT_LINK_UP = 0x0, | ||
| 411 | FUN_PORT_LINK_UP_WITH_ERR = 0x1, | ||
| 412 | FUN_PORT_LINK_DOWN = 0x2, | ||
| 413 | }; | ||
| 414 | |||
| 415 | enum fun_port_led_type { | ||
| 416 | FUN_PORT_LED_OFF = 0x0, | ||
| 417 | FUN_PORT_LED_AMBER = 0x1, | ||
| 418 | FUN_PORT_LED_GREEN = 0x2, | ||
| 419 | FUN_PORT_LED_BEACON_ON = 0x3, | ||
| 420 | FUN_PORT_LED_BEACON_OFF = 0x4, | ||
| 421 | }; | ||
| 422 | |||
| 423 | enum { | ||
| 424 | FUN_PORT_FLAG_MAC_DOWN = 0x1, | ||
| 425 | FUN_PORT_FLAG_MAC_UP = 0x2, | ||
| 426 | FUN_PORT_FLAG_NH_DOWN = 0x4, | ||
| 427 | FUN_PORT_FLAG_NH_UP = 0x8, | ||
| 428 | }; | ||
| 429 | |||
| 430 | enum { | ||
| 431 | FUN_PORT_FLAG_ENABLE_NOTIFY = 0x1, | ||
| 432 | }; | ||
| 433 | |||
| 434 | enum fun_port_lane_attr { | ||
| 435 | FUN_PORT_LANE_1 = 0x1, | ||
| 436 | FUN_PORT_LANE_2 = 0x2, | ||
| 437 | FUN_PORT_LANE_4 = 0x4, | ||
| 438 | FUN_PORT_LANE_SPEED_10G = 0x100, | ||
| 439 | FUN_PORT_LANE_SPEED_25G = 0x200, | ||
| 440 | FUN_PORT_LANE_SPEED_50G = 0x400, | ||
| 441 | FUN_PORT_LANE_SPLIT = 0x8000, | ||
| 442 | }; | ||
| 443 | |||
| 444 | enum fun_admin_port_subop { | ||
| 445 | FUN_ADMIN_PORT_SUBOP_INETADDR_EVENT = 0x24, | ||
| 446 | }; | ||
| 447 | |||
| 448 | enum fun_admin_port_key { | ||
| 449 | FUN_ADMIN_PORT_KEY_ILLEGAL = 0x0, | ||
| 450 | FUN_ADMIN_PORT_KEY_MTU = 0x1, | ||
| 451 | FUN_ADMIN_PORT_KEY_FEC = 0x2, | ||
| 452 | FUN_ADMIN_PORT_KEY_SPEED = 0x3, | ||
| 453 | FUN_ADMIN_PORT_KEY_DEBOUNCE = 0x4, | ||
| 454 | FUN_ADMIN_PORT_KEY_DUPLEX = 0x5, | ||
| 455 | FUN_ADMIN_PORT_KEY_MACADDR = 0x6, | ||
| 456 | FUN_ADMIN_PORT_KEY_LINKMODE = 0x7, | ||
| 457 | FUN_ADMIN_PORT_KEY_BREAKOUT = 0x8, | ||
| 458 | FUN_ADMIN_PORT_KEY_ENABLE = 0x9, | ||
| 459 | FUN_ADMIN_PORT_KEY_DISABLE = 0xa, | ||
| 460 | FUN_ADMIN_PORT_KEY_ERR_DISABLE = 0xb, | ||
| 461 | FUN_ADMIN_PORT_KEY_CAPABILITIES = 0xc, | ||
| 462 | FUN_ADMIN_PORT_KEY_LP_CAPABILITIES = 0xd, | ||
| 463 | FUN_ADMIN_PORT_KEY_STATS_DMA_LOW = 0xe, | ||
| 464 | FUN_ADMIN_PORT_KEY_STATS_DMA_HIGH = 0xf, | ||
| 465 | FUN_ADMIN_PORT_KEY_LANE_ATTRS = 0x10, | ||
| 466 | FUN_ADMIN_PORT_KEY_LED = 0x11, | ||
| 467 | FUN_ADMIN_PORT_KEY_ADVERT = 0x12, | ||
| 468 | }; | ||
| 469 | |||
| 470 | struct fun_subop_imm { | ||
| 471 | __u8 subop; /* see fun_data_subop enum */ | ||
| 472 | __u8 flags; | ||
| 473 | __u8 nsgl; | ||
| 474 | __u8 rsvd0; | ||
| 475 | __be32 len; | ||
| 476 | |||
| 477 | __u8 data[]; | ||
| 478 | }; | ||
| 479 | |||
| 480 | enum fun_subop_sgl_flags { | ||
| 481 | FUN_SUBOP_SGL_USE_OFF8 = 0x1, | ||
| 482 | FUN_SUBOP_FLAG_FREE_BUF = 0x2, | ||
| 483 | FUN_SUBOP_FLAG_IS_REFBUF = 0x4, | ||
| 484 | FUN_SUBOP_SGL_FLAG_LOCAL = 0x8, | ||
| 485 | }; | ||
| 486 | |||
| 487 | enum fun_data_op { | ||
| 488 | FUN_DATAOP_INVALID = 0x0, | ||
| 489 | FUN_DATAOP_SL = 0x1, /* scatter */ | ||
| 490 | FUN_DATAOP_GL = 0x2, /* gather */ | ||
| 491 | FUN_DATAOP_SGL = 0x3, /* scatter-gather */ | ||
| 492 | FUN_DATAOP_IMM = 0x4, /* immediate data */ | ||
| 493 | FUN_DATAOP_RQBUF = 0x8, /* rq buffer */ | ||
| 494 | }; | ||
| 495 | |||
| 496 | struct fun_dataop_gl { | ||
| 497 | __u8 subop; | ||
| 498 | __u8 flags; | ||
| 499 | __be16 sgl_off; | ||
| 500 | __be32 sgl_len; | ||
| 501 | |||
| 502 | __be64 sgl_data; | ||
| 503 | }; | ||
| 504 | |||
| 505 | static inline void fun_dataop_gl_init(struct fun_dataop_gl *s, u8 flags, | ||
| 506 | u16 sgl_off, u32 sgl_len, u64 sgl_data) | ||
| 507 | { | ||
| 508 | s->subop = FUN_DATAOP_GL; | ||
| 509 | s->flags = flags; | ||
| 510 | s->sgl_off = cpu_to_be16(sgl_off); | ||
| 511 | s->sgl_len = cpu_to_be32(sgl_len); | ||
| 512 | s->sgl_data = cpu_to_be64(sgl_data); | ||
| 513 | } | ||
| 514 | |||
| 515 | struct fun_dataop_imm { | ||
| 516 | __u8 subop; | ||
| 517 | __u8 flags; | ||
| 518 | __be16 rsvd0; | ||
| 519 | __be32 sgl_len; | ||
| 520 | }; | ||
| 521 | |||
| 522 | struct fun_subop_sgl { | ||
| 523 | __u8 subop; | ||
| 524 | __u8 flags; | ||
| 525 | __u8 nsgl; | ||
| 526 | __u8 rsvd0; | ||
| 527 | __be32 sgl_len; | ||
| 528 | |||
| 529 | __be64 sgl_data; | ||
| 530 | }; | ||
| 531 | |||
| 532 | #define FUN_SUBOP_SGL_INIT(_subop, _flags, _nsgl, _sgl_len, _sgl_data) \ | ||
| 533 | (struct fun_subop_sgl) { \ | ||
| 534 | .subop = (_subop), .flags = (_flags), .nsgl = (_nsgl), \ | ||
| 535 | .sgl_len = cpu_to_be32(_sgl_len), \ | ||
| 536 | .sgl_data = cpu_to_be64(_sgl_data), \ | ||
| 537 | } | ||
| 538 | |||
| 539 | struct fun_dataop_rqbuf { | ||
| 540 | __u8 subop; | ||
| 541 | __u8 rsvd0; | ||
| 542 | __be16 cid; | ||
| 543 | __be32 bufoff; | ||
| 544 | }; | ||
| 545 | |||
| 546 | struct fun_dataop_hdr { | ||
| 547 | __u8 nsgl; | ||
| 548 | __u8 flags; | ||
| 549 | __u8 ngather; | ||
| 550 | __u8 nscatter; | ||
| 551 | __be32 total_len; | ||
| 552 | |||
| 553 | struct fun_dataop_imm imm[]; | ||
| 554 | }; | ||
| 555 | |||
| 556 | #define FUN_DATAOP_HDR_INIT(_nsgl, _flags, _ngather, _nscatter, _total_len) \ | ||
| 557 | (struct fun_dataop_hdr) { \ | ||
| 558 | .nsgl = _nsgl, .flags = _flags, .ngather = _ngather, \ | ||
| 559 | .nscatter = _nscatter, .total_len = cpu_to_be32(_total_len), \ | ||
| 560 | } | ||
| 561 | |||
| 562 | enum fun_port_inetaddr_event_type { | ||
| 563 | FUN_PORT_INETADDR_ADD = 0x1, | ||
| 564 | FUN_PORT_INETADDR_DEL = 0x2, | ||
| 565 | }; | ||
| 566 | |||
| 567 | enum fun_port_inetaddr_addr_family { | ||
| 568 | FUN_PORT_INETADDR_IPV4 = 0x1, | ||
| 569 | FUN_PORT_INETADDR_IPV6 = 0x2, | ||
| 570 | }; | ||
| 571 | |||
| 572 | struct fun_admin_port_req { | ||
| 573 | struct fun_admin_req_common common; | ||
| 574 | |||
| 575 | union port_req_subop { | ||
| 576 | struct fun_admin_port_create_req { | ||
| 577 | __u8 subop; | ||
| 578 | __u8 rsvd0; | ||
| 579 | __be16 flags; | ||
| 580 | __be32 id; | ||
| 581 | } create; | ||
| 582 | struct fun_admin_port_write_req { | ||
| 583 | __u8 subop; | ||
| 584 | __u8 rsvd0; | ||
| 585 | __be16 flags; | ||
| 586 | __be32 id; /* portid */ | ||
| 587 | |||
| 588 | struct fun_admin_write48_req write48[]; | ||
| 589 | } write; | ||
| 590 | struct fun_admin_port_read_req { | ||
| 591 | __u8 subop; | ||
| 592 | __u8 rsvd0; | ||
| 593 | __be16 flags; | ||
| 594 | __be32 id; /* portid */ | ||
| 595 | |||
| 596 | struct fun_admin_read48_req read48[]; | ||
| 597 | } read; | ||
| 598 | struct fun_admin_port_inetaddr_event_req { | ||
| 599 | __u8 subop; | ||
| 600 | __u8 rsvd0; | ||
| 601 | __u8 event_type; | ||
| 602 | __u8 addr_family; | ||
| 603 | __be32 id; | ||
| 604 | |||
| 605 | __u8 addr[]; | ||
| 606 | } inetaddr_event; | ||
| 607 | } u; | ||
| 608 | }; | ||
| 609 | |||
| 610 | #define FUN_ADMIN_PORT_CREATE_REQ_INIT(_subop, _flags, _id) \ | ||
| 611 | (struct fun_admin_port_create_req) { \ | ||
| 612 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 613 | .id = cpu_to_be32(_id), \ | ||
| 614 | } | ||
| 615 | |||
| 616 | #define FUN_ADMIN_PORT_WRITE_REQ_INIT(_subop, _flags, _id) \ | ||
| 617 | (struct fun_admin_port_write_req) { \ | ||
| 618 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 619 | .id = cpu_to_be32(_id), \ | ||
| 620 | } | ||
| 621 | |||
| 622 | #define FUN_ADMIN_PORT_READ_REQ_INIT(_subop, _flags, _id) \ | ||
| 623 | (struct fun_admin_port_read_req) { \ | ||
| 624 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 625 | .id = cpu_to_be32(_id), \ | ||
| 626 | } | ||
| 627 | |||
| 628 | struct fun_admin_port_rsp { | ||
| 629 | struct fun_admin_rsp_common common; | ||
| 630 | |||
| 631 | union port_rsp_subop { | ||
| 632 | struct fun_admin_port_create_rsp { | ||
| 633 | __u8 subop; | ||
| 634 | __u8 rsvd0[3]; | ||
| 635 | __be32 id; | ||
| 636 | |||
| 637 | __be16 lport; | ||
| 638 | __u8 rsvd1[6]; | ||
| 639 | } create; | ||
| 640 | struct fun_admin_port_write_rsp { | ||
| 641 | __u8 subop; | ||
| 642 | __u8 rsvd0[3]; | ||
| 643 | __be32 id; /* portid */ | ||
| 644 | |||
| 645 | struct fun_admin_write48_rsp write48[]; | ||
| 646 | } write; | ||
| 647 | struct fun_admin_port_read_rsp { | ||
| 648 | __u8 subop; | ||
| 649 | __u8 rsvd0[3]; | ||
| 650 | __be32 id; /* portid */ | ||
| 651 | |||
| 652 | struct fun_admin_read48_rsp read48[]; | ||
| 653 | } read; | ||
| 654 | struct fun_admin_port_inetaddr_event_rsp { | ||
| 655 | __u8 subop; | ||
| 656 | __u8 rsvd0[3]; | ||
| 657 | __be32 id; /* portid */ | ||
| 658 | } inetaddr_event; | ||
| 659 | } u; | ||
| 660 | }; | ||
| 661 | |||
| 662 | enum fun_xcvr_type { | ||
| 663 | FUN_XCVR_BASET = 0x0, | ||
| 664 | FUN_XCVR_CU = 0x1, | ||
| 665 | FUN_XCVR_SMF = 0x2, | ||
| 666 | FUN_XCVR_MMF = 0x3, | ||
| 667 | FUN_XCVR_AOC = 0x4, | ||
| 668 | FUN_XCVR_SFPP = 0x10, /* SFP+ or later */ | ||
| 669 | FUN_XCVR_QSFPP = 0x11, /* QSFP+ or later */ | ||
| 670 | FUN_XCVR_QSFPDD = 0x12, /* QSFP-DD */ | ||
| 671 | }; | ||
| 672 | |||
| 673 | struct fun_admin_port_notif { | ||
| 674 | struct fun_admin_rsp_common common; | ||
| 675 | |||
| 676 | __u8 subop; | ||
| 677 | __u8 rsvd0; | ||
| 678 | __be16 id; | ||
| 679 | __be32 speed; /* in 10 Mbps units */ | ||
| 680 | |||
| 681 | __u8 link_state; | ||
| 682 | __u8 missed_events; | ||
| 683 | __u8 link_down_reason; | ||
| 684 | __u8 xcvr_type; | ||
| 685 | __u8 flow_ctrl; | ||
| 686 | __u8 fec; | ||
| 687 | __u8 active_lanes; | ||
| 688 | __u8 rsvd1; | ||
| 689 | |||
| 690 | __be64 advertising; | ||
| 691 | |||
| 692 | __be64 lp_advertising; | ||
| 693 | }; | ||
| 694 | |||
| 695 | enum fun_eth_rss_const { | ||
| 696 | FUN_ETH_RSS_MAX_KEY_SIZE = 0x28, | ||
| 697 | FUN_ETH_RSS_MAX_INDIR_ENT = 0x40, | ||
| 698 | }; | ||
| 699 | |||
| 700 | enum fun_eth_hash_alg { | ||
| 701 | FUN_ETH_RSS_ALG_INVALID = 0x0, | ||
| 702 | FUN_ETH_RSS_ALG_TOEPLITZ = 0x1, | ||
| 703 | FUN_ETH_RSS_ALG_CRC32 = 0x2, | ||
| 704 | }; | ||
| 705 | |||
| 706 | struct fun_admin_rss_req { | ||
| 707 | struct fun_admin_req_common common; | ||
| 708 | |||
| 709 | union rss_req_subop { | ||
| 710 | struct fun_admin_rss_create_req { | ||
| 711 | __u8 subop; | ||
| 712 | __u8 rsvd0; | ||
| 713 | __be16 flags; | ||
| 714 | __be32 id; | ||
| 715 | |||
| 716 | __be32 rsvd1; | ||
| 717 | __be32 viid; /* VI flow id */ | ||
| 718 | |||
| 719 | __be64 metadata[1]; | ||
| 720 | |||
| 721 | __u8 alg; | ||
| 722 | __u8 keylen; | ||
| 723 | __u8 indir_nent; | ||
| 724 | __u8 rsvd2; | ||
| 725 | __be16 key_off; | ||
| 726 | __be16 indir_off; | ||
| 727 | |||
| 728 | struct fun_dataop_hdr dataop; | ||
| 729 | } create; | ||
| 730 | } u; | ||
| 731 | }; | ||
| 732 | |||
| 733 | #define FUN_ADMIN_RSS_CREATE_REQ_INIT(_subop, _flags, _id, _viid, _alg, \ | ||
| 734 | _keylen, _indir_nent, _key_off, \ | ||
| 735 | _indir_off) \ | ||
| 736 | (struct fun_admin_rss_create_req) { \ | ||
| 737 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 738 | .id = cpu_to_be32(_id), .viid = cpu_to_be32(_viid), \ | ||
| 739 | .alg = _alg, .keylen = _keylen, .indir_nent = _indir_nent, \ | ||
| 740 | .key_off = cpu_to_be16(_key_off), \ | ||
| 741 | .indir_off = cpu_to_be16(_indir_off), \ | ||
| 742 | } | ||
| 743 | |||
| 744 | struct fun_admin_vi_req { | ||
| 745 | struct fun_admin_req_common common; | ||
| 746 | |||
| 747 | union vi_req_subop { | ||
| 748 | struct fun_admin_vi_create_req { | ||
| 749 | __u8 subop; | ||
| 750 | __u8 rsvd0; | ||
| 751 | __be16 flags; | ||
| 752 | __be32 id; | ||
| 753 | |||
| 754 | __be32 rsvd1; | ||
| 755 | __be32 portid; /* port flow id */ | ||
| 756 | } create; | ||
| 757 | } u; | ||
| 758 | }; | ||
| 759 | |||
| 760 | #define FUN_ADMIN_VI_CREATE_REQ_INIT(_subop, _flags, _id, _portid) \ | ||
| 761 | (struct fun_admin_vi_create_req) { \ | ||
| 762 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 763 | .id = cpu_to_be32(_id), .portid = cpu_to_be32(_portid), \ | ||
| 764 | } | ||
| 765 | |||
| 766 | struct fun_admin_eth_req { | ||
| 767 | struct fun_admin_req_common common; | ||
| 768 | |||
| 769 | union eth_req_subop { | ||
| 770 | struct fun_admin_eth_create_req { | ||
| 771 | __u8 subop; | ||
| 772 | __u8 rsvd0; | ||
| 773 | __be16 flags; | ||
| 774 | __be32 id; | ||
| 775 | |||
| 776 | __be32 rsvd1; | ||
| 777 | __be32 portid; /* port flow id */ | ||
| 778 | } create; | ||
| 779 | } u; | ||
| 780 | }; | ||
| 781 | |||
| 782 | #define FUN_ADMIN_ETH_CREATE_REQ_INIT(_subop, _flags, _id, _portid) \ | ||
| 783 | (struct fun_admin_eth_create_req) { \ | ||
| 784 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 785 | .id = cpu_to_be32(_id), .portid = cpu_to_be32(_portid), \ | ||
| 786 | } | ||
| 787 | |||
| 788 | enum { | ||
| 789 | FUN_ADMIN_SWU_UPGRADE_FLAG_INIT = 0x10, | ||
| 790 | FUN_ADMIN_SWU_UPGRADE_FLAG_COMPLETE = 0x20, | ||
| 791 | FUN_ADMIN_SWU_UPGRADE_FLAG_DOWNGRADE = 0x40, | ||
| 792 | FUN_ADMIN_SWU_UPGRADE_FLAG_ACTIVE_IMAGE = 0x80, | ||
| 793 | FUN_ADMIN_SWU_UPGRADE_FLAG_ASYNC = 0x1, | ||
| 794 | }; | ||
| 795 | |||
| 796 | enum fun_admin_swu_subop { | ||
| 797 | FUN_ADMIN_SWU_SUBOP_GET_VERSION = 0x20, | ||
| 798 | FUN_ADMIN_SWU_SUBOP_UPGRADE = 0x21, | ||
| 799 | FUN_ADMIN_SWU_SUBOP_UPGRADE_DATA = 0x22, | ||
| 800 | FUN_ADMIN_SWU_SUBOP_GET_ALL_VERSIONS = 0x23, | ||
| 801 | }; | ||
| 802 | |||
| 803 | struct fun_admin_swu_req { | ||
| 804 | struct fun_admin_req_common common; | ||
| 805 | |||
| 806 | union swu_req_subop { | ||
| 807 | struct fun_admin_swu_create_req { | ||
| 808 | __u8 subop; | ||
| 809 | __u8 rsvd0; | ||
| 810 | __be16 flags; | ||
| 811 | __be32 id; | ||
| 812 | } create; | ||
| 813 | struct fun_admin_swu_upgrade_req { | ||
| 814 | __u8 subop; | ||
| 815 | __u8 rsvd0; | ||
| 816 | __be16 flags; | ||
| 817 | __be32 id; | ||
| 818 | |||
| 819 | __be32 fourcc; | ||
| 820 | __be32 rsvd1; | ||
| 821 | |||
| 822 | __be64 image_size; /* upgrade image length */ | ||
| 823 | } upgrade; | ||
| 824 | struct fun_admin_swu_upgrade_data_req { | ||
| 825 | __u8 subop; | ||
| 826 | __u8 rsvd0; | ||
| 827 | __be16 flags; | ||
| 828 | __be32 id; | ||
| 829 | |||
| 830 | __be32 offset; /* offset of data in this command */ | ||
| 831 | __be32 size; /* total size of data in this command */ | ||
| 832 | } upgrade_data; | ||
| 833 | } u; | ||
| 834 | |||
| 835 | struct fun_subop_sgl sgl[]; /* in, out buffers through sgl */ | ||
| 836 | }; | ||
| 837 | |||
| 838 | #define FUN_ADMIN_SWU_CREATE_REQ_INIT(_subop, _flags, _id) \ | ||
| 839 | (struct fun_admin_swu_create_req) { \ | ||
| 840 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 841 | .id = cpu_to_be32(_id), \ | ||
| 842 | } | ||
| 843 | |||
| 844 | #define FUN_ADMIN_SWU_UPGRADE_REQ_INIT(_subop, _flags, _id, _fourcc, \ | ||
| 845 | _image_size) \ | ||
| 846 | (struct fun_admin_swu_upgrade_req) { \ | ||
| 847 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 848 | .id = cpu_to_be32(_id), .fourcc = cpu_to_be32(_fourcc), \ | ||
| 849 | .image_size = cpu_to_be64(_image_size), \ | ||
| 850 | } | ||
| 851 | |||
| 852 | #define FUN_ADMIN_SWU_UPGRADE_DATA_REQ_INIT(_subop, _flags, _id, _offset, \ | ||
| 853 | _size) \ | ||
| 854 | (struct fun_admin_swu_upgrade_data_req) { \ | ||
| 855 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 856 | .id = cpu_to_be32(_id), .offset = cpu_to_be32(_offset), \ | ||
| 857 | .size = cpu_to_be32(_size), \ | ||
| 858 | } | ||
| 859 | |||
| 860 | struct fun_admin_swu_rsp { | ||
| 861 | struct fun_admin_rsp_common common; | ||
| 862 | |||
| 863 | union swu_rsp_subop { | ||
| 864 | struct fun_admin_swu_create_rsp { | ||
| 865 | __u8 subop; | ||
| 866 | __u8 rsvd0; | ||
| 867 | __be16 flags; | ||
| 868 | __be32 id; | ||
| 869 | } create; | ||
| 870 | struct fun_admin_swu_upgrade_rsp { | ||
| 871 | __u8 subop; | ||
| 872 | __u8 rsvd0[3]; | ||
| 873 | __be32 id; | ||
| 874 | |||
| 875 | __be32 fourcc; | ||
| 876 | __be32 status; | ||
| 877 | |||
| 878 | __be32 progress; | ||
| 879 | __be32 unused; | ||
| 880 | } upgrade; | ||
| 881 | struct fun_admin_swu_upgrade_data_rsp { | ||
| 882 | __u8 subop; | ||
| 883 | __u8 rsvd0; | ||
| 884 | __be16 flags; | ||
| 885 | __be32 id; | ||
| 886 | |||
| 887 | __be32 offset; | ||
| 888 | __be32 size; | ||
| 889 | } upgrade_data; | ||
| 890 | } u; | ||
| 891 | }; | ||
| 892 | |||
| 893 | enum fun_ktls_version { | ||
| 894 | FUN_KTLS_TLSV2 = 0x20, | ||
| 895 | FUN_KTLS_TLSV3 = 0x30, | ||
| 896 | }; | ||
| 897 | |||
| 898 | enum fun_ktls_cipher { | ||
| 899 | FUN_KTLS_CIPHER_AES_GCM_128 = 0x33, | ||
| 900 | FUN_KTLS_CIPHER_AES_GCM_256 = 0x34, | ||
| 901 | FUN_KTLS_CIPHER_AES_CCM_128 = 0x35, | ||
| 902 | FUN_KTLS_CIPHER_CHACHA20_POLY1305 = 0x36, | ||
| 903 | }; | ||
| 904 | |||
| 905 | enum fun_ktls_modify_flags { | ||
| 906 | FUN_KTLS_MODIFY_REMOVE = 0x1, | ||
| 907 | }; | ||
| 908 | |||
| 909 | struct fun_admin_ktls_create_req { | ||
| 910 | struct fun_admin_req_common common; | ||
| 911 | |||
| 912 | __u8 subop; | ||
| 913 | __u8 rsvd0; | ||
| 914 | __be16 flags; | ||
| 915 | __be32 id; | ||
| 916 | }; | ||
| 917 | |||
| 918 | #define FUN_ADMIN_KTLS_CREATE_REQ_INIT(_subop, _flags, _id) \ | ||
| 919 | (struct fun_admin_ktls_create_req) { \ | ||
| 920 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 921 | .id = cpu_to_be32(_id), \ | ||
| 922 | } | ||
| 923 | |||
| 924 | struct fun_admin_ktls_create_rsp { | ||
| 925 | struct fun_admin_rsp_common common; | ||
| 926 | |||
| 927 | __u8 subop; | ||
| 928 | __u8 rsvd0[3]; | ||
| 929 | __be32 id; | ||
| 930 | }; | ||
| 931 | |||
| 932 | struct fun_admin_ktls_modify_req { | ||
| 933 | struct fun_admin_req_common common; | ||
| 934 | |||
| 935 | __u8 subop; | ||
| 936 | __u8 rsvd0; | ||
| 937 | __be16 flags; | ||
| 938 | __be32 id; | ||
| 939 | |||
| 940 | __be64 tlsid; | ||
| 941 | |||
| 942 | __be32 tcp_seq; | ||
| 943 | __u8 version; | ||
| 944 | __u8 cipher; | ||
| 945 | __u8 rsvd1[2]; | ||
| 946 | |||
| 947 | __u8 record_seq[8]; | ||
| 948 | |||
| 949 | __u8 key[32]; | ||
| 950 | |||
| 951 | __u8 iv[16]; | ||
| 952 | |||
| 953 | __u8 salt[8]; | ||
| 954 | }; | ||
| 955 | |||
| 956 | #define FUN_ADMIN_KTLS_MODIFY_REQ_INIT(_subop, _flags, _id, _tlsid, _tcp_seq, \ | ||
| 957 | _version, _cipher) \ | ||
| 958 | (struct fun_admin_ktls_modify_req) { \ | ||
| 959 | .subop = (_subop), .flags = cpu_to_be16(_flags), \ | ||
| 960 | .id = cpu_to_be32(_id), .tlsid = cpu_to_be64(_tlsid), \ | ||
| 961 | .tcp_seq = cpu_to_be32(_tcp_seq), .version = _version, \ | ||
| 962 | .cipher = _cipher, \ | ||
| 963 | } | ||
| 964 | |||
| 965 | struct fun_admin_ktls_modify_rsp { | ||
| 966 | struct fun_admin_rsp_common common; | ||
| 967 | |||
| 968 | __u8 subop; | ||
| 969 | __u8 rsvd0[3]; | ||
| 970 | __be32 id; | ||
| 971 | |||
| 972 | __be64 tlsid; | ||
| 973 | }; | ||
| 974 | |||
| 975 | struct fun_req_common { | ||
| 976 | __u8 op; | ||
| 977 | __u8 len8; | ||
| 978 | __be16 flags; | ||
| 979 | __u8 suboff8; | ||
| 980 | __u8 rsvd0; | ||
| 981 | __be16 cid; | ||
| 982 | }; | ||
| 983 | |||
| 984 | struct fun_rsp_common { | ||
| 985 | __u8 op; | ||
| 986 | __u8 len8; | ||
| 987 | __be16 flags; | ||
| 988 | __u8 suboff8; | ||
| 989 | __u8 ret; | ||
| 990 | __be16 cid; | ||
| 991 | }; | ||
| 992 | |||
| 993 | struct fun_cqe_info { | ||
| 994 | __be16 sqhd; | ||
| 995 | __be16 sqid; | ||
| 996 | __be16 cid; | ||
| 997 | __be16 sf_p; | ||
| 998 | }; | ||
| 999 | |||
| 1000 | enum fun_eprq_def { | ||
| 1001 | FUN_EPRQ_PKT_ALIGN = 0x80, | ||
| 1002 | }; | ||
| 1003 | |||
| 1004 | struct fun_eprq_rqbuf { | ||
| 1005 | __be64 bufaddr; | ||
| 1006 | }; | ||
| 1007 | |||
| 1008 | #define FUN_EPRQ_RQBUF_INIT(_bufaddr) \ | ||
| 1009 | (struct fun_eprq_rqbuf) { \ | ||
| 1010 | .bufaddr = cpu_to_be64(_bufaddr), \ | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | enum fun_eth_op { | ||
| 1014 | FUN_ETH_OP_TX = 0x1, | ||
| 1015 | FUN_ETH_OP_RX = 0x2, | ||
| 1016 | }; | ||
| 1017 | |||
| 1018 | enum { | ||
| 1019 | FUN_ETH_OFFLOAD_EN = 0x8000, | ||
| 1020 | FUN_ETH_OUTER_EN = 0x4000, | ||
| 1021 | FUN_ETH_INNER_LSO = 0x2000, | ||
| 1022 | FUN_ETH_INNER_TSO = 0x1000, | ||
| 1023 | FUN_ETH_OUTER_IPV6 = 0x800, | ||
| 1024 | FUN_ETH_OUTER_UDP = 0x400, | ||
| 1025 | FUN_ETH_INNER_IPV6 = 0x200, | ||
| 1026 | FUN_ETH_INNER_UDP = 0x100, | ||
| 1027 | FUN_ETH_UPDATE_OUTER_L3_LEN = 0x80, | ||
| 1028 | FUN_ETH_UPDATE_OUTER_L3_CKSUM = 0x40, | ||
| 1029 | FUN_ETH_UPDATE_OUTER_L4_LEN = 0x20, | ||
| 1030 | FUN_ETH_UPDATE_OUTER_L4_CKSUM = 0x10, | ||
| 1031 | FUN_ETH_UPDATE_INNER_L3_LEN = 0x8, | ||
| 1032 | FUN_ETH_UPDATE_INNER_L3_CKSUM = 0x4, | ||
| 1033 | FUN_ETH_UPDATE_INNER_L4_LEN = 0x2, | ||
| 1034 | FUN_ETH_UPDATE_INNER_L4_CKSUM = 0x1, | ||
| 1035 | }; | ||
| 1036 | |||
| 1037 | struct fun_eth_offload { | ||
| 1038 | __be16 flags; /* combination of above flags */ | ||
| 1039 | __be16 mss; /* TSO max seg size */ | ||
| 1040 | __be16 tcp_doff_flags; /* TCP data offset + flags 16b word */ | ||
| 1041 | __be16 vlan; | ||
| 1042 | |||
| 1043 | __be16 inner_l3_off; /* Inner L3 header offset */ | ||
| 1044 | __be16 inner_l4_off; /* Inner L4 header offset */ | ||
| 1045 | __be16 outer_l3_off; /* Outer L3 header offset */ | ||
| 1046 | __be16 outer_l4_off; /* Outer L4 header offset */ | ||
| 1047 | }; | ||
| 1048 | |||
| 1049 | static inline void fun_eth_offload_init(struct fun_eth_offload *s, u16 flags, | ||
| 1050 | u16 mss, __be16 tcp_doff_flags, | ||
| 1051 | __be16 vlan, u16 inner_l3_off, | ||
| 1052 | u16 inner_l4_off, u16 outer_l3_off, | ||
| 1053 | u16 outer_l4_off) | ||
| 1054 | { | ||
| 1055 | s->flags = cpu_to_be16(flags); | ||
| 1056 | s->mss = cpu_to_be16(mss); | ||
| 1057 | s->tcp_doff_flags = tcp_doff_flags; | ||
| 1058 | s->vlan = vlan; | ||
| 1059 | s->inner_l3_off = cpu_to_be16(inner_l3_off); | ||
| 1060 | s->inner_l4_off = cpu_to_be16(inner_l4_off); | ||
| 1061 | s->outer_l3_off = cpu_to_be16(outer_l3_off); | ||
| 1062 | s->outer_l4_off = cpu_to_be16(outer_l4_off); | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | struct fun_eth_tls { | ||
| 1066 | __be64 tlsid; | ||
| 1067 | }; | ||
| 1068 | |||
| 1069 | enum { | ||
| 1070 | FUN_ETH_TX_TLS = 0x8000, | ||
| 1071 | }; | ||
| 1072 | |||
| 1073 | struct fun_eth_tx_req { | ||
| 1074 | __u8 op; | ||
| 1075 | __u8 len8; | ||
| 1076 | __be16 flags; | ||
| 1077 | __u8 suboff8; | ||
| 1078 | __u8 repr_idn; | ||
| 1079 | __be16 encap_proto; | ||
| 1080 | |||
| 1081 | struct fun_eth_offload offload; | ||
| 1082 | |||
| 1083 | struct fun_dataop_hdr dataop; | ||
| 1084 | }; | ||
| 1085 | |||
| 1086 | struct fun_eth_rx_cv { | ||
| 1087 | __be16 il4_prot_to_l2_type; | ||
| 1088 | }; | ||
| 1089 | |||
| 1090 | #define FUN_ETH_RX_CV_IL4_PROT_S 13U | ||
| 1091 | #define FUN_ETH_RX_CV_IL4_PROT_M 0x3 | ||
| 1092 | |||
| 1093 | #define FUN_ETH_RX_CV_IL3_PROT_S 11U | ||
| 1094 | #define FUN_ETH_RX_CV_IL3_PROT_M 0x3 | ||
| 1095 | |||
| 1096 | #define FUN_ETH_RX_CV_OL4_PROT_S 8U | ||
| 1097 | #define FUN_ETH_RX_CV_OL4_PROT_M 0x7 | ||
| 1098 | |||
| 1099 | #define FUN_ETH_RX_CV_ENCAP_TYPE_S 6U | ||
| 1100 | #define FUN_ETH_RX_CV_ENCAP_TYPE_M 0x3 | ||
| 1101 | |||
| 1102 | #define FUN_ETH_RX_CV_OL3_PROT_S 4U | ||
| 1103 | #define FUN_ETH_RX_CV_OL3_PROT_M 0x3 | ||
| 1104 | |||
| 1105 | #define FUN_ETH_RX_CV_VLAN_TYPE_S 3U | ||
| 1106 | #define FUN_ETH_RX_CV_VLAN_TYPE_M 0x1 | ||
| 1107 | |||
| 1108 | #define FUN_ETH_RX_CV_L2_TYPE_S 2U | ||
| 1109 | #define FUN_ETH_RX_CV_L2_TYPE_M 0x1 | ||
| 1110 | |||
| 1111 | enum fun_rx_cv { | ||
| 1112 | FUN_RX_CV_NONE = 0x0, | ||
| 1113 | FUN_RX_CV_IP = 0x2, | ||
| 1114 | FUN_RX_CV_IP6 = 0x3, | ||
| 1115 | FUN_RX_CV_TCP = 0x2, | ||
| 1116 | FUN_RX_CV_UDP = 0x3, | ||
| 1117 | FUN_RX_CV_VXLAN = 0x2, | ||
| 1118 | FUN_RX_CV_MPLS = 0x3, | ||
| 1119 | }; | ||
| 1120 | |||
| 1121 | struct fun_eth_cqe { | ||
| 1122 | __u8 op; | ||
| 1123 | __u8 len8; | ||
| 1124 | __u8 nsgl; | ||
| 1125 | __u8 repr_idn; | ||
| 1126 | __be32 pkt_len; | ||
| 1127 | |||
| 1128 | __be64 timestamp; | ||
| 1129 | |||
| 1130 | __be16 pkt_cv; | ||
| 1131 | __be16 rsvd0; | ||
| 1132 | __be32 hash; | ||
| 1133 | |||
| 1134 | __be16 encap_proto; | ||
| 1135 | __be16 vlan; | ||
| 1136 | __be32 rsvd1; | ||
| 1137 | |||
| 1138 | __be32 buf_offset; | ||
| 1139 | __be16 headroom; | ||
| 1140 | __be16 csum; | ||
| 1141 | }; | ||
| 1142 | |||
| 1143 | enum fun_admin_adi_attr { | ||
| 1144 | FUN_ADMIN_ADI_ATTR_MACADDR = 0x1, | ||
| 1145 | FUN_ADMIN_ADI_ATTR_VLAN = 0x2, | ||
| 1146 | FUN_ADMIN_ADI_ATTR_RATE = 0x3, | ||
| 1147 | }; | ||
| 1148 | |||
| 1149 | struct fun_adi_param { | ||
| 1150 | union adi_param { | ||
| 1151 | struct fun_adi_mac { | ||
| 1152 | __be64 addr; | ||
| 1153 | } mac; | ||
| 1154 | struct fun_adi_vlan { | ||
| 1155 | __be32 rsvd; | ||
| 1156 | __be16 eth_type; | ||
| 1157 | __be16 tci; | ||
| 1158 | } vlan; | ||
| 1159 | struct fun_adi_rate { | ||
| 1160 | __be32 rsvd; | ||
| 1161 | __be32 tx_mbps; | ||
| 1162 | } rate; | ||
| 1163 | } u; | ||
| 1164 | }; | ||
| 1165 | |||
| 1166 | #define FUN_ADI_MAC_INIT(_addr) \ | ||
| 1167 | (struct fun_adi_mac) { \ | ||
| 1168 | .addr = cpu_to_be64(_addr), \ | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | #define FUN_ADI_VLAN_INIT(_eth_type, _tci) \ | ||
| 1172 | (struct fun_adi_vlan) { \ | ||
| 1173 | .eth_type = cpu_to_be16(_eth_type), .tci = cpu_to_be16(_tci), \ | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | #define FUN_ADI_RATE_INIT(_tx_mbps) \ | ||
| 1177 | (struct fun_adi_rate) { \ | ||
| 1178 | .tx_mbps = cpu_to_be32(_tx_mbps), \ | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | struct fun_admin_adi_req { | ||
| 1182 | struct fun_admin_req_common common; | ||
| 1183 | |||
| 1184 | union adi_req_subop { | ||
| 1185 | struct fun_admin_adi_write_req { | ||
| 1186 | __u8 subop; | ||
| 1187 | __u8 attribute; | ||
| 1188 | __be16 rsvd; | ||
| 1189 | __be32 id; | ||
| 1190 | |||
| 1191 | struct fun_adi_param param; | ||
| 1192 | } write; | ||
| 1193 | } u; | ||
| 1194 | }; | ||
| 1195 | |||
| 1196 | #define FUN_ADMIN_ADI_WRITE_REQ_INIT(_subop, _attribute, _id) \ | ||
| 1197 | (struct fun_admin_adi_write_req) { \ | ||
| 1198 | .subop = (_subop), .attribute = (_attribute), \ | ||
| 1199 | .id = cpu_to_be32(_id), \ | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | #endif /* __FUN_HCI_H */ | ||
| @@ -0,0 +1,601 @@ | |||
| 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) | ||
| 2 | |||
| 3 | #include <linux/dma-mapping.h> | ||
| 4 | #include <linux/interrupt.h> | ||
| 5 | #include <linux/log2.h> | ||
| 6 | #include <linux/mm.h> | ||
| 7 | #include <linux/netdevice.h> | ||
| 8 | #include <linux/pci.h> | ||
| 9 | #include <linux/slab.h> | ||
| 10 | |||
| 11 | #include "fun_dev.h" | ||
| 12 | #include "fun_queue.h" | ||
| 13 | |||
| 14 | /* Allocate memory for a queue. This includes the memory for the HW descriptor | ||
| 15 | * ring, an optional 64b HW write-back area, and an optional SW state ring. | ||
| 16 | * Returns the virtual and DMA addresses of the HW ring, the VA of the SW ring, | ||
| 17 | * and the VA of the write-back area. | ||
| 18 | */ | ||
| 19 | void *fun_alloc_ring_mem(struct device *dma_dev, size_t depth, | ||
| 20 | size_t hw_desc_sz, size_t sw_desc_sz, bool wb, | ||
| 21 | int numa_node, dma_addr_t *dma_addr, void **sw_va, | ||
| 22 | volatile __be64 **wb_va) | ||
| 23 | { | ||
| 24 | int dev_node = dev_to_node(dma_dev); | ||
| 25 | size_t dma_sz; | ||
| 26 | void *va; | ||
| 27 | |||
| 28 | if (numa_node == NUMA_NO_NODE) | ||
| 29 | numa_node = dev_node; | ||
| 30 | |||
| 31 | /* Place optional write-back area at end of descriptor ring. */ | ||
| 32 | dma_sz = hw_desc_sz * depth; | ||
| 33 | if (wb) | ||
| 34 | dma_sz += sizeof(u64); | ||
| 35 | |||
| 36 | set_dev_node(dma_dev, numa_node); | ||
| 37 | va = dma_alloc_coherent(dma_dev, dma_sz, dma_addr, GFP_KERNEL); | ||
| 38 | set_dev_node(dma_dev, dev_node); | ||
| 39 | if (!va) | ||
| 40 | return NULL; | ||
| 41 | |||
| 42 | if (sw_desc_sz) { | ||
| 43 | *sw_va = kvzalloc_node(sw_desc_sz * depth, GFP_KERNEL, | ||
| 44 | numa_node); | ||
| 45 | if (!*sw_va) { | ||
| 46 | dma_free_coherent(dma_dev, dma_sz, va, *dma_addr); | ||
| 47 | return NULL; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | if (wb) | ||
| 52 | *wb_va = va + dma_sz - sizeof(u64); | ||
| 53 | return va; | ||
| 54 | } | ||
| 55 | EXPORT_SYMBOL_GPL(fun_alloc_ring_mem); | ||
| 56 | |||
| 57 | void fun_free_ring_mem(struct device *dma_dev, size_t depth, size_t hw_desc_sz, | ||
| 58 | bool wb, void *hw_va, dma_addr_t dma_addr, void *sw_va) | ||
| 59 | { | ||
| 60 | if (hw_va) { | ||
| 61 | size_t sz = depth * hw_desc_sz; | ||
| 62 | |||
| 63 | if (wb) | ||
| 64 | sz += sizeof(u64); | ||
| 65 | dma_free_coherent(dma_dev, sz, hw_va, dma_addr); | ||
| 66 | } | ||
| 67 | kvfree(sw_va); | ||
| 68 | } | ||
| 69 | EXPORT_SYMBOL_GPL(fun_free_ring_mem); | ||
| 70 | |||
| 71 | /* Prepare and issue an admin command to create an SQ on the device with the | ||
| 72 | * provided parameters. If the queue ID is auto-allocated by the device it is | ||
| 73 | * returned in *sqidp. | ||
| 74 | */ | ||
| 75 | int fun_sq_create(struct fun_dev *fdev, u16 flags, u32 sqid, u32 cqid, | ||
| 76 | u8 sqe_size_log2, u32 sq_depth, dma_addr_t dma_addr, | ||
| 77 | u8 coal_nentries, u8 coal_usec, u32 irq_num, | ||
| 78 | u32 scan_start_id, u32 scan_end_id, | ||
| 79 | u32 rq_buf_size_log2, u32 *sqidp, u32 __iomem **dbp) | ||
| 80 | { | ||
| 81 | union { | ||
| 82 | struct fun_admin_epsq_req req; | ||
| 83 | struct fun_admin_generic_create_rsp rsp; | ||
| 84 | } cmd; | ||
| 85 | dma_addr_t wb_addr; | ||
| 86 | u32 hw_qid; | ||
| 87 | int rc; | ||
| 88 | |||
| 89 | if (sq_depth > fdev->q_depth) | ||
| 90 | return -EINVAL; | ||
| 91 | if (flags & FUN_ADMIN_EPSQ_CREATE_FLAG_RQ) | ||
| 92 | sqe_size_log2 = ilog2(sizeof(struct fun_eprq_rqbuf)); | ||
| 93 | |||
| 94 | wb_addr = dma_addr + (sq_depth << sqe_size_log2); | ||
| 95 | |||
| 96 | cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_EPSQ, | ||
| 97 | sizeof(cmd.req)); | ||
| 98 | cmd.req.u.create = | ||
| 99 | FUN_ADMIN_EPSQ_CREATE_REQ_INIT(FUN_ADMIN_SUBOP_CREATE, flags, | ||
| 100 | sqid, cqid, sqe_size_log2, | ||
| 101 | sq_depth - 1, dma_addr, 0, | ||
| 102 | coal_nentries, coal_usec, | ||
| 103 | irq_num, scan_start_id, | ||
| 104 | scan_end_id, 0, | ||
| 105 | rq_buf_size_log2, | ||
| 106 | ilog2(sizeof(u64)), wb_addr); | ||
| 107 | |||
| 108 | rc = fun_submit_admin_sync_cmd(fdev, &cmd.req.common, | ||
| 109 | &cmd.rsp, sizeof(cmd.rsp), 0); | ||
| 110 | if (rc) | ||
| 111 | return rc; | ||
| 112 | |||
| 113 | hw_qid = be32_to_cpu(cmd.rsp.id); | ||
| 114 | *dbp = fun_sq_db_addr(fdev, hw_qid); | ||
| 115 | if (flags & FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR) | ||
| 116 | *sqidp = hw_qid; | ||
| 117 | return rc; | ||
| 118 | } | ||
| 119 | EXPORT_SYMBOL_GPL(fun_sq_create); | ||
| 120 | |||
| 121 | /* Prepare and issue an admin command to create a CQ on the device with the | ||
| 122 | * provided parameters. If the queue ID is auto-allocated by the device it is | ||
| 123 | * returned in *cqidp. | ||
| 124 | */ | ||
| 125 | int fun_cq_create(struct fun_dev *fdev, u16 flags, u32 cqid, u32 rqid, | ||
| 126 | u8 cqe_size_log2, u32 cq_depth, dma_addr_t dma_addr, | ||
| 127 | u16 headroom, u16 tailroom, u8 coal_nentries, u8 coal_usec, | ||
| 128 | u32 irq_num, u32 scan_start_id, u32 scan_end_id, u32 *cqidp, | ||
| 129 | u32 __iomem **dbp) | ||
| 130 | { | ||
| 131 | union { | ||
| 132 | struct fun_admin_epcq_req req; | ||
| 133 | struct fun_admin_generic_create_rsp rsp; | ||
| 134 | } cmd; | ||
| 135 | u32 hw_qid; | ||
| 136 | int rc; | ||
| 137 | |||
| 138 | if (cq_depth > fdev->q_depth) | ||
| 139 | return -EINVAL; | ||
| 140 | |||
| 141 | cmd.req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_EPCQ, | ||
| 142 | sizeof(cmd.req)); | ||
| 143 | cmd.req.u.create = | ||
| 144 | FUN_ADMIN_EPCQ_CREATE_REQ_INIT(FUN_ADMIN_SUBOP_CREATE, flags, | ||
| 145 | cqid, rqid, cqe_size_log2, | ||
| 146 | cq_depth - 1, dma_addr, tailroom, | ||
| 147 | headroom / 2, 0, coal_nentries, | ||
| 148 | coal_usec, irq_num, | ||
| 149 | scan_start_id, scan_end_id, 0); | ||
| 150 | |||
| 151 | rc = fun_submit_admin_sync_cmd(fdev, &cmd.req.common, | ||
| 152 | &cmd.rsp, sizeof(cmd.rsp), 0); | ||
| 153 | if (rc) | ||
| 154 | return rc; | ||
| 155 | |||
| 156 | hw_qid = be32_to_cpu(cmd.rsp.id); | ||
| 157 | *dbp = fun_cq_db_addr(fdev, hw_qid); | ||
| 158 | if (flags & FUN_ADMIN_RES_CREATE_FLAG_ALLOCATOR) | ||
| 159 | *cqidp = hw_qid; | ||
| 160 | return rc; | ||
| 161 | } | ||
| 162 | EXPORT_SYMBOL_GPL(fun_cq_create); | ||
| 163 | |||
| 164 | static bool fun_sq_is_head_wb(const struct fun_queue *funq) | ||
| 165 | { | ||
| 166 | return funq->sq_flags & FUN_ADMIN_EPSQ_CREATE_FLAG_HEAD_WB_ADDRESS; | ||
| 167 | } | ||
| 168 | |||
| 169 | static void fun_clean_rq(struct fun_queue *funq) | ||
| 170 | { | ||
| 171 | struct fun_dev *fdev = funq->fdev; | ||
| 172 | struct fun_rq_info *rqinfo; | ||
| 173 | unsigned int i; | ||
| 174 | |||
| 175 | for (i = 0; i < funq->rq_depth; i++) { | ||
| 176 | rqinfo = &funq->rq_info[i]; | ||
| 177 | if (rqinfo->page) { | ||
| 178 | dma_unmap_page(fdev->dev, rqinfo->dma, PAGE_SIZE, | ||
| 179 | DMA_FROM_DEVICE); | ||
| 180 | put_page(rqinfo->page); | ||
| 181 | rqinfo->page = NULL; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | static int fun_fill_rq(struct fun_queue *funq) | ||
| 187 | { | ||
| 188 | struct device *dev = funq->fdev->dev; | ||
| 189 | int i, node = dev_to_node(dev); | ||
| 190 | struct fun_rq_info *rqinfo; | ||
| 191 | |||
| 192 | for (i = 0; i < funq->rq_depth; i++) { | ||
| 193 | rqinfo = &funq->rq_info[i]; | ||
| 194 | rqinfo->page = alloc_pages_node(node, GFP_KERNEL, 0); | ||
| 195 | if (unlikely(!rqinfo->page)) | ||
| 196 | return -ENOMEM; | ||
| 197 | |||
| 198 | rqinfo->dma = dma_map_page(dev, rqinfo->page, 0, | ||
| 199 | PAGE_SIZE, DMA_FROM_DEVICE); | ||
| 200 | if (unlikely(dma_mapping_error(dev, rqinfo->dma))) { | ||
| 201 | put_page(rqinfo->page); | ||
| 202 | rqinfo->page = NULL; | ||
| 203 | return -ENOMEM; | ||
| 204 | } | ||
| 205 | |||
| 206 | funq->rqes[i] = FUN_EPRQ_RQBUF_INIT(rqinfo->dma); | ||
| 207 | } | ||
| 208 | |||
| 209 | funq->rq_tail = funq->rq_depth - 1; | ||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | static void fun_rq_update_pos(struct fun_queue *funq, int buf_offset) | ||
| 214 | { | ||
| 215 | if (buf_offset <= funq->rq_buf_offset) { | ||
| 216 | struct fun_rq_info *rqinfo = &funq->rq_info[funq->rq_buf_idx]; | ||
| 217 | struct device *dev = funq->fdev->dev; | ||
| 218 | |||
| 219 | dma_sync_single_for_device(dev, rqinfo->dma, PAGE_SIZE, | ||
| 220 | DMA_FROM_DEVICE); | ||
| 221 | funq->num_rqe_to_fill++; | ||
| 222 | if (++funq->rq_buf_idx == funq->rq_depth) | ||
| 223 | funq->rq_buf_idx = 0; | ||
| 224 | } | ||
| 225 | funq->rq_buf_offset = buf_offset; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* Given a command response with data scattered across >= 1 RQ buffers return | ||
| 229 | * a pointer to a contiguous buffer containing all the data. If the data is in | ||
| 230 | * one RQ buffer the start address within that buffer is returned, otherwise a | ||
| 231 | * new buffer is allocated and the data is gathered into it. | ||
| 232 | */ | ||
| 233 | static void *fun_data_from_rq(struct fun_queue *funq, | ||
| 234 | const struct fun_rsp_common *rsp, bool *need_free) | ||
| 235 | { | ||
| 236 | u32 bufoff, total_len, remaining, fragsize, dataoff; | ||
| 237 | struct device *dma_dev = funq->fdev->dev; | ||
| 238 | const struct fun_dataop_rqbuf *databuf; | ||
| 239 | const struct fun_dataop_hdr *dataop; | ||
| 240 | const struct fun_rq_info *rqinfo; | ||
| 241 | void *data; | ||
| 242 | |||
| 243 | dataop = (void *)rsp + rsp->suboff8 * 8; | ||
| 244 | total_len = be32_to_cpu(dataop->total_len); | ||
| 245 | |||
| 246 | if (likely(dataop->nsgl == 1)) { | ||
| 247 | databuf = (struct fun_dataop_rqbuf *)dataop->imm; | ||
| 248 | bufoff = be32_to_cpu(databuf->bufoff); | ||
| 249 | fun_rq_update_pos(funq, bufoff); | ||
| 250 | rqinfo = &funq->rq_info[funq->rq_buf_idx]; | ||
| 251 | dma_sync_single_for_cpu(dma_dev, rqinfo->dma + bufoff, | ||
| 252 | total_len, DMA_FROM_DEVICE); | ||
| 253 | *need_free = false; | ||
| 254 | return page_address(rqinfo->page) + bufoff; | ||
| 255 | } | ||
| 256 | |||
| 257 | /* For scattered completions gather the fragments into one buffer. */ | ||
| 258 | |||
| 259 | data = kmalloc(total_len, GFP_ATOMIC); | ||
| 260 | /* NULL is OK here. In case of failure we still need to consume the data | ||
| 261 | * for proper buffer accounting but indicate an error in the response. | ||
| 262 | */ | ||
| 263 | if (likely(data)) | ||
| 264 | *need_free = true; | ||
| 265 | |||
| 266 | dataoff = 0; | ||
| 267 | for (remaining = total_len; remaining; remaining -= fragsize) { | ||
| 268 | fun_rq_update_pos(funq, 0); | ||
| 269 | fragsize = min_t(unsigned int, PAGE_SIZE, remaining); | ||
| 270 | if (data) { | ||
| 271 | rqinfo = &funq->rq_info[funq->rq_buf_idx]; | ||
| 272 | dma_sync_single_for_cpu(dma_dev, rqinfo->dma, fragsize, | ||
| 273 | DMA_FROM_DEVICE); | ||
| 274 | memcpy(data + dataoff, page_address(rqinfo->page), | ||
| 275 | fragsize); | ||
| 276 | dataoff += fragsize; | ||
| 277 | } | ||
| 278 | } | ||
| 279 | return data; | ||
| 280 | } | ||
| 281 | |||
| 282 | unsigned int __fun_process_cq(struct fun_queue *funq, unsigned int max) | ||
| 283 | { | ||
| 284 | const struct fun_cqe_info *info; | ||
| 285 | struct fun_rsp_common *rsp; | ||
| 286 | unsigned int new_cqes; | ||
| 287 | u16 sf_p, flags; | ||
| 288 | bool need_free; | ||
| 289 | void *cqe; | ||
| 290 | |||
| 291 | if (!max) | ||
| 292 | max = funq->cq_depth - 1; | ||
| 293 | |||
| 294 | for (new_cqes = 0; new_cqes < max; new_cqes++) { | ||
| 295 | cqe = funq->cqes + (funq->cq_head << funq->cqe_size_log2); | ||
| 296 | info = funq_cqe_info(funq, cqe); | ||
| 297 | sf_p = be16_to_cpu(info->sf_p); | ||
| 298 | |||
| 299 | if ((sf_p & 1) != funq->cq_phase) | ||
| 300 | break; | ||
| 301 | |||
| 302 | /* ensure the phase tag is read before other CQE fields */ | ||
| 303 | dma_rmb(); | ||
| 304 | |||
| 305 | if (++funq->cq_head == funq->cq_depth) { | ||
| 306 | funq->cq_head = 0; | ||
| 307 | funq->cq_phase = !funq->cq_phase; | ||
| 308 | } | ||
| 309 | |||
| 310 | rsp = cqe; | ||
| 311 | flags = be16_to_cpu(rsp->flags); | ||
| 312 | |||
| 313 | need_free = false; | ||
| 314 | if (unlikely(flags & FUN_REQ_COMMON_FLAG_CQE_IN_RQBUF)) { | ||
| 315 | rsp = fun_data_from_rq(funq, rsp, &need_free); | ||
| 316 | if (!rsp) { | ||
| 317 | rsp = cqe; | ||
| 318 | rsp->len8 = 1; | ||
| 319 | if (rsp->ret == 0) | ||
| 320 | rsp->ret = ENOMEM; | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | if (funq->cq_cb) | ||
| 325 | funq->cq_cb(funq, funq->cb_data, rsp, info); | ||
| 326 | if (need_free) | ||
| 327 | kfree(rsp); | ||
| 328 | } | ||
| 329 | |||
| 330 | dev_dbg(funq->fdev->dev, "CQ %u, new CQEs %u/%u, head %u, phase %u\n", | ||
| 331 | funq->cqid, new_cqes, max, funq->cq_head, funq->cq_phase); | ||
| 332 | return new_cqes; | ||
| 333 | } | ||
| 334 | |||
| 335 | unsigned int fun_process_cq(struct fun_queue *funq, unsigned int max) | ||
| 336 | { | ||
| 337 | unsigned int processed; | ||
| 338 | u32 db; | ||
| 339 | |||
| 340 | processed = __fun_process_cq(funq, max); | ||
| 341 | |||
| 342 | if (funq->num_rqe_to_fill) { | ||
| 343 | funq->rq_tail = (funq->rq_tail + funq->num_rqe_to_fill) % | ||
| 344 | funq->rq_depth; | ||
| 345 | funq->num_rqe_to_fill = 0; | ||
| 346 | writel(funq->rq_tail, funq->rq_db); | ||
| 347 | } | ||
| 348 | |||
| 349 | db = funq->cq_head | FUN_DB_IRQ_ARM_F; | ||
| 350 | writel(db, funq->cq_db); | ||
| 351 | return processed; | ||
| 352 | } | ||
| 353 | |||
| 354 | static int fun_alloc_sqes(struct fun_queue *funq) | ||
| 355 | { | ||
| 356 | funq->sq_cmds = fun_alloc_ring_mem(funq->fdev->dev, funq->sq_depth, | ||
| 357 | 1 << funq->sqe_size_log2, 0, | ||
| 358 | fun_sq_is_head_wb(funq), | ||
| 359 | NUMA_NO_NODE, &funq->sq_dma_addr, | ||
| 360 | NULL, &funq->sq_head); | ||
| 361 | return funq->sq_cmds ? 0 : -ENOMEM; | ||
| 362 | } | ||
| 363 | |||
| 364 | static int fun_alloc_cqes(struct fun_queue *funq) | ||
| 365 | { | ||
| 366 | funq->cqes = fun_alloc_ring_mem(funq->fdev->dev, funq->cq_depth, | ||
| 367 | 1 << funq->cqe_size_log2, 0, false, | ||
| 368 | NUMA_NO_NODE, &funq->cq_dma_addr, NULL, | ||
| 369 | NULL); | ||
| 370 | return funq->cqes ? 0 : -ENOMEM; | ||
| 371 | } | ||
| 372 | |||
| 373 | static int fun_alloc_rqes(struct fun_queue *funq) | ||
| 374 | { | ||
| 375 | funq->rqes = fun_alloc_ring_mem(funq->fdev->dev, funq->rq_depth, | ||
| 376 | sizeof(*funq->rqes), | ||
| 377 | sizeof(*funq->rq_info), false, | ||
| 378 | NUMA_NO_NODE, &funq->rq_dma_addr, | ||
| 379 | (void **)&funq->rq_info, NULL); | ||
| 380 | return funq->rqes ? 0 : -ENOMEM; | ||
| 381 | } | ||
| 382 | |||
| 383 | /* Free a queue's structures. */ | ||
| 384 | void fun_free_queue(struct fun_queue *funq) | ||
| 385 | { | ||
| 386 | struct device *dev = funq->fdev->dev; | ||
| 387 | |||
| 388 | fun_free_ring_mem(dev, funq->cq_depth, 1 << funq->cqe_size_log2, false, | ||
| 389 | funq->cqes, funq->cq_dma_addr, NULL); | ||
| 390 | fun_free_ring_mem(dev, funq->sq_depth, 1 << funq->sqe_size_log2, | ||
| 391 | fun_sq_is_head_wb(funq), funq->sq_cmds, | ||
| 392 | funq->sq_dma_addr, NULL); | ||
| 393 | |||
| 394 | if (funq->rqes) { | ||
| 395 | fun_clean_rq(funq); | ||
| 396 | fun_free_ring_mem(dev, funq->rq_depth, sizeof(*funq->rqes), | ||
| 397 | false, funq->rqes, funq->rq_dma_addr, | ||
| 398 | funq->rq_info); | ||
| 399 | } | ||
| 400 | |||
| 401 | kfree(funq); | ||
| 402 | } | ||
| 403 | |||
| 404 | /* Allocate and initialize a funq's structures. */ | ||
| 405 | struct fun_queue *fun_alloc_queue(struct fun_dev *fdev, int qid, | ||
| 406 | const struct fun_queue_alloc_req *req) | ||
| 407 | { | ||
| 408 | struct fun_queue *funq = kzalloc(sizeof(*funq), GFP_KERNEL); | ||
| 409 | |||
| 410 | if (!funq) | ||
| 411 | return NULL; | ||
| 412 | |||
| 413 | funq->fdev = fdev; | ||
| 414 | spin_lock_init(&funq->sq_lock); | ||
| 415 | |||
| 416 | funq->qid = qid; | ||
| 417 | |||
| 418 | /* Initial CQ/SQ/RQ ids */ | ||
| 419 | if (req->rq_depth) { | ||
| 420 | funq->cqid = 2 * qid; | ||
| 421 | if (funq->qid) { | ||
| 422 | /* I/O Q: use rqid = cqid, sqid = +1 */ | ||
| 423 | funq->rqid = funq->cqid; | ||
| 424 | funq->sqid = funq->rqid + 1; | ||
| 425 | } else { | ||
| 426 | /* Admin Q: sqid is always 0, use ID 1 for RQ */ | ||
| 427 | funq->sqid = 0; | ||
| 428 | funq->rqid = 1; | ||
| 429 | } | ||
| 430 | } else { | ||
| 431 | funq->cqid = qid; | ||
| 432 | funq->sqid = qid; | ||
| 433 | } | ||
| 434 | |||
| 435 | funq->cq_flags = req->cq_flags; | ||
| 436 | funq->sq_flags = req->sq_flags; | ||
| 437 | |||
| 438 | funq->cqe_size_log2 = req->cqe_size_log2; | ||
| 439 | funq->sqe_size_log2 = req->sqe_size_log2; | ||
| 440 | |||
| 441 | funq->cq_depth = req->cq_depth; | ||
| 442 | funq->sq_depth = req->sq_depth; | ||
| 443 | |||
| 444 | funq->cq_intcoal_nentries = req->cq_intcoal_nentries; | ||
| 445 | funq->cq_intcoal_usec = req->cq_intcoal_usec; | ||
| 446 | |||
| 447 | funq->sq_intcoal_nentries = req->sq_intcoal_nentries; | ||
| 448 | funq->sq_intcoal_usec = req->sq_intcoal_usec; | ||
| 449 | |||
| 450 | if (fun_alloc_cqes(funq)) | ||
| 451 | goto free_funq; | ||
| 452 | |||
| 453 | funq->cq_phase = 1; | ||
| 454 | |||
| 455 | if (fun_alloc_sqes(funq)) | ||
| 456 | goto free_funq; | ||
| 457 | |||
| 458 | if (req->rq_depth) { | ||
| 459 | funq->rq_flags = req->rq_flags | FUN_ADMIN_EPSQ_CREATE_FLAG_RQ; | ||
| 460 | funq->rq_depth = req->rq_depth; | ||
| 461 | funq->rq_buf_offset = -1; | ||
| 462 | |||
| 463 | if (fun_alloc_rqes(funq) || fun_fill_rq(funq)) | ||
| 464 | goto free_funq; | ||
| 465 | } | ||
| 466 | |||
| 467 | funq->cq_vector = -1; | ||
| 468 | funq->cqe_info_offset = (1 << funq->cqe_size_log2) - sizeof(struct fun_cqe_info); | ||
| 469 | |||
| 470 | /* SQ/CQ 0 are implicitly created, assign their doorbells now. | ||
| 471 | * Other queues are assigned doorbells at their explicit creation. | ||
| 472 | */ | ||
| 473 | if (funq->sqid == 0) | ||
| 474 | funq->sq_db = fun_sq_db_addr(fdev, 0); | ||
| 475 | if (funq->cqid == 0) | ||
| 476 | funq->cq_db = fun_cq_db_addr(fdev, 0); | ||
| 477 | |||
| 478 | return funq; | ||
| 479 | |||
| 480 | free_funq: | ||
| 481 | fun_free_queue(funq); | ||
| 482 | return NULL; | ||
| 483 | } | ||
| 484 | |||
| 485 | /* Create a funq's CQ on the device. */ | ||
| 486 | static int fun_create_cq(struct fun_queue *funq) | ||
| 487 | { | ||
| 488 | struct fun_dev *fdev = funq->fdev; | ||
| 489 | unsigned int rqid; | ||
| 490 | int rc; | ||
| 491 | |||
| 492 | rqid = funq->cq_flags & FUN_ADMIN_EPCQ_CREATE_FLAG_RQ ? | ||
| 493 | funq->rqid : FUN_HCI_ID_INVALID; | ||
| 494 | rc = fun_cq_create(fdev, funq->cq_flags, funq->cqid, rqid, | ||
| 495 | funq->cqe_size_log2, funq->cq_depth, | ||
| 496 | funq->cq_dma_addr, 0, 0, funq->cq_intcoal_nentries, | ||
| 497 | funq->cq_intcoal_usec, funq->cq_vector, 0, 0, | ||
| 498 | &funq->cqid, &funq->cq_db); | ||
| 499 | if (!rc) | ||
| 500 | dev_dbg(fdev->dev, "created CQ %u\n", funq->cqid); | ||
| 501 | |||
| 502 | return rc; | ||
| 503 | } | ||
| 504 | |||
| 505 | /* Create a funq's SQ on the device. */ | ||
| 506 | static int fun_create_sq(struct fun_queue *funq) | ||
| 507 | { | ||
| 508 | struct fun_dev *fdev = funq->fdev; | ||
| 509 | int rc; | ||
| 510 | |||
| 511 | rc = fun_sq_create(fdev, funq->sq_flags, funq->sqid, funq->cqid, | ||
| 512 | funq->sqe_size_log2, funq->sq_depth, | ||
| 513 | funq->sq_dma_addr, funq->sq_intcoal_nentries, | ||
| 514 | funq->sq_intcoal_usec, funq->cq_vector, 0, 0, | ||
| 515 | 0, &funq->sqid, &funq->sq_db); | ||
| 516 | if (!rc) | ||
| 517 | dev_dbg(fdev->dev, "created SQ %u\n", funq->sqid); | ||
| 518 | |||
| 519 | return rc; | ||
| 520 | } | ||
| 521 | |||
| 522 | /* Create a funq's RQ on the device. */ | ||
| 523 | int fun_create_rq(struct fun_queue *funq) | ||
| 524 | { | ||
| 525 | struct fun_dev *fdev = funq->fdev; | ||
| 526 | int rc; | ||
| 527 | |||
| 528 | rc = fun_sq_create(fdev, funq->rq_flags, funq->rqid, funq->cqid, 0, | ||
| 529 | funq->rq_depth, funq->rq_dma_addr, 0, 0, | ||
| 530 | funq->cq_vector, 0, 0, PAGE_SHIFT, &funq->rqid, | ||
| 531 | &funq->rq_db); | ||
| 532 | if (!rc) | ||
| 533 | dev_dbg(fdev->dev, "created RQ %u\n", funq->rqid); | ||
| 534 | |||
| 535 | return rc; | ||
| 536 | } | ||
| 537 | |||
| 538 | static unsigned int funq_irq(struct fun_queue *funq) | ||
| 539 | { | ||
| 540 | return pci_irq_vector(to_pci_dev(funq->fdev->dev), funq->cq_vector); | ||
| 541 | } | ||
| 542 | |||
| 543 | int fun_request_irq(struct fun_queue *funq, const char *devname, | ||
| 544 | irq_handler_t handler, void *data) | ||
| 545 | { | ||
| 546 | int rc; | ||
| 547 | |||
| 548 | if (funq->cq_vector < 0) | ||
| 549 | return -EINVAL; | ||
| 550 | |||
| 551 | funq->irq_handler = handler; | ||
| 552 | funq->irq_data = data; | ||
| 553 | |||
| 554 | snprintf(funq->irqname, sizeof(funq->irqname), | ||
| 555 | funq->qid ? "%s-q[%d]" : "%s-adminq", devname, funq->qid); | ||
| 556 | |||
| 557 | rc = request_irq(funq_irq(funq), handler, 0, funq->irqname, data); | ||
| 558 | if (rc) | ||
| 559 | funq->irq_handler = NULL; | ||
| 560 | |||
| 561 | return rc; | ||
| 562 | } | ||
| 563 | |||
| 564 | /* Create all component queues of a funq on the device. */ | ||
| 565 | int fun_create_queue(struct fun_queue *funq) | ||
| 566 | { | ||
| 567 | int rc; | ||
| 568 | |||
| 569 | rc = fun_create_cq(funq); | ||
| 570 | if (rc) | ||
| 571 | return rc; | ||
| 572 | |||
| 573 | if (funq->rq_depth) { | ||
| 574 | rc = fun_create_rq(funq); | ||
| 575 | if (rc) | ||
| 576 | goto release_cq; | ||
| 577 | } | ||
| 578 | |||
| 579 | rc = fun_create_sq(funq); | ||
| 580 | if (rc) | ||
| 581 | goto release_rq; | ||
| 582 | |||
| 583 | return 0; | ||
| 584 | |||
| 585 | release_rq: | ||
| 586 | fun_destroy_sq(funq->fdev, funq->rqid); | ||
| 587 | release_cq: | ||
| 588 | fun_destroy_cq(funq->fdev, funq->cqid); | ||
| 589 | return rc; | ||
| 590 | } | ||
| 591 | |||
| 592 | void fun_free_irq(struct fun_queue *funq) | ||
| 593 | { | ||
| 594 | if (funq->irq_handler) { | ||
| 595 | unsigned int vector = funq_irq(funq); | ||
| 596 | |||
| 597 | free_irq(vector, funq->irq_data); | ||
| 598 | funq->irq_handler = NULL; | ||
| 599 | funq->irq_data = NULL; | ||
| 600 | } | ||
| 601 | } | ||
| @@ -0,0 +1,175 @@ | |||
| 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ | ||
| 2 | |||
| 3 | #ifndef _FUN_QEUEUE_H | ||
| 4 | #define _FUN_QEUEUE_H | ||
| 5 | |||
| 6 | #include <linux/interrupt.h> | ||
| 7 | #include <linux/io.h> | ||
| 8 | |||
| 9 | struct device; | ||
| 10 | struct fun_dev; | ||
| 11 | struct fun_queue; | ||
| 12 | struct fun_cqe_info; | ||
| 13 | struct fun_rsp_common; | ||
| 14 | |||
| 15 | typedef void (*cq_callback_t)(struct fun_queue *funq, void *data, void *msg, | ||
| 16 | const struct fun_cqe_info *info); | ||
| 17 | |||
| 18 | struct fun_rq_info { | ||
| 19 | dma_addr_t dma; | ||
| 20 | struct page *page; | ||
| 21 | }; | ||
| 22 | |||
| 23 | /* A queue group consisting of an SQ, a CQ, and an optional RQ. */ | ||
| 24 | struct fun_queue { | ||
| 25 | struct fun_dev *fdev; | ||
| 26 | spinlock_t sq_lock; | ||
| 27 | |||
| 28 | dma_addr_t cq_dma_addr; | ||
| 29 | dma_addr_t sq_dma_addr; | ||
| 30 | dma_addr_t rq_dma_addr; | ||
| 31 | |||
| 32 | u32 __iomem *cq_db; | ||
| 33 | u32 __iomem *sq_db; | ||
| 34 | u32 __iomem *rq_db; | ||
| 35 | |||
| 36 | void *cqes; | ||
| 37 | void *sq_cmds; | ||
| 38 | struct fun_eprq_rqbuf *rqes; | ||
| 39 | struct fun_rq_info *rq_info; | ||
| 40 | |||
| 41 | u32 cqid; | ||
| 42 | u32 sqid; | ||
| 43 | u32 rqid; | ||
| 44 | |||
| 45 | u32 cq_depth; | ||
| 46 | u32 sq_depth; | ||
| 47 | u32 rq_depth; | ||
| 48 | |||
| 49 | u16 cq_head; | ||
| 50 | u16 sq_tail; | ||
| 51 | u16 rq_tail; | ||
| 52 | |||
| 53 | u8 cqe_size_log2; | ||
| 54 | u8 sqe_size_log2; | ||
| 55 | |||
| 56 | u16 cqe_info_offset; | ||
| 57 | |||
| 58 | u16 rq_buf_idx; | ||
| 59 | int rq_buf_offset; | ||
| 60 | u16 num_rqe_to_fill; | ||
| 61 | |||
| 62 | u8 cq_intcoal_usec; | ||
| 63 | u8 cq_intcoal_nentries; | ||
| 64 | u8 sq_intcoal_usec; | ||
| 65 | u8 sq_intcoal_nentries; | ||
| 66 | |||
| 67 | u16 cq_flags; | ||
| 68 | u16 sq_flags; | ||
| 69 | u16 rq_flags; | ||
| 70 | |||
| 71 | /* SQ head writeback */ | ||
| 72 | u16 sq_comp; | ||
| 73 | |||
| 74 | volatile __be64 *sq_head; | ||
| 75 | |||
| 76 | cq_callback_t cq_cb; | ||
| 77 | void *cb_data; | ||
| 78 | |||
| 79 | irq_handler_t irq_handler; | ||
| 80 | void *irq_data; | ||
| 81 | s16 cq_vector; | ||
| 82 | u8 cq_phase; | ||
| 83 | |||
| 84 | /* I/O q index */ | ||
| 85 | u16 qid; | ||
| 86 | |||
| 87 | char irqname[24]; | ||
| 88 | }; | ||
| 89 | |||
| 90 | static inline void *fun_sqe_at(const struct fun_queue *funq, unsigned int pos) | ||
| 91 | { | ||
| 92 | return funq->sq_cmds + (pos << funq->sqe_size_log2); | ||
| 93 | } | ||
| 94 | |||
| 95 | static inline void funq_sq_post_tail(struct fun_queue *funq, u16 tail) | ||
| 96 | { | ||
| 97 | if (++tail == funq->sq_depth) | ||
| 98 | tail = 0; | ||
| 99 | funq->sq_tail = tail; | ||
| 100 | writel(tail, funq->sq_db); | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline struct fun_cqe_info *funq_cqe_info(const struct fun_queue *funq, | ||
| 104 | void *cqe) | ||
| 105 | { | ||
| 106 | return cqe + funq->cqe_info_offset; | ||
| 107 | } | ||
| 108 | |||
| 109 | static inline void funq_rq_post(struct fun_queue *funq) | ||
| 110 | { | ||
| 111 | writel(funq->rq_tail, funq->rq_db); | ||
| 112 | } | ||
| 113 | |||
| 114 | struct fun_queue_alloc_req { | ||
| 115 | u8 cqe_size_log2; | ||
| 116 | u8 sqe_size_log2; | ||
| 117 | |||
| 118 | u16 cq_flags; | ||
| 119 | u16 sq_flags; | ||
| 120 | u16 rq_flags; | ||
| 121 | |||
| 122 | u32 cq_depth; | ||
| 123 | u32 sq_depth; | ||
| 124 | u32 rq_depth; | ||
| 125 | |||
| 126 | u8 cq_intcoal_usec; | ||
| 127 | u8 cq_intcoal_nentries; | ||
| 128 | u8 sq_intcoal_usec; | ||
| 129 | u8 sq_intcoal_nentries; | ||
| 130 | }; | ||
| 131 | |||
| 132 | int fun_sq_create(struct fun_dev *fdev, u16 flags, u32 sqid, u32 cqid, | ||
| 133 | u8 sqe_size_log2, u32 sq_depth, dma_addr_t dma_addr, | ||
| 134 | u8 coal_nentries, u8 coal_usec, u32 irq_num, | ||
| 135 | u32 scan_start_id, u32 scan_end_id, | ||
| 136 | u32 rq_buf_size_log2, u32 *sqidp, u32 __iomem **dbp); | ||
| 137 | int fun_cq_create(struct fun_dev *fdev, u16 flags, u32 cqid, u32 rqid, | ||
| 138 | u8 cqe_size_log2, u32 cq_depth, dma_addr_t dma_addr, | ||
| 139 | u16 headroom, u16 tailroom, u8 coal_nentries, u8 coal_usec, | ||
| 140 | u32 irq_num, u32 scan_start_id, u32 scan_end_id, | ||
| 141 | u32 *cqidp, u32 __iomem **dbp); | ||
| 142 | void *fun_alloc_ring_mem(struct device *dma_dev, size_t depth, | ||
| 143 | size_t hw_desc_sz, size_t sw_desc_size, bool wb, | ||
| 144 | int numa_node, dma_addr_t *dma_addr, void **sw_va, | ||
| 145 | volatile __be64 **wb_va); | ||
| 146 | void fun_free_ring_mem(struct device *dma_dev, size_t depth, size_t hw_desc_sz, | ||
| 147 | bool wb, void *hw_va, dma_addr_t dma_addr, void *sw_va); | ||
| 148 | |||
| 149 | #define fun_destroy_sq(fdev, sqid) \ | ||
| 150 | fun_res_destroy((fdev), FUN_ADMIN_OP_EPSQ, 0, (sqid)) | ||
| 151 | #define fun_destroy_cq(fdev, cqid) \ | ||
| 152 | fun_res_destroy((fdev), FUN_ADMIN_OP_EPCQ, 0, (cqid)) | ||
| 153 | |||
| 154 | struct fun_queue *fun_alloc_queue(struct fun_dev *fdev, int qid, | ||
| 155 | const struct fun_queue_alloc_req *req); | ||
| 156 | void fun_free_queue(struct fun_queue *funq); | ||
| 157 | |||
| 158 | static inline void fun_set_cq_callback(struct fun_queue *funq, cq_callback_t cb, | ||
| 159 | void *cb_data) | ||
| 160 | { | ||
| 161 | funq->cq_cb = cb; | ||
| 162 | funq->cb_data = cb_data; | ||
| 163 | } | ||
| 164 | |||
| 165 | int fun_create_rq(struct fun_queue *funq); | ||
| 166 | int fun_create_queue(struct fun_queue *funq); | ||
| 167 | |||
| 168 | void fun_free_irq(struct fun_queue *funq); | ||
| 169 | int fun_request_irq(struct fun_queue *funq, const char *devname, | ||
| 170 | irq_handler_t handler, void *data); | ||
| 171 | |||
| 172 | unsigned int __fun_process_cq(struct fun_queue *funq, unsigned int max); | ||
| 173 | unsigned int fun_process_cq(struct fun_queue *funq, unsigned int max); | ||
| 174 | |||
| 175 | #endif /* _FUN_QEUEUE_H */ | ||