[PATCH 10] ARM: Add support for IXP4xx CPU and for Goramo Multilink router platform.

Krzysztof Halasa khc at pm.waw.pl
Mon Dec 20 18:06:12 EST 2010


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

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 8cb86cb..56b5a0d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -39,6 +39,13 @@ config ARCH_IMX
 	bool "Freescale iMX-based"
 	select GENERIC_GPIO
 
+config ARCH_IXP4XX
+	bool "Intel IXP4xx-based"
+	select CPU_32v5
+	select ARCH_SUPPORTS_BIG_ENDIAN
+	select CPU_BIG_ENDIAN
+	select ARCH_HAS_LOWLEVEL_INIT
+
 config ARCH_STM
 	bool "SigmaTel/FSL iMX-based"
 	select GENERIC_GPIO
@@ -67,6 +74,7 @@ source arch/arm/cpu/Kconfig
 source arch/arm/mach-at91/Kconfig
 source arch/arm/mach-ep93xx/Kconfig
 source arch/arm/mach-imx/Kconfig
+source arch/arm/mach-ixp4xx/Kconfig
 source arch/arm/mach-stm/Kconfig
 source arch/arm/mach-netx/Kconfig
 source arch/arm/mach-nomadik/Kconfig
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 9729c23..2e9f99d 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -41,6 +41,7 @@ CPPFLAGS += $(CFLAGS_ABI) $(arch-y) $(tune-y)
 machine-$(CONFIG_ARCH_AT91)		:= at91
 machine-$(CONFIG_ARCH_EP93XX)		:= ep93xx
 machine-$(CONFIG_ARCH_IMX)		:= imx
+machine-$(CONFIG_ARCH_IXP4XX)		:= ixp4xx
 machine-$(CONFIG_ARCH_STM)		:= stm
 machine-$(CONFIG_ARCH_NOMADIK)		:= nomadik
 machine-$(CONFIG_ARCH_NETX)		:= netx
@@ -73,6 +74,7 @@ board-$(CONFIG_MACH_FREESCALE_MX35_3STACK)	:= freescale-mx35-3-stack
 board-$(CONFIG_MACH_IMX21ADS)			:= imx21ads
 board-$(CONFIG_MACH_IMX27ADS)			:= imx27ads
 board-$(CONFIG_MACH_MMCCPU)			:= mmccpu
+board-$(CONFIG_MACH_MULTILINK)			:= multilink
 board-$(CONFIG_MACH_MX1ADS)			:= mx1ads
 board-$(CONFIG_MACH_NOMADIK_8815NHK)		:= nhk8815
 board-$(CONFIG_MACH_NXDB500)			:= netx
diff --git a/arch/arm/boards/multilink/Makefile b/arch/arm/boards/multilink/Makefile
new file mode 100644
index 0000000..d5a8bbc
--- /dev/null
+++ b/arch/arm/boards/multilink/Makefile
@@ -0,0 +1 @@
+obj-y += lowlevel_init.o multilink.o
diff --git a/arch/arm/boards/multilink/config.h b/arch/arm/boards/multilink/config.h
new file mode 100644
index 0000000..e69de29
diff --git a/arch/arm/boards/multilink/lowlevel_init.S b/arch/arm/boards/multilink/lowlevel_init.S
new file mode 100644
index 0000000..970ef72
--- /dev/null
+++ b/arch/arm/boards/multilink/lowlevel_init.S
@@ -0,0 +1,64 @@
+#include <mach/ixp4xx-regs.h>
+
+	.section ".text_bare_init", "ax"
+	.balign 0x40
+	.space 0x40 /* configuration block at 0x40 */
+
+	.macro DELAY_FOR cycles, reg0
+	ldr \reg0, =\cycles
+	subs \reg0, \reg0, #1
+	subne pc, pc, #0xc
+	.endm
+
+#define CFG_SDRAM_SIZE    0x50 /* u32 */
+#define CFG_SDRAM_CONF    0x54 /* u32 */
+#define CFG_SDRAM_MODE    0x58 /* u32 */
+#define CFG_SDRAM_REFRESH 0x5C /* u32 */
+
+.globl board_init_lowlevel
+board_init_lowlevel:
+	mov r8, #IXP4XX_EXP_BASE(0)
+	ldr r1, [r8, #CFG_SDRAM_CONF]
+	ldr r2, =IXP4XX_SDRAM_CONFIG
+	str r1, [r2]
+
+	/* disable refresh cycles */
+	mov r1, #0
+	add r2, r2, #4 /* r2 = IXP4XX_SDRAM_REFRESH */
+	str r1, [r2]
+
+	/* send NOP command */
+	mov r1, #3
+	add r3, r2, #4 /* r3 = IXP4XX_SDRAM_IR */
+	str r1, [r3]
+	DELAY_FOR 0x4000, r0
+
+	/* set SDRAM internal refresh */
+	ldr r1, [r8, #CFG_SDRAM_REFRESH]
+	str r1, [r2]
+	DELAY_FOR 0x4000, r0
+
+	/* send precharge-all command to close all open banks */
+	mov r1, #2
+	str r1, [r3]
+	DELAY_FOR 0x4000, r0
+
+	/* provide 8 auto-refresh cycles */
+	mov r1, #4
+	mov r4, #8
+1:	str r1, [r3]
+	DELAY_FOR 0x100, r0
+	subs r4, r4, #1
+	bne 1b
+
+	/* set mode register in SDRAM */
+	ldr r1, [r8, #CFG_SDRAM_MODE]
+	str r1, [r3]
+	DELAY_FOR 0x4000, r0
+
+	/* send normal operation command */
+	mov r1, #6
+	str r1, [r3]
+	DELAY_FOR 0x4000, r0
+
+	mov pc, lr
diff --git a/arch/arm/boards/multilink/multilink.c b/arch/arm/boards/multilink/multilink.c
new file mode 100644
index 0000000..f2fb8f4
--- /dev/null
+++ b/arch/arm/boards/multilink/multilink.c
@@ -0,0 +1,144 @@
+//#define DEBUG
+#include <common.h>
+#include <errno.h>
+#include <init.h>
+#include <linux/types.h>
+#include <asm/armlinux.h>
+#include <asm/io.h>
+#include <generated/mach-types.h>
+#include <mach/ixp4xx-regs.h>
+#include <mach/platform.h>
+
+/* offsets from start of flash ROM = 0x50000000 */
+#define CFG_ETH0_ADDRESS     0x40 /* 6 bytes */
+#define CFG_ETH1_ADDRESS     0x46 /* 6 bytes */
+#define CFG_REV              0x4C /* u32 */
+#define CFG_SDRAM_SIZE       0x50 /* u32 */
+#define CFG_SDRAM_CONF       0x54 /* u32 */
+#define CFG_SDRAM_MODE       0x58 /* u32 */
+#define CFG_SDRAM_REFRESH    0x5C /* u32 */
+
+#define CFG_HW_BITS          0x60 /* u32 */
+#define  CFG_HW_USB_PORTS    0x00000007 /* 0 = no NEC chip, 1-5 = ports # */
+#define  CFG_HW_HAS_PCI_SLOT 0x00000008
+#define  CFG_HW_HAS_ETH0     0x00000010
+#define  CFG_HW_HAS_ETH1     0x00000020
+#define  CFG_HW_HAS_HSS0     0x00000040
+#define  CFG_HW_HAS_HSS1     0x00000080
+#define  CFG_HW_HAS_UART0    0x00000100
+#define  CFG_HW_HAS_UART1    0x00000200
+#define  CFG_HW_HAS_EEPROM   0x00000400
+
+#define ETH_ALEN             6
+
+#define BAREBOX_START        0x00000
+#define BAREBOX_LENGTH       0x34000
+#define NPE_A_START          (BAREBOX_START + BAREBOX_LENGTH)
+#define NPE_A_LENGTH         0x05000
+#define NPE_B_START          (NPE_A_START + NPE_A_LENGTH)
+#define NPE_B_LENGTH         0x03000
+#define NPE_C_START          (NPE_B_START + NPE_B_LENGTH)
+#define NPE_C_LENGTH         0x04000
+#define NPE_ENV0_START       (NPE_C_START + NPE_C_LENGTH)
+#define NPE_ENV0_LENGTH      0x20000
+
+static struct device_d cfi_dev = {
+	.name     = "cfi_flash",
+	.map_base = IXP4XX_EXP_BASE(0),
+	.size     = 16 * 1024 * 1024,
+};
+
+static struct eth_plat_info eth_pinfo[2] = {
+	{
+		.regs     = IXP4XX_EthB_BASE,
+		.npe      = 1,
+		.phy      = 0,
+		.rxq      = 20,
+		.txreadyq = 29,
+	}, {
+		.regs     = IXP4XX_EthC_BASE,
+		.npe      = 2,
+		.phy      = 1,
+		.rxq      = 21,
+		.txreadyq = 30,
+	}
+};
+
+static struct device_d eth_dev[2] = {
+	{
+		.name           = "ixp4xx_eth",
+		.platform_data  = &eth_pinfo[0],
+	}, {
+		.id             = 1,
+		.name           = "ixp4xx_eth",
+		.platform_data  = &eth_pinfo[1],
+	}
+};
+
+static struct memory_platform_data ram_pdata = {
+	.name           = "ram0",
+	.flags          = DEVFS_RDWR,
+};
+
+struct device_d sdram_dev = {
+	.id             = -1,
+	.name           = "mem",
+	.map_base       = 0x00000000,
+	.size           = 32 * 1024 * 1024,
+	.platform_data  = &ram_pdata,
+};
+
+static inline u8 __init flash_readb(u32 addr)
+{
+	return __raw_readb(IXP4XX_EXP_BASE(0) + addr);
+}
+
+static int __init gml_devices_init(void)
+{
+	u32 hw_bits;
+	int i;
+
+	IXP4XX_EXP_CS0 = IXP4XX_EXP_EN | IXP4XX_EXP_INTEL |
+		IXP4XX_EXP_BITS(24) | IXP4XX_EXP_WR_EN | IXP4XX_EXP_BYTE_RD16;
+	i = register_device(&cfi_dev);
+	if (i)
+		goto out;
+
+	devfs_add_partition("nor0", BAREBOX_START, BAREBOX_LENGTH,
+			    DEVFS_PARTITION_FIXED | DEVFS_PARTITION_READONLY, "barebox");
+	devfs_add_partition("nor0", NPE_A_START, NPE_A_LENGTH,
+			    DEVFS_PARTITION_FIXED | DEVFS_PARTITION_READONLY, "NPE-A");
+	devfs_add_partition("nor0", NPE_B_START, NPE_B_LENGTH,
+			    DEVFS_PARTITION_FIXED | DEVFS_PARTITION_READONLY, "NPE-B");
+	devfs_add_partition("nor0", NPE_C_START, NPE_C_LENGTH,
+			    DEVFS_PARTITION_FIXED | DEVFS_PARTITION_READONLY, "NPE-C");
+	devfs_add_partition("nor0", NPE_ENV0_START, NPE_ENV0_LENGTH,
+			    DEVFS_PARTITION_FIXED, "env0");
+
+	hw_bits = __raw_readl(IXP4XX_EXP_BASE(0) + CFG_HW_BITS);
+
+	if (hw_bits & CFG_HW_HAS_ETH0) {
+		for (i = 0; i < ETH_ALEN; i++)
+			eth_pinfo[0].hwaddr[i] = flash_readb(CFG_ETH0_ADDRESS + i);
+		register_device(&eth_dev[0]);
+	}
+
+	if (hw_bits & CFG_HW_HAS_ETH1) {
+		for (i = 0; i < ETH_ALEN; i++)
+			eth_pinfo[1].hwaddr[i] = flash_readb(CFG_ETH1_ADDRESS + i);
+		register_device(&eth_dev[1]);
+	}
+
+	sdram_dev.size = __raw_readl(IXP4XX_EXP_BASE(0) + CFG_SDRAM_SIZE);
+	i = register_device(&sdram_dev);
+	if (i)
+		goto out;
+
+	armlinux_add_dram(&sdram_dev);
+	armlinux_set_bootparams((void *)(0x00000100));
+	armlinux_set_architecture(MACH_TYPE_GORAMO_MLR);
+
+out:
+	return i;
+}
+device_initcall(gml_devices_init);
diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig
new file mode 100644
index 0000000..f5e4b41
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/Kconfig
@@ -0,0 +1,26 @@
+if ARCH_IXP4XX
+
+choice
+	prompt "IXP4xx Board Type"
+
+config MACH_MULTILINK
+	bool "Goramo MultiLink"
+	select MACH_HAS_LOWLEVEL_INIT
+endchoice
+
+config BOARDINFO
+	default "Goramo MultiLink" if MACH_MULTILINK
+
+config IXP4XX_QMGR
+	tristate "IXP4xx Queue Manager support"
+	help
+	  This driver supports IXP4xx built-in hardware queue manager
+	  and is required by the Ethernet driver.
+
+config IXP4XX_NPE
+	tristate "IXP4xx Network Processor Engine support"
+	help
+	  This driver supports IXP4xx built-in network coprocessors
+	  and is required by the Ethernet driver.
+
+endif
diff --git a/arch/arm/mach-ixp4xx/Makefile b/arch/arm/mach-ixp4xx/Makefile
new file mode 100644
index 0000000..aa38991
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/Makefile
@@ -0,0 +1,3 @@
+obj-y += lowlevel_init.o generic.o
+obj-$(CONFIG_IXP4XX_QMGR) += qmgr.o
+obj-$(CONFIG_IXP4XX_NPE) += npe.o
diff --git a/arch/arm/mach-ixp4xx/generic.c b/arch/arm/mach-ixp4xx/generic.c
new file mode 100644
index 0000000..02394a4
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/generic.c
@@ -0,0 +1,120 @@
+#include <common.h>
+#include <init.h>
+#include <ns16550.h>
+#include <asm/armlinux.h>
+#include <asm/io.h>
+#include <mach/ixp4xx-regs.h>
+
+#define OSTS_FREQUENCY 66666000
+
+void reset_cpu(ulong addr)
+{
+	/* Use on-chip reset capability */
+	/* This may not work on IXP425 rev. A0 */
+
+	IXP4XX_OSWK = IXP4XX_WDT_KEY;
+	IXP4XX_OSWT = 0; /* request immediate reset */
+	IXP4XX_OSWE = IXP4XX_WDT_RESET_ENABLE | IXP4XX_WDT_COUNT_ENABLE;
+	while (1)
+		;
+}
+
+#include <clock.h>
+
+/**
+ * @brief Provide a simple clock read
+ *
+ * Nothing is simpler.. read direct from clock and provide it
+ * to the caller.
+ *
+ * @return clock counter
+ */
+static uint64_t ixp4xx_clocksource_read(void)
+{
+	return IXP4XX_OSTS;
+}
+
+static struct clocksource cs = {
+	.read = ixp4xx_clocksource_read,
+	.mask = 0xffffffff,
+	.shift = 10,
+};
+
+/**
+ * @brief Initialize the Clock
+ *
+ * We use the Time-Stamp Timer
+ *
+ * @return result of @ref init_clock
+ */
+static int ixp4xx_clocksource_init(void)
+{
+	cs.mult = clocksource_hz2mult(OSTS_FREQUENCY, cs.shift);
+
+	return init_clock(&cs);
+}
+
+/* Run me at boot time */
+core_initcall(ixp4xx_clocksource_init);
+
+
+#ifdef CONFIG_DRIVER_SERIAL_NS16550
+
+/**
+ * @brief UART port register read function for IXP4XX
+ *
+ * @param base base address of UART
+ * @param reg_idx register index
+ *
+ * @return character read from register
+ */
+unsigned int ixp4xx_uart_read(unsigned long base, unsigned char reg_idx)
+{
+	return readb(4 * reg_idx + 3 /* big-endian */ + (uint8_t *)base);
+}
+EXPORT_SYMBOL(ixp4xx_uart_read);
+
+/**
+ * @brief UART port register write function for IXP4XX
+ *
+ * @param val value to write
+ * @param base base address of UART
+ * @param reg_idx register index
+ *
+ * @return void
+ */
+void ixp4xx_uart_write(unsigned int val, unsigned long base, unsigned char reg_idx)
+{
+	writeb(val, 4 * reg_idx + 3 /* big-endian */ + (uint8_t *)base);
+}
+EXPORT_SYMBOL(ixp4xx_uart_write);
+
+
+static struct NS16550_plat serial_plat = {
+	.clock = 14745600,
+	.f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR,
+	.reg_read = ixp4xx_uart_read,
+	.reg_write = ixp4xx_uart_write,
+};
+
+static struct device_d ixp4xx_serial_device = {
+	.name = "serial_ns16550",
+	.map_base = (u32)IXP4XX_UART1_BASE,
+	.size = 1024,
+	.platform_data = (void *)&serial_plat,
+};
+
+/**
+ * @brief UART serial port initialization
+ *
+ * @return result of device registration
+ */
+static int ixp4xx_console_init(void)
+{
+	/* Register the serial port */
+	return register_device(&ixp4xx_serial_device);
+}
+
+console_initcall(ixp4xx_console_init);
+
+#endif /* CONFIG_DRIVER_SERIAL_NS16550 */
diff --git a/arch/arm/mach-ixp4xx/include/mach/cpu.h b/arch/arm/mach-ixp4xx/include/mach/cpu.h
new file mode 100644
index 0000000..3de021e
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/cpu.h
@@ -0,0 +1,70 @@
+/*
+ * arch/arm/mach-ixp4xx/include/mach/cpu.h
+ *
+ * IXP4XX cpu type detection
+ *
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __ASM_ARCH_CPU_H__
+#define __ASM_ARCH_CPU_H__
+
+#include <linux/types.h>
+
+/* Processor id value in CP15 Register 0 */
+#define IXP42X_PROCESSOR_ID_VALUE 0x690541c0 /* including unused 0x690541Ex */
+#define IXP42X_PROCESSOR_ID_MASK  0xffffffc0
+
+#define IXP43X_PROCESSOR_ID_VALUE 0x69054040
+#define IXP43X_PROCESSOR_ID_MASK  0xfffffff0
+
+#define IXP46X_PROCESSOR_ID_VALUE 0x69054200 /* including IXP455 */
+#define IXP46X_PROCESSOR_ID_MASK  0xfffffff0
+
+#define cpu_is_ixp42x_rev_a0() ((read_cpuid_id() & (IXP42X_PROCESSOR_ID_MASK | 0xF)) == \
+				IXP42X_PROCESSOR_ID_VALUE)
+#define cpu_is_ixp42x() ((read_cpuid_id() & IXP42X_PROCESSOR_ID_MASK) == \
+			 IXP42X_PROCESSOR_ID_VALUE)
+#define cpu_is_ixp43x() ((read_cpuid_id() & IXP43X_PROCESSOR_ID_MASK) == \
+			 IXP43X_PROCESSOR_ID_VALUE)
+#define cpu_is_ixp46x() ((read_cpuid_id() & IXP46X_PROCESSOR_ID_MASK) == \
+			 IXP46X_PROCESSOR_ID_VALUE)
+
+/*
+ * The CPU ID never changes at run time, so we might as well tell the
+ * compiler that it's constant.  Use this function to read the CPU ID
+ * rather than directly reading processor_id or read_cpuid() directly.
+ */
+static inline u32 __attribute_const__ read_cpuid_id(void)
+{
+	u32 val;
+	asm("mrc p15, 0, %0, c0, c0, 0" : "=r" (val) : : "cc");
+
+	return val;
+}
+
+static inline u32 ixp4xx_read_feature_bits(void)
+{
+	u32 val = ~IXP4XX_EXP_CFG2;
+
+	if (cpu_is_ixp42x_rev_a0())
+		return IXP42X_FEATURE_MASK & ~(IXP4XX_FEATURE_RCOMP |
+					       IXP4XX_FEATURE_AES);
+	if (cpu_is_ixp42x())
+		return val & IXP42X_FEATURE_MASK;
+	if (cpu_is_ixp43x())
+		return val & IXP43X_FEATURE_MASK;
+	return val & IXP46X_FEATURE_MASK;
+}
+
+static inline void ixp4xx_write_feature_bits(u32 value)
+{
+	IXP4XX_EXP_CFG2 = ~value;
+}
+
+#endif  /* _ASM_ARCH_CPU_H */
diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h
new file mode 100644
index 0000000..da4dc8a
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h
@@ -0,0 +1,365 @@
+/*
+ * Register definitions for IXP4xx chipset.
+ *
+ * Copyright (C) 2002 Intel Corporation.
+ * Copyright (C) 2003-2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _ASM_ARM_IXP4XX_H_
+#define _ASM_ARM_IXP4XX_H_
+
+#ifdef __ASSEMBLER__
+#define IXP4XX_REG(reg)  (reg)
+#else
+#define IXP4XX_REG(reg)  (*(volatile u32 *)(reg))
+#define IXP4XX_BASE(reg) ((u32*)(reg))
+#endif
+
+/* Expansion Bus region */
+
+/* Queue Manager */
+#define IXP4XX_QMGR_BASE              (0x60000000)
+#define IXP4XX_QMGR_REGION_SIZE       (0x00004000)
+
+
+/* PCI Config registers */
+#define IXP4XX_PCI_CFG_BASE           (0xC0000000)
+#define IXP4XX_PCI_CFG_REGION_SIZE    (0x00001000)
+
+/* Peripheral space */
+#define IXP4XX_PERIPHERAL_BASE        (0xC8000000)
+#define IXP4XX_PERIPHERAL_REGION_SIZE (0x00013000)
+
+/*
+ * Debug UART
+ *
+ * This is basically a remap of UART1 into a region that is section
+ * aligned so that it can be used with the low-level debug code.
+ */
+#define IXP4XX_DEBUG_UART_BASE        (0xC8000000)
+#define IXP4XX_DEBUG_UART_REGION_SIZE (0x00001000)
+
+/* Expansion Bus Controller registers. */
+#define IXP4XX_EXP_CS0       IXP4XX_REG(0xC4000000)
+#define IXP4XX_EXP_CS1       IXP4XX_REG(0xC4000004)
+#define IXP4XX_EXP_CS2       IXP4XX_REG(0xC4000008)
+#define IXP4XX_EXP_CS3       IXP4XX_REG(0xC400000C)
+#define IXP4XX_EXP_CS4       IXP4XX_REG(0xC4000010)
+#define IXP4XX_EXP_CS5       IXP4XX_REG(0xC4000014)
+#define IXP4XX_EXP_CS6       IXP4XX_REG(0xC4000018)
+#define IXP4XX_EXP_CS7       IXP4XX_REG(0xC400001C)
+
+#define IXP4XX_EXP_BASE(n)  (0x50000000 + (n) * 1000000)
+
+#define IXP4XX_EXP_EN        0x80000000
+#define IXP4XX_EXP_T1(n)    (0x10000000 * (n)) /* valid: 0 - 3 */
+#define IXP4XX_EXP_T2(n)    (0x04000000 * (n)) /* valid: 0 - 3 */
+#define IXP4XX_EXP_T3(n)    (0x00400000 * (n)) /* valid: 0 - 15 */
+#define IXP4XX_EXP_T4(n)    (0x00100000 * (n)) /* valid: 0 - 3 */
+#define IXP4XX_EXP_T5(n)    (0x00010000 * (n)) /* valid: 0 - 15 */
+#define IXP4XX_EXP_INTEL     0x00000000
+#define IXP4XX_EXP_MOTO      0x00004000
+#define IXP4XX_EXP_HPI       0x00008000
+#define IXP4XX_EXP_BITS(n)  (0x00000400 * ((n) - 9)) /* valid: 9 - 24 */
+#define IXP4XX_EXP_BYTE_RD16 0x00000040
+#define IXP4XX_EXP_HRDY_POL  0x00000020
+#define IXP4XX_EXP_MUX_EN    0x00000010
+#define IXP4XX_EXP_SPLT_EN   0x00000008
+#define IXP4XX_EXP_WR_EN     0x00000002
+#define IXP4XX_EXP_BYTE_EN   0x00000001
+
+#define IXP4XX_EXP_CFG0      IXP4XX_REG(0xC4000020)
+#define IXP4XX_EXP_CFG1      IXP4XX_REG(0xC4000024)
+#define IXP4XX_EXP_CFG2      IXP4XX_REG(0xC4000028)
+#define IXP4XX_EXP_CFG3      IXP4XX_REG(0xC400002C)
+
+
+/* Peripheral Space Register Region Base Addresses */
+#define IXP4XX_UART1_BASE    IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x0000)
+#define IXP4XX_UART2_BASE    IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x1000)
+#define IXP4XX_PMU_BASE      IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x2000)
+#define IXP4XX_INTC_BASE     IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x3000)
+#define IXP4XX_GPIO_BASE     IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x4000)
+#define IXP4XX_NPEA_BASE     IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x6000)
+#define IXP4XX_NPEB_BASE     IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x7000)
+#define IXP4XX_NPEC_BASE     IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x8000)
+#define IXP4XX_EthB_BASE     IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x9000)
+#define IXP4XX_EthC_BASE     IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0xA000)
+#define IXP4XX_USB_BASE      IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0xB000)
+/* IXP46x only */
+#define IXP4XX_EthA_BASE     IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0xC000)
+#define IXP4XX_EthB1_BASE    IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0xD000)
+#define IXP4XX_EthB2_BASE    IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0xE000)
+#define IXP4XX_EthB3_BASE    IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0xF000)
+#define IXP4XX_TIMESYNC_BASE IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x10000)
+#define IXP4XX_I2C_BASE      IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x11000)
+#define IXP4XX_SSP_BASE      IXP4XX_BASE(IXP4XX_PERIPHERAL_BASE + 0x12000)
+
+/*
+  Constants to make it easy to access Interrupt Controller registers
+ */
+#define IXP4XX_ICPR_OFFSET   0x00 /* Interrupt Status */
+#define IXP4XX_ICMR_OFFSET   0x04 /* Interrupt Enable */
+#define IXP4XX_ICLR_OFFSET   0x08 /* Interrupt IRQ/FIQ Select */
+#define IXP4XX_ICIP_OFFSET   0x0C /* IRQ Status */
+#define IXP4XX_ICFP_OFFSET   0x10 /* FIQ Status */
+#define IXP4XX_ICHR_OFFSET   0x14 /* Interrupt Priority */
+#define IXP4XX_ICIH_OFFSET   0x18 /* IRQ Highest Pri Int */
+#define IXP4XX_ICFH_OFFSET   0x1C /* FIQ Highest Pri Int */
+
+/* IXP465-only */
+#define IXP4XX_ICPR2_OFFSET  0x20 /* Interrupt Status 2 */
+#define IXP4XX_ICMR2_OFFSET  0x24 /* Interrupt Enable 2 */
+#define IXP4XX_ICLR2_OFFSET  0x28 /* Interrupt IRQ/FIQ Select 2 */
+#define IXP4XX_ICIP2_OFFSET  0x2C /* IRQ Status */
+#define IXP4XX_ICFP2_OFFSET  0x30 /* FIQ Status */
+#define IXP4XX_ICEEN_OFFSET  0x34 /* Error High Pri Enable */
+
+
+/* Interrupt Controller Register Definitions. */
+#define IXP4XX_INTC_REG(x)   ((volatile u32 *)(IXP4XX_INTC_BASE + (x)))
+
+#define IXP4XX_ICPR          IXP4XX_INTC_REG(IXP4XX_ICPR_OFFSET)
+#define IXP4XX_ICMR          IXP4XX_INTC_REG(IXP4XX_ICMR_OFFSET)
+#define IXP4XX_ICLR          IXP4XX_INTC_REG(IXP4XX_ICLR_OFFSET)
+#define IXP4XX_ICIP          IXP4XX_INTC_REG(IXP4XX_ICIP_OFFSET)
+#define IXP4XX_ICFP          IXP4XX_INTC_REG(IXP4XX_ICFP_OFFSET)
+#define IXP4XX_ICHR          IXP4XX_INTC_REG(IXP4XX_ICHR_OFFSET)
+#define IXP4XX_ICIH          IXP4XX_INTC_REG(IXP4XX_ICIH_OFFSET)
+#define IXP4XX_ICFH          IXP4XX_INTC_REG(IXP4XX_ICFH_OFFSET)
+#define IXP4XX_ICPR2         IXP4XX_INTC_REG(IXP4XX_ICPR2_OFFSET)
+#define IXP4XX_ICMR2         IXP4XX_INTC_REG(IXP4XX_ICMR2_OFFSET)
+#define IXP4XX_ICLR2         IXP4XX_INTC_REG(IXP4XX_ICLR2_OFFSET)
+#define IXP4XX_ICIP2         IXP4XX_INTC_REG(IXP4XX_ICIP2_OFFSET)
+#define IXP4XX_ICFP2         IXP4XX_INTC_REG(IXP4XX_ICFP2_OFFSET)
+#define IXP4XX_ICEEN         IXP4XX_INTC_REG(IXP4XX_ICEEN_OFFSET)
+
+/* Constants to make it easy to access GPIO registers */
+#define IXP4XX_GPIO_GPOUTR_OFFSET   0x00
+#define IXP4XX_GPIO_GPOER_OFFSET    0x04
+#define IXP4XX_GPIO_GPINR_OFFSET    0x08
+#define IXP4XX_GPIO_GPISR_OFFSET    0x0C
+#define IXP4XX_GPIO_GPIT1R_OFFSET   0x10
+#define IXP4XX_GPIO_GPIT2R_OFFSET   0x14
+#define IXP4XX_GPIO_GPCLKR_OFFSET   0x18
+#define IXP4XX_GPIO_GPDBSELR_OFFSET 0x1C
+
+/* GPIO Register Definitions - perform only 32-bit reads/writes */
+#define IXP4XX_GPIO_REG(x)   ((volatile u32 *)(IXP4XX_GPIO_BASE + (x)))
+
+#define IXP4XX_GPIO_GPOUTR   IXP4XX_GPIO_REG(IXP4XX_GPIO_GPOUTR_OFFSET)
+#define IXP4XX_GPIO_GPOER    IXP4XX_GPIO_REG(IXP4XX_GPIO_GPOER_OFFSET)
+#define IXP4XX_GPIO_GPINR    IXP4XX_GPIO_REG(IXP4XX_GPIO_GPINR_OFFSET)
+#define IXP4XX_GPIO_GPISR    IXP4XX_GPIO_REG(IXP4XX_GPIO_GPISR_OFFSET)
+#define IXP4XX_GPIO_GPIT1R   IXP4XX_GPIO_REG(IXP4XX_GPIO_GPIT1R_OFFSET)
+#define IXP4XX_GPIO_GPIT2R   IXP4XX_GPIO_REG(IXP4XX_GPIO_GPIT2R_OFFSET)
+#define IXP4XX_GPIO_GPCLKR   IXP4XX_GPIO_REG(IXP4XX_GPIO_GPCLKR_OFFSET)
+#define IXP4XX_GPIO_GPDBSELR IXP4XX_GPIO_REG(IXP4XX_GPIO_GPDBSELR_OFFSET)
+
+/* GPIO register bit definitions */
+
+/* Interrupt styles */
+#define IXP4XX_GPIO_STYLE_ACTIVE_HIGH  0x0
+#define IXP4XX_GPIO_STYLE_ACTIVE_LOW   0x1
+#define IXP4XX_GPIO_STYLE_RISING_EDGE  0x2
+#define IXP4XX_GPIO_STYLE_FALLING_EDGE 0x3
+#define IXP4XX_GPIO_STYLE_TRANSITIONAL 0x4
+
+/* Mask used to clear interrupt styles */
+#define IXP4XX_GPIO_STYLE_CLEAR        0x7
+#define IXP4XX_GPIO_STYLE_SIZE         3
+
+/* Operating System Timer Register Definitions. */
+#define IXP4XX_OSTS          IXP4XX_REG(0xC8005000) /* Continious TimeStamp */
+#define IXP4XX_OST1          IXP4XX_REG(0xC8005004) /* Timer 1 Timestamp */
+#define IXP4XX_OSRT1         IXP4XX_REG(0xC8005008) /* Timer 1 Reload */
+#define IXP4XX_OST2          IXP4XX_REG(0xC800500C) /* Timer 2 Timestamp */
+#define IXP4XX_OSRT2         IXP4XX_REG(0xC8005010) /* Timer 2 Reload */
+#define IXP4XX_OSWT          IXP4XX_REG(0xC8005014) /* Watchdog Timer */
+#define IXP4XX_OSWE          IXP4XX_REG(0xC8005018) /* Watchdog Enable */
+#define IXP4XX_OSWK          IXP4XX_REG(0xC800501C) /* Watchdog Key */
+#define IXP4XX_OSST          IXP4XX_REG(0xC8005020) /* Timer Status */
+
+/* Timer register values and bit definitions */
+#define IXP4XX_OST_ENABLE            0x00000001
+#define IXP4XX_OST_ONE_SHOT          0x00000002
+/* Low order bits of reload value ignored */
+#define IXP4XX_OST_RELOAD_MASK       0x00000003
+#define IXP4XX_OST_DISABLED          0x00000000
+#define IXP4XX_OSST_TIMER_1_PEND     0x00000001
+#define IXP4XX_OSST_TIMER_2_PEND     0x00000002
+#define IXP4XX_OSST_TIMER_TS_PEND    0x00000004
+#define IXP4XX_OSST_TIMER_WDOG_PEND  0x00000008
+#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010
+
+#define IXP4XX_WDT_KEY               0x0000482E
+
+#define IXP4XX_WDT_RESET_ENABLE      0x00000001
+#define IXP4XX_WDT_IRQ_ENABLE        0x00000002
+#define IXP4XX_WDT_COUNT_ENABLE      0x00000004
+
+
+/* Constants to make it easy to access PCI Control/Status registers */
+#define PCI_NP_AD_OFFSET           0x00
+#define PCI_NP_CBE_OFFSET          0x04
+#define PCI_NP_WDATA_OFFSET        0x08
+#define PCI_NP_RDATA_OFFSET        0x0c
+#define PCI_CRP_AD_CBE_OFFSET      0x10
+#define PCI_CRP_WDATA_OFFSET       0x14
+#define PCI_CRP_RDATA_OFFSET       0x18
+#define PCI_CSR_OFFSET             0x1c
+#define PCI_ISR_OFFSET             0x20
+#define PCI_INTEN_OFFSET           0x24
+#define PCI_DMACTRL_OFFSET         0x28
+#define PCI_AHBMEMBASE_OFFSET      0x2c
+#define PCI_AHBIOBASE_OFFSET       0x30
+#define PCI_PCIMEMBASE_OFFSET      0x34
+#define PCI_AHBDOORBELL_OFFSET     0x38
+#define PCI_PCIDOORBELL_OFFSET     0x3C
+#define PCI_ATPDMA0_AHBADDR_OFFSET 0x40
+#define PCI_ATPDMA0_PCIADDR_OFFSET 0x44
+#define PCI_ATPDMA0_LENADDR_OFFSET 0x48
+#define PCI_ATPDMA1_AHBADDR_OFFSET 0x4C
+#define PCI_ATPDMA1_PCIADDR_OFFSET 0x50
+#define PCI_ATPDMA1_LENADDR_OFFSET 0x54
+
+/* PCI Control/Status Registers */
+#define IXP4XX_PCI_CSR(x) ((volatile u32 *)(IXP4XX_PCI_CFG_BASE + (x)))
+
+#define PCI_NP_AD            IXP4XX_PCI_CSR(PCI_NP_AD_OFFSET)
+#define PCI_NP_CBE           IXP4XX_PCI_CSR(PCI_NP_CBE_OFFSET)
+#define PCI_NP_WDATA         IXP4XX_PCI_CSR(PCI_NP_WDATA_OFFSET)
+#define PCI_NP_RDATA         IXP4XX_PCI_CSR(PCI_NP_RDATA_OFFSET)
+#define PCI_CRP_AD_CBE       IXP4XX_PCI_CSR(PCI_CRP_AD_CBE_OFFSET)
+#define PCI_CRP_WDATA        IXP4XX_PCI_CSR(PCI_CRP_WDATA_OFFSET)
+#define PCI_CRP_RDATA        IXP4XX_PCI_CSR(PCI_CRP_RDATA_OFFSET)
+#define PCI_CSR              IXP4XX_PCI_CSR(PCI_CSR_OFFSET)
+#define PCI_ISR              IXP4XX_PCI_CSR(PCI_ISR_OFFSET)
+#define PCI_INTEN            IXP4XX_PCI_CSR(PCI_INTEN_OFFSET)
+#define PCI_DMACTRL          IXP4XX_PCI_CSR(PCI_DMACTRL_OFFSET)
+#define PCI_AHBMEMBASE       IXP4XX_PCI_CSR(PCI_AHBMEMBASE_OFFSET)
+#define PCI_AHBIOBASE        IXP4XX_PCI_CSR(PCI_AHBIOBASE_OFFSET)
+#define PCI_PCIMEMBASE       IXP4XX_PCI_CSR(PCI_PCIMEMBASE_OFFSET)
+#define PCI_AHBDOORBELL      IXP4XX_PCI_CSR(PCI_AHBDOORBELL_OFFSET)
+#define PCI_PCIDOORBELL      IXP4XX_PCI_CSR(PCI_PCIDOORBELL_OFFSET)
+#define PCI_ATPDMA0_AHBADDR  IXP4XX_PCI_CSR(PCI_ATPDMA0_AHBADDR_OFFSET)
+#define PCI_ATPDMA0_PCIADDR  IXP4XX_PCI_CSR(PCI_ATPDMA0_PCIADDR_OFFSET)
+#define PCI_ATPDMA0_LENADDR  IXP4XX_PCI_CSR(PCI_ATPDMA0_LENADDR_OFFSET)
+#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)
+
+/* PCI register values and bit definitions */
+
+/* CSR bit definitions */
+#define PCI_CSR_HOST         0x00000001
+#define PCI_CSR_ARBEN        0x00000002
+#define PCI_CSR_ADS          0x00000004
+#define PCI_CSR_PDS          0x00000008
+#define PCI_CSR_ABE          0x00000010
+#define PCI_CSR_DBT          0x00000020
+#define PCI_CSR_ASE          0x00000100
+#define PCI_CSR_IC           0x00008000
+
+/* ISR (Interrupt status) Register bit definitions */
+#define PCI_ISR_PSE          0x00000001
+#define PCI_ISR_PFE          0x00000002
+#define PCI_ISR_PPE          0x00000004
+#define PCI_ISR_AHBE         0x00000008
+#define PCI_ISR_APDC         0x00000010
+#define PCI_ISR_PADC         0x00000020
+#define PCI_ISR_ADB          0x00000040
+#define PCI_ISR_PDB          0x00000080
+
+/* INTEN (Interrupt Enable) Register bit definitions */
+#define PCI_INTEN_PSE        0x00000001
+#define PCI_INTEN_PFE        0x00000002
+#define PCI_INTEN_PPE        0x00000004
+#define PCI_INTEN_AHBE       0x00000008
+#define PCI_INTEN_APDC       0x00000010
+#define PCI_INTEN_PADC       0x00000020
+#define PCI_INTEN_ADB        0x00000040
+#define PCI_INTEN_PDB        0x00000080
+
+/* Shift value for byte enable on NP cmd/byte enable register */
+#define IXP4XX_PCI_NP_CBE_BESL 4
+
+/* PCI commands supported by NP access unit */
+#define NP_CMD_IOREAD        0x2
+#define NP_CMD_IOWRITE       0x3
+#define NP_CMD_CONFIGREAD    0xA
+#define NP_CMD_CONFIGWRITE   0xB
+#define NP_CMD_MEMREAD       0x6
+#define NP_CMD_MEMWRITE      0x7
+
+/* Constants for CRP access into local config space */
+#define CRP_AD_CBE_BESL      20
+#define CRP_AD_CBE_WRITE     0x00010000
+
+/* USB Device Controller */
+# define IXP4XX_USB_REG(x)   (*((volatile u32 *)(x)))
+
+/* SDRAM Controller registers. */
+#define IXP4XX_SDRAM_CONFIG  IXP4XX_REG(0xCC000000)
+#define IXP4XX_SDRAM_REFRESH IXP4XX_REG(0xCC000004)
+#define IXP4XX_SDRAM_IR      IXP4XX_REG(0xCC000008)
+
+/* "fuse" bits of IXP_EXP_CFG2 */
+/* All IXP4xx CPUs */
+#define IXP4XX_FEATURE_RCOMP            (1 << 0)
+#define IXP4XX_FEATURE_USB_DEVICE       (1 << 1)
+#define IXP4XX_FEATURE_HASH             (1 << 2)
+#define IXP4XX_FEATURE_AES              (1 << 3)
+#define IXP4XX_FEATURE_DES              (1 << 4)
+#define IXP4XX_FEATURE_HDLC             (1 << 5)
+#define IXP4XX_FEATURE_AAL              (1 << 6)
+#define IXP4XX_FEATURE_HSS              (1 << 7)
+#define IXP4XX_FEATURE_UTOPIA           (1 << 8)
+#define IXP4XX_FEATURE_NPEB_ETH0        (1 << 9)
+#define IXP4XX_FEATURE_NPEC_ETH         (1 << 10)
+#define IXP4XX_FEATURE_RESET_NPEA       (1 << 11)
+#define IXP4XX_FEATURE_RESET_NPEB       (1 << 12)
+#define IXP4XX_FEATURE_RESET_NPEC       (1 << 13)
+#define IXP4XX_FEATURE_PCI              (1 << 14)
+#define IXP4XX_FEATURE_UTOPIA_PHY_LIMIT (3 << 16)
+#define IXP4XX_FEATURE_XSCALE_MAX_FREQ  (3 << 22)
+#define IXP42X_FEATURE_MASK (IXP4XX_FEATURE_RCOMP            | \
+			     IXP4XX_FEATURE_USB_DEVICE       | \
+			     IXP4XX_FEATURE_HASH             | \
+			     IXP4XX_FEATURE_AES              | \
+			     IXP4XX_FEATURE_DES              | \
+			     IXP4XX_FEATURE_HDLC             | \
+			     IXP4XX_FEATURE_AAL              | \
+			     IXP4XX_FEATURE_HSS              | \
+			     IXP4XX_FEATURE_UTOPIA           | \
+			     IXP4XX_FEATURE_NPEB_ETH0        | \
+			     IXP4XX_FEATURE_NPEC_ETH         | \
+			     IXP4XX_FEATURE_RESET_NPEA       | \
+			     IXP4XX_FEATURE_RESET_NPEB       | \
+			     IXP4XX_FEATURE_RESET_NPEC       | \
+			     IXP4XX_FEATURE_PCI              | \
+			     IXP4XX_FEATURE_UTOPIA_PHY_LIMIT | \
+			     IXP4XX_FEATURE_XSCALE_MAX_FREQ)
+
+/* IXP43x/46x CPUs */
+#define IXP4XX_FEATURE_ECC_TIMESYNC     (1 << 15)
+#define IXP4XX_FEATURE_USB_HOST         (1 << 18)
+#define IXP4XX_FEATURE_NPEA_ETH         (1 << 19)
+#define IXP43X_FEATURE_MASK (IXP42X_FEATURE_MASK             | \
+			     IXP4XX_FEATURE_ECC_TIMESYNC     | \
+			     IXP4XX_FEATURE_USB_HOST         | \
+			     IXP4XX_FEATURE_NPEA_ETH)
+
+/* IXP46x CPU (including IXP455) only */
+#define IXP4XX_FEATURE_NPEB_ETH_1_TO_3  (1 << 20)
+#define IXP4XX_FEATURE_RSA              (1 << 21)
+#define IXP46X_FEATURE_MASK (IXP43X_FEATURE_MASK             | \
+			     IXP4XX_FEATURE_NPEB_ETH_1_TO_3  | \
+			     IXP4XX_FEATURE_RSA)
+
+#endif
diff --git a/arch/arm/mach-ixp4xx/include/mach/npe.h b/arch/arm/mach-ixp4xx/include/mach/npe.h
new file mode 100644
index 0000000..18bd01b
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/npe.h
@@ -0,0 +1,30 @@
+#ifndef __IXP4XX_NPE_H
+#define __IXP4XX_NPE_H
+
+#include <common.h>
+
+#define NPE_NAME_LENGTH 5
+
+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 npe_regs *regs;
+	int id, valid;
+	const char name[NPE_NAME_LENGTH + 1];
+};
+
+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);
+struct npe *npe_request(int id);
+
+#endif /* __IXP4XX_NPE_H */
diff --git a/arch/arm/mach-ixp4xx/include/mach/platform.h b/arch/arm/mach-ixp4xx/include/mach/platform.h
new file mode 100644
index 0000000..1df4aa4
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/platform.h
@@ -0,0 +1,15 @@
+#include <asm/types.h>
+
+#define IXP4XX_ETH_NPEA 0x00
+#define IXP4XX_ETH_NPEB 0x10
+#define IXP4XX_ETH_NPEC 0x20
+
+/* Information about built-in Ethernet MAC interfaces */
+struct eth_plat_info {
+	void *regs;
+	u8 npe;
+	u8 phy; /* MII PHY ID, 0 - 31 */
+	u8 rxq; /* configurable, currently 0 - 31 only */
+	u8 txreadyq;
+	u8 hwaddr[6];
+};
diff --git a/arch/arm/mach-ixp4xx/include/mach/qmgr.h b/arch/arm/mach-ixp4xx/include/mach/qmgr.h
new file mode 100644
index 0000000..4e9b8d4
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/qmgr.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 Krzysztof Halasa <khc at pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef IXP4XX_QMGR_H
+#define IXP4XX_QMGR_H
+
+#include <common.h>
+#include <mach/ixp4xx-regs.h>
+#include <asm/io.h>
+
+#define DEBUG_QMGR       0
+
+#define HALF_QUEUES     32
+#define QUEUES          64
+#define MAX_QUEUE_LENGTH 4 /* in dwords */
+
+#define QUEUE_STAT1_EMPTY               1 /* queue status bits */
+#define QUEUE_STAT1_NEARLY_EMPTY        2
+#define QUEUE_STAT1_NEARLY_FULL         4
+#define QUEUE_STAT1_FULL                8
+#define QUEUE_STAT2_UNDERFLOW           1
+#define QUEUE_STAT2_OVERFLOW            2
+
+#define QUEUE_WATERMARK_0_ENTRIES       0
+#define QUEUE_WATERMARK_1_ENTRY         1
+#define QUEUE_WATERMARK_2_ENTRIES       2
+#define QUEUE_WATERMARK_4_ENTRIES       3
+#define QUEUE_WATERMARK_8_ENTRIES       4
+#define QUEUE_WATERMARK_16_ENTRIES      5
+#define QUEUE_WATERMARK_32_ENTRIES      6
+#define QUEUE_WATERMARK_64_ENTRIES      7
+
+/* queue interrupt request conditions */
+#define QUEUE_IRQ_SRC_EMPTY             0
+#define QUEUE_IRQ_SRC_NEARLY_EMPTY      1
+#define QUEUE_IRQ_SRC_NEARLY_FULL       2
+#define QUEUE_IRQ_SRC_FULL              3
+#define QUEUE_IRQ_SRC_NOT_EMPTY         4
+#define QUEUE_IRQ_SRC_NOT_NEARLY_EMPTY  5
+#define QUEUE_IRQ_SRC_NOT_NEARLY_FULL   6
+#define QUEUE_IRQ_SRC_NOT_FULL          7
+
+struct qmgr_regs {
+	u32 acc[QUEUES][MAX_QUEUE_LENGTH]; /* 0x000 - 0x3FF */
+	u32 stat1[4];         /* 0x400 - 0x40F */
+	u32 stat2[2];         /* 0x410 - 0x417 */
+	u32 statne_h;         /* 0x418 - queue nearly empty */
+	u32 statf_h;          /* 0x41C - queue full */
+	u32 irqsrc[4];        /* 0x420 - 0x42F IRC source */
+	u32 irqen[2];         /* 0x430 - 0x437 IRQ enabled */
+	u32 irqstat[2];       /* 0x438 - 0x43F - IRQ access only */
+	u32 reserved[1776];
+	u32 sram[2048];       /* 0x2000 - 0x3FFF - config and buffer */
+};
+
+static const struct qmgr_regs *qmgr_regs = (struct qmgr_regs *)IXP4XX_QMGR_BASE;
+
+void qmgr_set_irq(unsigned int queue, int src,
+		  void (*handler)(void *pdev), void *pdev);
+void qmgr_enable_irq(unsigned int queue);
+void qmgr_disable_irq(unsigned int queue);
+
+/* request_ and release_queue() must be called from non-IRQ context */
+
+#if DEBUG_QMGR
+extern char qmgr_queue_descs[HALF_QUEUES][32];
+
+void qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+			unsigned int nearly_empty_watermark,
+			unsigned int nearly_full_watermark,
+			const char *desc_format, const char* name);
+#else
+void __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+			  unsigned int nearly_empty_watermark,
+			  unsigned int nearly_full_watermark);
+#define qmgr_request_queue(queue, len, nearly_empty_watermark,       \
+			   nearly_full_watermark, desc_format, name) \
+	__qmgr_request_queue(queue, len, nearly_empty_watermark,     \
+			     nearly_full_watermark)
+#endif
+
+void qmgr_release_queue(unsigned int queue);
+
+
+static inline void qmgr_put_entry(unsigned int queue, u32 val)
+{
+#if DEBUG_QMGR
+	BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */
+
+	fprintf(stderr, "Queue %s(%i) put %X\n",
+		qmgr_queue_descs[queue], queue, val);
+#endif
+	__raw_writel(val, &qmgr_regs->acc[queue][0]);
+}
+
+static inline u32 qmgr_get_entry(unsigned int queue)
+{
+	u32 val;
+	val = __raw_readl(&qmgr_regs->acc[queue][0]);
+#if DEBUG_QMGR
+	BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */
+
+	fprintf(stderr, "Queue %s(%i) get %X\n",
+		qmgr_queue_descs[queue], queue, val);
+#endif
+	return val;
+}
+
+static inline int __qmgr_get_stat1(unsigned int queue)
+{
+	return (__raw_readl(&qmgr_regs->stat1[queue >> 3])
+		>> ((queue & 7) << 2)) & 0xF;
+}
+
+/**
+ * qmgr_stat_empty() - checks if a hardware queue is empty
+ * @queue: queue number
+ *
+ * Returns non-zero value if the queue is empty.
+ */
+static inline int qmgr_stat_empty(unsigned int queue)
+{
+	return __qmgr_get_stat1(queue) & QUEUE_STAT1_EMPTY;
+}
+
+/**
+ * qmgr_stat_below_low_watermark() - checks if a queue is below low watermark
+ * @queue: queue number
+ *
+ * Returns non-zero value if the queue is below low watermark.
+ */
+static inline int qmgr_stat_below_low_watermark(unsigned int queue)
+{
+	return __qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_EMPTY;
+}
+
+/**
+ * qmgr_stat_above_high_watermark() - checks if a queue is above high watermark
+ * @queue: queue number
+ *
+ * Returns non-zero value if the queue is above high watermark
+ */
+static inline int qmgr_stat_above_high_watermark(unsigned int queue)
+{
+	return __qmgr_get_stat1(queue) & QUEUE_STAT1_NEARLY_FULL;
+}
+
+/**
+ * qmgr_stat_full() - checks if a hardware queue is full
+ * @queue: queue number
+ *
+ * Returns non-zero value if the queue is full.
+ */
+static inline int qmgr_stat_full(unsigned int queue)
+{
+	return __qmgr_get_stat1(queue) & QUEUE_STAT1_FULL;
+}
+
+#endif
diff --git a/arch/arm/mach-ixp4xx/lowlevel_init.S b/arch/arm/mach-ixp4xx/lowlevel_init.S
new file mode 100644
index 0000000..a4e93ef
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/lowlevel_init.S
@@ -0,0 +1,17 @@
+#include <mach/ixp4xx-regs.h>
+
+	.section ".text_bare_init", "ax"
+
+	.globl arch_init_lowlevel
+arch_init_lowlevel:
+	ldr r0, cfg
+	ldr r1, [r0]
+	and r1, r1, #~0x80000000 /* unmap EXP bus from 0x0 */
+	str r1, [r0]
+
+	/* return to ROM */
+	orr lr, #0x50000000
+	mov pc, lr
+
+cfg:
+	.word IXP4XX_EXP_CFG0
diff --git a/arch/arm/mach-ixp4xx/npe.c b/arch/arm/mach-ixp4xx/npe.c
new file mode 100644
index 0000000..a8ddd5a
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/npe.c
@@ -0,0 +1,668 @@
+/*
+ * Intel IXP4xx Network Processor Engine driver for Linux
+ *
+ * Copyright (C) 2007 Krzysztof Halasa <khc at pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <fs.h>
+#include <init.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <mach/ixp4xx-regs.h>
+#include <mach/cpu.h>
+#include <mach/npe.h>
+
+#define DEBUG_MSG                      0
+#define DEBUG_FW                       0
+
+#define NPE_COUNT                      3
+#define MAX_RETRIES                    1000  /* microseconds */
+#define NPE_42X_DATA_SIZE              0x800 /* in dwords */
+#define NPE_46X_DATA_SIZE              0x1000
+#define NPE_A_42X_INSTR_SIZE           0x1000
+#define NPE_B_AND_C_42X_INSTR_SIZE     0x800
+#define NPE_46X_INSTR_SIZE             0x1000
+#define REGS_SIZE                      0x1000
+
+#define NPE_PHYS_REG                   32
+
+#define FW_MAGIC                       0xFEEDF00D
+#define FW_BLOCK_TYPE_INSTR            0x0
+#define FW_BLOCK_TYPE_DATA             0x1
+#define FW_BLOCK_TYPE_EOF              0xF
+
+/* NPE exec status (read) and command (write) */
+#define CMD_NPE_STEP                   0x01
+#define CMD_NPE_START                  0x02
+#define CMD_NPE_STOP                   0x03
+#define CMD_NPE_CLR_PIPE               0x04
+#define CMD_CLR_PROFILE_CNT            0x0C
+#define CMD_RD_INS_MEM                 0x10 /* instruction memory */
+#define CMD_WR_INS_MEM                 0x11
+#define CMD_RD_DATA_MEM                0x12 /* data memory */
+#define CMD_WR_DATA_MEM                0x13
+#define CMD_RD_ECS_REG                 0x14 /* exec access register */
+#define CMD_WR_ECS_REG                 0x15
+
+#define STAT_RUN                       0x80000000
+#define STAT_STOP                      0x40000000
+#define STAT_CLEAR                     0x20000000
+#define STAT_ECS_K                     0x00800000 /* pipeline clean */
+
+#define NPE_STEVT                      0x1B
+#define NPE_STARTPC                    0x1C
+#define NPE_REGMAP                     0x1E
+#define NPE_CINDEX                     0x1F
+
+#define INSTR_WR_REG_SHORT             0x0000C000
+#define INSTR_WR_REG_BYTE              0x00004000
+#define INSTR_RD_FIFO                  0x0F888220
+#define INSTR_RESET_MBOX               0x0FAC8210
+
+#define ECS_BG_CTXT_REG_0              0x00 /* Background Executing Context */
+#define ECS_BG_CTXT_REG_1              0x01 /* Stack level */
+#define ECS_BG_CTXT_REG_2              0x02
+#define ECS_PRI_1_CTXT_REG_0           0x04 /* Priority 1 Executing Context */
+#define ECS_PRI_1_CTXT_REG_1           0x05 /* Stack level */
+#define ECS_PRI_1_CTXT_REG_2           0x06
+#define ECS_PRI_2_CTXT_REG_0           0x08 /* Priority 2 Executing Context */
+#define ECS_PRI_2_CTXT_REG_1           0x09 /* Stack level */
+#define ECS_PRI_2_CTXT_REG_2           0x0A
+#define ECS_DBG_CTXT_REG_0             0x0C /* Debug Executing Context */
+#define ECS_DBG_CTXT_REG_1             0x0D /* Stack level */
+#define ECS_DBG_CTXT_REG_2             0x0E
+#define ECS_INSTRUCT_REG               0x11 /* NPE Instruction Register */
+
+#define ECS_REG_0_ACTIVE               0x80000000 /* all levels */
+#define ECS_REG_0_NEXTPC_MASK          0x1FFF0000 /* BG/PRI1/PRI2 levels */
+#define ECS_REG_0_LDUR_BITS            8
+#define ECS_REG_0_LDUR_MASK            0x00000700 /* all levels */
+#define ECS_REG_1_CCTXT_BITS           16
+#define ECS_REG_1_CCTXT_MASK           0x000F0000 /* all levels */
+#define ECS_REG_1_SELCTXT_BITS         0
+#define ECS_REG_1_SELCTXT_MASK         0x0000000F /* all levels */
+#define ECS_DBG_REG_2_IF               0x00100000 /* debug level */
+#define ECS_DBG_REG_2_IE               0x00080000 /* debug level */
+
+/* NPE watchpoint_fifo register bit */
+#define WFIFO_VALID                    0x80000000
+
+/* NPE messaging_status register bit definitions */
+#define MSGSTAT_OFNE  0x00010000 /* OutFifoNotEmpty */
+#define MSGSTAT_IFNF  0x00020000 /* InFifoNotFull */
+#define MSGSTAT_OFNF  0x00040000 /* OutFifoNotFull */
+#define MSGSTAT_IFNE  0x00080000 /* InFifoNotEmpty */
+#define MSGSTAT_MBINT 0x00100000 /* Mailbox interrupt */
+#define MSGSTAT_IFINT 0x00200000 /* InFifo interrupt */
+#define MSGSTAT_OFINT 0x00400000 /* OutFifo interrupt */
+#define MSGSTAT_WFINT 0x00800000 /* WatchFifo interrupt */
+
+/* NPE messaging_control register bit definitions */
+#define MSGCTL_OUT_FIFO       0x00010000 /* enable output FIFO */
+#define MSGCTL_IN_FIFO        0x00020000 /* enable input FIFO */
+#define MSGCTL_OUT_FIFO_WRITE 0x01000000 /* enable FIFO + WRITE */
+#define MSGCTL_IN_FIFO_WRITE  0x02000000
+
+/* NPE mailbox_status value for reset */
+#define RESET_MBOX_STAT          0x0000F0F0
+
+#define print_npe(npe, fmt, ...) fprintf(stderr, "%s: " fmt, npe->name, ## __VA_ARGS__)
+
+#if DEBUG_MSG
+#define debug_msg(npe, fmt, ...) print_npe(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] = {
+	{
+		.regs = (struct npe_regs *)IXP4XX_NPEA_BASE,
+		.id = 0,
+		.name = "NPE-A",
+	}, {
+		.regs = (struct npe_regs *)IXP4XX_NPEB_BASE,
+		.id = 1,
+		.name = "NPE-B",
+	}, {
+		.regs = (struct npe_regs *)IXP4XX_NPEC_BASE,
+		.id = 2,
+		.name = "NPE-C",
+	}
+};
+
+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 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(npe, "reset: npe_debug_instr(): timeout\n");
+	return -ETIMEDOUT;
+}
+
+static int 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 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 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(npe, "npe_reset: read FIFO = 0x%X\n",
+			  __raw_readl(&npe->regs->in_out_fifo));
+
+	while (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE)
+		/* step execution of the NPE intruction to read inFIFO using
+		   the Debug Executing Context stack */
+		if (npe_debug_instr(npe, INSTR_RD_FIFO, 0, 0))
+			return -ETIMEDOUT;
+
+	/* reset the mailbox reg from the XScale side */
+	__raw_writel(RESET_MBOX_STAT, &npe->regs->mailbox_status);
+	/* from NPE side */
+	if (npe_debug_instr(npe, INSTR_RESET_MBOX, 0, 0))
+		return -ETIMEDOUT;
+
+	/* Reset the physical registers in the NPE register file */
+	for (val = 0; val < NPE_PHYS_REG; val++) {
+		if (npe_logical_reg_write16(npe, NPE_REGMAP, val >> 1, 0))
+			return -ETIMEDOUT;
+		/* address is either 0 or 4 */
+		if (npe_logical_reg_write32(npe, (val & 1) * 4, 0, 0))
+			return -ETIMEDOUT;
+	}
+
+	/* Reset the context store = each context's Context Store registers */
+
+	/* Context 0 has no STARTPC. Instead, this value is used to set NextPC
+	   for Background ECS, to set where NPE starts executing code */
+	val = npe_cmd_read(npe, ECS_BG_CTXT_REG_0, CMD_RD_ECS_REG);
+	val &= ~ECS_REG_0_NEXTPC_MASK;
+	val |= (0 /* NextPC */ << 16) & ECS_REG_0_NEXTPC_MASK;
+	npe_cmd_write(npe, ECS_BG_CTXT_REG_0, CMD_WR_ECS_REG, val);
+
+	for (i = 0; i < 16; i++) {
+		if (i) { /* Context 0 has no STEVT nor STARTPC */
+			/* STEVT = off, 0x80 */
+			if (npe_logical_reg_write8(npe, NPE_STEVT, 0x80, i))
+				return -ETIMEDOUT;
+			if (npe_logical_reg_write16(npe, NPE_STARTPC, 0, i))
+				return -ETIMEDOUT;
+		}
+		/* REGMAP = d0->p0, d8->p2, d16->p4 */
+		if (npe_logical_reg_write16(npe, NPE_REGMAP, 0x820, i))
+			return -ETIMEDOUT;
+		if (npe_logical_reg_write8(npe, NPE_CINDEX, 0, i))
+			return -ETIMEDOUT;
+	}
+
+	/* post exec */
+	/* clear active bit in debug level */
+	npe_cmd_write(npe, ECS_DBG_CTXT_REG_0, CMD_WR_ECS_REG, 0);
+	/* clear the pipeline */
+	__raw_writel(CMD_NPE_CLR_PIPE, &npe->regs->exec_status_cmd);
+	/* restore previous values */
+	__raw_writel(exec_count, &npe->regs->exec_count);
+	npe_cmd_write(npe, ECS_DBG_CTXT_REG_2, CMD_WR_ECS_REG, ctx_reg2);
+
+	/* write reset values to Execution Context Stack registers */
+	for (val = 0; val < ARRAY_SIZE(ecs_reset); val++)
+		npe_cmd_write(npe, ecs_reset[val].reg, CMD_WR_ECS_REG,
+			      ecs_reset[val].val);
+
+	/* clear the profile counter */
+	__raw_writel(CMD_CLR_PROFILE_CNT, &npe->regs->exec_status_cmd);
+
+	__raw_writel(0, &npe->regs->exec_count);
+	__raw_writel(0, &npe->regs->action_points[0]);
+	__raw_writel(0, &npe->regs->action_points[1]);
+	__raw_writel(0, &npe->regs->action_points[2]);
+	__raw_writel(0, &npe->regs->action_points[3]);
+	__raw_writel(0, &npe->regs->watch_count);
+
+	val = ixp4xx_read_feature_bits();
+	/* reset the NPE */
+	ixp4xx_write_feature_bits(val &
+				  ~(IXP4XX_FEATURE_RESET_NPEA << npe->id));
+	/* deassert reset */
+	ixp4xx_write_feature_bits(val |
+				  (IXP4XX_FEATURE_RESET_NPEA << npe->id));
+	for (i = 0; i < MAX_RETRIES; i++) {
+		if (ixp4xx_read_feature_bits() &
+		    (IXP4XX_FEATURE_RESET_NPEA << npe->id))
+			break; /* NPE is back alive */
+		udelay(1);
+	}
+	if (i == MAX_RETRIES)
+		return -ETIMEDOUT;
+
+	npe_stop(npe);
+
+	/* restore NPE configuration bus Control Register - parity settings */
+	__raw_writel(ctl, &npe->regs->messaging_control);
+	return 0;
+}
+
+
+int npe_send_message(struct npe *npe, const void *msg, const char *what)
+{
+	const u32 *send = msg;
+	int cycles = 0;
+
+	debug_msg(npe, "Trying to send message %s [%08X:%08X]\n",
+		  what, send[0], send[1]);
+
+	if (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE) {
+		debug_msg(npe, "NPE input FIFO not empty\n");
+		return -EIO;
+	}
+
+	__raw_writel(send[0], &npe->regs->in_out_fifo);
+
+	if (!(__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNF)) {
+		debug_msg(npe, "NPE input FIFO full\n");
+		return -EIO;
+	}
+
+	__raw_writel(send[1], &npe->regs->in_out_fifo);
+
+	while ((cycles < MAX_RETRIES) &&
+	       (__raw_readl(&npe->regs->messaging_status) & MSGSTAT_IFNE)) {
+		udelay(1);
+		cycles++;
+	}
+
+	if (cycles == MAX_RETRIES) {
+		debug_msg(npe, "Timeout sending message\n");
+		return -ETIMEDOUT;
+	}
+
+#if DEBUG_MSG > 1
+	debug_msg(npe, "Sending a message took %i cycles\n", cycles);
+#endif
+	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;
+	}
+
+#if DEBUG_MSG > 1
+	debug_msg(npe, "Receiving a message took %i cycles\n", cycles);
+#endif
+	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)
+{
+	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;
+	char name[5 /* "/dev/" */ + NPE_NAME_LENGTH + 1 /* NUL */];
+	size_t image_size;
+
+	sprintf(name, "/dev/%s", npe->name);
+	if (!(image = read_file(name, &image_size))) {
+		print_npe(npe, "bad or missing microcode file %s", name);
+		return -EIO;
+	}
+
+	err = -EINVAL;
+	if (image_size < sizeof(struct dl_image)) {
+		print_npe(npe, "incomplete microcode file %s\n", name);
+		goto err;
+	}
+
+#if DEBUG_FW
+	print_npe(npe, "microcode: %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(npe, "bad microcode file %s magic: 0x%X\n", name, image->magic);
+		goto err;
+	}
+	if ((image->size * 4 + sizeof(struct dl_image)) > image_size) {
+		print_npe(npe, "incomplete microcode file %s\n", name);
+		goto err;
+	}
+	if (((image->id >> 24) & 0xF /* NPE ID */) != npe->id) {
+		print_npe(npe, "NPE ID mismatch in microcode file %s\n", name);
+		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_ixp42x() && ((image->id >> 28) & 0xF /* device ID */)) {
+		print_npe(npe, "IXP43x/IXP46x microcode ignored on IXP42x\n");
+		goto err;
+	}
+
+	if (npe_running(npe)) {
+		print_npe(npe, "unable to load microcode file %s, NPE is already running\n", name);
+		err = -EBUSY;
+		goto err;
+	}
+
+	if (cpu_is_ixp42x()) {
+		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(npe, "microcode EOF block marker not found\n");
+		goto err;
+	}
+
+#if DEBUG_FW
+	print_npe(npe, "%i microcode 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(npe, "invalid offset 0x%X of "
+				  "microcode 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(npe, "invalid microcode block #%i type 0x%X\n",
+				  i, blk->type);
+			goto err;
+		}
+		if (blk->offset + sizeof(*cb) / 4 + cb->size > image->size) {
+			print_npe(npe, "microcode block #%i doesn't "
+				  "fit in microcode 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(npe, "unable to start\n");
+	free(image);
+	return 0;
+
+too_big:
+	print_npe(npe, "microcode 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:
+	free(image);
+	return err;
+}
+
+
+struct npe *npe_request(int id)
+{
+	if (id < NPE_COUNT)
+		if (npe_tab[id].valid)
+			return &npe_tab[id];
+	return NULL;
+}
+
+static int __init npe_init(void)
+{
+	int i;
+
+	for (i = 0; i < NPE_COUNT; i++) {
+		struct npe *npe = &npe_tab[i];
+		if (!(ixp4xx_read_feature_bits() & (IXP4XX_FEATURE_RESET_NPEA << i)))
+			continue; /* NPE already disabled or not present */
+		if (npe_reset(npe))
+			continue;
+		npe->valid = 1;
+	}
+	return 0;
+}
+
+coredevice_initcall(npe_init);
diff --git a/arch/arm/mach-ixp4xx/qmgr.c b/arch/arm/mach-ixp4xx/qmgr.c
new file mode 100644
index 0000000..49cba60
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/qmgr.c
@@ -0,0 +1,269 @@
+/*
+ * Intel IXP4xx Queue Manager driver for Linux
+ *
+ * Copyright (C) 2007 Krzysztof Halasa <khc at pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <init.h>
+#include <errno.h>
+#include <mach/qmgr.h>
+
+static u32 used_sram_bitmap[4]; /* 128 16-dword pages */
+
+#if DEBUG_QMGR
+char qmgr_queue_descs[HALF_QUEUES][32];
+#endif
+
+#ifdef CONFIG_USE_IRQ
+
+static void (*irq_handlers[HALF_QUEUES])(void *pdev);
+static void *irq_pdevs[HALF_QUEUES];
+
+#undef fls
+static inline int fls(int x)
+{
+	int ret;
+	asm("clz\t%0, %1" : "=r" (ret) : "r" (x) : "cc");
+	ret = 32 - ret;
+	return ret;
+}
+
+
+void qmgr_set_irq(unsigned int queue, int src,
+		  void (*handler)(void *pdev), void *pdev)
+{
+	const u32 *reg;
+	int bit;
+	BUG_ON(src > QUEUE_IRQ_SRC_NOT_FULL);
+	reg = &qmgr_regs->irqsrc[queue >> 3]; /* 8 queues per u32 */
+	bit = (queue % 8) * 4; /* 3 bits + 1 reserved bit per queue */
+	__raw_writel((__raw_readl(reg) & ~(7 << bit)) | (src << bit), reg);
+
+	irq_handlers[queue] = handler;
+	irq_pdevs[queue] = pdev;
+}
+
+
+static void qmgr_irq1_a0(void *data)
+{
+	int i;
+	u32 en_bitmap, src, stat;
+
+	/* ACK - it may clear any bits so don't rely on it */
+	__raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[0]);
+
+	en_bitmap = qmgr_regs->irqen[0];
+	while (en_bitmap) {
+		i = fls(en_bitmap) - 1; /* number of the last "low" queue */
+		en_bitmap &= ~BIT(i);
+		src = qmgr_regs->irqsrc[i >> 3];
+		stat = qmgr_regs->stat1[i >> 3];
+		if (src & 4) /* the IRQ condition is inverted */
+			stat = ~stat;
+		if (stat & BIT(src & 3))
+			irq_handlers[i](irq_pdevs[i]);
+	}
+}
+
+
+static void qmgr_irq1(void *data)
+{
+	int i;
+	u32 req_bitmap = __raw_readl(&qmgr_regs->irqstat[0]);
+
+	if (!req_bitmap)
+		return;
+	__raw_writel(req_bitmap, &qmgr_regs->irqstat[0]); /* ACK */
+
+	while (req_bitmap) {
+		i = fls(req_bitmap) - 1; /* number of the last queue */
+		req_bitmap &= ~BIT(i);
+		irq_handlers[i](irq_pdevs[i]);
+	}
+}
+
+
+void qmgr_enable_irq(unsigned int queue)
+{
+	u32 mask = 1 << queue;
+
+	__raw_writel(__raw_readl(&qmgr_regs->irqen[0]) | mask,
+		     &qmgr_regs->irqen[0]);
+}
+
+void qmgr_disable_irq(unsigned int queue)
+{
+	u32 mask = 1 << queue;
+
+	__raw_writel(__raw_readl(&qmgr_regs->irqen[0]) & ~mask,
+		     &qmgr_regs->irqen[0]);
+	__raw_writel(mask, &qmgr_regs->irqstat[0]); /* clear */
+}
+
+#endif /* CONFIG_USE_IRQ */
+
+static inline void shift_mask(u32 *mask)
+{
+	mask[3] = mask[3] << 1 | mask[2] >> 31;
+	mask[2] = mask[2] << 1 | mask[1] >> 31;
+	mask[1] = mask[1] << 1 | mask[0] >> 31;
+	mask[0] <<= 1;
+}
+
+#if DEBUG_QMGR
+void qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+			unsigned int nearly_empty_watermark,
+			unsigned int nearly_full_watermark,
+			const char *desc_format, const char* name)
+#else
+void __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+			  unsigned int nearly_empty_watermark,
+			  unsigned int nearly_full_watermark)
+#endif
+{
+	u32 cfg, addr = 0, mask[4]; /* in 16-dwords */
+
+	BUG_ON(queue >= HALF_QUEUES);
+	BUG_ON((nearly_empty_watermark | nearly_full_watermark) & ~7);
+
+	switch (len) {
+	case  16:
+		cfg = 0 << 24;
+		mask[0] = 0x1;
+		break;
+	case  32:
+		cfg = 1 << 24;
+		mask[0] = 0x3;
+		break;
+	case  64:
+		cfg = 2 << 24;
+		mask[0] = 0xF;
+		break;
+	case 128:
+		cfg = 3 << 24;
+		mask[0] = 0xFF;
+		break;
+	default:
+		BUG();
+	}
+
+	cfg |= nearly_empty_watermark << 26;
+	cfg |= nearly_full_watermark << 29;
+	len /= 16; /* in 16-dwords: 1, 2, 4 or 8 */
+	mask[1] = mask[2] = mask[3] = 0;
+
+	BUG_ON(__raw_readl(&qmgr_regs->sram[queue]));
+
+	while (1) {
+		if (!(used_sram_bitmap[0] & mask[0]) &&
+		    !(used_sram_bitmap[1] & mask[1]) &&
+		    !(used_sram_bitmap[2] & mask[2]) &&
+		    !(used_sram_bitmap[3] & mask[3]))
+			break; /* found free space */
+
+		addr++;
+		shift_mask(mask);
+		if (addr + len > ARRAY_SIZE(qmgr_regs->sram)) {
+			fprintf(stderr, "qmgr: no free SRAM space for"
+				" queue %i\n", queue);
+			BUG();
+		}
+	}
+
+	used_sram_bitmap[0] |= mask[0];
+	used_sram_bitmap[1] |= mask[1];
+	used_sram_bitmap[2] |= mask[2];
+	used_sram_bitmap[3] |= mask[3];
+	__raw_writel(cfg | (addr << 14), &qmgr_regs->sram[queue]);
+#if DEBUG_QMGR
+	/* no snprintf() */
+	sprintf(qmgr_queue_descs[queue], desc_format, name);
+	fprintf(stderr, "qmgr: requested queue %s(%i) addr = 0x%02X\n",
+		qmgr_queue_descs[queue], queue, addr);
+#endif
+}
+
+void qmgr_release_queue(unsigned int queue)
+{
+	u32 cfg, addr, mask[4];
+
+	BUG_ON(queue >= HALF_QUEUES); /* not in valid range */
+
+	cfg = __raw_readl(&qmgr_regs->sram[queue]);
+	addr = (cfg >> 14) & 0xFF;
+
+	BUG_ON(!addr); /* not requested */
+
+	switch ((cfg >> 24) & 3) {
+	case 0: mask[0] = 0x1; break;
+	case 1: mask[0] = 0x3; break;
+	case 2: mask[0] = 0xF; break;
+	case 3: mask[0] = 0xFF; break;
+	}
+
+	mask[1] = mask[2] = mask[3] = 0;
+
+	while (addr--)
+		shift_mask(mask);
+
+#if DEBUG_QMGR
+	fprintf(stderr, "qmgr: releasing queue %s(%i)\n",
+		qmgr_queue_descs[queue], queue);
+	qmgr_queue_descs[queue][0] = '\x0';
+#endif
+	__raw_writel(0, &qmgr_regs->sram[queue]);
+
+	used_sram_bitmap[0] &= ~mask[0];
+	used_sram_bitmap[1] &= ~mask[1];
+	used_sram_bitmap[2] &= ~mask[2];
+	used_sram_bitmap[3] &= ~mask[3];
+#ifdef CONFIG_USE_IRQ
+	irq_handlers[queue] = NULL; /* catch IRQ bugs */
+#endif
+
+	while ((addr = qmgr_get_entry(queue)))
+		fprintf(stderr, "qmgr: released queue %i not empty: 0x%08X\n",
+			queue, addr);
+}
+
+static int __init qmgr_init(void)
+{
+	int i;
+#ifdef CONFIG_USE_IRQ
+	interrupt_handler_t *handler;
+#endif
+
+	/* reset qmgr registers */
+	for (i = 0; i < 4; i++) {
+		__raw_writel(0x33333333, &qmgr_regs->stat1[i]);
+		__raw_writel(0, &qmgr_regs->irqsrc[i]);
+	}
+	for (i = 0; i < 2; i++) {
+		__raw_writel(0, &qmgr_regs->stat2[i]);
+		__raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[i]); /* clear */
+		__raw_writel(0, &qmgr_regs->irqen[i]);
+	}
+
+	__raw_writel(0xFFFFFFFF, &qmgr_regs->statne_h);
+	__raw_writel(0, &qmgr_regs->statf_h);
+
+	for (i = 0; i < QUEUES; i++)
+		__raw_writel(0, &qmgr_regs->sram[i]);
+
+#ifdef CONFIG_USE_IRQ
+	if (cpu_is_ixp42x_rev_a0())
+		handler = qmgr_irq1_a0;
+	else
+		handler = qmgr_irq1;
+
+	irq_install_handler(IXP425_QM1_IRQ, handler, NULL);
+#endif
+	used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */
+	return 0;
+}
+
+coredevice_initcall(qmgr_init);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 69f3352..df6e42c 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -77,6 +77,14 @@ config DRIVER_NET_MACB
 	depends on HAS_MACB
 	select MIIDEV
 
+config DRIVER_NET_IXP4XX_ETH
+	tristate "Intel IXP4xx Ethernet support"
+	depends on ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR
+	select MIIDEV
+	help
+	  Say Y here if you want to use built-in Ethernet ports
+	  on IXP4xx processor.
+
 config DRIVER_NET_TAP
 	bool "tap Ethernet driver"
 	depends on LINUX
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 96d3d32..97cb44b 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_DRIVER_NET_MACB)		+= macb.o
 obj-$(CONFIG_DRIVER_NET_TAP)		+= tap.o
 obj-$(CONFIG_MIIDEV)			+= miidev.o
 obj-$(CONFIG_NET_USB)			+= usb/
+obj-$(CONFIG_DRIVER_NET_IXP4XX_ETH)	+= ixp4xx_eth.o
diff --git a/drivers/net/ixp4xx_eth.c b/drivers/net/ixp4xx_eth.c
new file mode 100644
index 0000000..71e6d92
--- /dev/null
+++ b/drivers/net/ixp4xx_eth.c
@@ -0,0 +1,763 @@
+/*
+ * Intel IXP4xx Ethernet driver for Linux
+ *
+ * Copyright (C) 2007 Krzysztof Halasa <khc at pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * 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 port	2		0		1
+ * RX queue (variable)	20		21		22
+ * TX queue		23		24		25
+ * RX-free queue	26		27		28
+ * TX-done queue is always 31, per-port RX queue is configurable
+ *
+ *
+ * Queue entries:
+ * bits 0 -> 1  - NPE ID (RX and TX-done)
+ * bits 0 -> 2  - priority (TX, per 802.1D)
+ * bits 3 -> 4  - port ID (user-set?)
+ * bits 5 -> 31 - physical descriptor address
+ */
+
+#include <common.h>
+#include <init.h>
+#include <malloc.h>
+#include <miidev.h>
+#include <net.h>
+#include <errno.h>
+#include <mach/ixp4xx-regs.h>
+#include <mach/platform.h>
+#include <mach/cpu.h>
+#include <mach/npe.h>
+#include <mach/qmgr.h>
+
+#define DEBUG_DESC		0
+#define DEBUG_RX		0
+#define DEBUG_TX		0
+#define DEBUG_PKT_BYTES		0
+#define DEBUG_MDIO		0
+#define DEBUG_OPEN		0
+#define DEBUG_CLOSE		0
+
+#define RX_DESCS		16 /* also length of all RX queues */
+#define TX_DESCS		16 /* also length of all TX queues */
+#define TXDONE_QUEUE_LEN	16 /* dwords */
+
+#define MAX_MRU			1536 /* 0x600 */
+#define RX_BUFF_SIZE		MAX_MRU
+
+#define MAX_MDIO_RETRIES	100 /* microseconds, typically 30 cycles */
+#define MAX_CLOSE_WAIT		1000 /* microseconds, typically 2-3 cycles */
+#define ETH_ALEN		6
+
+#define PHYSICAL_ID(port)	(((port)->npe->id + 2) % 3)
+#define LOGICAL_ID(port)	((port)->npe->id << 4)
+#define RX_QUEUE(port)		((port)->npe->id + 20) /* can be changed */
+#define TX_QUEUE(port)		((port)->npe->id + 23)
+#define RXFREE_QUEUE(port)	((port)->npe->id + 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 */
+};
+
+/* NPE message structure */
+struct msg {
+	u8 cmd, eth_id, params[6];
+};
+
+/* Ethernet packet descriptor, 32 bytes */
+struct desc {
+	u8 *next;    /* pointer to next buffer, unused */
+
+	u16 buf_len; /* buffer length */
+	u16 pkt_len; /* packet length */
+	u8 *data;    /* pointer to data buffer in RAM */
+	u8 dest_id;
+	u8 src_id;
+	u16 flags;
+	u8 qos;
+	u8 padlen;
+	u16 vlan_tci;
+
+	u8 dst_mac[ETH_ALEN], src_mac[ETH_ALEN];
+};
+
+struct port {
+	struct desc rx_desc_tab[RX_DESCS]; /* alignment: 0x10 */
+	struct desc tx_desc_tab[TX_DESCS];
+	u8 *buff_tab;
+	struct eth_regs *regs;
+	struct npe *npe;
+	u8 firmware[4];
+	struct eth_plat_info *pinfo;
+	struct mii_device miidev;
+	struct eth_device eth;
+};
+
+#define rx_buff(port, n) ((port)->buff_tab + MAX_MRU * (n))
+#define tx_buff(port, n) ((port)->buff_tab + MAX_MRU * (RX_DESCS + (n)))
+
+static struct eth_regs *mdio_regs; /* mdio command and status only */
+
+static int ixp4xx_mdio_cmd(int write, const struct device_d *dev, unsigned char phy_id,
+			   unsigned char location, unsigned short value)
+{
+	int cycles = 0;
+
+	if (__raw_readl(&mdio_regs->mdio_command[3]) & 0x80) {
+		fprintf(stderr, "%s%d: MII not ready to transmit\n", dev->name, dev->id);
+		return -1;
+	}
+
+	if (write) {
+		__raw_writel(value & 0xFF, &mdio_regs->mdio_command[0]);
+		__raw_writel(value >> 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) {
+		fprintf(stderr, "%s%d: MII write failed\n", dev->name, dev->id);
+		return -1;
+	}
+
+#if DEBUG_MDIO
+	fprintf(stderr, "%s%d: mdio_%s() took %i cycles\n", dev->name, dev->id,
+		write ? "write" : "read", cycles);
+#endif
+
+	if (write)
+		return 0;
+
+	if (__raw_readl(&mdio_regs->mdio_status[3]) & 0x80) {
+#if DEBUG_MDIO
+		fprintf(stderr, "%s%d: MII read failed\n", dev->name, dev->id);
+#endif
+		return -1;
+	}
+
+	value = (__raw_readl(&mdio_regs->mdio_status[0]) & 0xFF) |
+		((__raw_readl(&mdio_regs->mdio_status[1]) & 0xFF) << 8);
+#if DEBUG_MDIO
+	fprintf(stderr, "%s%d: MII read [%i] -> 0x%X\n", dev->name, dev->id, location, value);
+#endif
+
+	return value;
+}
+
+static int ixp4xx_mdio_read(struct mii_device *mii, int phy_id, int location)
+{
+	int ret = ixp4xx_mdio_cmd(0, &mii->dev, phy_id, location, 0);
+	return ret;
+}
+
+static int ixp4xx_mdio_write(struct mii_device *mii, int phy_id, int location, int value)
+{
+	int ret = ixp4xx_mdio_cmd(1, &mii->dev, phy_id, location, value);
+#if DEBUG_MDIO
+	fprintf(stderr, "%s%d: MII write [%i] <- 0x%X, err = %i\n",
+		mii->dev.name, mii->dev.id, location, value, ret);
+#endif
+	return ret;
+}
+
+static int ixp4xx_adjust_link(struct eth_device *dev)
+{
+	struct port *port = dev->priv;
+	int reg, speed, duplex;
+
+	miidev_wait_aneg(&port->miidev);
+
+	reg = mii_read(&port->miidev, port->miidev.address, MII_BMSR);
+	if (reg < 0)
+		goto err_out;
+	if (!(reg & BMSR_LSTATUS)) {
+		printf("%s%d: Link is down", dev->dev.name, dev->dev.id);
+		return -1;
+	}
+
+	reg = mii_read(&port->miidev, port->miidev.address, MII_BMCR);
+	if (reg < 0)
+		goto err_out;
+
+	if (reg & BMCR_ANENABLE) {
+		reg = mii_read(&port->miidev, port->miidev.address, MII_LPA);
+		if (reg < 0)
+			goto err_out;
+		duplex = reg & LPA_DUPLEX;
+		speed = reg & LPA_100 ? 100 : 10;
+	} else {
+		duplex = reg & BMCR_FULLDPLX;
+		speed = reg & BMCR_SPEED100 ? 100 : 10;
+	}
+
+	if (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]);
+
+	fprintf(stderr, "%s%d: link up, speed %u Mb/s, %s duplex\n",
+		dev->dev.name, dev->dev.id, speed, duplex ? "full" : "half");
+	return 0;
+
+err_out:
+	printf("%s%d: failed to read MII data\n", dev->dev.name, dev->dev.id);
+	return -EIO;
+}
+
+static inline void debug_pkt(struct eth_device *dev, const char *func,
+			     u8 *data, int len)
+{
+#if DEBUG_PKT_BYTES
+	int i;
+
+	fprintf(stderr, "%s%d: %s(%4i) ", dev->dev.name, dev->dev.id, func, len);
+	for (i = 0; i < len; i++) {
+		if (i >= DEBUG_PKT_BYTES)
+			break;
+		fprintf(stderr, "%s%02X",
+			((i == 6) || (i == 12) || (i >= 14)) ? " " : "",
+			data[i]);
+	}
+	fprintf(stderr, "\n");
+#endif
+}
+
+
+static inline void debug_desc(struct desc *desc)
+{
+#if DEBUG_DESC
+	fprintf(stderr, "%07X: %X %3X %3X %07X %2X < %2X %4X %X"
+		" %X %X %02X%02X%02X%02X%02X%02X < %02X%02X%02X%02X%02X%02X\n",
+		(u32)desc, (u32)desc->next, desc->buf_len, desc->pkt_len,
+		(u32)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 addr, n;
+	struct desc *tab;
+
+	if (!(addr = qmgr_get_entry(queue)))
+		return -1;
+
+	addr &= ~0x1F; /* mask out non-address bits */
+	tab = is_tx ? port->tx_desc_tab : port->rx_desc_tab;
+	n = (addr - (u32)tab) / sizeof(struct desc);
+	BUG_ON(n >= (is_tx ? TX_DESCS : RX_DESCS));
+	debug_desc((struct desc*)addr);
+	BUG_ON(tab[n].next);
+	return n;
+}
+
+static inline void queue_put_desc(unsigned int queue, struct desc *desc)
+{
+	debug_desc(desc);
+	BUG_ON(((u32)desc) & 0x1F);
+	qmgr_put_entry(queue, (u32)desc);
+	/* Don't check for queue overflow here, we've allocated sufficient
+	   length and queues >= 32 don't support this check anyway. */
+}
+
+
+static int ixp4xx_eth_poll(struct eth_device *dev)
+{
+	struct port *port = dev->priv;
+	struct desc *desc;
+	u8 *buff;
+	int n, len;
+
+#if DEBUG_RX
+	fprintf(stderr, "%s%d: eth_poll\n", dev->dev.name, dev->dev.id);
+#endif
+
+	if ((n = queue_get_desc(RX_QUEUE(port), port, 0)) < 0) {
+#if DEBUG_RX
+		fprintf(stderr, "%s%d: eth_poll = no packet received\n", dev->dev.name, dev->dev.id);
+#endif
+		return 0;
+	}
+
+	barrier();
+	desc = &port->rx_desc_tab[n];
+	buff = rx_buff(port, n);
+	len = desc->pkt_len;
+	/* process received frame */
+	memcpy((void *)NetRxPackets[0], buff, len);
+	debug_pkt(dev, "RX", desc->data, len);
+
+	/* put the new buffer on RX-free queue */
+	desc->buf_len = MAX_MRU;
+	desc->pkt_len = 0;
+	queue_put_desc(RXFREE_QUEUE(port), desc);
+
+	net_receive(NetRxPackets[0], len);
+
+#if DEBUG_RX
+	fprintf(stderr, "%s%d: eth_poll end\n", dev->dev.name, dev->dev.id);
+#endif
+	return 0;
+}
+
+
+static int ixp4xx_eth_xmit(struct eth_device *dev, void *data, int len)
+{
+	struct port *port = dev->priv;
+	int n;
+	struct desc *desc;
+
+#if DEBUG_TX
+	fprintf(stderr, "%s%d: eth_xmit\n", dev->dev.name, dev->dev.id);
+#endif
+
+	if (unlikely(len > 1500))
+		return -1;
+
+	debug_pkt(dev, "TX", data, len);
+
+	if ((n = queue_get_desc(TXDONE_QUEUE, port, 1)) < 0)
+		return -1; /* no free buffers */
+	desc = &port->tx_desc_tab[n];
+	desc->data = tx_buff(port, n);
+	desc->buf_len = desc->pkt_len = len;
+	memcpy(desc->data, data, len);
+
+	/* NPE firmware pads short frames with zeros internally */
+	// wmb();
+	barrier();
+	queue_put_desc(TX_QUEUE(port), desc);
+
+#if DEBUG_TX
+	fprintf(stderr, "%s%d: eth_xmit end\n", dev->dev.name, dev->dev.id);
+#endif
+	return 0;
+}
+
+static void request_queues(struct port *port, struct eth_device *dev)
+{
+	qmgr_request_queue(RXFREE_QUEUE(port), RX_DESCS, 0, 0, "%s:RX-free", dev->dev.name);
+	qmgr_request_queue(RX_QUEUE(port), RX_DESCS, 0, 0, "%s:RX", dev->dev.name);
+	qmgr_request_queue(TX_QUEUE(port), TX_DESCS, 0, 0, "%s:TX", dev->dev.name);
+
+	/* Common TX-done queue handles buffers sent out by the NPEs */
+	qmgr_request_queue(TXDONE_QUEUE, TXDONE_QUEUE_LEN, 0, 0,
+			   "%s:TX-done", dev->dev.name);
+}
+
+static void release_queues(struct port *port)
+{
+	qmgr_release_queue(RXFREE_QUEUE(port));
+	qmgr_release_queue(RX_QUEUE(port));
+	qmgr_release_queue(TX_QUEUE(port));
+	qmgr_release_queue(TXDONE_QUEUE);
+}
+
+static void init_queues(struct port *port)
+{
+	int i;
+
+	memset(port->tx_desc_tab, 0, sizeof(port->tx_desc_tab)); /* descs */
+	memset(port->rx_desc_tab, 0, sizeof(port->rx_desc_tab));
+
+	/* Setup RX buffers */
+	for (i = 0; i < RX_DESCS; i++) {
+		struct desc *desc = &port->rx_desc_tab[i];
+		desc->buf_len = MAX_MRU;
+		desc->data = rx_buff(port, i);
+	}
+}
+
+static int ixp4xx_eth_open(struct eth_device *dev)
+{
+	struct port *port = dev->priv;
+	struct npe *npe = port->npe;
+	struct msg msg;
+	int i, err;
+
+#if DEBUG_OPEN
+	fprintf(stderr, "%s%d: opening %p\n", dev->dev.name, dev->dev.id, dev);
+#endif
+
+	if (!npe_running(npe)) {
+		err = npe_load_firmware(npe);
+		if (err)
+			return err;
+
+		if (npe_recv_message(npe, &msg, "ETH_GET_STATUS")) {
+			fprintf(stderr, "%s%d: %s not responding\n", dev->dev.name, dev->dev.id, npe->name);
+			return -EIO;
+		}
+		memcpy(port->firmware, msg.params + 2, 4);
+	}
+
+	if (ixp4xx_adjust_link(dev))
+		return -ENOLINK;
+
+	port->buff_tab = xmalloc((RX_DESCS + TX_DESCS) * MAX_MRU);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = NPE_VLAN_SETRXQOSENTRY;
+	msg.eth_id = LOGICAL_ID(port);
+	msg.params[3] = RX_QUEUE(port) | 0x80;
+	msg.params[4] = RX_QUEUE(port) >> 4; /* MSB of offset */
+	msg.params[5] = RX_QUEUE(port) << 4; /* LSB of offset */
+	for (i = 0; i < 8; i++) {
+		msg.params[1] = i;
+		if (npe_send_recv_message(port->npe, &msg, "ETH_SET_RXQ")) {
+			err = -EIO;
+			goto out;
+		}
+	}
+
+	msg.cmd = NPE_EDB_SETPORTADDRESS;
+	msg.eth_id = PHYSICAL_ID(port);
+	memcpy(msg.params, port->pinfo->hwaddr, ETH_ALEN);
+	if (npe_send_recv_message(port->npe, &msg, "ETH_SET_MAC")) {
+		err = -EIO;
+		goto out;
+	}
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = NPE_FW_SETFIREWALLMODE;
+	msg.eth_id = LOGICAL_ID(port);
+	if (npe_send_recv_message(port->npe, &msg, "ETH_SET_FIREWALL_MODE")) {
+		err = -EIO;
+		goto out;
+	}
+
+	request_queues(port, dev);
+	init_queues(port);
+
+	for (i = 0; i < ETH_ALEN; i++)
+		__raw_writel(port->pinfo->hwaddr[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 */
+	for (i = 0; i < TX_DESCS; i++)
+		queue_put_desc(TXDONE_QUEUE, &port->tx_desc_tab[i]);
+
+	for (i = 0; i < RX_DESCS; i++)
+		queue_put_desc(RXFREE_QUEUE(port), &port->rx_desc_tab[i]);
+
+	__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 0
+	qmgr_set_irq(RX_QUEUE(port), QUEUE_IRQ_SRC_NOT_EMPTY,
+		     eth_rx_irq, dev);
+	qmgr_set_irq(TXDONE_QUEUE, QUEUE_IRQ_SRC_NOT_EMPTY,
+		     eth_txdone_irq, NULL);
+	qmgr_enable_irq(TXDONE_QUEUE);
+#endif
+	memset(&msg, 0, sizeof(msg));
+#if DEBUG_OPEN
+	fprintf(stderr, "%s%d opened\n", dev->dev.name, dev->dev.id);
+#endif
+	return 0;
+out:
+	free(port->buff_tab);
+	port->buff_tab = NULL;
+#if DEBUG_OPEN
+	fprintf(stderr, "%s%d open failed (%i)\n", dev->dev.name, dev->dev.id, err);
+#endif
+	return err;
+}
+
+static void ixp4xx_eth_close(struct eth_device *dev)
+{
+	struct port *port = dev->priv;
+	struct msg msg;
+	int buffs = RX_DESCS; /* allocated RX buffers */
+	int i;
+
+#if DEBUG_CLOSE
+	fprintf(stderr, "%s%d: closing\n", dev->dev.name, dev->dev.id);
+#endif
+#if 0
+	qmgr_disable_irq(RX_QUEUE(port));
+#endif
+
+	if (!port->buff_tab)
+		return; /* already closed */
+
+	while (queue_get_desc(RXFREE_QUEUE(port), port, 0) >= 0)
+		buffs--;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = NPE_SETLOOPBACK_MODE;
+	msg.eth_id = LOGICAL_ID(port);
+	msg.params[1] = 1;
+	if (npe_send_recv_message(port->npe, &msg, "ETH_ENABLE_LOOPBACK"))
+		fprintf(stderr, "%s%d: unable to enable loopback\n", dev->dev.name, dev->dev.id);
+
+#if DEBUG_CLOSE
+	fprintf(stderr, "%s%d: draining RX queue\n", dev->dev.name, dev->dev.id);
+#endif
+	i = 0;
+	do { /* drain RX buffers */
+		while (queue_get_desc(RX_QUEUE(port), port, 0) >= 0)
+			buffs--;
+		if (!buffs)
+			break;
+		if (qmgr_stat_full(TXDONE_QUEUE) && !(i % 10)) {
+			/* we have to inject some packet */
+			struct desc *desc;
+			int n = queue_get_desc(TXDONE_QUEUE, port, 1);
+			BUG_ON(n < 0);
+			desc = &port->tx_desc_tab[n];
+			desc->buf_len = desc->pkt_len = 1;
+			//wmb();
+			barrier();
+			queue_put_desc(TX_QUEUE(port), desc);
+		}
+		udelay(1);
+	} while (++i < MAX_CLOSE_WAIT);
+
+	if (buffs)
+		fprintf(stderr, "%s%d: unable to drain RX queue, %i buffer(s) left in NPE\n",
+			dev->dev.name, dev->dev.id, buffs);
+#if DEBUG_CLOSE
+	if (!buffs)
+		fprintf(stderr, "%s%d: draining RX queue took %i cycles\n", dev->dev.name, dev->dev.id, i);
+#endif
+
+	buffs = TX_DESCS;
+	while (queue_get_desc(TX_QUEUE(port), port, 1) >= 0)
+		buffs--; /* cancel TX */
+
+	i = 0;
+	do {
+		while (queue_get_desc(TXDONE_QUEUE, port, 1) >= 0)
+			buffs--;
+		if (!buffs)
+			break;
+	} while (++i < MAX_CLOSE_WAIT);
+
+	if (buffs)
+		fprintf(stderr, "%s%d: unable to drain TX queue, %i buffer(s) left in NPE\n",
+			dev->dev.name, dev->dev.id, buffs);
+#if DEBUG_CLOSE
+	if (!buffs)
+		fprintf(stderr, "%s%d: draining TX queues took %i cycles\n", dev->dev.name, dev->dev.id, i);
+#endif
+
+	msg.params[1] = 0;
+	if (npe_send_recv_message(port->npe, &msg, "ETH_DISABLE_LOOPBACK"))
+		fprintf(stderr, "%s%d: unable to disable loopback\n", dev->dev.name, dev->dev.id);
+
+#if 0
+	qmgr_disable_irq(TXDONE_QUEUE);
+#endif
+	release_queues(port);
+	free(port->buff_tab);
+	port->buff_tab = NULL;
+#if DEBUG_CLOSE
+	fprintf(stderr, "%s%d: closed\n", dev->dev.name, dev->dev.id);
+#endif
+}
+
+static int ixp4xx_eth_get_hwaddr(struct eth_device *eth, unsigned char *addr)
+{
+	struct port *port = eth->priv;
+	memcpy(addr, port->pinfo->hwaddr, 6);
+	return 0;
+}
+
+static int ixp4xx_eth_set_hwaddr(struct eth_device *eth, unsigned char *addr)
+{
+	struct port *port = eth->priv;
+	memcpy(port->pinfo->hwaddr, addr, 6);
+	return 0;
+}
+
+static int ixp4xx_eth_init(struct eth_device *eth)
+{
+	struct port *port = eth->priv;
+
+	__raw_writel(DEFAULT_CORE_CNTRL | CORE_RESET,
+		     &port->regs->core_control);
+	udelay(50);
+	__raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control);
+	udelay(50);
+
+	miidev_restart_aneg(&port->miidev);
+	return 0;
+}
+
+static int ixp4xx_eth_probe(struct device_d *dev)
+{
+	struct npe *npe;
+	struct port *port;
+	struct eth_plat_info *pinfo = dev->platform_data;
+
+	if (!pinfo) {
+		fprintf(stderr, "ixp4xx_eth: no platform information\n");
+		return -ENODEV;
+	}
+
+	if (!(npe = npe_request(pinfo->npe))) {
+		fprintf(stderr, "ixp4xx_eth: unable to acquire NPE\n");
+		return -ENODEV;
+	}
+
+	port = memalign(0x20, sizeof(*port));
+	memset(port, 0, sizeof(*port));
+
+	port->regs = pinfo->regs;
+	port->npe = npe;
+	port->pinfo = pinfo;
+	port->eth.dev.id = -1;
+	port->eth.priv = port;
+	port->eth.init = ixp4xx_eth_init;
+	port->eth.open = ixp4xx_eth_open;
+	port->eth.halt = ixp4xx_eth_close;
+	port->eth.send = ixp4xx_eth_xmit;
+	port->eth.recv = ixp4xx_eth_poll;
+	port->eth.get_ethaddr = ixp4xx_eth_get_hwaddr;
+	port->eth.set_ethaddr = ixp4xx_eth_set_hwaddr;
+
+	port->miidev.dev.id = -1;
+	port->miidev.read = ixp4xx_mdio_read;
+	port->miidev.write = ixp4xx_mdio_write;
+	port->miidev.address = pinfo->phy;
+	port->miidev.edev = &port->eth;
+	mii_register(&port->miidev);
+	eth_register(&port->eth);
+	return 0;
+}
+
+static struct driver_d ixp4xx_eth_driver = {
+	.name  = "ixp4xx_eth",
+	.probe = ixp4xx_eth_probe,
+};
+
+static int __init ixp4xx_eth_module_init(void)
+{
+	if (cpu_is_ixp43x()) {
+		/* IXP43x lacks NPE-B and uses NPE-C for MII PHY access */
+		if (!(ixp4xx_read_feature_bits() & IXP4XX_FEATURE_NPEC_ETH))
+			return -ENOSYS;
+		mdio_regs = (struct eth_regs *)IXP4XX_EthC_BASE;
+	} else {
+		/* All MII PHY accesses use NPE-B Ethernet registers */
+		if (!(ixp4xx_read_feature_bits() & IXP4XX_FEATURE_NPEB_ETH0))
+			return -ENOSYS;
+		mdio_regs = (struct eth_regs *)IXP4XX_EthB_BASE;
+	}
+
+	__raw_writel(DEFAULT_CORE_CNTRL, &mdio_regs->core_control);
+
+	register_driver(&ixp4xx_eth_driver);
+	return 0;
+}
+
+device_initcall(ixp4xx_eth_module_init);
diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c
index 290619f..0f25ceb 100644
--- a/drivers/serial/serial_ns16550.c
+++ b/drivers/serial/serial_ns16550.c
@@ -102,7 +102,11 @@ static void ns16550_serial_init_port(struct console_device *cdev)
 	baud_divisor = ns16550_calc_divisor(cdev, CONFIG_BAUDRATE);
 
 	/* initializing the device for the first time */
+#ifdef CONFIG_ARCH_IXP4XX
+	plat->reg_write(IER_UUE, base, ier); /* Enable UART operation */
+#else
 	plat->reg_write(0x00, base, ier);
+#endif
 #ifdef CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS
 	plat->reg_write(0x07, base, mdr1);	/* Disable */
 #endif
@@ -181,7 +185,11 @@ static int ns16550_setbaudrate(struct console_device *cdev, int baud_rate)
 	    cdev->dev->platform_data;
 	unsigned long base = cdev->dev->map_base;
 	unsigned int baud_divisor = ns16550_calc_divisor(cdev, baud_rate);
+#ifdef CONFIG_ARCH_IXP4XX
+	plat->reg_write(IER_UUE, base, ier); /* Enable UART operation */
+#else
 	plat->reg_write(0x00, base, ier);
+#endif
 	plat->reg_write(LCR_BKSE, base, lcr);
 	plat->reg_write(baud_divisor & 0xff, base, dll);
 	plat->reg_write((baud_divisor >> 8) & 0xff, base, dlm);
diff --git a/drivers/serial/serial_ns16550.h b/drivers/serial/serial_ns16550.h
index eb2fd10..ba20a6f 100644
--- a/drivers/serial/serial_ns16550.h
+++ b/drivers/serial/serial_ns16550.h
@@ -50,6 +50,8 @@
 #define dll		rbr
 #define dlm		ier
 
+#define IER_UUE		0x40	/* UART Unit Enable (XScale) */
+
 #define FCR_FIFO_EN     0x01	/* Fifo enable */
 #define FCR_RXSR        0x02	/* Receiver soft reset */
 #define FCR_TXSR        0x04	/* Transmitter soft reset */



More information about the barebox mailing list