]> err.no Git - linux-2.6/commitdiff
[ARM] 4713/3: Adds drivers for IXP4xx QMgr and NPE features
authorKrzysztof Halasa <khc@pm.waw.pl>
Tue, 1 Jan 2008 20:55:23 +0000 (21:55 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 4 Feb 2008 13:15:25 +0000 (13:15 +0000)
This patch adds drivers for IXP4xx hardware Queue Manager and for
Network Processor Engines. Requires patch #4712 (reading/writing
CPU feature (aka fuse) bits).

Posted to linux-arm-kernel on 2 Dec 2007 and revised.

Signed-off-by: Krzysztof Halasa <khc@pm.waw.pl>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mach-ixp4xx/Kconfig
arch/arm/mach-ixp4xx/Makefile
arch/arm/mach-ixp4xx/ixp4xx_npe.c [new file with mode: 0644]
arch/arm/mach-ixp4xx/ixp4xx_qmgr.c [new file with mode: 0644]
include/asm-arm/arch-ixp4xx/npe.h [new file with mode: 0644]
include/asm-arm/arch-ixp4xx/qmgr.h [new file with mode: 0644]

index 61b2dfcb89d623dd401cd8226ec2d66bbf939690..e774447c059285e27edb9c2612f7c116218ad486 100644 (file)
@@ -189,6 +189,20 @@ config IXP4XX_INDIRECT_PCI
          need to use the indirect method instead. If you don't know
          what you need, leave this option unselected.
 
+config IXP4XX_QMGR
+       tristate "IXP4xx Queue Manager support"
+       help
+         This driver supports IXP4xx built-in hardware queue manager
+         and is automatically selected by Ethernet and HSS drivers.
+
+config IXP4XX_NPE
+       tristate "IXP4xx Network Processor Engine support"
+       select HOTPLUG
+       select FW_LOADER
+       help
+         This driver supports IXP4xx built-in network coprocessors
+         and is automatically selected by Ethernet and HSS drivers.
+
 endmenu
 
 endif
index 77e00ade558589297aaa425558958a66a61fbffb..4bb97e13f95798baa0bbee98a72b20b5bfd3f744 100644 (file)
@@ -30,3 +30,5 @@ obj-$(CONFIG_MACH_GATEWAY7001)        += gateway7001-setup.o
 obj-$(CONFIG_MACH_WG302V2)     += wg302v2-setup.o
 
 obj-$(CONFIG_PCI)              += $(obj-pci-$(CONFIG_PCI)) common-pci.o
+obj-$(CONFIG_IXP4XX_QMGR)      += ixp4xx_qmgr.o
+obj-$(CONFIG_IXP4XX_NPE)       += ixp4xx_npe.o
diff --git a/arch/arm/mach-ixp4xx/ixp4xx_npe.c b/arch/arm/mach-ixp4xx/ixp4xx_npe.c
new file mode 100644 (file)
index 0000000..83c137e
--- /dev/null
@@ -0,0 +1,741 @@
+/*
+ * Intel IXP4xx Network Processor Engine driver for Linux
+ *
+ * Copyright (C) 2007 Krzysztof Halasa <khc@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.
+ *
+ * The code is based on publicly available information:
+ * - Intel IXP4xx Developer's Manual and other e-papers
+ * - Intel IXP400 Access Library Software (BSD license)
+ * - previous works by Christian Hohnstaedt <chohnstaedt@innominate.com>
+ *   Thanks, Christian.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/arch/npe.h>
+
+#define DEBUG_MSG                      0
+#define DEBUG_FW                       0
+
+#define NPE_COUNT                      3
+#define MAX_RETRIES                    1000    /* microseconds */
+#define NPE_42X_DATA_SIZE              0x800   /* in dwords */
+#define NPE_46X_DATA_SIZE              0x1000
+#define NPE_A_42X_INSTR_SIZE           0x1000
+#define NPE_B_AND_C_42X_INSTR_SIZE     0x800
+#define NPE_46X_INSTR_SIZE             0x1000
+#define REGS_SIZE                      0x1000
+
+#define NPE_PHYS_REG                   32
+
+#define FW_MAGIC                       0xFEEDF00D
+#define FW_BLOCK_TYPE_INSTR            0x0
+#define FW_BLOCK_TYPE_DATA             0x1
+#define FW_BLOCK_TYPE_EOF              0xF
+
+/* NPE exec status (read) and command (write) */
+#define CMD_NPE_STEP                   0x01
+#define CMD_NPE_START                  0x02
+#define CMD_NPE_STOP                   0x03
+#define CMD_NPE_CLR_PIPE               0x04
+#define CMD_CLR_PROFILE_CNT            0x0C
+#define CMD_RD_INS_MEM                 0x10 /* instruction memory */
+#define CMD_WR_INS_MEM                 0x11
+#define CMD_RD_DATA_MEM                        0x12 /* data memory */
+#define CMD_WR_DATA_MEM                        0x13
+#define CMD_RD_ECS_REG                 0x14 /* exec access register */
+#define CMD_WR_ECS_REG                 0x15
+
+#define STAT_RUN                       0x80000000
+#define STAT_STOP                      0x40000000
+#define STAT_CLEAR                     0x20000000
+#define STAT_ECS_K                     0x00800000 /* pipeline clean */
+
+#define NPE_STEVT                      0x1B
+#define NPE_STARTPC                    0x1C
+#define NPE_REGMAP                     0x1E
+#define NPE_CINDEX                     0x1F
+
+#define INSTR_WR_REG_SHORT             0x0000C000
+#define INSTR_WR_REG_BYTE              0x00004000
+#define INSTR_RD_FIFO                  0x0F888220
+#define INSTR_RESET_MBOX               0x0FAC8210
+
+#define ECS_BG_CTXT_REG_0              0x00 /* Background Executing Context */
+#define ECS_BG_CTXT_REG_1              0x01 /*         Stack level */
+#define ECS_BG_CTXT_REG_2              0x02
+#define ECS_PRI_1_CTXT_REG_0           0x04 /* Priority 1 Executing Context */
+#define ECS_PRI_1_CTXT_REG_1           0x05 /*         Stack level */
+#define ECS_PRI_1_CTXT_REG_2           0x06
+#define ECS_PRI_2_CTXT_REG_0           0x08 /* Priority 2 Executing Context */
+#define ECS_PRI_2_CTXT_REG_1           0x09 /*         Stack level */
+#define ECS_PRI_2_CTXT_REG_2           0x0A
+#define ECS_DBG_CTXT_REG_0             0x0C /* Debug Executing Context */
+#define ECS_DBG_CTXT_REG_1             0x0D /*         Stack level */
+#define ECS_DBG_CTXT_REG_2             0x0E
+#define ECS_INSTRUCT_REG               0x11 /* NPE Instruction Register */
+
+#define ECS_REG_0_ACTIVE               0x80000000 /* all levels */
+#define ECS_REG_0_NEXTPC_MASK          0x1FFF0000 /* BG/PRI1/PRI2 levels */
+#define ECS_REG_0_LDUR_BITS            8
+#define ECS_REG_0_LDUR_MASK            0x00000700 /* all levels */
+#define ECS_REG_1_CCTXT_BITS           16
+#define ECS_REG_1_CCTXT_MASK           0x000F0000 /* all levels */
+#define ECS_REG_1_SELCTXT_BITS         0
+#define ECS_REG_1_SELCTXT_MASK         0x0000000F /* all levels */
+#define ECS_DBG_REG_2_IF               0x00100000 /* debug level */
+#define ECS_DBG_REG_2_IE               0x00080000 /* debug level */
+
+/* NPE watchpoint_fifo register bit */
+#define WFIFO_VALID                    0x80000000
+
+/* NPE messaging_status register bit definitions */
+#define MSGSTAT_OFNE   0x00010000 /* OutFifoNotEmpty */
+#define MSGSTAT_IFNF   0x00020000 /* InFifoNotFull */
+#define MSGSTAT_OFNF   0x00040000 /* OutFifoNotFull */
+#define MSGSTAT_IFNE   0x00080000 /* InFifoNotEmpty */
+#define MSGSTAT_MBINT  0x00100000 /* Mailbox interrupt */
+#define MSGSTAT_IFINT  0x00200000 /* InFifo interrupt */
+#define MSGSTAT_OFINT  0x00400000 /* OutFifo interrupt */
+#define MSGSTAT_WFINT  0x00800000 /* WatchFifo interrupt */
+
+/* NPE messaging_control register bit definitions */
+#define MSGCTL_OUT_FIFO                        0x00010000 /* enable output FIFO */
+#define MSGCTL_IN_FIFO                 0x00020000 /* enable input FIFO */
+#define MSGCTL_OUT_FIFO_WRITE          0x01000000 /* enable FIFO + WRITE */
+#define MSGCTL_IN_FIFO_WRITE           0x02000000
+
+/* NPE mailbox_status value for reset */
+#define RESET_MBOX_STAT                        0x0000F0F0
+
+const char *npe_names[] = { "NPE-A", "NPE-B", "NPE-C" };
+
+#define print_npe(pri, npe, fmt, ...)                                  \
+       printk(pri "%s: " fmt, npe_name(npe), ## __VA_ARGS__)
+
+#if DEBUG_MSG
+#define debug_msg(npe, fmt, ...)                                       \
+       print_npe(KERN_DEBUG, npe, fmt, ## __VA_ARGS__)
+#else
+#define debug_msg(npe, fmt, ...)
+#endif
+
+static struct {
+       u32 reg, val;
+} ecs_reset[] = {
+       { ECS_BG_CTXT_REG_0,    0xA0000000 },
+       { ECS_BG_CTXT_REG_1,    0x01000000 },
+       { ECS_BG_CTXT_REG_2,    0x00008000 },
+       { ECS_PRI_1_CTXT_REG_0, 0x20000080 },
+       { ECS_PRI_1_CTXT_REG_1, 0x01000000 },
+       { ECS_PRI_1_CTXT_REG_2, 0x00008000 },
+       { ECS_PRI_2_CTXT_REG_0, 0x20000080 },
+       { ECS_PRI_2_CTXT_REG_1, 0x01000000 },
+       { ECS_PRI_2_CTXT_REG_2, 0x00008000 },
+       { ECS_DBG_CTXT_REG_0,   0x20000000 },
+       { ECS_DBG_CTXT_REG_1,   0x00000000 },
+       { ECS_DBG_CTXT_REG_2,   0x001E0000 },
+       { ECS_INSTRUCT_REG,     0x1003C00F },
+};
+
+static struct npe npe_tab[NPE_COUNT] = {
+       {
+               .id     = 0,
+               .regs   = (struct npe_regs __iomem *)IXP4XX_NPEA_BASE_VIRT,
+               .regs_phys = IXP4XX_NPEA_BASE_PHYS,
+       }, {
+               .id     = 1,
+               .regs   = (struct npe_regs __iomem *)IXP4XX_NPEB_BASE_VIRT,
+               .regs_phys = IXP4XX_NPEB_BASE_PHYS,
+       }, {
+               .id     = 2,
+               .regs   = (struct npe_regs __iomem *)IXP4XX_NPEC_BASE_VIRT,
+               .regs_phys = IXP4XX_NPEC_BASE_PHYS,
+       }
+};
+
+int npe_running(struct npe *npe)
+{
+       return (__raw_readl(&npe->regs->exec_status_cmd) & STAT_RUN) != 0;
+}
+
+static void npe_cmd_write(struct npe *npe, u32 addr, int cmd, u32 data)
+{
+       __raw_writel(data, &npe->regs->exec_data);
+       __raw_writel(addr, &npe->regs->exec_addr);
+       __raw_writel(cmd, &npe->regs->exec_status_cmd);
+}
+
+static u32 npe_cmd_read(struct npe *npe, u32 addr, int cmd)
+{
+       __raw_writel(addr, &npe->regs->exec_addr);
+       __raw_writel(cmd, &npe->regs->exec_status_cmd);
+       /* Iintroduce extra read cycles after issuing read command to NPE
+          so that we read the register after the NPE has updated it.
+          This is to overcome race condition between XScale and NPE */
+       __raw_readl(&npe->regs->exec_data);
+       __raw_readl(&npe->regs->exec_data);
+       return __raw_readl(&npe->regs->exec_data);
+}
+
+static void npe_clear_active(struct npe *npe, u32 reg)
+{
+       u32 val = npe_cmd_read(npe, reg, CMD_RD_ECS_REG);
+       npe_cmd_write(npe, reg, CMD_WR_ECS_REG, val & ~ECS_REG_0_ACTIVE);
+}
+
+static void npe_start(struct npe *npe)
+{
+       /* ensure only Background Context Stack Level is active */
+       npe_clear_active(npe, ECS_PRI_1_CTXT_REG_0);
+       npe_clear_active(npe, ECS_PRI_2_CTXT_REG_0);
+       npe_clear_active(npe, ECS_DBG_CTXT_REG_0);
+
+       __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd);
+       __raw_writel(CMD_NPE_START, &npe->regs->exec_status_cmd);
+}
+
+static void npe_stop(struct npe *npe)
+{
+       __raw_writel(CMD_NPE_STOP, &npe->regs->exec_status_cmd);
+       __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd); /*FIXME?*/
+}
+
+static int __must_check npe_debug_instr(struct npe *npe, u32 instr, u32 ctx,
+                                       u32 ldur)
+{
+       u32 wc;
+       int i;
+
+       /* set the Active bit, and the LDUR, in the debug level */
+       npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG,
+                     ECS_REG_0_ACTIVE | (ldur << ECS_REG_0_LDUR_BITS));
+
+       /* set CCTXT at ECS DEBUG L3 to specify in which context to execute
+          the instruction, and set SELCTXT at ECS DEBUG Level to specify
+          which context store to access.
+          Debug ECS Level Reg 1 has form 0x000n000n, where n = context number
+       */
+       npe_cmd_write(npe, ECS_DBG_CTXT_REG_1, CMD_WR_ECS_REG,
+                     (ctx << ECS_REG_1_CCTXT_BITS) |
+                     (ctx << ECS_REG_1_SELCTXT_BITS));
+
+       /* clear the pipeline */
+       __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd);
+
+       /* load NPE instruction into the instruction register */
+       npe_cmd_write(npe, ECS_INSTRUCT_REG, CMD_WR_ECS_REG, instr);
+
+       /* we need this value later to wait for completion of NPE execution
+          step */
+       wc = __raw_readl(&npe->regs->watch_count);
+
+       /* issue a Step One command via the Execution Control register */
+       __raw_writel(CMD_NPE_STEP, &npe->regs->exec_status_cmd);
+
+       /* Watch Count register increments when NPE completes an instruction */
+       for (i = 0; i < MAX_RETRIES; i++) {
+               if (wc != __raw_readl(&npe->regs->watch_count))
+                       return 0;
+               udelay(1);
+       }
+
+       print_npe(KERN_ERR, npe, "reset: npe_debug_instr(): timeout\n");
+       return -ETIMEDOUT;
+}
+
+static int __must_check npe_logical_reg_write8(struct npe *npe, u32 addr,
+                                              u8 val, u32 ctx)
+{
+       /* here we build the NPE assembler instruction: mov8 d0, #0 */
+       u32 instr = INSTR_WR_REG_BYTE | /* OpCode */
+               addr << 9 |             /* base Operand */
+               (val & 0x1F) << 4 |     /* lower 5 bits to immediate data */
+               (val & ~0x1F) << (18 - 5);/* higher 3 bits to CoProc instr. */
+       return npe_debug_instr(npe, instr, ctx, 1); /* execute it */
+}
+
+static int __must_check npe_logical_reg_write16(struct npe *npe, u32 addr,
+                                               u16 val, u32 ctx)
+{
+       /* here we build the NPE assembler instruction: mov16 d0, #0 */
+       u32 instr = INSTR_WR_REG_SHORT | /* OpCode */
+               addr << 9 |             /* base Operand */
+               (val & 0x1F) << 4 |     /* lower 5 bits to immediate data */
+               (val & ~0x1F) << (18 - 5);/* higher 11 bits to CoProc instr. */
+       return npe_debug_instr(npe, instr, ctx, 1); /* execute it */
+}
+
+static int __must_check npe_logical_reg_write32(struct npe *npe, u32 addr,
+                                               u32 val, u32 ctx)
+{
+       /* write in 16 bit steps first the high and then the low value */
+       if (npe_logical_reg_write16(npe, addr, val >> 16, ctx))
+               return -ETIMEDOUT;
+       return npe_logical_reg_write16(npe, addr + 2, val & 0xFFFF, ctx);
+}
+
+static int npe_reset(struct npe *npe)
+{
+       u32 val, ctl, exec_count, ctx_reg2;
+       int i;
+
+       ctl = (__raw_readl(&npe->regs->messaging_control) | 0x3F000000) &
+               0x3F3FFFFF;
+
+       /* disable parity interrupt */
+       __raw_writel(ctl & 0x3F00FFFF, &npe->regs->messaging_control);
+
+       /* pre exec - debug instruction */
+       /* turn off the halt bit by clearing Execution Count register. */
+       exec_count = __raw_readl(&npe->regs->exec_count);
+       __raw_writel(0, &npe->regs->exec_count);
+       /* ensure that IF and IE are on (temporarily), so that we don't end up
+          stepping forever */
+       ctx_reg2 = npe_cmd_read(npe, ECS_DBG_CTXT_REG_2, CMD_RD_ECS_REG);
+       npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2 |
+                     ECS_DBG_REG_2_IF | ECS_DBG_REG_2_IE);
+
+       /* clear the FIFOs */
+       while (__raw_readl(&npe->regs->watchpoint_fifo) & WFIFO_VALID)
+               ;
+       while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_OFNE)
+               /* read from the outFIFO until empty */
+               print_npe(KERN_DEBUG, npe, "npe_reset: read FIFO = 0x%X\n",
+                         __raw_readl(&npe->regs->in_out_fifo));
+
+       while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE)
+               /* step execution of the NPE intruction to read inFIFO using
+                  the Debug Executing Context stack */
+               if (npe_debug_instr(npe, INSTR_RD_FIFO, 0, 0))
+                       return -ETIMEDOUT;
+
+       /* reset the mailbox reg from the XScale side */
+       __raw_writel(RESET_MBOX_STAT, &npe->regs->mailbox_status);
+       /* from NPE side */
+       if (npe_debug_instr(npe, INSTR_RESET_MBOX, 0, 0))
+               return -ETIMEDOUT;
+
+       /* Reset the physical registers in the NPE register file */
+       for (val = 0; val < NPE_PHYS_REG; val++) {
+               if (npe_logical_reg_write16(npe, NPE_REGMAP, val >> 1, 0))
+                       return -ETIMEDOUT;
+               /* address is either 0 or 4 */
+               if (npe_logical_reg_write32(npe, (val & 1) * 4, 0, 0))
+                       return -ETIMEDOUT;
+       }
+
+       /* Reset the context store = each context's Context Store registers */
+
+       /* Context 0 has no STARTPC. Instead, this value is used to set NextPC
+          for Background ECS, to set where NPE starts executing code */
+       val = npe_cmd_read(npe, ECS_BG_CTXT_REG_0, CMD_RD_ECS_REG);
+       val &= ~ECS_REG_0_NEXTPC_MASK;
+       val |= (0 /* NextPC */ << 16) & ECS_REG_0_NEXTPC_MASK;
+       npe_cmd_write(npe, ECS_BG_CTXT_REG_0, CMD_WR_ECS_REG, val);
+
+       for (i = 0; i < 16; i++) {
+               if (i) {        /* Context 0 has no STEVT nor STARTPC */
+                       /* STEVT = off, 0x80 */
+                       if (npe_logical_reg_write8(npe, NPE_STEVT, 0x80, i))
+                               return -ETIMEDOUT;
+                       if (npe_logical_reg_write16(npe, NPE_STARTPC, 0, i))
+                               return -ETIMEDOUT;
+               }
+               /* REGMAP = d0->p0, d8->p2, d16->p4 */
+               if (npe_logical_reg_write16(npe, NPE_REGMAP, 0x820, i))
+                       return -ETIMEDOUT;
+               if (npe_logical_reg_write8(npe, NPE_CINDEX, 0, i))
+                       return -ETIMEDOUT;
+       }
+
+       /* post exec */
+       /* clear active bit in debug level */
+       npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG, 0);
+       /* clear the pipeline */
+       __raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd);
+       /* restore previous values */
+       __raw_writel(exec_count, &npe->regs->exec_count);
+       npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2);
+
+       /* write reset values to Execution Context Stack registers */
+       for (val = 0; val < ARRAY_SIZE(ecs_reset); val++)
+               npe_cmd_write(npe, ecs_reset[val].reg, CMD_WR_ECS_REG,
+                             ecs_reset[val].val);
+
+       /* clear the profile counter */
+       __raw_writel(CMD_CLR_PROFILE_CNT, &npe->regs->exec_status_cmd);
+
+       __raw_writel(0, &npe->regs->exec_count);
+       __raw_writel(0, &npe->regs->action_points[0]);
+       __raw_writel(0, &npe->regs->action_points[1]);
+       __raw_writel(0, &npe->regs->action_points[2]);
+       __raw_writel(0, &npe->regs->action_points[3]);
+       __raw_writel(0, &npe->regs->watch_count);
+
+       val = ixp4xx_read_feature_bits();
+       /* reset the NPE */
+       ixp4xx_write_feature_bits(val &
+                                 ~(IXP4XX_FEATURE_RESET_NPEA << npe->id));
+       for (i = 0; i < MAX_RETRIES; i++) {
+               if (!(ixp4xx_read_feature_bits() &
+                     (IXP4XX_FEATURE_RESET_NPEA << npe->id)))
+                       break;  /* reset completed */
+               udelay(1);
+       }
+       if (i == MAX_RETRIES)
+               return -ETIMEDOUT;
+
+       /* deassert reset */
+       ixp4xx_write_feature_bits(val |
+                                 (IXP4XX_FEATURE_RESET_NPEA << npe->id));
+       for (i = 0; i < MAX_RETRIES; i++) {
+               if (ixp4xx_read_feature_bits() &
+                   (IXP4XX_FEATURE_RESET_NPEA << npe->id))
+                       break;  /* NPE is back alive */
+               udelay(1);
+       }
+       if (i == MAX_RETRIES)
+               return -ETIMEDOUT;
+
+       npe_stop(npe);
+
+       /* restore NPE configuration bus Control Register - parity settings */
+       __raw_writel(ctl, &npe->regs->messaging_control);
+       return 0;
+}
+
+
+int npe_send_message(struct npe *npe, const void *msg, const char *what)
+{
+       const u32 *send = msg;
+       int cycles = 0;
+
+       debug_msg(npe, "Trying to send message %s [%08X:%08X]\n",
+                 what, send[0], send[1]);
+
+       if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE) {
+               debug_msg(npe, "NPE input FIFO not empty\n");
+               return -EIO;
+       }
+
+       __raw_writel(send[0], &npe->regs->in_out_fifo);
+
+       if (!(__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNF)) {
+               debug_msg(npe, "NPE input FIFO full\n");
+               return -EIO;
+       }
+
+       __raw_writel(send[1], &npe->regs->in_out_fifo);
+
+       while ((cycles < MAX_RETRIES) &&
+              (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE)) {
+               udelay(1);
+               cycles++;
+       }
+
+       if (cycles == MAX_RETRIES) {
+               debug_msg(npe, "Timeout sending message\n");
+               return -ETIMEDOUT;
+       }
+
+       debug_msg(npe, "Sending a message took %i cycles\n", cycles);
+       return 0;
+}
+
+int npe_recv_message(struct npe *npe, void *msg, const char *what)
+{
+       u32 *recv = msg;
+       int cycles = 0, cnt = 0;
+
+       debug_msg(npe, "Trying to receive message %s\n", what);
+
+       while (cycles < MAX_RETRIES) {
+               if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_OFNE) {
+                       recv[cnt++] = __raw_readl(&npe->regs->in_out_fifo);
+                       if (cnt == 2)
+                               break;
+               } else {
+                       udelay(1);
+                       cycles++;
+               }
+       }
+
+       switch(cnt) {
+       case 1:
+               debug_msg(npe, "Received [%08X]\n", recv[0]);
+               break;
+       case 2:
+               debug_msg(npe, "Received [%08X:%08X]\n", recv[0], recv[1]);
+               break;
+       }
+
+       if (cycles == MAX_RETRIES) {
+               debug_msg(npe, "Timeout waiting for message\n");
+               return -ETIMEDOUT;
+       }
+
+       debug_msg(npe, "Receiving a message took %i cycles\n", cycles);
+       return 0;
+}
+
+int npe_send_recv_message(struct npe *npe, void *msg, const char *what)
+{
+       int result;
+       u32 *send = msg, recv[2];
+
+       if ((result = npe_send_message(npe, msg, what)) != 0)
+               return result;
+       if ((result = npe_recv_message(npe, recv, what)) != 0)
+               return result;
+
+       if ((recv[0] != send[0]) || (recv[1] != send[1])) {
+               debug_msg(npe, "Message %s: unexpected message received\n",
+                         what);
+               return -EIO;
+       }
+       return 0;
+}
+
+
+int npe_load_firmware(struct npe *npe, const char *name, struct device *dev)
+{
+       const struct firmware *fw_entry;
+
+       struct dl_block {
+               u32 type;
+               u32 offset;
+       } *blk;
+
+       struct dl_image {
+               u32 magic;
+               u32 id;
+               u32 size;
+               union {
+                       u32 data[0];
+                       struct dl_block blocks[0];
+               };
+       } *image;
+
+       struct dl_codeblock {
+               u32 npe_addr;
+               u32 size;
+               u32 data[0];
+       } *cb;
+
+       int i, j, err, data_size, instr_size, blocks, table_end;
+       u32 cmd;
+
+       if ((err = request_firmware(&fw_entry, name, dev)) != 0)
+               return err;
+
+       err = -EINVAL;
+       if (fw_entry->size < sizeof(struct dl_image)) {
+               print_npe(KERN_ERR, npe, "incomplete firmware file\n");
+               goto err;
+       }
+       image = (struct dl_image*)fw_entry->data;
+
+#if DEBUG_FW
+       print_npe(KERN_DEBUG, npe, "firmware: %08X %08X %08X (0x%X bytes)\n",
+                 image->magic, image->id, image->size, image->size * 4);
+#endif
+
+       if (image->magic == swab32(FW_MAGIC)) { /* swapped file */
+               image->id = swab32(image->id);
+               image->size = swab32(image->size);
+       } else if (image->magic != FW_MAGIC) {
+               print_npe(KERN_ERR, npe, "bad firmware file magic: 0x%X\n",
+                         image->magic);
+               goto err;
+       }
+       if ((image->size * 4 + sizeof(struct dl_image)) != fw_entry->size) {
+               print_npe(KERN_ERR, npe,
+                         "inconsistent size of firmware file\n");
+               goto err;
+       }
+       if (((image->id >> 24) & 0xF /* NPE ID */) != npe->id) {
+               print_npe(KERN_ERR, npe, "firmware file NPE ID mismatch\n");
+               goto err;
+       }
+       if (image->magic == swab32(FW_MAGIC))
+               for (i = 0; i < image->size; i++)
+                       image->data[i] = swab32(image->data[i]);
+
+       if (!cpu_is_ixp46x() && ((image->id >> 28) & 0xF /* device ID */)) {
+               print_npe(KERN_INFO, npe, "IXP46x firmware ignored on "
+                         "IXP42x\n");
+               goto err;
+       }
+
+       if (npe_running(npe)) {
+               print_npe(KERN_INFO, npe, "unable to load firmware, NPE is "
+                         "already running\n");
+               err = -EBUSY;
+               goto err;
+       }
+#if 0
+       npe_stop(npe);
+       npe_reset(npe);
+#endif
+
+       print_npe(KERN_INFO, npe, "firmware functionality 0x%X, "
+                 "revision 0x%X:%X\n", (image->id >> 16) & 0xFF,
+                 (image->id >> 8) & 0xFF, image->id & 0xFF);
+
+       if (!cpu_is_ixp46x()) {
+               if (!npe->id)
+                       instr_size = NPE_A_42X_INSTR_SIZE;
+               else
+                       instr_size = NPE_B_AND_C_42X_INSTR_SIZE;
+               data_size = NPE_42X_DATA_SIZE;
+       } else {
+               instr_size = NPE_46X_INSTR_SIZE;
+               data_size = NPE_46X_DATA_SIZE;
+       }
+
+       for (blocks = 0; blocks * sizeof(struct dl_block) / 4 < image->size;
+            blocks++)
+               if (image->blocks[blocks].type == FW_BLOCK_TYPE_EOF)
+                       break;
+       if (blocks * sizeof(struct dl_block) / 4 >= image->size) {
+               print_npe(KERN_INFO, npe, "firmware EOF block marker not "
+                         "found\n");
+               goto err;
+       }
+
+#if DEBUG_FW
+       print_npe(KERN_DEBUG, npe, "%i firmware blocks found\n", blocks);
+#endif
+
+       table_end = blocks * sizeof(struct dl_block) / 4 + 1 /* EOF marker */;
+       for (i = 0, blk = image->blocks; i < blocks; i++, blk++) {
+               if (blk->offset > image->size - sizeof(struct dl_codeblock) / 4
+                   || blk->offset < table_end) {
+                       print_npe(KERN_INFO, npe, "invalid offset 0x%X of "
+                                 "firmware block #%i\n", blk->offset, i);
+                       goto err;
+               }
+
+               cb = (struct dl_codeblock*)&image->data[blk->offset];
+               if (blk->type == FW_BLOCK_TYPE_INSTR) {
+                       if (cb->npe_addr + cb->size > instr_size)
+                               goto too_big;
+                       cmd = CMD_WR_INS_MEM;
+               } else if (blk->type == FW_BLOCK_TYPE_DATA) {
+                       if (cb->npe_addr + cb->size > data_size)
+                               goto too_big;
+                       cmd = CMD_WR_DATA_MEM;
+               } else {
+                       print_npe(KERN_INFO, npe, "invalid firmware block #%i "
+                                 "type 0x%X\n", i, blk->type);
+                       goto err;
+               }
+               if (blk->offset + sizeof(*cb) / 4 + cb->size > image->size) {
+                       print_npe(KERN_INFO, npe, "firmware block #%i doesn't "
+                                 "fit in firmware image: type %c, start 0x%X,"
+                                 " length 0x%X\n", i,
+                                 blk->type == FW_BLOCK_TYPE_INSTR ? 'I' : 'D',
+                                 cb->npe_addr, cb->size);
+                       goto err;
+               }
+
+               for (j = 0; j < cb->size; j++)
+                       npe_cmd_write(npe, cb->npe_addr + j, cmd, cb->data[j]);
+       }
+
+       npe_start(npe);
+       if (!npe_running(npe))
+               print_npe(KERN_ERR, npe, "unable to start\n");
+       release_firmware(fw_entry);
+       return 0;
+
+too_big:
+       print_npe(KERN_INFO, npe, "firmware block #%i doesn't fit in NPE "
+                 "memory: type %c, start 0x%X, length 0x%X\n", i,
+                 blk->type == FW_BLOCK_TYPE_INSTR ? 'I' : 'D',
+                 cb->npe_addr, cb->size);
+err:
+       release_firmware(fw_entry);
+       return err;
+}
+
+
+struct npe *npe_request(int id)
+{
+       if (id < NPE_COUNT)
+               if (npe_tab[id].valid)
+                       if (try_module_get(THIS_MODULE))
+                               return &npe_tab[id];
+       return NULL;
+}
+
+void npe_release(struct npe *npe)
+{
+       module_put(THIS_MODULE);
+}
+
+
+static int __init npe_init_module(void)
+{
+
+       int i, found = 0;
+
+       for (i = 0; i < NPE_COUNT; i++) {
+               struct npe *npe = &npe_tab[i];
+               if (!(ixp4xx_read_feature_bits() &
+                     (IXP4XX_FEATURE_RESET_NPEA << i)))
+                       continue; /* NPE already disabled or not present */
+               if (!(npe->mem_res = request_mem_region(npe->regs_phys,
+                                                       REGS_SIZE,
+                                                       npe_name(npe)))) {
+                       print_npe(KERN_ERR, npe,
+                                 "failed to request memory region\n");
+                       continue;
+               }
+
+               if (npe_reset(npe))
+                       continue;
+               npe->valid = 1;
+               found++;
+       }
+
+       if (!found)
+               return -ENOSYS;
+       return 0;
+}
+
+static void __exit npe_cleanup_module(void)
+{
+       int i;
+
+       for (i = 0; i < NPE_COUNT; i++)
+               if (npe_tab[i].mem_res) {
+                       npe_reset(&npe_tab[i]);
+                       release_resource(npe_tab[i].mem_res);
+               }
+}
+
+module_init(npe_init_module);
+module_exit(npe_cleanup_module);
+
+MODULE_AUTHOR("Krzysztof Halasa");
+MODULE_LICENSE("GPL v2");
+
+EXPORT_SYMBOL(npe_names);
+EXPORT_SYMBOL(npe_running);
+EXPORT_SYMBOL(npe_request);
+EXPORT_SYMBOL(npe_release);
+EXPORT_SYMBOL(npe_load_firmware);
+EXPORT_SYMBOL(npe_send_message);
+EXPORT_SYMBOL(npe_recv_message);
+EXPORT_SYMBOL(npe_send_recv_message);
diff --git a/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c b/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c
new file mode 100644 (file)
index 0000000..e833013
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * Intel IXP4xx Queue Manager driver for Linux
+ *
+ * Copyright (C) 2007 Krzysztof Halasa <khc@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 <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/arch/qmgr.h>
+
+#define DEBUG          0
+
+struct qmgr_regs __iomem *qmgr_regs;
+static struct resource *mem_res;
+static spinlock_t qmgr_lock;
+static u32 used_sram_bitmap[4]; /* 128 16-dword pages */
+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)
+{
+       u32 __iomem *reg = &qmgr_regs->irqsrc[queue / 8]; /* 8 queues / u32 */
+       int bit = (queue % 8) * 4; /* 3 bits + 1 reserved bit per queue */
+       unsigned long flags;
+
+       src &= 7;
+       spin_lock_irqsave(&qmgr_lock, flags);
+       __raw_writel((__raw_readl(reg) & ~(7 << bit)) | (src << bit), reg);
+       irq_handlers[queue] = handler;
+       irq_pdevs[queue] = pdev;
+       spin_unlock_irqrestore(&qmgr_lock, flags);
+}
+
+
+static irqreturn_t qmgr_irq1(int irq, void *pdev)
+{
+       int i;
+       u32 val = __raw_readl(&qmgr_regs->irqstat[0]);
+       __raw_writel(val, &qmgr_regs->irqstat[0]); /* ACK */
+
+       for (i = 0; i < HALF_QUEUES; i++)
+               if (val & (1 << i))
+                       irq_handlers[i](irq_pdevs[i]);
+
+       return val ? IRQ_HANDLED : 0;
+}
+
+
+void qmgr_enable_irq(unsigned int queue)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&qmgr_lock, flags);
+       __raw_writel(__raw_readl(&qmgr_regs->irqen[0]) | (1 << queue),
+                    &qmgr_regs->irqen[0]);
+       spin_unlock_irqrestore(&qmgr_lock, flags);
+}
+
+void qmgr_disable_irq(unsigned int queue)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&qmgr_lock, flags);
+       __raw_writel(__raw_readl(&qmgr_regs->irqen[0]) & ~(1 << queue),
+                    &qmgr_regs->irqen[0]);
+       spin_unlock_irqrestore(&qmgr_lock, flags);
+}
+
+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;
+}
+
+int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+                      unsigned int nearly_empty_watermark,
+                      unsigned int nearly_full_watermark)
+{
+       u32 cfg, addr = 0, mask[4]; /* in 16-dwords */
+       int err;
+
+       if (queue >= HALF_QUEUES)
+               return -ERANGE;
+
+       if ((nearly_empty_watermark | nearly_full_watermark) & ~7)
+               return -EINVAL;
+
+       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:
+               return -EINVAL;
+       }
+
+       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;
+
+       if (!try_module_get(THIS_MODULE))
+               return -ENODEV;
+
+       spin_lock_irq(&qmgr_lock);
+       if (__raw_readl(&qmgr_regs->sram[queue])) {
+               err = -EBUSY;
+               goto err;
+       }
+
+       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)) {
+                       printk(KERN_ERR "qmgr: no free SRAM space for"
+                              " queue %i\n", queue);
+                       err = -ENOMEM;
+                       goto err;
+               }
+       }
+
+       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]);
+       spin_unlock_irq(&qmgr_lock);
+
+#if DEBUG
+       printk(KERN_DEBUG "qmgr: requested queue %i, addr = 0x%02X\n",
+              queue, addr);
+#endif
+       return 0;
+
+err:
+       spin_unlock_irq(&qmgr_lock);
+       module_put(THIS_MODULE);
+       return err;
+}
+
+void qmgr_release_queue(unsigned int queue)
+{
+       u32 cfg, addr, mask[4];
+
+       BUG_ON(queue >= HALF_QUEUES); /* not in valid range */
+
+       spin_lock_irq(&qmgr_lock);
+       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;
+       }
+
+       while (addr--)
+               shift_mask(mask);
+
+       __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];
+       irq_handlers[queue] = NULL; /* catch IRQ bugs */
+       spin_unlock_irq(&qmgr_lock);
+
+       module_put(THIS_MODULE);
+#if DEBUG
+       printk(KERN_DEBUG "qmgr: released queue %i\n", queue);
+#endif
+}
+
+static int qmgr_init(void)
+{
+       int i, err;
+       mem_res = request_mem_region(IXP4XX_QMGR_BASE_PHYS,
+                                    IXP4XX_QMGR_REGION_SIZE,
+                                    "IXP4xx Queue Manager");
+       if (mem_res == NULL)
+               return -EBUSY;
+
+       qmgr_regs = ioremap(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE);
+       if (qmgr_regs == NULL) {
+               err = -ENOMEM;
+               goto error_map;
+       }
+
+       /* 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]);
+       }
+
+       for (i = 0; i < QUEUES; i++)
+               __raw_writel(0, &qmgr_regs->sram[i]);
+
+       err = request_irq(IRQ_IXP4XX_QM1, qmgr_irq1, 0,
+                         "IXP4xx Queue Manager", NULL);
+       if (err) {
+               printk(KERN_ERR "qmgr: failed to request IRQ%i\n",
+                      IRQ_IXP4XX_QM1);
+               goto error_irq;
+       }
+
+       used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */
+       spin_lock_init(&qmgr_lock);
+
+       printk(KERN_INFO "IXP4xx Queue Manager initialized.\n");
+       return 0;
+
+error_irq:
+       iounmap(qmgr_regs);
+error_map:
+       release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE);
+       return err;
+}
+
+static void qmgr_remove(void)
+{
+       free_irq(IRQ_IXP4XX_QM1, NULL);
+       synchronize_irq(IRQ_IXP4XX_QM1);
+       iounmap(qmgr_regs);
+       release_mem_region(IXP4XX_QMGR_BASE_PHYS, IXP4XX_QMGR_REGION_SIZE);
+}
+
+module_init(qmgr_init);
+module_exit(qmgr_remove);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Krzysztof Halasa");
+
+EXPORT_SYMBOL(qmgr_regs);
+EXPORT_SYMBOL(qmgr_set_irq);
+EXPORT_SYMBOL(qmgr_enable_irq);
+EXPORT_SYMBOL(qmgr_disable_irq);
+EXPORT_SYMBOL(qmgr_request_queue);
+EXPORT_SYMBOL(qmgr_release_queue);
diff --git a/include/asm-arm/arch-ixp4xx/npe.h b/include/asm-arm/arch-ixp4xx/npe.h
new file mode 100644 (file)
index 0000000..37d0511
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef __IXP4XX_NPE_H
+#define __IXP4XX_NPE_H
+
+#include <linux/kernel.h>
+
+extern const char *npe_names[];
+
+struct npe_regs {
+       u32 exec_addr, exec_data, exec_status_cmd, exec_count;
+       u32 action_points[4];
+       u32 watchpoint_fifo, watch_count;
+       u32 profile_count;
+       u32 messaging_status, messaging_control;
+       u32 mailbox_status, /*messaging_*/ in_out_fifo;
+};
+
+struct npe {
+       struct resource *mem_res;
+       struct npe_regs __iomem *regs;
+       u32 regs_phys;
+       int id;
+       int valid;
+};
+
+
+static inline const char *npe_name(struct npe *npe)
+{
+       return npe_names[npe->id];
+}
+
+int npe_running(struct npe *npe);
+int npe_send_message(struct npe *npe, const void *msg, const char *what);
+int npe_recv_message(struct npe *npe, void *msg, const char *what);
+int npe_send_recv_message(struct npe *npe, void *msg, const char *what);
+int npe_load_firmware(struct npe *npe, const char *name, struct device *dev);
+struct npe *npe_request(int id);
+void npe_release(struct npe *npe);
+
+#endif /* __IXP4XX_NPE_H */
diff --git a/include/asm-arm/arch-ixp4xx/qmgr.h b/include/asm-arm/arch-ixp4xx/qmgr.h
new file mode 100644 (file)
index 0000000..1e52b95
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2007 Krzysztof Halasa <khc@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 <linux/io.h>
+#include <linux/kernel.h>
+
+#define HALF_QUEUES    32
+#define QUEUES         64      /* only 32 lower queues currently supported */
+#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 */
+};
+
+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 */
+int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+                      unsigned int nearly_empty_watermark,
+                      unsigned int nearly_full_watermark);
+void qmgr_release_queue(unsigned int queue);
+
+
+static inline void qmgr_put_entry(unsigned int queue, u32 val)
+{
+       extern struct qmgr_regs __iomem *qmgr_regs;
+       __raw_writel(val, &qmgr_regs->acc[queue][0]);
+}
+
+static inline u32 qmgr_get_entry(unsigned int queue)
+{
+       extern struct qmgr_regs __iomem *qmgr_regs;
+       return __raw_readl(&qmgr_regs->acc[queue][0]);
+}
+
+static inline int qmgr_get_stat1(unsigned int queue)
+{
+       extern struct qmgr_regs __iomem *qmgr_regs;
+       return (__raw_readl(&qmgr_regs->stat1[queue >> 3])
+               >> ((queue & 7) << 2)) & 0xF;
+}
+
+static inline int qmgr_get_stat2(unsigned int queue)
+{
+       extern struct qmgr_regs __iomem *qmgr_regs;
+       return (__raw_readl(&qmgr_regs->stat2[queue >> 4])
+               >> ((queue & 0xF) << 1)) & 0x3;
+}
+
+static inline int qmgr_stat_empty(unsigned int queue)
+{
+       return !!(qmgr_get_stat1(queue) & QUEUE_STAT1_EMPTY);
+}
+
+static inline int qmgr_stat_nearly_empty(unsigned int queue)
+{
+       return !!(qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_EMPTY);
+}
+
+static inline int qmgr_stat_nearly_full(unsigned int queue)
+{
+       return !!(qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_FULL);
+}
+
+static inline int qmgr_stat_full(unsigned int queue)
+{
+       return !!(qmgr_get_stat1(queue) & QUEUE_STAT1_FULL);
+}
+
+static inline int qmgr_stat_underflow(unsigned int queue)
+{
+       return !!(qmgr_get_stat2(queue) & QUEUE_STAT2_UNDERFLOW);
+}
+
+static inline int qmgr_stat_overflow(unsigned int queue)
+{
+       return !!(qmgr_get_stat2(queue) & QUEUE_STAT2_OVERFLOW);
+}
+
+#endif