[PATCH] ARM: Support for IXP4xx hardware Queue Manager.

Krzysztof Halasa khc at pm.waw.pl
Sun Apr 7 15:55:18 EDT 2013


Signed-off-by: Krzysztof Hałasa <khc at pm.waw.pl>

diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig
index e69de29..9244be9 100644
--- a/arch/arm/mach-ixp4xx/Kconfig
+++ b/arch/arm/mach-ixp4xx/Kconfig
@@ -0,0 +1,9 @@
+if ARCH_IXP4XX
+
+config IXP4XX_QMGR
+	tristate "IXP4xx Queue Manager support"
+	help
+	  This driver supports IXP4xx built-in hardware queue manager
+	  and is required by the Ethernet driver.
+
+endif
diff --git a/arch/arm/mach-ixp4xx/Makefile b/arch/arm/mach-ixp4xx/Makefile
index d8a3d7f..09a0d63 100644
--- a/arch/arm/mach-ixp4xx/Makefile
+++ b/arch/arm/mach-ixp4xx/Makefile
@@ -1 +1,2 @@
 obj-y += generic.o
+obj-$(CONFIG_IXP4XX_QMGR) += qmgr.o
diff --git a/arch/arm/mach-ixp4xx/include/mach/qmgr.h b/arch/arm/mach-ixp4xx/include/mach/qmgr.h
new file mode 100644
index 0000000..4e9b8d4
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/qmgr.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 Krzysztof Halasa <khc at pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef IXP4XX_QMGR_H
+#define IXP4XX_QMGR_H
+
+#include <common.h>
+#include <mach/ixp4xx-regs.h>
+#include <asm/io.h>
+
+#define DEBUG_QMGR       0
+
+#define HALF_QUEUES     32
+#define QUEUES          64
+#define MAX_QUEUE_LENGTH 4 /* in dwords */
+
+#define QUEUE_STAT1_EMPTY               1 /* queue status bits */
+#define QUEUE_STAT1_NEARLY_EMPTY        2
+#define QUEUE_STAT1_NEARLY_FULL         4
+#define QUEUE_STAT1_FULL                8
+#define QUEUE_STAT2_UNDERFLOW           1
+#define QUEUE_STAT2_OVERFLOW            2
+
+#define QUEUE_WATERMARK_0_ENTRIES       0
+#define QUEUE_WATERMARK_1_ENTRY         1
+#define QUEUE_WATERMARK_2_ENTRIES       2
+#define QUEUE_WATERMARK_4_ENTRIES       3
+#define QUEUE_WATERMARK_8_ENTRIES       4
+#define QUEUE_WATERMARK_16_ENTRIES      5
+#define QUEUE_WATERMARK_32_ENTRIES      6
+#define QUEUE_WATERMARK_64_ENTRIES      7
+
+/* queue interrupt request conditions */
+#define QUEUE_IRQ_SRC_EMPTY             0
+#define QUEUE_IRQ_SRC_NEARLY_EMPTY      1
+#define QUEUE_IRQ_SRC_NEARLY_FULL       2
+#define QUEUE_IRQ_SRC_FULL              3
+#define QUEUE_IRQ_SRC_NOT_EMPTY         4
+#define QUEUE_IRQ_SRC_NOT_NEARLY_EMPTY  5
+#define QUEUE_IRQ_SRC_NOT_NEARLY_FULL   6
+#define QUEUE_IRQ_SRC_NOT_FULL          7
+
+struct qmgr_regs {
+	u32 acc[QUEUES][MAX_QUEUE_LENGTH]; /* 0x000 - 0x3FF */
+	u32 stat1[4];         /* 0x400 - 0x40F */
+	u32 stat2[2];         /* 0x410 - 0x417 */
+	u32 statne_h;         /* 0x418 - queue nearly empty */
+	u32 statf_h;          /* 0x41C - queue full */
+	u32 irqsrc[4];        /* 0x420 - 0x42F IRC source */
+	u32 irqen[2];         /* 0x430 - 0x437 IRQ enabled */
+	u32 irqstat[2];       /* 0x438 - 0x43F - IRQ access only */
+	u32 reserved[1776];
+	u32 sram[2048];       /* 0x2000 - 0x3FFF - config and buffer */
+};
+
+static const struct qmgr_regs *qmgr_regs = (struct qmgr_regs *)IXP4XX_QMGR_BASE;
+
+void qmgr_set_irq(unsigned int queue, int src,
+		  void (*handler)(void *pdev), void *pdev);
+void qmgr_enable_irq(unsigned int queue);
+void qmgr_disable_irq(unsigned int queue);
+
+/* request_ and release_queue() must be called from non-IRQ context */
+
+#if DEBUG_QMGR
+extern char qmgr_queue_descs[HALF_QUEUES][32];
+
+void qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+			unsigned int nearly_empty_watermark,
+			unsigned int nearly_full_watermark,
+			const char *desc_format, const char* name);
+#else
+void __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+			  unsigned int nearly_empty_watermark,
+			  unsigned int nearly_full_watermark);
+#define qmgr_request_queue(queue, len, nearly_empty_watermark,       \
+			   nearly_full_watermark, desc_format, name) \
+	__qmgr_request_queue(queue, len, nearly_empty_watermark,     \
+			     nearly_full_watermark)
+#endif
+
+void qmgr_release_queue(unsigned int queue);
+
+
+static inline void qmgr_put_entry(unsigned int queue, u32 val)
+{
+#if DEBUG_QMGR
+	BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */
+
+	fprintf(stderr, "Queue %s(%i) put %X\n",
+		qmgr_queue_descs[queue], queue, val);
+#endif
+	__raw_writel(val, &qmgr_regs->acc[queue][0]);
+}
+
+static inline u32 qmgr_get_entry(unsigned int queue)
+{
+	u32 val;
+	val = __raw_readl(&qmgr_regs->acc[queue][0]);
+#if DEBUG_QMGR
+	BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */
+
+	fprintf(stderr, "Queue %s(%i) get %X\n",
+		qmgr_queue_descs[queue], queue, val);
+#endif
+	return val;
+}
+
+static inline int __qmgr_get_stat1(unsigned int queue)
+{
+	return (__raw_readl(&qmgr_regs->stat1[queue >> 3])
+		>> ((queue & 7) << 2)) & 0xF;
+}
+
+/**
+ * qmgr_stat_empty() - checks if a hardware queue is empty
+ * @queue: queue number
+ *
+ * Returns non-zero value if the queue is empty.
+ */
+static inline int qmgr_stat_empty(unsigned int queue)
+{
+	return __qmgr_get_stat1(queue) & QUEUE_STAT1_EMPTY;
+}
+
+/**
+ * qmgr_stat_below_low_watermark() - checks if a queue is below low watermark
+ * @queue: queue number
+ *
+ * Returns non-zero value if the queue is below low watermark.
+ */
+static inline int qmgr_stat_below_low_watermark(unsigned int queue)
+{
+	return __qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_EMPTY;
+}
+
+/**
+ * qmgr_stat_above_high_watermark() - checks if a queue is above high watermark
+ * @queue: queue number
+ *
+ * Returns non-zero value if the queue is above high watermark
+ */
+static inline int qmgr_stat_above_high_watermark(unsigned int queue)
+{
+	return __qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_FULL;
+}
+
+/**
+ * qmgr_stat_full() - checks if a hardware queue is full
+ * @queue: queue number
+ *
+ * Returns non-zero value if the queue is full.
+ */
+static inline int qmgr_stat_full(unsigned int queue)
+{
+	return __qmgr_get_stat1(queue) & QUEUE_STAT1_FULL;
+}
+
+#endif
diff --git a/arch/arm/mach-ixp4xx/qmgr.c b/arch/arm/mach-ixp4xx/qmgr.c
new file mode 100644
index 0000000..81b6522
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/qmgr.c
@@ -0,0 +1,259 @@
+/*
+ * Intel IXP4xx Queue Manager driver for Linux
+ *
+ * Copyright (C) 2007 Krzysztof Halasa <khc at pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <init.h>
+#include <errno.h>
+#include <mach/qmgr.h>
+
+static u32 used_sram_bitmap[4]; /* 128 16-dword pages */
+
+#if DEBUG_QMGR
+char qmgr_queue_descs[HALF_QUEUES][32];
+#endif
+
+#ifdef CONFIG_USE_IRQ
+
+static void (*irq_handlers[HALF_QUEUES])(void *pdev);
+static void *irq_pdevs[HALF_QUEUES];
+
+void qmgr_set_irq(unsigned int queue, int src,
+		  void (*handler)(void *pdev), void *pdev)
+{
+	const u32 *reg;
+	int bit;
+	BUG_ON(src > QUEUE_IRQ_SRC_NOT_FULL);
+	reg = &qmgr_regs->irqsrc[queue >> 3]; /* 8 queues per u32 */
+	bit = (queue % 8) * 4; /* 3 bits + 1 reserved bit per queue */
+	__raw_writel((__raw_readl(reg) & ~(7 << bit)) | (src << bit), reg);
+
+	irq_handlers[queue] = handler;
+	irq_pdevs[queue] = pdev;
+}
+
+
+static void qmgr_irq1_a0(void *data)
+{
+	int i;
+	u32 en_bitmap, src, stat;
+
+	/* ACK - it may clear any bits so don't rely on it */
+	__raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[0]);
+
+	en_bitmap = qmgr_regs->irqen[0];
+	while (en_bitmap) {
+		i = fls(en_bitmap) - 1; /* number of the last "low" queue */
+		en_bitmap &= ~BIT(i);
+		src = qmgr_regs->irqsrc[i >> 3];
+		stat = qmgr_regs->stat1[i >> 3];
+		if (src & 4) /* the IRQ condition is inverted */
+			stat = ~stat;
+		if (stat & BIT(src & 3))
+			irq_handlers[i](irq_pdevs[i]);
+	}
+}
+
+
+static void qmgr_irq1(void *data)
+{
+	int i;
+	u32 req_bitmap = __raw_readl(&qmgr_regs->irqstat[0]);
+
+	if (!req_bitmap)
+		return;
+	__raw_writel(req_bitmap, &qmgr_regs->irqstat[0]); /* ACK */
+
+	while (req_bitmap) {
+		i = fls(req_bitmap) - 1; /* number of the last queue */
+		req_bitmap &= ~BIT(i);
+		irq_handlers[i](irq_pdevs[i]);
+	}
+}
+
+
+void qmgr_enable_irq(unsigned int queue)
+{
+	u32 mask = 1 << queue;
+
+	__raw_writel(__raw_readl(&qmgr_regs->irqen[0]) | mask,
+		     &qmgr_regs->irqen[0]);
+}
+
+void qmgr_disable_irq(unsigned int queue)
+{
+	u32 mask = 1 << queue;
+
+	__raw_writel(__raw_readl(&qmgr_regs->irqen[0]) & ~mask,
+		     &qmgr_regs->irqen[0]);
+	__raw_writel(mask, &qmgr_regs->irqstat[0]); /* clear */
+}
+
+#endif /* CONFIG_USE_IRQ */
+
+static inline void shift_mask(u32 *mask)
+{
+	mask[3] = mask[3] << 1 | mask[2] >> 31;
+	mask[2] = mask[2] << 1 | mask[1] >> 31;
+	mask[1] = mask[1] << 1 | mask[0] >> 31;
+	mask[0] <<= 1;
+}
+
+#if DEBUG_QMGR
+void qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+			unsigned int nearly_empty_watermark,
+			unsigned int nearly_full_watermark,
+			const char *desc_format, const char* name)
+#else
+void __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+			  unsigned int nearly_empty_watermark,
+			  unsigned int nearly_full_watermark)
+#endif
+{
+	u32 cfg, addr = 0, mask[4]; /* in 16-dwords */
+
+	BUG_ON(queue >= HALF_QUEUES);
+	BUG_ON((nearly_empty_watermark | nearly_full_watermark) & ~7);
+
+	switch (len) {
+	case  16:
+		cfg = 0 << 24;
+		mask[0] = 0x1;
+		break;
+	case  32:
+		cfg = 1 << 24;
+		mask[0] = 0x3;
+		break;
+	case  64:
+		cfg = 2 << 24;
+		mask[0] = 0xF;
+		break;
+	case 128:
+		cfg = 3 << 24;
+		mask[0] = 0xFF;
+		break;
+	default:
+		BUG();
+	}
+
+	cfg |= nearly_empty_watermark << 26;
+	cfg |= nearly_full_watermark << 29;
+	len /= 16; /* in 16-dwords: 1, 2, 4 or 8 */
+	mask[1] = mask[2] = mask[3] = 0;
+
+	BUG_ON(__raw_readl(&qmgr_regs->sram[queue]));
+
+	while (1) {
+		if (!(used_sram_bitmap[0] & mask[0]) &&
+		    !(used_sram_bitmap[1] & mask[1]) &&
+		    !(used_sram_bitmap[2] & mask[2]) &&
+		    !(used_sram_bitmap[3] & mask[3]))
+			break; /* found free space */
+
+		addr++;
+		shift_mask(mask);
+		if (addr + len > ARRAY_SIZE(qmgr_regs->sram)) {
+			fprintf(stderr, "qmgr: no free SRAM space for"
+				" queue %i\n", queue);
+			BUG();
+		}
+	}
+
+	used_sram_bitmap[0] |= mask[0];
+	used_sram_bitmap[1] |= mask[1];
+	used_sram_bitmap[2] |= mask[2];
+	used_sram_bitmap[3] |= mask[3];
+	__raw_writel(cfg | (addr << 14), &qmgr_regs->sram[queue]);
+#if DEBUG_QMGR
+	/* no snprintf() */
+	sprintf(qmgr_queue_descs[queue], desc_format, name);
+	fprintf(stderr, "qmgr: requested queue %s(%i) addr = 0x%02X\n",
+		qmgr_queue_descs[queue], queue, addr);
+#endif
+}
+
+void qmgr_release_queue(unsigned int queue)
+{
+	u32 cfg, addr, mask[4];
+
+	BUG_ON(queue >= HALF_QUEUES); /* not in valid range */
+
+	cfg = __raw_readl(&qmgr_regs->sram[queue]);
+	addr = (cfg >> 14) & 0xFF;
+
+	BUG_ON(!addr); /* not requested */
+
+	switch ((cfg >> 24) & 3) {
+	case 0: mask[0] = 0x1; break;
+	case 1: mask[0] = 0x3; break;
+	case 2: mask[0] = 0xF; break;
+	case 3: mask[0] = 0xFF; break;
+	}
+
+	mask[1] = mask[2] = mask[3] = 0;
+
+	while (addr--)
+		shift_mask(mask);
+
+#if DEBUG_QMGR
+	fprintf(stderr, "qmgr: releasing queue %s(%i)\n",
+		qmgr_queue_descs[queue], queue);
+	qmgr_queue_descs[queue][0] = '\x0';
+#endif
+	__raw_writel(0, &qmgr_regs->sram[queue]);
+
+	used_sram_bitmap[0] &= ~mask[0];
+	used_sram_bitmap[1] &= ~mask[1];
+	used_sram_bitmap[2] &= ~mask[2];
+	used_sram_bitmap[3] &= ~mask[3];
+#ifdef CONFIG_USE_IRQ
+	irq_handlers[queue] = NULL; /* catch IRQ bugs */
+#endif
+
+	while ((addr = qmgr_get_entry(queue)))
+		fprintf(stderr, "qmgr: released queue %i not empty: 0x%08X\n",
+			queue, addr);
+}
+
+static int __init qmgr_init(void)
+{
+	int i;
+#ifdef CONFIG_USE_IRQ
+	interrupt_handler_t *handler;
+#endif
+
+	/* reset qmgr registers */
+	for (i = 0; i < 4; i++) {
+		__raw_writel(0x33333333, &qmgr_regs->stat1[i]);
+		__raw_writel(0, &qmgr_regs->irqsrc[i]);
+	}
+	for (i = 0; i < 2; i++) {
+		__raw_writel(0, &qmgr_regs->stat2[i]);
+		__raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[i]); /* clear */
+		__raw_writel(0, &qmgr_regs->irqen[i]);
+	}
+
+	__raw_writel(0xFFFFFFFF, &qmgr_regs->statne_h);
+	__raw_writel(0, &qmgr_regs->statf_h);
+
+	for (i = 0; i < QUEUES; i++)
+		__raw_writel(0, &qmgr_regs->sram[i]);
+
+#ifdef CONFIG_USE_IRQ
+	if (cpu_is_ixp42x_rev_a0())
+		handler = qmgr_irq1_a0;
+	else
+		handler = qmgr_irq1;
+
+	irq_install_handler(IXP425_QM1_IRQ, handler, NULL);
+#endif
+	used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */
+	return 0;
+}
+
+coredevice_initcall(qmgr_init);



More information about the barebox mailing list