// SPDX-License-Identifier: GPL-2.0 /* Marvell RVU Admin Function Devlink * * Copyright (C) 2020 Marvell. * */ #include #include "rvu.h" #include "rvu_reg.h" #include "rvu_struct.h" #define DRV_NAME "octeontx2-af" static int rvu_report_pair_start(struct devlink_fmsg *fmsg, const char *name) { int err; err = devlink_fmsg_pair_nest_start(fmsg, name); if (err) return err; return devlink_fmsg_obj_nest_start(fmsg); } static int rvu_report_pair_end(struct devlink_fmsg *fmsg) { int err; err = devlink_fmsg_obj_nest_end(fmsg); if (err) return err; return devlink_fmsg_pair_nest_end(fmsg); } static bool rvu_common_request_irq(struct rvu *rvu, int offset, const char *name, irq_handler_t fn) { struct rvu_devlink *rvu_dl = rvu->rvu_dl; int rc; sprintf(&rvu->irq_name[offset * NAME_SIZE], name); rc = request_irq(pci_irq_vector(rvu->pdev, offset), fn, 0, &rvu->irq_name[offset * NAME_SIZE], rvu_dl); if (rc) dev_warn(rvu->dev, "Failed to register %s irq\n", name); else rvu->irq_allocated[offset] = true; return rvu->irq_allocated[offset]; } static void rvu_nix_intr_work(struct work_struct *work) { struct rvu_nix_health_reporters *rvu_nix_health_reporter; rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, intr_work); devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_intr_reporter, "NIX_AF_RVU Error", rvu_nix_health_reporter->nix_event_ctx); } static irqreturn_t rvu_nix_af_rvu_intr_handler(int irq, void *rvu_irq) { struct rvu_nix_event_ctx *nix_event_context; struct rvu_devlink *rvu_dl = rvu_irq; struct rvu *rvu; int blkaddr; u64 intr; rvu = rvu_dl->rvu; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return IRQ_NONE; nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; intr = rvu_read64(rvu, blkaddr, NIX_AF_RVU_INT); nix_event_context->nix_af_rvu_int = intr; /* Clear interrupts */ rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT, intr); rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1C, ~0ULL); queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->intr_work); return IRQ_HANDLED; } static void rvu_nix_gen_work(struct work_struct *work) { struct rvu_nix_health_reporters *rvu_nix_health_reporter; rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, gen_work); devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_gen_reporter, "NIX_AF_GEN Error", rvu_nix_health_reporter->nix_event_ctx); } static irqreturn_t rvu_nix_af_rvu_gen_handler(int irq, void *rvu_irq) { struct rvu_nix_event_ctx *nix_event_context; struct rvu_devlink *rvu_dl = rvu_irq; struct rvu *rvu; int blkaddr; u64 intr; rvu = rvu_dl->rvu; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return IRQ_NONE; nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; intr = rvu_read64(rvu, blkaddr, NIX_AF_GEN_INT); nix_event_context->nix_af_rvu_gen = intr; /* Clear interrupts */ rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT, intr); rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1C, ~0ULL); queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->gen_work); return IRQ_HANDLED; } static void rvu_nix_err_work(struct work_struct *work) { struct rvu_nix_health_reporters *rvu_nix_health_reporter; rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, err_work); devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_err_reporter, "NIX_AF_ERR Error", rvu_nix_health_reporter->nix_event_ctx); } static irqreturn_t rvu_nix_af_rvu_err_handler(int irq, void *rvu_irq) { struct rvu_nix_event_ctx *nix_event_context; struct rvu_devlink *rvu_dl = rvu_irq; struct rvu *rvu; int blkaddr; u64 intr; rvu = rvu_dl->rvu; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return IRQ_NONE; nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; intr = rvu_read64(rvu, blkaddr, NIX_AF_ERR_INT); nix_event_context->nix_af_rvu_err = intr; /* Clear interrupts */ rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT, intr); rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1C, ~0ULL); queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->err_work); return IRQ_HANDLED; } static void rvu_nix_ras_work(struct work_struct *work) { struct rvu_nix_health_reporters *rvu_nix_health_reporter; rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, ras_work); devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_ras_reporter, "NIX_AF_RAS Error", rvu_nix_health_reporter->nix_event_ctx); } static irqreturn_t rvu_nix_af_rvu_ras_handler(int irq, void *rvu_irq) { struct rvu_nix_event_ctx *nix_event_context; struct rvu_devlink *rvu_dl = rvu_irq; struct rvu *rvu; int blkaddr; u64 intr; rvu = rvu_dl->rvu; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return IRQ_NONE; nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; intr = rvu_read64(rvu, blkaddr, NIX_AF_ERR_INT); nix_event_context->nix_af_rvu_ras = intr; /* Clear interrupts */ rvu_write64(rvu, blkaddr, NIX_AF_RAS, intr); rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1C, ~0ULL); queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->ras_work); return IRQ_HANDLED; } static void rvu_nix_unregister_interrupts(struct rvu *rvu) { struct rvu_devlink *rvu_dl = rvu->rvu_dl; int offs, i, blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return; offs = rvu_read64(rvu, blkaddr, NIX_PRIV_AF_INT_CFG) & 0x3ff; if (!offs) return; rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1C, ~0ULL); rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1C, ~0ULL); rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1C, ~0ULL); rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1C, ~0ULL); if (rvu->irq_allocated[offs + NIX_AF_INT_VEC_RVU]) { free_irq(pci_irq_vector(rvu->pdev, offs + NIX_AF_INT_VEC_RVU), rvu_dl); rvu->irq_allocated[offs + NIX_AF_INT_VEC_RVU] = false; } for (i = NIX_AF_INT_VEC_AF_ERR; i < NIX_AF_INT_VEC_CNT; i++) if (rvu->irq_allocated[offs + i]) { free_irq(pci_irq_vector(rvu->pdev, offs + i), rvu_dl); rvu->irq_allocated[offs + i] = false; } } static int rvu_nix_register_interrupts(struct rvu *rvu) { int blkaddr, base; bool rc; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return blkaddr; /* Get NIX AF MSIX vectors offset. */ base = rvu_read64(rvu, blkaddr, NIX_PRIV_AF_INT_CFG) & 0x3ff; if (!base) { dev_warn(rvu->dev, "Failed to get NIX%d NIX_AF_INT vector offsets\n", blkaddr - BLKADDR_NIX0); return 0; } /* Register and enable NIX_AF_RVU_INT interrupt */ rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_RVU, "NIX_AF_RVU_INT", rvu_nix_af_rvu_intr_handler); if (!rc) goto err; rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1S, ~0ULL); /* Register and enable NIX_AF_GEN_INT interrupt */ rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_GEN, "NIX_AF_GEN_INT", rvu_nix_af_rvu_gen_handler); if (!rc) goto err; rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1S, ~0ULL); /* Register and enable NIX_AF_ERR_INT interrupt */ rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_AF_ERR, "NIX_AF_ERR_INT", rvu_nix_af_rvu_err_handler); if (!rc) goto err; rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1S, ~0ULL); /* Register and enable NIX_AF_RAS interrupt */ rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_POISON, "NIX_AF_RAS", rvu_nix_af_rvu_ras_handler); if (!rc) goto err; rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1S, ~0ULL); return 0; err: rvu_nix_unregister_interrupts(rvu); return rc; } static int rvu_nix_report_show(struct devlink_fmsg *fmsg, void *ctx, enum nix_af_rvu_health health_reporter) { struct rvu_nix_event_ctx *nix_event_context; u64 intr_val; int err; nix_event_context = ctx; switch (health_reporter) { case NIX_AF_RVU_INTR: intr_val = nix_event_context->nix_af_rvu_int; err = rvu_report_pair_start(fmsg, "NIX_AF_RVU"); if (err) return err; err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX RVU Interrupt Reg ", nix_event_context->nix_af_rvu_int); if (err) return err; if (intr_val & BIT_ULL(0)) { err = devlink_fmsg_string_put(fmsg, "\n\tUnmap Slot Error"); if (err) return err; } err = rvu_report_pair_end(fmsg); if (err) return err; break; case NIX_AF_RVU_GEN: intr_val = nix_event_context->nix_af_rvu_gen; err = rvu_report_pair_start(fmsg, "NIX_AF_GENERAL"); if (err) return err; err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX General Interrupt Reg ", nix_event_context->nix_af_rvu_gen); if (err) return err; if (intr_val & BIT_ULL(0)) { err = devlink_fmsg_string_put(fmsg, "\n\tRx multicast pkt drop"); if (err) return err; } if (intr_val & BIT_ULL(1)) { err = devlink_fmsg_string_put(fmsg, "\n\tRx mirror pkt drop"); if (err) return err; } if (intr_val & BIT_ULL(4)) { err = devlink_fmsg_string_put(fmsg, "\n\tSMQ flush done"); if (err) return err; } err = rvu_report_pair_end(fmsg); if (err) return err; break; case NIX_AF_RVU_ERR: intr_val = nix_event_context->nix_af_rvu_err; err = rvu_report_pair_start(fmsg, "NIX_AF_ERR"); if (err) return err; err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX Error Interrupt Reg ", nix_event_context->nix_af_rvu_err); if (err) return err; if (intr_val & BIT_ULL(14)) { err = devlink_fmsg_string_put(fmsg, "\n\tFault on NIX_AQ_INST_S read"); if (err) return err; } if (intr_val & BIT_ULL(13)) { err = devlink_fmsg_string_put(fmsg, "\n\tFault on NIX_AQ_RES_S write"); if (err) return err; } if (intr_val & BIT_ULL(12)) { err = devlink_fmsg_string_put(fmsg, "\n\tAQ Doorbell Error"); if (err) return err; } if (intr_val & BIT_ULL(6)) { err = devlink_fmsg_string_put(fmsg, "\n\tRx on unmapped PF_FUNC"); if (err) return err; } if (intr_val & BIT_ULL(5)) { err = devlink_fmsg_string_put(fmsg, "\n\tRx multicast replication error"); if (err) return err; } if (intr_val & BIT_ULL(4)) { err = devlink_fmsg_string_put(fmsg, "\n\tFault on NIX_RX_MCE_S read"); if (err) return err; } if (intr_val & BIT_ULL(3)) { err = devlink_fmsg_string_put(fmsg, "\n\tFault on multicast WQE read"); if (err) return err; } if (intr_val & BIT_ULL(2)) { err = devlink_fmsg_string_put(fmsg, "\n\tFault on mirror WQE read"); if (err) return err; } if (intr_val & BIT_ULL(1)) { err = devlink_fmsg_string_put(fmsg, "\n\tFault on mirror pkt write"); if (err) return err; } if (intr_val & BIT_ULL(0)) { err = devlink_fmsg_string_put(fmsg, "\n\tFault on multicast pkt write"); if (err) return err; } err = rvu_report_pair_end(fmsg); if (err) return err; break; case NIX_AF_RVU_RAS: intr_val = nix_event_context->nix_af_rvu_err; err = rvu_report_pair_start(fmsg, "NIX_AF_RAS"); if (err) return err; err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX RAS Interrupt Reg ", nix_event_context->nix_af_rvu_err); if (err) return err; err = devlink_fmsg_string_put(fmsg, "\n\tPoison Data on:"); if (err) return err; if (intr_val & BIT_ULL(34)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX_AQ_INST_S"); if (err) return err; } if (intr_val & BIT_ULL(33)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX_AQ_RES_S"); if (err) return err; } if (intr_val & BIT_ULL(32)) { err = devlink_fmsg_string_put(fmsg, "\n\tHW ctx"); if (err) return err; } if (intr_val & BIT_ULL(4)) { err = devlink_fmsg_string_put(fmsg, "\n\tPacket from mirror buffer"); if (err) return err; } if (intr_val & BIT_ULL(3)) { err = devlink_fmsg_string_put(fmsg, "\n\tPacket from multicast buffer"); if (err) return err; } if (intr_val & BIT_ULL(2)) { err = devlink_fmsg_string_put(fmsg, "\n\tWQE read from mirror buffer"); if (err) return err; } if (intr_val & BIT_ULL(1)) { err = devlink_fmsg_string_put(fmsg, "\n\tWQE read from multicast buffer"); if (err) return err; } if (intr_val & BIT_ULL(0)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX_RX_MCE_S read"); if (err) return err; } err = rvu_report_pair_end(fmsg); if (err) return err; break; default: return -EINVAL; } return 0; } static int rvu_hw_nix_intr_dump(struct devlink_health_reporter *reporter, struct devlink_fmsg *fmsg, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_devlink *rvu_dl = rvu->rvu_dl; struct rvu_nix_event_ctx *nix_ctx; nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_INTR) : rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_INTR); } static int rvu_hw_nix_intr_recover(struct devlink_health_reporter *reporter, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_nix_event_ctx *nix_event_ctx = ctx; int blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return blkaddr; if (nix_event_ctx->nix_af_rvu_int) rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1S, ~0ULL); return 0; } static int rvu_hw_nix_gen_dump(struct devlink_health_reporter *reporter, struct devlink_fmsg *fmsg, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_devlink *rvu_dl = rvu->rvu_dl; struct rvu_nix_event_ctx *nix_ctx; nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_GEN) : rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_GEN); } static int rvu_hw_nix_gen_recover(struct devlink_health_reporter *reporter, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_nix_event_ctx *nix_event_ctx = ctx; int blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return blkaddr; if (nix_event_ctx->nix_af_rvu_gen) rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1S, ~0ULL); return 0; } static int rvu_hw_nix_err_dump(struct devlink_health_reporter *reporter, struct devlink_fmsg *fmsg, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_devlink *rvu_dl = rvu->rvu_dl; struct rvu_nix_event_ctx *nix_ctx; nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_ERR) : rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_ERR); } static int rvu_hw_nix_err_recover(struct devlink_health_reporter *reporter, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_nix_event_ctx *nix_event_ctx = ctx; int blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return blkaddr; if (nix_event_ctx->nix_af_rvu_err) rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1S, ~0ULL); return 0; } static int rvu_hw_nix_ras_dump(struct devlink_health_reporter *reporter, struct devlink_fmsg *fmsg, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_devlink *rvu_dl = rvu->rvu_dl; struct rvu_nix_event_ctx *nix_ctx; nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx; return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_RAS) : rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_RAS); } static int rvu_hw_nix_ras_recover(struct devlink_health_reporter *reporter, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_nix_event_ctx *nix_event_ctx = ctx; int blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0); if (blkaddr < 0) return blkaddr; if (nix_event_ctx->nix_af_rvu_int) rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1S, ~0ULL); return 0; } RVU_REPORTERS(hw_nix_intr); RVU_REPORTERS(hw_nix_gen); RVU_REPORTERS(hw_nix_err); RVU_REPORTERS(hw_nix_ras); static void rvu_nix_health_reporters_destroy(struct rvu_devlink *rvu_dl); static int rvu_nix_register_reporters(struct rvu_devlink *rvu_dl) { struct rvu_nix_health_reporters *rvu_reporters; struct rvu_nix_event_ctx *nix_event_context; struct rvu *rvu = rvu_dl->rvu; rvu_reporters = kzalloc(sizeof(*rvu_reporters), GFP_KERNEL); if (!rvu_reporters) return -ENOMEM; rvu_dl->rvu_nix_health_reporter = rvu_reporters; nix_event_context = kzalloc(sizeof(*nix_event_context), GFP_KERNEL); if (!nix_event_context) return -ENOMEM; rvu_reporters->nix_event_ctx = nix_event_context; rvu_reporters->rvu_hw_nix_intr_reporter = devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_intr_reporter_ops, 0, rvu); if (IS_ERR(rvu_reporters->rvu_hw_nix_intr_reporter)) { dev_warn(rvu->dev, "Failed to create hw_nix_intr reporter, err=%ld\n", PTR_ERR(rvu_reporters->rvu_hw_nix_intr_reporter)); return PTR_ERR(rvu_reporters->rvu_hw_nix_intr_reporter); } rvu_reporters->rvu_hw_nix_gen_reporter = devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_gen_reporter_ops, 0, rvu); if (IS_ERR(rvu_reporters->rvu_hw_nix_gen_reporter)) { dev_warn(rvu->dev, "Failed to create hw_nix_gen reporter, err=%ld\n", PTR_ERR(rvu_reporters->rvu_hw_nix_gen_reporter)); return PTR_ERR(rvu_reporters->rvu_hw_nix_gen_reporter); } rvu_reporters->rvu_hw_nix_err_reporter = devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_err_reporter_ops, 0, rvu); if (IS_ERR(rvu_reporters->rvu_hw_nix_err_reporter)) { dev_warn(rvu->dev, "Failed to create hw_nix_err reporter, err=%ld\n", PTR_ERR(rvu_reporters->rvu_hw_nix_err_reporter)); return PTR_ERR(rvu_reporters->rvu_hw_nix_err_reporter); } rvu_reporters->rvu_hw_nix_ras_reporter = devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_ras_reporter_ops, 0, rvu); if (IS_ERR(rvu_reporters->rvu_hw_nix_ras_reporter)) { dev_warn(rvu->dev, "Failed to create hw_nix_ras reporter, err=%ld\n", PTR_ERR(rvu_reporters->rvu_hw_nix_ras_reporter)); return PTR_ERR(rvu_reporters->rvu_hw_nix_ras_reporter); } rvu_dl->devlink_wq = create_workqueue("rvu_devlink_wq"); if (!rvu_dl->devlink_wq) return -ENOMEM; INIT_WORK(&rvu_reporters->intr_work, rvu_nix_intr_work); INIT_WORK(&rvu_reporters->gen_work, rvu_nix_gen_work); INIT_WORK(&rvu_reporters->err_work, rvu_nix_err_work); INIT_WORK(&rvu_reporters->ras_work, rvu_nix_ras_work); return 0; } static int rvu_nix_health_reporters_create(struct rvu_devlink *rvu_dl) { struct rvu *rvu = rvu_dl->rvu; int err; err = rvu_nix_register_reporters(rvu_dl); if (err) { dev_warn(rvu->dev, "Failed to create nix reporter, err =%d\n", err); return err; } rvu_nix_register_interrupts(rvu); return 0; } static void rvu_nix_health_reporters_destroy(struct rvu_devlink *rvu_dl) { struct rvu_nix_health_reporters *nix_reporters; struct rvu *rvu = rvu_dl->rvu; nix_reporters = rvu_dl->rvu_nix_health_reporter; if (!nix_reporters->rvu_hw_nix_ras_reporter) return; if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_intr_reporter)) devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_intr_reporter); if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_gen_reporter)) devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_gen_reporter); if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_err_reporter)) devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_err_reporter); if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_ras_reporter)) devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_ras_reporter); rvu_nix_unregister_interrupts(rvu); kfree(rvu_dl->rvu_nix_health_reporter->nix_event_ctx); kfree(rvu_dl->rvu_nix_health_reporter); } static void rvu_npa_intr_work(struct work_struct *work) { struct rvu_npa_health_reporters *rvu_npa_health_reporter; rvu_npa_health_reporter = container_of(work, struct rvu_npa_health_reporters, intr_work); devlink_health_report(rvu_npa_health_reporter->rvu_hw_npa_intr_reporter, "NPA_AF_RVU Error", rvu_npa_health_reporter->npa_event_ctx); } static irqreturn_t rvu_npa_af_rvu_intr_handler(int irq, void *rvu_irq) { struct rvu_npa_event_ctx *npa_event_context; struct rvu_devlink *rvu_dl = rvu_irq; struct rvu *rvu; int blkaddr; u64 intr; rvu = rvu_dl->rvu; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return IRQ_NONE; npa_event_context = rvu_dl->rvu_npa_health_reporter->npa_event_ctx; intr = rvu_read64(rvu, blkaddr, NPA_AF_RVU_INT); npa_event_context->npa_af_rvu_int = intr; /* Clear interrupts */ rvu_write64(rvu, blkaddr, NPA_AF_RVU_INT, intr); rvu_write64(rvu, blkaddr, NPA_AF_RVU_INT_ENA_W1C, ~0ULL); queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_npa_health_reporter->intr_work); return IRQ_HANDLED; } static void rvu_npa_gen_work(struct work_struct *work) { struct rvu_npa_health_reporters *rvu_npa_health_reporter; rvu_npa_health_reporter = container_of(work, struct rvu_npa_health_reporters, gen_work); devlink_health_report(rvu_npa_health_reporter->rvu_hw_npa_gen_reporter, "NPA_AF_GEN Error", rvu_npa_health_reporter->npa_event_ctx); } static irqreturn_t rvu_npa_af_gen_intr_handler(int irq, void *rvu_irq) { struct rvu_npa_event_ctx *npa_event_context; struct rvu_devlink *rvu_dl = rvu_irq; struct rvu *rvu; int blkaddr; u64 intr; rvu = rvu_dl->rvu; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return IRQ_NONE; npa_event_context = rvu_dl->rvu_npa_health_reporter->npa_event_ctx; intr = rvu_read64(rvu, blkaddr, NPA_AF_GEN_INT); npa_event_context->npa_af_rvu_gen = intr; /* Clear interrupts */ rvu_write64(rvu, blkaddr, NPA_AF_GEN_INT, intr); rvu_write64(rvu, blkaddr, NPA_AF_GEN_INT_ENA_W1C, ~0ULL); queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_npa_health_reporter->gen_work); return IRQ_HANDLED; } static void rvu_npa_err_work(struct work_struct *work) { struct rvu_npa_health_reporters *rvu_npa_health_reporter; rvu_npa_health_reporter = container_of(work, struct rvu_npa_health_reporters, err_work); devlink_health_report(rvu_npa_health_reporter->rvu_hw_npa_err_reporter, "NPA_AF_ERR Error", rvu_npa_health_reporter->npa_event_ctx); } static irqreturn_t rvu_npa_af_err_intr_handler(int irq, void *rvu_irq) { struct rvu_npa_event_ctx *npa_event_context; struct rvu_devlink *rvu_dl = rvu_irq; struct rvu *rvu; int blkaddr; u64 intr; rvu = rvu_dl->rvu; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return IRQ_NONE; npa_event_context = rvu_dl->rvu_npa_health_reporter->npa_event_ctx; intr = rvu_read64(rvu, blkaddr, NPA_AF_ERR_INT); npa_event_context->npa_af_rvu_err = intr; /* Clear interrupts */ rvu_write64(rvu, blkaddr, NPA_AF_ERR_INT, intr); rvu_write64(rvu, blkaddr, NPA_AF_ERR_INT_ENA_W1C, ~0ULL); queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_npa_health_reporter->err_work); return IRQ_HANDLED; } static void rvu_npa_ras_work(struct work_struct *work) { struct rvu_npa_health_reporters *rvu_npa_health_reporter; rvu_npa_health_reporter = container_of(work, struct rvu_npa_health_reporters, ras_work); devlink_health_report(rvu_npa_health_reporter->rvu_hw_npa_ras_reporter, "HW NPA_AF_RAS Error reported", rvu_npa_health_reporter->npa_event_ctx); } static irqreturn_t rvu_npa_af_ras_intr_handler(int irq, void *rvu_irq) { struct rvu_npa_event_ctx *npa_event_context; struct rvu_devlink *rvu_dl = rvu_irq; struct rvu *rvu; int blkaddr; u64 intr; rvu = rvu_dl->rvu; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return IRQ_NONE; npa_event_context = rvu_dl->rvu_npa_health_reporter->npa_event_ctx; intr = rvu_read64(rvu, blkaddr, NPA_AF_RAS); npa_event_context->npa_af_rvu_ras = intr; /* Clear interrupts */ rvu_write64(rvu, blkaddr, NPA_AF_RAS, intr); rvu_write64(rvu, blkaddr, NPA_AF_RAS_ENA_W1C, ~0ULL); queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_npa_health_reporter->ras_work); return IRQ_HANDLED; } static void rvu_npa_unregister_interrupts(struct rvu *rvu) { struct rvu_devlink *rvu_dl = rvu->rvu_dl; int i, offs, blkaddr; u64 reg; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return; reg = rvu_read64(rvu, blkaddr, NPA_PRIV_AF_INT_CFG); offs = reg & 0x3FF; rvu_write64(rvu, blkaddr, NPA_AF_RVU_INT_ENA_W1C, ~0ULL); rvu_write64(rvu, blkaddr, NPA_AF_GEN_INT_ENA_W1C, ~0ULL); rvu_write64(rvu, blkaddr, NPA_AF_ERR_INT_ENA_W1C, ~0ULL); rvu_write64(rvu, blkaddr, NPA_AF_RAS_ENA_W1C, ~0ULL); for (i = 0; i < NPA_AF_INT_VEC_CNT; i++) if (rvu->irq_allocated[offs + i]) { free_irq(pci_irq_vector(rvu->pdev, offs + i), rvu_dl); rvu->irq_allocated[offs + i] = false; } } static int rvu_npa_register_interrupts(struct rvu *rvu) { int blkaddr, base; bool rc; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return blkaddr; /* Get NPA AF MSIX vectors offset. */ base = rvu_read64(rvu, blkaddr, NPA_PRIV_AF_INT_CFG) & 0x3ff; if (!base) { dev_warn(rvu->dev, "Failed to get NPA_AF_INT vector offsets\n"); return 0; } /* Register and enable NPA_AF_RVU_INT interrupt */ rc = rvu_common_request_irq(rvu, base + NPA_AF_INT_VEC_RVU, "NPA_AF_RVU_INT", rvu_npa_af_rvu_intr_handler); if (!rc) goto err; rvu_write64(rvu, blkaddr, NPA_AF_RVU_INT_ENA_W1S, ~0ULL); /* Register and enable NPA_AF_GEN_INT interrupt */ rc = rvu_common_request_irq(rvu, base + NPA_AF_INT_VEC_GEN, "NPA_AF_RVU_GEN", rvu_npa_af_gen_intr_handler); if (!rc) goto err; rvu_write64(rvu, blkaddr, NPA_AF_GEN_INT_ENA_W1S, ~0ULL); /* Register and enable NPA_AF_ERR_INT interrupt */ rc = rvu_common_request_irq(rvu, base + NPA_AF_INT_VEC_AF_ERR, "NPA_AF_ERR_INT", rvu_npa_af_err_intr_handler); if (!rc) goto err; rvu_write64(rvu, blkaddr, NPA_AF_ERR_INT_ENA_W1S, ~0ULL); /* Register and enable NPA_AF_RAS interrupt */ rc = rvu_common_request_irq(rvu, base + NPA_AF_INT_VEC_POISON, "NPA_AF_RAS", rvu_npa_af_ras_intr_handler); if (!rc) goto err; rvu_write64(rvu, blkaddr, NPA_AF_RAS_ENA_W1S, ~0ULL); return 0; err: rvu_npa_unregister_interrupts(rvu); return rc; } static int rvu_npa_report_show(struct devlink_fmsg *fmsg, void *ctx, enum npa_af_rvu_health health_reporter) { struct rvu_npa_event_ctx *npa_event_context; unsigned int alloc_dis, free_dis; u64 intr_val; int err; npa_event_context = ctx; switch (health_reporter) { case NPA_AF_RVU_GEN: intr_val = npa_event_context->npa_af_rvu_gen; err = rvu_report_pair_start(fmsg, "NPA_AF_GENERAL"); if (err) return err; err = devlink_fmsg_u64_pair_put(fmsg, "\tNPA General Interrupt Reg ", npa_event_context->npa_af_rvu_gen); if (err) return err; if (intr_val & BIT_ULL(32)) { err = devlink_fmsg_string_put(fmsg, "\n\tUnmap PF Error"); if (err) return err; } free_dis = FIELD_GET(GENMASK(15, 0), intr_val); if (free_dis & BIT(NPA_INPQ_NIX0_RX)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX0: free disabled RX"); if (err) return err; } if (free_dis & BIT(NPA_INPQ_NIX0_TX)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX0:free disabled TX"); if (err) return err; } if (free_dis & BIT(NPA_INPQ_NIX1_RX)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX1: free disabled RX"); if (err) return err; } if (free_dis & BIT(NPA_INPQ_NIX1_TX)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX1:free disabled TX"); if (err) return err; } if (free_dis & BIT(NPA_INPQ_SSO)) { err = devlink_fmsg_string_put(fmsg, "\n\tFree Disabled for SSO"); if (err) return err; } if (free_dis & BIT(NPA_INPQ_TIM)) { err = devlink_fmsg_string_put(fmsg, "\n\tFree Disabled for TIM"); if (err) return err; } if (free_dis & BIT(NPA_INPQ_DPI)) { err = devlink_fmsg_string_put(fmsg, "\n\tFree Disabled for DPI"); if (err) return err; } if (free_dis & BIT(NPA_INPQ_AURA_OP)) { err = devlink_fmsg_string_put(fmsg, "\n\tFree Disabled for AURA"); if (err) return err; } alloc_dis = FIELD_GET(GENMASK(31, 16), intr_val); if (alloc_dis & BIT(NPA_INPQ_NIX0_RX)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX0: alloc disabled RX"); if (err) return err; } if (alloc_dis & BIT(NPA_INPQ_NIX0_TX)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX0:alloc disabled TX"); if (err) return err; } if (alloc_dis & BIT(NPA_INPQ_NIX1_RX)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX1: alloc disabled RX"); if (err) return err; } if (alloc_dis & BIT(NPA_INPQ_NIX1_TX)) { err = devlink_fmsg_string_put(fmsg, "\n\tNIX1:alloc disabled TX"); if (err) return err; } if (alloc_dis & BIT(NPA_INPQ_SSO)) { err = devlink_fmsg_string_put(fmsg, "\n\tAlloc Disabled for SSO"); if (err) return err; } if (alloc_dis & BIT(NPA_INPQ_TIM)) { err = devlink_fmsg_string_put(fmsg, "\n\tAlloc Disabled for TIM"); if (err) return err; } if (alloc_dis & BIT(NPA_INPQ_DPI)) { err = devlink_fmsg_string_put(fmsg, "\n\tAlloc Disabled for DPI"); if (err) return err; } if (alloc_dis & BIT(NPA_INPQ_AURA_OP)) { err = devlink_fmsg_string_put(fmsg, "\n\tAlloc Disabled for AURA"); if (err) return err; } err = rvu_report_pair_end(fmsg); if (err) return err; break; case NPA_AF_RVU_ERR: err = rvu_report_pair_start(fmsg, "NPA_AF_ERR"); if (err) return err; err = devlink_fmsg_u64_pair_put(fmsg, "\tNPA Error Interrupt Reg ", npa_event_context->npa_af_rvu_err); if (err) return err; if (npa_event_context->npa_af_rvu_err & BIT_ULL(14)) { err = devlink_fmsg_string_put(fmsg, "\n\tFault on NPA_AQ_INST_S read"); if (err) return err; } if (npa_event_context->npa_af_rvu_err & BIT_ULL(13)) { err = devlink_fmsg_string_put(fmsg, "\n\tFault on NPA_AQ_RES_S write"); if (err) return err; } if (npa_event_context->npa_af_rvu_err & BIT_ULL(12)) { err = devlink_fmsg_string_put(fmsg, "\n\tAQ Doorbell Error"); if (err) return err; } err = rvu_report_pair_end(fmsg); if (err) return err; break; case NPA_AF_RVU_RAS: err = rvu_report_pair_start(fmsg, "NPA_AF_RVU_RAS"); if (err) return err; err = devlink_fmsg_u64_pair_put(fmsg, "\tNPA RAS Interrupt Reg ", npa_event_context->npa_af_rvu_ras); if (err) return err; if (npa_event_context->npa_af_rvu_ras & BIT_ULL(34)) { err = devlink_fmsg_string_put(fmsg, "\n\tPoison data on NPA_AQ_INST_S"); if (err) return err; } if (npa_event_context->npa_af_rvu_ras & BIT_ULL(33)) { err = devlink_fmsg_string_put(fmsg, "\n\tPoison data on NPA_AQ_RES_S"); if (err) return err; } if (npa_event_context->npa_af_rvu_ras & BIT_ULL(32)) { err = devlink_fmsg_string_put(fmsg, "\n\tPoison data on HW context"); if (err) return err; } err = rvu_report_pair_end(fmsg); if (err) return err; break; case NPA_AF_RVU_INTR: err = rvu_report_pair_start(fmsg, "NPA_AF_RVU"); if (err) return err; err = devlink_fmsg_u64_pair_put(fmsg, "\tNPA RVU Interrupt Reg ", npa_event_context->npa_af_rvu_int); if (err) return err; if (npa_event_context->npa_af_rvu_int & BIT_ULL(0)) { err = devlink_fmsg_string_put(fmsg, "\n\tUnmap Slot Error"); if (err) return err; } return rvu_report_pair_end(fmsg); default: return -EINVAL; } return 0; } static int rvu_hw_npa_intr_dump(struct devlink_health_reporter *reporter, struct devlink_fmsg *fmsg, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_devlink *rvu_dl = rvu->rvu_dl; struct rvu_npa_event_ctx *npa_ctx; npa_ctx = rvu_dl->rvu_npa_health_reporter->npa_event_ctx; return ctx ? rvu_npa_report_show(fmsg, ctx, NPA_AF_RVU_INTR) : rvu_npa_report_show(fmsg, npa_ctx, NPA_AF_RVU_INTR); } static int rvu_hw_npa_intr_recover(struct devlink_health_reporter *reporter, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_npa_event_ctx *npa_event_ctx = ctx; int blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return blkaddr; if (npa_event_ctx->npa_af_rvu_int) rvu_write64(rvu, blkaddr, NPA_AF_RVU_INT_ENA_W1S, ~0ULL); return 0; } static int rvu_hw_npa_gen_dump(struct devlink_health_reporter *reporter, struct devlink_fmsg *fmsg, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_devlink *rvu_dl = rvu->rvu_dl; struct rvu_npa_event_ctx *npa_ctx; npa_ctx = rvu_dl->rvu_npa_health_reporter->npa_event_ctx; return ctx ? rvu_npa_report_show(fmsg, ctx, NPA_AF_RVU_GEN) : rvu_npa_report_show(fmsg, npa_ctx, NPA_AF_RVU_GEN); } static int rvu_hw_npa_gen_recover(struct devlink_health_reporter *reporter, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_npa_event_ctx *npa_event_ctx = ctx; int blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return blkaddr; if (npa_event_ctx->npa_af_rvu_gen) rvu_write64(rvu, blkaddr, NPA_AF_GEN_INT_ENA_W1S, ~0ULL); return 0; } static int rvu_hw_npa_err_dump(struct devlink_health_reporter *reporter, struct devlink_fmsg *fmsg, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_devlink *rvu_dl = rvu->rvu_dl; struct rvu_npa_event_ctx *npa_ctx; npa_ctx = rvu_dl->rvu_npa_health_reporter->npa_event_ctx; return ctx ? rvu_npa_report_show(fmsg, ctx, NPA_AF_RVU_ERR) : rvu_npa_report_show(fmsg, npa_ctx, NPA_AF_RVU_ERR); } static int rvu_hw_npa_err_recover(struct devlink_health_reporter *reporter, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_npa_event_ctx *npa_event_ctx = ctx; int blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return blkaddr; if (npa_event_ctx->npa_af_rvu_err) rvu_write64(rvu, blkaddr, NPA_AF_ERR_INT_ENA_W1S, ~0ULL); return 0; } static int rvu_hw_npa_ras_dump(struct devlink_health_reporter *reporter, struct devlink_fmsg *fmsg, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_devlink *rvu_dl = rvu->rvu_dl; struct rvu_npa_event_ctx *npa_ctx; npa_ctx = rvu_dl->rvu_npa_health_reporter->npa_event_ctx; return ctx ? rvu_npa_report_show(fmsg, ctx, NPA_AF_RVU_RAS) : rvu_npa_report_show(fmsg, npa_ctx, NPA_AF_RVU_RAS); } static int rvu_hw_npa_ras_recover(struct devlink_health_reporter *reporter, void *ctx, struct netlink_ext_ack *netlink_extack) { struct rvu *rvu = devlink_health_reporter_priv(reporter); struct rvu_npa_event_ctx *npa_event_ctx = ctx; int blkaddr; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPA, 0); if (blkaddr < 0) return blkaddr; if (npa_event_ctx->npa_af_rvu_ras) rvu_write64(rvu, blkaddr, NPA_AF_RAS_ENA_W1S, ~0ULL); return 0; } RVU_REPORTERS(hw_npa_intr); RVU_REPORTERS(hw_npa_gen); RVU_REPORTERS(hw_npa_err); RVU_REPORTERS(hw_npa_ras); static void rvu_npa_health_reporters_destroy(struct rvu_devlink *rvu_dl); static int rvu_npa_register_reporters(struct rvu_devlink *rvu_dl) { struct rvu_npa_health_reporters *rvu_reporters; struct rvu_npa_event_ctx *npa_event_context; struct rvu *rvu = rvu_dl->rvu; rvu_reporters = kzalloc(sizeof(*rvu_reporters), GFP_KERNEL); if (!rvu_reporters) return -ENOMEM; rvu_dl->rvu_npa_health_reporter = rvu_reporters; npa_event_context = kzalloc(sizeof(*npa_event_context), GFP_KERNEL); if (!npa_event_context) return -ENOMEM; rvu_reporters->npa_event_ctx = npa_event_context; rvu_reporters->rvu_hw_npa_intr_reporter = devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_npa_intr_reporter_ops, 0, rvu); if (IS_ERR(rvu_reporters->rvu_hw_npa_intr_reporter)) { dev_warn(rvu->dev, "Failed to create hw_npa_intr reporter, err=%ld\n", PTR_ERR(rvu_reporters->rvu_hw_npa_intr_reporter)); return PTR_ERR(rvu_reporters->rvu_hw_npa_intr_reporter); } rvu_reporters->rvu_hw_npa_gen_reporter = devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_npa_gen_reporter_ops, 0, rvu); if (IS_ERR(rvu_reporters->rvu_hw_npa_gen_reporter)) { dev_warn(rvu->dev, "Failed to create hw_npa_gen reporter, err=%ld\n", PTR_ERR(rvu_reporters->rvu_hw_npa_gen_reporter)); return PTR_ERR(rvu_reporters->rvu_hw_npa_gen_reporter); } rvu_reporters->rvu_hw_npa_err_reporter = devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_npa_err_reporter_ops, 0, rvu); if (IS_ERR(rvu_reporters->rvu_hw_npa_err_reporter)) { dev_warn(rvu->dev, "Failed to create hw_npa_err reporter, err=%ld\n", PTR_ERR(rvu_reporters->rvu_hw_npa_err_reporter)); return PTR_ERR(rvu_reporters->rvu_hw_npa_err_reporter); } rvu_reporters->rvu_hw_npa_ras_reporter = devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_npa_ras_reporter_ops, 0, rvu); if (IS_ERR(rvu_reporters->rvu_hw_npa_ras_reporter)) { dev_warn(rvu->dev, "Failed to create hw_npa_ras reporter, err=%ld\n", PTR_ERR(rvu_reporters->rvu_hw_npa_ras_reporter)); return PTR_ERR(rvu_reporters->rvu_hw_npa_ras_reporter); } rvu_dl->devlink_wq = create_workqueue("rvu_devlink_wq"); if (!rvu_dl->devlink_wq) return -ENOMEM; INIT_WORK(&rvu_reporters->intr_work, rvu_npa_intr_work); INIT_WORK(&rvu_reporters->err_work, rvu_npa_err_work); INIT_WORK(&rvu_reporters->gen_work, rvu_npa_gen_work); INIT_WORK(&rvu_reporters->ras_work, rvu_npa_ras_work); return 0; } static int rvu_npa_health_reporters_create(struct rvu_devlink *rvu_dl) { struct rvu *rvu = rvu_dl->rvu; int err; err = rvu_npa_register_reporters(rvu_dl); if (err) { dev_warn(rvu->dev, "Failed to create npa reporter, err =%d\n", err); return err; } rvu_npa_register_interrupts(rvu); return 0; } static void rvu_npa_health_reporters_destroy(struct rvu_devlink *rvu_dl) { struct rvu_npa_health_reporters *npa_reporters; struct rvu *rvu = rvu_dl->rvu; npa_reporters = rvu_dl->rvu_npa_health_reporter; if (!npa_reporters->rvu_hw_npa_ras_reporter) return; if (!IS_ERR_OR_NULL(npa_reporters->rvu_hw_npa_intr_reporter)) devlink_health_reporter_destroy(npa_reporters->rvu_hw_npa_intr_reporter); if (!IS_ERR_OR_NULL(npa_reporters->rvu_hw_npa_gen_reporter)) devlink_health_reporter_destroy(npa_reporters->rvu_hw_npa_gen_reporter); if (!IS_ERR_OR_NULL(npa_reporters->rvu_hw_npa_err_reporter)) devlink_health_reporter_destroy(npa_reporters->rvu_hw_npa_err_reporter); if (!IS_ERR_OR_NULL(npa_reporters->rvu_hw_npa_ras_reporter)) devlink_health_reporter_destroy(npa_reporters->rvu_hw_npa_ras_reporter); rvu_npa_unregister_interrupts(rvu); kfree(rvu_dl->rvu_npa_health_reporter->npa_event_ctx); kfree(rvu_dl->rvu_npa_health_reporter); } static int rvu_health_reporters_create(struct rvu *rvu) { struct rvu_devlink *rvu_dl; int err; rvu_dl = rvu->rvu_dl; err = rvu_npa_health_reporters_create(rvu_dl); if (err) return err; return rvu_nix_health_reporters_create(rvu_dl); } static void rvu_health_reporters_destroy(struct rvu *rvu) { struct rvu_devlink *rvu_dl; if (!rvu->rvu_dl) return; rvu_dl = rvu->rvu_dl; rvu_npa_health_reporters_destroy(rvu_dl); rvu_nix_health_reporters_destroy(rvu_dl); } /* Devlink Params APIs */ static int rvu_af_dl_dwrr_mtu_validate(struct devlink *devlink, u32 id, union devlink_param_value val, struct netlink_ext_ack *extack) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; int dwrr_mtu = val.vu32; struct nix_txsch *txsch; struct nix_hw *nix_hw; if (!rvu->hw->cap.nix_common_dwrr_mtu) { NL_SET_ERR_MSG_MOD(extack, "Setting DWRR_MTU is not supported on this silicon"); return -EOPNOTSUPP; } if ((dwrr_mtu > 65536 || !is_power_of_2(dwrr_mtu)) && (dwrr_mtu != 9728 && dwrr_mtu != 10240)) { NL_SET_ERR_MSG_MOD(extack, "Invalid, supported MTUs are 0,2,4,8.16,32,64....4K,8K,32K,64K and 9728, 10240"); return -EINVAL; } nix_hw = get_nix_hw(rvu->hw, BLKADDR_NIX0); if (!nix_hw) return -ENODEV; txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ]; if (rvu_rsrc_free_count(&txsch->schq) != txsch->schq.max) { NL_SET_ERR_MSG_MOD(extack, "Changing DWRR MTU is not supported when there are active NIXLFs"); NL_SET_ERR_MSG_MOD(extack, "Make sure none of the PF/VF interfaces are initialized and retry"); return -EOPNOTSUPP; } return 0; } static int rvu_af_dl_dwrr_mtu_set(struct devlink *devlink, u32 id, struct devlink_param_gset_ctx *ctx) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; u64 dwrr_mtu; dwrr_mtu = convert_bytes_to_dwrr_mtu(ctx->val.vu32); rvu_write64(rvu, BLKADDR_NIX0, NIX_AF_DWRR_RPM_MTU, dwrr_mtu); return 0; } static int rvu_af_dl_dwrr_mtu_get(struct devlink *devlink, u32 id, struct devlink_param_gset_ctx *ctx) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; u64 dwrr_mtu; if (!rvu->hw->cap.nix_common_dwrr_mtu) return -EOPNOTSUPP; dwrr_mtu = rvu_read64(rvu, BLKADDR_NIX0, NIX_AF_DWRR_RPM_MTU); ctx->val.vu32 = convert_dwrr_mtu_to_bytes(dwrr_mtu); return 0; } enum rvu_af_dl_param_id { RVU_AF_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, RVU_AF_DEVLINK_PARAM_ID_DWRR_MTU, }; static const struct devlink_param rvu_af_dl_params[] = { DEVLINK_PARAM_DRIVER(RVU_AF_DEVLINK_PARAM_ID_DWRR_MTU, "dwrr_mtu", DEVLINK_PARAM_TYPE_U32, BIT(DEVLINK_PARAM_CMODE_RUNTIME), rvu_af_dl_dwrr_mtu_get, rvu_af_dl_dwrr_mtu_set, rvu_af_dl_dwrr_mtu_validate), }; /* Devlink switch mode */ static int rvu_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; struct rvu_switch *rswitch; rswitch = &rvu->rswitch; *mode = rswitch->mode; return 0; } static int rvu_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, struct netlink_ext_ack *extack) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; struct rvu_switch *rswitch; rswitch = &rvu->rswitch; switch (mode) { case DEVLINK_ESWITCH_MODE_LEGACY: case DEVLINK_ESWITCH_MODE_SWITCHDEV: if (rswitch->mode == mode) return 0; rswitch->mode = mode; if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV) rvu_switch_enable(rvu); else rvu_switch_disable(rvu); break; default: return -EINVAL; } return 0; } static int rvu_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, struct netlink_ext_ack *extack) { return devlink_info_driver_name_put(req, DRV_NAME); } static const struct devlink_ops rvu_devlink_ops = { .info_get = rvu_devlink_info_get, .eswitch_mode_get = rvu_devlink_eswitch_mode_get, .eswitch_mode_set = rvu_devlink_eswitch_mode_set, }; int rvu_register_dl(struct rvu *rvu) { struct rvu_devlink *rvu_dl; struct devlink *dl; int err; dl = devlink_alloc(&rvu_devlink_ops, sizeof(struct rvu_devlink), rvu->dev); if (!dl) { dev_warn(rvu->dev, "devlink_alloc failed\n"); return -ENOMEM; } err = devlink_register(dl); if (err) { dev_err(rvu->dev, "devlink register failed with error %d\n", err); devlink_free(dl); return err; } rvu_dl = devlink_priv(dl); rvu_dl->dl = dl; rvu_dl->rvu = rvu; rvu->rvu_dl = rvu_dl; err = rvu_health_reporters_create(rvu); if (err) { dev_err(rvu->dev, "devlink health reporter creation failed with error %d\n", err); goto err_dl_health; } err = devlink_params_register(dl, rvu_af_dl_params, ARRAY_SIZE(rvu_af_dl_params)); if (err) { dev_err(rvu->dev, "devlink params register failed with error %d", err); goto err_dl_health; } devlink_params_publish(dl); return 0; err_dl_health: rvu_health_reporters_destroy(rvu); devlink_unregister(dl); devlink_free(dl); return err; } void rvu_unregister_dl(struct rvu *rvu) { struct rvu_devlink *rvu_dl = rvu->rvu_dl; struct devlink *dl = rvu_dl->dl; if (!dl) return; devlink_params_unregister(dl, rvu_af_dl_params, ARRAY_SIZE(rvu_af_dl_params)); rvu_health_reporters_destroy(rvu); devlink_unregister(dl); devlink_free(dl); }