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 */ | |