This patch adds support for error handling in APM X-Gene SoC Queue Manager/Traffic Manager. Signed-off-by: Ravi Patel <rapatel@xxxxxxx> Signed-off-by: Keyur Chudgar <kchudgar@xxxxxxx> --- drivers/misc/xgene/qmtm/Makefile | 2 +- drivers/misc/xgene/qmtm/xgene_qmtm_error.c | 283 ++++++++++++++++++++++++++++ drivers/misc/xgene/qmtm/xgene_qmtm_main.c | 6 +- drivers/misc/xgene/qmtm/xgene_qmtm_main.h | 4 + 4 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 drivers/misc/xgene/qmtm/xgene_qmtm_error.c diff --git a/drivers/misc/xgene/qmtm/Makefile b/drivers/misc/xgene/qmtm/Makefile index 68c2a86..8448253 100644 --- a/drivers/misc/xgene/qmtm/Makefile +++ b/drivers/misc/xgene/qmtm/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_XGENE_QMTM) += xgene-qmtm.o -xgene-qmtm-objs := xgene_qmtm_main.o xgene_qmtm_storm.o +xgene-qmtm-objs := xgene_qmtm_main.o xgene_qmtm_storm.o xgene_qmtm_error.o diff --git a/drivers/misc/xgene/qmtm/xgene_qmtm_error.c b/drivers/misc/xgene/qmtm/xgene_qmtm_error.c new file mode 100644 index 0000000..5b4f4a9 --- /dev/null +++ b/drivers/misc/xgene/qmtm/xgene_qmtm_error.c @@ -0,0 +1,283 @@ +/* + * AppliedMicro X-Gene SOC Queue Manager/Traffic Manager driver + * + * Copyright (c) 2013 Applied Micro Circuits Corporation. + * Author: Ravi Patel <rapatel@xxxxxxx> + * Keyur Chudgar <kchudgar@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/interrupt.h> +#include "xgene_qmtm_main.h" + +#define QM_INTERRUPT_ADDR 0x00000124 +#define QM_INTERRUPTMASK_ADDR 0x00000128 +#define QUEUE_NOT_EMPTYMASK_MASK 0x80000000 +#define ACR_FIFO_CRITICALMASK_MASK 0x00000008 +#define QPCORE_ACR_ERRORMASK_MASK 0x00000004 +#define DEQ_AXI_ERRORMASK_MASK 0x00000002 +#define PBM_DEC_ERRORMASK_MASK 0x00000001 + +#define CSR_PBM_ERRINF_ADDR 0x00000134 +#define ACR_QID_RD(src) (((src) & 0x00ffc000)>>14) +#define QID_RD(src) (((src) & 0x000003ff)) + +#define CSR_MSGRD_ERRINF_ADDR 0x00000138 + +#define CSR_ERRQ_ADDR 0x00000218 +#define UNEXPECTED_EN_SET(dst, src) \ + (((dst) & ~0x80000000) | (((u32)(src)<<31) & 0x80000000)) +#define UNEXPECTED_QID_SET(dst, src) \ + (((dst) & ~0x03ff0000) | (((u32)(src)<<16) & 0x03ff0000)) +#define EXPECTED_EN_SET(dst, src) \ + (((dst) & ~0x00008000) | (((u32)(src)<<15) & 0x00008000)) +#define EXPECTED_QID_SET(dst, src) \ + (((dst) & ~0x000003ff) | (((u32)(src)) & 0x000003ff)) + +/* QMTM Error Reporting */ +enum xgene_qmtm_lerr { + QMTM_NO_ERR, + QMTM_MSG_SIZE_ERR, + QMTM_HOP_COUNT_ERR, + QMTM_VQ_ENQ_ERR, + QMTM_DISABLEDQ_ENQ_ERR, + QMTM_Q_OVERFLOW_ERR, + QMTM_ENQUEUE_ERR, + QMTM_DEQUEUE_ERR, +}; + +/* Parse Error Message received on Error Queue */ +static void xgene_qmtm_error_msg(struct xgene_qmtm_qinfo *qinfo, + struct xgene_qmtm_msg32 *msg) +{ + struct xgene_qmtm_msg16 *msg16 = &msg->msg16; + struct device *dev = &qinfo->qmtm->pdev->dev; + u16 queue_id = qinfo->queue_id; + + dev_err(dev, "Error ELErr[%d] LErr[%d] for Qid[%d]\n", + msg16->ELErr, msg16->LErr, queue_id); + + switch (msg16->LErr) { + case QMTM_MSG_SIZE_ERR: + dev_err(dev, "Msg Size Error for Enqueue on Queue %d\n", + queue_id); + break; + case QMTM_HOP_COUNT_ERR: + dev_err(dev, "Hop count error, hop count of 3 for Queue %d\n", + queue_id); + break; + case QMTM_VQ_ENQ_ERR: + dev_err(dev, "Enqueue on Virtual Queue %d\n", queue_id); + break; + case QMTM_DISABLEDQ_ENQ_ERR: + dev_err(dev, "Enqueue on disabled Queue %d\n", queue_id); + break; + case QMTM_Q_OVERFLOW_ERR: + dev_err(dev, "Queue %d overflow, message sent to Error Queue\n", + queue_id); + break; + case QMTM_ENQUEUE_ERR: + dev_err(dev, "Enqueue Queue\n"); + break; + case QMTM_DEQUEUE_ERR: + dev_err(dev, "Dequeue Queue\n"); + break; + default: + dev_err(dev, "Unknown Error\n"); + break; + } +} + +static void xgene_qmtm_error(struct xgene_qmtm *qmtm) +{ + struct device *dev = &qmtm->pdev->dev; + struct xgene_qmtm_qinfo qinfo; + u32 status; + u32 pbm_err; + u32 msgrd_err; + + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.qmtm = qmtm; + + xgene_qmtm_rd32(qmtm, QM_INTERRUPT_ADDR, &status); + dev_err(dev, "error interrupt status 0x%08X\n", status); + + xgene_qmtm_rd32(qmtm, CSR_PBM_ERRINF_ADDR, &pbm_err); + dev_err(dev, "CSR PBM ERRINF (0x%X) value 0x%08X\n", + CSR_PBM_ERRINF_ADDR, pbm_err); + + xgene_qmtm_rd32(qmtm, CSR_MSGRD_ERRINF_ADDR, &msgrd_err); + dev_err(dev, "CSR MSGRD ERRINF (0x%X) value 0x%08X\n", + CSR_MSGRD_ERRINF_ADDR, msgrd_err); + + qinfo.queue_id = QID_RD(msgrd_err); + dev_err(dev, "DEQ QID %d\n", qinfo.queue_id); + xgene_qmtm_read_qstate(&qinfo); + print_hex_dump(KERN_ERR, "DEQSTATE ", DUMP_PREFIX_ADDRESS, 16, 4, + qinfo.qstate, sizeof(qinfo.qstate), 1); + + qinfo.queue_id = ACR_QID_RD(msgrd_err); + dev_err(dev, "ENQ QID %d\n", qinfo.queue_id); + xgene_qmtm_read_qstate(&qinfo); + print_hex_dump(KERN_INFO, "ENQSTATE ", DUMP_PREFIX_ADDRESS, 16, 4, + qinfo.qstate, sizeof(qinfo.qstate), 1); + + xgene_qmtm_wr32(qmtm, QM_INTERRUPT_ADDR, status); +} + +static irqreturn_t xgene_qmtm_error_intr(int irq, void *qdev) +{ + xgene_qmtm_error((struct xgene_qmtm *)qdev); + + return IRQ_HANDLED; +} + +static irqreturn_t xgene_qmtm_error_queue_intr(int irq, void *qdev) +{ + struct xgene_qmtm_msg64 msg; + struct xgene_qmtm_qinfo *qinfo = (struct xgene_qmtm_qinfo *)qdev; + struct xgene_qmtm *qmtm = qinfo->qmtm; + struct device *dev = &qmtm->pdev->dev; + u16 queue_id = qinfo->queue_id; + u8 qmtm_ip = qinfo->qmtm_ip; + int rc; + + rc = xgene_qmtm_dequeue_msg(qinfo->qdesc, &msg); + if (rc < 0) { + /* Return if invalid interrupt */ + dev_err(dev, "QMTM%d QID %d PBN %d IRQ %d spurious\n", + qmtm_ip, queue_id, qinfo->pbn, irq); + return IRQ_HANDLED; + } + + xgene_qmtm_error(qmtm); + dev_err(dev, "QMTM%d Error: QID %d\n", qmtm_ip, queue_id); + print_hex_dump(KERN_INFO, "Err q MSG: ", DUMP_PREFIX_ADDRESS, + 16, 4, &msg, msg.msg32_1.msg16.NV ? 64 : 32, 1); + xgene_qmtm_error_msg(qinfo, &msg.msg32_1); + + return IRQ_HANDLED; +} + +int xgene_qmtm_enable_error(struct xgene_qmtm *qmtm) +{ + struct device *dev = &qmtm->pdev->dev; + struct xgene_qmtm_qinfo qinfo; + int rc = 0; + u32 val; + u16 irq = platform_get_irq(qmtm->pdev, 0); + u8 qmtm_ip = qmtm->qmtm_ip; + + if (irq) { + u32 mask; + + memset(qmtm->error_irq_s, 0, sizeof(qmtm->error_irq_s)); + snprintf(qmtm->error_irq_s, sizeof(qmtm->error_irq_s), + "%s_Err", qmtm->idev->name); + + rc = devm_request_irq(dev, irq, xgene_qmtm_error_intr, 0, + qmtm->error_irq_s, qmtm); + if (rc < 0) { + dev_err(dev, "request_irq %d failed for %s (%d)\n", + irq, qmtm->error_irq_s, rc); + return rc; + } + + qmtm->error_irq = irq; + + /* Enable QM hardware interrupts */ + mask = ~(u32) (PBM_DEC_ERRORMASK_MASK + | ACR_FIFO_CRITICALMASK_MASK + | QUEUE_NOT_EMPTYMASK_MASK + | DEQ_AXI_ERRORMASK_MASK + | QPCORE_ACR_ERRORMASK_MASK); + xgene_qmtm_wr32(qmtm, QM_INTERRUPTMASK_ADDR, mask); + } + + if (qmtm_ip == QMTM3) + return rc; + + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.sdev = qmtm->idev; + qinfo.qaccess = QACCESS_ALT; + qinfo.qtype = QTYPE_PQ; + qinfo.qsize = QSIZE_2KB; + qinfo.flags = XGENE_SLAVE_DEFAULT_FLAGS; + + /* create error queue */ + rc = xgene_qmtm_set_qinfo(&qinfo); + if (rc < 0) { + dev_err(dev, "QMTM %d unable to configure error queue\n", + qmtm_ip); + return rc; + } + + qmtm->error_qinfo = qmtm->qinfo[qinfo.queue_id]; + memset(qmtm->error_queue_irq_s, 0, sizeof(qmtm->error_queue_irq_s)); + snprintf(qmtm->error_queue_irq_s, sizeof(qmtm->error_queue_irq_s), + "%s_ErQ", qmtm->idev->name); + + rc = devm_request_irq(dev, qinfo.qdesc->irq, + xgene_qmtm_error_queue_intr, + 0, qmtm->error_queue_irq_s, qmtm->error_qinfo); + if (rc < 0) { + dev_err(dev, "request_irq %d failed for %s (%d)\n", + qinfo.qdesc->irq, qmtm->error_queue_irq_s, rc); + xgene_qmtm_clr_qinfo(&qinfo); + qmtm->error_qinfo = NULL; + return rc; + } + + val = 0; + val = UNEXPECTED_EN_SET(val, 1); + val = UNEXPECTED_QID_SET(val, qinfo.queue_id); + val = EXPECTED_EN_SET(val, 1); + val = EXPECTED_QID_SET(val, qinfo.queue_id); + xgene_qmtm_wr32(qmtm, CSR_ERRQ_ADDR, val); + + return rc; +} + +void xgene_qmtm_disable_error(struct xgene_qmtm *qmtm) +{ + struct xgene_qmtm_qinfo *error_qinfo = qmtm->error_qinfo; + struct device *dev = &qmtm->pdev->dev; + + /* Free QMTM Error IRQ */ + if (qmtm->error_irq) { + u32 mask; + + /* Disable QM hardware interrupts */ + mask = PBM_DEC_ERRORMASK_MASK + | ACR_FIFO_CRITICALMASK_MASK + | QUEUE_NOT_EMPTYMASK_MASK + | DEQ_AXI_ERRORMASK_MASK | QPCORE_ACR_ERRORMASK_MASK; + xgene_qmtm_wr32(qmtm, QM_INTERRUPTMASK_ADDR, mask); + devm_free_irq(dev, qmtm->error_irq, qmtm); + qmtm->error_irq = 0; + } + + if (error_qinfo) { + struct xgene_qmtm_qinfo qinfo; + + /* Free QMTM Error Queue IRQ */ + devm_free_irq(dev, error_qinfo->qdesc->irq, error_qinfo); + + /* Delete error queue */ + qinfo.sdev = error_qinfo->qmtm->idev; + qinfo.queue_id = error_qinfo->queue_id; + xgene_qmtm_clr_qinfo(&qinfo); + qmtm->error_qinfo = NULL; + + /* Unassign error queue */ + xgene_qmtm_wr32(qmtm, CSR_ERRQ_ADDR, 0); + } +} diff --git a/drivers/misc/xgene/qmtm/xgene_qmtm_main.c b/drivers/misc/xgene/qmtm/xgene_qmtm_main.c index cda63e0..833ff82 100644 --- a/drivers/misc/xgene/qmtm/xgene_qmtm_main.c +++ b/drivers/misc/xgene/qmtm/xgene_qmtm_main.c @@ -588,13 +588,17 @@ static int xgene_qmtm_enable(struct xgene_qmtm *qmtm) qmtm->write_qstate(&qinfo); } - return rc; + /* Enable error reporting */ + return xgene_qmtm_enable_error(qmtm); } static int xgene_qmtm_disable(struct xgene_qmtm *qmtm) { u32 queue_id; + /* Disable error reporting */ + xgene_qmtm_disable_error(qmtm); + for (queue_id = 0; queue_id < qmtm->max_queues; queue_id++) { if (qmtm->qinfo[queue_id]) { dev_err(&qmtm->pdev->dev, diff --git a/drivers/misc/xgene/qmtm/xgene_qmtm_main.h b/drivers/misc/xgene/qmtm/xgene_qmtm_main.h index d937462..2a60225 100644 --- a/drivers/misc/xgene/qmtm/xgene_qmtm_main.h +++ b/drivers/misc/xgene/qmtm/xgene_qmtm_main.h @@ -129,6 +129,10 @@ enum xgene_qmtm_fp_mode { void xgene_qmtm_wr32(struct xgene_qmtm *qmtm, u32 offset, u32 data); void xgene_qmtm_rd32(struct xgene_qmtm *qmtm, u32 offset, u32 *data); +/* QMTM Error handling */ +int xgene_qmtm_enable_error(struct xgene_qmtm *qmtm); +void xgene_qmtm_disable_error(struct xgene_qmtm *qmtm); + struct xgene_qmtm_sdev *storm_qmtm_get_sdev(char *name); #endif /* __XGENE_QMTM_MAIN_H__ */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html