Signed-off-by: Krzysztof Hałasa <khc@xxxxxxxxx> diff --git a/Documentation/boards.dox b/Documentation/boards.dox index 41de836..2d248e6 100644 --- a/Documentation/boards.dox +++ b/Documentation/boards.dox @@ -21,6 +21,7 @@ ARM type: @li @subpage board_loco @li @subpage chumbyone @li @subpage scb9328 +@li @subpage multilink @li @subpage netx @li @subpage dev_omap_arch @li @subpage a9m2440 diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index bb9b47b..2a775d5 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -79,6 +79,12 @@ config ARCH_IMX select WATCHDOG_IMX_RESET_SOURCE select HAS_DEBUG_LL +config ARCH_IXP4XX + bool "Intel IXP4xx-based" + select CPU_XSCALE + select ARCH_SUPPORTS_BIG_ENDIAN + select CPU_BIG_ENDIAN + config ARCH_MXS bool "Freescale i.MX23/28 (mxs) based" select GENERIC_GPIO @@ -153,6 +159,7 @@ source arch/arm/mach-clps711x/Kconfig source arch/arm/mach-ep93xx/Kconfig source arch/arm/mach-highbank/Kconfig source arch/arm/mach-imx/Kconfig +source arch/arm/mach-ixp4xx/Kconfig source arch/arm/mach-mxs/Kconfig source arch/arm/mach-netx/Kconfig source arch/arm/mach-nomadik/Kconfig diff --git a/arch/arm/Makefile b/arch/arm/Makefile index d506b12..554072e 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -57,6 +57,7 @@ machine-$(CONFIG_ARCH_CLPS711X) := clps711x machine-$(CONFIG_ARCH_EP93XX) := ep93xx machine-$(CONFIG_ARCH_HIGHBANK) := highbank machine-$(CONFIG_ARCH_IMX) := imx +machine-$(CONFIG_ARCH_IXP4XX) := ixp4xx machine-$(CONFIG_ARCH_MXS) := mxs machine-$(CONFIG_ARCH_NOMADIK) := nomadik machine-$(CONFIG_ARCH_NETX) := netx @@ -101,6 +102,7 @@ board-$(CONFIG_MACH_EUKREA_CPUIMX51SD) := eukrea_cpuimx51 board-$(CONFIG_MACH_FREESCALE_MX25_3STACK) := freescale-mx25-3-stack board-$(CONFIG_MACH_FREESCALE_MX35_3STACK) := freescale-mx35-3-stack board-$(CONFIG_MACH_GE863) := telit-evk-pro3 +board-$(CONFIG_MACH_GORAMO_MLR) := multilink board-$(CONFIG_MACH_HIGHBANK) := highbank board-$(CONFIG_MACH_IMX21ADS) := imx21ads board-$(CONFIG_MACH_IMX27ADS) := imx27ads 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..a48318c --- /dev/null +++ b/arch/arm/boards/multilink/lowlevel_init.S @@ -0,0 +1,67 @@ +#include <mach/ixp4xx-regs.h> +#include <mach/ixp4xx-head.h> + + .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 */ + + .section ".text_bare_init", "ax" + + .globl barebox_arm_reset_vector +barebox_arm_reset_vector: + ixp4xx_cpu_lowlevel_init + 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 r0, #0 + ldr r1, [r8, #CFG_SDRAM_SIZE] + mov r2, #0 + b barebox_arm_entry diff --git a/arch/arm/boards/multilink/multilink.c b/arch/arm/boards/multilink/multilink.c new file mode 100644 index 0000000..43a4a08 --- /dev/null +++ b/arch/arm/boards/multilink/multilink.c @@ -0,0 +1,109 @@ +//#define DEBUG +#include <common.h> +#include <errno.h> +#include <fs.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 0x3B000 +#define CRAMFS_START (BAREBOX_START + BAREBOX_LENGTH) +#define CRAMFS_LENGTH 0x05000 +#define ENV0_START (CRAMFS_START + CRAMFS_LENGTH) +#define ENV0_LENGTH 0x20000 + +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 inline u8 __init flash_readb(u32 addr) +{ + return __raw_readb(IXP4XX_EXP_BASE(0) + addr); +} + +static int __init gml_mem_init(void) +{ + arm_add_mem_device("ram0", 0, __raw_readl(IXP4XX_EXP_BASE(0) + CFG_SDRAM_SIZE)); + return 0; +} + +mem_initcall(gml_mem_init); + +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; + add_cfi_flash_device(0, IXP4XX_EXP_BASE(0), 16 * 1024 * 1024, 0); + devfs_add_partition("nor0", BAREBOX_START, BAREBOX_LENGTH, + DEVFS_PARTITION_FIXED | DEVFS_PARTITION_READONLY, "barebox"); + devfs_add_partition("nor0", CRAMFS_START, CRAMFS_LENGTH, + DEVFS_PARTITION_FIXED | DEVFS_PARTITION_READONLY, "cramfs"); + devfs_add_partition("nor0", ENV0_START, ENV0_LENGTH, + DEVFS_PARTITION_FIXED, "env0"); + + mkdir("/firmware", 0755); + mount("/dev/cramfs", "cramfs", "/firmware"); + + 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); + add_generic_device("ixp4xx_eth", DEVICE_ID_DYNAMIC, NULL, 0, 0, + IORESOURCE_MEM, ð_pinfo[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); + add_generic_device("ixp4xx_eth", DEVICE_ID_DYNAMIC, NULL, 0, 0, + IORESOURCE_MEM, ð_pinfo[1]); + } + + armlinux_set_bootparams((void *)(0x00000100)); + armlinux_set_architecture(MACH_TYPE_GORAMO_MLR); + return 0; +} +device_initcall(gml_devices_init); diff --git a/arch/arm/boards/multilink/multilink.dox b/arch/arm/boards/multilink/multilink.dox new file mode 100644 index 0000000..321cbeb --- /dev/null +++ b/arch/arm/boards/multilink/multilink.dox @@ -0,0 +1,34 @@ +/** @page multilink Goramo MultiLink + +These boards are based on Intel IXP42x CPU. + +Variants: + +MicroRouter module: IXP421 or IXP425 266 MHz CPU, 32 MiB SDRAM, + 1 or 2 Fast Ethernet ports, 2 sync serial ports, 1 RS-232 (console) port. + +MultiLink v.1: IXP425 266 or 533 MHz CPU, 64 - 256 MiB SDRAM, + 2 Fast Ethernet ports, 2 sync serial ports, 1 - 2 RS-232 ports, + a mini-PCI slot, 2 optional USB host connectors. + +MultiLink v.2: IXP425 266 or 533 MHz CPU, 64 - 256 MiB SDRAM, + 2 Fast Ethernet ports, 2 sync serial ports, 1 - 2 RS-232 ports, + 2 additional optional Fast or Gigabit Ethernet ports, a mini-PCI slot, + 4 optional USB host connectors, optional Real-Time Clock, + optional IDE connector. + +16 MiB Intel StrataFlash memory is partitioned as follows: + +0x00000-0x3B000 : Barebox image +0x3B000-0x40000 : compressed RAM filesystem (cramfs) +0x40000-0x60000 : env0 + +There is a factory configuration region within Barebox image at locations +0x40 - 0x7F. Details are in file multilink.c + +Barebox and cramfs images are combined in first two blocks of flash memory. +NPE-A microcode contained in cramfs is not used by Barebox (but may be used +by Linux HSS driver). NPE-B microcode is needed for eth0 and NPE-C for eth1. +Barebox needs version 2.4 of regular non-VLAN-aware microcode files (with +or without crypto support). +*/ diff --git a/arch/arm/lib/barebox.lds.S b/arch/arm/lib/barebox.lds.S index 10c63bf..b40b24b 100644 --- a/arch/arm/lib/barebox.lds.S +++ b/arch/arm/lib/barebox.lds.S @@ -40,6 +40,9 @@ SECTIONS _stext = .; _text = .; *(.text_entry*) +#ifdef CONFIG_MACH_GORAMO_MLR + . = 0x0080; /* config space at 0x40 - 0x7F */ +#endif __bare_init_start = .; *(.text_bare_init*) __bare_init_end = .; diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig new file mode 100644 index 0000000..db0b768 --- /dev/null +++ b/arch/arm/mach-ixp4xx/Kconfig @@ -0,0 +1,26 @@ +if ARCH_IXP4XX + +choice + prompt "IXP4xx Board Type" + +config MACH_GORAMO_MLR + bool "Goramo MultiLink" + select HAVE_CONFIGURABLE_MEMORY_LAYOUT +endchoice + +config BOARDINFO + default "Goramo MultiLink" if MACH_GORAMO_MLR + +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..7cfc924 --- /dev/null +++ b/arch/arm/mach-ixp4xx/Makefile @@ -0,0 +1,3 @@ +obj-y += 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..e81994b --- /dev/null +++ b/arch/arm/mach-ixp4xx/generic.c @@ -0,0 +1,114 @@ +#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, +}; + +/** + * @brief UART serial port initialization + * + * @return result of device registration + */ +static int ixp4xx_console_init(void) +{ + /* Register the serial port */ + add_ns16550_device(0, (u32)IXP4XX_UART1_BASE, 1024, IORESOURCE_MEM_8BIT, &serial_plat); + return 0; +} + +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-head.h b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-head.h new file mode 100644 index 0000000..b3d357f --- /dev/null +++ b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-head.h @@ -0,0 +1,16 @@ +#include <mach/ixp4xx-regs.h> +#include <asm/barebox-arm-head.h> + +.macro ixp4xx_cpu_lowlevel_init + + arm_cpu_lowlevel_init r0 + + add pc, #(0x50000000 - 4) /* jump to ROM area */ + + mov r0, #(IXP4XX_EXP_CFG0 & 0xFFFF0000) + orr r0, #(IXP4XX_EXP_CFG0 & 0x0000FFFF) + ldr r1, [r0] + and r1, r1, #~0x80000000 /* unmap EXP bus from 0x0 */ + str r1, [r0] + +.endm 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@xxxxxxxxx> + * + * 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/npe.c b/arch/arm/mach-ixp4xx/npe.c new file mode 100644 index 0000000..2811a03 --- /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@xxxxxxxxx> + * + * 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[10 /* "/firmware/" */ + NPE_NAME_LENGTH + 1 /* NUL */]; + size_t image_size; + + sprintf(name, "/firmware/%s", npe->name); + if (!(image = read_file(name, &image_size))) { + print_npe(npe, "bad or missing microcode file %s\n", 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..81b6522 --- /dev/null +++ b/arch/arm/mach-ixp4xx/qmgr.c @@ -0,0 +1,259 @@ +/* + * Intel IXP4xx Queue Manager driver for Linux + * + * Copyright (C) 2007 Krzysztof Halasa <khc@xxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include <init.h> +#include <errno.h> +#include <mach/qmgr.h> + +static u32 used_sram_bitmap[4]; /* 128 16-dword pages */ + +#if DEBUG_QMGR +char qmgr_queue_descs[HALF_QUEUES][32]; +#endif + +#ifdef CONFIG_USE_IRQ + +static void (*irq_handlers[HALF_QUEUES])(void *pdev); +static void *irq_pdevs[HALF_QUEUES]; + +void qmgr_set_irq(unsigned int queue, int src, + void (*handler)(void *pdev), void *pdev) +{ + const u32 *reg; + int bit; + BUG_ON(src > QUEUE_IRQ_SRC_NOT_FULL); + reg = &qmgr_regs->irqsrc[queue >> 3]; /* 8 queues per u32 */ + bit = (queue % 8) * 4; /* 3 bits + 1 reserved bit per queue */ + __raw_writel((__raw_readl(reg) & ~(7 << bit)) | (src << bit), reg); + + irq_handlers[queue] = handler; + irq_pdevs[queue] = pdev; +} + + +static void qmgr_irq1_a0(void *data) +{ + int i; + u32 en_bitmap, src, stat; + + /* ACK - it may clear any bits so don't rely on it */ + __raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[0]); + + en_bitmap = qmgr_regs->irqen[0]; + while (en_bitmap) { + i = fls(en_bitmap) - 1; /* number of the last "low" queue */ + en_bitmap &= ~BIT(i); + src = qmgr_regs->irqsrc[i >> 3]; + stat = qmgr_regs->stat1[i >> 3]; + if (src & 4) /* the IRQ condition is inverted */ + stat = ~stat; + if (stat & BIT(src & 3)) + irq_handlers[i](irq_pdevs[i]); + } +} + + +static void qmgr_irq1(void *data) +{ + int i; + u32 req_bitmap = __raw_readl(&qmgr_regs->irqstat[0]); + + if (!req_bitmap) + return; + __raw_writel(req_bitmap, &qmgr_regs->irqstat[0]); /* ACK */ + + while (req_bitmap) { + i = fls(req_bitmap) - 1; /* number of the last queue */ + req_bitmap &= ~BIT(i); + irq_handlers[i](irq_pdevs[i]); + } +} + + +void qmgr_enable_irq(unsigned int queue) +{ + u32 mask = 1 << queue; + + __raw_writel(__raw_readl(&qmgr_regs->irqen[0]) | mask, + &qmgr_regs->irqen[0]); +} + +void qmgr_disable_irq(unsigned int queue) +{ + u32 mask = 1 << queue; + + __raw_writel(__raw_readl(&qmgr_regs->irqen[0]) & ~mask, + &qmgr_regs->irqen[0]); + __raw_writel(mask, &qmgr_regs->irqstat[0]); /* clear */ +} + +#endif /* CONFIG_USE_IRQ */ + +static inline void shift_mask(u32 *mask) +{ + mask[3] = mask[3] << 1 | mask[2] >> 31; + mask[2] = mask[2] << 1 | mask[1] >> 31; + mask[1] = mask[1] << 1 | mask[0] >> 31; + mask[0] <<= 1; +} + +#if DEBUG_QMGR +void qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, + unsigned int nearly_empty_watermark, + unsigned int nearly_full_watermark, + const char *desc_format, const char* name) +#else +void __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */, + unsigned int nearly_empty_watermark, + unsigned int nearly_full_watermark) +#endif +{ + u32 cfg, addr = 0, mask[4]; /* in 16-dwords */ + + BUG_ON(queue >= HALF_QUEUES); + BUG_ON((nearly_empty_watermark | nearly_full_watermark) & ~7); + + switch (len) { + case 16: + cfg = 0 << 24; + mask[0] = 0x1; + break; + case 32: + cfg = 1 << 24; + mask[0] = 0x3; + break; + case 64: + cfg = 2 << 24; + mask[0] = 0xF; + break; + case 128: + cfg = 3 << 24; + mask[0] = 0xFF; + break; + default: + BUG(); + } + + cfg |= nearly_empty_watermark << 26; + cfg |= nearly_full_watermark << 29; + len /= 16; /* in 16-dwords: 1, 2, 4 or 8 */ + mask[1] = mask[2] = mask[3] = 0; + + BUG_ON(__raw_readl(&qmgr_regs->sram[queue])); + + while (1) { + if (!(used_sram_bitmap[0] & mask[0]) && + !(used_sram_bitmap[1] & mask[1]) && + !(used_sram_bitmap[2] & mask[2]) && + !(used_sram_bitmap[3] & mask[3])) + break; /* found free space */ + + addr++; + shift_mask(mask); + if (addr + len > ARRAY_SIZE(qmgr_regs->sram)) { + fprintf(stderr, "qmgr: no free SRAM space for" + " queue %i\n", queue); + BUG(); + } + } + + used_sram_bitmap[0] |= mask[0]; + used_sram_bitmap[1] |= mask[1]; + used_sram_bitmap[2] |= mask[2]; + used_sram_bitmap[3] |= mask[3]; + __raw_writel(cfg | (addr << 14), &qmgr_regs->sram[queue]); +#if DEBUG_QMGR + /* no snprintf() */ + sprintf(qmgr_queue_descs[queue], desc_format, name); + fprintf(stderr, "qmgr: requested queue %s(%i) addr = 0x%02X\n", + qmgr_queue_descs[queue], queue, addr); +#endif +} + +void qmgr_release_queue(unsigned int queue) +{ + u32 cfg, addr, mask[4]; + + BUG_ON(queue >= HALF_QUEUES); /* not in valid range */ + + cfg = __raw_readl(&qmgr_regs->sram[queue]); + addr = (cfg >> 14) & 0xFF; + + BUG_ON(!addr); /* not requested */ + + switch ((cfg >> 24) & 3) { + case 0: mask[0] = 0x1; break; + case 1: mask[0] = 0x3; break; + case 2: mask[0] = 0xF; break; + case 3: mask[0] = 0xFF; break; + } + + mask[1] = mask[2] = mask[3] = 0; + + while (addr--) + shift_mask(mask); + +#if DEBUG_QMGR + fprintf(stderr, "qmgr: releasing queue %s(%i)\n", + qmgr_queue_descs[queue], queue); + qmgr_queue_descs[queue][0] = '\x0'; +#endif + __raw_writel(0, &qmgr_regs->sram[queue]); + + used_sram_bitmap[0] &= ~mask[0]; + used_sram_bitmap[1] &= ~mask[1]; + used_sram_bitmap[2] &= ~mask[2]; + used_sram_bitmap[3] &= ~mask[3]; +#ifdef CONFIG_USE_IRQ + irq_handlers[queue] = NULL; /* catch IRQ bugs */ +#endif + + while ((addr = qmgr_get_entry(queue))) + fprintf(stderr, "qmgr: released queue %i not empty: 0x%08X\n", + queue, addr); +} + +static int __init qmgr_init(void) +{ + int i; +#ifdef CONFIG_USE_IRQ + interrupt_handler_t *handler; +#endif + + /* reset qmgr registers */ + for (i = 0; i < 4; i++) { + __raw_writel(0x33333333, &qmgr_regs->stat1[i]); + __raw_writel(0, &qmgr_regs->irqsrc[i]); + } + for (i = 0; i < 2; i++) { + __raw_writel(0, &qmgr_regs->stat2[i]); + __raw_writel(0xFFFFFFFF, &qmgr_regs->irqstat[i]); /* clear */ + __raw_writel(0, &qmgr_regs->irqen[i]); + } + + __raw_writel(0xFFFFFFFF, &qmgr_regs->statne_h); + __raw_writel(0, &qmgr_regs->statf_h); + + for (i = 0; i < QUEUES; i++) + __raw_writel(0, &qmgr_regs->sram[i]); + +#ifdef CONFIG_USE_IRQ + if (cpu_is_ixp42x_rev_a0()) + handler = qmgr_irq1_a0; + else + handler = qmgr_irq1; + + irq_install_handler(IXP425_QM1_IRQ, handler, NULL); +#endif + used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */ + return 0; +} + +coredevice_initcall(qmgr_init); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 2736094..d6164ea 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -93,6 +93,14 @@ config DRIVER_NET_MACB depends on HAS_MACB select PHYLIB +config DRIVER_NET_IXP4XX_ETH + tristate "Intel IXP4xx Ethernet support" + depends on ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR + select PHYLIB + 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 42136f8..4a2ced9 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_DRIVER_NET_MACB) += macb.o obj-$(CONFIG_DRIVER_NET_TAP) += tap.o obj-$(CONFIG_PHYLIB) += phy/ obj-$(CONFIG_NET_USB) += usb/ +obj-$(CONFIG_DRIVER_NET_IXP4XX_ETH) += ixp4xx_eth.o obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o obj-$(CONFIG_DRIVER_NET_KS8851_MLL) += ks8851_mll.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE) += designware.o diff --git a/drivers/net/ixp4xx_eth.c b/drivers/net/ixp4xx_eth.c new file mode 100644 index 0000000..1ae37f1 --- /dev/null +++ b/drivers/net/ixp4xx_eth.c @@ -0,0 +1,741 @@ +/* + * Intel IXP4xx Ethernet driver for Linux + * + * Copyright (C) 2007 Krzysztof Halasa <khc@xxxxxxxxx> + * + * 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 <net.h> +#include <errno.h> +#include <asm/mmu.h> +#include <linux/mii.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 io { + struct desc rx_desc_tab[RX_DESCS]; /* alignment: 0x10 */ + struct desc tx_desc_tab[TX_DESCS]; + u8 rx_buff_tab[RX_DESCS][MAX_MRU]; + u8 tx_buff_tab[TX_DESCS][MAX_MRU]; +}; + +struct port { + struct io *io; + struct eth_regs *regs; + struct npe *npe; + u8 firmware[4]; + struct eth_plat_info *pinfo; + struct mii_bus mii_bus; + struct eth_device eth; +}; + +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_bus *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_bus *mii, int phy_id, int location, u16 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 void ixp4xx_adjust_link(struct eth_device *dev) +{ + struct port *port = dev->priv; + + if (dev->phydev->duplex) + __raw_writel(DEFAULT_TX_CNTRL0 & ~TX_CNTRL0_HALFDUPLEX, + &port->regs->tx_control[0]); + else + __raw_writel(DEFAULT_TX_CNTRL0 | TX_CNTRL0_HALFDUPLEX, + &port->regs->tx_control[0]); +} + +static 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->io->tx_desc_tab : port->io->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->io->rx_desc_tab[n]; + buff = port->io->rx_buff_tab[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->io->tx_desc_tab[n]; + desc->data = port->io->tx_buff_tab[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->io->tx_desc_tab, 0, sizeof(port->io->tx_desc_tab)); /* descs */ + memset(port->io->rx_desc_tab, 0, sizeof(port->io->rx_desc_tab)); + + /* Setup RX buffers */ + for (i = 0; i < RX_DESCS; i++) { + struct desc *desc = &port->io->rx_desc_tab[i]; + desc->buf_len = MAX_MRU; + desc->data = port->io->rx_buff_tab[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); + } + + err = phy_device_connect(dev, &port->mii_bus, port->pinfo->phy, + ixp4xx_adjust_link, 0, PHY_INTERFACE_MODE_MII); + if (err) + return err; + + port->io = dma_alloc_coherent(sizeof(*port->io)); + + 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->io->tx_desc_tab[i]); + + for (i = 0; i < RX_DESCS; i++) + queue_put_desc(RXFREE_QUEUE(port), &port->io->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: + dma_free_coherent(port->io, sizeof(*port->io)); + port->io = 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->io) + 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->io->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); + dma_free_coherent(port->io, sizeof(*port->io)); + port->io = 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); + 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 = xmemalign(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->mii_bus.dev.id = -1; + port->mii_bus.read = ixp4xx_mdio_read; + port->mii_bus.write = ixp4xx_mdio_write; + mdiobus_register(&port->mii_bus); + eth_register(&port->eth); + dev->priv = port; + return 0; +} + +static void ixp4xx_eth_remove(struct device_d *dev) +{ + struct port *port = dev->priv; + ixp4xx_eth_close(&port->eth); + eth_unregister(&port->eth); + mdiobus_unregister(&port->mii_bus); + free(port); +} + +static struct driver_d ixp4xx_eth_driver = { + .name = "ixp4xx_eth", + .probe = ixp4xx_eth_probe, + .remove = ixp4xx_eth_remove, +}; + +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); + + platform_driver_register(&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 b7913aa..be1b968 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -137,7 +137,11 @@ static void ns16550_serial_init_port(struct console_device *cdev) { /* initializing the device for the first time */ ns16550_write(cdev, 0x00, lcr); /* select ier reg */ +#ifdef CONFIG_ARCH_IXP4XX + ns16550_write(cdev, IER_UUE, ier); /* Enable UART operation */ +#else ns16550_write(cdev, 0x00, ier); +#endif #ifdef CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS ns16550_write(cdev, 0x07, mdr1); /* Disable */ diff --git a/drivers/serial/serial_ns16550.h b/drivers/serial/serial_ns16550.h index db8fe64..132f46e 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 */ _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox