[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 = ð_pinfo[0],
+ }, {
+ .id = 1,
+ .name = "ixp4xx_eth",
+ .platform_data = ð_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(ð_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(ð_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