diff options
45 files changed, 6688 insertions, 0 deletions
diff --git a/target/linux/rb1xx-2.6/Makefile b/target/linux/rb1xx-2.6/Makefile new file mode 100644 index 0000000..17bd858 --- /dev/null +++ b/target/linux/rb1xx-2.6/Makefile @@ -0,0 +1,24 @@ +# +# Copyright (C) 2006 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk + +ARCH:=mipsel +BOARD:=rb1xx +BOARDNAME:=Microtik RouterBoard 1xx +FEATURES:=squashfs jffs2 broken + +define Target/Description + Build firmware images for ADMTek ADM5120 based boards + (e.g : RouterBoard RB1xx (111,112,133,153) +endef + +include $(INCLUDE_DIR)/kernel-build.mk + +# include the profiles +-include profiles/*.mk + +$(eval $(call BuildKernel)) diff --git a/target/linux/rb1xx-2.6/config/default b/target/linux/rb1xx-2.6/config/default new file mode 100644 index 0000000..2b018de --- /dev/null +++ b/target/linux/rb1xx-2.6/config/default @@ -0,0 +1,251 @@ +CONFIG_32BIT=y +# CONFIG_64BIT is not set +# CONFIG_64BIT_PHYS_ADDR is not set +CONFIG_ADM5120_GPIO=y +CONFIG_ADM5120_NR_UARTS=2 +CONFIG_BASE_SMALL=0 +CONFIG_BAYCOM_SER_FDX=m +CONFIG_BAYCOM_SER_HDX=m +CONFIG_BINFMT_MISC=m +# CONFIG_BLK_DEV_CF_MIPS is not set +CONFIG_CIFS_DEBUG2=y +CONFIG_CIFS_EXPERIMENTAL=y +CONFIG_CIFS_STATS2=y +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CMDLINE="console=ttyS0,115200 rootfs=jffs2,squashfs init=/etc/preinit" +# CONFIG_CPU_BIG_ENDIAN is not set +CONFIG_CPU_HAS_LLSC=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MIPS32=y +CONFIG_CPU_MIPS32_R1=y +# CONFIG_CPU_MIPS32_R2 is not set +# CONFIG_CPU_MIPS64_R1 is not set +# CONFIG_CPU_MIPS64_R2 is not set +CONFIG_CPU_MIPSR1=y +# CONFIG_CPU_NEVADA is not set +# CONFIG_CPU_R10000 is not set +# CONFIG_CPU_R3000 is not set +# CONFIG_CPU_R4300 is not set +# CONFIG_CPU_R4X00 is not set +# CONFIG_CPU_R5000 is not set +# CONFIG_CPU_R5432 is not set +# CONFIG_CPU_R6000 is not set +# CONFIG_CPU_R8000 is not set +# CONFIG_CPU_RM7000 is not set +# CONFIG_CPU_RM9000 is not set +# CONFIG_CPU_SB1 is not set +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +# CONFIG_CPU_TX39XX is not set +# CONFIG_CPU_TX49XX is not set +# CONFIG_CPU_VR41XX is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_DDB5477 is not set +# CONFIG_DM9000 is not set +CONFIG_DMA_NEED_PCI_MAP_STATE=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_ELF_CORE=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_EXT3_FS_XATTR=y +CONFIG_FIRMWARE_EDID=y +CONFIG_FS_MBCACHE=m +CONFIG_FS_POSIX_ACL=y +CONFIG_GENERIC_ACL=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +# CONFIG_GEN_RTC is not set +CONFIG_HWMON=y +# CONFIG_HWMON_DEBUG_CHIP is not set +CONFIG_HW_HAS_PCI=y +CONFIG_HW_RANDOM=y +CONFIG_HZ=250 +# CONFIG_HZ_100 is not set +# CONFIG_HZ_1024 is not set +# CONFIG_HZ_128 is not set +CONFIG_HZ_250=y +# CONFIG_HZ_256 is not set +# CONFIG_HZ_48 is not set +# CONFIG_I2C is not set +# CONFIG_IDE is not set +CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m +CONFIG_INET_XFRM_MODE_BEET=m +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_INPUT=y +# CONFIG_INPUT_EVDEV is not set +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_TUNNEL=m +CONFIG_IP_NF_NAT_SNMP_BASIC=m +CONFIG_IP_NF_NETBIOS_NS=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_POSIX_ACL=y +CONFIG_JFFS2_FS_SECURITY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JOLIET is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_MACH_DECSTATION is not set +# CONFIG_MACH_JAZZ is not set +# CONFIG_MACH_VR41XX is not set +CONFIG_MII=m +# CONFIG_MINIX_FS is not set +CONFIG_MINI_FO=y +CONFIG_MIPS=y +CONFIG_MIPS_ADM5120=y +CONFIG_MIPS_ADM5120_ENET=y +# CONFIG_MIPS_ATLAS is not set +# CONFIG_MIPS_BOSPORUS is not set +# CONFIG_MIPS_COBALT is not set +# CONFIG_MIPS_DB1000 is not set +# CONFIG_MIPS_DB1100 is not set +# CONFIG_MIPS_DB1200 is not set +# CONFIG_MIPS_DB1500 is not set +# CONFIG_MIPS_DB1550 is not set +# CONFIG_MIPS_EV64120 is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_MALTA is not set +# CONFIG_MIPS_MIRAGE is not set +# CONFIG_MIPS_MTX1 is not set +CONFIG_MIPS_MT_DISABLED=y +# CONFIG_MIPS_MT_SMP is not set +# CONFIG_MIPS_MT_SMTC is not set +# CONFIG_MIPS_PB1000 is not set +# CONFIG_MIPS_PB1100 is not set +# CONFIG_MIPS_PB1200 is not set +# CONFIG_MIPS_PB1500 is not set +# CONFIG_MIPS_PB1550 is not set +# CONFIG_MIPS_SEAD is not set +# CONFIG_MIPS_SIM is not set +# CONFIG_MIPS_VPE_LOADER is not set +# CONFIG_MIPS_XXS1500 is not set +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MOMENCO_JAGUAR_ATX is not set +# CONFIG_MOMENCO_OCELOT is not set +# CONFIG_MOMENCO_OCELOT_3 is not set +# CONFIG_MOMENCO_OCELOT_C is not set +# CONFIG_MOMENCO_OCELOT_G is not set +CONFIG_MTD=y +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_ADM5120 is not set +CONFIG_MTD_BLOCK=y +# CONFIG_MTD_BLOCK2MTD is not set +# CONFIG_MTD_CFI is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CHAR=y +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_CONCAT is not set +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +CONFIG_MTD_MAP_BANK_WIDTH_2=y +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_MYLOADER_PARTS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_NANDSIM is not set +CONFIG_MTD_NAND_RB500=y +CONFIG_MTD_NAND_VERIFY_WRITE=y +# CONFIG_MTD_OBSOLETE_CHIPS is not set +# CONFIG_MTD_ONENAND is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_PLATRAM is not set +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_SLRAM is not set +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_KEY=y +# CONFIG_NET_PCI is not set +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_PAGE_SIZE_16KB is not set +CONFIG_PAGE_SIZE_4KB=y +# CONFIG_PAGE_SIZE_64KB is not set +# CONFIG_PAGE_SIZE_8KB is not set +# CONFIG_PARTITION_ADVANCED is not set +# CONFIG_PCIPCWATCHDOG is not set +CONFIG_PCI_ADM5120=y +# CONFIG_PMC_YOSEMITE is not set +# CONFIG_PNX8550_JBS is not set +# CONFIG_PNX8550_V2PCI is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_RTC is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_ADM5120=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SGI_IP22 is not set +# CONFIG_SGI_IP27 is not set +# CONFIG_SGI_IP32 is not set +# CONFIG_SIBYTE_BIGSUR is not set +# CONFIG_SIBYTE_CARMEL is not set +# CONFIG_SIBYTE_CRHINE is not set +# CONFIG_SIBYTE_CRHONE is not set +# CONFIG_SIBYTE_LITTLESUR is not set +# CONFIG_SIBYTE_PTSWARM is not set +# CONFIG_SIBYTE_RHONE is not set +# CONFIG_SIBYTE_SENTOSA is not set +# CONFIG_SIBYTE_SWARM is not set +CONFIG_SOFT_WATCHDOG=m +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_SYN_COOKIES=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_TOSHIBA_JMR3927 is not set +# CONFIG_TOSHIBA_RBTX4927 is not set +# CONFIG_TOSHIBA_RBTX4938 is not set +CONFIG_TRAD_SIGNALS=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_USB is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED=y +CONFIG_YAFFS_AUTO_YAFFS2=y +# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set +# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set +CONFIG_YAFFS_DOES_ECC=y +CONFIG_YAFFS_ECC_WRONG_ORDER=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y +CONFIG_YAFFS_YAFFS1=y +CONFIG_YAFFS_YAFFS2=y diff --git a/target/linux/rb1xx-2.6/files/arch/mips/adm5120/Makefile b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/Makefile new file mode 100644 index 0000000..a68b4a1 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the ADMtek ADM5120 SoC specific parts of the kernel +# + +obj-y := setup.o prom.o irq.o memory.o mipsIRQ.o adm5120_info.o + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/target/linux/rb1xx-2.6/files/arch/mips/adm5120/adm5120_info.c b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/adm5120_info.c new file mode 100644 index 0000000..18e2674 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/adm5120_info.c @@ -0,0 +1,186 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/types.h> +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> + +#include <adm5120_info.h> +#include <adm5120_defs.h> +#include <adm5120_switch.h> +#include <myloader.h> + +/* boot loaders specific definitions */ +#define CFE_EPTSEAL 0x43464531 /* CFE1 is the magic number to recognize CFE from other bootloaders */ + +struct adm5120_info adm5120_info = { + .cpu_speed = CPU_SPEED_175, + .cpu_package = CPU_PACKAGE_PQFP, + .boot_loader = BOOT_LOADER_UNKNOWN, + .board_type = BOARD_TYPE_UNKNOWN +}; + +static char *boot_loader_names[BOOT_LOADER_LAST+1] = { + [BOOT_LOADER_UNKNOWN] = "Unknown", + [BOOT_LOADER_CFE] = "CFE", + [BOOT_LOADER_UBOOT] = "U-Boot", + [BOOT_LOADER_MYLOADER] = "MyLoader" +}; + +/* + * CPU settings detection + */ +#define CODE_GET_PC(c) ((c) & CODE_PC_MASK) +#define CODE_GET_REV(c) (((c) >> CODE_REV_SHIFT) & CODE_REV_MASK) +#define CODE_GET_PK(c) (((c) >> CODE_PK_SHIFT) & CODE_PK_MASK) +#define CODE_GET_CLKS(c) (((c) >> CODE_CLKS_SHIFT) & CODE_CLKS_MASK) +#define CODE_GET_NAB(c) (((c) & CODE_NAB) != 0) + +static void __init detect_cpu_info(void) +{ + uint32_t *reg; + uint32_t code; + uint32_t clks; + + reg = (uint32_t *)KSEG1ADDR(ADM5120_SWITCH_BASE+SWITCH_REG_CODE); + code = *reg; + + clks = CODE_GET_CLKS(code); + + adm5120_info.product_code = CODE_GET_PC(code); + adm5120_info.revision = CODE_GET_REV(code); + + adm5120_info.cpu_speed = CPU_SPEED_175; + if (clks & 1) + adm5120_info.cpu_speed += 25000000; + if (clks & 2) + adm5120_info.cpu_speed += 50000000; + + adm5120_info.cpu_package = (CODE_GET_PK(code) == CODE_PK_BGA) ? + CPU_PACKAGE_BGA : CPU_PACKAGE_PQFP; + + adm5120_info.nand_boot = CODE_GET_NAB(code); + +} + +/* + * Boot loader detection routines + */ +static int __init detect_cfe(void) +{ + /* + * This method only works, when we are booted directly from the CFE. + */ + uint32_t cfe_handle = (uint32_t) fw_arg0; + uint32_t cfe_a1_val = (uint32_t) fw_arg1; + uint32_t cfe_entry = (uint32_t) fw_arg2; + uint32_t cfe_seal = (uint32_t) fw_arg3; + + /* Check for CFE by finding the CFE magic number */ + if (cfe_seal != CFE_EPTSEAL) { + /* We are not booted from CFE */ + return 0; + } + + /* cfe_a1_val must be 0, because only one CPU present in the ADM5120 SoC */ + if (cfe_a1_val != 0) { + return 0; + } + + /* The cfe_handle, and the cfe_entry must be kernel mode addresses */ + if ((cfe_handle < KSEG0) || (cfe_entry < KSEG0)) { + return 0; + } + + return 1; +} + +static int __init detect_uboot(void) +{ + /* FIXME: not yet implemented */ + return 0; +} + +static int __init detect_myloader(void) +{ + struct mylo_system_params *sysp; + struct mylo_board_params *boardp; + struct mylo_partition_table *parts; + + sysp = (struct mylo_system_params *)(MYLO_MIPS_SYS_PARAMS); + boardp = (struct mylo_board_params *)(MYLO_MIPS_BOARD_PARAMS); + parts = (struct mylo_partition_table *)(MYLO_MIPS_PARTITIONS); + + /* Check for some magic numbers */ + if ((sysp->magic != MYLO_MAGIC_SYS_PARAMS) || + (boardp->magic != MYLO_MAGIC_BOARD_PARAMS) || + (parts->magic != MYLO_MAGIC_PARTITIONS)) + return 0; + + return 1; +} + +static int __init detect_routerboot(void) +{ + /* FIXME: not yet implemented */ + return 0; +} + +static int __init detect_bootloader(void) +{ + if (detect_cfe()) + return BOOT_LOADER_CFE; + + if (detect_uboot()) + return BOOT_LOADER_UBOOT; + + if (detect_myloader()) + return BOOT_LOADER_MYLOADER; + + if (detect_routerboot()) + return BOOT_LOADER_ROUTERBOOT; + + return BOOT_LOADER_UNKNOWN; +} + +/* + * Board detection + */ +static void __init detect_board_type(void) +{ + /* FIXME: not yet implemented */ +} + +void __init adm5120_info_show(void) +{ + printk("ADM%04X%s revision %d, running at %ldMHz\n", + adm5120_info.product_code, + (adm5120_info.cpu_package == CPU_PACKAGE_BGA) ? "" : "P", + adm5120_info.revision, + (adm5120_info.cpu_speed / 1000000) + ); + printk("Boot loader is: %s\n", boot_loader_names[adm5120_info.boot_loader]); + printk("Booted from : %s flash\n", adm5120_info.nand_boot ? "NAND" : "NOR"); +} + +void __init adm5120_info_init(void) +{ + detect_cpu_info(); + adm5120_info.boot_loader = detect_bootloader(); + detect_board_type(); + + adm5120_info_show(); +} diff --git a/target/linux/rb1xx-2.6/files/arch/mips/adm5120/irq.c b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/irq.c new file mode 100644 index 0000000..46f3bb0 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/irq.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) ADMtek Incorporated. + * Creator : daniell@admtek.com.tw + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000, 2001 MIPS Technologies, Inc. + * Copyright (C) 2001 Ralf Baechle + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + */ + +#include <linux/autoconf.h> +#include <linux/init.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/pm.h> + +#include <asm/irq.h> +#include <asm/time.h> +#include <asm/mipsregs.h> +#include <asm/gdb-stub.h> +#include <asm/irq_cpu.h> + +#define MIPS_CPU_TIMER_IRQ 7 + +extern int setup_irq(unsigned int irq, struct irqaction *irqaction); +extern irq_desc_t irq_desc[]; +extern asmlinkage void mipsIRQ(void); + +int mips_int_lock(void); +void mips_int_unlock(int); + +unsigned int mips_counter_frequency; + +#define ADM5120_INTC_REG(reg) (*(volatile u32 *)(KSEG1ADDR(0x12200000+(reg)))) +#define ADM5120_INTC_STATUS ADM5120_INTC_REG(0x00) +#define ADM5120_INTC_ENABLE ADM5120_INTC_REG(0x08) +#define ADM5120_INTC_DISABLE ADM5120_INTC_REG(0x0c) +#define ADM5120_IRQ_MAX 9 +#define ADM5120_IRQ_MASK 0x3ff + +void adm5120_hw0_irqdispatch(struct pt_regs *regs) +{ + unsigned long intsrc; + int i; + + intsrc = ADM5120_INTC_STATUS & ADM5120_IRQ_MASK; + + for (i = 0; intsrc; intsrc >>= 1, i++) + if (intsrc & 0x1) + do_IRQ(i); + else + spurious_interrupt(); +} + +void mips_timer_interrupt(struct pt_regs *regs) +{ + write_c0_compare(read_c0_count()+ mips_counter_frequency/HZ); + ll_timer_interrupt(MIPS_CPU_TIMER_IRQ); +} + +/* Main interrupt dispatcher */ +asmlinkage void plat_irq_dispatch(struct pt_regs *regs) +{ + unsigned int cp0_cause = read_c0_cause() & read_c0_status(); + + if (cp0_cause & CAUSEF_IP7) { + mips_timer_interrupt( regs); + } else if (cp0_cause & CAUSEF_IP2) { + adm5120_hw0_irqdispatch( regs); + } +} + +void enable_adm5120_irq(unsigned int irq) +{ + int s; + + /* Disable all interrupts (FIQ/IRQ) */ + s = mips_int_lock(); + + if ((irq < 0) || (irq > ADM5120_IRQ_MAX)) + goto err_exit; + + ADM5120_INTC_ENABLE = (1<<irq); + +err_exit: + + /* Restore the interrupts states */ + mips_int_unlock(s); +} + + +void disable_adm5120_irq(unsigned int irq) +{ + int s; + + /* Disable all interrupts (FIQ/IRQ) */ + s = mips_int_lock(); + + if ((irq < 0) || (irq > ADM5120_IRQ_MAX)) + goto err_exit; + + ADM5120_INTC_DISABLE = (1<<irq); + +err_exit: + /* Restore the interrupts states */ + mips_int_unlock(s); +} + +unsigned int startup_adm5120_irq(unsigned int irq) +{ + enable_adm5120_irq(irq); + return 0; +} + +void shutdown_adm5120_irq(unsigned int irq) +{ + disable_adm5120_irq(irq); +} + +static inline void ack_adm5120_irq(unsigned int irq_nr) +{ + ADM5120_INTC_DISABLE = (1 << irq_nr); +} + + +static void end_adm5120_irq(unsigned int irq_nr) +{ + ADM5120_INTC_ENABLE = (1 << irq_nr); +} + +static hw_irq_controller adm5120_irq_type = { + .typename = "MIPS", + .startup = startup_adm5120_irq, + .shutdown = shutdown_adm5120_irq, + .enable = enable_adm5120_irq, + .disable = disable_adm5120_irq, + .ack = ack_adm5120_irq, + .end = end_adm5120_irq, + .set_affinity = NULL, +}; + + +void __init arch_init_irq(void) +{ + int i; + + for (i = 0; i <= ADM5120_IRQ_MAX; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + irq_desc[i].chip = &adm5120_irq_type; + } +} diff --git a/target/linux/rb1xx-2.6/files/arch/mips/adm5120/memory.c b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/memory.c new file mode 100644 index 0000000..e5c7f0c --- /dev/null +++ b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/memory.c @@ -0,0 +1,135 @@ +/***************************************************************************** + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * Copyright (C) 2003 ADMtek Incorporated. + * daniell@admtek.com.tw + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * ######################################################################## + * + *****************************************************************************/ + +#include <linux/autoconf.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/bootmem.h> +#include <linux/pfn.h> +#include <linux/string.h> + +#include <asm/bootinfo.h> +#include <asm/page.h> +#include <asm/sections.h> + +#include <asm-mips/mips-boards/prom.h> + +extern char *prom_getenv(char *envname); + +#define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK) + +#define ADM5120_MEMCTRL 0x1200001c +#define ADM5120_MEMCTRL_SDRAM_MASK 0x7 + +static const unsigned long adm_sdramsize[] __initdata = { + 0x0, /* Reserved */ + 0x0400000, /* 4Mb */ + 0x0800000, /* 8Mb */ + 0x1000000, /* 16Mb */ + 0x4000000, /* 64Mb */ + 0x8000000, /* 128Mb */ +}; + +/* determined physical memory size, not overridden by command line args */ +unsigned long physical_memsize = 0L; + +struct prom_pmemblock mdesc[PROM_MAX_PMEMBLOCKS]; + +struct prom_pmemblock * __init prom_getmdesc(void) +{ + char *memsize_str; + unsigned int memsize; + char cmdline[CL_SIZE], *ptr; + + memsize_str = prom_getenv("memsize"); + + if (!memsize_str) + { + prom_printf("memsize not set in boot prom, set to default (8Mb)\n"); + physical_memsize = 0x00800000; + } + else +#ifdef DEBUG + prom_printf("prom_memsize = %s\n", memsize_str); +#endif + physical_memsize = simple_strtol(memsize_str, NULL, 0); + + /* Check the command line for a memsize directive that overrides + * the physical/default amount */ + strcpy(cmdline, arcs_cmdline); + ptr = strstr(cmdline, "memsize="); + if (ptr && (ptr != cmdline) && (*(ptr - 1) != ' ')) + ptr = strstr(ptr, " memsize="); + + if (ptr) + memsize = memparse(ptr + 8, &ptr); + else + memsize = physical_memsize; + + memset(mdesc, 0, sizeof(mdesc)); + + mdesc[0].type = BOOT_MEM_RAM; + mdesc[0].base = CPHYSADDR(PFN_ALIGN(&_end)); + mdesc[0].size = memsize - mdesc[0].base; + + return &mdesc[0]; +} + +void __init prom_meminit(void) +{ + struct prom_pmemblock *p; + + p = prom_getmdesc(); + + while (p->size) + { + long type; + unsigned long base, size; + base = p->base; + type = p->type, + size = p->size; + add_memory_region(base, size, type); + p++; + } +} + +#if 0 +void __init prom_meminit(void) +{ + unsigned long base = CPHYSADDR(PFN_ALIGN(&_end)); + unsigned long size; + + u32 memctrl = *(u32*)KSEG1ADDR(ADM5120_MEMCTRL); + size = adm_sdramsize[memctrl & ADM5120_MEMCTRL_SDRAM_MASK]; + add_memory_region(base, size-base, BOOT_MEM_RAM); +} +#endif + +unsigned long __init prom_free_prom_memory(void) +{ + /* We do not have to prom memory to free */ + return 0; +} diff --git a/target/linux/rb1xx-2.6/files/arch/mips/adm5120/mipsIRQ.S b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/mipsIRQ.S new file mode 100644 index 0000000..f118fb4 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/mipsIRQ.S @@ -0,0 +1,135 @@ +/* + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999, 2000 MIPS Technologies, Inc. All rights reserved. + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * ######################################################################## + * + * Interrupt exception dispatch code. + * + */ +#include <linux/autoconf.h> + +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> + +#define STATUS_IE 0x00000001 + +/* A lot of complication here is taken away because: + * + * 1) We handle one interrupt and return, sitting in a loop and moving across + * all the pending IRQ bits in the cause register is _NOT_ the answer, the + * common case is one pending IRQ so optimize in that direction. + * + * 2) We need not check against bits in the status register IRQ mask, that + * would make this routine slow as hell. + * + * 3) Linux only thinks in terms of all IRQs on or all IRQs off, nothing in + * between like BSD spl() brain-damage. + * + * Furthermore, the IRQs on the MIPS board look basically (barring software + * IRQs which we don't use at all and all external interrupt sources are + * combined together on hardware interrupt 0 (MIPS IRQ 2)) like: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 Combined hardware interrupt (hw0) + * 3 Hardware (ignored) + * 4 Hardware (ignored) + * 5 Hardware (ignored) + * 6 Hardware (ignored) + * 7 R4k timer (what we use) + * + * Note: On the SEAD board thing are a little bit different. + * Here IRQ 2 (hw0) is wired to the UART0 and IRQ 3 (hw1) is wired + * wired to UART1. + * + * We handle the IRQ according to _our_ priority which is: + * + * Highest ---- R4k Timer + * Lowest ---- Combined hardware interrupt + * + * then we just return, if multiple IRQs are pending then we will just take + * another exception, big deal. + */ + + .text + .set noreorder + .set noat + .align 5 + +NESTED(mipsIRQ, PT_SIZE, sp) + SAVE_ALL + CLI + .set at + + mfc0 s0, CP0_CAUSE + mfc0 s1, CP0_STATUS + and s0, s0, s1 + + /* First we check for r4k counter/timer IRQ. */ + andi a0, s0, CAUSEF_IP7 + beq a0, zero, 1f + nop + + move a0, sp + jal mips_timer_interrupt + nop + + j ret_from_irq + nop + +1: + andi a0, s0, CAUSEF_IP2 + beq a0, zero, 1f + nop + + move a0, sp + jal adm5120_hw0_irqdispatch + nop +1: + j ret_from_irq + nop + +END(mipsIRQ) + + +LEAF(mips_int_lock) + .set noreorder + mfc0 v0, CP0_STATUS + li v1, ~STATUS_IE + and v1, v1, v0 + mtc0 v1, CP0_STATUS + j ra + and v0, v0, STATUS_IE + .set reorder +END(mips_int_lock) + + +LEAF(mips_int_unlock) + mfc0 v0, CP0_STATUS + and a0, a0, STATUS_IE + or v0, v0, a0 + mtc0 v0, CP0_STATUS + j ra + nop +END(mips_int_unlock) + diff --git a/target/linux/rb1xx-2.6/files/arch/mips/adm5120/prom.c b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/prom.c new file mode 100644 index 0000000..e644fc3 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/prom.c @@ -0,0 +1,118 @@ +/***************************************************************************** + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * Copyright (C) 2003 ADMtek Incorporated. + * daniell@admtek.com.tw + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + *****************************************************************************/ + +#include <linux/init.h> +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/bootmem.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> + +#include <adm5120_info.h> + +void setup_prom_printf(int); +void prom_printf(char *, ...); +void prom_meminit(void); + +#define ADM5120_ENVC 1 + +char *adm5120_envp[2*ADM5120_ENVC] = {"memsize","0x001000000"}; + +#define READCSR(r) *(volatile unsigned long *)(0xB2600000+(r)) +#define WRITECSR(r,v) *(volatile unsigned long *)(0xB2600000+(r)) = v + +#define UART_DR_REG 0x00 +#define UART_FR_REG 0x18 +#define UART_TX_FIFO_FULL 0x20 + +int putPromChar(char c) +{ + WRITECSR(UART_DR_REG, c); + while ( (READCSR(UART_FR_REG) & UART_TX_FIFO_FULL) ); + return 0; +} + +/* + * Ugly prom_printf used for debugging + */ + +void prom_printf(char *fmt, ...) +{ + va_list args; + int l; + char *p, *buf_end; + char buf[1024]; + + va_start(args, fmt); + l = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf) */ + va_end(args); + + buf_end = buf + l; + + for (p = buf; p < buf_end; p++) { + /* Crude cr/nl handling is better than none */ + if (*p == '\n') + putPromChar('\r'); + putPromChar(*p); + } +} + +char *prom_getenv(char *envname) +{ + int i, index=0; + + i = strlen(envname); + + printk(KERN_INFO "GETENV: envname is %s\n", envname); + + while(index < (2*ADM5120_ENVC)) { + if(strncmp(envname, adm5120_envp[index], i) == 0) { + printk(KERN_INFO "GETENV: returning %s\n", adm5120_envp[index+1]); + return(adm5120_envp[index+1]); + } + index += 2; + } + + printk(KERN_INFO "GETENV: not found.\n"); + return(NULL); +} + + +/* + * initialize the prom module. + */ +void __init prom_init(void) +{ + /* you should these macros defined in include/asm/bootinfo.h */ + mips_machgroup = MACH_GROUP_ADM_GW; + mips_machtype = MACH_ADM_GW_5120; + + adm5120_info_init(); + + /* init command line, register a default kernel command line */ + strcpy(&(arcs_cmdline[0]), "console=ttyS0,115200 rootfstype=squashfs,jffs2 init=/etc/preinit"); + + /* init memory map */ + prom_meminit(); +} diff --git a/target/linux/rb1xx-2.6/files/arch/mips/adm5120/setup.c b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/setup.c new file mode 100644 index 0000000..1b99d77 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/arch/mips/adm5120/setup.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) ADMtek Incorporated. + * Creator : daniell@admtek.com.tw + * Copyright 1999, 2000 MIPS Technologies, Inc. + * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005 + */ + +#include <linux/autoconf.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> + +#include <asm/reboot.h> +#include <asm/io.h> +#include <asm/time.h> + +#include <adm5120_info.h> + +#define ADM5120_SOFTRESET 0x12000004 +#define STATUS_IE 0x00000001 +#define ALLINTS (IE_IRQ0 | IE_IRQ5 | STATUS_IE) + +void mips_time_init(void); + +extern unsigned int mips_counter_frequency; + +void adm5120_restart(char *command) +{ + *(u32*)KSEG1ADDR(ADM5120_SOFTRESET)=1; +} + + +void adm5120_halt(void) +{ + printk(KERN_NOTICE "\n** You can safely turn off the power\n"); + while (1); +} + + +void adm5120_power_off(void) +{ + adm5120_halt(); +} + +void __init adm5120_time_init(void) +{ + mips_counter_frequency = adm5120_info.cpu_speed >> 1; +} + +void __init plat_timer_setup(struct irqaction *irq) +{ + /* to generate the first timer interrupt */ + write_c0_compare(read_c0_count()+ mips_counter_frequency/HZ); + clear_c0_status(ST0_BEV); + set_c0_status(ALLINTS); +} + +void __init plat_mem_setup(void) +{ + printk(KERN_INFO "ADM5120 board setup\n"); + + board_time_init = adm5120_time_init; + //board_timer_setup = mips_timer_setup; + + _machine_restart = adm5120_restart; + _machine_halt = adm5120_halt; + pm_power_off = adm5120_power_off; + + set_io_port_base(KSEG1); +} + +const char *get_system_type(void) +{ + return "ADM5120 Board"; +} + +static struct resource adm5120_hcd_resources[] = { + [0] = { + .start = 0x11200000, + .end = 0x11200084, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 0x3, + .end = 0x3, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device adm5120hcd_device = { + .name = "adm5120-hcd", + .id = -1, + .num_resources = ARRAY_SIZE(adm5120_hcd_resources), + .resource = adm5120_hcd_resources, +}; + +static struct platform_device *devices[] __initdata = { + &adm5120hcd_device, +}; + +static int __init adm5120_init(void) +{ + return platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +subsys_initcall(adm5120_init); diff --git a/target/linux/rb1xx-2.6/files/arch/mips/pci/ops-adm5120.c b/target/linux/rb1xx-2.6/files/arch/mips/pci/ops-adm5120.c new file mode 100644 index 0000000..91dae89 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/arch/mips/pci/ops-adm5120.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) ADMtek Incorporated. + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu> + * Copyright (C) 2007 OpenWrt.org + */ + +#include <linux/autoconf.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <adm5120_defs.h> + +volatile u32* pci_config_address_reg = (volatile u32*)KSEG1ADDR(ADM5120_PCICFG_ADDR); +volatile u32* pci_config_data_reg = (volatile u32*)KSEG1ADDR(ADM5120_PCICFG_DATA); + +#define PCI_ENABLE 0x80000000 + +static int pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, uint32_t *val) +{ + *pci_config_address_reg = ((bus->number & 0xff) << 0x10) | + ((devfn & 0xff) << 0x08) | (where & 0xfc) | PCI_ENABLE; + switch (size) { + case 1: + *val = ((*pci_config_data_reg)>>((where&3)<<3))&0xff; + break; + case 2: + *val = ((*pci_config_data_reg)>>((where&3)<<3))&0xffff; + break; + default: + *val = (*pci_config_data_reg); + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, uint32_t val) +{ + *pci_config_address_reg = ((bus->number & 0xff) << 0x10) | + ((devfn & 0xff) << 0x08) | (where & 0xfc) | PCI_ENABLE; + switch (size) { + case 1: + *(volatile u8 *)(((int)pci_config_data_reg) + + (where & 3)) = val; + break; + case 2: + *(volatile u16 *)(((int)pci_config_data_reg) + + (where & 2)) = (val); + break; + default: + *pci_config_data_reg = (val); + } + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops adm5120_pci_ops = { + .read = pci_config_read, + .write = pci_config_write, +}; diff --git a/target/linux/rb1xx-2.6/files/arch/mips/pci/pci-adm5120.c b/target/linux/rb1xx-2.6/files/arch/mips/pci/pci-adm5120.c new file mode 100644 index 0000000..78de001 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/arch/mips/pci/pci-adm5120.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) ADMtek Incorporated. + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu> + * Copyright (C) 2007 OpenWrt.org + */ + +#include <linux/autoconf.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <adm5120_info.h> +#include <adm5120_defs.h> + +extern struct pci_ops adm5120_pci_ops; + +#define PCI_CMM_IOACC_EN 0x1 +#define PCI_CMM_MEMACC_EN 0x2 +#define PCI_CMM_MASTER_EN 0x4 +#define PCI_CMM_DEF \ + (PCI_CMM_IOACC_EN | PCI_CMM_MEMACC_EN | PCI_CMM_MASTER_EN) + +#define PCI_DEF_CACHE_LINE_SZ 4 + + +struct resource pci_io_resource = { + .name = "ADM5120 PCI I/O", + .start = ADM5120_PCIIO_BASE, + .end = ADM5120_PCICFG_ADDR-1, + .flags = IORESOURCE_IO +}; + +struct resource pci_mem_resource = { + .name = "ADM5120 PCI MEM", + .start = ADM5120_PCIMEM_BASE, + .end = ADM5120_PCIIO_BASE-1, + .flags = IORESOURCE_MEM +}; + +static struct pci_controller adm5120_controller = { + .pci_ops = &adm5120_pci_ops, + .io_resource = &pci_io_resource, + .mem_resource = &pci_mem_resource, +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + if (slot < 2 || slot > 4) + return -1; + return slot + 4; +} + +static void adm5120_pci_fixup(struct pci_dev *dev) +{ + if (dev->devfn == 0) { + pci_write_config_word(dev, PCI_COMMAND, PCI_CMM_DEF); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, + PCI_DEF_CACHE_LINE_SZ); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0); + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, adm5120_pci_fixup); + + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +static int __init adm5120_pci_setup(void) +{ + int pci_bios; + + pci_bios = adm5120_has_pci(); + + printk("adm5120: system has %sPCI BIOS\n", pci_bios ? "" : "no "); + if (pci_bios == 0) + return 1; + + /* Avoid ISA compat ranges. */ + PCIBIOS_MIN_IO = 0x00000000; + PCIBIOS_MIN_MEM = 0x00000000; + + /* Set I/O resource limits. */ + ioport_resource.end = 0x1fffffff; + iomem_resource.end = 0xffffffff; + + register_pci_controller(&adm5120_controller); + return 0; +} + +subsys_initcall(adm5120_pci_setup); diff --git a/target/linux/rb1xx-2.6/files/drivers/char/adm5120_gpio.c b/target/linux/rb1xx-2.6/files/drivers/char/adm5120_gpio.c new file mode 100644 index 0000000..6fccfb0 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/drivers/char/adm5120_gpio.c @@ -0,0 +1,58 @@ +/* + * ADM5120 LED (GPIO) driver + * + * Copyright (C) Jeroen Vreeken (pe1rxq@amsat.org), 2005 + */ + +#include <linux/autoconf.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> + +#define LED_MINOR 151 +#define GPIO_IO ((unsigned long *)0xb20000b8) + +static ssize_t adm5120_led_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + unsigned char val; + + if (!len || get_user(val, data)) + return -EFAULT; + *GPIO_IO=(*GPIO_IO & 0x00ffffff) | (val<<24); + return 1; +} + +static struct file_operations adm5120_led_fops = { + .owner = THIS_MODULE, + .write = adm5120_led_write, +}; + +static struct miscdevice adm5120_led_device = { + LED_MINOR, + "led", + &adm5120_led_fops, +}; + +static int __init adm5120_led_init(void) +{ + printk(KERN_INFO "ADM5120 LED & GPIO driver\n"); + if (misc_register(&adm5120_led_device)) { + printk(KERN_WARNING "Couldn't register device %d\n", LED_MINOR); + return -EBUSY; + } + return 0; +} + +static void __exit adm5120_led_exit(void) +{ + misc_deregister(&adm5120_led_device); +} + +module_init(adm5120_led_init); +module_exit(adm5120_led_exit); + +MODULE_DESCRIPTION("ADM5120 LED and GPIO driver"); +MODULE_AUTHOR("Jeroen Vreeken"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/rb1xx-2.6/files/drivers/mtd/nand/rbmipsnand.c b/target/linux/rb1xx-2.6/files/drivers/mtd/nand/rbmipsnand.c new file mode 100644 index 0000000..9d6588a --- /dev/null +++ b/target/linux/rb1xx-2.6/files/drivers/mtd/nand/rbmipsnand.c @@ -0,0 +1,196 @@ +#include <linux/init.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bootinfo.h> +#ifdef CONFIG_MIPS_ADM5120 +#include <asm/mach-adm5120/adm5120_defs.h> +#endif + +#define IDT434_REG_BASE ((volatile void *) KSEG1ADDR(0x18000000)) + +#define GPIOF 0x050000 +#define GPIOC 0x050004 +#define GPIOD 0x050008 + +#define GPIO_RDY (1 << 0x08) +#define GPIO_WPX (1 << 0x09) +#define GPIO_ALE (1 << 0x0a) +#define GPIO_CLE (1 << 0x0b) + +#define DEV2BASE 0x010020 + +#define LO_WPX (1 << 0) +#define LO_ALE (1 << 1) +#define LO_CLE (1 << 2) +#define LO_CEX (1 << 3) +#define LO_FOFF (1 << 5) +#define LO_SPICS (1 << 6) +#define LO_ULED (1 << 7) + +#define MEM32(x) *((volatile unsigned *) (x)) + + +/* RouterBoard RB1xx specific definitions */ +#define SMEM1(x) (*((volatile unsigned char *) (KSEG1ADDR(ADM5120_SRAM1_BASE) + x))) + +#define NAND_RW_REG 0x0 //data register +#define NAND_SET_CEn 0x1 //CE# low +#define NAND_CLR_CEn 0x2 //CE# high +#define NAND_CLR_CLE 0x3 //CLE low +#define NAND_SET_CLE 0x4 //CLE high +#define NAND_CLR_ALE 0x5 //ALE low +#define NAND_SET_ALE 0x6 //ALE high +#define NAND_SET_SPn 0x7 //SP# low (use spare area) +#define NAND_CLR_SPn 0x8 //SP# high (do not use spare area) +#define NAND_SET_WPn 0x9 //WP# low +#define NAND_CLR_WPn 0xA //WP# high +#define NAND_STS_REG 0xB //Status register + +static void __iomem *p_nand; + +#ifdef CONFIG_MIKROTIK_RB500 +extern void changeLatchU5(unsigned char orMask, unsigned char nandMask); + +static int rb500_dev_ready(struct mtd_info *mtd) +{ + return MEM32(IDT434_REG_BASE + GPIOD) & GPIO_RDY; +} + +/* + * hardware specific access to control-lines + * + * ctrl: + * NAND_CLE: bit 2 -> bit 3 + * NAND_ALE: bit 3 -> bit 2 + */ +static void rbmips_hwcontrol500(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + unsigned char orbits, nandbits; + + if (ctrl & NAND_CTRL_CHANGE) { + + orbits = (ctrl & NAND_CLE) << 1; + orbits |= (ctrl & NAND_ALE) >> 1; + + nandbits = (~ctrl & NAND_CLE) << 1; + nandbits |= (~ctrl & NAND_ALE) >> 1; + + changeLatchU5(orbits, nandbits); + } + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); + +} +#endif + +#ifdef CONFIG_MIPS_ADM5120 +static int rb100_dev_ready(struct mtd_info *mtd) +{ + return SMEM1(NAND_STS_REG) & 0x80; /* found out by experiment */ +} + +static void rbmips_hwcontrol100(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) + SMEM1(ctrl) = 0x01; + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); +} +#endif + + +static struct mtd_partition partition_info[] = { + { + name:"RouterBoard NAND Boot", + offset:0, + size:4 * 1024 * 1024}, + { + name:"RouterBoard NAND Main", + offset:MTDPART_OFS_NXTBLK, + size:MTDPART_SIZ_FULL} +}; + +static struct mtd_info rmtd; +static struct nand_chip rnand; + +static unsigned init_ok = 0; + +unsigned get_rbnand_block_size(void) +{ + if (init_ok) + return rmtd.writesize; + else + return 0; +} + +EXPORT_SYMBOL(get_rbnand_block_size); + +int __init rbmips_init(void) +{ + memset(&rmtd, 0, sizeof(rmtd)); + memset(&rnand, 0, sizeof(rnand)); + +#ifdef CONFIG_MIKROTIK_RB500 + printk("RB500 nand\n"); + changeLatchU5(LO_WPX | LO_FOFF | LO_CEX, + LO_ULED | LO_ALE | LO_CLE); + rnand.cmd_ctrl = rbmips_hwcontrol500; + + rnand.dev_ready = rb500_dev_ready; + rnand.IO_ADDR_W = (unsigned char *) + KSEG1ADDR(MEM32(IDT434_REG_BASE + DEV2BASE)); + rnand.IO_ADDR_R = rnand.IO_ADDR_W; +#endif + +#ifdef CONFIG_MIPS_ADM5120 + printk("RB100 nand\n"); + /* enable NAND flash */ + MEM32(0xB2000064) = 0x100; + /* boot done */ + MEM32(0xB2000008) = 0x1; + SMEM1(NAND_SET_SPn) = 0x01; + SMEM1(NAND_CLR_WPn) = 0x01; + rnand.IO_ADDR_R = (unsigned char *)KSEG1ADDR(ADM5120_SRAM1_BASE); + rnand.IO_ADDR_W = rnand.IO_ADDR_R; + rnand.cmd_ctrl = rbmips_hwcontrol100; + rnand.dev_ready = rb100_dev_ready; +#endif + p_nand = (void __iomem *) ioremap((void *) 0x18a20000, 0x1000); + if (!p_nand) { + printk("RBnand Unable ioremap buffer"); + return -ENXIO; + } + rnand.ecc.mode = NAND_ECC_SOFT; + rnand.chip_delay = 25; + rnand.options |= NAND_NO_AUTOINCR; + rmtd.priv = &rnand; + +#ifdef CONFIG_MIKROTIK_RB500 + int *b; + + b = (int *) KSEG1ADDR(0x18010020); + printk("dev2base 0x%08x mask 0x%08x c 0x%08x tc 0x%08x\n", b[0], + b[1], b[2], b[3]); +#endif + + if (nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1) + && nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)) { + printk("RBxxx nand device not found"); + iounmap((void *) p_nand); + return -ENXIO; + } + + add_mtd_partitions(&rmtd, partition_info, 2); + init_ok = 1; + return 0; +} + +module_init(rbmips_init); diff --git a/target/linux/rb1xx-2.6/files/drivers/net/adm5120sw.c b/target/linux/rb1xx-2.6/files/drivers/net/adm5120sw.c new file mode 100644 index 0000000..fb1752b --- /dev/null +++ b/target/linux/rb1xx-2.6/files/drivers/net/adm5120sw.c @@ -0,0 +1,507 @@ +/* + * ADM5120 built in ethernet switch driver + * + * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005 + * + * Inspiration for this driver came from the original ADMtek 2.4 + * driver, Copyright ADMtek Inc. + */ +#include <linux/autoconf.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <asm/mipsregs.h> +#include <asm/irq.h> +#include <asm/io.h> +#include "adm5120sw.h" + +#include "adm5120_info.h" + +MODULE_AUTHOR("Jeroen Vreeken (pe1rxq@amsat.org)"); +MODULE_DESCRIPTION("ADM5120 ethernet switch driver"); +MODULE_LICENSE("GPL"); + +/* + * The ADM5120 uses an internal matrix to determine which ports + * belong to which VLAN. + * The default generates a VLAN (and device) for each port + * (including MII port) and the CPU port is part of all of them. + * + * Another example, one big switch and everything mapped to eth0: + * 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00 + */ +static unsigned char vlan_matrix[SW_DEVS] = { + 0x41, 0x42, 0x44, 0x48, 0x50, 0x60 +}; + +static int adm5120_nrdevs; + +static struct net_device *adm5120_devs[SW_DEVS]; +static struct adm5120_dma + adm5120_dma_txh_v[ADM5120_DMA_TXH] __attribute__((aligned(16))), + adm5120_dma_txl_v[ADM5120_DMA_TXL] __attribute__((aligned(16))), + adm5120_dma_rxh_v[ADM5120_DMA_RXH] __attribute__((aligned(16))), + adm5120_dma_rxl_v[ADM5120_DMA_RXL] __attribute__((aligned(16))), + *adm5120_dma_txh, + *adm5120_dma_txl, + *adm5120_dma_rxh, + *adm5120_dma_rxl; +static struct sk_buff + *adm5120_skb_rxh[ADM5120_DMA_RXH], + *adm5120_skb_rxl[ADM5120_DMA_RXL], + *adm5120_skb_txh[ADM5120_DMA_TXH], + *adm5120_skb_txl[ADM5120_DMA_TXL]; +static int adm5120_rxhi = 0; +static int adm5120_rxli = 0; +/* We don't use high priority tx for now */ +/*static int adm5120_txhi = 0;*/ +static int adm5120_txli = 0; +static int adm5120_txhit = 0; +static int adm5120_txlit = 0; +static int adm5120_if_open = 0; + +static inline void adm5120_set_reg(unsigned int reg, unsigned long val) +{ + *(volatile unsigned long*)(SW_BASE+reg) = val; +} + +static inline unsigned long adm5120_get_reg(unsigned int reg) +{ + return *(volatile unsigned long*)(SW_BASE+reg); +} + +static inline void adm5120_rxfixup(struct adm5120_dma *dma, + struct sk_buff **skbl, int num) +{ + int i; + + /* Resubmit the entire ring */ + for (i=0; i<num; i++) { + dma[i].status = 0; + dma[i].cntl = 0; + dma[i].len = ADM5120_DMA_RXSIZE; + dma[i].data = ADM5120_DMA_ADDR(skbl[i]->data) | + ADM5120_DMA_OWN | (i==num-1 ? ADM5120_DMA_RINGEND : 0); + } +} + +static inline void adm5120_rx(struct adm5120_dma *dma, struct sk_buff **skbl, + int *index, int num) +{ + struct sk_buff *skb, *skbn; + struct adm5120_sw *priv; + struct net_device *dev; + int port, vlan, len; + + while (!(dma[*index].data & ADM5120_DMA_OWN)) { + port = (dma[*index].status & ADM5120_DMA_PORTID); + port >>= ADM5120_DMA_PORTSHIFT; + for (vlan = 0; vlan < adm5120_nrdevs; vlan++) { + if ((1<<port) & vlan_matrix[vlan]) + break; + } + if (vlan == adm5120_nrdevs) + vlan = 0; + dev = adm5120_devs[vlan]; + skb = skbl[*index]; + len = (dma[*index].status & ADM5120_DMA_LEN); + len >>= ADM5120_DMA_LENSHIFT; + len -= ETH_FCS; + + priv = netdev_priv(dev); + if (len <= 0 || len > ADM5120_DMA_RXSIZE || + dma[*index].status & ADM5120_DMA_FCSERR) { + priv->stats.rx_errors++; + skbn = NULL; + } else { + skbn = dev_alloc_skb(ADM5120_DMA_RXSIZE+16); + if (skbn) { + skb_put(skb, len); + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + dev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes+=len; + skb_reserve(skbn, 2); + skbl[*index] = skbn; + } else { + printk(KERN_INFO "%s recycling!\n", dev->name); + } + } + + dma[*index].status = 0; + dma[*index].cntl = 0; + dma[*index].len = ADM5120_DMA_RXSIZE; + dma[*index].data = ADM5120_DMA_ADDR(skbl[*index]->data) | + ADM5120_DMA_OWN | + (num-1==*index ? ADM5120_DMA_RINGEND : 0); + if (num == ++*index) + *index = 0; + if (skbn) + netif_rx(skb); + } +} + +static inline void adm5120_tx(struct adm5120_dma *dma, struct sk_buff **skbl, + int *index, int num) +{ + while((dma[*index].data & ADM5120_DMA_OWN) == 0 && skbl[*index]) { + dev_kfree_skb_irq(skbl[*index]); + skbl[*index] = NULL; + if (++*index == num) + *index = 0; + } +} + +static irqreturn_t adm5120_sw_irq(int irq, void *dev_id) +{ + unsigned long intreg; + + adm5120_set_reg(ADM5120_INT_MASK, + adm5120_get_reg(ADM5120_INT_MASK) | ADM5120_INTHANDLE); + + intreg = adm5120_get_reg(ADM5120_INT_ST); + adm5120_set_reg(ADM5120_INT_ST, intreg); + + if (intreg & ADM5120_INT_RXH) + adm5120_rx(adm5120_dma_rxh, adm5120_skb_rxh, &adm5120_rxhi, + ADM5120_DMA_RXH); + if (intreg & ADM5120_INT_HFULL) + adm5120_rxfixup(adm5120_dma_rxh, adm5120_skb_rxh, + ADM5120_DMA_RXH); + if (intreg & ADM5120_INT_RXL) + adm5120_rx(adm5120_dma_rxl, adm5120_skb_rxl, &adm5120_rxli, + ADM5120_DMA_RXL); + if (intreg & ADM5120_INT_LFULL) + adm5120_rxfixup(adm5120_dma_rxl, adm5120_skb_rxl, + ADM5120_DMA_RXL); + if (intreg & ADM5120_INT_TXH) + adm5120_tx(adm5120_dma_txh, adm5120_skb_txh, &adm5120_txhit, + ADM5120_DMA_TXH); + if (intreg & ADM5120_INT_TXL) + adm5120_tx(adm5120_dma_txl, adm5120_skb_txl, &adm5120_txlit, + ADM5120_DMA_TXL); + + adm5120_set_reg(ADM5120_INT_MASK, + adm5120_get_reg(ADM5120_INT_MASK) & ~ADM5120_INTHANDLE); + + return IRQ_HANDLED; +} + +static void adm5120_set_vlan(char *matrix) +{ + unsigned long val; + + val = matrix[0] + (matrix[1]<<8) + (matrix[2]<<16) + (matrix[3]<<24); + adm5120_set_reg(ADM5120_VLAN_GI, val); + val = matrix[4] + (matrix[5]<<8); + adm5120_set_reg(ADM5120_VLAN_GII, val); +} + +static int adm5120_sw_open(struct net_device *dev) +{ + if (!adm5120_if_open++) + adm5120_set_reg(ADM5120_INT_MASK, + adm5120_get_reg(ADM5120_INT_MASK) & ~ADM5120_INTHANDLE); + netif_start_queue(dev); + return 0; +} + +static int adm5120_sw_stop(struct net_device *dev) +{ + netif_stop_queue(dev); + if (!--adm5120_if_open) + adm5120_set_reg(ADM5120_INT_MASK, + adm5120_get_reg(ADM5120_INT_MASK) | ADM5120_INTMASKALL); + return 0; +} + +static int adm5120_sw_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct adm5120_dma *dma = adm5120_dma_txl; + struct sk_buff **skbl = adm5120_skb_txl; + struct adm5120_sw *priv = netdev_priv(dev); + int *index = &adm5120_txli; + int num = ADM5120_DMA_TXL; + int trigger = ADM5120_SEND_TRIG_L; + + dev->trans_start = jiffies; + if (dma[*index].data & ADM5120_DMA_OWN) { + dev_kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + + dma[*index].data = ADM5120_DMA_ADDR(skb->data) | ADM5120_DMA_OWN; + if (*index == num-1) + dma[*index].data |= ADM5120_DMA_RINGEND; + dma[*index].status = + ((skb->len<ETH_ZLEN?ETH_ZLEN:skb->len) << ADM5120_DMA_LENSHIFT) | + (0x1 << priv->port); + dma[*index].len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + skbl[*index]=skb; + + if (++*index == num) + *index = 0; + adm5120_set_reg(ADM5120_SEND_TRIG, trigger); + + return 0; +} + +static void adm5120_tx_timeout(struct net_device *dev) +{ + netif_wake_queue(dev); +} + +static struct net_device_stats *adm5120_sw_stats(struct net_device *dev) +{ + return &((struct adm5120_sw *)netdev_priv(dev))->stats; +} + +static void adm5120_set_multicast_list(struct net_device *dev) +{ + struct adm5120_sw *priv = netdev_priv(dev); + int portmask; + + portmask = vlan_matrix[priv->port] & 0x3f; + + if (dev->flags & IFF_PROMISC) + adm5120_set_reg(ADM5120_CPUP_CONF, + adm5120_get_reg(ADM5120_CPUP_CONF) & + ~((portmask << ADM5120_DISUNSHIFT) & ADM5120_DISUNALL)); + else + adm5120_set_reg(ADM5120_CPUP_CONF, + adm5120_get_reg(ADM5120_CPUP_CONF) | + (portmask << ADM5120_DISUNSHIFT)); + + if (dev->flags & IFF_PROMISC || dev->flags & IFF_ALLMULTI || + dev->mc_count) + adm5120_set_reg(ADM5120_CPUP_CONF, + adm5120_get_reg(ADM5120_CPUP_CONF) & + ~((portmask << ADM5120_DISMCSHIFT) & ADM5120_DISMCALL)); + else + adm5120_set_reg(ADM5120_CPUP_CONF, + adm5120_get_reg(ADM5120_CPUP_CONF) | + (portmask << ADM5120_DISMCSHIFT)); +} + +static void adm5120_write_mac(struct net_device *dev) +{ + struct adm5120_sw *priv = netdev_priv(dev); + unsigned char *mac = dev->dev_addr; + + adm5120_set_reg(ADM5120_MAC_WT1, + mac[2] | (mac[3]<<8) | (mac[4]<<16) | (mac[5]<<24)); + adm5120_set_reg(ADM5120_MAC_WT0, (priv->port<<3) | + (mac[0]<<16) | (mac[1]<<24) | ADM5120_MAC_WRITE | ADM5120_VLAN_EN); + + while (!(adm5120_get_reg(ADM5120_MAC_WT0) & ADM5120_MAC_WRITE_DONE)); +} + +static int adm5120_sw_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + adm5120_write_mac(dev); + return 0; +} + +static int adm5120_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + int err; + struct adm5120_sw_info info; + struct adm5120_sw *priv = netdev_priv(dev); + + switch(cmd) { + case SIOCGADMINFO: + info.magic = 0x5120; + info.ports = adm5120_nrdevs; + info.vlan = priv->port; + err = copy_to_user(rq->ifr_data, &info, sizeof(info)); + if (err) + return -EFAULT; + break; + case SIOCSMATRIX: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + err = copy_from_user(vlan_matrix, rq->ifr_data, + sizeof(vlan_matrix)); + if (err) + return -EFAULT; + adm5120_set_vlan(vlan_matrix); + break; + case SIOCGMATRIX: + err = copy_to_user(rq->ifr_data, vlan_matrix, + sizeof(vlan_matrix)); + if (err) + return -EFAULT; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static void adm5120_dma_tx_init(struct adm5120_dma *dma, struct sk_buff **skb, + int num) +{ + memset(dma, 0, sizeof(struct adm5120_dma)*num); + dma[num-1].data |= ADM5120_DMA_RINGEND; + memset(skb, 0, sizeof(struct skb*)*num); +} + +static void adm5120_dma_rx_init(struct adm5120_dma *dma, struct sk_buff **skb, + int num) +{ + int i; + + memset(dma, 0, sizeof(struct adm5120_dma)*num); + for (i=0; i<num; i++) { + skb[i] = dev_alloc_skb(ADM5120_DMA_RXSIZE+16); + if (!skb[i]) { + i=num; + break; + } + skb_reserve(skb[i], 2); + dma[i].data = ADM5120_DMA_ADDR(skb[i]->data) | ADM5120_DMA_OWN; + dma[i].cntl = 0; + dma[i].len = ADM5120_DMA_RXSIZE; + dma[i].status = 0; + } + dma[i-1].data |= ADM5120_DMA_RINGEND; +} + +static int __init adm5120_sw_init(void) +{ + int i, err; + struct net_device *dev; + + err = request_irq(SW_IRQ, adm5120_sw_irq, 0, "ethernet switch", NULL); + if (err) + goto out; + + /* MII port? */ + if (adm5120_get_reg(ADM5120_CODE) & ADM5120_CODE_PQFP) + adm5120_nrdevs = 5; + /* CFE based devices only have two enet ports */ + else if (adm5120_info.boot_loader == BOOT_LOADER_CFE) + adm5120_nrdevs = 2; + else + adm5120_nrdevs = 6; + + adm5120_set_reg(ADM5120_CPUP_CONF, + ADM5120_DISCCPUPORT | ADM5120_CRC_PADDING | + ADM5120_DISUNALL | ADM5120_DISMCALL); + adm5120_set_reg(ADM5120_PORT_CONF0, ADM5120_ENMC | ADM5120_ENBP); + + adm5120_set_reg(ADM5120_PHY_CNTL2, adm5120_get_reg(ADM5120_PHY_CNTL2) | + ADM5120_AUTONEG | ADM5120_NORMAL | ADM5120_AUTOMDIX); + adm5120_set_reg(ADM5120_PHY_CNTL3, adm5120_get_reg(ADM5120_PHY_CNTL3) | + ADM5120_PHY_NTH); + + adm5120_set_reg(ADM5120_INT_MASK, ADM5120_INTMASKALL); + adm5120_set_reg(ADM5120_INT_ST, ADM5120_INTMASKALL); + + adm5120_dma_txh = (void *)KSEG1ADDR((u32)adm5120_dma_txh_v); + adm5120_dma_txl = (void *)KSEG1ADDR((u32)adm5120_dma_txl_v); + adm5120_dma_rxh = (void *)KSEG1ADDR((u32)adm5120_dma_rxh_v); + adm5120_dma_rxl = (void *)KSEG1ADDR((u32)adm5120_dma_rxl_v); + + adm5120_dma_tx_init(adm5120_dma_txh, adm5120_skb_txh, ADM5120_DMA_TXH); + adm5120_dma_tx_init(adm5120_dma_txl, adm5120_skb_txl, ADM5120_DMA_TXL); + adm5120_dma_rx_init(adm5120_dma_rxh, adm5120_skb_rxh, ADM5120_DMA_RXH); + adm5120_dma_rx_init(adm5120_dma_rxl, adm5120_skb_rxl, ADM5120_DMA_RXL); + adm5120_set_reg(ADM5120_SEND_HBADDR, KSEG1ADDR(adm5120_dma_txh)); + adm5120_set_reg(ADM5120_SEND_LBADDR, KSEG1ADDR(adm5120_dma_txl)); + adm5120_set_reg(ADM5120_RECEIVE_HBADDR, KSEG1ADDR(adm5120_dma_rxh)); + adm5120_set_reg(ADM5120_RECEIVE_LBADDR, KSEG1ADDR(adm5120_dma_rxl)); + + adm5120_set_vlan(vlan_matrix); + + for (i=0; i<adm5120_nrdevs; i++) { + adm5120_devs[i] = alloc_etherdev(sizeof(struct adm5120_sw)); + if (!adm5120_devs[i]) { + err = -ENOMEM; + goto out_int; + } + + dev = adm5120_devs[i]; + SET_MODULE_OWNER(dev); + memset(netdev_priv(dev), 0, sizeof(struct adm5120_sw)); + ((struct adm5120_sw*)netdev_priv(dev))->port = i; + dev->base_addr = SW_BASE; + dev->irq = SW_IRQ; + dev->open = adm5120_sw_open; + dev->hard_start_xmit = adm5120_sw_tx; + dev->stop = adm5120_sw_stop; + dev->get_stats = adm5120_sw_stats; + dev->set_multicast_list = adm5120_set_multicast_list; + dev->do_ioctl = adm5120_do_ioctl; + dev->tx_timeout = adm5120_tx_timeout; + dev->watchdog_timeo = ETH_TX_TIMEOUT; + dev->set_mac_address = adm5120_sw_set_mac_address; + /* HACK alert!!! In the original admtek driver it is asumed + that you can read the MAC addressess from flash, but edimax + decided to leave that space intentionally blank... + */ + memcpy(dev->dev_addr, "\x00\x50\xfc\x11\x22\x01", 6); + dev->dev_addr[5] += i; + adm5120_write_mac(dev); + + if ((err = register_netdev(dev))) { + free_netdev(dev); + goto out_int; + } + printk(KERN_INFO "%s: ADM5120 switch port%d\n", dev->name, i); + } + adm5120_set_reg(ADM5120_CPUP_CONF, + ADM5120_CRC_PADDING | ADM5120_DISUNALL | ADM5120_DISMCALL); + + return 0; + +out_int: + /* Undo everything that did succeed */ + for (; i; i--) { + unregister_netdev(adm5120_devs[i-1]); + free_netdev(adm5120_devs[i-1]); + } + free_irq(SW_IRQ, NULL); +out: + printk(KERN_ERR "ADM5120 Ethernet switch init failed\n"); + return err; +} + +static void __exit adm5120_sw_exit(void) +{ + int i; + + for (i = 0; i < adm5120_nrdevs; i++) { + unregister_netdev(adm5120_devs[i]); + free_netdev(adm5120_devs[i-1]); + } + + free_irq(SW_IRQ, NULL); + + for (i = 0; i < ADM5120_DMA_RXH; i++) { + if (!adm5120_skb_rxh[i]) + break; + kfree_skb(adm5120_skb_rxh[i]); + } + for (i = 0; i < ADM5120_DMA_RXL; i++) { + if (!adm5120_skb_rxl[i]) + break; + kfree_skb(adm5120_skb_rxl[i]); + } +} + +module_init(adm5120_sw_init); +module_exit(adm5120_sw_exit); diff --git a/target/linux/rb1xx-2.6/files/drivers/net/adm5120sw.h b/target/linux/rb1xx-2.6/files/drivers/net/adm5120sw.h new file mode 100644 index 0000000..19388a9 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/drivers/net/adm5120sw.h @@ -0,0 +1,106 @@ +/* + * Defines for ADM5120 built in ethernet switch driver + * + * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005 + * + * Values come from ADM5120 datasheet and original ADMtek 2.4 driver, + * Copyright ADMtek Inc. + */ + +#ifndef _INCLUDE_ADM5120SW_H_ +#define _INCLUDE_ADM5120SW_H_ + +#define SW_BASE KSEG1ADDR(0x12000000) +#define SW_DEVS 6 +#define SW_IRQ 9 + +#define ETH_TX_TIMEOUT HZ/4 +#define ETH_FCS 4; + +#define ADM5120_CODE 0x00 /* CPU description */ +#define ADM5120_CODE_PQFP 0x20000000 /* package type */ +#define ADM5120_CPUP_CONF 0x24 /* CPU port config */ +#define ADM5120_DISCCPUPORT 0x00000001 /* disable cpu port */ +#define ADM5120_CRC_PADDING 0x00000002 /* software crc */ +#define ADM5120_DISUNSHIFT 9 +#define ADM5120_DISUNALL 0x00007e00 /* disable unknown from all */ +#define ADM5120_DISMCSHIFT 16 +#define ADM5120_DISMCALL 0x003f0000 /* disable multicast from all */ +#define ADM5120_PORT_CONF0 0x28 +#define ADM5120_ENMC 0x00003f00 /* Enable MC routing (ex cpu) */ +#define ADM5120_ENBP 0x003f0000 /* Enable Back Pressure */ +#define ADM5120_VLAN_GI 0x40 /* VLAN settings */ +#define ADM5120_VLAN_GII 0x44 +#define ADM5120_SEND_TRIG 0x48 +#define ADM5120_SEND_TRIG_L 0x00000001 +#define ADM5120_SEND_TRIG_H 0x00000002 +#define ADM5120_MAC_WT0 0x58 +#define ADM5120_MAC_WRITE 0x00000001 +#define ADM5120_MAC_WRITE_DONE 0x00000002 +#define ADM5120_VLAN_EN 0x00000040 +#define ADM5120_MAC_WT1 0x5c +#define ADM5120_PHY_CNTL2 0x7c +#define ADM5120_AUTONEG 0x0000001f /* Auto negotiate */ +#define ADM5120_NORMAL 0x01f00000 /* PHY normal mode */ +#define ADM5120_AUTOMDIX 0x3e000000 /* Auto MDIX */ +#define ADM5120_PHY_CNTL3 0x80 +#define ADM5120_PHY_NTH 0x00000400 +#define ADM5120_INT_ST 0xb0 +#define ADM5120_INT_RXH 0x0000004 +#define ADM5120_INT_RXL 0x0000008 +#define ADM5120_INT_HFULL 0x0000010 +#define ADM5120_INT_LFULL 0x0000020 +#define ADM5120_INT_TXH 0x0000001 +#define ADM5120_INT_TXL 0x0000002 +#define ADM5120_INT_MASK 0xb4 +#define ADM5120_INTMASKALL 0x1FDEFFF /* All interrupts */ +#define ADM5120_INTHANDLE (ADM5120_INT_RXH | ADM5120_INT_RXL | \ + ADM5120_INT_HFULL | ADM5120_INT_LFULL | \ + ADM5120_INT_TXH | ADM5120_INT_TXL) +#define ADM5120_SEND_HBADDR 0xd0 +#define ADM5120_SEND_LBADDR 0xd4 +#define ADM5120_RECEIVE_HBADDR 0xd8 +#define ADM5120_RECEIVE_LBADDR 0xdc + +struct adm5120_dma { + u32 data; + u32 cntl; + u32 len; + u32 status; +} __attribute__ ((packed)); + +#define ADM5120_DMA_MASK 0x01ffffff +#define ADM5120_DMA_OWN 0x80000000 /* buffer owner */ +#define ADM5120_DMA_RINGEND 0x10000000 /* Last in DMA ring */ + +#define ADM5120_DMA_ADDR(ptr) ((u32)(ptr) & ADM5120_DMA_MASK) +#define ADM5120_DMA_PORTID 0x00007000 +#define ADM5120_DMA_PORTSHIFT 12 +#define ADM5120_DMA_LEN 0x07ff0000 +#define ADM5120_DMA_LENSHIFT 16 +#define ADM5120_DMA_FCSERR 0x00000008 + +#define ADM5120_DMA_TXH 16 +#define ADM5120_DMA_TXL 64 +#define ADM5120_DMA_RXH 16 +#define ADM5120_DMA_RXL 8 + +#define ADM5120_DMA_RXSIZE 1550 +#define ADM5120_DMA_EXTRA 20 + +struct adm5120_sw { + int port; + struct net_device_stats stats; +}; + +#define SIOCSMATRIX SIOCDEVPRIVATE +#define SIOCGMATRIX SIOCDEVPRIVATE+1 +#define SIOCGADMINFO SIOCDEVPRIVATE+2 + +struct adm5120_sw_info { + u16 magic; + u16 ports; + u16 vlan; +}; + +#endif /* _INCLUDE_ADM5120SW_H_ */ diff --git a/target/linux/rb1xx-2.6/files/drivers/serial/adm5120_uart.c b/target/linux/rb1xx-2.6/files/drivers/serial/adm5120_uart.c new file mode 100644 index 0000000..6b42928 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/drivers/serial/adm5120_uart.c @@ -0,0 +1,524 @@ +/* + * Serial driver for ADM5120 SoC + * + * Derived from drivers/serial/uart00.c + * Copyright 2001 Altera Corporation + * + * Some pieces are derived from the ADMtek 2.4 serial driver. + * Copyright (C) ADMtek Incorporated, 2003 + * daniell@admtek.com.tw + * Which again was derived from drivers/char/serial.c + * Copyright (C) Linus Torvalds et al. + * + * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2005 + */ + +#include <linux/autoconf.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/console.h> + +#define ADM5120_UART_BASE0 0x12600000 +#define ADM5120_UART_BASE1 0x12800000 +#define ADM5120_UART_SIZE 0x20 + +#define ADM5120_UART_IRQ0 1 +#define ADM5120_UART_IRQ1 2 + +#define ADM5120_UART_REG(base, reg) \ + (*(volatile u32 *)KSEG1ADDR((base)+(reg))) + +#define ADM5120_UARTCLK_FREQ 62500000 +#define ADM5120_UART_BAUDDIV(rate) ((unsigned long)(ADM5120_UARTCLK_FREQ/(16*(rate)) - 1)) + +#define ADM5120_UART_BAUD115200 ADM5120_UART_BAUDDIV(115200) + +#define ADM5120_UART_DATA 0x00 +#define ADM5120_UART_RS 0x04 +#define ADM5120_UART_LCR_H 0x08 +#define ADM5120_UART_LCR_M 0x0c +#define ADM5120_UART_LCR_L 0x10 +#define ADM5120_UART_CR 0x14 +#define ADM5120_UART_FR 0x18 +#define ADM5120_UART_IR 0x1c + +#define ADM5120_UART_FE 0x01 +#define ADM5120_UART_PE 0x02 +#define ADM5120_UART_BE 0x04 +#define ADM5120_UART_OE 0x08 +#define ADM5120_UART_ERR 0x0f +#define ADM5120_UART_FIFO_EN 0x10 +#define ADM5120_UART_EN 0x01 +#define ADM5120_UART_TIE 0x20 +#define ADM5120_UART_RIE 0x50 +#define ADM5120_UART_IE 0x78 +#define ADM5120_UART_CTS 0x01 +#define ADM5120_UART_DSR 0x02 +#define ADM5120_UART_DCD 0x04 +#define ADM5120_UART_TXFF 0x20 +#define ADM5120_UART_TXFE 0x80 +#define ADM5120_UART_RXFE 0x10 +#define ADM5120_UART_BRK 0x01 +#define ADM5120_UART_PEN 0x02 +#define ADM5120_UART_EPS 0x04 +#define ADM5120_UART_STP2 0x08 +#define ADM5120_UART_W5 0x00 +#define ADM5120_UART_W6 0x20 +#define ADM5120_UART_W7 0x40 +#define ADM5120_UART_W8 0x60 +#define ADM5120_UART_MIS 0x01 +#define ADM5120_UART_RIS 0x02 +#define ADM5120_UART_TIS 0x04 +#define ADM5120_UART_RTIS 0x08 + +static void adm5120ser_stop_tx(struct uart_port *port) +{ + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) &= ~ADM5120_UART_TIE; +} + +static void adm5120ser_irq_rx(struct uart_port *port) +{ + struct tty_struct *tty = port->info->tty; + unsigned int status, ch, rds, flg, ignored = 0; + + status = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + while (!(status & ADM5120_UART_RXFE)) { + /* + * We need to read rds before reading the + * character from the fifo + */ + rds = ADM5120_UART_REG(port->iobase, ADM5120_UART_RS); + ch = ADM5120_UART_REG(port->iobase, ADM5120_UART_DATA); + port->icount.rx++; + + if (tty->low_latency) + tty_flip_buffer_push(tty); + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + if (rds & ADM5120_UART_ERR) + goto handle_error; + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + error_return: + tty_insert_flip_char(tty, ch, flg); + + ignore_char: + status = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + } + out: + tty_flip_buffer_push(tty); + return; + + handle_error: + ADM5120_UART_REG(port->iobase, ADM5120_UART_RS) = 0xff; + if (rds & ADM5120_UART_BE) { + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } else if (rds & ADM5120_UART_PE) + port->icount.parity++; + else if (rds & ADM5120_UART_FE) + port->icount.frame++; + if (rds & ADM5120_UART_OE) + port->icount.overrun++; + + if (rds & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + rds &= port->read_status_mask; + + if (rds & ADM5120_UART_BE) + flg = TTY_BREAK; + else if (rds & ADM5120_UART_PE) + flg = TTY_PARITY; + else if (rds & ADM5120_UART_FE) + flg = TTY_FRAME; + + if (rds & ADM5120_UART_OE) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + tty_insert_flip_char(tty, ch, flg); + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef CONFIG_MAGIC_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +static void adm5120ser_irq_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + int count; + + if (port->x_char) { + ADM5120_UART_REG(port->iobase, ADM5120_UART_DATA) = + port->x_char; + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + adm5120ser_stop_tx(port); + return; + } + + count = port->fifosize >> 1; + do { + ADM5120_UART_REG(port->iobase, ADM5120_UART_DATA) = + xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + adm5120ser_stop_tx(port); +} + +static void adm5120ser_irq_modem(struct uart_port *port) +{ + unsigned int status; + + status = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + + if (status & ADM5120_UART_DCD) + uart_handle_dcd_change(port, status & ADM5120_UART_DCD); + + if (status & ADM5120_UART_DSR) + port->icount.dsr++; + + if (status & ADM5120_UART_CTS) + uart_handle_cts_change(port, status & ADM5120_UART_CTS); + + wake_up_interruptible(&port->info->delta_msr_wait); +} + +static irqreturn_t adm5120ser_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned long ir = ADM5120_UART_REG(port->iobase, ADM5120_UART_IR); + + if (ir & (ADM5120_UART_RIS | ADM5120_UART_RTIS)) + adm5120ser_irq_rx(port); + if (ir & ADM5120_UART_TIS) + adm5120ser_irq_tx(port); + if (ir & ADM5120_UART_MIS) { + adm5120ser_irq_modem(port); + ADM5120_UART_REG(port->iobase, ADM5120_UART_IR) = 0xff; + } + + return IRQ_HANDLED; +} + +static unsigned int adm5120ser_tx_empty(struct uart_port *port) +{ + unsigned int fr = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + return (fr & ADM5120_UART_TXFE) ? TIOCSER_TEMT : 0; +} + +static void adm5120ser_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int adm5120ser_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int fr = ADM5120_UART_REG(port->iobase, ADM5120_UART_FR); + + if (fr & ADM5120_UART_CTS) + result |= TIOCM_CTS; + if (fr & ADM5120_UART_DSR) + result |= TIOCM_DSR; + if (fr & ADM5120_UART_DCD) + result |= TIOCM_CAR; + return result; +} + +static void adm5120ser_start_tx(struct uart_port *port) +{ + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) |= ADM5120_UART_TIE; +} + +static void adm5120ser_stop_rx(struct uart_port *port) +{ + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) &= ~ADM5120_UART_RIE; +} + +static void adm5120ser_enable_ms(struct uart_port *port) +{ +} + +static void adm5120ser_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned long lcrh; + + spin_lock_irqsave(&port->lock, flags); + lcrh = ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_H); + if (break_state == -1) + lcrh |= ADM5120_UART_BRK; + else + lcrh &= ~ADM5120_UART_BRK; + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_H) = lcrh; + spin_unlock_irqrestore(&port->lock, flags); +} + +static int adm5120ser_startup(struct uart_port *port) +{ + int ret; + + ret = request_irq(port->irq, adm5120ser_irq, 0, "ADM5120 UART", port); + if (ret) { + printk(KERN_ERR "Couldn't get irq %d\n", port->irq); + return ret; + } + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_H) |= + ADM5120_UART_FIFO_EN; + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) |= + ADM5120_UART_EN | ADM5120_UART_IE; + return 0; +} + +static void adm5120ser_shutdown(struct uart_port *port) +{ + ADM5120_UART_REG(port->iobase, ADM5120_UART_CR) &= ~ADM5120_UART_IE; + free_irq(port->irq, port); +} + +static void adm5120ser_set_termios(struct uart_port *port, + struct termios *termios, struct termios *old) +{ + unsigned int baud, quot, lcrh; + unsigned long flags; + + termios->c_cflag |= CREAD; + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + lcrh = ADM5120_UART_FIFO_EN; + switch (termios->c_cflag & CSIZE) { + case CS5: + lcrh |= ADM5120_UART_W5; + break; + case CS6: + lcrh |= ADM5120_UART_W6; + break; + case CS7: + lcrh |= ADM5120_UART_W7; + break; + default: + lcrh |= ADM5120_UART_W8; + break; + } + if (termios->c_cflag & CSTOPB) + lcrh |= ADM5120_UART_STP2; + if (termios->c_cflag & PARENB) { + lcrh |= ADM5120_UART_PEN; + if (!(termios->c_cflag & PARODD)) + lcrh |= ADM5120_UART_EPS; + } + + spin_lock_irqsave(port->lock, flags); + + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_H) = lcrh; + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = ADM5120_UART_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= ADM5120_UART_FE | ADM5120_UART_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= ADM5120_UART_BE; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= ADM5120_UART_FE | ADM5120_UART_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= ADM5120_UART_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= ADM5120_UART_OE; + } + + quot = ADM5120_UART_BAUD115200; + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_L) = quot & 0xff; + ADM5120_UART_REG(port->iobase, ADM5120_UART_LCR_M) = quot >> 8; + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *adm5120ser_type(struct uart_port *port) +{ + return port->type == PORT_ADM5120 ? "ADM5120" : NULL; +} + +static void adm5120ser_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_ADM5120; +} + +static void adm5120ser_release_port(struct uart_port *port) +{ + release_mem_region(port->iobase, ADM5120_UART_SIZE); +} + +static int adm5120ser_request_port(struct uart_port *port) +{ + return request_mem_region(port->iobase, ADM5120_UART_SIZE, + "adm5120-uart") != NULL ? 0 : -EBUSY; +} + +static struct uart_ops adm5120ser_ops = { + .tx_empty = adm5120ser_tx_empty, + .set_mctrl = adm5120ser_set_mctrl, + .get_mctrl = adm5120ser_get_mctrl, + .stop_tx = adm5120ser_stop_tx, + .start_tx = adm5120ser_start_tx, + .stop_rx = adm5120ser_stop_rx, + .enable_ms = adm5120ser_enable_ms, + .break_ctl = adm5120ser_break_ctl, + .startup = adm5120ser_startup, + .shutdown = adm5120ser_shutdown, + .set_termios = adm5120ser_set_termios, + .type = adm5120ser_type, + .config_port = adm5120ser_config_port, + .release_port = adm5120ser_release_port, + .request_port = adm5120ser_request_port, +}; + +static void adm5120console_put(const char c) +{ + while ((ADM5120_UART_REG(ADM5120_UART_BASE0, ADM5120_UART_FR) & + ADM5120_UART_TXFF) != 0); + ADM5120_UART_REG(ADM5120_UART_BASE0, ADM5120_UART_DATA) = c; +} + +static void adm5120console_write(struct console *con, const char *s, + unsigned int count) +{ + while (count--) { + if (*s == '\n') + adm5120console_put('\r'); + adm5120console_put(*s); + s++; + } +} + +static int __init adm5120console_setup(struct console *con, char *options) +{ + /* Set to 115200 baud, 8N1 and enable FIFO */ + ADM5120_UART_REG(ADM5120_UART_BASE0, ADM5120_UART_LCR_L) = + ADM5120_UART_BAUD115200 & 0xff; + ADM5120_UART_REG(ADM5120_UART_BASE0, ADM5120_UART_LCR_M) = + ADM5120_UART_BAUD115200 >> 8; + ADM5120_UART_REG(ADM5120_UART_BASE0, ADM5120_UART_LCR_H) = + ADM5120_UART_W8 | ADM5120_UART_FIFO_EN; + /* Enable port */ + ADM5120_UART_REG(ADM5120_UART_BASE0, ADM5120_UART_CR) = + ADM5120_UART_EN; + + return 0; +} + +static struct uart_driver adm5120ser_reg; + +static struct console adm5120_serconsole = { + .name = "ttyS", + .write = adm5120console_write, + .device = uart_console_device, + .setup = adm5120console_setup, + .flags = CON_PRINTBUFFER, + .cflag = B115200 | CS8 | CREAD, + .index = 0, + .data = &adm5120ser_reg, +}; + +static int __init adm5120console_init(void) +{ + register_console(&adm5120_serconsole); + return 0; +} + +console_initcall(adm5120console_init); + + +static struct uart_port adm5120ser_ports[] = { + { + .iobase = ADM5120_UART_BASE0, + .irq = ADM5120_UART_IRQ0, + .uartclk = ADM5120_UARTCLK_FREQ, + .fifosize = 16, + .ops = &adm5120ser_ops, + .line = 0, + .flags = ASYNC_BOOT_AUTOCONF, + }, +#if (CONFIG_ADM5120_NR_UARTS > 1) + { + .iobase = ADM5120_UART_BASE1, + .irq = ADM5120_UART_IRQ1, + .uartclk = ADM5120_UARTCLK_FREQ, + .fifosize = 16, + .ops = &adm5120ser_ops, + .line = 1, + .flags = ASYNC_BOOT_AUTOCONF, + }, +#endif +}; + +static struct uart_driver adm5120ser_reg = { + .owner = THIS_MODULE, + .driver_name = "ttyS", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = CONFIG_ADM5120_NR_UARTS, + .cons = &adm5120_serconsole, +}; + +static int __init adm5120ser_init(void) +{ + int ret, i; + + ret = uart_register_driver(&adm5120ser_reg); + if (!ret) { + for (i = 0; i < CONFIG_ADM5120_NR_UARTS; i++) + uart_add_one_port(&adm5120ser_reg, &adm5120ser_ports[i]); + } + + return ret; +} + +__initcall(adm5120ser_init); diff --git a/target/linux/rb1xx-2.6/files/drivers/usb/host/adm5120-hcd.c b/target/linux/rb1xx-2.6/files/drivers/usb/host/adm5120-hcd.c new file mode 100644 index 0000000..b4a1899 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/drivers/usb/host/adm5120-hcd.c @@ -0,0 +1,790 @@ +/* + * HCD driver for ADM5120 SoC + * + * Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * + * Based on the ADMtek 2.4 driver + * (C) Copyright 2003 Junius Chen <juniusc@admtek.com.tw> + * Which again was based on the ohci and uhci drivers. + */ + +#include <linux/autoconf.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/usb.h> +#include <linux/platform_device.h> + +#include "../core/hcd.h" + +MODULE_DESCRIPTION("ADM5120 USB Host Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jeroen Vreeken (pe1rxq@amsat.org)"); + +#define ADMHCD_REG_CONTROL 0x00 +#define ADMHCD_REG_INTSTATUS 0x04 +#define ADMHCD_REG_INTENABLE 0x08 +#define ADMHCD_REG_HOSTCONTROL 0x10 +#define ADMHCD_REG_FMINTERVAL 0x18 +#define ADMHCD_REG_FMNUMBER 0x1c +#define ADMHCD_REG_LSTHRESH 0x70 +#define ADMHCD_REG_RHDESCR 0x74 +#define ADMHCD_REG_PORTSTATUS0 0x78 +#define ADMHCD_REG_PORTSTATUS1 0x7c +#define ADMHCD_REG_HOSTHEAD 0x80 + + +#define ADMHCD_NUMPORTS 2 + +#define ADMHCD_HOST_EN 0x00000001 /* Host enable */ +#define ADMHCD_SW_INTREQ 0x00000002 /* request software int */ +#define ADMHCD_SW_RESET 0x00000008 /* Reset */ + +#define ADMHCD_INT_TD 0x00100000 /* TD completed */ +#define ADMHCD_INT_SW 0x20000000 /* software interrupt */ +#define ADMHCD_INT_FATAL 0x40000000 /* Fatal interrupt */ +#define ADMHCD_INT_ACT 0x80000000 /* Interrupt active */ + +#define ADMHCD_STATE_RST 0x00000000 /* bus state reset */ +#define ADMHCD_STATE_RES 0x00000001 /* bus state resume */ +#define ADMHCD_STATE_OP 0x00000002 /* bus state operational */ +#define ADMHCD_STATE_SUS 0x00000003 /* bus state suspended */ +#define ADMHCD_DMA_EN 0x00000004 /* enable dma engine */ + +#define ADMHCD_NPS 0x00000020 /* No Power Switch */ +#define ADMHCD_LPSC 0x04000000 /* Local power switch change */ + +#define ADMHCD_CCS 0x00000001 /* current connect status */ +#define ADMHCD_PES 0x00000002 /* port enable status */ +#define ADMHCD_PSS 0x00000004 /* port suspend status */ +#define ADMHCD_POCI 0x00000008 /* port overcurrent indicator */ +#define ADMHCD_PRS 0x00000010 /* port reset status */ +#define ADMHCD_PPS 0x00000100 /* port power status */ +#define ADMHCD_LSDA 0x00000200 /* low speed device attached */ +#define ADMHCD_CSC 0x00010000 /* connect status change */ +#define ADMHCD_PESC 0x00020000 /* enable status change */ +#define ADMHCD_PSSC 0x00040000 /* suspend status change */ +#define ADMHCD_OCIC 0x00080000 /* overcurrent change*/ +#define ADMHCD_PRSC 0x00100000 /* reset status change */ + + +struct admhcd_ed { + /* Don't change first four, they used for DMA */ + u32 control; + struct admhcd_td *tail; + struct admhcd_td *head; + struct admhcd_ed *next; + /* the rest is for the driver only: */ + struct admhcd_td *cur; + struct usb_host_endpoint *ep; + struct urb *urb; + struct admhcd_ed *real; +} __attribute__ ((packed)); + +#define ADMHCD_ED_EPSHIFT 7 /* Shift for endpoint number */ +#define ADMHCD_ED_INT 0x00000800 /* Is this an int endpoint */ +#define ADMHCD_ED_SPEED 0x00002000 /* Is it a high speed dev? */ +#define ADMHCD_ED_SKIP 0x00004000 /* Skip this ED */ +#define ADMHCD_ED_FORMAT 0x00008000 /* Is this an isoc endpoint */ +#define ADMHCD_ED_MAXSHIFT 16 /* Shift for max packet size */ + +struct admhcd_td { + /* Don't change first four, they are used for DMA */ + u32 control; + u32 buffer; + u32 buflen; + struct admhcd_td *next; + /* the rest is for the driver only: */ + struct urb *urb; + struct admhcd_td *real; +} __attribute__ ((packed)); + +#define ADMHCD_TD_OWN 0x80000000 +#define ADMHCD_TD_TOGGLE 0x00000000 +#define ADMHCD_TD_DATA0 0x01000000 +#define ADMHCD_TD_DATA1 0x01800000 +#define ADMHCD_TD_OUT 0x00200000 +#define ADMHCD_TD_IN 0x00400000 +#define ADMHCD_TD_SETUP 0x00000000 +#define ADMHCD_TD_ISO 0x00010000 +#define ADMHCD_TD_R 0x00040000 +#define ADMHCD_TD_INTEN 0x00010000 + +static int admhcd_td_err[16] = { + 0, /* No */ + -EREMOTEIO, /* CRC */ + -EREMOTEIO, /* bit stuff */ + -EREMOTEIO, /* data toggle */ + -EPIPE, /* stall */ + -ETIMEDOUT, /* timeout */ + -EPROTO, /* pid err */ + -EPROTO, /* unexpected pid */ + -EREMOTEIO, /* data overrun */ + -EREMOTEIO, /* data underrun */ + -ETIMEDOUT, /* 1010 */ + -ETIMEDOUT, /* 1011 */ + -EREMOTEIO, /* buffer overrun */ + -EREMOTEIO, /* buffer underrun */ + -ETIMEDOUT, /* 1110 */ + -ETIMEDOUT, /* 1111 */ +}; + +#define ADMHCD_TD_ERRMASK 0x38000000 +#define ADMHCD_TD_ERRSHIFT 27 + +#define TD(td) ((struct admhcd_td *)(((u32)(td)) & ~0xf)) +#define ED(ed) ((struct admhcd_ed *)(((u32)(ed)) & ~0xf)) + +struct admhcd { + u32 base; + u32 dma_en; + spinlock_t lock; + unsigned long flags; +}; + +#define hcd_to_admhcd(hcd) ((struct admhcd *)(hcd)->hcd_priv) + +static char hcd_name[] = "adm5120-hcd"; + +static u32 admhcd_reg_get(struct admhcd *ahcd, int reg) +{ + return *(volatile u32 *)KSEG1ADDR(ahcd->base+reg); +} + +static void admhcd_reg_set(struct admhcd *ahcd, int reg, u32 val) +{ + *(volatile u32 *)KSEG1ADDR(ahcd->base+reg) = val; +} + +static void admhcd_lock(struct admhcd *ahcd) +{ + spin_lock_irqsave(&ahcd->lock, ahcd->flags); + ahcd->dma_en = admhcd_reg_get(ahcd, ADMHCD_REG_HOSTCONTROL) & + ADMHCD_DMA_EN; + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP); +} + +static void admhcd_unlock(struct admhcd *ahcd) +{ + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, + ADMHCD_STATE_OP | ahcd->dma_en); + spin_unlock_irqrestore(&ahcd->lock, ahcd->flags); +} + +static struct admhcd_td *admhcd_td_alloc(struct admhcd_ed *ed, struct urb *urb) +{ + struct admhcd_td *tdn, *td; + + tdn = kmalloc(sizeof(struct admhcd_td), GFP_ATOMIC); + if (!tdn) + return NULL; + tdn->real = tdn; + tdn = (struct admhcd_td *)KSEG1ADDR(tdn); + memset(tdn, 0, sizeof(struct admhcd_td)); + if (ed->cur == NULL) { + ed->cur = tdn; + ed->head = tdn; + ed->tail = tdn; + td = tdn; + } else { + /* Supply back the old tail and link in new td as tail */ + td = TD(ed->tail); + TD(ed->tail)->next = tdn; + ed->tail = tdn; + } + td->urb = urb; + + return td; +} + +static void admhcd_td_free(struct admhcd_ed *ed, struct urb *urb) +{ + struct admhcd_td *td, **tdp; + + if (urb == NULL) + ed->control |= ADMHCD_ED_SKIP; + tdp = &ed->cur; + td = ed->cur; + do { + if (td->urb == urb) + break; + tdp = &td->next; + td = TD(td->next); + } while (td); + while (td && td->urb == urb) { + *tdp = TD(td->next); + kfree(td->real); + td = *tdp; + } +} + +/* Find an endpoint's descriptor, if needed allocate a new one and link it + in the DMA chain + */ +static struct admhcd_ed *admhcd_get_ed(struct admhcd *ahcd, + struct usb_host_endpoint *ep, struct urb *urb) +{ + struct admhcd_ed *hosthead; + struct admhcd_ed *found = NULL, *ed = NULL; + unsigned int pipe = urb->pipe; + + admhcd_lock(ahcd); + hosthead = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD); + if (hosthead) { + for (ed = hosthead;; ed = ED(ed->next)) { + if (ed->ep == ep) { + found = ed; + break; + } + if (ED(ed->next) == hosthead) + break; + } + } + if (!found) { + found = kmalloc(sizeof(struct admhcd_ed), GFP_ATOMIC); + if (!found) + goto out; + memset(found, 0, sizeof(struct admhcd_ed)); + found->real = found; + found->ep = ep; + found = (struct admhcd_ed *)KSEG1ADDR(found); + found->control = usb_pipedevice(pipe) | + (usb_pipeendpoint(pipe) << ADMHCD_ED_EPSHIFT) | + (usb_pipeint(pipe) ? ADMHCD_ED_INT : 0) | + (urb->dev->speed == USB_SPEED_FULL ? ADMHCD_ED_SPEED : 0) | + (usb_pipeisoc(pipe) ? ADMHCD_ED_FORMAT : 0) | + (usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe)) << ADMHCD_ED_MAXSHIFT); + /* Alloc first dummy td */ + admhcd_td_alloc(found, NULL); + if (hosthead) { + found->next = hosthead; + ed->next = found; + } else { + found->next = found; + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, (u32)found); + } + } +out: + admhcd_unlock(ahcd); + return found; +} + +static struct admhcd_td *admhcd_td_fill(u32 control, struct admhcd_td *td, + dma_addr_t data, int len) +{ + td->buffer = data; + td->buflen = len; + td->control = control; + return TD(td->next); +} + +static void admhcd_ed_start(struct admhcd *ahcd, struct admhcd_ed *ed) +{ + struct admhcd_td *td = ed->cur; + + if (ed->urb) + return; + if (td->urb) { + ed->urb = td->urb; + while (1) { + td->control |= ADMHCD_TD_OWN; + if (TD(td->next)->urb != td->urb) { + td->buflen |= ADMHCD_TD_INTEN; + break; + } + td = TD(td->next); + } + } + ed->head = TD(ed->head); + ahcd->dma_en |= ADMHCD_DMA_EN; +} + +static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs) +{ + struct usb_hcd *hcd = (struct usb_hcd *)ptr; + struct admhcd *ahcd = hcd_to_admhcd(hcd); + u32 intstatus; + + intstatus = admhcd_reg_get(ahcd, ADMHCD_REG_INTSTATUS); + if (intstatus & ADMHCD_INT_FATAL) { + admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_FATAL); + // + } + if (intstatus & ADMHCD_INT_SW) { + admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_SW); + // + } + if (intstatus & ADMHCD_INT_TD) { + struct admhcd_ed *ed, *head; + + admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_TD); + + head = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD); + ed = head; + if (ed) do { + /* Is it a finished TD? */ + if (ed->urb && !(ed->cur->control & ADMHCD_TD_OWN)) { + struct admhcd_td *td; + int error; + + td = ed->cur; + error = (td->control & ADMHCD_TD_ERRMASK) >> + ADMHCD_TD_ERRSHIFT; + ed->urb->status = admhcd_td_err[error]; + admhcd_td_free(ed, ed->urb); + // Calculate real length!!! + ed->urb->actual_length = ed->urb->transfer_buffer_length; + ed->urb->hcpriv = NULL; + usb_hcd_giveback_urb(hcd, ed->urb); + ed->urb = NULL; + } + admhcd_ed_start(ahcd, ed); + ed = ED(ed->next); + } while (ed != head); + } + + return IRQ_HANDLED; +} + +static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep, + struct urb *urb, int mem_flags) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + struct admhcd_ed *ed; + struct admhcd_td *td; + int size = 0, i, zero = 0, ret = 0; + unsigned int pipe = urb->pipe, toggle = 0; + dma_addr_t data = (dma_addr_t)urb->transfer_buffer; + int data_len = urb->transfer_buffer_length; + + ed = admhcd_get_ed(ahcd, ep, urb); + if (!ed) + return -ENOMEM; + + switch(usb_pipetype(pipe)) { + case PIPE_CONTROL: + size = 2; + case PIPE_INTERRUPT: + case PIPE_BULK: + default: + size += urb->transfer_buffer_length / 4096; + if (urb->transfer_buffer_length % 4096) + size++; + if (size == 0) + size++; + else if (urb->transfer_flags & URB_ZERO_PACKET && + !(urb->transfer_buffer_length % + usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe)))) { + size++; + zero = 1; + } + break; + case PIPE_ISOCHRONOUS: + size = urb->number_of_packets; + break; + } + + admhcd_lock(ahcd); + /* Remember the first td */ + td = admhcd_td_alloc(ed, urb); + if (!td) { + ret = -ENOMEM; + goto out; + } + /* Allocate additionall tds first */ + for (i = 1; i < size; i++) { + if (admhcd_td_alloc(ed, urb) == NULL) { + admhcd_td_free(ed, urb); + ret = -ENOMEM; + goto out; + } + } + + if (usb_gettoggle(urb->dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) + toggle = ADMHCD_TD_TOGGLE; + else { + toggle = ADMHCD_TD_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), 1); + } + + switch(usb_pipetype(pipe)) { + case PIPE_CONTROL: + td = admhcd_td_fill(ADMHCD_TD_SETUP | ADMHCD_TD_DATA0, + td, (dma_addr_t)urb->setup_packet, 8); + while (data_len > 0) { + td = admhcd_td_fill(ADMHCD_TD_DATA1 + | ADMHCD_TD_R | + (usb_pipeout(pipe) ? + ADMHCD_TD_OUT : ADMHCD_TD_IN), td, + data, data_len % 4097); + data_len -= 4096; + } + admhcd_td_fill(ADMHCD_TD_DATA1 | (usb_pipeout(pipe) ? + ADMHCD_TD_IN : ADMHCD_TD_OUT), td, + data, 0); + break; + case PIPE_INTERRUPT: + case PIPE_BULK: + //info ok for interrupt? + i = 0; + while(data_len > 4096) { + td = admhcd_td_fill((usb_pipeout(pipe) ? + ADMHCD_TD_OUT : + ADMHCD_TD_IN | ADMHCD_TD_R) | + (i ? ADMHCD_TD_TOGGLE : toggle), td, + data, 4096); + data += 4096; + data_len -= 4096; + i++; + } + td = admhcd_td_fill((usb_pipeout(pipe) ? + ADMHCD_TD_OUT : ADMHCD_TD_IN) | + (i ? ADMHCD_TD_TOGGLE : toggle), td, data, data_len); + i++; + if (zero) + admhcd_td_fill((usb_pipeout(pipe) ? + ADMHCD_TD_OUT : ADMHCD_TD_IN) | + (i ? ADMHCD_TD_TOGGLE : toggle), td, 0, 0); + break; + case PIPE_ISOCHRONOUS: + for (i = 0; i < urb->number_of_packets; i++) { + td = admhcd_td_fill(ADMHCD_TD_ISO | + ((urb->start_frame + i) & 0xffff), td, + data + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].length); + } + break; + } + urb->hcpriv = ed; + admhcd_ed_start(ahcd, ed); +out: + admhcd_unlock(ahcd); + return ret; +} + +static int admhcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + struct admhcd_ed *ed; + + admhcd_lock(ahcd); + + ed = urb->hcpriv; + if (ed && ed->urb != urb) + admhcd_td_free(ed, urb); + + admhcd_unlock(ahcd); + return 0; +} + +static void admhcd_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + struct admhcd_ed *ed, *edt, *head; + + admhcd_lock(ahcd); + + head = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD); + if (!head) + goto out; + for (ed = head; ED(ed->next) != head; ed = ED(ed->next)) + if (ed->ep == ep) + break; + if (ed->ep != ep) + goto out; + while (ed->cur) + admhcd_td_free(ed, ed->cur->urb); + if (head == ed) { + if (ED(ed->next) == ed) { + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0); + ahcd->dma_en = 0; + goto out_free; + } + head = ED(ed->next); + for (edt = head; ED(edt->next) != head; edt = ED(edt->next)); + edt->next = ED(ed->next); + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, (u32)ed->next); + goto out_free; + } + for (edt = head; edt->next != ed; edt = edt->next); + edt->next = ed->next; +out_free: + kfree(ed->real); +out: + admhcd_unlock(ahcd); +} + +static int admhcd_get_frame_number(struct usb_hcd *hcd) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + + return admhcd_reg_get(ahcd, ADMHCD_REG_FMNUMBER) & 0x0000ffff; +} + +static int admhcd_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + int port; + + *buf = 0; + for (port = 0; port < ADMHCD_NUMPORTS; port++) { + if (admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4) & + (ADMHCD_CSC | ADMHCD_PESC | ADMHCD_PSSC | ADMHCD_OCIC | + ADMHCD_PRSC)) + *buf |= (1 << (port + 1)); + } + return !!*buf; +} + +static __u8 root_hub_hub_des[] = { + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x0a, 0x00, /* __u16 wHubCharacteristics; */ + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0mA */ + 0x00, /* __u8 DeviceRemovable; */ + 0xff, /* __u8 PortPwrCtrlMask; */ +}; + +static int admhcd_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct admhcd *ahcd = hcd_to_admhcd(hcd); + int retval = 0, len; + unsigned int port = wIndex -1; + + switch (typeReq) { + + case GetHubStatus: + *(__le32 *)buf = cpu_to_le32(0); + break; + case GetPortStatus: + if (port >= ADMHCD_NUMPORTS) + goto err; + *(__le32 *)buf = cpu_to_le32( + admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4)); + break; + case SetHubFeature: /* We don't implement these */ + case ClearHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto err; + } + case SetPortFeature: + if (port >= ADMHCD_NUMPORTS) + goto err; + + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PSS); + break; + case USB_PORT_FEAT_RESET: + if (admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4) + & ADMHCD_CCS) { + admhcd_reg_set(ahcd, + ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PRS | ADMHCD_CSC); + mdelay(50); + admhcd_reg_set(ahcd, + ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PES | ADMHCD_CSC); + } + break; + case USB_PORT_FEAT_POWER: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PPS); + break; + default: + goto err; + } + break; + case ClearPortFeature: + if (port >= ADMHCD_NUMPORTS) + goto err; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_CCS); + break; + case USB_PORT_FEAT_C_ENABLE: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PESC); + break; + case USB_PORT_FEAT_SUSPEND: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_POCI); + break; + case USB_PORT_FEAT_C_SUSPEND: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PSSC); + case USB_PORT_FEAT_POWER: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_LSDA); + break; + case USB_PORT_FEAT_C_CONNECTION: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_CSC); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_OCIC); + break; + case USB_PORT_FEAT_C_RESET: + admhcd_reg_set(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4, + ADMHCD_PRSC); + break; + default: + goto err; + } + break; + case GetHubDescriptor: + len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength); + memcpy(buf, root_hub_hub_des, len); + break; + default: +err: + retval = -EPIPE; + } + + return retval; +} + +static struct hc_driver adm5120_hc_driver = { + .description = hcd_name, + .product_desc = "ADM5120 HCD", + .hcd_priv_size = sizeof(struct admhcd), + .flags = HCD_USB11, + .urb_enqueue = admhcd_urb_enqueue, + .urb_dequeue = admhcd_urb_dequeue, + .endpoint_disable = admhcd_endpoint_disable, + .get_frame_number = admhcd_get_frame_number, + .hub_status_data = admhcd_hub_status_data, + .hub_control = admhcd_hub_control, +}; + +static int __init adm5120hcd_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct admhcd *ahcd; + struct usb_device *udev; + int err = 0; + + if (!request_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start, hcd_name)) { + pr_debug("couldn't request mem\n"); + err = -EBUSY; + goto out; + } + + //hcd = usb_create_hcd(&adm5120_hc_driver, pdev, pdev->bus_id); + if (!hcd) + goto out_mem; + + ahcd = hcd_to_admhcd(hcd); + dev_set_drvdata(pdev, ahcd); + hcd->self.controller = pdev; + //hcd->self.bus_name = pdev->bus_id; + hcd->irq = pdev->resource[1].start; + hcd->regs = (void *)pdev->resource[0].start; + hcd->product_desc = hcd_name; + ahcd->base = pdev->resource[0].start; + + if (request_irq(pdev->resource[1].start, adm5120hcd_irq, 0, hcd_name, + hcd)) { + pr_debug("couldn't request irq\n"); + err = -EBUSY; + goto out_hcd; + } + + //err = usb_register_bus(&hcd->self); + //if (err < 0) + // goto out_irq; + + spin_lock_init(&ahcd->lock); + + admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0); + mdelay(10); + admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET); + while (admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET) + mdelay(1); + + admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_HOST_EN); + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0x00000000); + admhcd_reg_set(ahcd, ADMHCD_REG_FMINTERVAL, 0x20002edf); + admhcd_reg_set(ahcd, ADMHCD_REG_LSTHRESH, 0x628); + admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, + ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD); + admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, + ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD); + admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, ADMHCD_NPS | ADMHCD_LPSC); + admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP); + + udev = usb_alloc_dev(NULL, &hcd->self, 0); + if (!udev) { + err = -ENOMEM; + goto out_bus; + } + + udev->speed = USB_SPEED_FULL; + hcd->state = HC_STATE_RUNNING; + + //err = hcd_register_root(udev, hcd); + //if (err != 0) { + // usb_put_dev(udev); + // goto out_dev; + //} + + return 0; + +out_dev: + usb_put_dev(udev); +out_bus: + //usb_deregister_bus(&hcd->self); +out_irq: + free_irq(pdev->resource[1].start, hcd); +out_hcd: + usb_put_hcd(hcd); +out_mem: + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start); +out: + return err; +} + +static int __init_or_module adm5120hcd_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); +} + +static struct platform_driver adm5120hcd_driver = { + .probe = adm5120hcd_probe, + .remove = adm5120hcd_remove, + .driver = { + .name = "adm5120-hcd", + .owner = THIS_MODULE, + }, +}; + +static int __init adm5120hcd_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + return platform_driver_register(&adm5120hcd_driver); +} + +static void __exit adm5120hcd_exit(void) +{ + platform_driver_unregister(&adm5120hcd_driver); +} + +module_init(adm5120hcd_init); +module_exit(adm5120hcd_exit); diff --git a/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/adm5120_defs.h b/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/adm5120_defs.h new file mode 100644 index 0000000..36979c2 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/adm5120_defs.h @@ -0,0 +1,60 @@ +/* + * $Id$ + * + * ADM5120 SoC definitions + * + * This file defines some constants specific to the ADM5120 SoC + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef _ADM5120_DEFS_H +#define _ADM5120_DEFS_H + +#define ADM5120_SDRAM0_BASE 0x00000000 +#define ADM5120_SDRAM1_BASE 0x01000000 +#define ADM5120_SRAM1_BASE 0x10000000 +#define ADM5120_MPMC_BASE 0x11000000 +#define ADM5120_USBC_BASE 0x11200000 +#define ADM5120_PCIMEM_BASE 0x11400000 +#define ADM5120_PCIIO_BASE 0x11500000 +#define ADM5120_PCICFG_ADDR 0x115FFFF0 +#define ADM5120_PCICFG_DATA 0x115FFFF8 +#define ADM5120_SWITCH_BASE 0x12000000 +#define ADM5120_INTC_BASE 0x12200000 +#define ADM5120_UART0_BASE 0x12600000 +#define ADM5120_UART1_BASE 0x12800000 +#define ADM5120_SRAM0_BASE 0x1FC00000 + +#define ADM5120_MPMC_SIZE 0x1000 +#define ADM5120_USBC_SIZE 0x84 +#define ADM5120_PCIMEM_SIZE (ADM5120_PCIIO_BASE - ADM5120_PCIMEM_BASE) +#define ADM5120_PCIIO_SIZE (ADM5120_PCICFG_ADDR - ADM5120_PCIIO_BASE) +#define ADM5120_PCICFG_SIZE 0x10 +#define ADM5120_SWITCH_SIZE 0x114 +#define ADM5120_INTC_SIZE 0x28 +#define ADM5120_UART_SIZE 0x20 + +#define ADM5120_CLK_175 175000000 +#define ADM5120_CLK_200 200000000 +#define ADM5120_CLK_225 225000000 +#define ADM5120_CLK_250 250000000 + +#define ADM5120_UART_CLOCK 62500000 + +#endif /* _ADM5120_DEFS_H */ diff --git a/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/adm5120_info.h b/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/adm5120_info.h new file mode 100644 index 0000000..df1f24d --- /dev/null +++ b/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/adm5120_info.h @@ -0,0 +1,70 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) Gabor Juhos <juhosg@freemail.hu> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _ADM5120_INFO_H +#define _ADM5120_INFO_H + +#include <linux/types.h> + +struct adm5120_info { + unsigned int product_code; + unsigned int revision; + unsigned int cpu_package; + unsigned int nand_boot; + unsigned long cpu_speed; + unsigned int boot_loader; + unsigned int board_type; + unsigned int num_iface; +}; + +#define CPU_SPEED_175 175000000 +#define CPU_SPEED_200 200000000 +#define CPU_SPEED_225 225000000 +#define CPU_SPEED_250 250000000 + +#define CPU_PACKAGE_PQFP 0 +#define CPU_PACKAGE_BGA 1 + +#define BOOT_LOADER_UNKNOWN 0 +#define BOOT_LOADER_CFE 1 +#define BOOT_LOADER_UBOOT 2 +#define BOOT_LOADER_MYLOADER 3 +#define BOOT_LOADER_ROUTERBOOT 4 +#define BOOT_LOADER_LAST 4 + +#define BOARD_TYPE_UNKNOWN 0 +#define BOARD_TYPE_WP54G_WRT 1 +#define BOARD_TYPE_WP54G 2 +#define BOARD_TYPE_WP54AG 3 +#define BOARD_TYPE_WPP54G 4 +#define BOARD_TYPE_WPP54AG 5 +#define BOARD_TYPE_NP28G 6 +#define BOARD_TYPE_NP28GHS 7 +#define BOARD_TYPE_NP27G 8 +#define BOARD_TYPE_WP54Gv1C 9 +#define BOARD_TYPE_RB_111 10 +#define BOARD_TYPE_RB_112 11 +#define BOARD_TYPE_RB_133 12 +#define BOARD_TYPE_RB_133C 13 +#define BOARD_TYPE_RB_150 14 +#define BOARD_TYPE_RB_153 15 +#define BOART_TYPE_LAST 15 + +extern struct adm5120_info adm5120_info; +extern void adm5120_info_init(void); + +static inline int adm5120_has_pci(void) +{ + return (adm5120_info.cpu_package == CPU_PACKAGE_BGA); +} + +#endif /* _ADM5120_INFO_H */ diff --git a/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/adm5120_switch.h b/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/adm5120_switch.h new file mode 100644 index 0000000..52ea79e --- /dev/null +++ b/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/adm5120_switch.h @@ -0,0 +1,89 @@ +/* + * ADM5120 ethernet switch definitions + * + * This header file defines the hardware registers of the ADM5120 SoC + * built-in Ethernet switch. + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ADM5120_SWITCH_H +#define _ADM5120_SWITCH_H + +#define BITMASK(len) ((1 << (len))-1) +#define ONEBIT(at) (1 << (at)) + +/* Switch register offsets */ +#define SWITCH_REG_CODE 0x0000 +#define SWITCH_REG_SOFT_RESET 0x0004 +#define SWITCH_REG_MEMCTRL 0x001C +#define SWITCH_REG_CPUP_CONF 0x0024 +#define SWITCH_REG_PORT_CONF0 0x0028 +#define SWITCH_REG_PORT_CONF1 0x002C +#define SWITCH_REG_PORT_CONF2 0x0030 +#define SWITCH_REG_VLAN_G1 0x0040 +#define SWITCH_REG_VLAN_G2 0x0044 +#define SWITCH_REG_SEND_TRIG 0x0048 +#define SWITCH_REG_MAC_WT0 0x0058 +#define SWITCH_REG_MAC_WT1 0x005C +#define SWITCH_REG_PHY_CNTL0 0x0068 +#define SWITCH_REG_PHY_CNTL1 0x006C +#define SWITCH_REG_PHY_CNTL2 0x007C +#define SWITCH_REG_PHY_CNTL3 0x0080 +#define SWITCH_REG_PRI_CNTL 0x0084 +#define SWITCH_REG_INT_STATUS 0x00B0 +#define SWITCH_REG_INT_MASK 0x00B4 +#define SWITCH_REG_GPIO_CONF0 0x00B8 +#define SWITCH_REG_GPIO_CONF2 0x00BC +#define SWITCH_REG_WDOG0 0x00C0 +#define SWITCH_REG_WDOG1 0x00C4 +#define SWITCH_REG_PHY_CNTL4 0x00A0 + +#define SWITCH_REG_SEND_HBADDR 0x00D0 +#define SWITCH_REG_SEND_LBADDR 0x00D4 +#define SWITCH_REG_RECV_HBADDR 0x00D8 +#define SWITCH_REG_RECV_LBADDR 0x00DC + +#define SWITCH_REG_TIMER_INT 0x00F0 +#define SWITCH_REG_TIMER 0x00F4 + +#define SWITCH_REG_PORT0_LED 0x0100 +#define SWITCH_REG_PORT1_LED 0x0104 +#define SWITCH_REG_PORT2_LED 0x0108 +#define SWITCH_REG_PORT3_LED 0x010C +#define SWITCH_REG_PORT4_LED 0x0110 + +/* CODE register bits */ +#define CODE_PC_MASK BITMASK(16) /* Product Code */ +#define CODE_REV_SHIFT 16 +#define CODE_REV_MASK BITMASK(4) /* Product Revision */ +#define CODE_CLKS_SHIFT 20 +#define CODE_CLKS_MASK BITMASK(2) /* Clock Speed */ +#define CODE_CLKS_175 0 /* 175 MHz */ +#define CODE_CLKS_200 1 /* 200 MHz */ +#define CODE_CLKS_225 2 /* 225 MHz */ +#define CODE_CLKS_250 3 /* 250 MHz */ +#define CODE_NAB ONEBIT(24) /* NAND boot */ +#define CODE_PK_MASK BITMASK(1) /* Package type */ +#define CODE_PK_SHIFT 29 +#define CODE_PK_BGA 0 /* BGA package */ +#define CODE_PK_PQFP 1 /* PQFP package */ + + +#endif /* _ADM5120_SWITCH_H */ diff --git a/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/myloader.h b/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/myloader.h new file mode 100644 index 0000000..3c0c602 --- /dev/null +++ b/target/linux/rb1xx-2.6/files/include/asm-mips/mach-adm5120/myloader.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2006,2007 Gabor Juhos + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _MYLOADER_H_ +#define _MYLOADER_H_ + +/* + * Firmware file format: + * + * <header> + * [<block descriptor 0>] + * ... + * [<block descriptor n>] + * <null block descriptor> + * [<block data 0>] + * ... + * [<block data n>] + * + * + */ + +/* Myloader specific magic numbers */ +#define MYLO_MAGIC_FIRMWARE 0x4C594D00 +#define MYLO_MAGIC_20021103 0x20021103 +#define MYLO_MAGIC_20021107 0x20021107 + +#define MYLO_MAGIC_SYS_PARAMS MYLO_MAGIC_20021107 +#define MYLO_MAGIC_PARTITIONS MYLO_MAGIC_20021103 +#define MYLO_MAGIC_BOARD_PARAMS MYLO_MAGIC_20021103 + +/* + * Addresses of the data structures provided by MyLoader + */ +#define MYLO_MIPS_SYS_PARAMS 0x80000800 /* System Parameters */ +#define MYLO_MIPS_BOARD_PARAMS 0x80000A00 /* Board Parameters */ +#define MYLO_MIPS_PARTITIONS 0x80000C00 /* Partition Table */ +#define MYLO_MIPS_BOOT_PARAMS 0x80000E00 /* Boot Parameters */ + +/* Vendor ID's (seems to be same as the PCI vendor ID's) */ +#define VENID_COMPEX 0x11F6 + +/* Devices based on the ADM5120 */ +#define DEVID_COMPEX_NP27G 0x0078 +#define DEVID_COMPEX_NP28G 0x044C +#define DEVID_COMPEX_NP28GHS 0x044E +#define DEVID_COMPEX_WP54Gv1C 0x0514 +#define DEVID_COMPEX_WP54G 0x0515 +#define DEVID_COMPEX_WP54AG 0x0546 +#define DEVID_COMPEX_WPP54AG 0x0550 +#define DEVID_COMPEX_WPP54G 0x0555 + +/* Devices based on the IXP422 */ +#define DEVID_COMPEX_WP18 0x047E +#define DEVID_COMPEX_NP18A 0x0489 + +/* Other devices */ +#define DEVID_COMPEX_NP26G8M 0x03E8 +#define DEVID_COMPEX_NP26G16M 0x03E9 + +struct mylo_fw_header { + uint32_t magic; /* must be MYLO_MAGIC_FIRMWARE */ + uint32_t crc; /* CRC of the whole firmware */ + uint32_t res0; /* unknown/unused */ + uint32_t res1; /* unknown/unused */ + uint16_t vid; /* vendor ID */ + uint16_t did; /* device ID */ + uint16_t svid; /* sub vendor ID */ + uint16_t sdid; /* sub device ID */ + uint32_t rev; /* device revision */ + uint32_t fwhi; /* FIXME: firmware version high? */ + uint32_t fwlo; /* FIXME: firmware version low? */ + uint32_t flags; /* firmware flags */ +}; + +#define FW_FLAG_BOARD_PARAMS_WP 0x01 /* board parameters are write protected */ +#define FW_FLAG_BOOT_SECTOR_WE 0x02 /* enable of write boot sectors (below 64K) */ + +struct mylo_fw_blockdesc { + uint32_t type; /* block type */ + uint32_t addr; /* relative address to flash start */ + uint32_t dlen; /* size of block data in bytes */ + uint32_t blen; /* total size of block in bytes */ +}; + +#define FW_DESC_TYPE_UNUSED 0 +#define FW_DESC_TYPE_USED 1 + +struct mylo_partition { + uint16_t flags; /* partition flags */ + uint16_t type; /* type of the partition */ + uint32_t addr; /* relative address of the partition from the + flash start */ + uint32_t size; /* size of the partition in bytes */ + uint32_t param; /* if this is the active partition, the + MyLoader load code to this address */ +}; + +#define PARTITION_FLAG_ACTIVE 0x8000 /* this is the active partition, + * MyLoader loads firmware from here */ +#define PARTITION_FLAG_ISRAM 0x2000 /* FIXME: this is a RAM partition? */ +#define PARTIIION_FLAG_RAMLOAD 0x1000 /* FIXME: load this partition into the RAM? */ +#define PARTITION_FLAG_PRELOAD 0x0800 /* the partition data preloaded to RAM + * before decompression */ +#define PARTITION_FLAG_HAVEHDR 0x0002 /* the partition data have a header */ + +#define PARTITION_TYPE_FREE 0 +#define PARTITION_TYPE_USED 1 + +#define MYLO_MAX_PARTITIONS 8 /* maximum number of partitions in the + partition table */ + +struct mylo_partition_table { + uint32_t magic; /* must be MYLO_MAGIC_PARTITIONS */ + uint32_t res0; /* unknown/unused */ + uint32_t res1; /* unknown/unused */ + uint32_t res2; /* unknown/unused */ + struct mylo_partition partitions[MYLO_MAX_PARTITIONS]; +}; + +struct mylo_partition_header { + uint32_t len; /* length of the partition data */ + uint32_t crc; /* CRC value of the partition data */ +}; + +struct mylo_system_params { + uint32_t magic; /* must be MYLO_MAGIC_SYS_PARAMS */ + uint32_t res0; + uint32_t res1; + uint32_t mylo_ver; + uint16_t vid; /* Vendor ID */ + uint16_t did; /* Device ID */ + uint16_t svid; /* Sub Vendor ID */ + uint16_t sdid; /* Sub Device ID */ + uint32_t rev; /* device revision */ + uint32_t fwhi; + uint32_t fwlo; + uint32_t tftp_addr; + uint32_t prog_start; + uint32_t flash_size; /* Size of boot FLASH in bytes */ + uint32_t dram_size; /* Size of onboard RAM in bytes */ +}; + + +struct mylo_eth_addr { + uint8_t mac[6]; + uint8_t csum[2]; +}; + +#define MYLO_ETHADDR_COUNT 8 /* maximum number of ethernet address + in the board parameters */ + +struct mylo_board_params { + uint32_t magic; /* must be MYLO_MAGIC_BOARD_PARAMS */ + uint32_t res0; + uint32_t res1; + uint32_t res2; + struct mylo_eth_addr addr[MYLO_ETHADDR_COUNT]; +}; + +#endif /* _MYLOADER_H_*/ diff --git a/target/linux/rb1xx-2.6/image/Makefile b/target/linux/rb1xx-2.6/image/Makefile new file mode 100644 index 0000000..e920a03 --- /dev/null +++ b/target/linux/rb1xx-2.6/image/Makefile @@ -0,0 +1,57 @@ +# +# Copyright (C) 2006 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/image.mk +include $(INCLUDE_DIR)/kernel.mk + +LOADADDR = 0x81000000 # RAM start + 16M +KERNEL_ENTRY = 0x80101000 +RAMSIZE = 0x00100000 # 1MB +IMAGE_COPY = 1 + +LOADER_MAKEOPTS= \ + KDIR=$(KDIR) \ + LOADADDR=$(LOADADDR) \ + KERNEL_ENTRY=$(KERNEL_ENTRY) \ + RAMSIZE=$(RAMSIZE) \ + IMAGE_COPY=$(IMAGE_COPY) + +#define Build/Compile +# $(CC) -o $(STAGING_DIR)/bin/patch-cmdline $(PLATFORM_DIR)/src/patch-cmdline.c +#endef + +define Build/Clean + $(MAKE) -C $(GENERIC_PLATFORM_DIR)/image/lzma-loader $(LOADER_MAKEOPTS) clean +endef + +define Image/Prepare + cat $(KDIR)/vmlinux | $(STAGING_DIR)/bin/lzma e -si -so -eos -lc1 -lp2 -pb2 > $(KDIR)/vmlinux.lzma + $(MAKE) -C $(GENERIC_PLATFORM_DIR)/image/lzma-loader $(LOADER_MAKEOPTS) clean compile +endef + +define Image/BuildKernel + $(CP) $(KDIR)/loader.elf $(BIN_DIR)/openwrt-$(BOARD)-$(KERNEL)-vmlinux +endef + +define Image/cmdline/jffs2-64k +block2mtd.block2mtd=/dev/cfa2,65536 root=/dev/mtdblock0 rootfstype=jffs2 init=/etc/preinit +endef + +define Image/cmdline/jffs2-128k +block2mtd.block2mtd=/dev/cfa2,131072 root=/dev/mtdblock0 rootfstype=jffs2 init=/etc/preinit +endef + +define Image/cmdline/ext2 +root=/dev/cfa2 rootfstype=ext2 init=/etc/preinit +endef + +define Image/Build + #$(STAGING_DIR)/bin/patch-cmdline $(KDIR)/vmlinux.elf '$(strip $(call Image/cmdline/$(1))) ' + ./gen_image.sh $(BIN_DIR)/openwrt-$(BOARD)-$(KERNEL)-$(1).bin 4 $(KDIR)/vmlinux.elf $(CONFIG_TARGET_ROOTFS_FSPART) $(KDIR)/root.$(1) +endef + +$(eval $(call BuildImage)) diff --git a/target/linux/rb1xx-2.6/image/gen_image.sh b/target/linux/rb1xx-2.6/image/gen_image.sh new file mode 100755 index 0000000..e812859 --- /dev/null +++ b/target/linux/rb1xx-2.6/image/gen_image.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +OUTPUT="$1" +KERNELSIZE="$2" +KERNELIMAGE="$3" +ROOTFSSIZE="$4" +ROOTFSIMAGE="$5" + +rm -f "$OUTPUT" + +# create partition table +set `ptgen -o "$OUTPUT" -h 16 -s 32 -t 0x27 -p ${KERNELSIZE}m -t 0x83 -p ${ROOTFSSIZE}m` + +KERNELOFFSET="$(($1 / 512))" +ROOTFSOFFSET="$(($2 / 512))" + +dd if="$KERNELIMAGE" of="$OUTPUT" bs=512 conv=notrunc seek="$KERNELOFFSET" +dd if="$ROOTFSIMAGE" of="$OUTPUT" bs=512 conv=notrunc seek="$ROOTFSOFFSET" diff --git a/target/linux/rb1xx-2.6/image/lzma-loader/Makefile b/target/linux/rb1xx-2.6/image/lzma-loader/Makefile new file mode 100644 index 0000000..e2afb18 --- /dev/null +++ b/target/linux/rb1xx-2.6/image/lzma-loader/Makefile @@ -0,0 +1,34 @@ +# +# Copyright (C) 2006 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +# $Id: Makefile 4439 2006-08-04 17:14:36Z nbd $ + +include $(TOPDIR)/rules.mk + +PKG_NAME := lzma-loader +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +$(PKG_BUILD_DIR)/.prepared: + mkdir $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ + touch $@ + +$(PKG_BUILD_DIR)/loader.gz: $(PKG_BUILD_DIR)/.prepared + $(MAKE) -C $(PKG_BUILD_DIR) CC="$(TARGET_CC)" \ + LD="$(TARGET_CROSS)ld" CROSS_COMPILE="$(TARGET_CROSS)" + +download: +prepare: $(PKG_BUILD_DIR)/.prepared +compile: $(PKG_BUILD_DIR)/loader.gz +install: + +ifneq ($(TARGET),) +install: compile + $(CP) $(PKG_BUILD_DIR)/loader.gz $(PKG_BUILD_DIR)/loader.elf $(TARGET)/ +endif + +clean: + rm -rf $(PKG_BUILD_DIR) diff --git a/target/linux/rb1xx-2.6/image/lzma-loader/src/LzmaDecode.c b/target/linux/rb1xx-2.6/image/lzma-loader/src/LzmaDecode.c new file mode 100644 index 0000000..951700b --- /dev/null +++ b/target/linux/rb1xx-2.6/image/lzma-loader/src/LzmaDecode.c @@ -0,0 +1,663 @@ +/* + LzmaDecode.c + LZMA Decoder + + LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this code, expressly permits you to + statically or dynamically link your code (or bind by name) to the + interfaces of this file without subjecting your linked code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#include "LzmaDecode.h" + +#ifndef Byte +#define Byte unsigned char +#endif + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +typedef struct _CRangeDecoder +{ + Byte *Buffer; + Byte *BufferLim; + UInt32 Range; + UInt32 Code; + #ifdef _LZMA_IN_CB + ILzmaInCallback *InCallback; + int Result; + #endif + int ExtraBytes; +} CRangeDecoder; + +Byte RangeDecoderReadByte(CRangeDecoder *rd) +{ + if (rd->Buffer == rd->BufferLim) + { + #ifdef _LZMA_IN_CB + UInt32 size; + rd->Result = rd->InCallback->Read(rd->InCallback, &rd->Buffer, &size); + rd->BufferLim = rd->Buffer + size; + if (size == 0) + #endif + { + rd->ExtraBytes = 1; + return 0xFF; + } + } + return (*rd->Buffer++); +} + +/* #define ReadByte (*rd->Buffer++) */ +#define ReadByte (RangeDecoderReadByte(rd)) + +void RangeDecoderInit(CRangeDecoder *rd, + #ifdef _LZMA_IN_CB + ILzmaInCallback *inCallback + #else + Byte *stream, UInt32 bufferSize + #endif + ) +{ + int i; + #ifdef _LZMA_IN_CB + rd->InCallback = inCallback; + rd->Buffer = rd->BufferLim = 0; + #else + rd->Buffer = stream; + rd->BufferLim = stream + bufferSize; + #endif + rd->ExtraBytes = 0; + rd->Code = 0; + rd->Range = (0xFFFFFFFF); + for(i = 0; i < 5; i++) + rd->Code = (rd->Code << 8) | ReadByte; +} + +#define RC_INIT_VAR UInt32 range = rd->Range; UInt32 code = rd->Code; +#define RC_FLUSH_VAR rd->Range = range; rd->Code = code; +#define RC_NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | ReadByte; } + +UInt32 RangeDecoderDecodeDirectBits(CRangeDecoder *rd, int numTotalBits) +{ + RC_INIT_VAR + UInt32 result = 0; + int i; + for (i = numTotalBits; i > 0; i--) + { + /* UInt32 t; */ + range >>= 1; + + result <<= 1; + if (code >= range) + { + code -= range; + result |= 1; + } + /* + t = (code - range) >> 31; + t &= 1; + code -= range & (t - 1); + result = (result + result) | (1 - t); + */ + RC_NORMALIZE + } + RC_FLUSH_VAR + return result; +} + +int RangeDecoderBitDecode(CProb *prob, CRangeDecoder *rd) +{ + UInt32 bound = (rd->Range >> kNumBitModelTotalBits) * *prob; + if (rd->Code < bound) + { + rd->Range = bound; + *prob += (kBitModelTotal - *prob) >> kNumMoveBits; + if (rd->Range < kTopValue) + { + rd->Code = (rd->Code << 8) | ReadByte; + rd->Range <<= 8; + } + return 0; + } + else + { + rd->Range -= bound; + rd->Code -= bound; + *prob -= (*prob) >> kNumMoveBits; + if (rd->Range < kTopValue) + { + rd->Code = (rd->Code << 8) | ReadByte; + rd->Range <<= 8; + } + return 1; + } +} + +#define RC_GET_BIT2(prob, mi, A0, A1) \ + UInt32 bound = (range >> kNumBitModelTotalBits) * *prob; \ + if (code < bound) \ + { A0; range = bound; *prob += (kBitModelTotal - *prob) >> kNumMoveBits; mi <<= 1; } \ + else \ + { A1; range -= bound; code -= bound; *prob -= (*prob) >> kNumMoveBits; mi = (mi + mi) + 1; } \ + RC_NORMALIZE + +#define RC_GET_BIT(prob, mi) RC_GET_BIT2(prob, mi, ; , ;) + +int RangeDecoderBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) +{ + int mi = 1; + int i; + #ifdef _LZMA_LOC_OPT + RC_INIT_VAR + #endif + for(i = numLevels; i > 0; i--) + { + #ifdef _LZMA_LOC_OPT + CProb *prob = probs + mi; + RC_GET_BIT(prob, mi) + #else + mi = (mi + mi) + RangeDecoderBitDecode(probs + mi, rd); + #endif + } + #ifdef _LZMA_LOC_OPT + RC_FLUSH_VAR + #endif + return mi - (1 << numLevels); +} + +int RangeDecoderReverseBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) +{ + int mi = 1; + int i; + int symbol = 0; + #ifdef _LZMA_LOC_OPT + RC_INIT_VAR + #endif + for(i = 0; i < numLevels; i++) + { + #ifdef _LZMA_LOC_OPT + CProb *prob = probs + mi; + RC_GET_BIT2(prob, mi, ; , symbol |= (1 << i)) + #else + int bit = RangeDecoderBitDecode(probs + mi, rd); + mi = mi + mi + bit; + symbol |= (bit << i); + #endif + } + #ifdef _LZMA_LOC_OPT + RC_FLUSH_VAR + #endif + return symbol; +} + +Byte LzmaLiteralDecode(CProb *probs, CRangeDecoder *rd) +{ + int symbol = 1; + #ifdef _LZMA_LOC_OPT + RC_INIT_VAR + #endif + do + { + #ifdef _LZMA_LOC_OPT + CProb *prob = probs + symbol; + RC_GET_BIT(prob, symbol) + #else + symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); + #endif + } + while (symbol < 0x100); + #ifdef _LZMA_LOC_OPT + RC_FLUSH_VAR + #endif + return symbol; +} + +Byte LzmaLiteralDecodeMatch(CProb *probs, CRangeDecoder *rd, Byte matchByte) +{ + int symbol = 1; + #ifdef _LZMA_LOC_OPT + RC_INIT_VAR + #endif + do + { + int bit; + int matchBit = (matchByte >> 7) & 1; + matchByte <<= 1; + #ifdef _LZMA_LOC_OPT + { + CProb *prob = probs + ((1 + matchBit) << 8) + symbol; + RC_GET_BIT2(prob, symbol, bit = 0, bit = 1) + } + #else + bit = RangeDecoderBitDecode(probs + ((1 + matchBit) << 8) + symbol, rd); + symbol = (symbol << 1) | bit; + #endif + if (matchBit != bit) + { + while (symbol < 0x100) + { + #ifdef _LZMA_LOC_OPT + CProb *prob = probs + symbol; + RC_GET_BIT(prob, symbol) + #else + symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); + #endif + } + break; + } + } + while (symbol < 0x100); + #ifdef _LZMA_LOC_OPT + RC_FLUSH_VAR + #endif + return symbol; +} + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + +int LzmaLenDecode(CProb *p, CRangeDecoder *rd, int posState) +{ + if(RangeDecoderBitDecode(p + LenChoice, rd) == 0) + return RangeDecoderBitTreeDecode(p + LenLow + + (posState << kLenNumLowBits), kLenNumLowBits, rd); + if(RangeDecoderBitDecode(p + LenChoice2, rd) == 0) + return kLenNumLowSymbols + RangeDecoderBitTreeDecode(p + LenMid + + (posState << kLenNumMidBits), kLenNumMidBits, rd); + return kLenNumLowSymbols + kLenNumMidSymbols + + RangeDecoderBitTreeDecode(p + LenHigh, kLenNumHighBits, rd); +} + +#define kNumStates 12 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +#ifdef _LZMA_OUT_READ + +typedef struct _LzmaVarState +{ + CRangeDecoder RangeDecoder; + Byte *Dictionary; + UInt32 DictionarySize; + UInt32 DictionaryPos; + UInt32 GlobalPos; + UInt32 Reps[4]; + int lc; + int lp; + int pb; + int State; + int PreviousIsMatch; + int RemainLen; +} LzmaVarState; + +int LzmaDecoderInit( + unsigned char *buffer, UInt32 bufferSize, + int lc, int lp, int pb, + unsigned char *dictionary, UInt32 dictionarySize, + #ifdef _LZMA_IN_CB + ILzmaInCallback *inCallback + #else + unsigned char *inStream, UInt32 inSize + #endif + ) +{ + LzmaVarState *vs = (LzmaVarState *)buffer; + CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); + UInt32 i; + if (bufferSize < numProbs * sizeof(CProb) + sizeof(LzmaVarState)) + return LZMA_RESULT_NOT_ENOUGH_MEM; + vs->Dictionary = dictionary; + vs->DictionarySize = dictionarySize; + vs->DictionaryPos = 0; + vs->GlobalPos = 0; + vs->Reps[0] = vs->Reps[1] = vs->Reps[2] = vs->Reps[3] = 1; + vs->lc = lc; + vs->lp = lp; + vs->pb = pb; + vs->State = 0; + vs->PreviousIsMatch = 0; + vs->RemainLen = 0; + dictionary[dictionarySize - 1] = 0; + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + RangeDecoderInit(&vs->RangeDecoder, + #ifdef _LZMA_IN_CB + inCallback + #else + inStream, inSize + #endif + ); + return LZMA_RESULT_OK; +} + +int LzmaDecode(unsigned char *buffer, + unsigned char *outStream, UInt32 outSize, + UInt32 *outSizeProcessed) +{ + LzmaVarState *vs = (LzmaVarState *)buffer; + CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); + CRangeDecoder rd = vs->RangeDecoder; + int state = vs->State; + int previousIsMatch = vs->PreviousIsMatch; + Byte previousByte; + UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; + UInt32 nowPos = 0; + UInt32 posStateMask = (1 << (vs->pb)) - 1; + UInt32 literalPosMask = (1 << (vs->lp)) - 1; + int lc = vs->lc; + int len = vs->RemainLen; + UInt32 globalPos = vs->GlobalPos; + + Byte *dictionary = vs->Dictionary; + UInt32 dictionarySize = vs->DictionarySize; + UInt32 dictionaryPos = vs->DictionaryPos; + + if (len == -1) + { + *outSizeProcessed = 0; + return LZMA_RESULT_OK; + } + + while(len > 0 && nowPos < outSize) + { + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + len--; + } + if (dictionaryPos == 0) + previousByte = dictionary[dictionarySize - 1]; + else + previousByte = dictionary[dictionaryPos - 1]; +#else + +int LzmaDecode( + Byte *buffer, UInt32 bufferSize, + int lc, int lp, int pb, + #ifdef _LZMA_IN_CB + ILzmaInCallback *inCallback, + #else + unsigned char *inStream, UInt32 inSize, + #endif + unsigned char *outStream, UInt32 outSize, + UInt32 *outSizeProcessed) +{ + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); + CProb *p = (CProb *)buffer; + CRangeDecoder rd; + UInt32 i; + int state = 0; + int previousIsMatch = 0; + Byte previousByte = 0; + UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; + UInt32 nowPos = 0; + UInt32 posStateMask = (1 << pb) - 1; + UInt32 literalPosMask = (1 << lp) - 1; + int len = 0; + if (bufferSize < numProbs * sizeof(CProb)) + return LZMA_RESULT_NOT_ENOUGH_MEM; + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + RangeDecoderInit(&rd, + #ifdef _LZMA_IN_CB + inCallback + #else + inStream, inSize + #endif + ); +#endif + + *outSizeProcessed = 0; + while(nowPos < outSize) + { + int posState = (int)( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & posStateMask); + #ifdef _LZMA_IN_CB + if (rd.Result != LZMA_RESULT_OK) + return rd.Result; + #endif + if (rd.ExtraBytes != 0) + return LZMA_RESULT_DATA_ERROR; + if (RangeDecoderBitDecode(p + IsMatch + (state << kNumPosBitsMax) + posState, &rd) == 0) + { + CProb *probs = p + Literal + (LZMA_LIT_SIZE * + ((( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & literalPosMask) << lc) + (previousByte >> (8 - lc)))); + + if (state < 4) state = 0; + else if (state < 10) state -= 3; + else state -= 6; + if (previousIsMatch) + { + Byte matchByte; + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + matchByte = dictionary[pos]; + #else + matchByte = outStream[nowPos - rep0]; + #endif + previousByte = LzmaLiteralDecodeMatch(probs, &rd, matchByte); + previousIsMatch = 0; + } + else + previousByte = LzmaLiteralDecode(probs, &rd); + outStream[nowPos++] = previousByte; + #ifdef _LZMA_OUT_READ + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #endif + } + else + { + previousIsMatch = 1; + if (RangeDecoderBitDecode(p + IsRep + state, &rd) == 1) + { + if (RangeDecoderBitDecode(p + IsRepG0 + state, &rd) == 0) + { + if (RangeDecoderBitDecode(p + IsRep0Long + (state << kNumPosBitsMax) + posState, &rd) == 0) + { + #ifdef _LZMA_OUT_READ + UInt32 pos; + #endif + if ( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + == 0) + return LZMA_RESULT_DATA_ERROR; + state = state < 7 ? 9 : 11; + #ifdef _LZMA_OUT_READ + pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + outStream[nowPos++] = previousByte; + continue; + } + } + else + { + UInt32 distance; + if(RangeDecoderBitDecode(p + IsRepG1 + state, &rd) == 0) + distance = rep1; + else + { + if(RangeDecoderBitDecode(p + IsRepG2 + state, &rd) == 0) + distance = rep2; + else + { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + len = LzmaLenDecode(p + RepLenCoder, &rd, posState); + state = state < 7 ? 8 : 11; + } + else + { + int posSlot; + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < 7 ? 7 : 10; + len = LzmaLenDecode(p + LenCoder, &rd, posState); + posSlot = RangeDecoderBitTreeDecode(p + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits), kNumPosSlotBits, &rd); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + rep0 = ((2 | ((UInt32)posSlot & 1)) << numDirectBits); + if (posSlot < kEndPosModelIndex) + { + rep0 += RangeDecoderReverseBitTreeDecode( + p + SpecPos + rep0 - posSlot - 1, numDirectBits, &rd); + } + else + { + rep0 += RangeDecoderDecodeDirectBits(&rd, + numDirectBits - kNumAlignBits) << kNumAlignBits; + rep0 += RangeDecoderReverseBitTreeDecode(p + Align, kNumAlignBits, &rd); + } + } + else + rep0 = posSlot; + rep0++; + } + if (rep0 == (UInt32)(0)) + { + /* it's for stream version */ + len = -1; + break; + } + if (rep0 > nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + { + return LZMA_RESULT_DATA_ERROR; + } + len += kMatchMinLen; + do + { + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + outStream[nowPos++] = previousByte; + len--; + } + while(len > 0 && nowPos < outSize); + } + } + + #ifdef _LZMA_OUT_READ + vs->RangeDecoder = rd; + vs->DictionaryPos = dictionaryPos; + vs->GlobalPos = globalPos + nowPos; + vs->Reps[0] = rep0; + vs->Reps[1] = rep1; + vs->Reps[2] = rep2; + vs->Reps[3] = rep3; + vs->State = state; + vs->PreviousIsMatch = previousIsMatch; + vs->RemainLen = len; + #endif + + *outSizeProcessed = nowPos; + return LZMA_RESULT_OK; +} diff --git a/target/linux/rb1xx-2.6/image/lzma-loader/src/LzmaDecode.h b/target/linux/rb1xx-2.6/image/lzma-loader/src/LzmaDecode.h new file mode 100644 index 0000000..f58944e --- /dev/null +++ b/target/linux/rb1xx-2.6/image/lzma-loader/src/LzmaDecode.h @@ -0,0 +1,100 @@ +/* + LzmaDecode.h + LZMA Decoder interface + + LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this code, expressly permits you to + statically or dynamically link your code (or bind by name) to the + interfaces of this file without subjecting your linked code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#ifndef __LZMADECODE_H +#define __LZMADECODE_H + +/* #define _LZMA_IN_CB */ +/* Use callback for input data */ + +/* #define _LZMA_OUT_READ */ +/* Use read function for output data */ + +/* #define _LZMA_PROB32 */ +/* It can increase speed on some 32-bit CPUs, + but memory usage will be doubled in that case */ + +/* #define _LZMA_LOC_OPT */ +/* Enable local speed optimizations inside code */ + +#ifndef UInt32 +#ifdef _LZMA_UINT32_IS_ULONG +#define UInt32 unsigned long +#else +#define UInt32 unsigned int +#endif +#endif + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb unsigned short +#endif + +#define LZMA_RESULT_OK 0 +#define LZMA_RESULT_DATA_ERROR 1 +#define LZMA_RESULT_NOT_ENOUGH_MEM 2 + +#ifdef _LZMA_IN_CB +typedef struct _ILzmaInCallback +{ + int (*Read)(void *object, unsigned char **buffer, UInt32 *bufferSize); +} ILzmaInCallback; +#endif + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +/* +bufferSize = (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)))* sizeof(CProb) +bufferSize += 100 in case of _LZMA_OUT_READ +by default CProb is unsigned short, +but if specify _LZMA_PROB_32, CProb will be UInt32(unsigned int) +*/ + +#ifdef _LZMA_OUT_READ +int LzmaDecoderInit( + unsigned char *buffer, UInt32 bufferSize, + int lc, int lp, int pb, + unsigned char *dictionary, UInt32 dictionarySize, + #ifdef _LZMA_IN_CB + ILzmaInCallback *inCallback + #else + unsigned char *inStream, UInt32 inSize + #endif +); +#endif + +int LzmaDecode( + unsigned char *buffer, + #ifndef _LZMA_OUT_READ + UInt32 bufferSize, + int lc, int lp, int pb, + #ifdef _LZMA_IN_CB + ILzmaInCallback *inCallback, + #else + unsigned char *inStream, UInt32 inSize, + #endif + #endif + unsigned char *outStream, UInt32 outSize, + UInt32 *outSizeProcessed); + +#endif diff --git a/target/linux/rb1xx-2.6/image/lzma-loader/src/Makefile b/target/linux/rb1xx-2.6/image/lzma-loader/src/Makefile new file mode 100644 index 0000000..85a8646 --- /dev/null +++ b/target/linux/rb1xx-2.6/image/lzma-loader/src/Makefile @@ -0,0 +1,77 @@ +# +# Makefile for Broadcom BCM947XX boards +# +# Copyright 2001-2003, Broadcom Corporation +# All Rights Reserved. +# +# THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY +# KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM +# SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. +# +# Copyright 2004 Manuel Novoa III <mjn3@codepoet.org> +# Modified to support bzip'd kernels. +# Of course, it would be better to integrate bunzip capability into CFE. +# +# Copyright 2005 Oleg I. Vdovikin <oleg@cs.msu.su> +# Cleaned up, modified for lzma support, removed from kernel +# + +TEXT_START := 0x80001000 +BZ_TEXT_START := 0x80300000 + +OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .reginfo -R .note -R .comment -R .mdebug -S + +CFLAGS = -D__KERNEL__ -Wall -Wstrict-prototypes -Wno-trigraphs -Os \ + -fno-strict-aliasing -fno-common -fomit-frame-pointer -G 0 -mno-abicalls -fno-pic \ + -ffunction-sections -pipe -mlong-calls -fno-common \ + -mabi=32 -march=mips32 -Wa,-32 -Wa,-march=mips32 -Wa,-mips32 -Wa,--trap +CFLAGS += -DLOADADDR=$(TEXT_START) -D_LZMA_IN_CB + +ASFLAGS = $(CFLAGS) -D__ASSEMBLY__ -DBZ_TEXT_START=$(BZ_TEXT_START) + +SEDFLAGS := s/BZ_TEXT_START/$(BZ_TEXT_START)/;s/TEXT_START/$(TEXT_START)/ + +OBJECTS := head.o data.o + +all: loader.gz loader.elf + +# Don't build dependencies, this may die if $(CC) isn't gcc +dep: + +install: + +loader.gz: loader + gzip -nc9 $< > $@ + +loader.elf: loader.o + cp $< $@ + +loader: loader.o + $(OBJCOPY) $< $@ + +loader.o: loader.lds $(OBJECTS) + $(LD) -static --gc-sections -no-warn-mismatch -T loader.lds -o $@ $(OBJECTS) + +loader.lds: loader.lds.in Makefile + @sed "$(SEDFLAGS)" < $< > $@ + +data.o: data.lds decompress.image + $(LD) -no-warn-mismatch -T data.lds -r -o $@ -b binary decompress.image -b elf32-tradlittlemips + +data.lds: + @echo "SECTIONS { .data : { code_start = .; *(.data) code_stop = .; }}" > $@ + +decompress.image: decompress + $(OBJCOPY) $< $@ + +decompress: decompress.lds decompress.o LzmaDecode.o + $(LD) -static --gc-sections -no-warn-mismatch -T decompress.lds -o $@ decompress.o LzmaDecode.o + +decompress.lds: decompress.lds.in Makefile + @sed "$(SEDFLAGS)" < $< > $@ + +mrproper: clean + +clean: + rm -f loader.gz loader decompress *.lds *.o *.image diff --git a/target/linux/rb1xx-2.6/image/lzma-loader/src/README b/target/linux/rb1xx-2.6/image/lzma-loader/src/README new file mode 100644 index 0000000..16649e9 --- /dev/null +++ b/target/linux/rb1xx-2.6/image/lzma-loader/src/README @@ -0,0 +1,55 @@ +/* + * LZMA compressed kernel decompressor for bcm947xx boards + * + * Copyright (C) 2005 by Oleg I. Vdovikin <oleg@cs.msu.su> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +The code is intended to decompress kernel, being compressed using lzma utility +build using 7zip LZMA SDK. This utility is located in the LZMA_Alone directory + +decompressor code expects that your .trx file consist of three partitions: + +1) decompressor itself (this is gziped code which pmon/cfe will extract and run +on boot-up instead of real kernel) +2) LZMA compressed kernel (both streamed and regular modes are supported now) +3) Root filesystem + +Please be sure to apply the following patch for use this new trx layout (it will +allow using both new and old trx files for root filesystem lookup code) + +--- linuz/arch/mips/brcm-boards/bcm947xx/setup.c 2005-01-23 19:24:27.503322896 +0300 ++++ linux/arch/mips/brcm-boards/bcm947xx/setup.c 2005-01-23 19:29:05.237100944 +0300 +@@ -221,7 +221,9 @@ + /* Try looking at TRX header for rootfs offset */ + if (le32_to_cpu(trx->magic) == TRX_MAGIC) { + bcm947xx_parts[1].offset = off; +- if (le32_to_cpu(trx->offsets[1]) > off) ++ if (le32_to_cpu(trx->offsets[2]) > off) ++ off = le32_to_cpu(trx->offsets[2]); ++ else if (le32_to_cpu(trx->offsets[1]) > off) + off = le32_to_cpu(trx->offsets[1]); + continue; + } + + +Revision history: + 0.02 Initial release + 0.03 Added Mineharu Takahara <mtakahar@yahoo.com> patch to pass actual + output size to decoder (stream mode compressed input is not + a requirement anymore) + 0.04 Reordered functions using lds script diff --git a/target/linux/rb1xx-2.6/image/lzma-loader/src/decompress.c b/target/linux/rb1xx-2.6/image/lzma-loader/src/decompress.c new file mode 100644 index 0000000..d361024 --- /dev/null +++ b/target/linux/rb1xx-2.6/image/lzma-loader/src/decompress.c @@ -0,0 +1,239 @@ +/* + * LZMA compressed kernel decompressor for bcm947xx boards + * + * Copyright (C) 2005 by Oleg I. Vdovikin <oleg@cs.msu.su> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Please note, this was code based on the bunzip2 decompressor code + * by Manuel Novoa III (mjn3@codepoet.org), although the only thing left + * is an idea and part of original vendor code + * + * + * 12-Mar-2005 Mineharu Takahara <mtakahar@yahoo.com> + * pass actual output size to decoder (stream mode + * compressed input is not a requirement anymore) + * + * 24-Apr-2005 Oleg I. Vdovikin + * reordered functions using lds script, removed forward decl + * + * 24-Mar-2007 Gabor Juhos + * pass original values of the a0,a1,a2,a3 registers to the kernel + * + */ + +#include "LzmaDecode.h" + +#define BCM4710_FLASH 0x1fc00000 /* Flash */ + +#define KSEG0 0x80000000 +#define KSEG1 0xa0000000 + +#define KSEG1ADDR(a) ((((unsigned)(a)) & 0x1fffffffU) | KSEG1) + +#define Index_Invalidate_I 0x00 +#define Index_Writeback_Inv_D 0x01 + +#define cache_unroll(base,op) \ + __asm__ __volatile__( \ + ".set noreorder;\n" \ + ".set mips3;\n" \ + "cache %1, (%0);\n" \ + ".set mips0;\n" \ + ".set reorder\n" \ + : \ + : "r" (base), \ + "i" (op)); + +static __inline__ void blast_icache(unsigned long size, unsigned long lsize) +{ + unsigned long start = KSEG0; + unsigned long end = (start + size); + + while(start < end) { + cache_unroll(start,Index_Invalidate_I); + start += lsize; + } +} + +static __inline__ void blast_dcache(unsigned long size, unsigned long lsize) +{ + unsigned long start = KSEG0; + unsigned long end = (start + size); + + while(start < end) { + cache_unroll(start,Index_Writeback_Inv_D); + start += lsize; + } +} + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ + +struct trx_header { + unsigned int magic; /* "HDR0" */ + unsigned int len; /* Length of file including header */ + unsigned int crc32; /* 32-bit CRC from flag_version to end of file */ + unsigned int flag_version; /* 0:15 flags, 16:31 version */ + unsigned int offsets[3]; /* Offsets of partitions from start of header */ +}; + +/* beyound the image end, size not known in advance */ +extern unsigned char workspace[]; + +unsigned int offset; +unsigned char *data; + +typedef void (*kernel_entry)(unsigned long reg_a0, unsigned long reg_a1, + unsigned long reg_a2, unsigned long reg_a3); + +/* flash access should be aligned, so wrapper is used */ +/* read byte from the flash, all accesses are 32-bit aligned */ +static int read_byte(void *object, unsigned char **buffer, UInt32 *bufferSize) +{ + static unsigned int val; + + if (((unsigned int)offset % 4) == 0) { + val = *(unsigned int *)data; + data += 4; + } + + *bufferSize = 1; + *buffer = ((unsigned char *)&val) + (offset++ & 3); + + return LZMA_RESULT_OK; +} + +static __inline__ unsigned char get_byte(void) +{ + unsigned char *buffer; + UInt32 fake; + + return read_byte(0, &buffer, &fake), *buffer; +} + +/* should be the first function */ +void entry(unsigned long reg_a0, unsigned long reg_a1, + unsigned long reg_a2, unsigned long reg_a3, + unsigned long icache_size, unsigned long icache_lsize, + unsigned long dcache_size, unsigned long dcache_lsize) +{ + unsigned int i; /* temp value */ + unsigned int lc; /* literal context bits */ + unsigned int lp; /* literal pos state bits */ + unsigned int pb; /* pos state bits */ + unsigned int osize; /* uncompressed size */ + + ILzmaInCallback callback; + callback.Read = read_byte; + + uart_write_str("decompress kernel ... "); + + /* look for trx header, 32-bit data access */ + for (data = ((unsigned char *) KSEG1ADDR(BCM4710_FLASH)); + ((struct trx_header *)data)->magic != TRX_MAGIC; data += 65536); + + /* compressed kernel is in the partition 0 or 1 */ + if (((struct trx_header *)data)->offsets[1] > 65536) + data += ((struct trx_header *)data)->offsets[0]; + else + data += ((struct trx_header *)data)->offsets[1]; + + offset = 0; + + /* lzma args */ + i = get_byte(); + lc = i % 9, i = i / 9; + lp = i % 5, pb = i / 5; + + /* skip rest of the LZMA coder property */ + for (i = 0; i < 4; i++) + get_byte(); + + /* read the lower half of uncompressed size in the header */ + osize = ((unsigned int)get_byte()) + + ((unsigned int)get_byte() << 8) + + ((unsigned int)get_byte() << 16) + + ((unsigned int)get_byte() << 24); + + /* skip rest of the header (upper half of uncompressed size) */ + for (i = 0; i < 4; i++) + get_byte(); + + /* decompress kernel */ + if (LzmaDecode(workspace, ~0, lc, lp, pb, &callback, + (unsigned char*)LOADADDR, osize, &i) == LZMA_RESULT_OK) + { + blast_dcache(dcache_size, dcache_lsize); + blast_icache(icache_size, icache_lsize); + + /* Jump to load address */ + uart_write_str("ok\r\n"); + ((kernel_entry) LOADADDR)(reg_a0, reg_a1, reg_a2, reg_a3); + } + uart_write_str("failed\r\n"); + while (1 ); +} + +/* ********************************************************************* + * + * ADM5120 UART driver File: dev_adm_uart.c + * + * This is a console device driver for an ADM5120 UART + * + ********************************************************************* + * + * Copyright 2006 + * Compex Systems. All rights reserved. + * + ********************************************************************* */ + +#define READCSR(r) *(volatile UInt32 *)(0xB2600000+(r)) +#define WRITECSR(r,v) *(volatile UInt32 *)(0xB2600000+(r)) = v + +#define UART_DR_REG 0x00 +#define UART_FR_REG 0x18 +#define UART_TX_FIFO_FULL 0x20 + +int uart_write(int val) +{ + WRITECSR(UART_DR_REG, val); + while ( (READCSR(UART_FR_REG) & UART_TX_FIFO_FULL) ); + return 0; +} + +int uart_write_str(char * str) +{ + while ( *str != 0 ) { + uart_write ( *str++ ); + } + return 0; +} + +int uart_write_hex(int val) +{ + int i; + int tmp; + + uart_write_str("0x"); + for ( i=0 ; i<8 ; i++ ) { + tmp = (val >> ((7-i) * 4 )) & 0xf; + tmp = tmp < 10 ? (tmp + '0') : (tmp + 'A' - 10); + uart_write(tmp); + } + uart_write_str("\r\n"); + return 0; +} + diff --git a/target/linux/rb1xx-2.6/image/lzma-loader/src/decompress.lds.in b/target/linux/rb1xx-2.6/image/lzma-loader/src/decompress.lds.in new file mode 100644 index 0000000..33f56f8 --- /dev/null +++ b/target/linux/rb1xx-2.6/image/lzma-loader/src/decompress.lds.in @@ -0,0 +1,20 @@ +OUTPUT_ARCH(mips) +ENTRY(entry) +SECTIONS { + . = BZ_TEXT_START; + .text : { + *(.text.entry) + *(.text) + *(.rodata) + } + + .data : { + *(.data) + } + + .bss : { + *(.bss) + } + + workspace = .; +} diff --git a/target/linux/rb1xx-2.6/image/lzma-loader/src/head.S b/target/linux/rb1xx-2.6/image/lzma-loader/src/head.S new file mode 100644 index 0000000..7f0e9ad --- /dev/null +++ b/target/linux/rb1xx-2.6/image/lzma-loader/src/head.S @@ -0,0 +1,166 @@ +/* Copyright 2007 Gabor Juhos */ +/* keep original values of the a0,a1,a2,a3 registers */ +/* cache manipulation adapted from Broadcom code */ +/* Copyright 2005 Oleg I. Vdovikin (oleg@cs.msu.su) */ +/* cache manipulation adapted from Broadcom code */ +/* idea taken from original bunzip2 decompressor code */ +/* Copyright 2004 Manuel Novoa III (mjn3@codepoet.org) */ +/* Licensed under the linux kernel's version of the GPL.*/ + +#include <asm/asm.h> +#include <asm/regdef.h> + +#define KSEG0 0x80000000 + +#define C0_CONFIG $16 +#define C0_TAGLO $28 +#define C0_TAGHI $29 + +#define CONF1_DA_SHIFT 7 /* D$ associativity */ +#define CONF1_DA_MASK 0x00000380 +#define CONF1_DA_BASE 1 +#define CONF1_DL_SHIFT 10 /* D$ line size */ +#define CONF1_DL_MASK 0x00001c00 +#define CONF1_DL_BASE 2 +#define CONF1_DS_SHIFT 13 /* D$ sets/way */ +#define CONF1_DS_MASK 0x0000e000 +#define CONF1_DS_BASE 64 +#define CONF1_IA_SHIFT 16 /* I$ associativity */ +#define CONF1_IA_MASK 0x00070000 +#define CONF1_IA_BASE 1 +#define CONF1_IL_SHIFT 19 /* I$ line size */ +#define CONF1_IL_MASK 0x00380000 +#define CONF1_IL_BASE 2 +#define CONF1_IS_SHIFT 22 /* Instruction cache sets/way */ +#define CONF1_IS_MASK 0x01c00000 +#define CONF1_IS_BASE 64 + +#define Index_Invalidate_I 0x00 +#define Index_Writeback_Inv_D 0x01 + + .text + LEAF(startup) + .set noreorder + + /* Copy decompressor code to the right place */ + li t0, BZ_TEXT_START + + la t1, code_start + la t2, code_stop +$L1: + lw t3, 0(t1) + sw t3, 0(t0) + add t1, 4 + blt t1, t2, $L1 + add t0, 4 + + /* At this point we need to invalidate dcache and */ + /* icache before jumping to new code */ + +1: /* Get cache sizes */ + .set mips32 + mfc0 s0,C0_CONFIG,1 + .set mips0 + + li s1,CONF1_DL_MASK + and s1,s0 + beq s1,zero,nodc + nop + + srl s1,CONF1_DL_SHIFT + li t0,CONF1_DL_BASE + sll s1,t0,s1 /* s1 has D$ cache line size */ + + li s2,CONF1_DA_MASK + and s2,s0 + srl s2,CONF1_DA_SHIFT + addiu s2,CONF1_DA_BASE /* s2 now has D$ associativity */ + + li t0,CONF1_DS_MASK + and t0,s0 + srl t0,CONF1_DS_SHIFT + li s3,CONF1_DS_BASE + sll s3,s3,t0 /* s3 has D$ sets per way */ + + multu s2,s3 /* sets/way * associativity */ + mflo t0 /* total cache lines */ + + multu s1,t0 /* D$ linesize * lines */ + mflo s2 /* s2 is now D$ size in bytes */ + + /* Initilize the D$: */ + mtc0 zero,C0_TAGLO + mtc0 zero,C0_TAGHI + + li t0,KSEG0 /* Just an address for the first $ line */ + addu t1,t0,s2 /* + size of cache == end */ + + .set mips3 +1: cache Index_Writeback_Inv_D,0(t0) + .set mips0 + bne t0,t1,1b + addu t0,s1 + +nodc: + /* Now we get to do it all again for the I$ */ + + move s3,zero /* just in case there is no icache */ + move s4,zero + + li t0,CONF1_IL_MASK + and t0,s0 + beq t0,zero,noic + nop + + srl t0,CONF1_IL_SHIFT + li s3,CONF1_IL_BASE + sll s3,t0 /* s3 has I$ cache line size */ + + li t0,CONF1_IA_MASK + and t0,s0 + srl t0,CONF1_IA_SHIFT + addiu s4,t0,CONF1_IA_BASE /* s4 now has I$ associativity */ + + li t0,CONF1_IS_MASK + and t0,s0 + srl t0,CONF1_IS_SHIFT + li s5,CONF1_IS_BASE + sll s5,t0 /* s5 has I$ sets per way */ + + multu s4,s5 /* sets/way * associativity */ + mflo t0 /* s4 is now total cache lines */ + + multu s3,t0 /* I$ linesize * lines */ + mflo s4 /* s4 is cache size in bytes */ + + /* Initilize the I$: */ + mtc0 zero,C0_TAGLO + mtc0 zero,C0_TAGHI + + li t0,KSEG0 /* Just an address for the first $ line */ + addu t1,t0,s4 /* + size of cache == end */ + + .set mips3 +1: cache Index_Invalidate_I,0(t0) + .set mips0 + bne t0,t1,1b + addu t0,s3 + +noic: + li t0, BZ_TEXT_START + + addiu sp, -32 /* reserve stack for parameters */ +#if 0 + sw a0, 0(sp) + sw a1, 4(sp) + sw a2, 8(sp) + sw a3, 12(sp) +#endif + sw s3, 16(sp) /* icache line size */ + sw s4, 20(sp) /* icache size */ + sw s1, 24(sp) /* dcache line size */ + jr t0 + sw s2, 28(sp) /* dcache size */ + + .set reorder + END(startup) diff --git a/target/linux/rb1xx-2.6/image/lzma-loader/src/loader.lds.in b/target/linux/rb1xx-2.6/image/lzma-loader/src/loader.lds.in new file mode 100644 index 0000000..20f2ea9 --- /dev/null +++ b/target/linux/rb1xx-2.6/image/lzma-loader/src/loader.lds.in @@ -0,0 +1,17 @@ +OUTPUT_ARCH(mips) +ENTRY(startup) +SECTIONS { + . = TEXT_START; + .text : { + *(.text) + *(.rodata) + } + + .data : { + *(.data) + } + + .bss : { + *(.bss) + } +} diff --git a/target/linux/rb1xx-2.6/patches/001-adm5120.patch b/target/linux/rb1xx-2.6/patches/001-adm5120.patch new file mode 100644 index 0000000..ed7a307 --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/001-adm5120.patch @@ -0,0 +1,64 @@ +diff -urN linux-2.6.19.2/arch/mips/Kconfig linux-2.6.19.2.new/arch/mips/Kconfig +--- linux-2.6.19.2/arch/mips/Kconfig 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2.new/arch/mips/Kconfig 2007-01-23 14:49:38.000000000 +0100 +@@ -12,6 +12,18 @@ + prompt "System type" + default SGI_IP22 + ++config MIPS_ADM5120 ++ bool "Support for ADM5120 SoC" ++ select SYS_HAS_CPU_MIPS32_R1 ++ select DMA_NONCOHERENT ++ select HW_HAS_PCI ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SYS_SUPPORTS_32BIT_KERNEL ++ ++config PCI_ADM5120 ++ bool "Add PCI control support for ADM5120" ++ depends on MIPS_ADM5120 && PCI ++ + config MIPS_MTX1 + bool "4G Systems MTX-1 board" + select DMA_NONCOHERENT +diff -urN linux-2.6.19.2/arch/mips/Makefile linux-2.6.19.2.new/arch/mips/Makefile +--- linux-2.6.19.2/arch/mips/Makefile 2007-01-23 14:02:57.000000000 +0100 ++++ linux-2.6.19.2.new/arch/mips/Makefile 2007-01-23 14:49:39.000000000 +0100 +@@ -165,6 +165,14 @@ + load-$(CONFIG_MACH_JAZZ) += 0xffffffff80080000 + + # ++# ADMtek 5120 ++# ++ ++core-$(CONFIG_MIPS_ADM5120) += arch/mips/adm5120/ ++cflags-$(CONFIG_MIPS_ADM5120) += -Iinclude/asm-mips/mach-adm5120 ++load-$(CONFIG_MIPS_ADM5120) += 0xffffffff80101000 ++ ++# + # Common Alchemy Au1x00 stuff + # + core-$(CONFIG_SOC_AU1X00) += arch/mips/au1000/common/ +diff -urN linux-2.6.19.2/arch/mips/pci/Makefile linux-2.6.19.2.new/arch/mips/pci/Makefile +--- linux-2.6.19.2/arch/mips/pci/Makefile 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2.new/arch/mips/pci/Makefile 2007-01-23 14:49:40.000000000 +0100 +@@ -53,3 +53,4 @@ + obj-$(CONFIG_VICTOR_MPC30X) += fixup-mpc30x.o + obj-$(CONFIG_ZAO_CAPCELLA) += fixup-capcella.o + obj-$(CONFIG_WR_PPMC) += fixup-wrppmc.o ++obj-$(CONFIG_PCI_ADM5120) += ops-adm5120.o pci-adm5120.o +diff -urN linux-2.6.19.2/include/asm-mips/bootinfo.h linux-2.6.19.2.new/include/asm-mips/bootinfo.h +--- linux-2.6.19.2/include/asm-mips/bootinfo.h 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2.new/include/asm-mips/bootinfo.h 2007-01-23 14:49:40.000000000 +0100 +@@ -212,6 +212,12 @@ + #define MACH_GROUP_NEC_EMMA2RH 25 /* NEC EMMA2RH (was 23) */ + #define MACH_NEC_MARKEINS 0 /* NEC EMMA2RH Mark-eins */ + ++/* ++ * Valid machtype for group ADMtek ++ */ ++#define MACH_GROUP_ADM_GW 23 ++#define MACH_ADM_GW_5120 0 ++ + #define CL_SIZE COMMAND_LINE_SIZE + + const char *get_system_type(void); diff --git a/target/linux/rb1xx-2.6/patches/002-adm5120_flash.patch b/target/linux/rb1xx-2.6/patches/002-adm5120_flash.patch new file mode 100644 index 0000000..503a495 --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/002-adm5120_flash.patch @@ -0,0 +1,25 @@ +diff -urN linux-2.6.19.2/drivers/mtd/maps/Kconfig linux-2.6.19.2-adm5120/drivers/mtd/maps/Kconfig +--- linux-2.6.19.2/drivers/mtd/maps/Kconfig 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/mtd/maps/Kconfig 2007-01-23 01:13:39.000000000 +0100 +@@ -622,5 +622,10 @@ + + This selection automatically selects the map_ram driver. + ++config MTD_ADM5120 ++ tristate "Map driver for ADMtek ADM5120 boards" ++ depends on MIPS_ADM5120 ++ select MTD_CFI_AMDSTD ++ + endmenu + +diff -urN linux-2.6.19.2/drivers/mtd/maps/Makefile linux-2.6.19.2-adm5120/drivers/mtd/maps/Makefile +--- linux-2.6.19.2/drivers/mtd/maps/Makefile 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/mtd/maps/Makefile 2007-01-23 01:20:52.000000000 +0100 +@@ -43,6 +43,7 @@ + obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o + obj-$(CONFIG_MTD_PCI) += pci.o + obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o ++obj-$(CONFIG_MTD_ADM5120) += adm5120_mtd.o + obj-$(CONFIG_MTD_LASAT) += lasat.o + obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o + obj-$(CONFIG_MTD_EDB7312) += edb7312.o diff --git a/target/linux/rb1xx-2.6/patches/003-adm5120_switch.patch b/target/linux/rb1xx-2.6/patches/003-adm5120_switch.patch new file mode 100644 index 0000000..f6fec30 --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/003-adm5120_switch.patch @@ -0,0 +1,25 @@ +diff -urN linux-2.6.19.2/drivers/net/Kconfig linux-2.6.19.2-adm5120/drivers/net/Kconfig +--- linux-2.6.19.2/drivers/net/Kconfig 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/net/Kconfig 2007-01-23 01:13:39.000000000 +0100 +@@ -450,6 +450,10 @@ + If you have an Alchemy Semi AU1X00 based system + say Y. Otherwise, say N. + ++config MIPS_ADM5120_ENET ++ tristate "MIPS ADM5120 Ethernet switch support" ++ depends on NET_ETHERNET && MIPS_ADM5120 ++ + config NET_SB1250_MAC + tristate "SB1250 Ethernet support" + depends on NET_ETHERNET && SIBYTE_SB1xxx_SOC +diff -urN linux-2.6.19.2/drivers/net/Makefile linux-2.6.19.2-adm5120/drivers/net/Makefile +--- linux-2.6.19.2/drivers/net/Makefile 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/net/Makefile 2007-01-23 01:13:39.000000000 +0100 +@@ -164,6 +164,7 @@ + # This is also a 82596 and should probably be merged + obj-$(CONFIG_LP486E) += lp486e.o + ++obj-$(CONFIG_MIPS_ADM5120_ENET) += adm5120sw.o + obj-$(CONFIG_ETH16I) += eth16i.o + obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o + obj-$(CONFIG_HPLANCE) += hplance.o 7990.o diff --git a/target/linux/rb1xx-2.6/patches/004-adm5120_uart.patch b/target/linux/rb1xx-2.6/patches/004-adm5120_uart.patch new file mode 100644 index 0000000..6f2ef7b --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/004-adm5120_uart.patch @@ -0,0 +1,50 @@ +diff -urN linux-2.6.19.2/drivers/serial/Kconfig linux-2.6.19.2-adm5120/drivers/serial/Kconfig +--- linux-2.6.19.2/drivers/serial/Kconfig 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/serial/Kconfig 2007-01-23 01:13:39.000000000 +0100 +@@ -246,6 +246,22 @@ + + comment "Non-8250 serial port support" + ++config SERIAL_ADM5120 ++ bool "ADM5120 serial port support" ++ depends on MIPS_ADM5120 ++ select SERIAL_CORE ++ select SERIAL_CORE_CONSOLE ++ help ++ Driver for the on chip UARTs on the ADM5120 SoC ++ ++config ADM5120_NR_UARTS ++ int "Maximum number of ADM5120 serial ports" ++ depends on SERIAL_ADM5120 ++ default "2" ++ ---help--- ++ Set this to the number of serial ports you want the driver to ++ support. ++ + config SERIAL_AMBA_PL010 + tristate "ARM AMBA PL010 serial port support" + depends on ARM_AMBA && (BROKEN || !ARCH_VERSATILE) +diff -urN linux-2.6.19.2/drivers/serial/Makefile linux-2.6.19.2-adm5120/drivers/serial/Makefile +--- linux-2.6.19.2/drivers/serial/Makefile 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/serial/Makefile 2007-01-23 01:21:45.000000000 +0100 +@@ -20,6 +20,7 @@ + obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o + obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o + obj-$(CONFIG_SERIAL_8250_AU1X00) += 8250_au1x00.o ++obj-$(CONFIG_SERIAL_ADM5120) += adm5120_uart.o + obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o + obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o + obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o +diff -urN linux-2.6.19.2/include/linux/serial_core.h linux-2.6.19.2-adm5120/include/linux/serial_core.h +--- linux-2.6.19.2/include/linux/serial_core.h 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/include/linux/serial_core.h 2007-01-23 01:13:39.000000000 +0100 +@@ -133,6 +133,9 @@ + #define PORT_S3C2412 73 + + ++/* ADMtek ADM5120 SoC */ ++#define PORT_ADM5120 68 ++ + #ifdef __KERNEL__ + + #include <linux/compiler.h> diff --git a/target/linux/rb1xx-2.6/patches/005-adm5120_usb.patch b/target/linux/rb1xx-2.6/patches/005-adm5120_usb.patch new file mode 100644 index 0000000..40e4fbe --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/005-adm5120_usb.patch @@ -0,0 +1,53 @@ +diff -urN linux-2.6.19.2/drivers/usb/core/hub.c linux-2.6.19.2-adm5120/drivers/usb/core/hub.c +--- linux-2.6.19.2/drivers/usb/core/hub.c 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/usb/core/hub.c 2007-01-23 01:13:39.000000000 +0100 +@@ -2156,6 +2156,8 @@ + USB_DT_DEVICE << 8, 0, + buf, GET_DESCRIPTOR_BUFSIZE, + (i ? USB_CTRL_GET_TIMEOUT : 1000)); ++printk(KERN_CRIT "usb_control_msg: %d %d %d (%d)\n", r, buf->bMaxPacketSize0, ++buf->bDescriptorType, USB_DT_DEVICE); + switch (buf->bMaxPacketSize0) { + case 8: case 16: case 32: case 64: case 255: + if (buf->bDescriptorType == +diff -urN linux-2.6.19.2/drivers/usb/host/Kconfig linux-2.6.19.2-adm5120/drivers/usb/host/Kconfig +--- linux-2.6.19.2/drivers/usb/host/Kconfig 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/usb/host/Kconfig 2007-01-23 01:13:39.000000000 +0100 +@@ -192,3 +192,6 @@ + To compile this driver as a module, choose M here: the + module will be called "sl811_cs". + ++config USB_ADM5120_HCD ++ tristate "ADM5120 HCD support" ++ depends on USB && MIPS_ADM5120 +diff -urN linux-2.6.19.2/drivers/usb/host/Makefile linux-2.6.19.2-adm5120/drivers/usb/host/Makefile +--- linux-2.6.19.2/drivers/usb/host/Makefile 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/usb/host/Makefile 2007-01-23 01:13:39.000000000 +0100 +@@ -16,3 +16,4 @@ + obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o + obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o + obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o ++obj-$(CONFIG_USB_ADM5120_HCD) += adm5120-hcd.o +diff -urN linux-2.6.19.2/drivers/usb/Kconfig linux-2.6.19.2-adm5120/drivers/usb/Kconfig +--- linux-2.6.19.2/drivers/usb/Kconfig 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/usb/Kconfig 2007-01-23 01:13:39.000000000 +0100 +@@ -91,8 +91,6 @@ + + source "drivers/usb/net/Kconfig" + +-source "drivers/usb/mon/Kconfig" +- + comment "USB port drivers" + depends on USB + +diff -urN linux-2.6.19.2/drivers/usb/Makefile linux-2.6.19.2-adm5120/drivers/usb/Makefile +--- linux-2.6.19.2/drivers/usb/Makefile 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/usb/Makefile 2007-01-23 01:22:18.000000000 +0100 +@@ -17,6 +17,7 @@ + obj-$(CONFIG_USB_U132_HCD) += host/ + obj-$(CONFIG_ETRAX_USB_HOST) += host/ + obj-$(CONFIG_USB_OHCI_AT91) += host/ ++obj-$(CONFIG_USB_ADM5120_HCD) += host/ + + obj-$(CONFIG_USB_ACM) += class/ + obj-$(CONFIG_USB_PRINTER) += class/ diff --git a/target/linux/rb1xx-2.6/patches/006-adm5120_gpio.patch b/target/linux/rb1xx-2.6/patches/006-adm5120_gpio.patch new file mode 100644 index 0000000..593743d --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/006-adm5120_gpio.patch @@ -0,0 +1,25 @@ +diff -urN linux-2.6.19.2/drivers/char/Kconfig linux-2.6.19.2-adm5120/drivers/char/Kconfig +--- linux-2.6.19.2/drivers/char/Kconfig 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/char/Kconfig 2007-01-23 01:13:39.000000000 +0100 +@@ -1034,7 +1034,9 @@ + The mmtimer device allows direct userspace access to the + Altix system timer. + +-source "drivers/char/tpm/Kconfig" ++config ADM5120_GPIO ++ tristate "ADM5120 GPIO" ++ depends on MIPS_ADM5120 + + config TELCLOCK + tristate "Telecom clock driver for MPBL0010 ATCA SBC" +diff -urN linux-2.6.19.2/drivers/char/Makefile linux-2.6.19.2-adm5120/drivers/char/Makefile +--- linux-2.6.19.2/drivers/char/Makefile 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2-adm5120/drivers/char/Makefile 2007-01-23 01:20:01.000000000 +0100 +@@ -90,6 +90,7 @@ + obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o + obj-$(CONFIG_TANBAC_TB0219) += tb0219.o + obj-$(CONFIG_TELCLOCK) += tlclk.o ++obj-$(CONFIG_ADM5120_GPIO) += adm5120_gpio.o + + obj-$(CONFIG_WATCHDOG) += watchdog/ + obj-$(CONFIG_MWAVE) += mwave/ diff --git a/target/linux/rb1xx-2.6/patches/100-mtd-myloder-partition-parser.patch b/target/linux/rb1xx-2.6/patches/100-mtd-myloder-partition-parser.patch new file mode 100644 index 0000000..81ec37d --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/100-mtd-myloder-partition-parser.patch @@ -0,0 +1,37 @@ +diff -Nur linux-2.6.19.2/drivers/mtd/Kconfig.old linux-2.6.19.2/drivers/mtd/Kconfig +--- linux-2.6.19.2/drivers/mtd/Kconfig.old 2007-03-25 08:13:02.000000000 +0200 ++++ linux-2.6.19.2/drivers/mtd/Kconfig 2007-03-26 13:37:47.000000000 +0200 +@@ -157,6 +157,22 @@ + for your particular device. It won't happen automatically. The + 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example. + ++config MTD_MYLOADER_PARTS ++ tristate "MyLoader partition parsing" ++ depends on MIPS_ADM5120 && MTD_PARTITIONS ++ ---help--- ++ MyLoader is a bootloader which allows the user to define partitions ++ in flash devices, by putting a table in the second erase block ++ on the device, similar to a partition table. This table gives the ++ offsets and lengths of the user defined partitions. ++ ++ If you need code which can detect and parse these tables, and ++ register MTD 'partitions' corresponding to each image detected, ++ enable this option. ++ ++ You will still need the parsing functions to be called by the driver ++ for your particular device. It won't happen automatically. ++ + comment "User Modules And Translation Layers" + depends on MTD + +diff -Nur linux-2.6.19.2/drivers/mtd/Makefile.old linux-2.6.19.2/drivers/mtd/Makefile +--- linux-2.6.19.2/drivers/mtd/Makefile.old 2007-01-10 20:10:37.000000000 +0100 ++++ linux-2.6.19.2/drivers/mtd/Makefile 2007-03-26 13:28:23.000000000 +0200 +@@ -12,6 +12,7 @@ + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o + obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o + obj-$(CONFIG_MTD_AFS_PARTS) += afs.o ++obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_CHAR) += mtdchar.o diff --git a/target/linux/rb1xx-2.6/patches/101-mtd-mxflash.patch b/target/linux/rb1xx-2.6/patches/101-mtd-mxflash.patch new file mode 100755 index 0000000..754a91e --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/101-mtd-mxflash.patch @@ -0,0 +1,31 @@ +--- linux-2.6.19.2/drivers/mtd/chips/Kconfig.old 2007-01-11 03:10:36.000000000 +0800 ++++ linux-2.6.19.2/drivers/mtd/chips/Kconfig 2007-03-29 14:43:32.000000000 +0800 +@@ -199,6 +199,14 @@ + provides support for one of those command sets, used on chips + including the AMD Am29LV320. + ++config MTD_CFI_AMDSTD_FORCE_BOTTOM_BOOT ++ bool "Force bottom boot for Macronix flash chips" ++ depends on MTD_CFI_AMDSTD ++ help ++ Some Macronix flash chips have wrong CFI info, and the driver may ++ detect the type incorrectly. Select this if the chip part number ++ ends with BTC. ++ + config MTD_CFI_STAA + tristate "Support for ST (Advanced Architecture) flash chips" + depends on MTD_GEN_PROBE +--- linux-2.6.19.2/drivers/mtd/chips/cfi_cmdset_0002.c.old 2007-03-22 16:14:56.000000000 +0800 ++++ linux-2.6.19.2/drivers/mtd/chips/cfi_cmdset_0002.c 2007-03-29 14:07:28.000000000 +0800 +@@ -320,6 +320,11 @@ + cfi_tell_features(extp); + #endif + ++#ifdef CONFIG_MTD_CFI_AMDSTD_FORCE_BOTTOM_BOOT ++ extp->TopBottom = 2; ++ bootloc = extp->TopBottom; ++#endif ++ + bootloc = extp->TopBottom; + if ((bootloc != 2) && (bootloc != 3)) { + printk(KERN_WARNING "%s: CFI does not contain boot " diff --git a/target/linux/rb1xx-2.6/patches/120-cf.patch b/target/linux/rb1xx-2.6/patches/120-cf.patch new file mode 100644 index 0000000..a22c526 --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/120-cf.patch @@ -0,0 +1,1011 @@ +diff -urN linux.old/drivers/block/Kconfig linux.dev/drivers/block/Kconfig +--- linux.old/drivers/block/Kconfig 2006-10-26 02:43:39.000000000 +0200 ++++ linux.dev/drivers/block/Kconfig 2006-10-26 00:11:14.000000000 +0200 +@@ -456,4 +456,12 @@ + This driver provides Support for ATA over Ethernet block + devices like the Coraid EtherDrive (R) Storage Blade. + ++config BLK_DEV_CF_MIPS ++ bool "CF slot of RB532 board" ++ depends on MIKROTIK_RB500 || MIPS_ADM5120 ++ default y ++ help ++ The Routerboard 532 has a CF slot on it. Enable the special block ++ device driver for it. ++ + endmenu +diff -urN linux.old/drivers/block/Makefile linux.dev/drivers/block/Makefile +--- linux.old/drivers/block/Makefile 2006-06-18 03:49:35.000000000 +0200 ++++ linux.dev/drivers/block/Makefile 2006-10-26 02:44:10.000000000 +0200 +@@ -29,4 +29,5 @@ + obj-$(CONFIG_VIODASD) += viodasd.o + obj-$(CONFIG_BLK_DEV_SX8) += sx8.o + obj-$(CONFIG_BLK_DEV_UB) += ub.o ++obj-$(CONFIG_BLK_DEV_CF_MIPS) += rb500/ + +diff -urN linux.old/drivers/block/rb500/ata.c linux.dev/drivers/block/rb500/ata.c +--- linux.old/drivers/block/rb500/ata.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/drivers/block/rb500/ata.c 2006-10-26 00:11:14.000000000 +0200 +@@ -0,0 +1,485 @@ ++/* CF-mips driver ++ This is a block driver for the direct (mmaped) interface to the CF-slot, ++ found in Routerboard.com's RB532 board ++ See SDK provided from routerboard.com. ++ ++ Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6. ++ Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org> ++ ++ This work is redistributed under the terms of the GNU General Public License. ++*/ ++ ++#include <linux/kernel.h> /* printk() */ ++#include <linux/module.h> /* module to be loadable */ ++#include <linux/delay.h> ++#include <linux/sched.h> ++#include <linux/pci.h> ++#include <linux/ioport.h> /* request_mem_region() */ ++#include <asm/unaligned.h> /* ioremap() */ ++#include <asm/io.h> /* ioremap() */ ++#include <asm/rc32434/rb.h> ++ ++#include "ata.h" ++ ++#define REQUEST_MEM_REGION 0 ++#define DEBUG 1 ++ ++#if DEBUG ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++#define SECS 1000000 /* unit for wait_not_busy() is 1us */ ++ ++unsigned cf_head = 0; ++unsigned cf_cyl = 0; ++unsigned cf_spt = 0; ++unsigned cf_sectors = 0; ++static unsigned cf_block_size = 1; ++static void *baddr = 0; ++ ++#define DBUF32 ((volatile u32 *)((unsigned long)dev->baddr | ATA_DBUF_OFFSET)) ++ ++ ++static void cf_do_tasklet(unsigned long dev_l); ++ ++ ++static inline void wareg(u8 val, unsigned reg, struct cf_mips_dev* dev) ++{ ++ writeb(val, dev->baddr + ATA_REG_OFFSET + reg); ++} ++ ++static inline u8 rareg(unsigned reg, struct cf_mips_dev* dev) ++{ ++ return readb(dev->baddr + ATA_REG_OFFSET + reg); ++} ++ ++static inline int get_gpio_bit(gpio_func ofs, struct cf_mips_dev *dev) ++{ ++ return (gpio_get(ofs) >> dev->pin) & 1; ++} ++ ++static inline void set_gpio_bit(int bit, gpio_func ofs, struct cf_mips_dev *dev) ++{ ++ gpio_set(ofs, (1 << dev->pin), ((bit & 1) << dev->pin)); ++} ++ ++static inline int cfrdy(struct cf_mips_dev *dev) ++{ ++ return get_gpio_bit(DATA, dev); ++} ++ ++static inline void prepare_cf_irq(struct cf_mips_dev *dev) ++{ ++ set_gpio_bit(1, ILEVEL, dev); /* interrupt on cf ready (not busy) */ ++ set_gpio_bit(0, ISTAT, dev); /* clear interrupt status */ ++} ++ ++static inline int cf_present(struct cf_mips_dev* dev) ++{ ++ /* TODO: read and configure CIS into memory mapped mode ++ * TODO: parse CISTPL_CONFIG on CF+ cards to get base address (0x200) ++ * TODO: maybe adjust power saving setting for Hitachi Microdrive ++ */ ++ int i; ++ ++ /* setup CFRDY GPIO as input */ ++ set_gpio_bit(0, FUNC, dev); ++ set_gpio_bit(0, CFG, dev); ++ ++ for (i = 0; i < 0x10; ++i) { ++ if (rareg(i,dev) != 0xff) ++ return 1; ++ } ++ return 0; ++} ++ ++static inline int is_busy(struct cf_mips_dev *dev) ++{ ++ return !cfrdy(dev); ++} ++ ++static int wait_not_busy(int to_us, int wait_for_busy,struct cf_mips_dev *dev) ++{ ++ int us_passed = 0; ++ if (wait_for_busy && !is_busy(dev)) { ++ /* busy must appear within 400ns, ++ * but it may dissapear before we see it ++ * => must not wait for busy in a loop ++ */ ++ ndelay(400); ++ } ++ ++ do { ++ if (us_passed) ++ udelay(1); /* never reached in async mode */ ++ if (!is_busy(dev)) { ++ if (us_passed > 1 * SECS) { ++ printk(KERN_WARNING "cf-mips: not busy ok (after %dus)" ++ ", status 0x%02x\n", us_passed, (unsigned) rareg(ATA_REG_ST,dev)); ++ } ++ return CF_TRANS_OK; ++ } ++ if (us_passed == 1 * SECS) { ++ printk(KERN_WARNING "cf-mips: wait not busy %dus..\n", to_us); ++ } ++ if (dev->async_mode) { ++ dev->to_timer.expires = jiffies + (to_us * HZ / SECS); ++ dev->irq_enable_time = jiffies; ++ prepare_cf_irq(dev); ++ if (is_busy(dev)) { ++ add_timer(&dev->to_timer); ++ enable_irq(dev->irq); ++ return CF_TRANS_IN_PROGRESS; ++ } ++ continue; ++ } ++ ++us_passed; ++ } while (us_passed < to_us); ++ ++ printk(KERN_ERR "cf-mips: wait not busy timeout (%dus)" ++ ", status 0x%02x, state %d\n", ++ to_us, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate); ++ return CF_TRANS_FAILED; ++} ++ ++static irqreturn_t cf_irq_handler(int irq, void *dev_id) ++{ ++ /* While tasklet has not disabled irq, irq will be retried all the time ++ * because of ILEVEL matching GPIO pin status => deadlock. ++ * To avoid this, we change ILEVEL to 0. ++ */ ++ struct cf_mips_dev *dev=dev_id; ++ ++ set_gpio_bit(0, ILEVEL, dev); ++ set_gpio_bit(0, ISTAT, dev); ++ ++ del_timer(&dev->to_timer); ++ tasklet_schedule(&dev->tasklet); ++ return IRQ_HANDLED; ++} ++ ++static int do_reset(struct cf_mips_dev *dev) ++{ ++ printk(KERN_INFO "cf-mips: resetting..\n"); ++ ++ wareg(ATA_REG_DC_SRST, ATA_REG_DC,dev); ++ udelay(1); /* FIXME: how long should we wait here? */ ++ wareg(0, ATA_REG_DC,dev); ++ ++ return wait_not_busy(30 * SECS, 1,dev); ++} ++ ++static int set_multiple(struct cf_mips_dev *dev) ++{ ++ if (dev->block_size <= 1) ++ return CF_TRANS_OK; ++ ++ wareg(dev->block_size, ATA_REG_SC,dev); ++ wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev); ++ wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev); ++ ++ return wait_not_busy(10 * SECS, 1,dev); ++} ++ ++static int set_cmd(struct cf_mips_dev *dev) ++{ ++ //DEBUGP(KERN_INFO "cf-mips: ata cmd 0x%02x\n", dev->tcmd); ++ // sector_count should be <=24 bits.. ++ BUG_ON(dev->tsect_start>=0x10000000); ++ // This way, it addresses 2^24 * 512 = 128G ++ ++ if (dev->tsector_count) { ++ wareg(dev->tsector_count & 0xff, ATA_REG_SC,dev); ++ wareg(dev->tsect_start & 0xff, ATA_REG_SN,dev); ++ wareg((dev->tsect_start >> 8) & 0xff, ATA_REG_CL,dev); ++ wareg((dev->tsect_start >> 16) & 0xff, ATA_REG_CH,dev); ++ } ++ wareg(((dev->tsect_start >> 24) & 0x0f) | ATA_REG_DH_BASE | ATA_REG_DH_LBA, ++ ATA_REG_DH,dev); /* select drive on all commands */ ++ wareg(dev->tcmd, ATA_REG_CMD,dev); ++ return wait_not_busy(10 * SECS, 1,dev); ++} ++ ++static int do_trans(struct cf_mips_dev *dev) ++{ ++ int res; ++ unsigned st; ++ int transfered; ++ ++ //printk("do_trans: %d sectors left\n",dev->tsectors_left); ++ while (dev->tsectors_left) { ++ transfered = 0; ++ ++ st = rareg(ATA_REG_ST,dev); ++ if (!(st & ATA_REG_ST_DRQ)) { ++ printk(KERN_ERR "cf-mips: do_trans without DRQ (status 0x%x)!\n", st); ++ if (st & ATA_REG_ST_ERR) { ++ int errId = rareg(ATA_REG_ERR,dev); ++ printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n", ++ (dev->tread ? "read" : "write"), st, errId); ++ } ++ return CF_TRANS_FAILED; ++ } ++ do { /* Fill/read the buffer one block */ ++ u32 *qbuf, *qend; ++ qbuf = (u32 *)dev->tbuf; ++ qend = qbuf + CF_SECT_SIZE / sizeof(u32); ++ if (dev->tread) { ++ while (qbuf!=qend) ++ put_unaligned(*DBUF32,qbuf++); ++ //*(qbuf++) = *DBUF32; ++ } ++ else { ++ while(qbuf!=qend) ++ *DBUF32 = get_unaligned(qbuf++); ++ } ++ ++ dev->tsectors_left--; ++ dev->tbuf += CF_SECT_SIZE; ++ dev->tbuf_size -= CF_SECT_SIZE; ++ transfered++; ++ } while (transfered != dev->block_size && dev->tsectors_left > 0); ++ ++ res = wait_not_busy(10 * SECS, 1,dev); ++ if (res != CF_TRANS_OK) ++ return res; ++ }; ++ ++ st = rareg(ATA_REG_ST,dev); ++ if (st & (ATA_REG_ST_DRQ | ATA_REG_ST_DWF | ATA_REG_ST_ERR)) { ++ if (st & ATA_REG_ST_DRQ) { ++ printk(KERN_ERR "cf-mips: DRQ after all %d sectors are %s" ++ ", status 0x%x\n", dev->tsector_count, (dev->tread ? "read" : "written"), st); ++ } else if (st & ATA_REG_ST_DWF) { ++ printk(KERN_ERR "cf-mips: write fault, status 0x%x\n", st); ++ } else { ++ int errId = rareg(ATA_REG_ERR,dev); ++ printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n", ++ (dev->tread ? "read" : "write"), st, errId); ++ } ++ return CF_TRANS_FAILED; ++ } ++ return CF_TRANS_OK; ++} ++ ++static int cf_do_state(struct cf_mips_dev *dev) ++{ ++ int res; ++ switch (dev->tstate) { /* fall through everywhere */ ++ case TS_IDLE: ++ dev->tstate = TS_READY; ++ if (is_busy(dev)) { ++ dev->tstate = TS_AFTER_RESET; ++ res = do_reset(dev); ++ if (res != CF_TRANS_OK) ++ break; ++ } ++ case TS_AFTER_RESET: ++ if (dev->tstate == TS_AFTER_RESET) { ++ dev->tstate = TS_READY; ++ res = set_multiple(dev); ++ if (res != CF_TRANS_OK) ++ break; ++ } ++ case TS_READY: ++ dev->tstate = TS_CMD; ++ res = set_cmd(dev); ++ if (res != CF_TRANS_OK) ++ break;; ++ case TS_CMD: ++ dev->tstate = TS_TRANS; ++ case TS_TRANS: ++ res = do_trans(dev); ++ break; ++ default: ++ printk(KERN_ERR "cf-mips: BUG: unknown tstate %d\n", dev->tstate); ++ return CF_TRANS_FAILED; ++ } ++ if (res != CF_TRANS_IN_PROGRESS) ++ dev->tstate = TS_IDLE; ++ return res; ++} ++ ++static void cf_do_tasklet(unsigned long dev_l) ++{ ++ struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l; ++ int res; ++ ++ disable_irq(dev->irq); ++ ++ if (dev->tstate == TS_IDLE) ++ return; /* can happen when irq is first registered */ ++ ++#if 0 ++ DEBUGP(KERN_WARNING "cf-mips: not busy ok (tasklet) status 0x%02x\n", ++ (unsigned) rareg(ATA_REG_ST,dev)); ++#endif ++ ++ res = cf_do_state(dev); ++ if (res == CF_TRANS_IN_PROGRESS) ++ return; ++ cf_async_trans_done(dev,res); ++} ++ ++static void cf_async_timeout(unsigned long dev_l) ++{ ++ struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l; ++ disable_irq(dev->irq); ++ /* Perhaps send abort to the device? */ ++ printk(KERN_ERR "cf-mips: wait not busy timeout (%lus)" ++ ", status 0x%02x, state %d\n", ++ jiffies - dev->irq_enable_time, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate); ++ dev->tstate = TS_IDLE; ++ cf_async_trans_done(dev,CF_TRANS_FAILED); ++} ++ ++int cf_do_transfer(struct cf_mips_dev* dev,sector_t sector, unsigned long nsect, ++ char* buffer, int is_write) ++{ ++ BUG_ON(dev->tstate!=TS_IDLE); ++ if (nsect > ATA_MAX_SECT_PER_CMD) { ++ printk(KERN_WARNING "cf-mips: sector count %lu out of range\n",nsect); ++ return CF_TRANS_FAILED; ++ } ++ if (sector + nsect > dev->sectors) { ++ printk(KERN_WARNING "cf-mips: sector %lu out of range\n",sector); ++ return CF_TRANS_FAILED; ++ } ++ dev->tbuf = buffer; ++ dev->tbuf_size = nsect*512; ++ dev->tsect_start = sector; ++ dev->tsector_count = nsect; ++ dev->tsectors_left = dev->tsector_count; ++ dev->tread = (is_write)?0:1; ++ ++ dev->tcmd = (dev->block_size == 1 ? ++ (is_write ? ATA_CMD_WRITE_SECTORS : ATA_CMD_READ_SECTORS) : ++ (is_write ? ATA_CMD_WRITE_MULTIPLE : ATA_CMD_READ_MULTIPLE)); ++ ++ return cf_do_state(dev); ++} ++ ++static int do_identify(struct cf_mips_dev *dev) ++{ ++ u16 sbuf[CF_SECT_SIZE >> 1]; ++ int res; ++ char tstr[17]; //serial ++ BUG_ON(dev->tstate!=TS_IDLE); ++ dev->tbuf = (char *) sbuf; ++ dev->tbuf_size = CF_SECT_SIZE; ++ dev->tsect_start = 0; ++ dev->tsector_count = 0; ++ dev->tsectors_left = 1; ++ dev->tread = 1; ++ dev->tcmd = ATA_CMD_IDENTIFY_DRIVE; ++ ++ DEBUGP(KERN_INFO "cf-mips: identify drive..\n"); ++ res = cf_do_state(dev); ++ if (res == CF_TRANS_IN_PROGRESS) { ++ printk(KERN_ERR "cf-mips: BUG: async identify cmd\n"); ++ return CF_TRANS_FAILED; ++ } ++ if (res != CF_TRANS_OK) ++ return 0; ++ ++ dev->head = sbuf[3]; ++ dev->cyl = sbuf[1]; ++ dev->spt = sbuf[6]; ++ dev->sectors = ((unsigned long) sbuf[7] << 16) | sbuf[8]; ++ dev->dtype=sbuf[0]; ++ memcpy(tstr,&sbuf[12],16); ++ tstr[16]=0; ++ printk(KERN_INFO "cf-mips: %s detected, C/H/S=%d/%d/%d sectors=%u (%uMB) Serial=%s\n", ++ (sbuf[0] == 0x848A ? "CF card" : "ATA drive"), dev->cyl, dev->head, ++ dev->spt, dev->sectors, dev->sectors >> 11,tstr); ++ return 1; ++} ++ ++static void init_multiple(struct cf_mips_dev * dev) ++{ ++ int res; ++ DEBUGP(KERN_INFO "cf-mips: detecting block size\n"); ++ ++ dev->block_size = 128; /* max block size = 128 sectors (64KB) */ ++ do { ++ wareg(dev->block_size, ATA_REG_SC,dev); ++ wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev); ++ wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev); ++ ++ res = wait_not_busy(10 * SECS, 1,dev); ++ if (res != CF_TRANS_OK) { ++ printk(KERN_ERR "cf-mips: failed to detect block size: busy!\n"); ++ dev->block_size = 1; ++ return; ++ } ++ if ((rareg(ATA_REG_ST,dev) & ATA_REG_ST_ERR) == 0) ++ break; ++ dev->block_size /= 2; ++ } while (dev->block_size > 1); ++ ++ printk(KERN_INFO "cf-mips: multiple sectors = %d\n", dev->block_size); ++} ++ ++int cf_init(struct cf_mips_dev *dev) ++{ ++ tasklet_init(&dev->tasklet,cf_do_tasklet,(unsigned long)dev); ++ dev->baddr = ioremap_nocache((unsigned long)dev->base, CFDEV_BUF_SIZE); ++ if (!dev->baddr) { ++ printk(KERN_ERR "cf-mips: cf_init: ioremap for (%lx,%x) failed\n", ++ (unsigned long) dev->base, CFDEV_BUF_SIZE); ++ return -EBUSY; ++ } ++ ++ if (!cf_present(dev)) { ++ printk(KERN_WARNING "cf-mips: cf card not present\n"); ++ iounmap(dev->baddr); ++ return -ENODEV; ++ } ++ ++ if (do_reset(dev) != CF_TRANS_OK) { ++ printk(KERN_ERR "cf-mips: cf reset failed\n"); ++ iounmap(dev->baddr); ++ return -EBUSY; ++ } ++ ++ if (!do_identify(dev)) { ++ printk(KERN_ERR "cf-mips: cf identify failed\n"); ++ iounmap(dev->baddr); ++ return -EBUSY; ++ } ++ ++/* set_apm_level(ATA_APM_WITH_STANDBY); */ ++ init_multiple(dev); ++ ++ init_timer(&dev->to_timer); ++ dev->to_timer.function = cf_async_timeout; ++ dev->to_timer.data = (unsigned long)dev; ++ ++ prepare_cf_irq(dev); ++ if (request_irq(dev->irq, cf_irq_handler, 0, "CF Mips", dev)) { ++ printk(KERN_ERR "cf-mips: failed to get irq\n"); ++ iounmap(dev->baddr); ++ return -EBUSY; ++ } ++ /* Disable below would be odd, because request will enable, and the tasklet ++ will disable it itself */ ++ //disable_irq(dev->irq); ++ ++ dev->async_mode = 1; ++ ++ return 0; ++} ++ ++void cf_cleanup(struct cf_mips_dev *dev) ++{ ++ iounmap(dev->baddr); ++ free_irq(dev->irq, NULL); ++#if REQUEST_MEM_REGION ++ release_mem_region((unsigned long)dev->base, CFDEV_BUF_SIZE); ++#endif ++} ++ ++ ++/*eof*/ +diff -urN linux.old/drivers/block/rb500/ata.h linux.dev/drivers/block/rb500/ata.h +--- linux.old/drivers/block/rb500/ata.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/drivers/block/rb500/ata.h 2006-10-26 00:11:14.000000000 +0200 +@@ -0,0 +1,143 @@ ++/* CF-mips driver ++ This is a block driver for the direct (mmaped) interface to the CF-slot, ++ found in Routerboard.com's RB532 board ++ See SDK provided from routerboard.com. ++ ++ Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6. ++ Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org> ++ ++ This work is redistributed under the terms of the GNU General Public License. ++*/ ++ ++#ifndef __CFMIPS_ATA_H__ ++#define __CFMIPS_ATA_H__ ++ ++#include <linux/interrupt.h> ++ ++#define CFG_DC_DEV1 (void*)0xb8010010 ++#define CFG_DC_DEVBASE 0x0 ++#define CFG_DC_DEVMASK 0x4 ++#define CFG_DC_DEVC 0x8 ++#define CFG_DC_DEVTC 0xC ++ ++#define CFDEV_BUF_SIZE 0x1000 ++#define ATA_CIS_OFFSET 0x200 ++#define ATA_REG_OFFSET 0x800 ++#define ATA_DBUF_OFFSET 0xC00 ++ ++#define ATA_REG_FEAT 0x1 ++#define ATA_REG_SC 0x2 ++#define ATA_REG_SN 0x3 ++#define ATA_REG_CL 0x4 ++#define ATA_REG_CH 0x5 ++#define ATA_REG_DH 0x6 ++#define ATA_REG_DH_BASE 0xa0 ++#define ATA_REG_DH_LBA 0x40 ++#define ATA_REG_DH_DRV 0x10 ++#define ATA_REG_CMD 0x7 ++#define ATA_REG_ST 0x7 ++#define ATA_REG_ST_BUSY 0x80 ++#define ATA_REG_ST_RDY 0x40 ++#define ATA_REG_ST_DWF 0x20 ++#define ATA_REG_ST_DSC 0x10 ++#define ATA_REG_ST_DRQ 0x08 ++#define ATA_REG_ST_CORR 0x04 ++#define ATA_REG_ST_ERR 0x01 ++#define ATA_REG_ERR 0xd ++#define ATA_REG_DC 0xe ++#define ATA_REG_DC_IEN 0x02 ++#define ATA_REG_DC_SRST 0x04 ++ ++#define ATA_CMD_READ_SECTORS 0x20 ++#define ATA_CMD_WRITE_SECTORS 0x30 ++#define ATA_CMD_EXEC_DRIVE_DIAG 0x90 ++#define ATA_CMD_READ_MULTIPLE 0xC4 ++#define ATA_CMD_WRITE_MULTIPLE 0xC5 ++#define ATA_CMD_SET_MULTIPLE 0xC6 ++#define ATA_CMD_IDENTIFY_DRIVE 0xEC ++#define ATA_CMD_SET_FEATURES 0xEF ++ ++#define ATA_FEATURE_ENABLE_APM 0x05 ++#define ATA_FEATURE_DISABLE_APM 0x85 ++#define ATA_APM_DISABLED 0x00 ++#define ATA_APM_MIN_POWER 0x01 ++#define ATA_APM_WITH_STANDBY 0x7f ++#define ATA_APM_WITHOUT_STANDBY 0x80 ++#define ATA_APM_MAX_PERFORMANCE 0xfe ++ ++#define CF_SECT_SIZE 0x200 ++/* That is the ratio CF_SECT_SIZE/512 (the kernel sector size) */ ++#define CF_KERNEL_MUL 1 ++#define ATA_MAX_SECT_PER_CMD 0x100 ++ ++#define CF_TRANS_FAILED 0 ++#define CF_TRANS_OK 1 ++#define CF_TRANS_IN_PROGRESS 2 ++ ++ ++enum trans_state { ++ TS_IDLE = 0, ++ TS_AFTER_RESET, ++ TS_READY, ++ TS_CMD, ++ TS_TRANS ++}; ++ ++// ++// #if DEBUG ++// static unsigned long busy_time; ++// #endif ++ ++/** Struct to hold the cfdev ++Actually, all the data here only has one instance. However, for ++reasons of programming conformity, it is passed around as a pointer ++*/ ++struct cf_mips_dev { ++ void *base; /* base address for I/O */ ++ void *baddr; /* remapped address */ ++ ++ int pin; /* gpio pin */ ++ int irq; /* gpio irq */ ++ ++ unsigned head; ++ unsigned cyl; ++ unsigned spt; ++ unsigned sectors; ++ ++ unsigned short block_size; ++ unsigned dtype ; // ATA or CF ++ struct request_queue *queue; ++ struct gendisk *gd; ++ ++ /* Transaction state */ ++ enum trans_state tstate; ++ char *tbuf; ++ unsigned long tbuf_size; ++ sector_t tsect_start; ++ unsigned tsector_count; ++ unsigned tsectors_left; ++ int tread; ++ unsigned tcmd; ++ int async_mode; ++ unsigned long irq_enable_time; ++ ++ struct request *active_req; /* A request is being carried out. Is that different from tstate? */ ++ int users; ++ struct timer_list to_timer; ++ struct tasklet_struct tasklet; ++ ++ /** This lock ensures that the requests to this device are all done ++ atomically. Transfers can run in parallel, requests are all queued ++ one-by-one */ ++ spinlock_t lock; ++}; ++ ++int cf_do_transfer(struct cf_mips_dev* dev,sector_t sector, unsigned long nsect, ++ char* buffer, int is_write); ++int cf_init(struct cf_mips_dev* dev); ++void cf_cleanup(struct cf_mips_dev* dev); ++ ++void cf_async_trans_done(struct cf_mips_dev* dev, int result); ++// void *cf_get_next_buf(unsigned long *buf_size); ++ ++#endif +diff -urN linux.old/drivers/block/rb500/bdev.c linux.dev/drivers/block/rb500/bdev.c +--- linux.old/drivers/block/rb500/bdev.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/drivers/block/rb500/bdev.c 2006-10-26 00:11:14.000000000 +0200 +@@ -0,0 +1,339 @@ ++/* CF-mips driver ++ This is a block driver for the direct (mmaped) interface to the CF-slot, ++ found in Routerboard.com's RB532 board ++ See SDK provided from routerboard.com. ++ ++ Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6. ++ Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org> ++ ++ This work is redistributed under the terms of the GNU General Public License. ++*/ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/wait.h> ++#include <linux/fs.h> ++#include <linux/genhd.h> ++#include <linux/blkdev.h> ++#include <linux/blkpg.h> ++#include <linux/hdreg.h> ++#include <linux/platform_device.h> ++ ++#include <asm/uaccess.h> ++#include <asm/io.h> ++ ++#include <asm/rc32434/rb.h> ++ ++#ifdef DEBUG ++#define DEBUGP printk ++#define DLEVEL 1 ++#else ++#define DEBUGP(format, args...) ++#define DLEVEL 0 ++#endif ++ ++#define CF_MIPS_MAJOR 13 ++#define MAJOR_NR CF_MIPS_MAJOR ++#define CF_MAX_PART 16 /* max 15 partitions */ ++ ++#include "ata.h" ++ ++//extern struct block_device_operations cf_bdops; ++ ++// static struct hd_struct cf_parts[CF_MAX_PART]; ++// static int cf_part_sizes[CF_MAX_PART]; ++// static int cf_hsect_sizes[CF_MAX_PART]; ++// static int cf_max_sectors[CF_MAX_PART]; ++// static int cf_blksize_sizes[CF_MAX_PART]; ++ ++// static spinlock_t lock = SPIN_LOCK_UNLOCKED; ++ ++// volatile int cf_busy = 0; ++ ++static struct request *active_req = NULL; ++ ++static int cf_open (struct inode *, struct file *); ++static int cf_release (struct inode *, struct file *); ++static int cf_ioctl (struct inode *, struct file *, unsigned, unsigned long); ++ ++static void cf_request(request_queue_t * q); ++static int cf_transfer(const struct request *req); ++ ++/*long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); ++long (*compat_ioctl) (struct file *, unsigned, unsigned long);*/ ++// int (*direct_access) (struct block_device *, sector_t, unsigned long *); ++// int (*media_changed) (struct gendisk *); ++// int (*revalidate_disk) (struct gendisk *); ++ ++static struct block_device_operations cf_bdops = { ++ .owner = THIS_MODULE, ++ .open = cf_open, ++ .release = cf_release, ++ .ioctl = cf_ioctl, ++ .media_changed = NULL, ++ .unlocked_ioctl = NULL, ++ .revalidate_disk = NULL, ++ .compat_ioctl = NULL, ++ .direct_access = NULL ++}; ++ ++ ++int cf_mips_probe(struct platform_device *pdev) ++{ ++ struct gendisk* cf_gendisk=NULL; ++ struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data; ++ struct cf_mips_dev *dev; ++ struct resource *r; ++ int reg_result; ++ ++ reg_result = register_blkdev(MAJOR_NR, "cf-mips"); ++ if (reg_result < 0) { ++ printk(KERN_WARNING "cf-mips: can't get major %d\n", MAJOR_NR); ++ return reg_result; ++ } ++ ++ dev = (struct cf_mips_dev *)kmalloc(sizeof(struct cf_mips_dev),GFP_KERNEL); ++ if (!dev) ++ goto out_err; ++ memset(dev, 0, sizeof(struct cf_mips_dev)); ++ cdev->dev = dev; ++ ++ dev->pin = cdev->gpio_pin; ++ dev->irq = platform_get_irq_byname(pdev, "cf_irq"); ++ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cf_membase"); ++ dev->base = (void *) r->start; ++ ++ if (cf_init(dev)) goto out_err; ++ printk("init done"); ++ ++ spin_lock_init(&dev->lock); ++ dev->queue = blk_init_queue(cf_request,&dev->lock); ++ if (!dev->queue){ ++ printk(KERN_ERR "cf-mips: no mem for queue\n"); ++ goto out_err; ++ } ++ blk_queue_max_sectors(dev->queue,ATA_MAX_SECT_PER_CMD); ++ ++ /* For memory devices, it is always better to avoid crossing segments ++ inside the same request. */ ++/* if (dev->dtype==0x848A){ ++ printk(KERN_INFO "Setting boundary for cf to 0x%x",(dev->block_size*512)-1); ++ blk_queue_segment_boundary(dev->queue, (dev->block_size*512)-1); ++ }*/ ++ ++ dev->gd = alloc_disk(CF_MAX_PART); ++ cf_gendisk = dev->gd; ++ cdev->gd = dev->gd; ++ if (!cf_gendisk) goto out_err; /* Last of these goto's */ ++ ++ cf_gendisk->major = MAJOR_NR; ++ cf_gendisk->first_minor = 0; ++ cf_gendisk->queue=dev->queue; ++ BUG_ON(cf_gendisk->minors != CF_MAX_PART); ++ strcpy(cf_gendisk->disk_name,"cfa"); ++ cf_gendisk->fops = &cf_bdops; ++ cf_gendisk->flags = 0 ; /* is not yet GENHD_FL_REMOVABLE */ ++ cf_gendisk->private_data=dev; ++ ++ set_capacity(cf_gendisk,dev->sectors * CF_KERNEL_MUL); ++ ++ /* Let the disk go live */ ++ add_disk(cf_gendisk); ++#if 0 ++ result = cf_init(); ++ ++ /* default cfg for all partitions */ ++ memset(cf_parts, 0, sizeof (cf_parts[0]) * CF_MAX_PART); ++ memset(cf_part_sizes, 0, sizeof (cf_part_sizes[0]) * CF_MAX_PART); ++ for (i = 0; i < CF_MAX_PART; ++i) { ++ cf_hsect_sizes[i] = CF_SECT_SIZE; ++ cf_max_sectors[i] = ATA_MAX_SECT_PER_CMD; ++ cf_blksize_sizes[i] = BLOCK_SIZE; ++ } ++ ++ /* setup info for whole disk (partition 0) */ ++ cf_part_sizes[0] = cf_sectors / 2; ++ cf_parts[0].nr_sects = cf_sectors; ++ ++ blk_size[MAJOR_NR] = cf_part_sizes; ++ blksize_size[MAJOR_NR] = cf_blksize_sizes; ++ max_sectors[MAJOR_NR] = cf_max_sectors; ++ hardsect_size[MAJOR_NR] = cf_hsect_sizes; ++ read_ahead[MAJOR_NR] = 8; /* (4kB) */ ++ ++ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); ++ ++ add_gendisk(&cf_gendisk); ++#endif ++// printk(KERN_INFO "cf-mips partition check: \n"); ++// register_disk(cf_gendisk, MKDEV(MAJOR_NR, 0), CF_MAX_PART, ++// &cf_bdops, dev->sectors); ++ return 0; ++ ++out_err: ++ if (dev->queue){ ++ blk_cleanup_queue(dev->queue); ++ } ++ if (reg_result) { ++ unregister_blkdev(MAJOR_NR, "cf-mips"); ++ return reg_result; ++ } ++ if (dev){ ++ cf_cleanup(dev); ++ kfree(dev); ++ } ++ return 1; ++} ++ ++static int ++cf_mips_remove(struct platform_device *pdev) ++{ ++ struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data; ++ struct cf_mips_dev *dev = (struct cf_mips_dev *) cdev->dev; ++ ++ unregister_blkdev(MAJOR_NR, "cf-mips"); ++ blk_cleanup_queue(dev->queue); ++ ++ del_gendisk(dev->gd); ++ cf_cleanup(dev); ++ return 0; ++} ++ ++ ++static struct platform_driver cf_driver = { ++ .driver.name = "rb500-cf", ++ .probe = cf_mips_probe, ++ .remove = cf_mips_remove, ++}; ++ ++static int __init cf_mips_init(void) ++{ ++ printk(KERN_INFO "cf-mips module loaded\n"); ++ return platform_driver_register(&cf_driver); ++} ++ ++static void cf_mips_cleanup(void) ++{ ++ platform_driver_unregister(&cf_driver); ++ printk(KERN_INFO "cf-mips module removed\n"); ++} ++ ++module_init(cf_mips_init); ++module_exit(cf_mips_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_BLOCKDEV_MAJOR(CF_MIPS_MAJOR); ++ ++ ++static int cf_open(struct inode *inode, struct file *filp) ++{ ++ struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data; ++ int minor = MINOR(inode->i_rdev); ++ ++ if (minor >= CF_MAX_PART) ++ return -ENODEV; ++ //DEBUGP(KERN_INFO "cf-mips module opened, minor %d\n", minor); ++ spin_lock(&dev->lock); ++ dev->users++; ++ spin_unlock(&dev->lock); ++ filp->private_data=dev; ++ ++ /* dirty workaround to set CFRDY GPIO as an input when some other ++ program sets it as an output */ ++ gpio_set(CFG, (1 << dev->pin), 0); ++ return 0; /* success */ ++} ++ ++static int cf_release(struct inode *inode, struct file *filp) ++{ ++ int minor = MINOR(inode->i_rdev); ++ struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data; ++ spin_lock(&dev->lock); ++ dev->users--; ++ spin_unlock(&dev->lock); ++ return 0; ++} ++ ++static int cf_ioctl(struct inode *inode, struct file *filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ unsigned minor = MINOR(inode->i_rdev); ++ struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data; ++ ++ DEBUGP(KERN_INFO "cf_ioctl cmd %u\n", cmd); ++ switch (cmd) { ++ case BLKRRPART: /* re-read partition table */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ printk(KERN_INFO "cf-mips partition check: \n"); ++ register_disk(dev->gd); ++ return 0; ++ ++ case HDIO_GETGEO: ++ { ++ struct hd_geometry geo; ++ geo.cylinders = dev->cyl; ++ geo.heads = dev->head; ++ geo.sectors = dev->spt; ++ geo.start = (*dev->gd->part)[minor].start_sect; ++ if (copy_to_user((void *) arg, &geo, sizeof (geo))) ++ return -EFAULT; ++ } ++ return 0; ++ } ++ ++ return -EINVAL; /* unknown command */ ++} ++ ++static void cf_request(request_queue_t * q) ++{ ++ struct cf_mips_dev* dev; ++ ++ struct request * req; ++ int status; ++ ++ /* We could have q->queuedata = dev , but haven't yet. */ ++ if (active_req) ++ return; ++ ++ while ((req=elv_next_request(q))!=NULL){ ++ dev=req->rq_disk->private_data; ++ status=cf_transfer(req); ++ if (status==CF_TRANS_IN_PROGRESS){ ++ active_req=req; ++ return; ++ } ++ end_request(req,status); ++ } ++} ++ ++static int cf_transfer(const struct request *req) ++{ ++ struct cf_mips_dev* dev=req->rq_disk->private_data; ++ ++ if (!blk_fs_request(req)){ ++ if (printk_ratelimit()) ++ printk(KERN_WARNING "cf-mips: skipping non-fs request 0x%x\n",req->cmd[0]); ++ return CF_TRANS_FAILED; ++ } ++ ++ return cf_do_transfer(dev,req->sector,req->current_nr_sectors,req->buffer,rq_data_dir(req)); ++} ++ ++void cf_async_trans_done(struct cf_mips_dev * dev,int result) ++{ ++ struct request *req; ++ ++ spin_lock(&dev->lock); ++ req=active_req; ++ active_req=NULL; ++ end_request(req,result); ++ spin_unlock(&dev->lock); ++ ++ spin_lock(&dev->lock); ++ cf_request(dev->queue); ++ spin_unlock(&dev->lock); ++} ++ +diff -urN linux.old/drivers/block/rb500/Makefile linux.dev/drivers/block/rb500/Makefile +--- linux.old/drivers/block/rb500/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/drivers/block/rb500/Makefile 2006-10-26 00:11:14.000000000 +0200 +@@ -0,0 +1,3 @@ ++## Makefile for the RB532 CF port ++ ++obj-y += bdev.o ata.o diff --git a/target/linux/rb1xx-2.6/patches/500-Nand.patch b/target/linux/rb1xx-2.6/patches/500-Nand.patch new file mode 100644 index 0000000..05cc800 --- /dev/null +++ b/target/linux/rb1xx-2.6/patches/500-Nand.patch @@ -0,0 +1,27 @@ +diff -urN linux.old/drivers/mtd/nand/Kconfig linux.dev/drivers/mtd/nand/Kconfig +--- linux.old/drivers/mtd/nand/Kconfig 2006-11-29 22:57:37.000000000 +0100 ++++ linux.dev/drivers/mtd/nand/Kconfig 2006-12-14 04:38:51.000000000 +0100 +@@ -75,6 +75,12 @@ + help + Support for NAND flash on Technologic Systems TS-7250 platform. + ++config MTD_NAND_RB500 ++ tristate "NAND Flash device on RB500 board" ++ depends on MTD_NAND ++ help ++ Support for NAND flash on RB500 platform. ++ + config MTD_NAND_IDS + tristate + +diff -urN linux.old/drivers/mtd/nand/Makefile linux.dev/drivers/mtd/nand/Makefile +--- linux.old/drivers/mtd/nand/Makefile 2006-11-29 22:57:37.000000000 +0100 ++++ linux.dev/drivers/mtd/nand/Makefile 2006-12-14 04:38:51.000000000 +0100 +@@ -9,6 +9,7 @@ + obj-$(CONFIG_MTD_NAND_SPIA) += spia.o + obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o + obj-$(CONFIG_MTD_NAND_TOTO) += toto.o ++obj-$(CONFIG_MTD_NAND_RB500) += rbmipsnand.o + obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o + obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o + obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o diff --git a/target/linux/rb1xx-2.6/profiles/100-Atheros.mk b/target/linux/rb1xx-2.6/profiles/100-Atheros.mk new file mode 100644 index 0000000..0295868 --- /dev/null +++ b/target/linux/rb1xx-2.6/profiles/100-Atheros.mk @@ -0,0 +1,17 @@ +# +# Copyright (C) 2006 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +define Profile/Atheros + NAME:=Atheros WiFi (default) + PACKAGES:=kmod-madwifi +endef + +define Profile/Atheros/Description + Package set compatible with hardware using Atheros WiFi cards +endef +$(eval $(call Profile,Atheros)) + diff --git a/target/linux/rb1xx-2.6/profiles/105-Texas.mk b/target/linux/rb1xx-2.6/profiles/105-Texas.mk new file mode 100644 index 0000000..727a0c0 --- /dev/null +++ b/target/linux/rb1xx-2.6/profiles/105-Texas.mk @@ -0,0 +1,17 @@ +# +# Copyright (C) 2006 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +define Profile/Texas + NAME:=Texas Instruments WiFi + PACKAGES:=kmod-acx +endef + +define Profile/Texas/Description + Package set compatible with hardware using Texas Instruments WiFi cards +endef +$(eval $(call Profile,Texas)) + diff --git a/target/linux/rb1xx-2.6/profiles/110-None.mk b/target/linux/rb1xx-2.6/profiles/110-None.mk new file mode 100644 index 0000000..2fcfacd --- /dev/null +++ b/target/linux/rb1xx-2.6/profiles/110-None.mk @@ -0,0 +1,17 @@ +# +# Copyright (C) 2006 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +define Profile/None + NAME:=No WiFi + PACKAGES:= +endef + +define Profile/None/Description + Package set without WiFi support +endef +$(eval $(call Profile,None)) + |