From 16b3d949d518321b92fa7b9a0379988a5f7d700c Mon Sep 17 00:00:00 2001 From: Gordon Farquharson Date: Sun, 10 Jun 2007 02:14:22 +0000 Subject: [PATCH] * Revert back to Christian Hohnstaedt's ixp4xx NPE driver (Krzysztof's driver does not currently achieve achieve the performance that Christian's does, and it is unlikely that Krzysztof will have the time to work on his before 2.6.22 is released). * Rename MAC patches to match names in nslu2-linux repository and refresh these patches to apply with the GPIO patch. The mac patches can be replaced with the new ones when Krzysztof's driver is developed further. svn path=/dists/trunk/linux-2.6/; revision=8957 --- debian/arch/arm/config.ixp4xx | 8 +- debian/changelog | 4 +- ...p4xx-net-driver-improve-mac-handling.patch | 42 + .../features/arm/ixp4xx-net-drivers.patch | 2410 -------- .../features/arm/ixp4xx-net-fuses.patch | 87 - .../arm/ixp4xx-npe-driver-0.3.1.patch | 4923 +++++++++++++++++ .../arm/nas100d-i2c-gpio-driver-support.patch | 12 +- .../features/arm/nas100d-mac_plat_info.patch | 42 +- .../features/arm/nas100d-setup-mac.patch | 19 +- .../arm/nslu2-i2c-gpio-driver-support.patch | 14 +- .../features/arm/nslu2-mac_plat_info.patch | 44 +- .../features/arm/nslu2-setup-mac.patch | 28 +- debian/patches/series/1~experimental.1 | 4 +- 13 files changed, 5060 insertions(+), 2577 deletions(-) create mode 100644 debian/patches/features/arm/ixp4xx-net-driver-improve-mac-handling.patch delete mode 100644 debian/patches/features/arm/ixp4xx-net-drivers.patch delete mode 100644 debian/patches/features/arm/ixp4xx-net-fuses.patch create mode 100644 debian/patches/features/arm/ixp4xx-npe-driver-0.3.1.patch diff --git a/debian/arch/arm/config.ixp4xx b/debian/arch/arm/config.ixp4xx index d9eb786c4..742b4ade3 100644 --- a/debian/arch/arm/config.ixp4xx +++ b/debian/arch/arm/config.ixp4xx @@ -109,8 +109,6 @@ CONFIG_MACH_NAS100D=y # CONFIG_DMABOUNCE=y # CONFIG_IXP4XX_INDIRECT_PCI is not set -CONFIG_IXP4XX_QMGR=m -CONFIG_IXP4XX_NPE=m # # Processor Type @@ -768,7 +766,11 @@ CONFIG_CICADA_PHY=m # CONFIG_NET_ETHERNET=y CONFIG_MII=y -CONFIG_IXP4XX_ETH=m +CONFIG_IXP4XX_QMGR=m +CONFIG_IXP4XX_NPE=m +CONFIG_IXP4XX_FW_LOAD=y +CONFIG_IXP4XX_MAC=m +CONFIG_IXP4XX_CRYPTO=m # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set # CONFIG_CASSINI is not set diff --git a/debian/changelog b/debian/changelog index 032f6b173..fb03db922 100644 --- a/debian/changelog +++ b/debian/changelog @@ -43,12 +43,10 @@ linux-2.6 (2.6.22~rc4-1~experimental.1) UNRELEASED; urgency=low [ Gordon Farquharson ] * arm: Mark CHELSIO_T3, NETXEN_NIC, BCM43XX, VIDEO_BT848, DVB_B2C2_FLEXCOP, and DVB_BUDGET as broken on ARM. - * arm/ixp4xx: Replace IXP4xx network drivers (Qmgr, NPE, Ethernet, - HSS) with the ones written by Krzysztof Halasa. * arm/ixp4xx: Add support for the new generic I2C GPIO driver on the NSLU2 and the NAS100D. Thanks to Michael-Luke Jones and Rod Whitby. - -- maximilian attems Fri, 08 Jun 2007 16:54:39 +0200 + -- Gordon Farquharson Sat, 9 Jun 2007 19:33:59 -0600 linux-2.6 (2.6.21-3) UNRELEASED; urgency=low diff --git a/debian/patches/features/arm/ixp4xx-net-driver-improve-mac-handling.patch b/debian/patches/features/arm/ixp4xx-net-driver-improve-mac-handling.patch new file mode 100644 index 000000000..8e41ecf47 --- /dev/null +++ b/debian/patches/features/arm/ixp4xx-net-driver-improve-mac-handling.patch @@ -0,0 +1,42 @@ +diff --git a/drivers/net/ixp4xx/mac_driver.c b/drivers/net/ixp4xx/mac_driver.c +index 2ae78e5..2212293 100644 +--- a/drivers/net/ixp4xx/mac_driver.c ++++ b/drivers/net/ixp4xx/mac_driver.c +@@ -350,6 +350,16 @@ static int ixmac_open (struct net_device *dev) + mac->npe_stat_num = inpe_stat_num += NPE_Q_STAT_NUM; + ++ /* Only use platform or random if there's currently no device hw addr */ ++ if (is_zero_ether_addr(dev->dev_addr)) { ++ if (is_zero_ether_addr(mac->plat->hwaddr)) { ++ random_ether_addr(dev->dev_addr); ++ dev->dev_addr[5] = mac->plat->phy_id; ++ } ++ else ++ memcpy(dev->dev_addr, mac->plat->hwaddr, 6); ++ } ++ + mac_set_uniaddr(dev); + media_check(dev, 1); + ixmac_set_rx_mode(dev); +@@ -692,20 +702,6 @@ static int mac_probe(struct platform_device *pdev) + + INIT_DELAYED_WORK(&mac->mdio_thread, mac_mdio_thread); + +- /* The place of the MAC address is very system dependent. +- * Here we use a random one to be replaced by one of the +- * following commands: +- * "ip link set address 02:03:04:04:04:01 dev eth0" +- * "ifconfig eth0 hw ether 02:03:04:04:04:07" +- */ +- +- if (is_zero_ether_addr(plat->hwaddr)) { +- random_ether_addr(dev->dev_addr); +- dev->dev_addr[5] = plat->phy_id; +- } +- else +- memcpy(dev->dev_addr, plat->hwaddr, 6); +- + printk(KERN_INFO IXMAC_NAME " driver " IXMAC_VERSION + ": %s on %s with PHY[%d] initialized\n", + dev->name, npe->plat->name, plat->phy_id); diff --git a/debian/patches/features/arm/ixp4xx-net-drivers.patch b/debian/patches/features/arm/ixp4xx-net-drivers.patch deleted file mode 100644 index cac923707..000000000 --- a/debian/patches/features/arm/ixp4xx-net-drivers.patch +++ /dev/null @@ -1,2410 +0,0 @@ -diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig -index 8a339cd..94abbb3 100644 -Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/Kconfig -=================================================================== ---- linux-2.6.22-rc4-armeb.orig/arch/arm/mach-ixp4xx/Kconfig 2007-06-05 06:26:14.000000000 -0700 -+++ linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/Kconfig 2007-06-05 06:26:47.000000000 -0700 -@@ -181,6 +181,20 @@ - 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 the Ethernet driver. -+ -+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 the Ethernet driver. -+ - endmenu - - endif -Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/Makefile -=================================================================== ---- linux-2.6.22-rc4-armeb.orig/arch/arm/mach-ixp4xx/Makefile 2007-06-05 06:26:14.000000000 -0700 -+++ linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/Makefile 2007-06-05 06:26:47.000000000 -0700 -@@ -28,3 +28,5 @@ - obj-$(CONFIG_MACH_FSG) += fsg-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 -Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/ixp4xx_npe.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/ixp4xx_npe.c 2007-06-05 06:26:47.000000000 -0700 -@@ -0,0 +1,737 @@ -+/* -+ * Intel IXP4xx Network Processor Engine driver for Linux -+ * -+ * Copyright (C) 2007 Krzysztof Halasa -+ * -+ * 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 -+ * Thanks, Christian. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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_fuses(); -+ /* reset the NPE */ -+ ixp4xx_write_fuses(val & ~(IXP4XX_FUSE_RESET_NPEA << npe->id)); -+ for (i = 0; i < MAX_RETRIES; i++) { -+ if (!(ixp4xx_read_fuses() & -+ (IXP4XX_FUSE_RESET_NPEA << npe->id))) -+ break; /* reset completed */ -+ udelay(1); -+ } -+ if (i == MAX_RETRIES) -+ return -ETIMEDOUT; -+ -+ /* deassert reset */ -+ ixp4xx_write_fuses(val | (IXP4XX_FUSE_RESET_NPEA << npe->id)); -+ for (i = 0; i < MAX_RETRIES; i++) { -+ if (ixp4xx_read_fuses() & (IXP4XX_FUSE_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_fuses() & (IXP4XX_FUSE_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); -Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c 2007-06-05 06:26:47.000000000 -0700 -@@ -0,0 +1,273 @@ -+/* -+ * Intel IXP4xx Queue Manager driver for Linux -+ * -+ * Copyright (C) 2007 Krzysztof Halasa -+ * -+ * 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 -+#include -+#include -+#include -+ -+#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_resource(mem_res); -+ return err; -+} -+ -+static void qmgr_remove(void) -+{ -+ free_irq(IRQ_IXP4XX_QM1, NULL); -+ synchronize_irq(IRQ_IXP4XX_QM1); -+ iounmap(qmgr_regs); -+ release_resource(mem_res); -+} -+ -+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); -Index: linux-2.6.22-rc4-armeb/drivers/net/arm/Kconfig -=================================================================== ---- linux-2.6.22-rc4-armeb.orig/drivers/net/arm/Kconfig 2007-04-25 20:08:32.000000000 -0700 -+++ linux-2.6.22-rc4-armeb/drivers/net/arm/Kconfig 2007-06-05 06:26:47.000000000 -0700 -@@ -46,3 +46,13 @@ - help - This is a driver for the ethernet hardware included in EP93xx CPUs. - Say Y if you are building a kernel for EP93xx based devices. -+ -+config IXP4XX_ETH -+ tristate "IXP4xx Ethernet support" -+ depends on NET_ETHERNET && ARM && ARCH_IXP4XX -+ select IXP4XX_NPE -+ select IXP4XX_QMGR -+ select MII -+ help -+ Say Y here if you want to use built-in Ethernet ports -+ on IXP4xx processor. -Index: linux-2.6.22-rc4-armeb/drivers/net/arm/Makefile -=================================================================== ---- linux-2.6.22-rc4-armeb.orig/drivers/net/arm/Makefile 2007-04-25 20:08:32.000000000 -0700 -+++ linux-2.6.22-rc4-armeb/drivers/net/arm/Makefile 2007-06-05 06:26:47.000000000 -0700 -@@ -9,3 +9,4 @@ - obj-$(CONFIG_ARM_ETHER1) += ether1.o - obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o - obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o -+obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o -Index: linux-2.6.22-rc4-armeb/drivers/net/arm/ixp4xx_eth.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22-rc4-armeb/drivers/net/arm/ixp4xx_eth.c 2007-06-05 06:26:47.000000000 -0700 -@@ -0,0 +1,1124 @@ -+/* -+ * Intel IXP4xx Ethernet driver for Linux -+ * -+ * Copyright (C) 2007 Krzysztof Halasa -+ * -+ * 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. -+ * -+ * Ethernet port config (0x00 is not present on IXP42X): -+ * -+ * logical port 0x00 0x10 0x20 -+ * NPE 0 (NPE-A) 1 (NPE-B) 2 (NPE-C) -+ * physical PortId 2 0 1 -+ * TX queue 23 24 25 -+ * RX-free queue 26 27 28 -+ * TX-done queue is always 31, RX queue is configurable -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DEBUG_QUEUES 0 -+#define DEBUG_RX 0 -+#define DEBUG_TX 0 -+#define DEBUG_PKT_BYTES 0 -+#define DEBUG_MDIO 0 -+#define DEBUG_CLOSE 0 -+ -+#define DRV_NAME "ixp4xx_eth" -+ -+#define TX_QUEUE_LEN 16 /* dwords */ -+#define PKT_DESCS 64 /* also length of queues: TX-done, RX-ready, RX */ -+ -+#define POOL_ALLOC_SIZE (sizeof(struct desc) * (PKT_DESCS)) -+#define REGS_SIZE 0x1000 -+#define MAX_MRU 1536 /* 0x600 */ -+ -+#define MDIO_INTERVAL (3 * HZ) -+#define MAX_MDIO_RETRIES 100 /* microseconds, typically 30 cycles */ -+#define MAX_CLOSE_WAIT 1000 /* microseconds, typically 2-3 cycles */ -+ -+#define NPE_ID(port) ((port)->id >> 4) -+#define PHYSICAL_ID(port) ((NPE_ID(port) + 2) % 3) -+#define TX_QUEUE(plat) (NPE_ID(port) + 23) -+#define RXFREE_QUEUE(plat) (NPE_ID(port) + 26) -+#define TXDONE_QUEUE 31 -+ -+/* TX Control Registers */ -+#define TX_CNTRL0_TX_EN 0x01 -+#define TX_CNTRL0_HALFDUPLEX 0x02 -+#define TX_CNTRL0_RETRY 0x04 -+#define TX_CNTRL0_PAD_EN 0x08 -+#define TX_CNTRL0_APPEND_FCS 0x10 -+#define TX_CNTRL0_2DEFER 0x20 -+#define TX_CNTRL0_RMII 0x40 /* reduced MII */ -+#define TX_CNTRL1_RETRIES 0x0F /* 4 bits */ -+ -+/* RX Control Registers */ -+#define RX_CNTRL0_RX_EN 0x01 -+#define RX_CNTRL0_PADSTRIP_EN 0x02 -+#define RX_CNTRL0_SEND_FCS 0x04 -+#define RX_CNTRL0_PAUSE_EN 0x08 -+#define RX_CNTRL0_LOOP_EN 0x10 -+#define RX_CNTRL0_ADDR_FLTR_EN 0x20 -+#define RX_CNTRL0_RX_RUNT_EN 0x40 -+#define RX_CNTRL0_BCAST_DIS 0x80 -+#define RX_CNTRL1_DEFER_EN 0x01 -+ -+/* Core Control Register */ -+#define CORE_RESET 0x01 -+#define CORE_RX_FIFO_FLUSH 0x02 -+#define CORE_TX_FIFO_FLUSH 0x04 -+#define CORE_SEND_JAM 0x08 -+#define CORE_MDC_EN 0x10 /* MDIO using NPE-B ETH-0 only */ -+ -+#define DEFAULT_TX_CNTRL0 (TX_CNTRL0_TX_EN | TX_CNTRL0_RETRY | \ -+ TX_CNTRL0_PAD_EN | TX_CNTRL0_APPEND_FCS | \ -+ TX_CNTRL0_2DEFER) -+#define DEFAULT_RX_CNTRL0 RX_CNTRL0_RX_EN -+#define DEFAULT_CORE_CNTRL CORE_MDC_EN -+ -+ -+/* NPE message codes */ -+#define NPE_GETSTATUS 0x00 -+#define NPE_EDB_SETPORTADDRESS 0x01 -+#define NPE_EDB_GETMACADDRESSDATABASE 0x02 -+#define NPE_EDB_SETMACADDRESSSDATABASE 0x03 -+#define NPE_GETSTATS 0x04 -+#define NPE_RESETSTATS 0x05 -+#define NPE_SETMAXFRAMELENGTHS 0x06 -+#define NPE_VLAN_SETRXTAGMODE 0x07 -+#define NPE_VLAN_SETDEFAULTRXVID 0x08 -+#define NPE_VLAN_SETPORTVLANTABLEENTRY 0x09 -+#define NPE_VLAN_SETPORTVLANTABLERANGE 0x0A -+#define NPE_VLAN_SETRXQOSENTRY 0x0B -+#define NPE_VLAN_SETPORTIDEXTRACTIONMODE 0x0C -+#define NPE_STP_SETBLOCKINGSTATE 0x0D -+#define NPE_FW_SETFIREWALLMODE 0x0E -+#define NPE_PC_SETFRAMECONTROLDURATIONID 0x0F -+#define NPE_PC_SETAPMACTABLE 0x11 -+#define NPE_SETLOOPBACK_MODE 0x12 -+#define NPE_PC_SETBSSIDTABLE 0x13 -+#define NPE_ADDRESS_FILTER_CONFIG 0x14 -+#define NPE_APPENDFCSCONFIG 0x15 -+#define NPE_NOTIFY_MAC_RECOVERY_DONE 0x16 -+#define NPE_MAC_RECOVERY_START 0x17 -+ -+ -+struct eth_regs { -+ u32 tx_control[2], __res1[2]; /* 000 */ -+ u32 rx_control[2], __res2[2]; /* 010 */ -+ u32 random_seed, __res3[3]; /* 020 */ -+ u32 partial_empty_threshold, __res4; /* 030 */ -+ u32 partial_full_threshold, __res5; /* 038 */ -+ u32 tx_start_bytes, __res6[3]; /* 040 */ -+ u32 tx_deferral, rx_deferral,__res7[2]; /* 050 */ -+ u32 tx_2part_deferral[2], __res8[2]; /* 060 */ -+ u32 slot_time, __res9[3]; /* 070 */ -+ u32 mdio_command[4]; /* 080 */ -+ u32 mdio_status[4]; /* 090 */ -+ u32 mcast_mask[6], __res10[2]; /* 0A0 */ -+ u32 mcast_addr[6], __res11[2]; /* 0C0 */ -+ u32 int_clock_threshold, __res12[3]; /* 0E0 */ -+ u32 hw_addr[6], __res13[61]; /* 0F0 */ -+ u32 core_control; /* 1FC */ -+}; -+ -+struct port { -+ struct resource *mem_res; -+ struct eth_regs __iomem *regs; -+ struct npe *npe; -+ struct net_device *netdev; -+ struct net_device_stats stat; -+ struct mii_if_info mii; -+ struct delayed_work mdio_thread; -+ struct mac_plat_info *plat; -+#ifdef __ARMEB__ -+ struct sk_buff *rx_buff_tab[PKT_DESCS]; -+#else -+ void *rx_buff_tab[PKT_DESCS]; -+#endif -+ struct desc *rx_desc_tab; /* coherent */ -+ int id; /* logical port ID */ -+ u32 rx_desc_tab_phys; -+}; -+ -+/* NPE message structure */ -+struct msg { -+#ifdef __ARMEB__ -+ u8 cmd, eth_id, byte2, byte3; -+ u8 byte4, byte5, byte6, byte7; -+#else -+ u8 byte3, byte2, eth_id, cmd; -+ u8 byte7, byte6, byte5, byte4; -+#endif -+}; -+ -+/* Ethernet packet descriptor */ -+struct desc { -+ u32 next; /* pointer to next buffer, unused */ -+ -+#ifdef __ARMEB__ -+ u16 buf_len; /* buffer length */ -+ u16 pkt_len; /* packet length */ -+ u32 data; /* pointer to data buffer in RAM */ -+ u8 dest_id; -+ u8 src_id; -+ u16 flags; -+ u8 qos; -+ u8 padlen; -+ u16 vlan_tci; -+#else -+ u16 pkt_len; /* packet length */ -+ u16 buf_len; /* buffer length */ -+ u32 data; /* pointer to data buffer in RAM */ -+ u16 flags; -+ u8 src_id; -+ u8 dest_id; -+ u16 vlan_tci; -+ u8 padlen; -+ u8 qos; -+#endif -+ -+#ifdef __ARMEB__ -+ u8 dst_mac_0, dst_mac_1, dst_mac_2, dst_mac_3; -+ u8 dst_mac_4, dst_mac_5, src_mac_0, src_mac_1; -+ u8 src_mac_2, src_mac_3, src_mac_4, src_mac_5; -+#else -+ u8 dst_mac_3, dst_mac_2, dst_mac_1, dst_mac_0; -+ u8 src_mac_1, src_mac_0, dst_mac_5, dst_mac_4; -+ u8 src_mac_5, src_mac_4, src_mac_3, src_mac_2; -+#endif -+}; -+ -+ -+#define rx_desc_phys(port, n) ((port)->rx_desc_tab_phys + \ -+ (n) * sizeof(struct desc)) -+#define tx_desc_phys(n) (tx_desc_tab_phys + (n) * sizeof(struct desc)) -+ -+#ifndef __ARMEB__ -+static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt) -+{ -+ int i; -+ for (i = 0; i < cnt; i++) -+ dest[i] = swab32(src[i]); -+} -+#endif -+ -+static spinlock_t mdio_lock; -+static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */ -+static int ports_open; -+static struct dma_pool *dma_pool; -+#ifdef __ARMEB__ -+static struct sk_buff *tx_buff_tab[PKT_DESCS]; -+#else -+static void *tx_buff_tab[PKT_DESCS]; -+#endif -+static struct desc *tx_desc_tab; /* coherent */ -+static struct device *tx_owner_tab[PKT_DESCS]; /* for dma_unmap_single() */ -+static u32 tx_desc_tab_phys; -+ -+ -+static u16 mdio_cmd(struct net_device *dev, int phy_id, int location, -+ int write, u16 cmd) -+{ -+ int cycles = 0; -+ -+ if (__raw_readl(&mdio_regs->mdio_command[3]) & 0x80) { -+ printk(KERN_ERR "%s: MII not ready to transmit\n", dev->name); -+ return 0; -+ } -+ -+ if (write) { -+ __raw_writel(cmd & 0xFF, &mdio_regs->mdio_command[0]); -+ __raw_writel(cmd >> 8, &mdio_regs->mdio_command[1]); -+ } -+ __raw_writel(((phy_id << 5) | location) & 0xFF, -+ &mdio_regs->mdio_command[2]); -+ __raw_writel((phy_id >> 3) | (write << 2) | 0x80 /* GO */, -+ &mdio_regs->mdio_command[3]); -+ -+ while ((cycles < MAX_MDIO_RETRIES) && -+ (__raw_readl(&mdio_regs->mdio_command[3]) & 0x80)) { -+ udelay(1); -+ cycles++; -+ } -+ -+ if (cycles == MAX_MDIO_RETRIES) { -+ printk(KERN_ERR "%s: MII write failed\n", dev->name); -+ return 0; -+ } -+ -+#if DEBUG_MDIO -+ printk(KERN_DEBUG "mdio_cmd() took %i cycles\n", cycles); -+#endif -+ -+ if (write) -+ return 0; -+ -+ if (__raw_readl(&mdio_regs->mdio_status[3]) & 0x80) { -+ printk(KERN_ERR "%s: MII read failed\n", dev->name); -+ return 0; -+ } -+ -+ return (__raw_readl(&mdio_regs->mdio_status[0]) & 0xFF) | -+ (__raw_readl(&mdio_regs->mdio_status[1]) << 8); -+} -+ -+static int mdio_read(struct net_device *dev, int phy_id, int location) -+{ -+ unsigned long flags; -+ u16 val; -+ -+ spin_lock_irqsave(&mdio_lock, flags); -+ val = mdio_cmd(dev, phy_id, location, 0, 0); -+ spin_unlock_irqrestore(&mdio_lock, flags); -+ return val; -+} -+ -+static void mdio_write(struct net_device *dev, int phy_id, int location, -+ int val) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&mdio_lock, flags); -+ mdio_cmd(dev, phy_id, location, 1, val); -+ spin_unlock_irqrestore(&mdio_lock, flags); -+} -+ -+static void eth_set_duplex(struct port *port) -+{ -+ if (port->mii.full_duplex) -+ __raw_writel(DEFAULT_TX_CNTRL0 & ~TX_CNTRL0_HALFDUPLEX, -+ &port->regs->tx_control[0]); -+ else -+ __raw_writel(DEFAULT_TX_CNTRL0 | TX_CNTRL0_HALFDUPLEX, -+ &port->regs->tx_control[0]); -+} -+ -+ -+static void mdio_thread(struct work_struct *work) -+{ -+ struct port *port = container_of(work, struct port, mdio_thread.work); -+ -+ if (mii_check_media(&port->mii, 1, 0)) -+ eth_set_duplex(port); -+ schedule_delayed_work(&port->mdio_thread, MDIO_INTERVAL); -+} -+ -+ -+static inline void debug_pkt(const char *func, u8 *data, int len) -+{ -+#if DEBUG_PKT_BYTES -+ int i; -+ -+ printk(KERN_DEBUG "%s(%i): ", func, len); -+ for (i = 0; i < len; i++) { -+ if (i >= DEBUG_PKT_BYTES) -+ break; -+ printk("%s%02X", -+ ((i == 6) || (i == 12) || (i >= 14)) ? " " : "", -+ data[i]); -+ } -+ printk("\n"); -+#endif -+} -+ -+ -+static inline void debug_desc(unsigned int queue, u32 desc_phys, -+ struct desc *desc, int is_get) -+{ -+#if DEBUG_QUEUES -+ const char *op = is_get ? "->" : "<-"; -+ -+ if (!desc_phys) { -+ printk(KERN_DEBUG "queue %2i %s NULL\n", queue, op); -+ return; -+ } -+ printk(KERN_DEBUG "queue %2i %s %X: %X %3X %3X %08X %2X < %2X %4X %X" -+ " %X %X %02X%02X%02X%02X%02X%02X < %02X%02X%02X%02X%02X%02X\n", -+ queue, op, desc_phys, desc->next, desc->buf_len, desc->pkt_len, -+ desc->data, desc->dest_id, desc->src_id, desc->flags, -+ desc->qos, desc->padlen, desc->vlan_tci, -+ desc->dst_mac_0, desc->dst_mac_1, desc->dst_mac_2, -+ desc->dst_mac_3, desc->dst_mac_4, desc->dst_mac_5, -+ desc->src_mac_0, desc->src_mac_1, desc->src_mac_2, -+ desc->src_mac_3, desc->src_mac_4, desc->src_mac_5); -+#endif -+} -+ -+static inline int queue_get_desc(unsigned int queue, struct port *port, -+ int is_tx) -+{ -+ u32 phys, tab_phys, n_desc; -+ struct desc *tab; -+ -+ if (!(phys = qmgr_get_entry(queue))) { -+ debug_desc(queue, phys, NULL, 1); -+ return -1; -+ } -+ -+ phys &= ~0x1F; /* mask out non-address bits */ -+ tab_phys = is_tx ? tx_desc_phys(0) : rx_desc_phys(port, 0); -+ tab = is_tx ? tx_desc_tab : port->rx_desc_tab; -+ n_desc = (phys - tab_phys) / sizeof(struct desc); -+ BUG_ON(n_desc >= PKT_DESCS); -+ -+ debug_desc(queue, phys, &tab[n_desc], 1); -+ BUG_ON(tab[n_desc].next); -+ return n_desc; -+} -+ -+static inline void queue_put_desc(unsigned int queue, u32 desc_phys, -+ struct desc *desc) -+{ -+ debug_desc(queue, desc_phys, desc, 0); -+ BUG_ON(desc_phys & 0x1F); -+ qmgr_put_entry(queue, desc_phys); -+} -+ -+ -+static void eth_rx_irq(void *pdev) -+{ -+ struct net_device *dev = pdev; -+ struct port *port = netdev_priv(dev); -+ -+#if DEBUG_RX -+ printk(KERN_DEBUG "eth_rx_irq() start\n"); -+#endif -+ qmgr_disable_irq(port->plat->rxq); -+ netif_rx_schedule(dev); -+} -+ -+static int eth_poll(struct net_device *dev, int *budget) -+{ -+ struct port *port = netdev_priv(dev); -+ unsigned int rxq = port->plat->rxq, rxfreeq = RXFREE_QUEUE(port->plat); -+ int quota = dev->quota, received = 0; -+ -+#if DEBUG_RX -+ printk(KERN_DEBUG "eth_poll() start\n"); -+#endif -+ -+ while (quota) { -+ struct sk_buff *skb; -+ struct desc *desc; -+ int n; -+#ifdef __ARMEB__ -+ struct sk_buff *temp; -+ u32 phys; -+#endif -+ -+ if ((n = queue_get_desc(rxq, port, 0)) < 0) { -+ dev->quota -= received; /* No packet received */ -+ *budget -= received; -+ received = 0; -+ netif_rx_complete(dev); -+ qmgr_enable_irq(rxq); -+ if (!qmgr_stat_empty(rxq) && -+ netif_rx_reschedule(dev, 0)) { -+ qmgr_disable_irq(rxq); -+ continue; -+ } -+ return 0; /* all work done */ -+ } -+ -+ desc = &port->rx_desc_tab[n]; -+ -+#ifdef __ARMEB__ -+ if ((skb = netdev_alloc_skb(dev, MAX_MRU)) != NULL) { -+ phys = dma_map_single(&dev->dev, skb->data, -+ MAX_MRU, DMA_FROM_DEVICE); -+ if (dma_mapping_error(phys)) { -+ dev_kfree_skb(skb); -+ skb = NULL; -+ } -+ } -+#else -+ skb = netdev_alloc_skb(dev, desc->pkt_len); -+#endif -+ -+ if (!skb) { -+ port->stat.rx_dropped++; -+ /* put the desc back on RX-ready queue */ -+ desc->buf_len = MAX_MRU; -+ desc->pkt_len = 0; -+ queue_put_desc(rxfreeq, rx_desc_phys(port, n), desc); -+ BUG_ON(qmgr_stat_overflow(rxfreeq)); -+ continue; -+ } -+ -+ /* process received frame */ -+#ifdef __ARMEB__ -+ temp = skb; -+ skb = port->rx_buff_tab[n]; -+ dma_unmap_single(&dev->dev, desc->data, -+ MAX_MRU, DMA_FROM_DEVICE); -+#else -+ dma_sync_single(&dev->dev, desc->data, -+ MAX_MRU, DMA_FROM_DEVICE); -+ memcpy_swab32((u32 *)skb->data, (u32 *)port->rx_buff_tab[n], -+ ALIGN(desc->pkt_len, 4) / 4); -+#endif -+ skb_put(skb, desc->pkt_len); -+ -+ debug_pkt("eth_poll", skb->data, skb->len); -+ -+ skb->protocol = eth_type_trans(skb, dev); -+ dev->last_rx = jiffies; -+ port->stat.rx_packets++; -+ port->stat.rx_bytes += skb->len; -+ netif_receive_skb(skb); -+ -+ /* put the new buffer on RX-free queue */ -+#ifdef __ARMEB__ -+ port->rx_buff_tab[n] = temp; -+ desc->data = phys; -+#endif -+ desc->buf_len = MAX_MRU; -+ desc->pkt_len = 0; -+ queue_put_desc(rxfreeq, rx_desc_phys(port, n), desc); -+ BUG_ON(qmgr_stat_overflow(rxfreeq)); -+ quota--; -+ received++; -+ } -+ dev->quota -= received; -+ *budget -= received; -+ return 1; /* not all work done */ -+} -+ -+static void eth_xmit_ready_irq(void *pdev) -+{ -+#if DEBUG_TX -+ printk(KERN_DEBUG "eth_xmit_ready_irq()\n"); -+#endif -+ netif_start_queue((struct net_device *)pdev); -+} -+ -+static int eth_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct port *port = netdev_priv(dev); -+ struct desc *desc; -+ void *buff; -+ int n; -+ -+#if DEBUG_TX -+ printk(KERN_DEBUG "eth_xmit() start\n"); -+#endif -+ if (unlikely(skb->len > MAX_MRU)) { -+ dev_kfree_skb(skb); -+ port->stat.tx_errors++; -+ return NETDEV_TX_OK; -+ } -+ -+ n = queue_get_desc(TXDONE_QUEUE, port, 1); -+ BUG_ON(n < 0); -+ desc = &tx_desc_tab[n]; -+ -+ if ((buff = tx_buff_tab[n]) != NULL) { -+ dma_unmap_single(tx_owner_tab[n], desc->data, -+ desc->buf_len, DMA_TO_DEVICE); -+#ifdef __ARMEB__ -+ dev_kfree_skb(buff); -+#else -+ kfree(buff); -+#endif -+ } -+ -+ /* disable VLAN functions in NPE image for now */ -+ memset(desc, 0, sizeof(*desc)); -+ desc->buf_len = desc->pkt_len = skb->len; -+#ifdef __ARMEB__ -+ buff = skb; -+ desc->data = dma_map_single(&dev->dev, skb->data, -+ skb->len, DMA_TO_DEVICE); -+#else -+ if ((buff = kmalloc(ALIGN(skb->len, 4), GFP_ATOMIC)) != NULL) { -+ /* buff must be dword - aligned */ -+ memcpy_swab32(buff, (u32 *)skb->data, ALIGN(skb->len, 4) / 4); -+ desc->data = dma_map_single(&dev->dev, buff, -+ ALIGN(skb->len, 4), DMA_TO_DEVICE); -+ } -+ dev_kfree_skb(skb); -+#endif -+ -+ if (!buff || dma_mapping_error(desc->data)) { -+#ifdef __ARMEB__ -+ dev_kfree_skb(buff); -+#else -+ kfree(buff); -+#endif -+ desc->data = 0; -+ tx_buff_tab[n] = NULL; -+ port->stat.tx_dropped++; -+ /* put the desc back on TX-done queue */ -+ queue_put_desc(TXDONE_QUEUE, tx_desc_phys(n), desc); -+ return NETDEV_TX_OK; -+ } -+ -+ tx_buff_tab[n] = buff; -+ tx_owner_tab[n] = &dev->dev; -+ -+#ifdef __ARMEB__ -+ debug_pkt("eth_xmit", skb->data, desc->pkt_len); -+#else -+ debug_pkt("eth_xmit", buff, desc->pkt_len); -+#endif -+ /* NPE firmware pads short frames with zeros internally */ -+ wmb(); -+ queue_put_desc(TX_QUEUE(port->plat), tx_desc_phys(n), desc); -+ BUG_ON(qmgr_stat_overflow(TX_QUEUE(port->plat))); -+ dev->trans_start = jiffies; -+ port->stat.tx_packets++; -+ port->stat.tx_bytes += desc->pkt_len; -+ -+ if (qmgr_stat_full(TX_QUEUE(port->plat))) { -+ netif_stop_queue(dev); -+ /* we could miss TX ready interrupt */ -+ if (!qmgr_stat_full(TX_QUEUE(port->plat))) -+ netif_start_queue(dev); -+ } -+ -+#if DEBUG_TX -+ printk(KERN_DEBUG "eth_xmit() end\n"); -+#endif -+ return NETDEV_TX_OK; -+} -+ -+ -+static struct net_device_stats *eth_stats(struct net_device *dev) -+{ -+ struct port *port = netdev_priv(dev); -+ return &port->stat; -+} -+ -+static void eth_set_mcast_list(struct net_device *dev) -+{ -+ struct port *port = netdev_priv(dev); -+ struct dev_mc_list *mclist = dev->mc_list; -+ u8 diffs[ETH_ALEN], *addr; -+ int cnt = dev->mc_count, i; -+ -+ if ((dev->flags & IFF_PROMISC) || !mclist || !cnt) { -+ __raw_writel(DEFAULT_RX_CNTRL0 & ~RX_CNTRL0_ADDR_FLTR_EN, -+ &port->regs->rx_control[0]); -+ return; -+ } -+ -+ memset(diffs, 0, ETH_ALEN); -+ addr = mclist->dmi_addr; /* first MAC address */ -+ -+ while (--cnt && (mclist = mclist->next)) -+ for (i = 0; i < ETH_ALEN; i++) -+ diffs[i] |= addr[i] ^ mclist->dmi_addr[i]; -+ -+ for (i = 0; i < ETH_ALEN; i++) { -+ __raw_writel(addr[i], &port->regs->mcast_addr[i]); -+ __raw_writel(~diffs[i], &port->regs->mcast_mask[i]); -+ } -+ -+ __raw_writel(DEFAULT_RX_CNTRL0 | RX_CNTRL0_ADDR_FLTR_EN, -+ &port->regs->rx_control[0]); -+} -+ -+ -+static int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd) -+{ -+ struct port *port = netdev_priv(dev); -+ unsigned int duplex_chg; -+ int err; -+ -+ if (!netif_running(dev)) -+ return -EINVAL; -+ err = generic_mii_ioctl(&port->mii, if_mii(req), cmd, &duplex_chg); -+ if (duplex_chg) -+ eth_set_duplex(port); -+ return err; -+} -+ -+ -+static int request_queues(struct port *port) -+{ -+ int err; -+ -+ err = qmgr_request_queue(RXFREE_QUEUE(port->plat), PKT_DESCS, 0, 0); -+ if (err) -+ return err; -+ -+ err = qmgr_request_queue(port->plat->rxq, PKT_DESCS, 0, 0); -+ if (err) -+ goto rel_rxfree; -+ -+ err = qmgr_request_queue(TX_QUEUE(port->plat), TX_QUEUE_LEN, 0, 0); -+ if (err) -+ goto rel_rx; -+ -+ /* TX-done queue handles skbs sent out by the NPEs */ -+ if (!ports_open) { -+ err = qmgr_request_queue(TXDONE_QUEUE, PKT_DESCS, 0, 0); -+ if (err) -+ goto rel_tx; -+ } -+ return 0; -+ -+rel_tx: -+ qmgr_release_queue(TX_QUEUE(port->plat)); -+rel_rx: -+ qmgr_release_queue(port->plat->rxq); -+rel_rxfree: -+ qmgr_release_queue(RXFREE_QUEUE(port->plat)); -+ return err; -+} -+ -+static void release_queues(struct port *port) -+{ -+ qmgr_release_queue(RXFREE_QUEUE(port->plat)); -+ qmgr_release_queue(port->plat->rxq); -+ qmgr_release_queue(TX_QUEUE(port->plat)); -+ -+ if (!ports_open) -+ qmgr_release_queue(TXDONE_QUEUE); -+} -+ -+static int init_queues(struct port *port) -+{ -+ int i; -+ -+ if (!ports_open) { -+ /* Setup TX descriptors - common to all ports */ -+ if (!(dma_pool = dma_pool_create(DRV_NAME, NULL, -+ POOL_ALLOC_SIZE, 32, 0))) -+ return -ENOMEM; -+ -+ if (!(tx_desc_tab = dma_pool_alloc(dma_pool, GFP_KERNEL, -+ &tx_desc_tab_phys))) -+ return -ENOMEM; -+ memset(tx_desc_tab, 0, POOL_ALLOC_SIZE); -+ memset(tx_buff_tab, 0, sizeof(tx_buff_tab)); /* static table */ -+ } -+ -+ /* Setup RX buffers */ -+ if (!(port->rx_desc_tab = dma_pool_alloc(dma_pool, GFP_KERNEL, -+ &port->rx_desc_tab_phys))) -+ return -ENOMEM; -+ memset(port->rx_desc_tab, 0, POOL_ALLOC_SIZE); -+ memset(port->rx_buff_tab, 0, sizeof(port->rx_buff_tab)); /* table */ -+ -+ for (i = 0; i < PKT_DESCS; i++) { -+ struct desc *desc = &port->rx_desc_tab[i]; -+ void *data; -+#ifdef __ARMEB__ -+ struct sk_buff *skb; -+ -+ if (!(skb = netdev_alloc_skb(port->netdev, MAX_MRU))) -+ return -ENOMEM; -+ port->rx_buff_tab[i] = skb; -+ data = skb->data; -+#else -+ if (!(data = kmalloc(MAX_MRU, GFP_KERNEL))) -+ return -ENOMEM; -+ port->rx_buff_tab[i] = data; -+#endif -+ desc->buf_len = MAX_MRU; -+ desc->data = dma_map_single(&port->netdev->dev, data, -+ MAX_MRU, DMA_FROM_DEVICE); -+ if (dma_mapping_error(desc->data)) { -+ desc->data = 0; -+ return -EIO; -+ } -+ } -+ return 0; -+} -+ -+static void destroy_queues(struct port *port) -+{ -+ int i; -+ -+ if (port->rx_desc_tab) { -+ for (i = 0; i < PKT_DESCS; i++) { -+ struct desc *desc = &port->rx_desc_tab[i]; -+ void *buff = port->rx_buff_tab[i]; /* may be skb */ -+ if (buff) { -+ if (desc->data) -+ dma_unmap_single(&port->netdev->dev, -+ desc->data, MAX_MRU, -+ DMA_FROM_DEVICE); -+#ifdef __ARMEB__ -+ dev_kfree_skb(buff); -+#else -+ kfree(buff); -+#endif -+ } -+ } -+ dma_pool_free(dma_pool, port->rx_desc_tab, -+ port->rx_desc_tab_phys); -+ port->rx_desc_tab = NULL; -+ } -+ -+ if (!ports_open && tx_desc_tab) { -+ for (i = 0; i < PKT_DESCS; i++) { -+ struct desc *desc = &tx_desc_tab[i]; -+ void *buff = tx_buff_tab[i]; /* may be skb */ -+ if (buff) { -+ if (desc->data) -+ dma_unmap_single(&port->netdev->dev, -+ desc->data, -+ desc->buf_len, -+ DMA_TO_DEVICE); -+#ifdef __ARMEB__ -+ dev_kfree_skb(buff); -+#else -+ kfree(buff); -+#endif -+ } -+ } -+ dma_pool_free(dma_pool, tx_desc_tab, tx_desc_tab_phys); -+ tx_desc_tab = NULL; -+ } -+ if (!ports_open && dma_pool) { -+ dma_pool_destroy(dma_pool); -+ dma_pool = NULL; -+ } -+} -+ -+static int eth_open(struct net_device *dev) -+{ -+ struct port *port = netdev_priv(dev); -+ struct npe *npe = port->npe; -+ struct msg msg; -+ int i, err; -+ -+ if (!npe_running(npe)) { -+ err = npe_load_firmware(npe, npe_name(npe), &dev->dev); -+ if (err) -+ return err; -+ -+ if (npe_recv_message(npe, &msg, "ETH_GET_STATUS")) { -+ printk(KERN_ERR "%s: %s not responding\n", dev->name, -+ npe_name(npe)); -+ return -EIO; -+ } -+ } -+ -+ memset(&msg, 0, sizeof(msg)); -+ msg.cmd = NPE_VLAN_SETRXQOSENTRY; -+ msg.eth_id = port->id; -+ msg.byte5 = port->plat->rxq | 0x80; -+ msg.byte7 = port->plat->rxq << 4; -+ for (i = 0; i < 8; i++) { -+ msg.byte3 = i; -+ if (npe_send_recv_message(port->npe, &msg, "ETH_SET_RXQ")) -+ return -EIO; -+ } -+ -+ msg.cmd = NPE_EDB_SETPORTADDRESS; -+ msg.eth_id = PHYSICAL_ID(port); -+ msg.byte2 = dev->dev_addr[0]; -+ msg.byte3 = dev->dev_addr[1]; -+ msg.byte4 = dev->dev_addr[2]; -+ msg.byte5 = dev->dev_addr[3]; -+ msg.byte6 = dev->dev_addr[4]; -+ msg.byte7 = dev->dev_addr[5]; -+ if (npe_send_recv_message(port->npe, &msg, "ETH_SET_MAC")) -+ return -EIO; -+ -+ memset(&msg, 0, sizeof(msg)); -+ msg.cmd = NPE_FW_SETFIREWALLMODE; -+ msg.eth_id = port->id; -+ if (npe_send_recv_message(port->npe, &msg, "ETH_SET_FIREWALL_MODE")) -+ return -EIO; -+ -+ if ((err = request_queues(port)) != 0) -+ return err; -+ -+ if ((err = init_queues(port)) != 0) { -+ destroy_queues(port); -+ release_queues(port); -+ return err; -+ } -+ -+ for (i = 0; i < ETH_ALEN; i++) -+ __raw_writel(dev->dev_addr[i], &port->regs->hw_addr[i]); -+ __raw_writel(0x08, &port->regs->random_seed); -+ __raw_writel(0x12, &port->regs->partial_empty_threshold); -+ __raw_writel(0x30, &port->regs->partial_full_threshold); -+ __raw_writel(0x08, &port->regs->tx_start_bytes); -+ __raw_writel(0x15, &port->regs->tx_deferral); -+ __raw_writel(0x08, &port->regs->tx_2part_deferral[0]); -+ __raw_writel(0x07, &port->regs->tx_2part_deferral[1]); -+ __raw_writel(0x80, &port->regs->slot_time); -+ __raw_writel(0x01, &port->regs->int_clock_threshold); -+ -+ /* Populate queues with buffers, no failure after this point */ -+ if (!ports_open) -+ for (i = 0; i < PKT_DESCS; i++) { -+ queue_put_desc(TXDONE_QUEUE, tx_desc_phys(i), -+ &tx_desc_tab[i]); -+ BUG_ON(qmgr_stat_overflow(TXDONE_QUEUE)); -+ } -+ -+ for (i = 0; i < PKT_DESCS; i++) { -+ queue_put_desc(RXFREE_QUEUE(port->plat), -+ rx_desc_phys(port, i), &port->rx_desc_tab[i]); -+ BUG_ON(qmgr_stat_overflow(RXFREE_QUEUE(port->plat))); -+ } -+ -+ __raw_writel(TX_CNTRL1_RETRIES, &port->regs->tx_control[1]); -+ __raw_writel(DEFAULT_TX_CNTRL0, &port->regs->tx_control[0]); -+ __raw_writel(0, &port->regs->rx_control[1]); -+ __raw_writel(DEFAULT_RX_CNTRL0, &port->regs->rx_control[0]); -+ -+ if (mii_check_media(&port->mii, 1, 1)) -+ eth_set_duplex(port); -+ eth_set_mcast_list(dev); -+ netif_start_queue(dev); -+ schedule_delayed_work(&port->mdio_thread, MDIO_INTERVAL); -+ -+ qmgr_set_irq(port->plat->rxq, QUEUE_IRQ_SRC_NOT_EMPTY, -+ eth_rx_irq, dev); -+ qmgr_set_irq(TX_QUEUE(port->plat), QUEUE_IRQ_SRC_NOT_FULL, -+ eth_xmit_ready_irq, dev); -+ qmgr_enable_irq(TX_QUEUE(port->plat)); -+ ports_open++; -+ netif_rx_schedule(dev); -+ return 0; -+} -+ -+static int eth_close(struct net_device *dev) -+{ -+ struct port *port = netdev_priv(dev); -+ struct msg msg; -+ int buffs = PKT_DESCS; /* allocated RX buffers */ -+ int i; -+ -+ ports_open--; -+ qmgr_disable_irq(port->plat->rxq); -+ qmgr_disable_irq(TX_QUEUE(port->plat)); -+ netif_stop_queue(dev); -+ -+ while (queue_get_desc(RXFREE_QUEUE(port->plat), port, 0) >= 0) -+ buffs--; -+ -+ memset(&msg, 0, sizeof(msg)); -+ msg.cmd = NPE_SETLOOPBACK_MODE; -+ msg.eth_id = port->id; -+ msg.byte3 = 1; -+ if (npe_send_recv_message(port->npe, &msg, "ETH_ENABLE_LOOPBACK")) -+ printk(KERN_CRIT "%s: unable to enable loopback\n", dev->name); -+ -+ i = 0; -+ do { /* drain RX buffers */ -+ while (queue_get_desc(port->plat->rxq, port, 0) >= 0) -+ buffs--; -+ if (!buffs) -+ break; -+ if (qmgr_stat_empty(TX_QUEUE(port->plat))) { -+ /* we have to inject some packet */ -+ int n = queue_get_desc(TXDONE_QUEUE, port, 1); -+ struct desc *desc; -+ u32 phys; -+ -+ BUG_ON(n < 0); -+ desc = &tx_desc_tab[n]; -+ phys = tx_desc_phys(n); -+ desc->buf_len = desc->pkt_len = 1; -+ wmb(); -+ queue_put_desc(TX_QUEUE(port->plat), phys, desc); -+ BUG_ON(qmgr_stat_overflow(TX_QUEUE(port->plat))); -+ } -+ udelay(1); -+ } while (++i < MAX_CLOSE_WAIT); -+ -+ if (buffs) -+ printk(KERN_CRIT "%s: unable to drain RX queue, %i buffer(s)" -+ " left in NPE\n", dev->name, buffs); -+#if DEBUG_CLOSE -+ if (!buffs) -+ printk(KERN_DEBUG "Draining RX queue took %i cycles\n", i); -+#endif -+ -+ msg.byte3 = 0; -+ if (npe_send_recv_message(port->npe, &msg, "ETH_DISABLE_LOOPBACK")) -+ printk(KERN_CRIT "%s: unable to disable loopback\n", -+ dev->name); -+ -+ if (ports_open) { -+ while ((i = queue_get_desc(TX_QUEUE(port->plat), -+ port, 1)) >= 0) { -+ queue_put_desc(TXDONE_QUEUE, tx_desc_phys(i), -+ &tx_desc_tab[i]); -+ BUG_ON(qmgr_stat_overflow(TXDONE_QUEUE)); -+ } -+ } else { -+ buffs = PKT_DESCS; -+ i = 0; -+ while (queue_get_desc(TX_QUEUE(port->plat), port, 1) >= 0) -+ buffs--; /* cancel TX */ -+ do { -+ while (queue_get_desc(TXDONE_QUEUE, port, 1) >= 0) -+ buffs--; -+ if (!buffs) -+ break; -+ } while (++i < MAX_CLOSE_WAIT); -+ -+ if (buffs) -+ printk(KERN_CRIT "%s: unable to drain TX queue, %i" -+ " buffer(s) left in NPE\n", dev->name, buffs); -+#if DEBUG_CLOSE -+ if (!buffs) -+ printk(KERN_DEBUG "Draining TX queues took %i " -+ "cycles\n", i); -+#endif -+ } -+ -+ cancel_rearming_delayed_work(&port->mdio_thread); -+ destroy_queues(port); -+ release_queues(port); -+ return 0; -+} -+ -+static int __devinit eth_init_one(struct platform_device *pdev) -+{ -+ struct port *port; -+ struct net_device *dev; -+ struct mac_plat_info *plat = pdev->dev.platform_data; -+ u32 regs_phys; -+ int err; -+ -+ if (!(dev = alloc_etherdev(sizeof(struct port)))) -+ return -ENOMEM; -+ -+ SET_MODULE_OWNER(dev); -+ SET_NETDEV_DEV(dev, &pdev->dev); -+ port = netdev_priv(dev); -+ port->netdev = dev; -+ port->id = pdev->id; -+ -+ switch (port->id) { -+ case IXP4XX_ETH_NPEA: -+ port->regs = (struct eth_regs __iomem *)IXP4XX_EthA_BASE_VIRT; -+ regs_phys = IXP4XX_EthA_BASE_PHYS; -+ break; -+ case IXP4XX_ETH_NPEB: -+ port->regs = (struct eth_regs __iomem *)IXP4XX_EthB_BASE_VIRT; -+ regs_phys = IXP4XX_EthB_BASE_PHYS; -+ break; -+ case IXP4XX_ETH_NPEC: -+ port->regs = (struct eth_regs __iomem *)IXP4XX_EthC_BASE_VIRT; -+ regs_phys = IXP4XX_EthC_BASE_PHYS; -+ break; -+ default: -+ err = -ENOSYS; -+ goto err_free; -+ } -+ -+ dev->open = eth_open; -+ dev->hard_start_xmit = eth_xmit; -+ dev->poll = eth_poll; -+ dev->stop = eth_close; -+ dev->get_stats = eth_stats; -+ dev->do_ioctl = eth_ioctl; -+ dev->set_multicast_list = eth_set_mcast_list; -+ dev->weight = 16; -+ dev->tx_queue_len = 100; -+ -+ if (!(port->npe = npe_request(NPE_ID(port)))) { -+ err = -EIO; -+ goto err_free; -+ } -+ -+ if (register_netdev(dev)) { -+ err = -EIO; -+ goto err_npe_rel; -+ } -+ -+ port->mem_res = request_mem_region(regs_phys, REGS_SIZE, dev->name); -+ if (!port->mem_res) { -+ err = -EBUSY; -+ goto err_unreg; -+ } -+ -+ port->plat = plat; -+ memcpy(dev->dev_addr, plat->hwaddr, ETH_ALEN); -+ -+ platform_set_drvdata(pdev, dev); -+ -+ __raw_writel(DEFAULT_CORE_CNTRL | CORE_RESET, -+ &port->regs->core_control); -+ udelay(50); -+ __raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control); -+ udelay(50); -+ -+ port->mii.dev = dev; -+ port->mii.mdio_read = mdio_read; -+ port->mii.mdio_write = mdio_write; -+ port->mii.phy_id = plat->phy; -+ port->mii.phy_id_mask = 0x1F; -+ port->mii.reg_num_mask = 0x1F; -+ -+ INIT_DELAYED_WORK(&port->mdio_thread, mdio_thread); -+ -+ printk(KERN_INFO "%s: MII PHY %i on %s\n", dev->name, plat->phy, -+ npe_name(port->npe)); -+ return 0; -+ -+err_unreg: -+ unregister_netdev(dev); -+err_npe_rel: -+ npe_release(port->npe); -+err_free: -+ free_netdev(dev); -+ return err; -+} -+ -+static int __devexit eth_remove_one(struct platform_device *pdev) -+{ -+ struct net_device *dev = platform_get_drvdata(pdev); -+ struct port *port = netdev_priv(dev); -+ -+ unregister_netdev(dev); -+ platform_set_drvdata(pdev, NULL); -+ npe_release(port->npe); -+ release_resource(port->mem_res); -+ free_netdev(dev); -+ return 0; -+} -+ -+static struct platform_driver drv = { -+ .driver.name = DRV_NAME, -+ .probe = eth_init_one, -+ .remove = eth_remove_one, -+}; -+ -+static int __init eth_init_module(void) -+{ -+ if (!(ixp4xx_read_fuses() & IXP4XX_FUSE_NPEB_ETH0)) -+ return -ENOSYS; -+ -+ /* All MII PHY accesses use NPE-B Ethernet registers */ -+ spin_lock_init(&mdio_lock); -+ mdio_regs = (struct eth_regs __iomem *)IXP4XX_EthB_BASE_VIRT; -+ __raw_writel(DEFAULT_CORE_CNTRL, &mdio_regs->core_control); -+ -+ return platform_driver_register(&drv); -+} -+ -+static void __exit eth_cleanup_module(void) -+{ -+ platform_driver_unregister(&drv); -+} -+ -+MODULE_AUTHOR("Krzysztof Halasa"); -+MODULE_DESCRIPTION("Intel IXP4xx Ethernet driver"); -+MODULE_LICENSE("GPL v2"); -+module_init(eth_init_module); -+module_exit(eth_cleanup_module); -Index: linux-2.6.22-rc4-armeb/include/asm-arm/arch-ixp4xx/npe.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22-rc4-armeb/include/asm-arm/arch-ixp4xx/npe.h 2007-06-05 06:26:47.000000000 -0700 -@@ -0,0 +1,41 @@ -+#ifndef __IXP4XX_NPE_H -+#define __IXP4XX_NPE_H -+ -+#include -+#include -+#include -+ -+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 */ -Index: linux-2.6.22-rc4-armeb/include/asm-arm/arch-ixp4xx/platform.h -=================================================================== ---- linux-2.6.22-rc4-armeb.orig/include/asm-arm/arch-ixp4xx/platform.h 2007-06-05 06:26:15.000000000 -0700 -+++ linux-2.6.22-rc4-armeb/include/asm-arm/arch-ixp4xx/platform.h 2007-06-05 06:26:47.000000000 -0700 -@@ -86,6 +86,17 @@ - unsigned long scl_pin; - }; - -+#define IXP4XX_ETH_NPEA 0x00 -+#define IXP4XX_ETH_NPEB 0x10 -+#define IXP4XX_ETH_NPEC 0x20 -+ -+/* Information about built-in Ethernet MAC interfaces */ -+struct mac_plat_info { -+ u8 phy; /* MII PHY ID, 0 - 31 */ -+ u8 rxq; /* configurable, currently 0 - 31 only */ -+ u8 hwaddr[6]; -+}; -+ - /* - * This structure provide a means for the board setup code - * to give information to th pata_ixp4xx driver. It is -Index: linux-2.6.22-rc4-armeb/include/asm-arm/arch-ixp4xx/qmgr.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22-rc4-armeb/include/asm-arm/arch-ixp4xx/qmgr.h 2007-06-05 06:26:47.000000000 -0700 -@@ -0,0 +1,124 @@ -+/* -+ * Copyright (C) 2007 Krzysztof Halasa -+ * -+ * 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 -+#include -+ -+#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 */ -+}; -+ -+extern struct qmgr_regs __iomem *qmgr_regs; -+ -+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) -+{ -+ __raw_writel(val, &qmgr_regs->acc[queue][0]); -+} -+ -+static inline u32 qmgr_get_entry(unsigned int queue) -+{ -+ return __raw_readl(&qmgr_regs->acc[queue][0]); -+} -+ -+static inline int qmgr_get_stat1(unsigned int queue) -+{ -+ return (__raw_readl(&qmgr_regs->stat1[queue >> 3]) -+ >> ((queue & 7) << 2)) & 0xF; -+} -+ -+static inline int qmgr_get_stat2(unsigned int queue) -+{ -+ 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 diff --git a/debian/patches/features/arm/ixp4xx-net-fuses.patch b/debian/patches/features/arm/ixp4xx-net-fuses.patch deleted file mode 100644 index ab5be6337..000000000 --- a/debian/patches/features/arm/ixp4xx-net-fuses.patch +++ /dev/null @@ -1,87 +0,0 @@ -Index: linux-2.6.22-rc3-armeb/arch/arm/mach-ixp4xx/common.c -=================================================================== ---- linux-2.6.22-rc3-armeb.orig/arch/arm/mach-ixp4xx/common.c 2007-05-27 07:07:26.000000000 -0700 -+++ linux-2.6.22-rc3-armeb/arch/arm/mach-ixp4xx/common.c 2007-05-27 07:08:24.000000000 -0700 -@@ -486,3 +486,17 @@ - clockevents_register_device(&clockevent_ixp4xx); - return 0; - } -+ -+/* fuses */ -+ -+u32 ixp4xx_read_fuses(void) -+{ -+ unsigned int fuses = ~*IXP4XX_EXP_CFG2; -+ fuses &= ~IXP4XX_FUSE_RESERVED; -+ if (!cpu_is_ixp46x()) -+ fuses &= ~IXP4XX_FUSE_IXP46X_ONLY; -+ -+ return fuses; -+} -+ -+EXPORT_SYMBOL(ixp4xx_read_fuses); -Index: linux-2.6.22-rc3-armeb/include/asm-arm/arch-ixp4xx/platform.h -=================================================================== ---- linux-2.6.22-rc3-armeb.orig/include/asm-arm/arch-ixp4xx/platform.h 2007-05-27 07:07:26.000000000 -0700 -+++ linux-2.6.22-rc3-armeb/include/asm-arm/arch-ixp4xx/platform.h 2007-05-27 10:18:14.000000000 -0700 -@@ -120,6 +120,47 @@ - extern int ixp4xx_setup(int nr, struct pci_sys_data *sys); - extern struct pci_bus *ixp4xx_scan_bus(int nr, struct pci_sys_data *sys); - -+/* Fuse definitions and functions */ -+ -+/* Fuse Bits of IXP_EXP_CFG2 */ -+#define IXP4XX_FUSE_RCOMP (1 << 0) -+#define IXP4XX_FUSE_USB_DEVICE (1 << 1) -+#define IXP4XX_FUSE_HASH (1 << 2) -+#define IXP4XX_FUSE_AES (1 << 3) -+#define IXP4XX_FUSE_DES (1 << 4) -+#define IXP4XX_FUSE_HDLC (1 << 5) -+#define IXP4XX_FUSE_AAL (1 << 6) -+#define IXP4XX_FUSE_HSS (1 << 7) -+#define IXP4XX_FUSE_UTOPIA (1 << 8) -+#define IXP4XX_FUSE_NPEB_ETH0 (1 << 9) -+#define IXP4XX_FUSE_NPEC_ETH (1 << 10) -+#define IXP4XX_FUSE_RESET_NPEA (1 << 11) -+#define IXP4XX_FUSE_RESET_NPEB (1 << 12) -+#define IXP4XX_FUSE_RESET_NPEC (1 << 13) -+#define IXP4XX_FUSE_PCI (1 << 14) -+#define IXP4XX_FUSE_ECC_TIMESYNC (1 << 15) -+#define IXP4XX_FUSE_UTOPIA_PHY_LIMIT (3 << 16) -+#define IXP4XX_FUSE_USB_HOST (1 << 18) -+#define IXP4XX_FUSE_NPEA_ETH (1 << 19) -+#define IXP4XX_FUSE_NPEB_ETH_1_TO_3 (1 << 20) -+#define IXP4XX_FUSE_RSA (1 << 21) -+#define IXP4XX_FUSE_XSCALE_MAX_FREQ (3 << 22) -+#define IXP4XX_FUSE_RESERVED (0xFF << 24) -+ -+#define IXP4XX_FUSE_IXP46X_ONLY (IXP4XX_FUSE_ECC_TIMESYNC | \ -+ IXP4XX_FUSE_USB_HOST | \ -+ IXP4XX_FUSE_NPEA_ETH | \ -+ IXP4XX_FUSE_NPEB_ETH_1_TO_3 | \ -+ IXP4XX_FUSE_RSA | \ -+ IXP4XX_FUSE_XSCALE_MAX_FREQ) -+ -+extern u32 ixp4xx_read_fuses(void); -+ -+static inline void ixp4xx_write_fuses(u32 value) -+{ -+ *IXP4XX_EXP_CFG2 = ~value; -+} -+ - /* - * GPIO-functions - */ -Index: linux-2.6.22-rc3-armeb/arch/arm/kernel/setup.c -=================================================================== ---- linux-2.6.22-rc3-armeb.orig/arch/arm/kernel/setup.c 2007-05-27 07:07:26.000000000 -0700 -+++ linux-2.6.22-rc3-armeb/arch/arm/kernel/setup.c 2007-05-27 07:08:24.000000000 -0700 -@@ -60,6 +60,8 @@ - extern void _stext, _text, _etext, __data_start, _edata, _end; - - unsigned int processor_id; -+EXPORT_SYMBOL(processor_id); -+ - unsigned int __machine_arch_type; - EXPORT_SYMBOL(__machine_arch_type); - diff --git a/debian/patches/features/arm/ixp4xx-npe-driver-0.3.1.patch b/debian/patches/features/arm/ixp4xx-npe-driver-0.3.1.patch new file mode 100644 index 000000000..165043cf0 --- /dev/null +++ b/debian/patches/features/arm/ixp4xx-npe-driver-0.3.1.patch @@ -0,0 +1,4923 @@ +diff --git a/Documentation/networking/ixp4xx/IxNpeMicrocode.h b/Documentation/networking/ixp4xx/IxNpeMicrocode.h +new file mode 100644 +index 0000000..b342b64 +--- /dev/null ++++ b/Documentation/networking/ixp4xx/IxNpeMicrocode.h +@@ -0,0 +1,143 @@ ++/* ++ * IxNpeMicrocode.h - Headerfile for compiling the Intel microcode C file ++ * ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ * ++ * ++ * compile with ++ * ++ * gcc -Wall IxNpeMicrocode.c -o IxNpeMicrocode ++ * ++ * Executing the resulting binary on your build-host creates the ++ * "NPE-[ABC].xxxxxxxx" files containing the selected microcode ++ * ++ * fetch the IxNpeMicrocode.c from the Intel Access Library. ++ * It will include this header. ++ * ++ * select Images for every NPE from the following ++ * (used C++ comments for easy uncommenting ....) ++ */ ++ ++// #define IX_NPEDL_NPEIMAGE_NPEA_ETH_SPAN_MASK_FIREWALL_VLAN_QOS_HDR_CONV_EXTMIB ++// #define IX_NPEDL_NPEIMAGE_NPEA_ETH_SPAN_VLAN_QOS_HDR_CONV_EXTMIB ++// #define IX_NPEDL_NPEIMAGE_NPEA_ETH_LEARN_FILTER_SPAN_MASK_FIREWALL_VLAN_QOS_EXTMIB ++// #define IX_NPEDL_NPEIMAGE_NPEA_HSS_TSLOT_SWITCH ++// #define IX_NPEDL_NPEIMAGE_NPEA_ETH_SPAN_FIREWALL_VLAN_QOS_HDR_CONV ++// #define IX_NPEDL_NPEIMAGE_NPEA_ETH_LEARN_FILTER_SPAN_FIREWALL_VLAN_QOS ++// #define IX_NPEDL_NPEIMAGE_NPEA_ETH_LEARN_FILTER_SPAN_FIREWALL ++// #define IX_NPEDL_NPEIMAGE_NPEA_HSS_2_PORT ++// #define IX_NPEDL_NPEIMAGE_NPEA_DMA ++// #define IX_NPEDL_NPEIMAGE_NPEA_ATM_MPHY_12_PORT ++// #define IX_NPEDL_NPEIMAGE_NPEA_HSS0_ATM_MPHY_1_PORT ++// #define IX_NPEDL_NPEIMAGE_NPEA_HSS0_ATM_SPHY_1_PORT ++// #define IX_NPEDL_NPEIMAGE_NPEA_HSS0 ++// #define IX_NPEDL_NPEIMAGE_NPEA_WEP ++ ++ ++// #define IX_NPEDL_NPEIMAGE_NPEB_ETH_SPAN_MASK_FIREWALL_VLAN_QOS_HDR_CONV_EXTMIB ++//#define IX_NPEDL_NPEIMAGE_NPEB_ETH_SPAN_VLAN_QOS_HDR_CONV_EXTMIB ++// #define IX_NPEDL_NPEIMAGE_NPEB_ETH_LEARN_FILTER_SPAN_MASK_FIREWALL_VLAN_QOS_EXTMIB ++// #define IX_NPEDL_NPEIMAGE_NPEB_DMA ++// #define IX_NPEDL_NPEIMAGE_NPEB_ETH_SPAN_FIREWALL_VLAN_QOS_HDR_CONV ++// #define IX_NPEDL_NPEIMAGE_NPEB_ETH_LEARN_FILTER_SPAN_FIREWALL_VLAN_QOS ++ #define IX_NPEDL_NPEIMAGE_NPEB_ETH_LEARN_FILTER_SPAN_FIREWALL ++ ++ ++// #define IX_NPEDL_NPEIMAGE_NPEC_ETH_SPAN_MASK_FIREWALL_VLAN_QOS_HDR_CONV_EXTMIB ++// #define IX_NPEDL_NPEIMAGE_NPEC_ETH_SPAN_VLAN_QOS_HDR_CONV_EXTMIB ++// #define IX_NPEDL_NPEIMAGE_NPEC_ETH_LEARN_FILTER_SPAN_MASK_FIREWALL_VLAN_QOS_EXTMIB ++// #define IX_NPEDL_NPEIMAGE_NPEC_DMA ++// #define IX_NPEDL_NPEIMAGE_NPEC_CRYPTO_AES_ETH_LEARN_FILTER_SPAN ++// #define IX_NPEDL_NPEIMAGE_NPEC_CRYPTO_AES_ETH_LEARN_FILTER_FIREWALL ++ #define IX_NPEDL_NPEIMAGE_NPEC_CRYPTO_AES_CCM_ETH ++// #define IX_NPEDL_NPEIMAGE_NPEC_CRYPTO_ETH_LEARN_FILTER_SPAN_FIREWALL ++// #define IX_NPEDL_NPEIMAGE_NPEC_ETH_SPAN_FIREWALL_VLAN_QOS_HDR_CONV ++// #define IX_NPEDL_NPEIMAGE_NPEC_ETH_LEARN_FILTER_SPAN_FIREWALL_VLAN_QOS ++// #define IX_NPEDL_NPEIMAGE_NPEC_ETH_LEARN_FILTER_SPAN_FIREWALL ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++#define to_le32(x) (x) ++#define to_be32(x) bswap_32(x) ++#else ++#define to_be32(x) (x) ++#define to_le32(x) bswap_32(x) ++#endif ++ ++struct dl_image { ++ unsigned magic; ++ unsigned id; ++ unsigned size; ++ unsigned data[0]; ++}; ++ ++const unsigned IxNpeMicrocode_array[]; ++ ++int main(int argc, char *argv[]) ++{ ++ struct dl_image *image = (struct dl_image *)IxNpeMicrocode_array; ++ int imgsiz, i, fd, cnt; ++ const unsigned *arrayptr = IxNpeMicrocode_array; ++ const char *names[] = { "IXP425", "IXP465", "unknown" }; ++ int bigendian = 1; ++ ++ if (argc > 1) { ++ if (!strcmp(argv[1], "-le")) ++ bigendian = 0; ++ else if (!strcmp(argv[1], "-be")) ++ bigendian = 1; ++ else { ++ printf("Usage: %s <-le|-be>\n", argv[0]); ++ return EXIT_FAILURE; ++ } ++ } ++ ++ for (image = (struct dl_image *)arrayptr, cnt=0; ++ (image->id != 0xfeedf00d) && (image->magic == 0xfeedf00d); ++ image = (struct dl_image *)(arrayptr), cnt++) ++ { ++ unsigned char field[4]; ++ imgsiz = image->size + 3; ++ *(unsigned*)field = to_be32(image->id); ++ char filename[40], slnk[10]; ++ ++ sprintf(filename, "NPE-%c.%08x", (field[0] & 0xf) + 'A', ++ image->id); ++ sprintf(slnk, "NPE-%c", (field[0] & 0xf) + 'A'); ++ printf("Writing image: %s.NPE_%c Func: %2x Rev: %02x.%02x " ++ "Size: %5d to: '%s'\n", ++ names[field[0] >> 4], (field[0] & 0xf) + 'A', ++ field[1], field[2], field[3], imgsiz*4, filename); ++ fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, 0644); ++ if (fd >= 0) { ++ for (i=0; i /dev/misc/npe ++ This also works if the driver is linked to the kernel ++ ++ Having a mix of both (e.g. solution 1 for NPE-B and solution 2 for NPE-C) ++ is perfectly ok and works. ++ ++ The state of the NPEs can be seen and changed at: ++ /sys/bus/platform/devices/ixp4xx_npe.X/state ++ ++ ++Obtaining the Microcode: ++------------------------ ++1) IxNpeMicrocode.h in this directory: ++ Download IPL_IXP400NPELIBRARYWITHCRYPTO-2_1.ZIP from Intel ++ It unpacks the Microcode IxNpeMicrocode.c ++ Read the Licence ! ++ Compile it with "gcc -Wall IxNpeMicrocode.c -o IxNpeMicrocode" on your host. ++ The resulting images can be moved to "/usr/lib/hotplug/firmware" ++ ++2) mc_grab.c in this directory: ++ Compile and execute it either on the host or on the target ++ to grab the microcode from a binary image like the RedBoot bootloader. ++ ++ +diff --git a/Documentation/networking/ixp4xx/mc_grab.c b/Documentation/networking/ixp4xx/mc_grab.c +new file mode 100644 +index 0000000..107fbb5 +--- /dev/null ++++ b/Documentation/networking/ixp4xx/mc_grab.c +@@ -0,0 +1,97 @@ ++/* ++ * mc_grab.c - grabs IXP4XX microcode from a binary datastream ++ * e.g. The redboot bootloader.... ++ * ++ * usage: mc_grab 1010200 2010200 < /dev/mtd/0 > /dev/misc/npe ++ * ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MAX_IMG 6 ++ ++static void print_mc_info(unsigned id, int siz) ++{ ++ unsigned char buf[sizeof(unsigned)]; ++ *(unsigned*)buf = id; ++ unsigned idx; ++ const char *names[] = { "IXP425", "IXP465", "unknown" }; ++ ++ idx = (buf[0] >> 4) < 2 ? (buf[0] >> 4) : 2; ++ ++ fprintf(stderr, "Device: %s:NPE_%c Func: %2x Rev: %02x.%02x " ++ "Size: %5d bytes ID:%08x\n", names[idx], (buf[0] & 0xf)+'A', ++ buf[1], buf[2], buf[3], siz*4, ntohl(id)); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int i,j; ++ unsigned char buf[sizeof(unsigned)]; ++ unsigned magic = htonl(0xfeedf00d); ++ unsigned id, my_ids[MAX_IMG+1], siz, sizbe; ++ int ret=1, verbose=0; ++ ++ for (i=0, j=0; i [ID1] [ID2] [IDn]\n", argv[0]); ++ return 1; ++ } ++ ++ while ((ret=read(0, buf, sizeof(unsigned))) == sizeof(unsigned)) { ++ if (*(unsigned*)buf != magic) ++ continue; ++ if ((ret=read(0, buf, sizeof(unsigned))) != sizeof(unsigned) ) ++ break; ++ id = *(unsigned*)buf; ++ ++ if (read(0, buf, sizeof(siz)) != sizeof(siz) ) ++ break; ++ sizbe = *(unsigned*)buf; ++ siz = ntohl(sizbe); ++ ++ if (verbose) ++ print_mc_info(id, siz); ++ ++ for(i=0; my_ids[i]; i++) ++ if (id == my_ids[i]) ++ break; ++ if (!my_ids[i]) ++ continue; ++ ++ if (!verbose) ++ print_mc_info(id, siz); ++ ++ write(1, &magic, sizeof(magic)); ++ write(1, &id, sizeof(id)); ++ write(1, &sizbe, sizeof(sizbe)); ++ for (i=0; i + #include + #include ++#include + + #include + #include +@@ -371,6 +372,90 @@ static struct platform_device *ixp46x_devices[] __initdata = { + &ixp46x_i2c_controller + }; + ++static struct npe_plat_data npea = { ++ .name = "NPE-A", ++ .data_size = 0x800, ++ .inst_size = 0x1000, ++ .id = 0, ++}; ++ ++static struct npe_plat_data npeb = { ++ .name = "NPE-B", ++ .data_size = 0x800, ++ .inst_size = 0x800, ++ .id = 1, ++}; ++ ++static struct npe_plat_data npec = { ++ .name = "NPE-C", ++ .data_size = 0x800, ++ .inst_size = 0x800, ++ .id = 2, ++}; ++ ++static struct resource res_npea = { ++ .start = IXP4XX_NPEA_BASE_PHYS, ++ .end = IXP4XX_NPEA_BASE_PHYS + 0xfff, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct resource res_npeb = { ++ .start = IXP4XX_NPEB_BASE_PHYS, ++ .end = IXP4XX_NPEB_BASE_PHYS + 0xfff, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct resource res_npec = { ++ .start = IXP4XX_NPEC_BASE_PHYS, ++ .end = IXP4XX_NPEC_BASE_PHYS + 0xfff, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct platform_device dev_npea = { ++ .name = "ixp4xx_npe", ++ .id = 0, ++ .dev.platform_data = &npea, ++ .num_resources = 1, ++ .resource = &res_npea, ++}; ++ ++static struct platform_device dev_npeb = { ++ .name = "ixp4xx_npe", ++ .id = 1, ++ .dev.platform_data = &npeb, ++ .num_resources = 1, ++ .resource = &res_npeb, ++}; ++ ++static struct platform_device dev_npec = { ++ .name = "ixp4xx_npe", ++ .id = 2, ++ .dev.platform_data = &npec, ++ .num_resources = 1, ++ .resource = &res_npec, ++}; ++ ++/* QMGR */ ++static struct resource res_qmgr[] = { ++{ ++ .start = IXP4XX_QMGR_BASE_PHYS, ++ .end = IXP4XX_QMGR_BASE_PHYS + IXP4XX_QMGR_REGION_SIZE -1, ++ .flags = IORESOURCE_MEM, ++}, { ++ .start = IRQ_IXP4XX_QM1, ++ .flags = IORESOURCE_IRQ, ++} }; ++ ++static struct platform_device qmgr = { ++ .name = "ixp4xx_qmgr", ++ .id = 0, ++ .dev = { ++ .coherent_dma_mask = DMA_32BIT_MASK, ++ }, ++ .num_resources = ARRAY_SIZE(res_qmgr), ++ .resource = res_qmgr, ++}; ++ + unsigned long ixp4xx_exp_bus_size; + EXPORT_SYMBOL(ixp4xx_exp_bus_size); + +@@ -392,8 +477,19 @@ void __init ixp4xx_sys_init(void) + break; + } + } ++ npeb.inst_size = 0x1000; ++ npec.inst_size = 0x1000; + } + ++ platform_device_register(&qmgr); ++ ++ if (ix_fuse() & IX_FUSE_NPEA) ++ platform_device_register(&dev_npea); ++ if (ix_fuse() & IX_FUSE_NPEB) ++ platform_device_register(&dev_npeb); ++ if (ix_fuse() & IX_FUSE_NPEC) ++ platform_device_register(&dev_npec); ++ + printk("IXP4xx: Using %luMiB expansion bus window size\n", + ixp4xx_exp_bus_size >> 20); + } +@@ -486,3 +582,16 @@ static int __init ixp4xx_clockevent_init(void) + clockevents_register_device(&clockevent_ixp4xx); + return 0; + } ++ ++/* fuses */ ++ ++u32 ix_fuse(void) ++{ ++ unsigned int fuses = ~(*IXP4XX_EXP_CFG2); ++ if (!cpu_is_ixp46x()) ++ fuses &= ~IX_FUSE_IXP46X_ONLY; ++ ++ return fuses; ++} ++ ++EXPORT_SYMBOL(ix_fuse); +diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c +index ec4f079..21818a1 100644 +--- a/arch/arm/mach-ixp4xx/ixdp425-setup.c ++++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c +@@ -101,10 +101,59 @@ static struct platform_device ixdp425_uart = { + .resource = ixdp425_uart_resources + }; + ++/* MACs */ ++static struct resource res_mac0 = { ++ .start = IXP4XX_EthB_BASE_PHYS, ++ .end = IXP4XX_EthB_BASE_PHYS + 0x1ff, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct resource res_mac1 = { ++ .start = IXP4XX_EthC_BASE_PHYS, ++ .end = IXP4XX_EthC_BASE_PHYS + 0x1ff, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct mac_plat_info plat_mac0 = { ++ .npe_id = 1, ++ .phy_id = 0, ++ .eth_id = 0, ++ .rxq_id = 27, ++ .txq_id = 24, ++ .rxdoneq_id = 4, ++}; ++ ++static struct mac_plat_info plat_mac1 = { ++ .npe_id = 2, ++ .phy_id = 1, ++ .eth_id = 1, ++ .rxq_id = 28, ++ .txq_id = 25, ++ .rxdoneq_id = 5, ++}; ++ ++static struct platform_device mac0 = { ++ .name = "ixp4xx_mac", ++ .id = 0, ++ .dev.platform_data = &plat_mac0, ++ .num_resources = 1, ++ .resource = &res_mac0, ++}; ++ ++static struct platform_device mac1 = { ++ .name = "ixp4xx_mac", ++ .id = 1, ++ .dev.platform_data = &plat_mac1, ++ .num_resources = 1, ++ .resource = &res_mac1, ++}; ++ + static struct platform_device *ixdp425_devices[] __initdata = { + &ixdp425_i2c_controller, + &ixdp425_flash, +- &ixdp425_uart ++ &ixdp425_uart, ++ &mac0, ++ &mac1, + }; + + static void __init ixdp425_init(void) +diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig +index 7d57f4a..ba1a25e 100644 +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -201,6 +201,8 @@ config MACB + + source "drivers/net/arm/Kconfig" + ++source "drivers/net/ixp4xx/Kconfig" ++ + config MACE + tristate "MACE (Power Mac ethernet) support" + depends on NET_ETHERNET && PPC_PMAC && PPC32 +diff --git a/drivers/net/Makefile b/drivers/net/Makefile +index a77affa..c463574 100644 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -221,6 +221,7 @@ obj-$(CONFIG_HAMRADIO) += hamradio/ + obj-$(CONFIG_IRDA) += irda/ + obj-$(CONFIG_ETRAX_ETHERNET) += cris/ + obj-$(CONFIG_ENP2611_MSF_NET) += ixp2000/ ++obj-$(CONFIG_IXP4XX_NPE) += ixp4xx/ + + obj-$(CONFIG_NETCONSOLE) += netconsole.o + +diff --git a/drivers/net/ixp4xx/Kconfig b/drivers/net/ixp4xx/Kconfig +new file mode 100644 +index 0000000..2f2b527 +--- /dev/null ++++ b/drivers/net/ixp4xx/Kconfig +@@ -0,0 +1,48 @@ ++config IXP4XX_QMGR ++ tristate "IXP4xx Queue Manager support" ++ depends on ARCH_IXP4XX ++ depends on NET_ETHERNET ++ help ++ The IXP4XX Queue manager is a configurable hardware ringbuffer. ++ It is used by the NPEs to exchange data from and to the CPU. ++ You can either use this OR the Intel Access Library (IAL) ++ ++config IXP4XX_NPE ++ tristate "IXP4xx NPE support" ++ depends on ARCH_IXP4XX ++ depends on NET_ETHERNET ++ help ++ The IXP4XX NPE driver supports the 3 CPU co-processors called ++ "Network Processing Engines" (NPE). It adds support fo downloading ++ the Microcode (firmware) via Hotplug or character-special-device. ++ More about this at: Documentation/networking/ixp4xx/README. ++ You can either use this OR the Intel Access Library (IAL) ++ ++config IXP4XX_FW_LOAD ++ bool "Use Firmware hotplug for Microcode download" ++ depends on IXP4XX_NPE ++ select HOTPLUG ++ select FW_LOADER ++ help ++ The default hotplug script will load the Firmware from ++ /usr/lib/hotplug/firmware/NPE-[ABC] ++ see Documentation/firmware_class/hotplug-script ++ ++config IXP4XX_MAC ++ tristate "IXP4xx MAC support" ++ depends on IXP4XX_NPE ++ depends on IXP4XX_QMGR ++ depends on NET_ETHERNET ++ select MII ++ help ++ The IXP4XX MAC driver supports the MACs on the IXP4XX CPUs. ++ There are 2 on ixp425 and up to 5 on ixdp465. ++ You can either use this OR the Intel Access Library (IAL) ++ ++config IXP4XX_CRYPTO ++ tristate "IXP4xx crypto support" ++ depends on IXP4XX_NPE ++ depends on IXP4XX_QMGR ++ help ++ This driver is a generic NPE-crypto access layer. ++ You need additional code in OCF for example. +diff --git a/drivers/net/ixp4xx/Makefile b/drivers/net/ixp4xx/Makefile +new file mode 100644 +index 0000000..0e04af3 +--- /dev/null ++++ b/drivers/net/ixp4xx/Makefile +@@ -0,0 +1,7 @@ ++obj-$(CONFIG_IXP4XX_QMGR) += ixp4xx_qmgr.o ++obj-$(CONFIG_IXP4XX_NPE) += ixp4xx_npe.o ++obj-$(CONFIG_IXP4XX_MAC) += ixp4xx_mac.o ++obj-$(CONFIG_IXP4XX_CRYPTO) += ixp4xx_crypto.o ++ ++ixp4xx_npe-objs := ucode_dl.o npe_mh.o npe.o ++ixp4xx_mac-objs := mac_driver.o phy.o +diff --git a/drivers/net/ixp4xx/ixp4xx_crypto.c b/drivers/net/ixp4xx/ixp4xx_crypto.c +new file mode 100644 +index 0000000..3848b0e +--- /dev/null ++++ b/drivers/net/ixp4xx/ixp4xx_crypto.c +@@ -0,0 +1,851 @@ ++/* ++ * ixp4xx_crypto.c - interface to the HW crypto ++ * ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SEND_QID 29 ++#define RECV_QID 30 ++ ++#define NPE_ID 2 /* NPE C */ ++ ++#define QUEUE_SIZE 64 ++#define MY_VERSION "0.0.1" ++ ++/* local head for all sa_ctx */ ++static struct ix_sa_master sa_master; ++ ++static const struct ix_hash_algo _hash_algos[] = { ++{ ++ .name = "MD5", ++ .cfgword = 0xAA010004, ++ .digest_len = 16, ++ .icv = "\x01\x23\x45\x67\x89\xAB\xCD\xEF" ++ "\xFE\xDC\xBA\x98\x76\x54\x32\x10", ++ .type = HASH_TYPE_MD5, ++},{ ++ .name = "SHA1", ++ .cfgword = 0x00000005, ++ .digest_len = 20, ++ .icv = "\x67\x45\x23\x01\xEF\xCD\xAB\x89\x98\xBA" ++ "\xDC\xFE\x10\x32\x54\x76\xC3\xD2\xE1\xF0", ++ .type = HASH_TYPE_SHA1, ++#if 0 ++},{ ++ .name = "CBC MAC", ++ .digest_len = 64, ++ .aad_len = 48, ++ .type = HASH_TYPE_CBCMAC, ++#endif ++} }; ++ ++static const struct ix_cipher_algo _cipher_algos[] = { ++{ ++ .name = "DES ECB", ++ .cfgword_enc = CIPH_ENCR | MOD_DES | MOD_ECB | KEYLEN_192, ++ .cfgword_dec = CIPH_DECR | MOD_DES | MOD_ECB | KEYLEN_192, ++ .block_len = 8, ++ .type = CIPHER_TYPE_DES, ++ .mode = CIPHER_MODE_ECB, ++},{ ++ .name = "DES CBC", ++ .cfgword_enc = CIPH_ENCR | MOD_DES | MOD_CBC_ENC | KEYLEN_192, ++ .cfgword_dec = CIPH_DECR | MOD_DES | MOD_CBC_DEC | KEYLEN_192, ++ .iv_len = 8, ++ .block_len = 8, ++ .type = CIPHER_TYPE_DES, ++ .mode = CIPHER_MODE_CBC, ++},{ ++ .name = "3DES ECB", ++ .cfgword_enc = CIPH_ENCR | MOD_TDEA3 | MOD_ECB | KEYLEN_192, ++ .cfgword_dec = CIPH_DECR | MOD_TDEA3 | MOD_ECB | KEYLEN_192, ++ .block_len = 8, ++ .type = CIPHER_TYPE_3DES, ++ .mode = CIPHER_MODE_ECB, ++},{ ++ .name = "3DES CBC", ++ .cfgword_enc = CIPH_ENCR | MOD_TDEA3 | MOD_CBC_ENC | KEYLEN_192, ++ .cfgword_dec = CIPH_DECR | MOD_TDEA3 | MOD_CBC_DEC | KEYLEN_192, ++ .iv_len = 8, ++ .block_len = 8, ++ .type = CIPHER_TYPE_3DES, ++ .mode = CIPHER_MODE_CBC, ++},{ ++ .name = "AES ECB", ++ .cfgword_enc = CIPH_ENCR | ALGO_AES | MOD_ECB, ++ .cfgword_dec = CIPH_DECR | ALGO_AES | MOD_ECB, ++ .block_len = 16, ++ .type = CIPHER_TYPE_AES, ++ .mode = CIPHER_MODE_ECB, ++},{ ++ .name = "AES CBC", ++ .cfgword_enc = CIPH_ENCR | ALGO_AES | MOD_CBC_ENC, ++ .cfgword_dec = CIPH_DECR | ALGO_AES | MOD_CBC_DEC, ++ .block_len = 16, ++ .iv_len = 16, ++ .type = CIPHER_TYPE_AES, ++ .mode = CIPHER_MODE_CBC, ++},{ ++ .name = "AES CTR", ++ .cfgword_enc = CIPH_ENCR | ALGO_AES | MOD_CTR, ++ .cfgword_dec = CIPH_ENCR | ALGO_AES | MOD_CTR, ++ .block_len = 16, ++ .iv_len = 16, ++ .type = CIPHER_TYPE_AES, ++ .mode = CIPHER_MODE_CTR, ++#if 0 ++},{ ++ .name = "AES CCM", ++ .cfgword_enc = CIPH_ENCR | ALGO_AES | MOD_CCM_ENC, ++ .cfgword_dec = CIPH_ENCR | ALGO_AES | MOD_CCM_DEC, ++ .block_len = 16, ++ .iv_len = 16, ++ .type = CIPHER_TYPE_AES, ++ .mode = CIPHER_MODE_CCM, ++#endif ++} }; ++ ++const struct ix_hash_algo *ix_hash_by_id(int type) ++{ ++ int i; ++ ++ for(i=0; inpe_dev = get_npe_by_id(NPE_ID); ++ if (! master->npe_dev) ++ goto err; ++ ++ npe = dev_get_drvdata(master->npe_dev); ++ ++ if (npe_status(npe) & IX_NPEDL_EXCTL_STATUS_RUN) { ++ switch (npe->img_info[1]) { ++ case 4: ++ printk(KERN_INFO "Crypto AES avaialable\n"); ++ break; ++ case 5: ++ printk(KERN_INFO "Crypto AES and CCM avaialable\n"); ++ break; ++ default: ++ printk(KERN_WARNING "Current microcode for %s has no" ++ " crypto capabilities\n", npe->plat->name); ++ break; ++ } ++ } ++ rwlock_init(&master->lock); ++ master->dmapool = dma_pool_create("ixp4xx_crypto", master->npe_dev, ++ sizeof(struct npe_crypt_cont), 32, 0); ++ if (!master->dmapool) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ master->sendq = request_queue(SEND_QID, QUEUE_SIZE); ++ if (IS_ERR(master->sendq)) { ++ printk(KERN_ERR "ixp4xx_crypto: Error requesting Q: %d\n", ++ SEND_QID); ++ ret = PTR_ERR(master->sendq); ++ goto err; ++ } ++ master->recvq = request_queue(RECV_QID, QUEUE_SIZE); ++ if (IS_ERR(master->recvq)) { ++ printk(KERN_ERR "ixp4xx_crypto: Error requesting Q: %d\n", ++ RECV_QID); ++ ret = PTR_ERR(master->recvq); ++ release_queue(master->sendq); ++ goto err; ++ } ++ ++ master->recvq->irq_cb = irqcb_recv; ++ queue_set_watermarks(master->recvq, 0, 0); ++ queue_set_irq_src(master->recvq, Q_IRQ_ID_NOT_E); ++ queue_enable_irq(master->recvq); ++ printk(KERN_INFO "ixp4xx_crypto " MY_VERSION " registered successfully\n"); ++ ++ return 0; ++err: ++ if (master->dmapool) ++ dma_pool_destroy(master->dmapool); ++ if (! master->npe_dev) ++ put_device(master->npe_dev); ++ return ret; ++ ++} ++ ++static void release_sa_master(struct ix_sa_master *master) ++{ ++ struct npe_crypt_cont *cont; ++ unsigned long flags; ++ ++ write_lock_irqsave(&master->lock, flags); ++ while (master->pool) { ++ cont = master->pool; ++ master->pool = cont->next; ++ dma_pool_free(master->dmapool, cont, cont->phys); ++ master->pool_size--; ++ } ++ write_unlock_irqrestore(&master->lock, flags); ++ if (master->pool_size) { ++ printk(KERN_ERR "ixp4xx_crypto: %d items lost from DMA pool\n", ++ master->pool_size); ++ } ++ ++ dma_pool_destroy(master->dmapool); ++ release_queue(master->sendq); ++ release_queue(master->recvq); ++ return_npe_dev(master->npe_dev); ++} ++ ++static struct npe_crypt_cont *ix_sa_get_cont(struct ix_sa_master *master) ++{ ++ unsigned long flags; ++ struct npe_crypt_cont *cont; ++ dma_addr_t handle; ++ ++ write_lock_irqsave(&master->lock, flags); ++ if (!master->pool) { ++ cont = dma_pool_alloc(master->dmapool, GFP_ATOMIC, &handle); ++ if (cont) { ++ master->pool_size++; ++ cont->phys = handle; ++ cont->virt = cont; ++ } ++ } else { ++ cont = master->pool; ++ master->pool = cont->next; ++ } ++ write_unlock_irqrestore(&master->lock, flags); ++ return cont; ++} ++ ++static void ++ix_sa_return_cont(struct ix_sa_master *master,struct npe_crypt_cont *cont) ++{ ++ unsigned long flags; ++ ++ write_lock_irqsave(&master->lock, flags); ++ cont->next = master->pool; ++ master->pool = cont; ++ write_unlock_irqrestore(&master->lock, flags); ++} ++ ++static void free_sa_dir(struct ix_sa_ctx *sa_ctx, struct ix_sa_dir *dir) ++{ ++ memset(dir->npe_ctx, 0, NPE_CTX_LEN); ++ dma_pool_free(sa_ctx->master->dmapool, dir->npe_ctx, ++ dir->npe_ctx_phys); ++} ++ ++static void ix_sa_ctx_destroy(struct ix_sa_ctx *sa_ctx) ++{ ++ BUG_ON(sa_ctx->state != STATE_UNLOADING); ++ free_sa_dir(sa_ctx, &sa_ctx->encrypt); ++ free_sa_dir(sa_ctx, &sa_ctx->decrypt); ++ kfree(sa_ctx); ++ module_put(THIS_MODULE); ++} ++ ++static void recv_pack(struct qm_queue *queue, u32 phys) ++{ ++ struct ix_sa_ctx *sa_ctx; ++ struct npe_crypt_cont *cr_cont; ++ struct npe_cont *cont; ++ int failed; ++ ++ failed = phys & 0x1; ++ phys &= ~0x3; ++ ++ cr_cont = dma_to_virt(queue->dev, phys); ++ cr_cont = cr_cont->virt; ++ sa_ctx = cr_cont->ctl.crypt.sa_ctx; ++ ++ phys = npe_to_cpu32(cr_cont->ctl.crypt.src_buf); ++ if (phys) { ++ cont = dma_to_virt(queue->dev, phys); ++ cont = cont->virt; ++ } else { ++ cont = NULL; ++ } ++ if (cr_cont->ctl.crypt.oper_type == OP_PERFORM) { ++ dma_unmap_single(sa_ctx->master->npe_dev, ++ cont->eth.phys_addr, ++ cont->eth.buf_len, ++ DMA_BIDIRECTIONAL); ++ if (sa_ctx->perf_cb) ++ sa_ctx->perf_cb(sa_ctx, cont->data, failed); ++ qmgr_return_cont(dev_get_drvdata(queue->dev), cont); ++ ix_sa_return_cont(sa_ctx->master, cr_cont); ++ if (atomic_dec_and_test(&sa_ctx->use_cnt)) ++ ix_sa_ctx_destroy(sa_ctx); ++ return; ++ } ++ ++ /* We are registering */ ++ switch (cr_cont->ctl.crypt.mode) { ++ case NPE_OP_HASH_GEN_ICV: ++ /* 1 out of 2 HMAC preparation operations completed */ ++ dma_unmap_single(sa_ctx->master->npe_dev, ++ cont->eth.phys_addr, ++ cont->eth.buf_len, ++ DMA_TO_DEVICE); ++ kfree(cont->data); ++ qmgr_return_cont(dev_get_drvdata(queue->dev), cont); ++ break; ++ case NPE_OP_ENC_GEN_KEY: ++ memcpy(sa_ctx->decrypt.npe_ctx + sizeof(u32), ++ sa_ctx->rev_aes->ctl.rev_aes_key + sizeof(u32), ++ sa_ctx->c_key.len); ++ /* REV AES data not needed anymore, free it */ ++ ix_sa_return_cont(sa_ctx->master, sa_ctx->rev_aes); ++ sa_ctx->rev_aes = NULL; ++ break; ++ default: ++ printk(KERN_ERR "Unknown crypt-register mode: %x\n", ++ cr_cont->ctl.crypt.mode); ++ ++ } ++ if (cr_cont->ctl.crypt.oper_type == OP_REG_DONE) { ++ if (sa_ctx->state == STATE_UNREGISTERED) ++ sa_ctx->state = STATE_REGISTERED; ++ if (sa_ctx->reg_cb) ++ sa_ctx->reg_cb(sa_ctx, failed); ++ } ++ ix_sa_return_cont(sa_ctx->master, cr_cont); ++ if (atomic_dec_and_test(&sa_ctx->use_cnt)) ++ ix_sa_ctx_destroy(sa_ctx); ++} ++ ++static void irqcb_recv(struct qm_queue *queue) ++{ ++ u32 phys; ++ ++ queue_ack_irq(queue); ++ while ((phys = queue_get_entry(queue))) ++ recv_pack(queue, phys); ++} ++ ++static int init_sa_dir(struct ix_sa_ctx *sa_ctx, struct ix_sa_dir *dir) ++{ ++ dir->npe_ctx = dma_pool_alloc(sa_ctx->master->dmapool, ++ sa_ctx->gfp_flags, &dir->npe_ctx_phys); ++ if (!dir->npe_ctx) { ++ return 1; ++ } ++ memset(dir->npe_ctx, 0, NPE_CTX_LEN); ++ return 0; ++} ++ ++struct ix_sa_ctx *ix_sa_ctx_new(int priv_len, gfp_t flags) ++{ ++ struct ix_sa_ctx *sa_ctx; ++ struct ix_sa_master *master = &sa_master; ++ struct npe_info *npe = dev_get_drvdata(master->npe_dev); ++ ++ /* first check if Microcode was downloaded into this NPE */ ++ if (!( npe_status(npe) & IX_NPEDL_EXCTL_STATUS_RUN)) { ++ printk(KERN_ERR "%s not running\n", npe->plat->name); ++ return NULL; ++ } ++ switch (npe->img_info[1]) { ++ case 4: ++ case 5: ++ break; ++ default: ++ /* No crypto Microcode */ ++ return NULL; ++ } ++ if (!try_module_get(THIS_MODULE)) { ++ return NULL; ++ } ++ ++ sa_ctx = kzalloc(sizeof(struct ix_sa_ctx) + priv_len, flags); ++ if (!sa_ctx) { ++ goto err_put; ++ } ++ ++ sa_ctx->master = master; ++ sa_ctx->gfp_flags = flags; ++ ++ if (init_sa_dir(sa_ctx, &sa_ctx->encrypt)) ++ goto err_free; ++ if (init_sa_dir(sa_ctx, &sa_ctx->decrypt)) { ++ free_sa_dir(sa_ctx, &sa_ctx->encrypt); ++ goto err_free; ++ } ++ if (priv_len) ++ sa_ctx->priv = sa_ctx + 1; ++ ++ atomic_set(&sa_ctx->use_cnt, 1); ++ return sa_ctx; ++ ++err_free: ++ kfree(sa_ctx); ++err_put: ++ module_put(THIS_MODULE); ++ return NULL; ++} ++ ++void ix_sa_ctx_free(struct ix_sa_ctx *sa_ctx) ++{ ++ sa_ctx->state = STATE_UNLOADING; ++ if (atomic_dec_and_test(&sa_ctx->use_cnt)) ++ ix_sa_ctx_destroy(sa_ctx); ++ else ++ printk("ix_sa_ctx_free -> delayed: %p %d\n", ++ sa_ctx, atomic_read(&sa_ctx->use_cnt)); ++} ++ ++/* http://www.ietf.org/rfc/rfc2104.txt */ ++#define HMAC_IPAD_VALUE 0x36 ++#define HMAC_OPAD_VALUE 0x5C ++#define PAD_BLOCKLEN 64 ++ ++static int register_chain_var(struct ix_sa_ctx *sa_ctx, ++ unsigned char *pad, u32 target, int init_len, u32 ctx_addr, int oper) ++{ ++ struct npe_crypt_cont *cr_cont; ++ struct npe_cont *cont; ++ ++ cr_cont = ix_sa_get_cont(sa_ctx->master); ++ if (!cr_cont) ++ return -ENOMEM; ++ ++ cr_cont->ctl.crypt.sa_ctx = sa_ctx; ++ cr_cont->ctl.crypt.auth_offs = 0; ++ cr_cont->ctl.crypt.auth_len =cpu_to_npe16(PAD_BLOCKLEN); ++ cr_cont->ctl.crypt.crypto_ctx = cpu_to_npe32(ctx_addr); ++ ++ cont = qmgr_get_cont(dev_get_drvdata(sa_ctx->master->sendq->dev)); ++ if (!cont) { ++ ix_sa_return_cont(sa_ctx->master, cr_cont); ++ return -ENOMEM; ++ } ++ ++ cont->data = pad; ++ cont->eth.next = 0; ++ cont->eth.buf_len = cpu_to_npe16(PAD_BLOCKLEN); ++ cont->eth.pkt_len = 0; ++ ++ cont->eth.phys_addr = cpu_to_npe32(dma_map_single( ++ sa_ctx->master->npe_dev, pad, PAD_BLOCKLEN, DMA_TO_DEVICE)); ++ ++ cr_cont->ctl.crypt.src_buf = cpu_to_npe32(cont->phys); ++ cr_cont->ctl.crypt.oper_type = oper; ++ ++ cr_cont->ctl.crypt.addr.icv = cpu_to_npe32(target); ++ cr_cont->ctl.crypt.mode = NPE_OP_HASH_GEN_ICV; ++ cr_cont->ctl.crypt.init_len = init_len; ++ ++ atomic_inc(&sa_ctx->use_cnt); ++ queue_put_entry(sa_ctx->master->sendq, cr_cont->phys); ++ if (queue_stat(sa_ctx->master->sendq) == 2) { /* overflow */ ++ atomic_dec(&sa_ctx->use_cnt); ++ qmgr_return_cont(dev_get_drvdata(sa_ctx->master->sendq->dev), ++ cont); ++ ix_sa_return_cont(sa_ctx->master, cr_cont); ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++/* Return value ++ * 0 if nothing registered, ++ * 1 if something registered and ++ * < 0 on error ++ */ ++static int ix_sa_ctx_setup_auth(struct ix_sa_ctx *sa_ctx, ++ const struct ix_hash_algo *algo, int len, int oper, int encrypt) ++{ ++ unsigned char *ipad, *opad; ++ u32 itarget, otarget, ctx_addr; ++ unsigned char *cinfo; ++ int init_len, i, ret = 0; ++ struct qm_qmgr *qmgr; ++ struct ix_sa_dir *dir; ++ u32 cfgword; ++ ++ dir = encrypt ? &sa_ctx->encrypt : &sa_ctx->decrypt; ++ cinfo = dir->npe_ctx + dir->npe_ctx_idx; ++ ++ qmgr = dev_get_drvdata(sa_ctx->master->sendq->dev); ++ ++ cinfo = dir->npe_ctx + dir->npe_ctx_idx; ++ sa_ctx->h_algo = algo; ++ ++ if (!algo) { ++ dir->npe_mode |= NPE_OP_HMAC_DISABLE; ++ return 0; ++ } ++ if (algo->type == HASH_TYPE_CBCMAC) { ++ dir->npe_mode |= NPE_OP_CCM_ENABLE | NPE_OP_HMAC_DISABLE; ++ return 0; ++ } ++ if (sa_ctx->h_key.len > 64 || sa_ctx->h_key.len < algo->digest_len) ++ return -EINVAL; ++ if (len > algo->digest_len || (len % 4)) ++ return -EINVAL; ++ if (!len) ++ len = algo->digest_len; ++ ++ sa_ctx->digest_len = len; ++ ++ /* write cfg word to cryptinfo */ ++ cfgword = algo->cfgword | ((len/4) << 8); ++ *(u32*)cinfo = cpu_to_be32(cfgword); ++ cinfo += sizeof(cfgword); ++ ++ /* write ICV to cryptinfo */ ++ memcpy(cinfo, algo->icv, algo->digest_len); ++ cinfo += algo->digest_len; ++ ++ itarget = dir->npe_ctx_phys + dir->npe_ctx_idx ++ + sizeof(algo->cfgword); ++ otarget = itarget + algo->digest_len; ++ ++ opad = kzalloc(PAD_BLOCKLEN, sa_ctx->gfp_flags | GFP_DMA); ++ if (!opad) { ++ return -ENOMEM; ++ } ++ ipad = kzalloc(PAD_BLOCKLEN, sa_ctx->gfp_flags | GFP_DMA); ++ if (!ipad) { ++ kfree(opad); ++ return -ENOMEM; ++ } ++ memcpy(ipad, sa_ctx->h_key.key, sa_ctx->h_key.len); ++ memcpy(opad, sa_ctx->h_key.key, sa_ctx->h_key.len); ++ for (i = 0; i < PAD_BLOCKLEN; i++) { ++ ipad[i] ^= HMAC_IPAD_VALUE; ++ opad[i] ^= HMAC_OPAD_VALUE; ++ } ++ init_len = cinfo - (dir->npe_ctx + dir->npe_ctx_idx); ++ ctx_addr = dir->npe_ctx_phys + dir->npe_ctx_idx; ++ ++ dir->npe_ctx_idx += init_len; ++ dir->npe_mode |= NPE_OP_HASH_ENABLE; ++ ++ if (!encrypt) ++ dir->npe_mode |= NPE_OP_HASH_VERIFY; ++ ++ /* register first chainvar */ ++ ret = register_chain_var(sa_ctx, opad, otarget, ++ init_len, ctx_addr, OP_REGISTER); ++ if (ret) { ++ kfree(ipad); ++ kfree(opad); ++ return ret; ++ } ++ ++ /* register second chainvar */ ++ ret = register_chain_var(sa_ctx, ipad, itarget, ++ init_len, ctx_addr, oper); ++ if (ret) { ++ kfree(ipad); ++ return ret; ++ } ++ ++ return 1; ++} ++ ++static int gen_rev_aes_key(struct ix_sa_ctx *sa_ctx, ++ u32 keylen_cfg, int cipher_op) ++{ ++ unsigned char *cinfo; ++ struct npe_crypt_cont *cr_cont; ++ ++ keylen_cfg |= CIPH_ENCR | ALGO_AES | MOD_ECB; ++ sa_ctx->rev_aes = ix_sa_get_cont(sa_ctx->master); ++ if (!sa_ctx->rev_aes) ++ return -ENOMEM; ++ ++ cinfo = sa_ctx->rev_aes->ctl.rev_aes_key; ++ *(u32*)cinfo = cpu_to_be32(keylen_cfg); ++ cinfo += sizeof(keylen_cfg); ++ ++ memcpy(cinfo, sa_ctx->c_key.key, sa_ctx->c_key.len); ++ ++ cr_cont = ix_sa_get_cont(sa_ctx->master); ++ if (!cr_cont) { ++ ix_sa_return_cont(sa_ctx->master, sa_ctx->rev_aes); ++ sa_ctx->rev_aes = NULL; ++ return -ENOMEM; ++ } ++ cr_cont->ctl.crypt.sa_ctx = sa_ctx; ++ cr_cont->ctl.crypt.oper_type = cipher_op; ++ ++ cr_cont->ctl.crypt.crypt_offs = 0; ++ cr_cont->ctl.crypt.crypt_len = cpu_to_npe16(AES_BLOCK128); ++ cr_cont->ctl.crypt.addr.rev_aes = cpu_to_npe32( ++ sa_ctx->rev_aes->phys + sizeof(keylen_cfg)); ++ ++ cr_cont->ctl.crypt.src_buf = 0; ++ cr_cont->ctl.crypt.crypto_ctx = cpu_to_npe32(sa_ctx->rev_aes->phys); ++ cr_cont->ctl.crypt.mode = NPE_OP_ENC_GEN_KEY; ++ cr_cont->ctl.crypt.init_len = sa_ctx->decrypt.npe_ctx_idx; ++ ++ atomic_inc(&sa_ctx->use_cnt); ++ queue_put_entry(sa_ctx->master->sendq, cr_cont->phys); ++ if (queue_stat(sa_ctx->master->sendq) == 2) { /* overflow */ ++ atomic_dec(&sa_ctx->use_cnt); ++ ix_sa_return_cont(sa_ctx->master, cr_cont); ++ ix_sa_return_cont(sa_ctx->master, sa_ctx->rev_aes); ++ sa_ctx->rev_aes = NULL; ++ return -ENOMEM; ++ } ++ ++ return 1; ++} ++ ++/* Return value ++ * 0 if nothing registered, ++ * 1 if something registered and ++ * < 0 on error ++ */ ++static int ix_sa_ctx_setup_cipher(struct ix_sa_ctx *sa_ctx, ++ const struct ix_cipher_algo *algo, int cipher_op, int encrypt) ++{ ++ unsigned char *cinfo; ++ int keylen, init_len; ++ u32 cipher_cfg; ++ u32 keylen_cfg = 0; ++ struct ix_sa_dir *dir; ++ ++ dir = encrypt ? &sa_ctx->encrypt : &sa_ctx->decrypt; ++ cinfo = dir->npe_ctx + dir->npe_ctx_idx; ++ ++ sa_ctx->c_algo = algo; ++ ++ if (!algo) ++ return 0; ++ ++ if (algo->type == CIPHER_TYPE_DES && sa_ctx->c_key.len != 8) ++ return -EINVAL; ++ ++ if (algo->type == CIPHER_TYPE_3DES && sa_ctx->c_key.len != 24) ++ return -EINVAL; ++ ++ keylen = 24; ++ ++ if (encrypt) { ++ cipher_cfg = algo->cfgword_enc; ++ dir->npe_mode |= NPE_OP_CRYPT_ENCRYPT; ++ } else { ++ cipher_cfg = algo->cfgword_dec; ++ } ++ if (algo->type == CIPHER_TYPE_AES) { ++ switch (sa_ctx->c_key.len) { ++ case 16: keylen_cfg = MOD_AES128 | KEYLEN_128; break; ++ case 24: keylen_cfg = MOD_AES192 | KEYLEN_192; break; ++ case 32: keylen_cfg = MOD_AES256 | KEYLEN_256; break; ++ default: return -EINVAL; ++ } ++ keylen = sa_ctx->c_key.len; ++ cipher_cfg |= keylen_cfg; ++ } ++ ++ /* write cfg word to cryptinfo */ ++ *(u32*)cinfo = cpu_to_be32(cipher_cfg); ++ cinfo += sizeof(cipher_cfg); ++ ++ /* write cipher key to cryptinfo */ ++ memcpy(cinfo, sa_ctx->c_key.key, sa_ctx->c_key.len); ++ cinfo += keylen; ++ ++ init_len = cinfo - (dir->npe_ctx + dir->npe_ctx_idx); ++ dir->npe_ctx_idx += init_len; ++ ++ dir->npe_mode |= NPE_OP_CRYPT_ENABLE; ++ ++ if (algo->type == CIPHER_TYPE_AES && !encrypt) { ++ return gen_rev_aes_key(sa_ctx, keylen_cfg, cipher_op); ++ } ++ ++ return 0; ++} ++ ++/* returns 0 on OK, <0 on error and 1 on overflow */ ++int ix_sa_crypto_perform(struct ix_sa_ctx *sa_ctx, u8 *data, void *ptr, ++ int datalen, int c_offs, int c_len, int a_offs, int a_len, ++ int hmac, char *iv, int encrypt) ++{ ++ struct npe_crypt_cont *cr_cont; ++ struct npe_cont *cont; ++ u32 data_phys; ++ int ret = -ENOMEM; ++ struct ix_sa_dir *dir; ++ ++ dir = encrypt ? &sa_ctx->encrypt : &sa_ctx->decrypt; ++ ++ if (sa_ctx->state != STATE_REGISTERED) ++ return -ENOENT; ++ ++ cr_cont = ix_sa_get_cont(sa_ctx->master); ++ if (!cr_cont) ++ return ret; ++ ++ cr_cont->ctl.crypt.sa_ctx = sa_ctx; ++ cr_cont->ctl.crypt.crypto_ctx = cpu_to_npe32(dir->npe_ctx_phys); ++ cr_cont->ctl.crypt.oper_type = OP_PERFORM; ++ cr_cont->ctl.crypt.mode = dir->npe_mode; ++ cr_cont->ctl.crypt.init_len = dir->npe_ctx_idx; ++ ++ if (sa_ctx->c_algo) { ++ cr_cont->ctl.crypt.crypt_offs = cpu_to_npe16(c_offs); ++ cr_cont->ctl.crypt.crypt_len = cpu_to_npe16(c_len); ++ if (sa_ctx->c_algo->iv_len) { ++ if (!iv) { ++ ret = -EINVAL; ++ goto err_cr; ++ } ++ memcpy(cr_cont->ctl.crypt.iv, iv, ++ sa_ctx->c_algo->iv_len); ++ } ++ } ++ ++ if (sa_ctx->h_algo) { ++ /* prepare hashing */ ++ cr_cont->ctl.crypt.auth_offs = cpu_to_npe16(a_offs); ++ cr_cont->ctl.crypt.auth_len = cpu_to_npe16(a_len); ++ } ++ ++ data_phys = dma_map_single(sa_ctx->master->npe_dev, ++ data, datalen, DMA_BIDIRECTIONAL); ++ if (hmac) ++ cr_cont->ctl.crypt.addr.icv = cpu_to_npe32(data_phys + hmac); ++ ++ /* Prepare the data ptr */ ++ cont = qmgr_get_cont(dev_get_drvdata(sa_ctx->master->sendq->dev)); ++ if (!cont) { ++ goto err_unmap; ++ } ++ ++ cont->data = ptr; ++ cont->eth.next = 0; ++ cont->eth.buf_len = cpu_to_npe16(datalen); ++ cont->eth.pkt_len = 0; ++ ++ cont->eth.phys_addr = cpu_to_npe32(data_phys); ++ cr_cont->ctl.crypt.src_buf = cpu_to_npe32(cont->phys); ++ ++ atomic_inc(&sa_ctx->use_cnt); ++ queue_put_entry(sa_ctx->master->sendq, cr_cont->phys); ++ if (queue_stat(sa_ctx->master->sendq) != 2) { ++ return 0; ++ } ++ ++ /* overflow */ ++ printk("%s: Overflow\n", __FUNCTION__); ++ ret = -EAGAIN; ++ atomic_dec(&sa_ctx->use_cnt); ++ qmgr_return_cont(dev_get_drvdata(sa_ctx->master->sendq->dev), cont); ++ ++err_unmap: ++ dma_unmap_single(sa_ctx->master->npe_dev, data_phys, datalen, ++ DMA_BIDIRECTIONAL); ++err_cr: ++ ix_sa_return_cont(sa_ctx->master, cr_cont); ++ ++ return ret; ++} ++ ++int ix_sa_ctx_setup_cipher_auth(struct ix_sa_ctx *sa_ctx, ++ const struct ix_cipher_algo *cipher, ++ const struct ix_hash_algo *auth, int len) ++{ ++ int ret = 0, sum = 0; ++ int cipher_op; ++ ++ if (sa_ctx->state != STATE_UNREGISTERED) ++ return -ENOENT; ++ ++ atomic_inc(&sa_ctx->use_cnt); ++ ++ cipher_op = auth ? OP_REGISTER : OP_REG_DONE; ++ if ((ret = ix_sa_ctx_setup_cipher(sa_ctx, cipher, OP_REGISTER, 1)) < 0) ++ goto out; ++ sum += ret; ++ if ((ret = ix_sa_ctx_setup_cipher(sa_ctx, cipher, cipher_op, 0)) < 0) ++ goto out; ++ sum += ret; ++ if ((ret = ix_sa_ctx_setup_auth(sa_ctx, auth, len, OP_REGISTER, 1)) < 0) ++ goto out; ++ sum += ret; ++ if ((ret = ix_sa_ctx_setup_auth(sa_ctx, auth, len, OP_REG_DONE, 0)) < 0) ++ goto out; ++ sum += ret; ++ ++ /* Nothing registered ? ++ * Ok, then we are done and call the callback here. ++ */ ++ if (!sum) { ++ if (sa_ctx->state == STATE_UNREGISTERED) ++ sa_ctx->state = STATE_REGISTERED; ++ if (sa_ctx->reg_cb) ++ sa_ctx->reg_cb(sa_ctx, 0); ++ } ++out: ++ atomic_dec(&sa_ctx->use_cnt); ++ return ret; ++} ++ ++static int __init init_crypto(void) ++{ ++ return init_sa_master(&sa_master); ++} ++ ++static void __exit finish_crypto(void) ++{ ++ release_sa_master(&sa_master); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Christian Hohnstaedt "); ++ ++EXPORT_SYMBOL(ix_hash_by_id); ++EXPORT_SYMBOL(ix_cipher_by_id); ++ ++EXPORT_SYMBOL(ix_sa_ctx_new); ++EXPORT_SYMBOL(ix_sa_ctx_free); ++EXPORT_SYMBOL(ix_sa_ctx_setup_cipher_auth); ++EXPORT_SYMBOL(ix_sa_crypto_perform); ++ ++module_init(init_crypto); ++module_exit(finish_crypto); ++ +diff --git a/drivers/net/ixp4xx/ixp4xx_qmgr.c b/drivers/net/ixp4xx/ixp4xx_qmgr.c +new file mode 100644 +index 0000000..b694dad +--- /dev/null ++++ b/drivers/net/ixp4xx/ixp4xx_qmgr.c +@@ -0,0 +1,474 @@ ++/* ++ * qmgr.c - reimplementation of the queue configuration interface. ++ * ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define IXQMGR_VERSION "IXP4XX Q Manager 0.2.1" ++ ++static struct device *qmgr_dev = NULL; ++ ++static int poll_freq = 4000; ++static int poll_enable = 0; ++static u32 timer_countup_ticks; ++ ++module_param(poll_freq, int, 0644); ++module_param(poll_enable, int, 0644); ++ ++int queue_len(struct qm_queue *queue) ++{ ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ int diff, offs; ++ u32 val; ++ ++ offs = queue->id/8 + QUE_LOW_STAT0; ++ val = *(qmgr->addr + IX_QMGR_QCFG_BASE + queue->id); ++ ++ diff = (val - (val >> 7)) & 0x7f; ++ if (!diff) { ++ /* diff == 0 means either empty or full, must look at STAT0 */ ++ if ((*(qmgr->addr + offs) >> ((queue->id % 8)*4)) & 0x04) ++ diff = queue->len; ++ } ++ return diff; ++} ++ ++static int request_pool(struct device *dev, int count) ++{ ++ int i; ++ struct npe_cont *cont; ++ struct qm_qmgr *qmgr = dev_get_drvdata(dev); ++ dma_addr_t handle; ++ ++ for (i=0; idmapool, GFP_KERNEL, &handle); ++ if (!cont) { ++ return -ENOMEM; ++ } ++ cont->phys = handle; ++ cont->virt = cont; ++ write_lock(&qmgr->lock); ++ cont->next = qmgr->pool; ++ qmgr->pool = cont; ++ write_unlock(&qmgr->lock); ++ } ++ return 0; ++} ++ ++static int free_pool(struct device *dev, int count) ++{ ++ int i; ++ struct npe_cont *cont; ++ struct qm_qmgr *qmgr = dev_get_drvdata(dev); ++ ++ for (i=0; ilock); ++ cont = qmgr->pool; ++ if (!cont) { ++ write_unlock(&qmgr->lock); ++ return -1; ++ } ++ qmgr->pool = cont->next; ++ write_unlock(&qmgr->lock); ++ dma_pool_free(qmgr->dmapool, cont, cont->phys); ++ } ++ return 0; ++} ++ ++static int get_free_qspace(struct qm_qmgr *qmgr, int len) ++{ ++ int words = (qmgr->res->end - qmgr->res->start + 1) / 4 - ++ IX_QMGR_SRAM_SPACE; ++ int i,q; ++ ++ for (i=0; iqueues[q]; ++ if (!qu) ++ continue; ++ if ((qu->addr + qu->len > i) && (qu->addr < i + len)) ++ break; ++ } ++ if (q == MAX_QUEUES) { ++ /* we have a free address */ ++ return i; ++ } ++ } ++ return -1; ++} ++ ++static inline int _log2(int x) ++{ ++ int r=0; ++ while(x>>=1) ++ r++; ++ return r; ++} ++ ++/* ++ * 32bit Config registers at IX_QMGR_QUECONFIG_BASE_OFFSET[Qid] ++ * 0 - 6 WRPTR Word offset to baseaddr (index 0 .. BSIZE-1) ++ * 7 -13 RDPTR '' ++ * 14 -21 BADDR baseaddr = (offset to IX_QMGR_QUEBUFFER_SPACE_OFFSET) >> 6 ++ * 22 -23 ESIZE entrySizeInWords (always 00 because entrySizeInWords==1) ++ * 24 -25 BSIZE qSizeInWords 00=16,01=32,10=64,11=128 ++ * 26 -28 NE nearly empty ++ * 29 -31 NF nearly full ++ */ ++static int conf_q_regs(struct qm_queue *queue) ++{ ++ int bsize = _log2(queue->len/16); ++ int baddr = queue->addr + IX_QMGR_QCFG_SIZE; ++ ++ /* +2, because baddr is in words and not in bytes */ ++ queue_write_cfg_reg(queue, (bsize << 24) | (baddr<<(14-6+2)) ); ++ ++ return 0; ++} ++ ++static void pmu_timer_restart(void) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ __asm__(" mcr p14,0,%0,c1,c1,0\n" /* write current counter */ ++ : : "r" (timer_countup_ticks)); ++ ++ __asm__(" mrc p14,0,r1,c4,c1,0; " /* get int enable register */ ++ " orr r1,r1,#1; " ++ " mcr p14,0,r1,c5,c1,0; " /* clear overflow */ ++ " mcr p14,0,r1,c4,c1,0\n" /* enable interrupts */ ++ : : : "r1"); ++ ++ local_irq_restore(flags); ++} ++ ++static void pmu_timer_init(void) ++{ ++ u32 controlRegisterMask = ++ BIT(0) | /* enable counters */ ++ BIT(2); /* reset clock counter; */ ++ ++ /* ++ * Compute the number of xscale cycles needed between each ++ * PMU IRQ. This is done from the result of an OS calibration loop. ++ * ++ * For 533MHz CPU, 533000000 tick/s / 4000 times/sec = 138250 ++ * 4000 times/sec = 37 mbufs/interrupt at line rate ++ * The pmu timer is reset to -138250 = 0xfffde3f6, to trigger an IRQ ++ * when this up counter overflows. ++ * ++ * The multiplication gives a number of instructions per second. ++ * which is close to the processor frequency, and then close to the ++ * PMU clock rate. ++ * ++ * 2 is the number of instructions per loop ++ * ++ */ ++ ++ timer_countup_ticks = - ((loops_per_jiffy * HZ * 2) / poll_freq); ++ ++ /* enable the CCNT (clock count) timer from the PMU */ ++ __asm__(" mcr p14,0,%0,c0,c1,0\n" ++ : : "r" (controlRegisterMask)); ++} ++ ++static void pmu_timer_disable(void) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ __asm__(" mrc p14,0,r1,c4,c1,0; " /* get int enable register */ ++ " and r1,r1,#0x1e; " ++ " mcr p14,0,r1,c4,c1,0\n" /* disable interrupts */ ++ : : : "r1"); ++ local_irq_restore(flags); ++} ++ ++void queue_set_watermarks(struct qm_queue *queue, unsigned ne, unsigned nf) ++{ ++ u32 val; ++ /* calculate the register values ++ * 0->0, 1->1, 2->2, 4->3, 8->4 16->5...*/ ++ ne = _log2(ne<<1) & 0x7; ++ nf = _log2(nf<<1) & 0x7; ++ ++ /* Mask out old watermarks */ ++ val = queue_read_cfg_reg(queue) & ~0xfc000000; ++ queue_write_cfg_reg(queue, val | (ne << 26) | (nf << 29)); ++} ++ ++int queue_set_irq_src(struct qm_queue *queue, int flag) ++{ ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ u32 reg; ++ int offs, bitoffs; ++ ++ /* Q 0-7 are in REG0, 8-15 are in REG1, etc. They occupy 4 bits/Q */ ++ offs = queue->id/8 + INT0_SRC_SELREG0; ++ bitoffs = (queue->id % 8)*4; ++ ++ reg = *(qmgr->addr + offs) & ~(0xf << bitoffs); ++ *(qmgr->addr + offs) = reg | (flag << bitoffs); ++ ++ return 0; ++} ++ ++static irqreturn_t irq_qm1(int irq, void *dev_id) ++{ ++ struct qm_qmgr *qmgr = dev_id; ++ int offs, reg; ++ struct qm_queue *queue; ++ ++ if (poll_enable) ++ pmu_timer_restart(); ++ ++ reg = *(qmgr->addr + QUE_INT_REG0); ++ while(reg) { ++ /* ++ * count leading zeros. "offs" gets ++ * the amount of leading 0 in "reg" ++ */ ++ asm ("clz %0, %1;" : "=r"(offs) : "r"(reg)); ++ offs = 31 - offs; ++ reg &= ~(1 << offs); ++ queue = qmgr->queues[offs]; ++ if (likely(queue)) { ++ if (likely(queue->irq_cb)) { ++ queue->irq_cb(queue); ++ } else { ++ printk(KERN_ERR "Missing callback for Q %d\n", ++ offs); ++ } ++ } else { ++ printk(KERN_ERR "IRQ for unregistered Q %d\n", offs); ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++struct qm_queue *request_queue(int qid, int len) ++{ ++ int ram; ++ struct qm_qmgr *qmgr; ++ struct qm_queue *queue; ++ ++ if (!qmgr_dev) ++ return ERR_PTR(-ENODEV); ++ ++ if ((qid < 0) || (qid > MAX_QUEUES)) ++ return ERR_PTR(-ERANGE); ++ ++ switch (len) { ++ case 16: ++ case 32: ++ case 64: ++ case 128: break; ++ default : return ERR_PTR(-EINVAL); ++ } ++ ++ qmgr = dev_get_drvdata(qmgr_dev); ++ ++ if (qmgr->queues[qid]) { ++ /* not an error, just in use already */ ++ return NULL; ++ } ++ if ((ram = get_free_qspace(qmgr, len)) < 0) { ++ printk(KERN_ERR "No free SRAM space for this queue\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ if (!(queue = kzalloc(sizeof(struct qm_queue), GFP_KERNEL))) ++ return ERR_PTR(-ENOMEM); ++ ++ if (!try_module_get(THIS_MODULE)) { ++ kfree(queue); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ queue->addr = ram; ++ queue->len = len; ++ queue->id = qid; ++ queue->dev = get_device(qmgr_dev); ++ queue->acc_reg = qmgr->addr + (4 * qid); ++ qmgr->queues[qid] = queue; ++ if (request_pool(qmgr_dev, len)) { ++ printk(KERN_ERR "Failed to request DMA pool of Q %d\n", qid); ++ } ++ ++ conf_q_regs(queue); ++ return queue; ++} ++ ++void release_queue(struct qm_queue *queue) ++{ ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ ++ BUG_ON(qmgr->queues[queue->id] != queue); ++ qmgr->queues[queue->id] = NULL; ++ ++ if (free_pool(queue->dev, queue->len)) { ++ printk(KERN_ERR "Failed to release DMA pool of Q %d\n", ++ queue->id); ++ } ++ queue_disable_irq(queue); ++ queue_write_cfg_reg(queue, 0); ++ ++ module_put(THIS_MODULE); ++ put_device(queue->dev); ++ kfree(queue); ++} ++ ++ ++ ++ ++static int qmgr_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct qm_qmgr *qmgr; ++ int size, ret=0, i; ++ ++ if (!(res = platform_get_resource(pdev, IORESOURCE_MEM, 0))) ++ return -EIO; ++ ++ if ((i = platform_get_irq(pdev, 0)) < 0) ++ return -EIO; ++ ++ if (!(qmgr = kzalloc(sizeof(struct qm_qmgr), GFP_KERNEL))) ++ return -ENOMEM; ++ ++ qmgr->irq = i; ++ size = res->end - res->start +1; ++ qmgr->res = request_mem_region(res->start, size, "ixp_qmgr"); ++ if (!qmgr->res) { ++ ret = -EBUSY; ++ goto out_free; ++ } ++ ++ qmgr->addr = ioremap(res->start, size); ++ if (!qmgr->addr) { ++ ret = -ENOMEM; ++ goto out_rel; ++ } ++ ++ /* Reset Q registers */ ++ for (i=0; i<4; i++) ++ *(qmgr->addr + QUE_LOW_STAT0 +i) = 0x33333333; ++ for (i=0; i<10; i++) ++ *(qmgr->addr + QUE_UO_STAT0 +i) = 0x0; ++ for (i=0; i<4; i++) ++ *(qmgr->addr + INT0_SRC_SELREG0 +i) = 0x0; ++ for (i=0; i<2; i++) { ++ *(qmgr->addr + QUE_IE_REG0 +i) = 0x00; ++ *(qmgr->addr + QUE_INT_REG0 +i) = 0xffffffff; ++ } ++ for (i=0; i<64; i++) { ++ *(qmgr->addr + IX_QMGR_QCFG_BASE + i) = 0x0; ++ } ++ ++ if (poll_enable) { ++ pmu_timer_init(); ++ qmgr->irq = IRQ_IXP4XX_XSCALE_PMU; ++ } ++ ret = request_irq(qmgr->irq, irq_qm1, SA_SHIRQ | SA_INTERRUPT, ++ "qmgr", qmgr); ++ if (ret) { ++ printk(KERN_ERR "Failed to request IRQ(%d)\n", qmgr->irq); ++ ret = -EIO; ++ goto out_rel; ++ } ++ if (poll_enable) ++ pmu_timer_restart(); ++ ++ rwlock_init(&qmgr->lock); ++ qmgr->dmapool = dma_pool_create("qmgr", &pdev->dev, ++ sizeof(struct npe_cont), 32, 0); ++ platform_set_drvdata(pdev, qmgr); ++ ++ qmgr_dev = &pdev->dev; ++ ++ printk(KERN_INFO IXQMGR_VERSION " initialized.\n"); ++ ++ return 0; ++ ++out_rel: ++ release_resource(qmgr->res); ++out_free: ++ kfree(qmgr); ++ return ret; ++} ++ ++static int qmgr_remove(struct platform_device *pdev) ++{ ++ struct qm_qmgr *qmgr = platform_get_drvdata(pdev); ++ int i; ++ ++ for (i=0; iqueues[i]) { ++ printk(KERN_ERR "WARNING Unreleased Q: %d\n", i); ++ release_queue(qmgr->queues[i]); ++ } ++ } ++ ++ if (poll_enable) ++ pmu_timer_disable(); ++ ++ synchronize_irq (qmgr->irq); ++ free_irq(qmgr->irq, qmgr); ++ ++ dma_pool_destroy(qmgr->dmapool); ++ iounmap(qmgr->addr); ++ release_resource(qmgr->res); ++ platform_set_drvdata(pdev, NULL); ++ qmgr_dev = NULL; ++ kfree(qmgr); ++ return 0; ++} ++ ++static struct platform_driver ixp4xx_qmgr = { ++ .driver.name = "ixp4xx_qmgr", ++ .probe = qmgr_probe, ++ .remove = qmgr_remove, ++}; ++ ++ ++static int __init init_qmgr(void) ++{ ++ return platform_driver_register(&ixp4xx_qmgr); ++} ++ ++static void __exit finish_qmgr(void) ++{ ++ platform_driver_unregister(&ixp4xx_qmgr); ++} ++ ++module_init(init_qmgr); ++module_exit(finish_qmgr); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Christian Hohnstaedt "); ++ ++EXPORT_SYMBOL(request_queue); ++EXPORT_SYMBOL(release_queue); ++EXPORT_SYMBOL(queue_set_irq_src); ++EXPORT_SYMBOL(queue_set_watermarks); ++EXPORT_SYMBOL(queue_len); +diff --git a/drivers/net/ixp4xx/mac.h b/drivers/net/ixp4xx/mac.h +new file mode 100644 +index 0000000..b124c24 +--- /dev/null ++++ b/drivers/net/ixp4xx/mac.h +@@ -0,0 +1,275 @@ ++/* ++ * Copyright (C) 2002-2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 32 bit offsets to be added to u32 *pointers */ ++#define MAC_TX_CNTRL1 0x00 // 0x000 ++#define MAC_TX_CNTRL2 0x01 // 0x004 ++#define MAC_RX_CNTRL1 0x04 // 0x010 ++#define MAC_RX_CNTRL2 0x05 // 0x014 ++#define MAC_RANDOM_SEED 0x08 // 0x020 ++#define MAC_THRESH_P_EMPTY 0x0c // 0x030 ++#define MAC_THRESH_P_FULL 0x0e // 0x038 ++#define MAC_BUF_SIZE_TX 0x10 // 0x040 ++#define MAC_TX_DEFER 0x14 // 0x050 ++#define MAC_RX_DEFER 0x15 // 0x054 ++#define MAC_TX_TWO_DEFER_1 0x18 // 0x060 ++#define MAC_TX_TWO_DEFER_2 0x19 // 0x064 ++#define MAC_SLOT_TIME 0x1c // 0x070 ++#define MAC_MDIO_CMD 0x20 // 0x080 4 registers 0x20 - 0x23 ++#define MAC_MDIO_STS 0x24 // 0x090 4 registers 0x24 - 0x27 ++#define MAC_ADDR_MASK 0x28 // 0x0A0 6 registers 0x28 - 0x2d ++#define MAC_ADDR 0x30 // 0x0C0 6 registers 0x30 - 0x35 ++#define MAC_INT_CLK_THRESH 0x38 // 0x0E0 1 register ++#define MAC_UNI_ADDR 0x3c // 0x0F0 6 registers 0x3c - 0x41 ++#define MAC_CORE_CNTRL 0x7f // 0x1fC ++ ++/* TX Control Register 1*/ ++ ++#define TX_CNTRL1_TX_EN BIT(0) ++#define TX_CNTRL1_DUPLEX BIT(1) ++#define TX_CNTRL1_RETRY BIT(2) ++#define TX_CNTRL1_PAD_EN BIT(3) ++#define TX_CNTRL1_FCS_EN BIT(4) ++#define TX_CNTRL1_2DEFER BIT(5) ++#define TX_CNTRL1_RMII BIT(6) ++ ++/* TX Control Register 2 */ ++#define TX_CNTRL2_RETRIES_MASK 0xf ++ ++/* RX Control Register 1 */ ++#define RX_CNTRL1_RX_EN BIT(0) ++#define RX_CNTRL1_PADSTRIP_EN BIT(1) ++#define RX_CNTRL1_CRC_EN BIT(2) ++#define RX_CNTRL1_PAUSE_EN BIT(3) ++#define RX_CNTRL1_LOOP_EN BIT(4) ++#define RX_CNTRL1_ADDR_FLTR_EN BIT(5) ++#define RX_CNTRL1_RX_RUNT_EN BIT(6) ++#define RX_CNTRL1_BCAST_DIS BIT(7) ++ ++/* RX Control Register 2 */ ++#define RX_CNTRL2_DEFER_EN BIT(0) ++ ++/* Core Control Register */ ++#define CORE_RESET BIT(0) ++#define CORE_RX_FIFO_FLUSH BIT(1) ++#define CORE_TX_FIFO_FLUSH BIT(2) ++#define CORE_SEND_JAM BIT(3) ++#define CORE_MDC_EN BIT(4) ++ ++/* Definitions for MII access routines*/ ++ ++#define MII_REG_SHL 16 ++#define MII_ADDR_SHL 21 ++ ++#define MII_GO BIT(31) ++#define MII_WRITE BIT(26) ++#define MII_READ_FAIL BIT(31) ++ ++#define MII_TIMEOUT_10TH_SECS 5 ++#define MII_10TH_SEC_IN_MILLIS 100 ++ ++/* ++ * ++ * Default values ++ * ++ */ ++ ++#define MAC_DEF_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) ++ ++#define MAC_TX_CNTRL1_DEFAULT (\ ++ TX_CNTRL1_TX_EN | \ ++ TX_CNTRL1_RETRY | \ ++ TX_CNTRL1_FCS_EN | \ ++ TX_CNTRL1_2DEFER | \ ++ TX_CNTRL1_PAD_EN ) ++ ++#define MAC_TX_MAX_RETRIES_DEFAULT 0x0f ++ ++#define MAC_RX_CNTRL1_DEFAULT ( \ ++ RX_CNTRL1_PADSTRIP_EN | \ ++ RX_CNTRL1_CRC_EN | \ ++ RX_CNTRL1_RX_EN ) ++ ++#define MAC_RX_CNTRL2_DEFAULT 0x0 ++#define MAC_TX_CNTRL2_DEFAULT TX_CNTRL2_RETRIES_MASK ++ ++/* Thresholds determined by NPE firmware FS */ ++#define MAC_THRESH_P_EMPTY_DEFAULT 0x12 ++#define MAC_THRESH_P_FULL_DEFAULT 0x30 ++ ++/* Number of bytes that must be in the tx fifo before ++ * transmission commences */ ++#define MAC_BUF_SIZE_TX_DEFAULT 0x8 ++ ++/* One-part deferral values */ ++#define MAC_TX_DEFER_DEFAULT 0x15 ++#define MAC_RX_DEFER_DEFAULT 0x16 ++ ++/* Two-part deferral values... */ ++#define MAC_TX_TWO_DEFER_1_DEFAULT 0x08 ++#define MAC_TX_TWO_DEFER_2_DEFAULT 0x07 ++ ++/* This value applies to MII */ ++#define MAC_SLOT_TIME_DEFAULT 0x80 ++ ++/* This value applies to RMII */ ++#define MAC_SLOT_TIME_RMII_DEFAULT 0xFF ++ ++#define MAC_ADDR_MASK_DEFAULT 0xFF ++ ++#define MAC_INT_CLK_THRESH_DEFAULT 0x1 ++/* The following is a value chosen at random */ ++#define MAC_RANDOM_SEED_DEFAULT 0x8 ++ ++/* By default we must configure the MAC to generate the MDC clock*/ ++#define CORE_DEFAULT (CORE_MDC_EN) ++ ++/* End of Intel provided register information */ ++ ++extern int ++mdio_read_register(struct net_device *dev, int phy_addr, int phy_reg); ++extern void ++mdio_write_register(struct net_device *dev, int phy_addr, int phy_reg, int val); ++extern void init_mdio(struct net_device *dev, int phy_id); ++ ++struct mac_info { ++ u32 __iomem *addr; ++ struct resource *res; ++ struct device *npe_dev; ++ struct net_device *netdev; ++ struct qm_qmgr *qmgr; ++ struct qm_queue *rxq; ++ struct qm_queue *txq; ++ struct qm_queue *rxdoneq; ++ u32 irqflags; ++ struct net_device_stats stat; ++ struct mii_if_info mii; ++ struct delayed_work mdio_thread; ++ int rxq_pkt; ++ int txq_pkt; ++ int unloading; ++ struct mac_plat_info *plat; ++ int npe_stat_num; ++ spinlock_t rx_lock; ++ u32 msg_enable; ++}; ++ ++static inline void mac_write_reg(struct mac_info *mac, int offset, u32 val) ++{ ++ *(mac->addr + offset) = val; ++} ++static inline u32 mac_read_reg(struct mac_info *mac, int offset) ++{ ++ return *(mac->addr + offset); ++} ++static inline void mac_set_regbit(struct mac_info *mac, int offset, u32 bit) ++{ ++ mac_write_reg(mac, offset, mac_read_reg(mac, offset) | bit); ++} ++static inline void mac_reset_regbit(struct mac_info *mac, int offset, u32 bit) ++{ ++ mac_write_reg(mac, offset, mac_read_reg(mac, offset) & ~bit); ++} ++ ++static inline void mac_mdio_cmd_write(struct mac_info *mac, u32 cmd) ++{ ++ int i; ++ for(i=0; i<4; i++) { ++ mac_write_reg(mac, MAC_MDIO_CMD + i, cmd & 0xff); ++ cmd >>=8; ++ } ++} ++ ++#define mac_mdio_cmd_read(mac) mac_mdio_read((mac), MAC_MDIO_CMD) ++#define mac_mdio_status_read(mac) mac_mdio_read((mac), MAC_MDIO_STS) ++static inline u32 mac_mdio_read(struct mac_info *mac, int offset) ++{ ++ int i; ++ u32 data = 0; ++ for(i=0; i<4; i++) { ++ data |= (mac_read_reg(mac, offset + i) & 0xff) << (i*8); ++ } ++ return data; ++} ++ ++static inline u32 mdio_cmd(int phy_addr, int phy_reg) ++{ ++ return phy_addr << MII_ADDR_SHL | ++ phy_reg << MII_REG_SHL | ++ MII_GO; ++} ++ ++#define MAC_REG_LIST { \ ++ MAC_TX_CNTRL1, MAC_TX_CNTRL2, \ ++ MAC_RX_CNTRL1, MAC_RX_CNTRL2, \ ++ MAC_RANDOM_SEED, MAC_THRESH_P_EMPTY, MAC_THRESH_P_FULL, \ ++ MAC_BUF_SIZE_TX, MAC_TX_DEFER, MAC_RX_DEFER, \ ++ MAC_TX_TWO_DEFER_1, MAC_TX_TWO_DEFER_2, MAC_SLOT_TIME, \ ++ MAC_ADDR_MASK +0, MAC_ADDR_MASK +1, MAC_ADDR_MASK +2, \ ++ MAC_ADDR_MASK +3, MAC_ADDR_MASK +4, MAC_ADDR_MASK +5, \ ++ MAC_ADDR +0, MAC_ADDR +1, MAC_ADDR +2, \ ++ MAC_ADDR +3, MAC_ADDR +4, MAC_ADDR +5, \ ++ MAC_INT_CLK_THRESH, \ ++ MAC_UNI_ADDR +0, MAC_UNI_ADDR +1, MAC_UNI_ADDR +2, \ ++ MAC_UNI_ADDR +3, MAC_UNI_ADDR +4, MAC_UNI_ADDR +5, \ ++ MAC_CORE_CNTRL \ ++} ++ ++#define NPE_STAT_NUM 34 ++#define NPE_STAT_NUM_BASE 22 ++#define NPE_Q_STAT_NUM 4 ++ ++#define NPE_Q_STAT_STRINGS \ ++ {"RX ready to use queue len "}, \ ++ {"RX received queue len "}, \ ++ {"TX to be send queue len "}, \ ++ {"TX done queue len "}, ++ ++#define NPE_STAT_STRINGS \ ++ {"StatsAlignmentErrors "}, \ ++ {"StatsFCSErrors "}, \ ++ {"StatsInternalMacReceiveErrors "}, \ ++ {"RxOverrunDiscards "}, \ ++ {"RxLearnedEntryDiscards "}, \ ++ {"RxLargeFramesDiscards "}, \ ++ {"RxSTPBlockedDiscards "}, \ ++ {"RxVLANTypeFilterDiscards "}, \ ++ {"RxVLANIdFilterDiscards "}, \ ++ {"RxInvalidSourceDiscards "}, \ ++ {"RxBlackListDiscards "}, \ ++ {"RxWhiteListDiscards "}, \ ++ {"RxUnderflowEntryDiscards "}, \ ++ {"StatsSingleCollisionFrames "}, \ ++ {"StatsMultipleCollisionFrames "}, \ ++ {"StatsDeferredTransmissions "}, \ ++ {"StatsLateCollisions "}, \ ++ {"StatsExcessiveCollsions "}, \ ++ {"StatsInternalMacTransmitErrors"}, \ ++ {"StatsCarrierSenseErrors "}, \ ++ {"TxLargeFrameDiscards "}, \ ++ {"TxVLANIdFilterDiscards "}, \ ++\ ++ {"RxValidFramesTotalOctets "}, \ ++ {"RxUcastPkts "}, \ ++ {"RxBcastPkts "}, \ ++ {"RxMcastPkts "}, \ ++ {"RxPkts64Octets "}, \ ++ {"RxPkts65to127Octets "}, \ ++ {"RxPkts128to255Octets "}, \ ++ {"RxPkts256to511Octets "}, \ ++ {"RxPkts512to1023Octets "}, \ ++ {"RxPkts1024to1518Octets "}, \ ++ {"RxInternalNPEReceiveErrors "}, \ ++ {"TxInternalNPETransmitErrors "} ++ +diff --git a/drivers/net/ixp4xx/mac_driver.c b/drivers/net/ixp4xx/mac_driver.c +new file mode 100644 +index 0000000..2ae78e5 +--- /dev/null ++++ b/drivers/net/ixp4xx/mac_driver.c +@@ -0,0 +1,850 @@ ++/* ++ * mac_driver.c - provide a network interface for each MAC ++ * ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include ++#include ++#include "mac.h" ++ ++#define MDIO_INTERVAL (3*HZ) ++#define RX_QUEUE_PREFILL 64 ++#define TX_QUEUE_PREFILL 16 ++ ++#define IXMAC_NAME "ixp4xx_mac" ++#define IXMAC_VERSION "0.3.1" ++ ++#define MAC_DEFAULT_REG(mac, name) \ ++ mac_write_reg(mac, MAC_ ## name, MAC_ ## name ## _DEFAULT) ++ ++#define TX_DONE_QID 31 ++ ++#define DMA_ALLOC_SIZE 2048 ++#define DMA_HDR_SIZE (sizeof(struct npe_cont)) ++#define DMA_BUF_SIZE (DMA_ALLOC_SIZE - DMA_HDR_SIZE) ++ ++/* Since the NPEs use 1 Return Q for sent frames, we need a device ++ * independent return Q. We call it tx_doneq. ++ * It will be initialized during module load and uninitialized ++ * during module unload. Evil hack, but there is no choice :-( ++ */ ++ ++static struct qm_queue *tx_doneq = NULL; ++static int debug = -1; ++module_param(debug, int, 0); ++ ++static int init_buffer(struct qm_queue *queue, int count) ++{ ++ int i; ++ struct npe_cont *cont; ++ ++ for (i=0; iphys = dma_map_single(queue->dev, cont, DMA_ALLOC_SIZE, ++ DMA_BIDIRECTIONAL); ++ if (dma_mapping_error(cont->phys)) ++ goto err; ++ ++ cont->data = cont+1; ++ /* now the buffer is on a 32 bit boundary. ++ * we add 2 bytes for good alignment to SKB */ ++ cont->data+=2; ++ cont->eth.next = 0; ++ cont->eth.buf_len = cpu_to_npe16(DMA_BUF_SIZE); ++ cont->eth.pkt_len = 0; ++ /* also add 2 alignment bytes from cont->data*/ ++ cont->eth.phys_addr = cpu_to_npe32(cont->phys+ DMA_HDR_SIZE+ 2); ++ ++ dma_sync_single(queue->dev, cont->phys, DMA_HDR_SIZE, ++ DMA_TO_DEVICE); ++ ++ queue_put_entry(queue, cont->phys); ++ if (queue_stat(queue) == 2) { /* overflow */ ++ dma_unmap_single(queue->dev, cont->phys, DMA_ALLOC_SIZE, ++ DMA_BIDIRECTIONAL); ++ goto err; ++ } ++ } ++ return i; ++err: ++ if (cont) ++ kfree(cont); ++ return i; ++} ++ ++static int destroy_buffer(struct qm_queue *queue, int count) ++{ ++ u32 phys; ++ int i; ++ struct npe_cont *cont; ++ ++ for (i=0; idev, phys, DMA_ALLOC_SIZE, ++ DMA_BIDIRECTIONAL); ++ cont = dma_to_virt(queue->dev, phys); ++ kfree(cont); ++ } ++ return i; ++} ++ ++static void mac_init(struct mac_info *mac) ++{ ++ MAC_DEFAULT_REG(mac, TX_CNTRL2); ++ MAC_DEFAULT_REG(mac, RANDOM_SEED); ++ MAC_DEFAULT_REG(mac, THRESH_P_EMPTY); ++ MAC_DEFAULT_REG(mac, THRESH_P_FULL); ++ MAC_DEFAULT_REG(mac, TX_DEFER); ++ MAC_DEFAULT_REG(mac, TX_TWO_DEFER_1); ++ MAC_DEFAULT_REG(mac, TX_TWO_DEFER_2); ++ MAC_DEFAULT_REG(mac, SLOT_TIME); ++ MAC_DEFAULT_REG(mac, INT_CLK_THRESH); ++ MAC_DEFAULT_REG(mac, BUF_SIZE_TX); ++ MAC_DEFAULT_REG(mac, TX_CNTRL1); ++ MAC_DEFAULT_REG(mac, RX_CNTRL1); ++} ++ ++static void mac_set_uniaddr(struct net_device *dev) ++{ ++ int i; ++ struct mac_info *mac = netdev_priv(dev); ++ struct npe_info *npe = dev_get_drvdata(mac->npe_dev); ++ ++ /* check for multicast */ ++ if (dev->dev_addr[0] & 1) ++ return; ++ ++ npe_mh_setportaddr(npe, mac->plat, dev->dev_addr); ++ npe_mh_disable_firewall(npe, mac->plat); ++ for (i=0; iaddr_len; i++) ++ mac_write_reg(mac, MAC_UNI_ADDR + i, dev->dev_addr[i]); ++} ++ ++static void update_duplex_mode(struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ if (netif_msg_link(mac)) { ++ printk(KERN_DEBUG "Link of %s is %s-duplex\n", dev->name, ++ mac->mii.full_duplex ? "full" : "half"); ++ } ++ if (mac->mii.full_duplex) { ++ mac_reset_regbit(mac, MAC_TX_CNTRL1, TX_CNTRL1_DUPLEX); ++ } else { ++ mac_set_regbit(mac, MAC_TX_CNTRL1, TX_CNTRL1_DUPLEX); ++ } ++} ++ ++static int media_check(struct net_device *dev, int init) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ ++ if (mii_check_media(&mac->mii, netif_msg_link(mac), init)) { ++ update_duplex_mode(dev); ++ return 1; ++ } ++ return 0; ++} ++ ++static void get_npe_stats(struct mac_info *mac, u32 *buf, int len, int reset) ++{ ++ struct npe_info *npe = dev_get_drvdata(mac->npe_dev); ++ u32 phys; ++ ++ memset(buf, len, 0); ++ phys = dma_map_single(mac->npe_dev, buf, len, DMA_BIDIRECTIONAL); ++ npe_mh_get_stats(npe, mac->plat, phys, reset); ++ dma_unmap_single(mac->npe_dev, phys, len, DMA_BIDIRECTIONAL); ++} ++ ++static void irqcb_recv(struct qm_queue *queue) ++{ ++ struct net_device *dev = queue->cb_data; ++ ++ queue_ack_irq(queue); ++ queue_disable_irq(queue); ++ if (netif_running(dev)) ++ netif_rx_schedule(dev); ++} ++ ++int ix_recv(struct net_device *dev, int *budget, struct qm_queue *queue) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ struct sk_buff *skb; ++ u32 phys; ++ struct npe_cont *cont; ++ ++ while (*budget > 0 && netif_running(dev) ) { ++ int len; ++ phys = queue_get_entry(queue) & ~0xf; ++ if (!phys) ++ break; ++ dma_sync_single(queue->dev, phys, DMA_HDR_SIZE, ++ DMA_FROM_DEVICE); ++ cont = dma_to_virt(queue->dev, phys); ++ len = npe_to_cpu16(cont->eth.pkt_len) -4; /* strip FCS */ ++ ++ if (unlikely(netif_msg_rx_status(mac))) { ++ printk(KERN_DEBUG "%s: RX packet size: %u\n", ++ dev->name, len); ++ queue_state(mac->rxq); ++ queue_state(mac->rxdoneq); ++ } ++ skb = dev_alloc_skb(len + 2); ++ if (likely(skb)) { ++ skb->dev = dev; ++ skb_reserve(skb, 2); ++ dma_sync_single(queue->dev, cont->eth.phys_addr, len, ++ DMA_FROM_DEVICE); ++#ifdef CONFIG_NPE_ADDRESS_COHERENT ++ /* swap the payload of the SKB */ ++ { ++ u32 *t = (u32*)(skb->data-2); ++ u32 *s = (u32*)(cont->data-2); ++ int i, j = (len+5)/4; ++ for (i=0; idata, len, 0); ++#endif ++ skb_put(skb, len); ++ skb->protocol = eth_type_trans(skb, dev); ++ dev->last_rx = jiffies; ++ netif_receive_skb(skb); ++ mac->stat.rx_packets++; ++ mac->stat.rx_bytes += skb->len; ++ } else { ++ mac->stat.rx_dropped++; ++ } ++ cont->eth.buf_len = cpu_to_npe16(DMA_BUF_SIZE); ++ cont->eth.pkt_len = 0; ++ dma_sync_single(queue->dev, phys, DMA_HDR_SIZE, DMA_TO_DEVICE); ++ queue_put_entry(mac->rxq, phys); ++ dev->quota--; ++ (*budget)--; ++ } ++ ++ return !budget; ++} ++ ++static int ix_poll(struct net_device *dev, int *budget) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ struct qm_queue *queue = mac->rxdoneq; ++ ++ for (;;) { ++ if (ix_recv(dev, budget, queue)) ++ return 1; ++ netif_rx_complete(dev); ++ queue_enable_irq(queue); ++ if (!queue_len(queue)) ++ break; ++ queue_disable_irq(queue); ++ if (netif_rx_reschedule(dev, 0)) ++ break; ++ } ++ return 0; ++} ++ ++static void ixmac_set_rx_mode (struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ struct dev_mc_list *mclist; ++ u8 aset[dev->addr_len], aclear[dev->addr_len]; ++ int i,j; ++ ++ if (dev->flags & IFF_PROMISC) { ++ mac_reset_regbit(mac, MAC_RX_CNTRL1, RX_CNTRL1_ADDR_FLTR_EN); ++ } else { ++ mac_set_regbit(mac, MAC_RX_CNTRL1, RX_CNTRL1_ADDR_FLTR_EN); ++ ++ mclist = dev->mc_list; ++ memset(aset, 0xff, dev->addr_len); ++ memset(aclear, 0x00, dev->addr_len); ++ for (i = 0; mclist && i < dev->mc_count; i++) { ++ for (j=0; j< dev->addr_len; j++) { ++ aset[j] &= mclist->dmi_addr[j]; ++ aclear[j] |= mclist->dmi_addr[j]; ++ } ++ mclist = mclist->next; ++ } ++ for (j=0; j< dev->addr_len; j++) { ++ aclear[j] = aset[j] | ~aclear[j]; ++ } ++ for (i=0; iaddr_len; i++) { ++ mac_write_reg(mac, MAC_ADDR + i, aset[i]); ++ mac_write_reg(mac, MAC_ADDR_MASK + i, aclear[i]); ++ } ++ } ++} ++ ++static int ixmac_open (struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ struct npe_info *npe = dev_get_drvdata(mac->npe_dev); ++ u32 buf[NPE_STAT_NUM]; ++ int i; ++ u32 phys; ++ ++ /* first check if the NPE is up and running */ ++ if (!( npe_status(npe) & IX_NPEDL_EXCTL_STATUS_RUN)) { ++ printk(KERN_ERR "%s: %s not running\n", dev->name, ++ npe->plat->name); ++ return -EIO; ++ } ++ if (npe_mh_status(npe)) { ++ printk(KERN_ERR "%s: %s not responding\n", dev->name, ++ npe->plat->name); ++ return -EIO; ++ } ++ mac->txq_pkt += init_buffer(mac->txq, TX_QUEUE_PREFILL - mac->txq_pkt); ++ mac->rxq_pkt += init_buffer(mac->rxq, RX_QUEUE_PREFILL - mac->rxq_pkt); ++ ++ queue_enable_irq(mac->rxdoneq); ++ ++ /* drain all buffers from then RX-done-q to make the IRQ happen */ ++ while ((phys = queue_get_entry(mac->rxdoneq) & ~0xf)) { ++ struct npe_cont *cont; ++ cont = dma_to_virt(mac->rxdoneq->dev, phys); ++ cont->eth.buf_len = cpu_to_npe16(DMA_BUF_SIZE); ++ cont->eth.pkt_len = 0; ++ dma_sync_single(mac->rxdoneq->dev, phys, DMA_HDR_SIZE, ++ DMA_TO_DEVICE); ++ queue_put_entry(mac->rxq, phys); ++ } ++ mac_init(mac); ++ npe_mh_set_rxqid(npe, mac->plat, mac->plat->rxdoneq_id); ++ get_npe_stats(mac, buf, sizeof(buf), 1); /* reset stats */ ++ get_npe_stats(mac, buf, sizeof(buf), 0); ++ /* ++ * if the extended stats contain random values ++ * the NPE image lacks extendet statistic counters ++ */ ++ for (i=NPE_STAT_NUM_BASE; i10000) ++ break; ++ } ++ mac->npe_stat_num = inpe_stat_num += NPE_Q_STAT_NUM; ++ ++ mac_set_uniaddr(dev); ++ media_check(dev, 1); ++ ixmac_set_rx_mode(dev); ++ netif_start_queue(dev); ++ schedule_delayed_work(&mac->mdio_thread, MDIO_INTERVAL); ++ if (netif_msg_ifup(mac)) { ++ printk(KERN_DEBUG "%s: open " IXMAC_NAME ++ " RX queue %d bufs, TX queue %d bufs\n", ++ dev->name, mac->rxq_pkt, mac->txq_pkt); ++ } ++ return 0; ++} ++ ++static int ixmac_start_xmit (struct sk_buff *skb, struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ struct npe_cont *cont; ++ u32 phys; ++ struct qm_queue *queue = mac->txq; ++ ++ if (unlikely(skb->len > DMA_BUF_SIZE)) { ++ dev_kfree_skb(skb); ++ mac->stat.tx_errors++; ++ return NETDEV_TX_OK; ++ } ++ phys = queue_get_entry(tx_doneq) & ~0xf; ++ if (!phys) ++ goto busy; ++ cont = dma_to_virt(queue->dev, phys); ++#ifdef CONFIG_NPE_ADDRESS_COHERENT ++ /* swap the payload of the SKB */ ++ { ++ u32 *s = (u32*)(skb->data-2); ++ u32 *t = (u32*)(cont->data-2); ++ int i,j = (skb->len+5) / 4; ++ for (i=0; idata); ++ memcpy(cont->data, skb->data, skb->len); ++#endif ++ cont->eth.buf_len = cpu_to_npe16(DMA_BUF_SIZE); ++ cont->eth.pkt_len = cpu_to_npe16(skb->len); ++ /* disable VLAN functions in NPE image for now */ ++ cont->eth.flags = 0; ++ dma_sync_single(queue->dev, phys, skb->len + DMA_HDR_SIZE, ++ DMA_TO_DEVICE); ++ queue_put_entry(queue, phys); ++ if (queue_stat(queue) == 2) { /* overflow */ ++ queue_put_entry(tx_doneq, phys); ++ goto busy; ++ } ++ dev_kfree_skb(skb); ++ ++ mac->stat.tx_packets++; ++ mac->stat.tx_bytes += skb->len; ++ dev->trans_start = jiffies; ++ if (netif_msg_tx_queued(mac)) { ++ printk(KERN_DEBUG "%s: TX packet size %u\n", ++ dev->name, skb->len); ++ queue_state(mac->txq); ++ queue_state(tx_doneq); ++ } ++ return NETDEV_TX_OK; ++busy: ++ return NETDEV_TX_BUSY; ++} ++ ++static int ixmac_close (struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ ++ netif_stop_queue (dev); ++ queue_disable_irq(mac->rxdoneq); ++ ++ mac->txq_pkt -= destroy_buffer(tx_doneq, mac->txq_pkt); ++ mac->rxq_pkt -= destroy_buffer(mac->rxq, mac->rxq_pkt); ++ ++ cancel_rearming_delayed_work(&(mac->mdio_thread)); ++ ++ if (netif_msg_ifdown(mac)) { ++ printk(KERN_DEBUG "%s: close " IXMAC_NAME ++ " RX queue %d bufs, TX queue %d bufs\n", ++ dev->name, mac->rxq_pkt, mac->txq_pkt); ++ } ++ return 0; ++} ++ ++static int ixmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ int rc, duplex_changed; ++ ++ if (!netif_running(dev)) ++ return -EINVAL; ++ if (!try_module_get(THIS_MODULE)) ++ return -ENODEV; ++ rc = generic_mii_ioctl(&mac->mii, if_mii(rq), cmd, &duplex_changed); ++ module_put(THIS_MODULE); ++ if (duplex_changed) ++ update_duplex_mode(dev); ++ return rc; ++} ++ ++static struct net_device_stats *ixmac_stats (struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ return &mac->stat; ++} ++ ++static void ixmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ struct npe_info *npe = dev_get_drvdata(mac->npe_dev); ++ ++ strcpy(info->driver, IXMAC_NAME); ++ strcpy(info->version, IXMAC_VERSION); ++ if (npe_status(npe) & IX_NPEDL_EXCTL_STATUS_RUN) { ++ snprintf(info->fw_version, 32, "%d.%d func [%d]", ++ npe->img_info[2], npe->img_info[3], npe->img_info[1]); ++ } ++ strncpy(info->bus_info, npe->plat->name, ETHTOOL_BUSINFO_LEN); ++} ++ ++static int ixmac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ mii_ethtool_gset(&mac->mii, cmd); ++ return 0; ++} ++ ++static int ixmac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ int rc; ++ rc = mii_ethtool_sset(&mac->mii, cmd); ++ return rc; ++} ++ ++static int ixmac_nway_reset(struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ return mii_nway_restart(&mac->mii); ++} ++ ++static u32 ixmac_get_link(struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ return mii_link_ok(&mac->mii); ++} ++ ++static const int mac_reg_list[] = MAC_REG_LIST; ++ ++static int ixmac_get_regs_len(struct net_device *dev) ++{ ++ return ARRAY_SIZE(mac_reg_list); ++} ++ ++static void ++ixmac_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) ++{ ++ int i; ++ struct mac_info *mac = netdev_priv(dev); ++ u8 *buf = regbuf; ++ ++ for (i=0; ilen; i++) { ++ buf[i] = mac_read_reg(mac, mac_reg_list[i]); ++ } ++} ++ ++static struct { ++ const char str[ETH_GSTRING_LEN]; ++} ethtool_stats_keys[NPE_STAT_NUM + NPE_Q_STAT_NUM] = { ++ NPE_Q_STAT_STRINGS ++ NPE_STAT_STRINGS ++}; ++ ++static void ixmac_get_strings(struct net_device *dev, u32 stringset, u8 *data) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ memcpy(data, ethtool_stats_keys, mac->npe_stat_num * ETH_GSTRING_LEN); ++} ++ ++static int ixmac_get_stats_count(struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ return mac->npe_stat_num; ++} ++ ++static u32 ixmac_get_msglevel(struct net_device *dev) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ return mac->msg_enable; ++} ++ ++static void ixmac_set_msglevel(struct net_device *dev, u32 datum) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ mac->msg_enable = datum; ++} ++ ++static void ixmac_get_ethtool_stats(struct net_device *dev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ int i; ++ struct mac_info *mac = netdev_priv(dev); ++ u32 buf[NPE_STAT_NUM]; ++ ++ data[0] = queue_len(mac->rxq); ++ data[1] = queue_len(mac->rxdoneq); ++ data[2] = queue_len(mac->txq); ++ data[3] = queue_len(tx_doneq); ++ ++ get_npe_stats(mac, buf, sizeof(buf), 0); ++ ++ for (i=0; in_stats-4; i++) { ++ data[i+4] = npe_to_cpu32(buf[i]); ++ } ++} ++ ++static struct ethtool_ops ixmac_ethtool_ops = { ++ .get_drvinfo = ixmac_get_drvinfo, ++ .get_settings = ixmac_get_settings, ++ .set_settings = ixmac_set_settings, ++ .nway_reset = ixmac_nway_reset, ++ .get_link = ixmac_get_link, ++ .get_msglevel = ixmac_get_msglevel, ++ .set_msglevel = ixmac_set_msglevel, ++ .get_regs_len = ixmac_get_regs_len, ++ .get_regs = ixmac_get_regs, ++ .get_perm_addr = ethtool_op_get_perm_addr, ++ .get_strings = ixmac_get_strings, ++ .get_stats_count = ixmac_get_stats_count, ++ .get_ethtool_stats = ixmac_get_ethtool_stats, ++}; ++ ++static void mac_mdio_thread(struct work_struct *work) ++{ ++ struct mac_info *mac = container_of(work, struct mac_info, ++ mdio_thread.work); ++ struct net_device *dev = mac->netdev; ++ ++ media_check(dev, 0); ++ schedule_delayed_work(&mac->mdio_thread, MDIO_INTERVAL); ++} ++ ++static int mac_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct mac_info *mac; ++ struct net_device *dev; ++ struct npe_info *npe; ++ struct mac_plat_info *plat = pdev->dev.platform_data; ++ int size, ret; ++ ++ if (!(res = platform_get_resource(pdev, IORESOURCE_MEM, 0))) { ++ return -EIO; ++ } ++ if (!(dev = alloc_etherdev (sizeof(struct mac_info)))) { ++ return -ENOMEM; ++ } ++ SET_MODULE_OWNER(dev); ++ SET_NETDEV_DEV(dev, &pdev->dev); ++ mac = netdev_priv(dev); ++ mac->netdev = dev; ++ ++ size = res->end - res->start +1; ++ mac->res = request_mem_region(res->start, size, IXMAC_NAME); ++ if (!mac->res) { ++ ret = -EBUSY; ++ goto out_free; ++ } ++ ++ mac->addr = ioremap(res->start, size); ++ if (!mac->addr) { ++ ret = -ENOMEM; ++ goto out_rel; ++ } ++ ++ dev->open = ixmac_open; ++ dev->hard_start_xmit = ixmac_start_xmit; ++ dev->poll = ix_poll; ++ dev->stop = ixmac_close; ++ dev->get_stats = ixmac_stats; ++ dev->do_ioctl = ixmac_ioctl; ++ dev->set_multicast_list = ixmac_set_rx_mode; ++ dev->ethtool_ops = &ixmac_ethtool_ops; ++ ++ dev->weight = 16; ++ dev->tx_queue_len = 100; ++ ++ mac->npe_dev = get_npe_by_id(plat->npe_id); ++ if (!mac->npe_dev) { ++ ret = -EIO; ++ goto out_unmap; ++ } ++ npe = dev_get_drvdata(mac->npe_dev); ++ ++ mac->rxq = request_queue(plat->rxq_id, 128); ++ if (IS_ERR(mac->rxq)) { ++ printk(KERN_ERR "Error requesting Q: %d\n", plat->rxq_id); ++ ret = -EBUSY; ++ goto out_putmod; ++ } ++ mac->txq = request_queue(plat->txq_id, 128); ++ if (IS_ERR(mac->txq)) { ++ printk(KERN_ERR "Error requesting Q: %d\n", plat->txq_id); ++ ret = -EBUSY; ++ goto out_putmod; ++ } ++ mac->rxdoneq = request_queue(plat->rxdoneq_id, 128); ++ if (IS_ERR(mac->rxdoneq)) { ++ printk(KERN_ERR "Error requesting Q: %d\n", plat->rxdoneq_id); ++ ret = -EBUSY; ++ goto out_putmod; ++ } ++ mac->rxdoneq->irq_cb = irqcb_recv; ++ mac->rxdoneq->cb_data = dev; ++ queue_set_watermarks(mac->rxdoneq, 0, 0); ++ queue_set_irq_src(mac->rxdoneq, Q_IRQ_ID_NOT_E); ++ ++ mac->qmgr = dev_get_drvdata(mac->rxq->dev); ++ if (register_netdev (dev)) { ++ ret = -EIO; ++ goto out_putmod; ++ } ++ ++ mac->plat = plat; ++ mac->npe_stat_num = NPE_STAT_NUM_BASE; ++ mac->msg_enable = netif_msg_init(debug, MAC_DEF_MSG_ENABLE); ++ ++ platform_set_drvdata(pdev, dev); ++ ++ mac_write_reg(mac, MAC_CORE_CNTRL, CORE_RESET); ++ udelay(500); ++ mac_write_reg(mac, MAC_CORE_CNTRL, CORE_MDC_EN); ++ ++ init_mdio(dev, plat->phy_id); ++ ++ INIT_DELAYED_WORK(&mac->mdio_thread, mac_mdio_thread); ++ ++ /* The place of the MAC address is very system dependent. ++ * Here we use a random one to be replaced by one of the ++ * following commands: ++ * "ip link set address 02:03:04:04:04:01 dev eth0" ++ * "ifconfig eth0 hw ether 02:03:04:04:04:07" ++ */ ++ ++ if (is_zero_ether_addr(plat->hwaddr)) { ++ random_ether_addr(dev->dev_addr); ++ dev->dev_addr[5] = plat->phy_id; ++ } ++ else ++ memcpy(dev->dev_addr, plat->hwaddr, 6); ++ ++ printk(KERN_INFO IXMAC_NAME " driver " IXMAC_VERSION ++ ": %s on %s with PHY[%d] initialized\n", ++ dev->name, npe->plat->name, plat->phy_id); ++ ++ return 0; ++ ++out_putmod: ++ if (mac->rxq) ++ release_queue(mac->rxq); ++ if (mac->txq) ++ release_queue(mac->txq); ++ if (mac->rxdoneq) ++ release_queue(mac->rxdoneq); ++ module_put(mac->npe_dev->driver->owner); ++out_unmap: ++ iounmap(mac->addr); ++out_rel: ++ release_resource(mac->res); ++out_free: ++ kfree(mac); ++ return ret; ++} ++ ++static void drain_npe(struct mac_info *mac) ++{ ++ struct npe_info *npe = dev_get_drvdata(mac->npe_dev); ++ struct npe_cont *cont; ++ u32 phys; ++ int loop = 0; ++ ++ /* Now there are some skb hold by the NPE. ++ * We switch the MAC in loopback mode and send a pseudo packet ++ * that will be returned by the NPE in its last SKB. ++ * We will also try to isolate the PHY to keep the packets internal. ++ */ ++ ++ if (mac->txq_pkt <2) ++ mac->txq_pkt += init_buffer(tx_doneq, 5); ++ ++ if (npe_status(npe) & IX_NPEDL_EXCTL_STATUS_RUN) { ++ mac_reset_regbit(mac, MAC_CORE_CNTRL, CORE_MDC_EN); ++ mac_set_regbit(mac, MAC_RX_CNTRL1, RX_CNTRL1_LOOP_EN); ++ ++ npe_mh_npe_loopback_mode(npe, mac->plat, 1); ++ mdelay(200); ++ ++ while (mac->rxq_pkt && loop++ < 2000 ) { ++ phys = queue_get_entry(tx_doneq) & ~0xf; ++ if (!phys) ++ break; ++ cont = dma_to_virt(queue->dev, phys); ++ /* actually the packets should never leave the system, ++ * but if they do, they shall contain 0s instead of ++ * intresting random data.... ++ */ ++ memset(cont->data, 0, 64); ++ cont->eth.pkt_len = 64; ++ dma_sync_single(mac->txq->dev, phys, 64 + DMA_HDR_SIZE, ++ DMA_TO_DEVICE); ++ queue_put_entry(mac->txq, phys); ++ if (queue_stat(mac->txq) == 2) { /* overflow */ ++ queue_put_entry(tx_doneq, phys); ++ break; ++ } ++ mdelay(1); ++ mac->rxq_pkt -= destroy_buffer(mac->rxdoneq, ++ mac->rxq_pkt); ++ } ++ npe_mh_npe_loopback_mode(npe, mac->plat, 0); ++ } ++ /* Flush MAC TX fifo to drain the bogus packages */ ++ mac_set_regbit(mac, MAC_CORE_CNTRL, CORE_TX_FIFO_FLUSH); ++ mac_reset_regbit(mac, MAC_RX_CNTRL1, RX_CNTRL1_RX_EN); ++ mac_reset_regbit(mac, MAC_TX_CNTRL1, TX_CNTRL1_TX_EN); ++ mac_reset_regbit(mac, MAC_RX_CNTRL1, RX_CNTRL1_LOOP_EN); ++ mac_reset_regbit(mac, MAC_CORE_CNTRL, CORE_TX_FIFO_FLUSH); ++ mac_reset_regbit(mac, MAC_CORE_CNTRL, CORE_TX_FIFO_FLUSH); ++} ++ ++static int mac_remove(struct platform_device *pdev) ++{ ++ struct net_device* dev = platform_get_drvdata(pdev); ++ struct mac_info *mac = netdev_priv(dev); ++ ++ unregister_netdev(dev); ++ ++ mac->rxq_pkt -= destroy_buffer(mac->rxq, mac->rxq_pkt); ++ if (mac->rxq_pkt) ++ drain_npe(mac); ++ ++ mac->txq_pkt -= destroy_buffer(mac->txq, mac->txq_pkt); ++ mac->txq_pkt -= destroy_buffer(tx_doneq, mac->txq_pkt); ++ ++ if (mac->rxq_pkt || mac->txq_pkt) ++ printk("Buffers lost in NPE: RX:%d, TX:%d\n", ++ mac->rxq_pkt, mac->txq_pkt); ++ ++ release_queue(mac->txq); ++ release_queue(mac->rxq); ++ release_queue(mac->rxdoneq); ++ ++ flush_scheduled_work(); ++ return_npe_dev(mac->npe_dev); ++ ++ iounmap(mac->addr); ++ release_resource(mac->res); ++ platform_set_drvdata(pdev, NULL); ++ free_netdev(dev); ++ return 0; ++} ++ ++static struct platform_driver ixp4xx_mac = { ++ .driver.name = IXMAC_NAME, ++ .probe = mac_probe, ++ .remove = mac_remove, ++}; ++ ++static int __init init_mac(void) ++{ ++ /* The TX done Queue handles skbs sent out by the NPE */ ++ tx_doneq = request_queue(TX_DONE_QID, 128); ++ if (IS_ERR(tx_doneq)) { ++ printk(KERN_ERR "Error requesting Q: %d\n", TX_DONE_QID); ++ return -EBUSY; ++ } ++ return platform_driver_register(&ixp4xx_mac); ++} ++ ++static void __exit finish_mac(void) ++{ ++ platform_driver_unregister(&ixp4xx_mac); ++ if (tx_doneq) { ++ release_queue(tx_doneq); ++ } ++} ++ ++module_init(init_mac); ++module_exit(finish_mac); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Christian Hohnstaedt "); ++ +diff --git a/drivers/net/ixp4xx/npe.c b/drivers/net/ixp4xx/npe.c +new file mode 100644 +index 0000000..2567298 +--- /dev/null ++++ b/drivers/net/ixp4xx/npe.c +@@ -0,0 +1,291 @@ ++ ++#include ++#include ++ ++#define RESET_NPE_PARITY 0x0800 ++#define PARITY_BIT_MASK 0x3F00FFFF ++#define CONFIG_CTRL_REG_MASK 0x3F3FFFFF ++#define MAX_RETRIES 1000000 ++#define NPE_PHYS_REG 32 ++#define RESET_MBST_VAL 0x0000F0F0 ++#define NPE_REGMAP 0x0000001E ++#define INSTR_WR_REG_SHORT 0x0000C000 ++#define INSTR_WR_REG_BYTE 0x00004000 ++#define MASK_ECS_REG_0_NEXTPC 0x1FFF0000 ++ ++#define INSTR_RD_FIFO 0x0F888220 ++#define INSTR_RESET_MBOX 0x0FAC8210 ++ ++#define ECS_REG_0_LDUR 8 ++#define ECS_REG_1_CCTXT 16 ++#define ECS_REG_1_SELCTXT 0 ++ ++#define ECS_BG_CTXT_REG_0 0x00 ++#define ECS_BG_CTXT_REG_1 0x01 ++#define ECS_BG_CTXT_REG_2 0x02 ++#define ECS_PRI_1_CTXT_REG_0 0x04 ++#define ECS_PRI_1_CTXT_REG_1 0x05 ++#define ECS_PRI_1_CTXT_REG_2 0x06 ++#define ECS_PRI_2_CTXT_REG_0 0x08 ++#define ECS_PRI_2_CTXT_REG_1 0x09 ++#define ECS_PRI_2_CTXT_REG_2 0x0A ++#define ECS_DBG_CTXT_REG_0 0x0C ++#define ECS_DBG_CTXT_REG_1 0x0D ++#define ECS_DBG_CTXT_REG_2 0x0E ++#define ECS_INSTRUCT_REG 0x11 ++ ++#define ECS_BG_CTXT_REG_0_RESET 0xA0000000 ++#define ECS_BG_CTXT_REG_1_RESET 0x01000000 ++#define ECS_BG_CTXT_REG_2_RESET 0x00008000 ++#define ECS_PRI_1_CTXT_REG_0_RESET 0x20000080 ++#define ECS_PRI_1_CTXT_REG_1_RESET 0x01000000 ++#define ECS_PRI_1_CTXT_REG_2_RESET 0x00008000 ++#define ECS_PRI_2_CTXT_REG_0_RESET 0x20000080 ++#define ECS_PRI_2_CTXT_REG_1_RESET 0x01000000 ++#define ECS_PRI_2_CTXT_REG_2_RESET 0x00008000 ++#define ECS_DBG_CTXT_REG_0_RESET 0x20000000 ++#define ECS_DBG_CTXT_REG_1_RESET 0x00000000 ++#define ECS_DBG_CTXT_REG_2_RESET 0x001E0000 ++#define ECS_INSTRUCT_REG_RESET 0x1003C00F ++ ++static struct { u32 reg; u32 val; } ecs_reset[] = ++{ ++ { ECS_BG_CTXT_REG_0, ECS_BG_CTXT_REG_0_RESET }, ++ { ECS_BG_CTXT_REG_1, ECS_BG_CTXT_REG_1_RESET }, ++ { ECS_BG_CTXT_REG_2, ECS_BG_CTXT_REG_2_RESET }, ++ { ECS_PRI_1_CTXT_REG_0, ECS_PRI_1_CTXT_REG_0_RESET }, ++ { ECS_PRI_1_CTXT_REG_1, ECS_PRI_1_CTXT_REG_1_RESET }, ++ { ECS_PRI_1_CTXT_REG_2, ECS_PRI_1_CTXT_REG_2_RESET }, ++ { ECS_PRI_2_CTXT_REG_0, ECS_PRI_2_CTXT_REG_0_RESET }, ++ { ECS_PRI_2_CTXT_REG_1, ECS_PRI_2_CTXT_REG_1_RESET }, ++ { ECS_PRI_2_CTXT_REG_2, ECS_PRI_2_CTXT_REG_2_RESET }, ++ { ECS_DBG_CTXT_REG_0, ECS_DBG_CTXT_REG_0_RESET }, ++ { ECS_DBG_CTXT_REG_1, ECS_DBG_CTXT_REG_1_RESET }, ++ { ECS_DBG_CTXT_REG_2, ECS_DBG_CTXT_REG_2_RESET }, ++ { ECS_INSTRUCT_REG, ECS_INSTRUCT_REG_RESET } ++}; ++ ++/* actually I have no idea what I'm doing here !! ++ * I only rewrite the "reset" sequence the way Intel does it. ++ */ ++ ++static void npe_debg_preexec(struct npe_info *npe) ++{ ++ u32 r = IX_NPEDL_MASK_ECS_DBG_REG_2_IF | IX_NPEDL_MASK_ECS_DBG_REG_2_IE; ++ ++ npe->exec_count = npe_reg_read(npe, IX_NPEDL_REG_OFFSET_EXCT); ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_EXCT, 0); ++ npe->ctx_reg2 = npe_read_ecs_reg(npe, ECS_DBG_CTXT_REG_2); ++ npe_write_ecs_reg(npe, ECS_DBG_CTXT_REG_2, npe->ctx_reg2 | r); ++} ++ ++static void npe_debg_postexec(struct npe_info *npe) ++{ ++ npe_write_ecs_reg(npe, ECS_DBG_CTXT_REG_0, 0); ++ npe_write_exctl(npe, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE); ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_EXCT, npe->exec_count); ++ npe_write_ecs_reg(npe, ECS_DBG_CTXT_REG_2, npe->ctx_reg2); ++} ++ ++static int ++npe_debg_inst_exec(struct npe_info *npe, u32 instr, u32 ctx, u32 ldur) ++{ ++ u32 regval, wc; ++ int c = 0; ++ ++ regval = IX_NPEDL_MASK_ECS_REG_0_ACTIVE | ++ (ldur << ECS_REG_0_LDUR); ++ npe_write_ecs_reg(npe, ECS_DBG_CTXT_REG_0 , regval); ++ /* set CCTXT at ECS DEBUG L3 to specify in which context ++ * to execute the instruction ++ */ ++ regval = (ctx << ECS_REG_1_CCTXT) | ++ (ctx << ECS_REG_1_SELCTXT); ++ npe_write_ecs_reg(npe, ECS_DBG_CTXT_REG_1, regval); ++ ++ /* clear the pipeline */ ++ npe_write_exctl(npe, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE); ++ ++ /* load NPE instruction into the instruction register */ ++ npe_write_ecs_reg(npe, ECS_INSTRUCT_REG, instr); ++ /* we need this value later to wait for ++ * completion of NPE execution step ++ */ ++ wc = npe_reg_read(npe, IX_NPEDL_REG_OFFSET_WC); ++ npe_write_exctl(npe, IX_NPEDL_EXCTL_CMD_NPE_STEP); ++ ++ /* Watch Count register increments when NPE completes an instruction */ ++ while (wc == npe_reg_read(npe, IX_NPEDL_REG_OFFSET_WC) && ++ ++c < MAX_RETRIES); ++ ++ if (c >= MAX_RETRIES) { ++ printk(KERN_ERR "%s reset:npe_debg_inst_exec(): Timeout\n", ++ npe->plat->name); ++ return 1; ++ } ++ return 0; ++} ++ ++static int npe_logical_reg_write8(struct npe_info *npe, u32 addr, u32 val) ++{ ++ u32 instr; ++ val &= 0xff; ++ /* here we build the NPE assembler instruction: ++ * mov8 d0, #0 */ ++ 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. */ ++ /* and execute it */ ++ return npe_debg_inst_exec(npe, instr, 0, 1); ++} ++ ++static int npe_logical_reg_write16(struct npe_info *npe, u32 addr, u32 val) ++{ ++ u32 instr; ++ /* here we build the NPE assembler instruction: ++ * mov16 d0, #0 */ ++ val &= 0xffff; ++ 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. */ ++ /* and execute it */ ++ return npe_debg_inst_exec(npe, instr, 0, 1); ++} ++ ++static int npe_logical_reg_write32(struct npe_info *npe, u32 addr, u32 val) ++{ ++ /* write in 16 bit steps first the high and then the low value */ ++ npe_logical_reg_write16(npe, addr, val >> 16); ++ return npe_logical_reg_write16(npe, addr+2, val & 0xffff); ++} ++ ++void npe_reset(struct npe_info *npe) ++{ ++ u32 reg, cfg_ctrl; ++ int i; ++ struct { u32 reset; int addr; int size; } ctx_reg[] = { ++ { 0x80, 0x1b, 8 }, ++ { 0, 0x1c, 16 }, ++ { 0x820, 0x1e, 16 }, ++ { 0, 0x1f, 8 } ++ }, *cr; ++ ++ cfg_ctrl = npe_reg_read(npe, IX_NPEDL_REG_OFFSET_CTL); ++ cfg_ctrl |= 0x3F000000; ++ /* disable the parity interrupt */ ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_CTL, cfg_ctrl & PARITY_BIT_MASK); ++ ++ npe_debg_preexec(npe); ++ ++ /* clear the FIFOs */ ++ while (npe_reg_read(npe, IX_NPEDL_REG_OFFSET_WFIFO) == ++ IX_NPEDL_MASK_WFIFO_VALID); ++ while (npe_reg_read(npe, IX_NPEDL_REG_OFFSET_STAT) == ++ IX_NPEDL_MASK_STAT_OFNE) ++ { ++ u32 reg; ++ reg = npe_reg_read(npe, IX_NPEDL_REG_OFFSET_FIFO); ++ printk("%s reset: Read FIFO:=%x\n", npe->plat->name, reg); ++ } ++ while (npe_reg_read(npe, IX_NPEDL_REG_OFFSET_STAT) == ++ IX_NPEDL_MASK_STAT_IFNE) { ++ npe_debg_inst_exec(npe, INSTR_RD_FIFO, 0, 0); ++ } ++ ++ /* Reset the mailbox reg */ ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_MBST, RESET_MBST_VAL); ++ npe_debg_inst_exec(npe, INSTR_RESET_MBOX, 0, 0); ++ ++ /* Reset the physical registers in the NPE register file */ ++ for (i=0; i> 1); ++ npe_logical_reg_write32(npe, (i&1) *4, 0); ++ } ++ ++ /* Reset the context store. Iterate over the 16 ctx s */ ++ for(i=0; i<16; i++) { ++ for (reg=0; reg<4; reg++) { ++ /* There is no (STEVT) register for Context 0. ++ * ignore if register=0 and ctx=0 */ ++ if (!(reg || i)) ++ continue; ++ /* Context 0 has no STARTPC. Instead, this value is ++ * used to set NextPC for Background ECS, ++ * to set where NPE starts executing code ++ */ ++ if (!i && reg==1) { ++ u32 r; ++ r = npe_read_ecs_reg(npe, ECS_BG_CTXT_REG_0); ++ r &= ~MASK_ECS_REG_0_NEXTPC; ++ r |= (cr->reset << 16) & MASK_ECS_REG_0_NEXTPC; ++ continue; ++ } ++ cr = ctx_reg + reg; ++ switch (cr->size) { ++ case 8: ++ npe_logical_reg_write8(npe, cr->addr, ++ cr->reset); ++ break; ++ case 16: ++ npe_logical_reg_write16(npe, cr->addr, ++ cr->reset); ++ } ++ } ++ } ++ npe_debg_postexec(npe); ++ ++ for (i=0; i< ARRAY_SIZE(ecs_reset); i++) { ++ npe_write_ecs_reg(npe, ecs_reset[i].reg, ecs_reset[i].val); ++ } ++ npe_write_exctl(npe, IX_NPEDL_EXCTL_CMD_CLR_PROFILE_CNT); ++ ++ for (i=IX_NPEDL_REG_OFFSET_EXCT; i<=IX_NPEDL_REG_OFFSET_AP3; i+=4) { ++ npe_reg_write(npe, i, 0); ++ } ++ ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_WC, 0); ++ ++ reg = *IXP4XX_EXP_CFG2; ++ reg |= 0x800 << npe->plat->id; /* IX_FUSE_NPE[ABC] */ ++ *IXP4XX_EXP_CFG2 = reg; ++ reg &= ~(0x800 << npe->plat->id); /* IX_FUSE_NPE[ABC] */ ++ *IXP4XX_EXP_CFG2 = reg; ++ ++ npe_stop(npe); ++ ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_CTL, ++ cfg_ctrl & CONFIG_CTRL_REG_MASK); ++ npe->loaded = 0; ++} ++ ++ ++void npe_stop(struct npe_info *npe) ++{ ++ npe_write_exctl(npe, IX_NPEDL_EXCTL_CMD_NPE_STOP); ++ npe_write_exctl(npe, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE); ++} ++ ++static void npe_reset_active(struct npe_info *npe, u32 reg) ++{ ++ u32 regval; ++ ++ regval = npe_read_ecs_reg(npe, reg); ++ regval &= ~IX_NPEDL_MASK_ECS_REG_0_ACTIVE; ++ npe_write_ecs_reg(npe, reg, regval); ++} ++ ++void npe_start(struct npe_info *npe) ++{ ++ npe_reset_active(npe, IX_NPEDL_ECS_PRI_1_CTXT_REG_0); ++ npe_reset_active(npe, IX_NPEDL_ECS_PRI_2_CTXT_REG_0); ++ npe_reset_active(npe, IX_NPEDL_ECS_DBG_CTXT_REG_0); ++ ++ npe_write_exctl(npe, IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE); ++ npe_write_exctl(npe, IX_NPEDL_EXCTL_CMD_NPE_START); ++} ++ ++EXPORT_SYMBOL(npe_stop); ++EXPORT_SYMBOL(npe_start); ++EXPORT_SYMBOL(npe_reset); +diff --git a/drivers/net/ixp4xx/npe_mh.c b/drivers/net/ixp4xx/npe_mh.c +new file mode 100644 +index 0000000..c1ebde5 +--- /dev/null ++++ b/drivers/net/ixp4xx/npe_mh.c +@@ -0,0 +1,170 @@ ++/* ++ * npe_mh.c - NPE message handler. ++ * ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++#include ++#include ++ ++#define MAX_RETRY 200 ++ ++struct npe_mh_msg { ++ union { ++ u8 byte[8]; /* Very desciptive name, I know ... */ ++ u32 data[2]; ++ } u; ++}; ++ ++/* ++ * The whole code in this function must be reworked. ++ * It is in a state that works but is not rock solid ++ */ ++static int send_message(struct npe_info *npe, struct npe_mh_msg *msg) ++{ ++ int i,j; ++ u32 send[2], recv[2]; ++ ++ for (i=0; i<2; i++) ++ send[i] = be32_to_cpu(msg->u.data[i]); ++ ++ if ((npe_reg_read(npe, IX_NPEDL_REG_OFFSET_STAT) & ++ IX_NPEMH_NPE_STAT_IFNE)) ++ return -1; ++ ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_FIFO, send[0]); ++ for(i=0; i=MAX_RETRY) ++ return -1; ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_FIFO, send[1]); ++ i=0; ++ while (!(npe_reg_read(npe, IX_NPEDL_REG_OFFSET_STAT) & ++ IX_NPEMH_NPE_STAT_OFNE)) { ++ if (i++>MAX_RETRY) { ++ printk("Waiting for Output FIFO NotEmpty failed\n"); ++ return -1; ++ } ++ } ++ //printk("Output FIFO Not Empty. Loops: %d\n", i); ++ j=0; ++ while (npe_reg_read(npe, IX_NPEDL_REG_OFFSET_STAT) & ++ IX_NPEMH_NPE_STAT_OFNE) { ++ recv[j&1] = npe_reg_read(npe,IX_NPEDL_REG_OFFSET_FIFO); ++ j++; ++ } ++ if ((recv[0] != send[0]) || (recv[1] != send[1])) { ++ if (send[0] || send[1]) { ++ /* all CMDs return the complete message as answer, ++ * only GETSTATUS returns the ImageID of the NPE ++ */ ++ printk("Unexpected answer: " ++ "Send %08x:%08x Ret %08x:%08x\n", ++ send[0], send[1], recv[0], recv[1]); ++ } ++ } ++ return 0; ++} ++ ++#define CMD 0 ++#define PORT 1 ++#define MAC 2 ++ ++#define IX_ETHNPE_NPE_GETSTATUS 0x00 ++#define IX_ETHNPE_EDB_SETPORTADDRESS 0x01 ++#define IX_ETHNPE_GETSTATS 0x04 ++#define IX_ETHNPE_RESETSTATS 0x05 ++#define IX_ETHNPE_FW_SETFIREWALLMODE 0x0E ++#define IX_ETHNPE_VLAN_SETRXQOSENTRY 0x0B ++#define IX_ETHNPE_SETLOOPBACK_MODE 0x12 ++ ++#define logical_id(mp) (((mp)->npe_id << 4) | ((mp)->port_id & 0xf)) ++ ++int npe_mh_status(struct npe_info *npe) ++{ ++ struct npe_mh_msg msg; ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.u.byte[CMD] = IX_ETHNPE_NPE_GETSTATUS; ++ return send_message(npe, &msg); ++} ++ ++int npe_mh_setportaddr(struct npe_info *npe, struct mac_plat_info *mp, ++ u8 *macaddr) ++{ ++ struct npe_mh_msg msg; ++ ++ msg.u.byte[CMD] = IX_ETHNPE_EDB_SETPORTADDRESS; ++ msg.u.byte[PORT] = mp->eth_id; ++ memcpy(msg.u.byte + MAC, macaddr, 6); ++ ++ return send_message(npe, &msg); ++} ++ ++int npe_mh_disable_firewall(struct npe_info *npe, struct mac_plat_info *mp) ++{ ++ struct npe_mh_msg msg; ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.u.byte[CMD] = IX_ETHNPE_FW_SETFIREWALLMODE; ++ msg.u.byte[PORT] = logical_id(mp); ++ ++ return send_message(npe, &msg); ++} ++ ++int npe_mh_npe_loopback_mode(struct npe_info *npe, struct mac_plat_info *mp, ++ int enable) ++{ ++ struct npe_mh_msg msg; ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.u.byte[CMD] = IX_ETHNPE_SETLOOPBACK_MODE; ++ msg.u.byte[PORT] = logical_id(mp); ++ msg.u.byte[3] = enable ? 1 : 0; ++ ++ return send_message(npe, &msg); ++} ++ ++int npe_mh_set_rxqid(struct npe_info *npe, struct mac_plat_info *mp, int qid) ++{ ++ struct npe_mh_msg msg; ++ int i, ret; ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.u.byte[CMD] = IX_ETHNPE_VLAN_SETRXQOSENTRY; ++ msg.u.byte[PORT] = logical_id(mp); ++ msg.u.byte[5] = qid | 0x80; ++ msg.u.byte[7] = qid<<4; ++ for(i=0; i<8; i++) { ++ msg.u.byte[3] = i; ++ if ((ret = send_message(npe, &msg))) ++ return ret; ++ } ++ return 0; ++} ++ ++int npe_mh_get_stats(struct npe_info *npe, struct mac_plat_info *mp, u32 phys, ++ int reset) ++{ ++ struct npe_mh_msg msg; ++ memset(&msg, 0, sizeof(msg)); ++ msg.u.byte[CMD] = reset ? IX_ETHNPE_RESETSTATS : IX_ETHNPE_GETSTATS; ++ msg.u.byte[PORT] = logical_id(mp); ++ msg.u.data[1] = cpu_to_npe32(cpu_to_be32(phys)); ++ ++ return send_message(npe, &msg); ++} ++ ++ ++EXPORT_SYMBOL(npe_mh_status); ++EXPORT_SYMBOL(npe_mh_setportaddr); ++EXPORT_SYMBOL(npe_mh_disable_firewall); ++EXPORT_SYMBOL(npe_mh_set_rxqid); ++EXPORT_SYMBOL(npe_mh_npe_loopback_mode); ++EXPORT_SYMBOL(npe_mh_get_stats); +diff --git a/drivers/net/ixp4xx/phy.c b/drivers/net/ixp4xx/phy.c +new file mode 100644 +index 0000000..5db2cf8 +--- /dev/null ++++ b/drivers/net/ixp4xx/phy.c +@@ -0,0 +1,113 @@ ++/* ++ * phy.c - MDIO functions and mii initialisation ++ * ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++ ++#include ++#include "mac.h" ++ ++#define MAX_PHYS (1<<5) ++ ++/* ++ * We must always use the same MAC for acessing the MDIO ++ * We may not use each MAC for its PHY :-( ++ */ ++ ++static struct net_device *phy_dev = NULL; ++static struct mutex mtx; ++ ++/* here we remember if the PHY is alive, to avoid log dumping */ ++static int phy_works[MAX_PHYS]; ++ ++int mdio_read_register(struct net_device *dev, int phy_addr, int phy_reg) ++{ ++ struct mac_info *mac; ++ u32 cmd, reg; ++ int cnt = 0; ++ ++ if (!phy_dev) ++ return 0; ++ ++ mac = netdev_priv(phy_dev); ++ cmd = mdio_cmd(phy_addr, phy_reg); ++ mutex_lock_interruptible(&mtx); ++ mac_mdio_cmd_write(mac, cmd); ++ while((cmd = mac_mdio_cmd_read(mac)) & MII_GO) { ++ if (++cnt >= 100) { ++ printk("%s: PHY[%d] access failed\n", ++ dev->name, phy_addr); ++ break; ++ } ++ schedule(); ++ } ++ reg = mac_mdio_status_read(mac); ++ mutex_unlock(&mtx); ++ if (reg & MII_READ_FAIL) { ++ if (phy_works[phy_addr]) { ++ printk("%s: PHY[%d] unresponsive\n", ++ dev->name, phy_addr); ++ } ++ reg = 0; ++ phy_works[phy_addr] = 0; ++ } else { ++ if ( !phy_works[phy_addr]) { ++ printk("%s: PHY[%d] responsive again\n", ++ dev->name, phy_addr); ++ } ++ phy_works[phy_addr] = 1; ++ } ++ return reg & 0xffff; ++} ++ ++void ++mdio_write_register(struct net_device *dev, int phy_addr, int phy_reg, int val) ++{ ++ struct mac_info *mac; ++ u32 cmd; ++ int cnt=0; ++ ++ if (!phy_dev) ++ return; ++ ++ mac = netdev_priv(phy_dev); ++ cmd = mdio_cmd(phy_addr, phy_reg) | MII_WRITE | val; ++ ++ mutex_lock_interruptible(&mtx); ++ mac_mdio_cmd_write(mac, cmd); ++ while((cmd = mac_mdio_cmd_read(mac)) & MII_GO) { ++ if (++cnt >= 100) { ++ printk("%s: PHY[%d] access failed\n", ++ dev->name, phy_addr); ++ break; ++ } ++ schedule(); ++ } ++ mutex_unlock(&mtx); ++} ++ ++void init_mdio(struct net_device *dev, int phy_id) ++{ ++ struct mac_info *mac = netdev_priv(dev); ++ int i; ++ ++ /* All phy operations should use the same MAC ++ * (my experience) ++ */ ++ if (mac->plat->eth_id == 0) { ++ mutex_init(&mtx); ++ phy_dev = dev; ++ for (i=0; imii.dev = dev; ++ mac->mii.phy_id = phy_id; ++ mac->mii.phy_id_mask = MAX_PHYS - 1; ++ mac->mii.reg_num_mask = 0x1f; ++ mac->mii.mdio_read = mdio_read_register; ++ mac->mii.mdio_write = mdio_write_register; ++} ++ +diff --git a/drivers/net/ixp4xx/ucode_dl.c b/drivers/net/ixp4xx/ucode_dl.c +new file mode 100644 +index 0000000..c1cfcfd +--- /dev/null ++++ b/drivers/net/ixp4xx/ucode_dl.c +@@ -0,0 +1,479 @@ ++/* ++ * ucode_dl.c - provide an NPE device and a char-dev for microcode download ++ * ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define IXNPE_VERSION "IXP4XX NPE driver Version 0.3.0" ++ ++#define DL_MAGIC 0xfeedf00d ++#define DL_MAGIC_SWAP 0x0df0edfe ++ ++#define EOF_BLOCK 0xf ++#define IMG_SIZE(image) (((image)->size * sizeof(u32)) + \ ++ sizeof(struct dl_image)) ++ ++#define BT_INSTR 0 ++#define BT_DATA 1 ++ ++enum blk_type { ++ instruction, ++ data, ++}; ++ ++struct dl_block { ++ u32 type; ++ u32 offset; ++}; ++ ++struct dl_image { ++ u32 magic; ++ u32 id; ++ u32 size; ++ union { ++ u32 data[0]; ++ struct dl_block block[0]; ++ } u; ++}; ++ ++struct dl_codeblock { ++ u32 npe_addr; ++ u32 size; ++ u32 data[0]; ++}; ++ ++static struct platform_driver ixp4xx_npe_driver; ++ ++static int match_by_npeid(struct device *dev, void *id) ++{ ++ struct npe_info *npe = dev_get_drvdata(dev); ++ if (!npe->plat) ++ return 0; ++ return (npe->plat->id == *(int*)id); ++} ++ ++struct device *get_npe_by_id(int id) ++{ ++ struct device *dev = driver_find_device(&ixp4xx_npe_driver.driver, ++ NULL, &id, match_by_npeid); ++ if (dev) { ++ struct npe_info *npe = dev_get_drvdata(dev); ++ if (!try_module_get(THIS_MODULE)) { ++ put_device(dev); ++ return NULL; ++ } ++ npe->usage++; ++ } ++ return dev; ++} ++ ++void return_npe_dev(struct device *dev) ++{ ++ struct npe_info *npe = dev_get_drvdata(dev); ++ put_device(dev); ++ module_put(THIS_MODULE); ++ npe->usage--; ++} ++ ++static int ++download_block(struct npe_info *npe, struct dl_codeblock *cb, unsigned type) ++{ ++ int i; ++ int cmd; ++ ++ switch (type) { ++ case BT_DATA: ++ cmd = IX_NPEDL_EXCTL_CMD_WR_DATA_MEM; ++ if (cb->npe_addr + cb->size > npe->plat->data_size) { ++ printk(KERN_INFO "Data size too large: %d+%d > %d\n", ++ cb->npe_addr, cb->size, npe->plat->data_size); ++ return -EIO; ++ } ++ break; ++ case BT_INSTR: ++ cmd = IX_NPEDL_EXCTL_CMD_WR_INS_MEM; ++ if (cb->npe_addr + cb->size > npe->plat->inst_size) { ++ printk(KERN_INFO "Instr size too large: %d+%d > %d\n", ++ cb->npe_addr, cb->size, npe->plat->inst_size); ++ return -EIO; ++ } ++ break; ++ default: ++ printk(KERN_INFO "Unknown CMD: %d\n", type); ++ return -EIO; ++ } ++ ++ for (i=0; i < cb->size; i++) { ++ npe_write_cmd(npe, cb->npe_addr + i, cb->data[i], cmd); ++ } ++ ++ return 0; ++} ++ ++static int store_npe_image(struct dl_image *image, struct device *dev) ++{ ++ struct dl_block *blk; ++ struct dl_codeblock *cb; ++ struct npe_info *npe; ++ int ret=0; ++ ++ if (!dev) { ++ dev = get_npe_by_id( (image->id >> 24) & 0xf); ++ return_npe_dev(dev); ++ } ++ if (!dev) ++ return -ENODEV; ++ ++ npe = dev_get_drvdata(dev); ++ if (npe->loaded && (npe->usage > 0)) { ++ printk(KERN_INFO "Cowardly refusing to reload an Image " ++ "into the used and running %s\n", npe->plat->name); ++ return 0; /* indicate success anyway... */ ++ } ++ if (!cpu_is_ixp46x() && ((image->id >> 28) & 0xf)) { ++ printk(KERN_INFO "IXP46x NPE image ignored on IXP42x\n"); ++ return -EIO; ++ } ++ ++ npe_stop(npe); ++ npe_reset(npe); ++ ++ for (blk = image->u.block; blk->type != EOF_BLOCK; blk++) { ++ if (blk->offset > image->size) { ++ printk(KERN_INFO "Block offset out of range\n"); ++ return -EIO; ++ } ++ cb = (struct dl_codeblock*)&image->u.data[blk->offset]; ++ if (blk->offset + cb->size + 2 > image->size) { ++ printk(KERN_INFO "Codeblock size out of range\n"); ++ return -EIO; ++ } ++ if ((ret = download_block(npe, cb, blk->type))) ++ return ret; ++ } ++ *(u32*)npe->img_info = cpu_to_be32(image->id); ++ npe_start(npe); ++ ++ printk(KERN_INFO "Image loaded to %s Func:%x, Rel: %x:%x, Status: %x\n", ++ npe->plat->name, npe->img_info[1], npe->img_info[2], ++ npe->img_info[3], npe_status(npe)); ++ if (npe_mh_status(npe)) { ++ printk(KERN_ERR "%s not responding\n", npe->plat->name); ++ } ++ npe->loaded = 1; ++ return 0; ++} ++ ++static int ucode_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = kmalloc(sizeof(struct dl_image), GFP_KERNEL); ++ if (!file->private_data) ++ return -ENOMEM; ++ return 0; ++} ++ ++static int ucode_close(struct inode *inode, struct file *file) ++{ ++ kfree(file->private_data); ++ return 0; ++} ++ ++static ssize_t ucode_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ union { ++ char *data; ++ struct dl_image *image; ++ } u; ++ const char __user *cbuf = buf; ++ ++ u.data = file->private_data; ++ ++ while (count) { ++ int len; ++ if (*ppos < sizeof(struct dl_image)) { ++ len = sizeof(struct dl_image) - *ppos; ++ len = len > count ? count : len; ++ if (copy_from_user(u.data + *ppos, cbuf, len)) ++ return -EFAULT; ++ count -= len; ++ *ppos += len; ++ cbuf += len; ++ continue; ++ } else if (*ppos == sizeof(struct dl_image)) { ++ void *data; ++ if (u.image->magic == DL_MAGIC_SWAP) { ++ printk(KERN_INFO "swapped image found\n"); ++ u.image->id = swab32(u.image->id); ++ u.image->size = swab32(u.image->size); ++ } else if (u.image->magic != DL_MAGIC) { ++ printk(KERN_INFO "Bad magic:%x\n", ++ u.image->magic); ++ return -EFAULT; ++ } ++ len = IMG_SIZE(u.image); ++ data = kmalloc(len, GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ memcpy(data, u.data, *ppos); ++ kfree(u.data); ++ u.data = (char*)data; ++ file->private_data = data; ++ } ++ len = IMG_SIZE(u.image) - *ppos; ++ len = len > count ? count : len; ++ if (copy_from_user(u.data + *ppos, cbuf, len)) ++ return -EFAULT; ++ count -= len; ++ *ppos += len; ++ cbuf += len; ++ if (*ppos == IMG_SIZE(u.image)) { ++ int ret, i; ++ *ppos = 0; ++ if (u.image->magic == DL_MAGIC_SWAP) { ++ for (i=0; isize; i++) { ++ u.image->u.data[i] = ++ swab32(u.image->u.data[i]); ++ } ++ u.image->magic = swab32(u.image->magic); ++ } ++ ret = store_npe_image(u.image, NULL); ++ if (ret) { ++ printk(KERN_INFO "Error in NPE image: %x\n", ++ u.image->id); ++ return ret; ++ } ++ } ++ } ++ return (cbuf-buf); ++} ++ ++static void npe_firmware_probe(struct device *dev) ++{ ++#if (defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)) \ ++ && defined(MODULE) ++ const struct firmware *fw_entry; ++ struct npe_info *npe = dev_get_drvdata(dev); ++ struct dl_image *image; ++ int ret = -1, i; ++ ++ if (request_firmware(&fw_entry, npe->plat->name, dev) != 0) { ++ return; ++ } ++ image = (struct dl_image*)fw_entry->data; ++ /* Sanity checks */ ++ if (fw_entry->size < sizeof(struct dl_image)) { ++ printk(KERN_ERR "Firmware error: too small\n"); ++ goto out; ++ } ++ if (image->magic == DL_MAGIC_SWAP) { ++ printk(KERN_INFO "swapped image found\n"); ++ image->id = swab32(image->id); ++ image->size = swab32(image->size); ++ } else if (image->magic != DL_MAGIC) { ++ printk(KERN_ERR "Bad magic:%x\n", image->magic); ++ goto out; ++ } ++ if (IMG_SIZE(image) != fw_entry->size) { ++ printk(KERN_ERR "Firmware error: bad size\n"); ++ goto out; ++ } ++ if (((image->id >> 24) & 0xf) != npe->plat->id) { ++ printk(KERN_ERR "NPE id missmatch\n"); ++ goto out; ++ } ++ if (image->magic == DL_MAGIC_SWAP) { ++ for (i=0; isize; i++) { ++ image->u.data[i] = swab32(image->u.data[i]); ++ } ++ image->magic = swab32(image->magic); ++ } ++ ++ ret = store_npe_image(image, dev); ++out: ++ if (ret) { ++ printk(KERN_ERR "Error downloading Firmware for %s\n", ++ npe->plat->name); ++ } ++ release_firmware(fw_entry); ++#endif ++} ++ ++static void disable_npe_irq(struct npe_info *npe) ++{ ++ u32 reg; ++ reg = npe_reg_read(npe, IX_NPEDL_REG_OFFSET_CTL); ++ reg &= ~(IX_NPEMH_NPE_CTL_OFE | IX_NPEMH_NPE_CTL_IFE); ++ reg |= IX_NPEMH_NPE_CTL_OFEWE | IX_NPEMH_NPE_CTL_IFEWE; ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_CTL, reg); ++} ++ ++static ssize_t show_npe_state(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct npe_info *npe = dev_get_drvdata(dev); ++ ++ strcpy(buf, npe_status(npe) & IX_NPEDL_EXCTL_STATUS_RUN ? ++ "start\n" : "stop\n"); ++ return strlen(buf); ++} ++ ++static ssize_t set_npe_state(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct npe_info *npe = dev_get_drvdata(dev); ++ ++ if (npe->usage) { ++ printk("%s in use: read-only\n", npe->plat->name); ++ return count; ++ } ++ if (!strncmp(buf, "start", 5)) { ++ npe_start(npe); ++ } ++ if (!strncmp(buf, "stop", 4)) { ++ npe_stop(npe); ++ } ++ if (!strncmp(buf, "reset", 5)) { ++ npe_stop(npe); ++ npe_reset(npe); ++ } ++ return count; ++} ++ ++static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_npe_state, set_npe_state); ++ ++static int npe_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct npe_info *npe; ++ struct npe_plat_data *plat = pdev->dev.platform_data; ++ int err, size, ret=0; ++ ++ if (!(res = platform_get_resource(pdev, IORESOURCE_MEM, 0))) ++ return -EIO; ++ ++ if (!(npe = kzalloc(sizeof(struct npe_info), GFP_KERNEL))) ++ return -ENOMEM; ++ ++ size = res->end - res->start +1; ++ npe->res = request_mem_region(res->start, size, plat->name); ++ if (!npe->res) { ++ ret = -EBUSY; ++ printk(KERN_ERR "Failed to get memregion(%x, %x)\n", ++ res->start, size); ++ goto out_free; ++ } ++ ++ npe->addr = ioremap(res->start, size); ++ if (!npe->addr) { ++ ret = -ENOMEM; ++ printk(KERN_ERR "Failed to ioremap(%x, %x)\n", ++ res->start, size); ++ goto out_rel; ++ } ++ ++ pdev->dev.coherent_dma_mask = DMA_32BIT_MASK; ++ ++ platform_set_drvdata(pdev, npe); ++ ++ err = device_create_file(&pdev->dev, &dev_attr_state); ++ if (err) ++ goto out_rel; ++ ++ npe->plat = plat; ++ disable_npe_irq(npe); ++ npe->usage = 0; ++ npe_reset(npe); ++ npe_firmware_probe(&pdev->dev); ++ ++ return 0; ++ ++out_rel: ++ release_resource(npe->res); ++out_free: ++ kfree(npe); ++ return ret; ++} ++ ++static struct file_operations ucode_dl_fops = { ++ .owner = THIS_MODULE, ++ .write = ucode_write, ++ .open = ucode_open, ++ .release = ucode_close, ++}; ++ ++static struct miscdevice ucode_dl_dev = { ++ .minor = MICROCODE_MINOR, ++ .name = "ixp4xx_ucode", ++ .fops = &ucode_dl_fops, ++}; ++ ++static int npe_remove(struct platform_device *pdev) ++{ ++ struct npe_info *npe = platform_get_drvdata(pdev); ++ ++ device_remove_file(&pdev->dev, &dev_attr_state); ++ ++ iounmap(npe->addr); ++ release_resource(npe->res); ++ kfree(npe); ++ return 0; ++} ++ ++static struct platform_driver ixp4xx_npe_driver = { ++ .driver = { ++ .name = "ixp4xx_npe", ++ .owner = THIS_MODULE, ++ }, ++ .probe = npe_probe, ++ .remove = npe_remove, ++}; ++ ++static int __init init_npedriver(void) ++{ ++ int ret; ++ if ((ret = misc_register(&ucode_dl_dev))){ ++ printk(KERN_ERR "Failed to register misc device %d\n", ++ MICROCODE_MINOR); ++ return ret; ++ } ++ if ((ret = platform_driver_register(&ixp4xx_npe_driver))) ++ misc_deregister(&ucode_dl_dev); ++ else ++ printk(KERN_INFO IXNPE_VERSION " initialized\n"); ++ ++ return ret; ++ ++} ++ ++static void __exit finish_npedriver(void) ++{ ++ misc_deregister(&ucode_dl_dev); ++ platform_driver_unregister(&ixp4xx_npe_driver); ++} ++ ++module_init(init_npedriver); ++module_exit(finish_npedriver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Christian Hohnstaedt "); ++ ++EXPORT_SYMBOL(get_npe_by_id); ++EXPORT_SYMBOL(return_npe_dev); +diff --git a/include/asm-arm/arch-ixp4xx/ixp4xx-regs.h b/include/asm-arm/arch-ixp4xx/ixp4xx-regs.h +index 5d949d7..9157e06 100644 +--- a/include/asm-arm/arch-ixp4xx/ixp4xx-regs.h ++++ b/include/asm-arm/arch-ixp4xx/ixp4xx-regs.h +@@ -22,6 +22,8 @@ + #ifndef _ASM_ARM_IXP4XX_H_ + #define _ASM_ARM_IXP4XX_H_ + ++#include "npe_regs.h" ++ + /* + * IXP4xx Linux Memory Map: + * +@@ -44,6 +46,12 @@ + */ + + /* ++ * PCI Memory Space ++ */ ++#define IXP4XX_PCIMEM_BASE_PHYS (0x48000000) ++#define IXP4XX_PCIMEM_REGION_SIZE (0x04000000) ++#define IXP4XX_PCIMEM_BAR_SIZE (0x01000000) ++/* + * Queue Manager + */ + #define IXP4XX_QMGR_BASE_PHYS (0x60000000) +@@ -322,7 +330,13 @@ + #define PCI_ATPDMA0_LENADDR_OFFSET 0x48 + #define PCI_ATPDMA1_AHBADDR_OFFSET 0x4C + #define PCI_ATPDMA1_PCIADDR_OFFSET 0x50 +-#define PCI_ATPDMA1_LENADDR_OFFSET 0x54 ++#define PCI_ATPDMA1_LENADDR_OFFSET 0x54 ++#define PCI_PTADMA0_AHBADDR_OFFSET 0x58 ++#define PCI_PTADMA0_PCIADDR_OFFSET 0x5c ++#define PCI_PTADMA0_LENADDR_OFFSET 0x60 ++#define PCI_PTADMA1_AHBADDR_OFFSET 0x64 ++#define PCI_PTADMA1_PCIADDR_OFFSET 0x68 ++#define PCI_PTADMA1_LENADDR_OFFSET 0x6c + + /* + * PCI Control/Status Registers +@@ -351,9 +365,15 @@ + #define PCI_ATPDMA1_AHBADDR IXP4XX_PCI_CSR(PCI_ATPDMA1_AHBADDR_OFFSET) + #define PCI_ATPDMA1_PCIADDR IXP4XX_PCI_CSR(PCI_ATPDMA1_PCIADDR_OFFSET) + #define PCI_ATPDMA1_LENADDR IXP4XX_PCI_CSR(PCI_ATPDMA1_LENADDR_OFFSET) ++#define PCI_PTADMA0_AHBADDR IXP4XX_PCI_CSR(PCI_PTADMA0_AHBADDR_OFFSET) ++#define PCI_PTADMA0_PCIADDR IXP4XX_PCI_CSR(PCI_PTADMA0_PCIADDR_OFFSET) ++#define PCI_PTADMA0_LENADDR IXP4XX_PCI_CSR(PCI_PTADMA0_LENADDR_OFFSET) ++#define PCI_PTADMA1_AHBADDR IXP4XX_PCI_CSR(PCI_PTADMA1_AHBADDR_OFFSET) ++#define PCI_PTADMA1_PCIADDR IXP4XX_PCI_CSR(PCI_PTADMA1_PCIADDR_OFFSET) ++#define PCI_PTADMA1_LENADDR IXP4XX_PCI_CSR(PCI_PTADMA1_LENADDR_OFFSET) + + /* +- * PCI register values and bit definitions ++ * PCI register values and bit definitions + */ + + /* CSR bit definitions */ +diff --git a/include/asm-arm/arch-ixp4xx/npe_regs.h b/include/asm-arm/arch-ixp4xx/npe_regs.h +new file mode 100644 +index 0000000..b79bb09 +--- /dev/null ++++ b/include/asm-arm/arch-ixp4xx/npe_regs.h +@@ -0,0 +1,82 @@ ++#ifndef NPE_REGS_H ++#define NPE_REGS_H ++ ++/* Execution Address */ ++#define IX_NPEDL_REG_OFFSET_EXAD 0x00 ++/* Execution Data */ ++#define IX_NPEDL_REG_OFFSET_EXDATA 0x04 ++/* Execution Control */ ++#define IX_NPEDL_REG_OFFSET_EXCTL 0x08 ++/* Execution Count */ ++#define IX_NPEDL_REG_OFFSET_EXCT 0x0C ++/* Action Point 0 */ ++#define IX_NPEDL_REG_OFFSET_AP0 0x10 ++/* Action Point 1 */ ++#define IX_NPEDL_REG_OFFSET_AP1 0x14 ++/* Action Point 2 */ ++#define IX_NPEDL_REG_OFFSET_AP2 0x18 ++/* Action Point 3 */ ++#define IX_NPEDL_REG_OFFSET_AP3 0x1C ++/* Watchpoint FIFO */ ++#define IX_NPEDL_REG_OFFSET_WFIFO 0x20 ++/* Watch Count */ ++#define IX_NPEDL_REG_OFFSET_WC 0x24 ++/* Profile Count */ ++#define IX_NPEDL_REG_OFFSET_PROFCT 0x28 ++ ++/* Messaging Status */ ++#define IX_NPEDL_REG_OFFSET_STAT 0x2C ++/* Messaging Control */ ++#define IX_NPEDL_REG_OFFSET_CTL 0x30 ++/* Mailbox Status */ ++#define IX_NPEDL_REG_OFFSET_MBST 0x34 ++/* messaging in/out FIFO */ ++#define IX_NPEDL_REG_OFFSET_FIFO 0x38 ++ ++ ++#define IX_NPEDL_MASK_ECS_DBG_REG_2_IF 0x00100000 ++#define IX_NPEDL_MASK_ECS_DBG_REG_2_IE 0x00080000 ++#define IX_NPEDL_MASK_ECS_REG_0_ACTIVE 0x80000000 ++ ++#define IX_NPEDL_EXCTL_CMD_NPE_STEP 0x01 ++#define IX_NPEDL_EXCTL_CMD_NPE_START 0x02 ++#define IX_NPEDL_EXCTL_CMD_NPE_STOP 0x03 ++#define IX_NPEDL_EXCTL_CMD_NPE_CLR_PIPE 0x04 ++#define IX_NPEDL_EXCTL_CMD_CLR_PROFILE_CNT 0x0C ++#define IX_NPEDL_EXCTL_CMD_RD_INS_MEM 0x10 ++#define IX_NPEDL_EXCTL_CMD_WR_INS_MEM 0x11 ++#define IX_NPEDL_EXCTL_CMD_RD_DATA_MEM 0x12 ++#define IX_NPEDL_EXCTL_CMD_WR_DATA_MEM 0x13 ++#define IX_NPEDL_EXCTL_CMD_RD_ECS_REG 0x14 ++#define IX_NPEDL_EXCTL_CMD_WR_ECS_REG 0x15 ++ ++#define IX_NPEDL_EXCTL_STATUS_RUN 0x80000000 ++#define IX_NPEDL_EXCTL_STATUS_STOP 0x40000000 ++#define IX_NPEDL_EXCTL_STATUS_CLEAR 0x20000000 ++ ++#define IX_NPEDL_MASK_WFIFO_VALID 0x80000000 ++#define IX_NPEDL_MASK_STAT_OFNE 0x00010000 ++#define IX_NPEDL_MASK_STAT_IFNE 0x00080000 ++ ++#define IX_NPEDL_ECS_DBG_CTXT_REG_0 0x0C ++#define IX_NPEDL_ECS_PRI_1_CTXT_REG_0 0x04 ++#define IX_NPEDL_ECS_PRI_2_CTXT_REG_0 0x08 ++ ++/* NPE control register bit definitions */ ++#define IX_NPEMH_NPE_CTL_OFE (1 << 16) /**< OutFifoEnable */ ++#define IX_NPEMH_NPE_CTL_IFE (1 << 17) /**< InFifoEnable */ ++#define IX_NPEMH_NPE_CTL_OFEWE (1 << 24) /**< OutFifoEnableWriteEnable */ ++#define IX_NPEMH_NPE_CTL_IFEWE (1 << 25) /**< InFifoEnableWriteEnable */ ++ ++/* NPE status register bit definitions */ ++#define IX_NPEMH_NPE_STAT_OFNE (1 << 16) /**< OutFifoNotEmpty */ ++#define IX_NPEMH_NPE_STAT_IFNF (1 << 17) /**< InFifoNotFull */ ++#define IX_NPEMH_NPE_STAT_OFNF (1 << 18) /**< OutFifoNotFull */ ++#define IX_NPEMH_NPE_STAT_IFNE (1 << 19) /**< InFifoNotEmpty */ ++#define IX_NPEMH_NPE_STAT_MBINT (1 << 20) /**< Mailbox interrupt */ ++#define IX_NPEMH_NPE_STAT_IFINT (1 << 21) /**< InFifo interrupt */ ++#define IX_NPEMH_NPE_STAT_OFINT (1 << 22) /**< OutFifo interrupt */ ++#define IX_NPEMH_NPE_STAT_WFINT (1 << 23) /**< WatchFifo interrupt */ ++ ++#endif ++ +diff --git a/include/asm-arm/arch-ixp4xx/platform.h b/include/asm-arm/arch-ixp4xx/platform.h +index 2a44d3d..e4f2063 100644 +--- a/include/asm-arm/arch-ixp4xx/platform.h ++++ b/include/asm-arm/arch-ixp4xx/platform.h +@@ -86,6 +86,25 @@ struct ixp4xx_i2c_pins { + unsigned long scl_pin; + }; + ++struct npe_plat_data { ++ const char *name; ++ int data_size; ++ int inst_size; ++ int id; /* Node ID */ ++}; ++ ++struct mac_plat_info { ++ int npe_id; /* Node ID of the NPE for this port */ ++ int port_id; /* Port ID for NPE-B @ ixp465 */ ++ int eth_id; /* Physical ID */ ++ int phy_id; /* ID of the connected PHY (PCB/platform dependent) */ ++ int rxq_id; /* Queue ID of the RX-free q */ ++ int rxdoneq_id; /* where incoming packets are returned */ ++ int txq_id; /* Where to push the outgoing packets */ ++ unsigned char hwaddr[6]; /* Desired hardware address */ ++ ++}; ++ + /* + * This structure provide a means for the board setup code + * to give information to th pata_ixp4xx driver. It is +@@ -120,6 +139,37 @@ struct pci_sys_data; + extern int ixp4xx_setup(int nr, struct pci_sys_data *sys); + extern struct pci_bus *ixp4xx_scan_bus(int nr, struct pci_sys_data *sys); + ++/* Fuse definitions and functions */ ++ ++/* Fuse Bits of IXP_EXP_CFG2 */ ++#define IX_FUSE_RCOMP (1 << 0) ++#define IX_FUSE_USB (1 << 1) ++#define IX_FUSE_HASH (1 << 2) ++#define IX_FUSE_AES (1 << 3) ++#define IX_FUSE_DES (1 << 4) ++#define IX_FUSE_HDLC (1 << 5) ++#define IX_FUSE_AAL (1 << 6) ++#define IX_FUSE_HSS (1 << 7) ++#define IX_FUSE_UTOPIA (1 << 8) ++#define IX_FUSE_ETH0 (1 << 9) ++#define IX_FUSE_ETH1 (1 << 10) ++#define IX_FUSE_NPEA (1 << 11) ++#define IX_FUSE_NPEB (1 << 12) ++#define IX_FUSE_NPEC (1 << 13) ++#define IX_FUSE_PCI (1 << 14) ++#define IX_FUSE_ECC (1 << 15) ++#define IX_FUSE_UTOPIA_PHY_LIMIT (3 << 16) ++#define IX_FUSE_USB_HOST (1 << 18) ++#define IX_FUSE_NPEA_ETH (1 << 19) ++#define IX_FUSE_NPEB_ETH (1 << 20) ++#define IX_FUSE_RSA (1 << 21) ++#define IX_FUSE_XSCALE_MAX_FREQ (3 << 22) ++ ++#define IX_FUSE_IXP46X_ONLY IX_FUSE_XSCALE_MAX_FREQ | IX_FUSE_RSA | \ ++ IX_FUSE_NPEB_ETH | IX_FUSE_NPEA_ETH | IX_FUSE_USB_HOST | IX_FUSE_ECC ++ ++extern u32 ix_fuse(void); ++ + /* + * GPIO-functions + */ +diff --git a/include/linux/ixp_crypto.h b/include/linux/ixp_crypto.h +new file mode 100644 +index 0000000..9b7cbb9 +--- /dev/null ++++ b/include/linux/ixp_crypto.h +@@ -0,0 +1,192 @@ ++ ++#ifndef IX_CRYPTO_H ++#define IX_CRYPTO_H ++ ++#define MAX_KEYLEN 64 ++#define NPE_CTX_LEN 80 ++#define AES_BLOCK128 16 ++ ++#define NPE_OP_HASH_GEN_ICV 0x50 ++#define NPE_OP_ENC_GEN_KEY 0xc9 ++ ++ ++#define NPE_OP_HASH_VERIFY 0x01 ++#define NPE_OP_CCM_ENABLE 0x04 ++#define NPE_OP_CRYPT_ENABLE 0x08 ++#define NPE_OP_HASH_ENABLE 0x10 ++#define NPE_OP_NOT_IN_PLACE 0x20 ++#define NPE_OP_HMAC_DISABLE 0x40 ++#define NPE_OP_CRYPT_ENCRYPT 0x80 ++ ++#define MOD_ECB 0x0000 ++#define MOD_CTR 0x1000 ++#define MOD_CBC_ENC 0x2000 ++#define MOD_CBC_DEC 0x3000 ++#define MOD_CCM_ENC 0x4000 ++#define MOD_CCM_DEC 0x5000 ++ ++#define ALGO_AES 0x0800 ++#define CIPH_DECR 0x0000 ++#define CIPH_ENCR 0x0400 ++ ++#define MOD_DES 0x0000 ++#define MOD_TDEA2 0x0100 ++#define MOD_TDEA3 0x0200 ++#define MOD_AES128 0x0000 ++#define MOD_AES192 0x0100 ++#define MOD_AES256 0x0200 ++ ++#define KEYLEN_128 4 ++#define KEYLEN_192 6 ++#define KEYLEN_256 8 ++ ++#define CIPHER_TYPE_NULL 0 ++#define CIPHER_TYPE_DES 1 ++#define CIPHER_TYPE_3DES 2 ++#define CIPHER_TYPE_AES 3 ++ ++#define CIPHER_MODE_ECB 1 ++#define CIPHER_MODE_CTR 2 ++#define CIPHER_MODE_CBC 3 ++#define CIPHER_MODE_CCM 4 ++ ++#define HASH_TYPE_NULL 0 ++#define HASH_TYPE_MD5 1 ++#define HASH_TYPE_SHA1 2 ++#define HASH_TYPE_CBCMAC 3 ++ ++#define OP_REG_DONE 1 ++#define OP_REGISTER 2 ++#define OP_PERFORM 3 ++ ++#define STATE_UNREGISTERED 0 ++#define STATE_REGISTERED 1 ++#define STATE_UNLOADING 2 ++ ++struct crypt_ctl { ++#ifndef CONFIG_NPE_ADDRESS_COHERENT ++ u8 mode; /* NPE operation */ ++ u8 init_len; ++ u16 reserved; ++#else ++ u16 reserved; ++ u8 init_len; ++ u8 mode; /* NPE operation */ ++#endif ++ u8 iv[16]; /* IV for CBC mode or CTR IV for CTR mode */ ++ union { ++ u32 icv; ++ u32 rev_aes; ++ } addr; ++ u32 src_buf; ++ u32 dest_buf; ++#ifndef CONFIG_NPE_ADDRESS_COHERENT ++ u16 auth_offs; /* Authentication start offset */ ++ u16 auth_len; /* Authentication data length */ ++ u16 crypt_offs; /* Cryption start offset */ ++ u16 crypt_len; /* Cryption data length */ ++#else ++ u16 auth_len; /* Authentication data length */ ++ u16 auth_offs; /* Authentication start offset */ ++ u16 crypt_len; /* Cryption data length */ ++ u16 crypt_offs; /* Cryption start offset */ ++#endif ++ u32 aadAddr; /* Additional Auth Data Addr for CCM mode */ ++ u32 crypto_ctx; /* NPE Crypto Param structure address */ ++ ++ /* Used by Host */ ++ struct ix_sa_ctx *sa_ctx; ++ int oper_type; ++}; ++ ++struct npe_crypt_cont { ++ union { ++ struct crypt_ctl crypt; ++ u8 rev_aes_key[NPE_CTX_LEN]; ++ } ctl; ++ struct npe_crypt_cont *next; ++ struct npe_crypt_cont *virt; ++ dma_addr_t phys; ++}; ++ ++struct ix_hash_algo { ++ char *name; ++ u32 cfgword; ++ int digest_len; ++ int aad_len; ++ unsigned char *icv; ++ int type; ++}; ++ ++struct ix_cipher_algo { ++ char *name; ++ u32 cfgword_enc; ++ u32 cfgword_dec; ++ int block_len; ++ int iv_len; ++ int type; ++ int mode; ++}; ++ ++struct ix_key { ++ u8 key[MAX_KEYLEN]; ++ int len; ++}; ++ ++struct ix_sa_master { ++ struct device *npe_dev; ++ struct qm_queue *sendq; ++ struct qm_queue *recvq; ++ struct dma_pool *dmapool; ++ struct npe_crypt_cont *pool; ++ int pool_size; ++ rwlock_t lock; ++}; ++ ++struct ix_sa_dir { ++ unsigned char *npe_ctx; ++ dma_addr_t npe_ctx_phys; ++ int npe_ctx_idx; ++ u8 npe_mode; ++}; ++ ++struct ix_sa_ctx { ++ struct list_head list; ++ struct ix_sa_master *master; ++ ++ const struct ix_hash_algo *h_algo; ++ const struct ix_cipher_algo *c_algo; ++ struct ix_key c_key; ++ struct ix_key h_key; ++ ++ int digest_len; ++ ++ struct ix_sa_dir encrypt; ++ struct ix_sa_dir decrypt; ++ ++ struct npe_crypt_cont *rev_aes; ++ gfp_t gfp_flags; ++ ++ int state; ++ void *priv; ++ ++ void(*reg_cb)(struct ix_sa_ctx*, int); ++ void(*perf_cb)(struct ix_sa_ctx*, void*, int); ++ atomic_t use_cnt; ++}; ++ ++const struct ix_hash_algo *ix_hash_by_id(int type); ++const struct ix_cipher_algo *ix_cipher_by_id(int type, int mode); ++ ++struct ix_sa_ctx *ix_sa_ctx_new(int priv_len, gfp_t flags); ++void ix_sa_ctx_free(struct ix_sa_ctx *sa_ctx); ++ ++int ix_sa_crypto_perform(struct ix_sa_ctx *sa_ctx, u8 *data, void *ptr, ++ int datalen, int c_offs, int c_len, int a_offs, int a_len, ++ int hmac, char *iv, int encrypt); ++ ++int ix_sa_ctx_setup_cipher_auth(struct ix_sa_ctx *sa_ctx, ++ const struct ix_cipher_algo *cipher, ++ const struct ix_hash_algo *auth, int len); ++ ++#endif +diff --git a/include/linux/ixp_npe.h b/include/linux/ixp_npe.h +new file mode 100644 +index 0000000..594ca96 +--- /dev/null ++++ b/include/linux/ixp_npe.h +@@ -0,0 +1,117 @@ ++/* ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++#ifndef NPE_DEVICE_H ++#define NPE_DEVICE_H ++ ++#include ++#include ++ ++#ifdef __ARMEB__ ++#undef CONFIG_NPE_ADDRESS_COHERENT ++#else ++#define CONFIG_NPE_ADDRESS_COHERENT ++#endif ++ ++#if defined(__ARMEB__) || defined (CONFIG_NPE_ADDRESS_COHERENT) ++#define npe_to_cpu32(x) (x) ++#define npe_to_cpu16(x) (x) ++#define cpu_to_npe32(x) (x) ++#define cpu_to_npe16(x) (x) ++#else ++#error NPE_DATA_COHERENT ++#define NPE_DATA_COHERENT ++#define npe_to_cpu32(x) be32_to_cpu(x) ++#define npe_to_cpu16(x) be16_to_cpu(x) ++#define cpu_to_npe32(x) cpu_to_be32(x) ++#define cpu_to_npe16(x) cpu_to_be16(x) ++#endif ++ ++ ++struct npe_info { ++ struct resource *res; ++ void __iomem *addr; ++ struct npe_plat_data *plat; ++ u8 img_info[4]; ++ int usage; ++ int loaded; ++ u32 exec_count; ++ u32 ctx_reg2; ++}; ++ ++ ++static inline void npe_reg_write(struct npe_info *npe, u32 reg, u32 val) ++{ ++ *(volatile u32*)((u8*)(npe->addr) + reg) = val; ++} ++ ++static inline u32 npe_reg_read(struct npe_info *npe, u32 reg) ++{ ++ return *(volatile u32*)((u8*)(npe->addr) + reg); ++} ++ ++static inline u32 npe_status(struct npe_info *npe) ++{ ++ return npe_reg_read(npe, IX_NPEDL_REG_OFFSET_EXCTL); ++} ++ ++/* ixNpeDlNpeMgrCommandIssue */ ++static inline void npe_write_exctl(struct npe_info *npe, u32 cmd) ++{ ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_EXCTL, cmd); ++} ++/* ixNpeDlNpeMgrWriteCommandIssue */ ++static inline void ++npe_write_cmd(struct npe_info *npe, u32 addr, u32 data, int cmd) ++{ ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_EXDATA, data); ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_EXAD, addr); ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_EXCTL, cmd); ++} ++/* ixNpeDlNpeMgrReadCommandIssue */ ++static inline u32 ++npe_read_cmd(struct npe_info *npe, u32 addr, int cmd) ++{ ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_EXAD, addr); ++ npe_reg_write(npe, IX_NPEDL_REG_OFFSET_EXCTL, cmd); ++ /* Intel reads the data twice - so do we... */ ++ npe_reg_read(npe, IX_NPEDL_REG_OFFSET_EXDATA); ++ return npe_reg_read(npe, IX_NPEDL_REG_OFFSET_EXDATA); ++} ++ ++/* ixNpeDlNpeMgrExecAccRegWrite */ ++static inline void npe_write_ecs_reg(struct npe_info *npe, u32 addr, u32 data) ++{ ++ npe_write_cmd(npe, addr, data, IX_NPEDL_EXCTL_CMD_WR_ECS_REG); ++} ++/* ixNpeDlNpeMgrExecAccRegRead */ ++static inline u32 npe_read_ecs_reg(struct npe_info *npe, u32 addr) ++{ ++ return npe_read_cmd(npe, addr, IX_NPEDL_EXCTL_CMD_RD_ECS_REG); ++} ++ ++extern void npe_stop(struct npe_info *npe); ++extern void npe_start(struct npe_info *npe); ++extern void npe_reset(struct npe_info *npe); ++ ++extern struct device *get_npe_by_id(int id); ++extern void return_npe_dev(struct device *dev); ++ ++/* NPE Messages */ ++extern int ++npe_mh_status(struct npe_info *npe); ++extern int ++npe_mh_setportaddr(struct npe_info *npe, struct mac_plat_info *mp, u8 *macaddr); ++extern int ++npe_mh_disable_firewall(struct npe_info *npe, struct mac_plat_info *mp); ++extern int ++npe_mh_set_rxqid(struct npe_info *npe, struct mac_plat_info *mp, int qid); ++extern int ++npe_mh_npe_loopback_mode(struct npe_info *npe, struct mac_plat_info *mp, int enable); ++extern int ++npe_mh_get_stats(struct npe_info *npe, struct mac_plat_info *mp, u32 phys, int reset); ++ ++#endif +diff --git a/include/linux/ixp_qmgr.h b/include/linux/ixp_qmgr.h +new file mode 100644 +index 0000000..c990ab7 +--- /dev/null ++++ b/include/linux/ixp_qmgr.h +@@ -0,0 +1,202 @@ ++/* ++ * Copyright (C) 2006 Christian Hohnstaedt ++ * ++ * This file is released under the GPLv2 ++ */ ++ ++#ifndef IX_QMGR_H ++#define IX_QMGR_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* All offsets are in 32bit words */ ++#define QUE_LOW_STAT0 0x100 /* 4x Status of the 32 lower queues 0-31 */ ++#define QUE_UO_STAT0 0x104 /* 2x Underflow/Overflow status bits*/ ++#define QUE_UPP_STAT0 0x106 /* 2x Status of thew 32 upper queues 32-63 */ ++#define INT0_SRC_SELREG0 0x108 /* 4x */ ++#define QUE_IE_REG0 0x10c /* 2x */ ++#define QUE_INT_REG0 0x10e /* 2x IRQ reg, write 1 to reset IRQ */ ++ ++#define IX_QMGR_QCFG_BASE 0x800 ++#define IX_QMGR_QCFG_SIZE 0x40 ++#define IX_QMGR_SRAM_SPACE (IX_QMGR_QCFG_BASE + IX_QMGR_QCFG_SIZE) ++ ++#define MAX_QUEUES 32 /* first, we only support the lower 32 queues */ ++#define MAX_NPES 3 ++ ++enum { ++ Q_IRQ_ID_E = 0, /* Queue Empty due to last read */ ++ Q_IRQ_ID_NE, /* Queue Nearly Empty due to last read */ ++ Q_IRQ_ID_NF, /* Queue Nearly Full due to last write */ ++ Q_IRQ_ID_F, /* Queue Full due to last write */ ++ Q_IRQ_ID_NOT_E, /* Queue Not Empty due to last write */ ++ Q_IRQ_ID_NOT_NE, /* Queue Not Nearly Empty due to last write */ ++ Q_IRQ_ID_NOT_NF, /* Queue Not Nearly Full due to last read */ ++ Q_IRQ_ID_NOT_F /* Queue Not Full due to last read */ ++}; ++ ++extern struct qm_queue *request_queue(int qid, int len); ++extern void release_queue(struct qm_queue *queue); ++extern int queue_set_irq_src(struct qm_queue *queue, int flag); ++extern void queue_set_watermarks(struct qm_queue *, unsigned ne, unsigned nf); ++extern int queue_len(struct qm_queue *queue); ++ ++struct qm_qmgr; ++struct qm_queue; ++ ++typedef void(*queue_cb)(struct qm_queue *); ++ ++struct qm_queue { ++ int addr; /* word offset from IX_QMGR_SRAM_SPACE */ ++ int len; /* size in words */ ++ int id; /* Q Id */ ++ u32 __iomem *acc_reg; ++ struct device *dev; ++ atomic_t use; ++ queue_cb irq_cb; ++ void *cb_data; ++}; ++ ++#ifndef CONFIG_NPE_ADDRESS_COHERENT ++struct eth_ctl { ++ u32 next; ++ u16 buf_len; ++ u16 pkt_len; ++ u32 phys_addr; ++ u8 dest_id; ++ u8 src_id; ++ u16 flags; ++ u8 qos; ++ u8 padlen; ++ u16 vlan_tci; ++ u8 dest_mac[ETH_ALEN]; ++ u8 src_mac[ETH_ALEN]; ++}; ++ ++#else ++struct eth_ctl { ++ u32 next; ++ u16 pkt_len; ++ u16 buf_len; ++ u32 phys_addr; ++ u16 flags; ++ u8 src_id; ++ u8 dest_id; ++ u16 vlan_tci; ++ u8 padlen; ++ u8 qos; ++ u8 dest_mac[ETH_ALEN]; ++ u8 src_mac[ETH_ALEN]; ++}; ++#endif ++ ++struct npe_cont { ++ struct eth_ctl eth; ++ void *data; ++ struct npe_cont *next; ++ struct npe_cont *virt; ++ dma_addr_t phys; ++}; ++ ++struct qm_qmgr { ++ u32 __iomem *addr; ++ struct resource *res; ++ struct qm_queue *queues[MAX_QUEUES]; ++ rwlock_t lock; ++ struct npe_cont *pool; ++ struct dma_pool *dmapool; ++ int irq; ++}; ++ ++static inline void queue_write_cfg_reg(struct qm_queue *queue, u32 val) ++{ ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ *(qmgr->addr + IX_QMGR_QCFG_BASE + queue->id) = val; ++} ++static inline u32 queue_read_cfg_reg(struct qm_queue *queue) ++{ ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ return *(qmgr->addr + IX_QMGR_QCFG_BASE + queue->id); ++} ++ ++static inline void queue_ack_irq(struct qm_queue *queue) ++{ ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ *(qmgr->addr + QUE_INT_REG0) = 1 << queue->id; ++} ++ ++static inline void queue_enable_irq(struct qm_queue *queue) ++{ ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ *(qmgr->addr + QUE_IE_REG0) |= 1 << queue->id; ++} ++ ++static inline void queue_disable_irq(struct qm_queue *queue) ++{ ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ *(qmgr->addr + QUE_IE_REG0) &= ~(1 << queue->id); ++} ++ ++static inline void queue_put_entry(struct qm_queue *queue, u32 entry) ++{ ++ *(queue->acc_reg) = npe_to_cpu32(entry); ++} ++ ++static inline u32 queue_get_entry(struct qm_queue *queue) ++{ ++ return cpu_to_npe32(*queue->acc_reg); ++} ++ ++static inline struct npe_cont *qmgr_get_cont(struct qm_qmgr *qmgr) ++{ ++ unsigned long flags; ++ struct npe_cont *cont; ++ ++ if (!qmgr->pool) ++ return NULL; ++ write_lock_irqsave(&qmgr->lock, flags); ++ cont = qmgr->pool; ++ qmgr->pool = cont->next; ++ write_unlock_irqrestore(&qmgr->lock, flags); ++ return cont; ++} ++ ++static inline void qmgr_return_cont(struct qm_qmgr *qmgr,struct npe_cont *cont) ++{ ++ unsigned long flags; ++ ++ write_lock_irqsave(&qmgr->lock, flags); ++ cont->next = qmgr->pool; ++ qmgr->pool = cont; ++ write_unlock_irqrestore(&qmgr->lock, flags); ++} ++ ++static inline int queue_stat(struct qm_queue *queue) ++{ ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ u32 reg = *(qmgr->addr + QUE_UO_STAT0 + (queue->id >> 4)); ++ return (reg >> (queue->id & 0xf) << 1) & 3; ++} ++ ++/* Prints the queue state, which is very, very helpfull for debugging */ ++static inline void queue_state(struct qm_queue *queue) ++{ ++ u32 val=0, lstat=0; ++ int offs; ++ struct qm_qmgr *qmgr = dev_get_drvdata(queue->dev); ++ ++ offs = queue->id/8 + QUE_LOW_STAT0; ++ val = *(qmgr->addr + IX_QMGR_QCFG_BASE + queue->id); ++ lstat = (*(qmgr->addr + offs) >> ((queue->id % 8)*4)) & 0x0f; ++ ++ printk("Qid[%02d]: Wptr=%4x, Rptr=%4x, diff=%4x, Stat:%x\n", queue->id, ++ val&0x7f, (val>>7) &0x7f, (val - (val >> 7)) & 0x7f, lstat); ++} ++ ++#endif diff --git a/debian/patches/features/arm/nas100d-i2c-gpio-driver-support.patch b/debian/patches/features/arm/nas100d-i2c-gpio-driver-support.patch index 979033e19..1725565ae 100644 --- a/debian/patches/features/arm/nas100d-i2c-gpio-driver-support.patch +++ b/debian/patches/features/arm/nas100d-i2c-gpio-driver-support.patch @@ -1,7 +1,7 @@ -Index: linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c -=================================================================== ---- linux-2.6.22-rc1-armeb.orig/arch/arm/mach-ixp4xx/nas100d-setup.c 2007-05-13 04:54:45.000000000 -0700 -+++ linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c 2007-05-13 05:05:44.000000000 -0700 +diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c +index 78a1741..54d884f 100644 +--- a/arch/arm/mach-ixp4xx/nas100d-setup.c ++++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -16,6 +16,7 @@ #include #include @@ -10,7 +10,7 @@ Index: linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c #include #include -@@ -68,16 +69,17 @@ +@@ -68,16 +69,17 @@ static struct platform_device nas100d_leds = { }; #endif @@ -33,7 +33,7 @@ Index: linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c }; static struct resource nas100d_uart_resources[] = { -@@ -124,7 +126,7 @@ +@@ -124,7 +126,7 @@ static struct platform_device nas100d_uart = { }; static struct platform_device *nas100d_devices[] __initdata = { diff --git a/debian/patches/features/arm/nas100d-mac_plat_info.patch b/debian/patches/features/arm/nas100d-mac_plat_info.patch index 2da4e9533..0b08e906c 100644 --- a/debian/patches/features/arm/nas100d-mac_plat_info.patch +++ b/debian/patches/features/arm/nas100d-mac_plat_info.patch @@ -1,25 +1,31 @@ -Index: linux-2.6.22-rc3-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c -=================================================================== ---- linux-2.6.22-rc3-armeb.orig/arch/arm/mach-ixp4xx/nas100d-setup.c 2007-05-27 05:03:46.000000000 -0700 -+++ linux-2.6.22-rc3-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c 2007-05-27 12:52:12.000000000 -0700 -@@ -125,12 +125,29 @@ +diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c +index 54d884f..5ef4c1f 100644 +--- a/arch/arm/mach-ixp4xx/nas100d-setup.c ++++ b/arch/arm/mach-ixp4xx/nas100d-setup.c +@@ -125,12 +125,35 @@ static struct platform_device nas100d_uart = { .resource = nas100d_uart_resources, }; -+/* Built-in 10/100 Ethernet MAC interfaces */ -+static struct mac_plat_info nas100d_plat_mac[] = { -+ { -+ .phy = 0, -+ .rxq = 3, -+ } ++static struct resource res_mac0 = { ++ .start = IXP4XX_EthB_BASE_PHYS, ++ .end = IXP4XX_EthB_BASE_PHYS + 0x1ff, ++ .flags = IORESOURCE_MEM, +}; + -+static struct platform_device nas100d_mac[] = { -+ { -+ .name = "ixp4xx_eth", -+ .id = IXP4XX_ETH_NPEB, -+ .dev.platform_data = nas100d_plat_mac, -+ } ++static struct mac_plat_info plat_mac0 = { ++ .npe_id = 1, ++ .phy_id = 0, ++ .eth_id = 0, ++ .rxq_id = 27, ++ .txq_id = 24, ++}; ++ ++static struct platform_device mac0 = { ++ .name = "ixp4xx_mac", ++ .id = 0, ++ .dev.platform_data = &plat_mac0, ++ .num_resources = 1, ++ .resource = &res_mac0, +}; + static struct platform_device *nas100d_devices[] __initdata = { @@ -28,7 +34,7 @@ Index: linux-2.6.22-rc3-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c #ifdef CONFIG_LEDS_IXP4XX &nas100d_leds, #endif -+ &nas100d_mac[0], ++ &mac0 }; static void nas100d_power_off(void) diff --git a/debian/patches/features/arm/nas100d-setup-mac.patch b/debian/patches/features/arm/nas100d-setup-mac.patch index 39d95fa92..10448c944 100644 --- a/debian/patches/features/arm/nas100d-setup-mac.patch +++ b/debian/patches/features/arm/nas100d-setup-mac.patch @@ -1,7 +1,7 @@ -Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c -=================================================================== ---- linux-2.6.22-rc4-armeb.orig/arch/arm/mach-ixp4xx/nas100d-setup.c -+++ linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c +diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c +index 5ef4c1f..870ec49 100644 +--- a/arch/arm/mach-ixp4xx/nas100d-setup.c ++++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -17,6 +17,7 @@ #include #include @@ -10,8 +10,8 @@ Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c #include #include -@@ -150,6 +151,30 @@ static struct platform_device *nas100d_d - &nas100d_mac[0], +@@ -156,6 +157,31 @@ static struct platform_device *nas100d_devices[] __initdata = { + &mac0 }; +static void nas100d_flash_add(struct mtd_info *mtd) @@ -23,14 +23,15 @@ Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c + if (mtd->read(mtd, 0x0FD8, 6, &retlen, mac) == 0 && retlen == 6) { + printk(KERN_INFO "nas100d mac: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); -+ memcpy(nas100d_plat_mac[0].hwaddr, mac, 6); ++ memcpy(plat_mac0.hwaddr, mac, 6); + } else { + printk(KERN_ERR "nas100d mac: read failed\n"); + } + } +} + -+static void nas100d_flash_remove(struct mtd_info *mtd) { ++static void nas100d_flash_remove(struct mtd_info *mtd) ++{ +} + +static struct mtd_notifier nas100d_flash_notifier = { @@ -41,7 +42,7 @@ Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nas100d-setup.c static void nas100d_power_off(void) { /* This causes the box to drop the power and go dead. */ -@@ -182,6 +207,8 @@ static void __init nas100d_init(void) +@@ -188,6 +214,8 @@ static void __init nas100d_init(void) (void)platform_device_register(&nas100d_uart); platform_add_devices(nas100d_devices, ARRAY_SIZE(nas100d_devices)); diff --git a/debian/patches/features/arm/nslu2-i2c-gpio-driver-support.patch b/debian/patches/features/arm/nslu2-i2c-gpio-driver-support.patch index 2770f796e..8917050a6 100644 --- a/debian/patches/features/arm/nslu2-i2c-gpio-driver-support.patch +++ b/debian/patches/features/arm/nslu2-i2c-gpio-driver-support.patch @@ -1,7 +1,7 @@ -Index: linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c -=================================================================== ---- linux-2.6.22-rc1-armeb.orig/arch/arm/mach-ixp4xx/nslu2-setup.c 2007-05-13 04:54:45.000000000 -0700 -+++ linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c 2007-05-13 05:01:20.000000000 -0700 +diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c +index 9bf8ccb..77277d2 100644 +--- a/arch/arm/mach-ixp4xx/nslu2-setup.c ++++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -18,6 +18,7 @@ #include #include @@ -10,7 +10,7 @@ Index: linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c #include #include -@@ -41,7 +42,7 @@ +@@ -41,7 +42,7 @@ static struct platform_device nslu2_flash = { .resource = &nslu2_flash_resource, }; @@ -19,7 +19,7 @@ Index: linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c .sda_pin = NSLU2_SDA_PIN, .scl_pin = NSLU2_SCL_PIN, }; -@@ -82,11 +83,12 @@ +@@ -82,11 +83,12 @@ static struct platform_device nslu2_leds = { }; #endif @@ -36,7 +36,7 @@ Index: linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c }; static struct platform_device nslu2_beeper = { -@@ -139,7 +141,7 @@ +@@ -139,7 +141,7 @@ static struct platform_device nslu2_uart = { }; static struct platform_device *nslu2_devices[] __initdata = { diff --git a/debian/patches/features/arm/nslu2-mac_plat_info.patch b/debian/patches/features/arm/nslu2-mac_plat_info.patch index af88208d2..30c8106dd 100644 --- a/debian/patches/features/arm/nslu2-mac_plat_info.patch +++ b/debian/patches/features/arm/nslu2-mac_plat_info.patch @@ -1,35 +1,41 @@ -Index: linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c -=================================================================== ---- linux-2.6.22-rc1-armeb.orig/arch/arm/mach-ixp4xx/nslu2-setup.c 2007-05-16 08:10:47.000000000 -0700 -+++ linux-2.6.22-rc1-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c 2007-05-16 08:15:14.000000000 -0700 -@@ -140,6 +140,22 @@ +diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c +index 77277d2..d109e04 100644 +--- a/arch/arm/mach-ixp4xx/nslu2-setup.c ++++ b/arch/arm/mach-ixp4xx/nslu2-setup.c +@@ -140,6 +140,28 @@ static struct platform_device nslu2_uart = { .resource = nslu2_uart_resources, }; -+/* Built-in 10/100 Ethernet MAC interfaces */ -+static struct mac_plat_info nslu2_plat_mac[] = { -+ { -+ .phy = 1, -+ .rxq = 3, -+ } ++static struct resource res_mac0 = { ++ .start = IXP4XX_EthB_BASE_PHYS, ++ .end = IXP4XX_EthB_BASE_PHYS + 0x1ff, ++ .flags = IORESOURCE_MEM, +}; + -+static struct platform_device nslu2_mac[] = { -+ { -+ .name = "ixp4xx_eth", -+ .id = IXP4XX_ETH_NPEB, -+ .dev.platform_data = nslu2_plat_mac, -+ } ++static struct mac_plat_info plat_mac0 = { ++ .npe_id = 1, ++ .phy_id = 1, ++ .eth_id = 0, ++ .rxq_id = 27, ++ .txq_id = 24, ++}; ++ ++static struct platform_device mac0 = { ++ .name = "ixp4xx_mac", ++ .id = 0, ++ .dev.platform_data = &plat_mac0, ++ .num_resources = 1, ++ .resource = &res_mac0, +}; + static struct platform_device *nslu2_devices[] __initdata = { &nslu2_i2c_gpio, &nslu2_flash, -@@ -147,6 +163,7 @@ +@@ -147,6 +169,7 @@ static struct platform_device *nslu2_devices[] __initdata = { #ifdef CONFIG_LEDS_IXP4XX &nslu2_leds, #endif -+ &nslu2_mac[0], ++ &mac0 }; static void nslu2_power_off(void) diff --git a/debian/patches/features/arm/nslu2-setup-mac.patch b/debian/patches/features/arm/nslu2-setup-mac.patch index f1e6a04ca..4eb1dfcf0 100644 --- a/debian/patches/features/arm/nslu2-setup-mac.patch +++ b/debian/patches/features/arm/nslu2-setup-mac.patch @@ -1,7 +1,7 @@ -Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c -=================================================================== ---- linux-2.6.22-rc4-armeb.orig/arch/arm/mach-ixp4xx/nslu2-setup.c -+++ linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c +diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c +index d109e04..7e3fa07 100644 +--- a/arch/arm/mach-ixp4xx/nslu2-setup.c ++++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -19,6 +19,7 @@ #include #include @@ -10,8 +10,8 @@ Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c #include #include -@@ -166,6 +167,30 @@ static struct platform_device *nslu2_dev - &nslu2_mac[0], +@@ -196,6 +197,32 @@ static struct sys_timer nslu2_timer = { + .init = nslu2_timer_init, }; +static void nslu2_flash_add(struct mtd_info *mtd) @@ -20,17 +20,19 @@ Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c + size_t retlen; + u_char mac[6]; + ++ /* The MAC is at a known offset... */ + if (mtd->read(mtd, 0x3FFB0, 6, &retlen, mac) == 0 && retlen == 6) { -+ printk(KERN_INFO "nslu2 mac: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", ++ printk(KERN_INFO "NSLU2 MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); -+ memcpy(nslu2_plat_mac[0].hwaddr, mac, 6); ++ memcpy(&plat_mac0.hwaddr, mac, 6); + } else { -+ printk(KERN_ERR "nslu2 mac: read failed\n"); ++ printk(KERN_ERR "NSLU2 MAC: read failed\n"); + } + } +} + -+static void nslu2_flash_remove(struct mtd_info *mtd) { ++static void nslu2_flash_remove(struct mtd_info *mtd) ++{ +} + +static struct mtd_notifier nslu2_flash_notifier = { @@ -38,10 +40,10 @@ Index: linux-2.6.22-rc4-armeb/arch/arm/mach-ixp4xx/nslu2-setup.c + .remove = nslu2_flash_remove, +}; + - static void nslu2_power_off(void) + static void __init nslu2_init(void) { - /* This causes the box to drop the power and go dead. */ -@@ -208,6 +233,8 @@ static void __init nslu2_init(void) + ixp4xx_sys_init(); +@@ -214,6 +241,8 @@ static void __init nslu2_init(void) (void)platform_device_register(&nslu2_uart); platform_add_devices(nslu2_devices, ARRAY_SIZE(nslu2_devices)); diff --git a/debian/patches/series/1~experimental.1 b/debian/patches/series/1~experimental.1 index 7f51e3be0..18b00009a 100644 --- a/debian/patches/series/1~experimental.1 +++ b/debian/patches/series/1~experimental.1 @@ -19,8 +19,8 @@ + bugfix/mips/tulip_dc21143.patch + features/mips/qemu-vga.patch + features/mips/sb1-duart.patch -+ features/arm/ixp4xx-net-fuses.patch -+ features/arm/ixp4xx-net-drivers.patch ++ features/arm/ixp4xx-npe-driver-0.3.1.patch ++ features/arm/ixp4xx-net-driver-improve-mac-handling.patch + features/arm/nslu2-i2c-gpio-driver-support.patch + features/arm/nas100d-i2c-gpio-driver-support.patch + features/arm/nslu2-mac_plat_info.patch