[PATCH V2 4/4] misc: xgene: Add error handling for APM X-Gene SoC Queue Manager/Traffic Manager

From: Ravi Patel
Date: Fri Dec 20 2013 - 21:58:08 EST


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 linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/