--- linux-2.6.13-rc2/arch/alpha/kernel/alpha_ksyms.c 2005-06-17 16:03:57.000000000 -0700 +++ devel/arch/alpha/kernel/alpha_ksyms.c 2005-07-12 00:27:15.000000000 -0700 @@ -185,15 +185,6 @@ EXPORT_SYMBOL(smp_num_cpus); EXPORT_SYMBOL(smp_call_function); EXPORT_SYMBOL(smp_call_function_on_cpu); EXPORT_SYMBOL(_atomic_dec_and_lock); -#ifdef CONFIG_DEBUG_SPINLOCK -EXPORT_SYMBOL(_raw_spin_unlock); -EXPORT_SYMBOL(debug_spin_lock); -EXPORT_SYMBOL(debug_spin_trylock); -#endif -#ifdef CONFIG_DEBUG_RWLOCK -EXPORT_SYMBOL(_raw_write_lock); -EXPORT_SYMBOL(_raw_read_lock); -#endif EXPORT_SYMBOL(cpu_present_mask); #endif /* CONFIG_SMP */ --- linux-2.6.13-rc2/arch/alpha/kernel/osf_sys.c 2005-06-17 16:03:57.000000000 -0700 +++ devel/arch/alpha/kernel/osf_sys.c 2005-07-12 00:27:24.000000000 -0700 @@ -974,6 +974,7 @@ osf_select(int n, fd_set __user *inp, fd size_t size; long timeout; int ret = -EINVAL; + struct fdtable *fdt; timeout = MAX_SCHEDULE_TIMEOUT; if (tvp) { @@ -995,7 +996,8 @@ osf_select(int n, fd_set __user *inp, fd } } - if (n < 0 || n > current->files->max_fdset) + fdt = files_fdtable(current->files); + if (n < 0 || n > fdt->max_fdset) goto out_nofds; /* --- linux-2.6.13-rc2/arch/alpha/kernel/pci.c 2005-03-01 23:38:34.000000000 -0800 +++ devel/arch/alpha/kernel/pci.c 2005-07-12 00:27:14.000000000 -0700 @@ -350,8 +350,24 @@ pcibios_resource_to_bus(struct pci_dev * region->end = res->end - offset; } +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + struct pci_controller *hose = (struct pci_controller *)dev->sysdata; + unsigned long offset = 0; + + if (res->flags & IORESOURCE_IO) + offset = hose->io_space->start; + else if (res->flags & IORESOURCE_MEM) + offset = hose->mem_space->start; + + res->start = region->start + offset; + res->end = region->end + offset; +} + #ifdef CONFIG_HOTPLUG EXPORT_SYMBOL(pcibios_resource_to_bus); +EXPORT_SYMBOL(pcibios_bus_to_resource); #endif int --- linux-2.6.13-rc2/arch/alpha/kernel/smp.c 2005-06-17 16:03:57.000000000 -0700 +++ devel/arch/alpha/kernel/smp.c 2005-07-12 00:27:15.000000000 -0700 @@ -989,175 +989,3 @@ flush_icache_user_range(struct vm_area_s preempt_enable(); } - -#ifdef CONFIG_DEBUG_SPINLOCK -void -_raw_spin_unlock(spinlock_t * lock) -{ - mb(); - lock->lock = 0; - - lock->on_cpu = -1; - lock->previous = NULL; - lock->task = NULL; - lock->base_file = "none"; - lock->line_no = 0; -} - -void -debug_spin_lock(spinlock_t * lock, const char *base_file, int line_no) -{ - long tmp; - long stuck; - void *inline_pc = __builtin_return_address(0); - unsigned long started = jiffies; - int printed = 0; - int cpu = smp_processor_id(); - - stuck = 1L << 30; - try_again: - - /* Use sub-sections to put the actual loop at the end - of this object file's text section so as to perfect - branch prediction. */ - __asm__ __volatile__( - "1: ldl_l %0,%1\n" - " subq %2,1,%2\n" - " blbs %0,2f\n" - " or %0,1,%0\n" - " stl_c %0,%1\n" - " beq %0,3f\n" - "4: mb\n" - ".subsection 2\n" - "2: ldl %0,%1\n" - " subq %2,1,%2\n" - "3: blt %2,4b\n" - " blbs %0,2b\n" - " br 1b\n" - ".previous" - : "=r" (tmp), "=m" (lock->lock), "=r" (stuck) - : "1" (lock->lock), "2" (stuck) : "memory"); - - if (stuck < 0) { - printk(KERN_WARNING - "%s:%d spinlock stuck in %s at %p(%d)" - " owner %s at %p(%d) %s:%d\n", - base_file, line_no, - current->comm, inline_pc, cpu, - lock->task->comm, lock->previous, - lock->on_cpu, lock->base_file, lock->line_no); - stuck = 1L << 36; - printed = 1; - goto try_again; - } - - /* Exiting. Got the lock. */ - lock->on_cpu = cpu; - lock->previous = inline_pc; - lock->task = current; - lock->base_file = base_file; - lock->line_no = line_no; - - if (printed) { - printk(KERN_WARNING - "%s:%d spinlock grabbed in %s at %p(%d) %ld ticks\n", - base_file, line_no, current->comm, inline_pc, - cpu, jiffies - started); - } -} - -int -debug_spin_trylock(spinlock_t * lock, const char *base_file, int line_no) -{ - int ret; - if ((ret = !test_and_set_bit(0, lock))) { - lock->on_cpu = smp_processor_id(); - lock->previous = __builtin_return_address(0); - lock->task = current; - } else { - lock->base_file = base_file; - lock->line_no = line_no; - } - return ret; -} -#endif /* CONFIG_DEBUG_SPINLOCK */ - -#ifdef CONFIG_DEBUG_RWLOCK -void _raw_write_lock(rwlock_t * lock) -{ - long regx, regy; - int stuck_lock, stuck_reader; - void *inline_pc = __builtin_return_address(0); - - try_again: - - stuck_lock = 1<<30; - stuck_reader = 1<<30; - - __asm__ __volatile__( - "1: ldl_l %1,%0\n" - " blbs %1,6f\n" - " blt %1,8f\n" - " mov 1,%1\n" - " stl_c %1,%0\n" - " beq %1,6f\n" - "4: mb\n" - ".subsection 2\n" - "6: blt %3,4b # debug\n" - " subl %3,1,%3 # debug\n" - " ldl %1,%0\n" - " blbs %1,6b\n" - "8: blt %4,4b # debug\n" - " subl %4,1,%4 # debug\n" - " ldl %1,%0\n" - " blt %1,8b\n" - " br 1b\n" - ".previous" - : "=m" (*(volatile int *)lock), "=&r" (regx), "=&r" (regy), - "=&r" (stuck_lock), "=&r" (stuck_reader) - : "0" (*(volatile int *)lock), "3" (stuck_lock), "4" (stuck_reader) : "memory"); - - if (stuck_lock < 0) { - printk(KERN_WARNING "write_lock stuck at %p\n", inline_pc); - goto try_again; - } - if (stuck_reader < 0) { - printk(KERN_WARNING "write_lock stuck on readers at %p\n", - inline_pc); - goto try_again; - } -} - -void _raw_read_lock(rwlock_t * lock) -{ - long regx; - int stuck_lock; - void *inline_pc = __builtin_return_address(0); - - try_again: - - stuck_lock = 1<<30; - - __asm__ __volatile__( - "1: ldl_l %1,%0;" - " blbs %1,6f;" - " subl %1,2,%1;" - " stl_c %1,%0;" - " beq %1,6f;" - "4: mb\n" - ".subsection 2\n" - "6: ldl %1,%0;" - " blt %2,4b # debug\n" - " subl %2,1,%2 # debug\n" - " blbs %1,6b;" - " br 1b\n" - ".previous" - : "=m" (*(volatile int *)lock), "=&r" (regx), "=&r" (stuck_lock) - : "0" (*(volatile int *)lock), "2" (stuck_lock) : "memory"); - - if (stuck_lock < 0) { - printk(KERN_WARNING "read_lock stuck at %p\n", inline_pc); - goto try_again; - } -} -#endif /* CONFIG_DEBUG_RWLOCK */ --- linux-2.6.13-rc2/arch/arm/configs/enp2611_defconfig 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/configs/enp2611_defconfig 2005-07-12 00:26:30.000000000 -0700 @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.12-git6 -# Sat Jun 25 00:57:29 2005 +# Linux kernel version: 2.6.13-rc2 +# Thu Jul 7 16:41:21 2005 # CONFIG_ARM=y CONFIG_MMU=y @@ -137,6 +137,7 @@ CONFIG_PCI_NAMES=y # # CONFIG_SMP is not set # CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set # CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y @@ -345,10 +346,9 @@ CONFIG_PACKET_MMAP=y CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y -CONFIG_IP_FIB_HASH=y -# CONFIG_IP_FIB_TRIE is not set # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y @@ -363,17 +363,8 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_TUNNEL is not set # CONFIG_IP_TCPDIAG is not set # CONFIG_IP_TCPDIAG_IPV6 is not set - -# -# TCP congestion control -# +# CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_BIC=y -CONFIG_TCP_CONG_WESTWOOD=m -CONFIG_TCP_CONG_HTCP=m -# CONFIG_TCP_CONG_HSTCP is not set -# CONFIG_TCP_CONG_HYBLA is not set -# CONFIG_TCP_CONG_VEGAS is not set -# CONFIG_TCP_CONG_SCALABLE is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -931,4 +922,3 @@ CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y -# CONFIG_TEXTSEARCH is not set --- linux-2.6.13-rc2/arch/arm/configs/ixdp2400_defconfig 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/configs/ixdp2400_defconfig 2005-07-12 00:26:30.000000000 -0700 @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.12-git6 -# Sat Jun 25 00:58:38 2005 +# Linux kernel version: 2.6.13-rc2 +# Thu Jul 7 16:49:01 2005 # CONFIG_ARM=y CONFIG_MMU=y @@ -138,6 +138,7 @@ CONFIG_PCI_NAMES=y # # CONFIG_SMP is not set # CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set # CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y @@ -346,10 +347,9 @@ CONFIG_PACKET_MMAP=y CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y -CONFIG_IP_FIB_HASH=y -# CONFIG_IP_FIB_TRIE is not set # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y @@ -364,17 +364,8 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_TUNNEL is not set # CONFIG_IP_TCPDIAG is not set # CONFIG_IP_TCPDIAG_IPV6 is not set - -# -# TCP congestion control -# +# CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_BIC=y -CONFIG_TCP_CONG_WESTWOOD=m -CONFIG_TCP_CONG_HTCP=m -# CONFIG_TCP_CONG_HSTCP is not set -# CONFIG_TCP_CONG_HYBLA is not set -# CONFIG_TCP_CONG_VEGAS is not set -# CONFIG_TCP_CONG_SCALABLE is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -932,4 +923,3 @@ CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y -# CONFIG_TEXTSEARCH is not set --- linux-2.6.13-rc2/arch/arm/configs/ixdp2401_defconfig 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/configs/ixdp2401_defconfig 2005-07-12 00:26:30.000000000 -0700 @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.12-git6 -# Sat Jun 25 00:59:35 2005 +# Linux kernel version: 2.6.13-rc2 +# Thu Jul 7 16:49:08 2005 # CONFIG_ARM=y CONFIG_MMU=y @@ -138,6 +138,7 @@ CONFIG_PCI_NAMES=y # # CONFIG_SMP is not set # CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set # CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y @@ -346,10 +347,9 @@ CONFIG_PACKET_MMAP=y CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y -CONFIG_IP_FIB_HASH=y -# CONFIG_IP_FIB_TRIE is not set # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y @@ -364,17 +364,8 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_TUNNEL is not set CONFIG_IP_TCPDIAG=y # CONFIG_IP_TCPDIAG_IPV6 is not set - -# -# TCP congestion control -# +# CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_BIC=y -CONFIG_TCP_CONG_WESTWOOD=m -CONFIG_TCP_CONG_HTCP=m -# CONFIG_TCP_CONG_HSTCP is not set -# CONFIG_TCP_CONG_HYBLA is not set -# CONFIG_TCP_CONG_VEGAS is not set -# CONFIG_TCP_CONG_SCALABLE is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -933,4 +924,3 @@ CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y -# CONFIG_TEXTSEARCH is not set --- linux-2.6.13-rc2/arch/arm/configs/ixdp2800_defconfig 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/configs/ixdp2800_defconfig 2005-07-12 00:26:30.000000000 -0700 @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.12-git6 -# Sat Jun 25 01:00:27 2005 +# Linux kernel version: 2.6.13-rc2 +# Thu Jul 7 16:49:20 2005 # CONFIG_ARM=y CONFIG_MMU=y @@ -138,6 +138,7 @@ CONFIG_PCI_NAMES=y # # CONFIG_SMP is not set # CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set # CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y @@ -346,10 +347,9 @@ CONFIG_PACKET_MMAP=y CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y -CONFIG_IP_FIB_HASH=y -# CONFIG_IP_FIB_TRIE is not set # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y @@ -364,17 +364,8 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_TUNNEL is not set # CONFIG_IP_TCPDIAG is not set # CONFIG_IP_TCPDIAG_IPV6 is not set - -# -# TCP congestion control -# +# CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_BIC=y -CONFIG_TCP_CONG_WESTWOOD=m -CONFIG_TCP_CONG_HTCP=m -# CONFIG_TCP_CONG_HSTCP is not set -# CONFIG_TCP_CONG_HYBLA is not set -# CONFIG_TCP_CONG_VEGAS is not set -# CONFIG_TCP_CONG_SCALABLE is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -932,4 +923,3 @@ CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y -# CONFIG_TEXTSEARCH is not set --- linux-2.6.13-rc2/arch/arm/configs/ixdp2801_defconfig 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/configs/ixdp2801_defconfig 2005-07-12 00:26:30.000000000 -0700 @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.12-git6 -# Sat Jun 25 01:01:18 2005 +# Linux kernel version: 2.6.13-rc2 +# Thu Jul 7 16:49:13 2005 # CONFIG_ARM=y CONFIG_MMU=y @@ -138,6 +138,7 @@ CONFIG_PCI_NAMES=y # # CONFIG_SMP is not set # CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set # CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y @@ -346,10 +347,9 @@ CONFIG_PACKET_MMAP=y CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y -CONFIG_IP_FIB_HASH=y -# CONFIG_IP_FIB_TRIE is not set # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y @@ -364,17 +364,8 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_TUNNEL is not set # CONFIG_IP_TCPDIAG is not set # CONFIG_IP_TCPDIAG_IPV6 is not set - -# -# TCP congestion control -# +# CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_BIC=y -CONFIG_TCP_CONG_WESTWOOD=m -CONFIG_TCP_CONG_HTCP=m -# CONFIG_TCP_CONG_HSTCP is not set -# CONFIG_TCP_CONG_HYBLA is not set -# CONFIG_TCP_CONG_VEGAS is not set -# CONFIG_TCP_CONG_SCALABLE is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -933,4 +924,3 @@ CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y -# CONFIG_TEXTSEARCH is not set --- linux-2.6.13-rc2/arch/arm/configs/omap_h2_1610_defconfig 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/arm/configs/omap_h2_1610_defconfig 2005-07-12 00:26:30.000000000 -0700 @@ -1,14 +1,13 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.12-rc1-bk2 -# Sun Mar 27 17:52:41 2005 +# Linux kernel version: 2.6.13-rc2 +# Fri Jul 8 04:49:34 2005 # CONFIG_ARM=y CONFIG_MMU=y CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_GENERIC_IOMAP=y # # Code maturity level options @@ -17,6 +16,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y CONFIG_BROKEN_ON_SMP=y CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 # # General setup @@ -33,8 +33,9 @@ CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_ALL is not set # CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_EPOLL=y @@ -82,10 +83,28 @@ CONFIG_ARCH_OMAP=y # CONFIG_ARCH_VERSATILE is not set # CONFIG_ARCH_IMX is not set # CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set # # TI OMAP Implementations # +CONFIG_ARCH_OMAP_OTG=y +CONFIG_ARCH_OMAP1=y +# CONFIG_ARCH_OMAP2 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_RESET_CLOCKS is not set +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +# CONFIG_OMAP_MPU_TIMER is not set +CONFIG_OMAP_32K_TIMER=y +CONFIG_OMAP_32K_TIMER_HZ=128 +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set # # OMAP Core Type @@ -93,7 +112,6 @@ CONFIG_ARCH_OMAP=y # CONFIG_ARCH_OMAP730 is not set # CONFIG_ARCH_OMAP1510 is not set CONFIG_ARCH_OMAP16XX=y -CONFIG_ARCH_OMAP_OTG=y # # OMAP Board Type @@ -101,21 +119,14 @@ CONFIG_ARCH_OMAP_OTG=y # CONFIG_MACH_OMAP_INNOVATOR is not set CONFIG_MACH_OMAP_H2=y # CONFIG_MACH_OMAP_H3 is not set -# CONFIG_MACH_OMAP_H4 is not set # CONFIG_MACH_OMAP_OSK is not set # CONFIG_MACH_OMAP_GENERIC is not set # -# OMAP Feature Selections +# OMAP CPU Speed # -CONFIG_OMAP_MUX=y -# CONFIG_OMAP_MUX_DEBUG is not set -CONFIG_OMAP_MUX_WARNINGS=y -CONFIG_OMAP_MPU_TIMER=y -# CONFIG_OMAP_32K_TIMER is not set -CONFIG_OMAP_LL_DEBUG_UART1=y -# CONFIG_OMAP_LL_DEBUG_UART2 is not set -# CONFIG_OMAP_LL_DEBUG_UART3 is not set +# CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER is not set +# CONFIG_OMAP_ARM_216MHZ is not set CONFIG_OMAP_ARM_192MHZ=y # CONFIG_OMAP_ARM_168MHZ is not set # CONFIG_OMAP_ARM_120MHZ is not set @@ -145,6 +156,7 @@ CONFIG_ARM_THUMB=y # # Bus support # +CONFIG_ISA_DMA_API=y # # PCCARD (PCMCIA/CardBus) support @@ -154,7 +166,16 @@ CONFIG_ARM_THUMB=y # # Kernel Features # +# CONFIG_SMP is not set CONFIG_PREEMPT=y +CONFIG_NO_IDLE_HZ=y +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_LEDS is not set CONFIG_ALIGNMENT_TRAP=y @@ -167,6 +188,22 @@ CONFIG_CMDLINE="mem=32M console=ttyS0,11 # CONFIG_XIP_KERNEL is not set # +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set + +# # Floating point emulation # @@ -202,7 +239,6 @@ CONFIG_PM=y CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y # CONFIG_FW_LOADER is not set -CONFIG_DEBUG_DRIVER=y # # Memory Technology Devices (MTD) @@ -292,7 +328,6 @@ CONFIG_MTD_CFI_UTIL=y # # Block devices # -# CONFIG_BLK_DEV_FD is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set @@ -327,6 +362,7 @@ CONFIG_SCSI_PROC_FS=y # CONFIG_CHR_DEV_OSST is not set # CONFIG_BLK_DEV_SR is not set # CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs @@ -356,6 +392,7 @@ CONFIG_SCSI_PROC_FS=y # # Fusion MPT device support # +# CONFIG_FUSION is not set # # IEEE 1394 (FireWire) support @@ -375,12 +412,12 @@ CONFIG_NET=y # CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set -# CONFIG_NETLINK_DEV is not set CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y @@ -395,6 +432,8 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_INET_TUNNEL is not set CONFIG_IP_TCPDIAG=y # CONFIG_IP_TCPDIAG_IPV6 is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_BIC=y # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -442,6 +481,7 @@ CONFIG_NETDEVICES=y CONFIG_NET_ETHERNET=y CONFIG_MII=y CONFIG_SMC91X=y +# CONFIG_DM9000 is not set # # Ethernet (1000 Mbit) @@ -518,7 +558,6 @@ CONFIG_SERIO=y CONFIG_SERIO_SERPORT=y # CONFIG_SERIO_RAW is not set # CONFIG_GAMEPORT is not set -CONFIG_SOUND_GAMEPORT=y # # Character devices @@ -567,13 +606,11 @@ CONFIG_WATCHDOG_NOWAYOUT=y # # Ftape, the floppy tape device driver # -# CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set # # TPM devices # -# CONFIG_TCG_TPM is not set # # I2C support @@ -604,7 +641,9 @@ CONFIG_I2C_CHARDEV=y # CONFIG_SENSORS_ADM1025 is not set # CONFIG_SENSORS_ADM1026 is not set # CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set # CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set # CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_FSCHER is not set # CONFIG_SENSORS_FSCPOS is not set @@ -620,6 +659,7 @@ CONFIG_I2C_CHARDEV=y # CONFIG_SENSORS_LM85 is not set # CONFIG_SENSORS_LM87 is not set # CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set # CONFIG_SENSORS_MAX1619 is not set # CONFIG_SENSORS_PC87360 is not set # CONFIG_SENSORS_SMSC47B397 is not set @@ -627,15 +667,21 @@ CONFIG_I2C_CHARDEV=y # CONFIG_SENSORS_W83781D is not set # CONFIG_SENSORS_W83L785TS is not set # CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set # # Other I2C Chip support # +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set # CONFIG_SENSORS_EEPROM is not set # CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set # CONFIG_SENSORS_PCF8591 is not set # CONFIG_SENSORS_RTC8564 is not set CONFIG_ISP1301_OMAP=y +CONFIG_TPS65010=y +# CONFIG_SENSORS_MAX6875 is not set # CONFIG_I2C_DEBUG_CORE is not set # CONFIG_I2C_DEBUG_ALGO is not set # CONFIG_I2C_DEBUG_BUS is not set @@ -663,8 +709,10 @@ CONFIG_FB=y # CONFIG_FB_CFB_COPYAREA is not set # CONFIG_FB_CFB_IMAGEBLIT is not set # CONFIG_FB_SOFT_CURSOR is not set +# CONFIG_FB_MACMODES is not set CONFIG_FB_MODE_HELPERS=y # CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set # CONFIG_FB_VIRTUAL is not set # @@ -677,11 +725,13 @@ CONFIG_FONTS=y CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y # CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set # CONFIG_FONT_PEARL_8x8 is not set # CONFIG_FONT_ACORN_8x8 is not set # CONFIG_FONT_MINI_4x6 is not set # CONFIG_FONT_SUN8x16 is not set # CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set # # Logo configuration @@ -729,14 +779,14 @@ CONFIG_USB_ARCH_HAS_OHCI=y # CONFIG_USB_GADGET=y # CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_NET2280 is not set # CONFIG_USB_GADGET_PXA2XX is not set # CONFIG_USB_GADGET_GOKU is not set -# CONFIG_USB_GADGET_SA1100 is not set # CONFIG_USB_GADGET_LH7A40X is not set -# CONFIG_USB_GADGET_DUMMY_HCD is not set CONFIG_USB_GADGET_OMAP=y CONFIG_USB_OMAP=y +# CONFIG_USB_GADGET_DUMMY_HCD is not set # CONFIG_USB_GADGET_DUALSPEED is not set # CONFIG_USB_ZERO is not set CONFIG_USB_ETH=y @@ -755,6 +805,7 @@ CONFIG_USB_ETH_RNDIS=y # CONFIG_EXT2_FS=y # CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set # CONFIG_EXT3_FS is not set # CONFIG_JBD is not set # CONFIG_REISERFS_FS is not set @@ -791,7 +842,6 @@ CONFIG_FAT_DEFAULT_CODEPAGE=437 # CONFIG_PROC_FS=y CONFIG_SYSFS=y -# CONFIG_DEVFS_FS is not set # CONFIG_DEVPTS_FS_XATTR is not set # CONFIG_TMPFS is not set # CONFIG_HUGETLB_PAGE is not set @@ -828,12 +878,14 @@ CONFIG_CRAMFS=y # CONFIG_NFS_FS=y CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set CONFIG_NFS_V4=y # CONFIG_NFS_DIRECTIO is not set # CONFIG_NFSD is not set CONFIG_ROOT_NFS=y CONFIG_LOCKD=y CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y CONFIG_SUNRPC=y CONFIG_SUNRPC_GSS=y CONFIG_RPCSEC_GSS_KRB5=y @@ -903,24 +955,11 @@ CONFIG_NLS_DEFAULT="iso8859-1" # Kernel hacking # # CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y -# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_KERNEL is not set CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_SCHEDSTATS is not set -# CONFIG_DEBUG_SLAB is not set -CONFIG_DEBUG_PREEMPT=y -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_KOBJECT is not set CONFIG_DEBUG_BUGVERBOSE=y -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_FS is not set CONFIG_FRAME_POINTER=y -CONFIG_DEBUG_USER=y -# CONFIG_DEBUG_WAITQ is not set -CONFIG_DEBUG_ERRORS=y -CONFIG_DEBUG_LL=y -# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_USER is not set # # Security options --- linux-2.6.13-rc2/arch/arm/Kconfig 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/Kconfig 2005-07-12 00:26:40.000000000 -0700 @@ -223,7 +223,9 @@ source "arch/arm/mach-pxa/Kconfig" source "arch/arm/mach-sa1100/Kconfig" -source "arch/arm/mach-omap/Kconfig" +source "arch/arm/plat-omap/Kconfig" + +source "arch/arm/mach-omap1/Kconfig" source "arch/arm/mach-s3c2410/Kconfig" @@ -514,7 +516,7 @@ config XIP_PHYS_ADDR endmenu -if (ARCH_SA1100 || ARCH_INTEGRATOR) +if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP1) menu "CPU Frequency scaling" @@ -749,6 +751,7 @@ source "drivers/misc/Kconfig" source "drivers/media/Kconfig" source "drivers/video/Kconfig" +source "drivers/char/speakup/Kconfig" source "sound/Kconfig" --- linux-2.6.13-rc2/arch/arm/kernel/armksyms.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/kernel/armksyms.c 2005-07-12 00:26:30.000000000 -0700 @@ -41,7 +41,10 @@ extern void fp_enter(void); * This has a special calling convention; it doesn't * modify any of the usual registers, except for LR. */ +#define EXPORT_CRC_ALIAS(sym) __CRC_SYMBOL(sym, "") + #define EXPORT_SYMBOL_ALIAS(sym,orig) \ + EXPORT_CRC_ALIAS(sym) \ const struct kernel_symbol __ksymtab_##sym \ __attribute__((section("__ksymtab"))) = \ { (unsigned long)&orig, #sym }; --- linux-2.6.13-rc2/arch/arm/kernel/bios32.c 2005-03-01 23:38:07.000000000 -0800 +++ devel/arch/arm/kernel/bios32.c 2005-07-12 00:27:14.000000000 -0700 @@ -447,9 +447,26 @@ pcibios_resource_to_bus(struct pci_dev * region->end = res->end - offset; } +void __devinit +pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + struct pci_sys_data *root = dev->sysdata; + unsigned long offset = 0; + + if (res->flags & IORESOURCE_IO) + offset = root->io_offset; + if (res->flags & IORESOURCE_MEM) + offset = root->mem_offset; + + res->start = region->start + offset; + res->end = region->end + offset; +} + #ifdef CONFIG_HOTPLUG EXPORT_SYMBOL(pcibios_fixup_bus); EXPORT_SYMBOL(pcibios_resource_to_bus); +EXPORT_SYMBOL(pcibios_bus_to_resource); #endif /* --- linux-2.6.13-rc2/arch/arm/kernel/smp.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/kernel/smp.c 2005-07-12 00:26:40.000000000 -0700 @@ -36,7 +36,7 @@ * The present bitmask indicates that the CPU is physically present. * The online bitmask indicates that the CPU is up and running. */ -cpumask_t cpu_present_mask; +cpumask_t cpu_possible_map; cpumask_t cpu_online_map; /* @@ -235,7 +235,8 @@ void __init smp_prepare_boot_cpu(void) { unsigned int cpu = smp_processor_id(); - cpu_set(cpu, cpu_present_mask); + cpu_set(cpu, cpu_possible_map); + cpu_set(cpu, cpu_present_map); cpu_set(cpu, cpu_online_map); } @@ -355,7 +356,7 @@ void show_ipi_list(struct seq_file *p) seq_puts(p, "IPI:"); - for_each_online_cpu(cpu) + for_each_present_cpu(cpu) seq_printf(p, " %10lu", per_cpu(ipi_data, cpu).ipi_count); seq_putc(p, '\n'); --- linux-2.6.13-rc2/arch/arm/mach-integrator/platsmp.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mach-integrator/platsmp.c 2005-07-12 00:26:40.000000000 -0700 @@ -174,11 +174,13 @@ void __init smp_prepare_cpus(unsigned in max_cpus = ncores; /* - * Initialise the present mask - this tells us which CPUs should - * be present. + * Initialise the possible/present maps. + * cpu_possible_map describes the set of CPUs which may be present + * cpu_present_map describes the set of CPUs populated */ for (i = 0; i < max_cpus; i++) { - cpu_set(i, cpu_present_mask); + cpu_set(i, cpu_possible_map); + cpu_set(i, cpu_present_map); } /* --- linux-2.6.13-rc2/arch/arm/mach-ixp2000/core.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mach-ixp2000/core.c 2005-07-12 00:26:30.000000000 -0700 @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -125,19 +125,6 @@ static struct map_desc ixp2000_io_desc[] } }; -static struct uart_port ixp2000_serial_port = { - .membase = (char *)(IXP2000_UART_VIRT_BASE + 3), - .mapbase = IXP2000_UART_PHYS_BASE + 3, - .irq = IRQ_IXP2000_UART, - .flags = UPF_SKIP_TEST, - .iotype = UPIO_MEM, - .regshift = 2, - .uartclk = 50000000, - .line = 0, - .type = PORT_XSCALE, - .fifosize = 16 -}; - void __init ixp2000_map_io(void) { extern unsigned int processor_id; @@ -157,12 +144,50 @@ void __init ixp2000_map_io(void) } iotable_init(ixp2000_io_desc, ARRAY_SIZE(ixp2000_io_desc)); - early_serial_setup(&ixp2000_serial_port); /* Set slowport to 8-bit mode. */ ixp2000_reg_write(IXP2000_SLOWPORT_FRM, 1); } + +/************************************************************************* + * Serial port support for IXP2000 + *************************************************************************/ +static struct plat_serial8250_port ixp2000_serial_port[] = { + { + .mapbase = IXP2000_UART_PHYS_BASE, + .membase = (char *)(IXP2000_UART_VIRT_BASE + 3), + .irq = IRQ_IXP2000_UART, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = 50000000, + }, + { }, +}; + +static struct resource ixp2000_uart_resource = { + .start = IXP2000_UART_PHYS_BASE, + .end = IXP2000_UART_PHYS_BASE + 0xffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device ixp2000_serial_device = { + .name = "serial8250", + .id = 0, + .dev = { + .platform_data = ixp2000_serial_port, + }, + .num_resources = 1, + .resource = &ixp2000_uart_resource, +}; + +void __init ixp2000_uart_init(void) +{ + platform_device_register(&ixp2000_serial_device); +} + + /************************************************************************* * Timer-tick functions for IXP2000 *************************************************************************/ --- linux-2.6.13-rc2/arch/arm/mach-ixp2000/enp2611.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mach-ixp2000/enp2611.c 2005-07-12 00:26:30.000000000 -0700 @@ -219,6 +219,7 @@ static struct platform_device *enp2611_d static void __init enp2611_init_machine(void) { platform_add_devices(enp2611_devices, ARRAY_SIZE(enp2611_devices)); + ixp2000_uart_init(); } --- linux-2.6.13-rc2/arch/arm/mach-ixp2000/ixdp2x00.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mach-ixp2000/ixdp2x00.c 2005-07-12 00:26:30.000000000 -0700 @@ -303,5 +303,6 @@ void __init ixdp2x00_init_machine(void) gpio_line_config(IXDP2X00_GPIO_I2C_ENABLE, GPIO_OUT); platform_add_devices(ixdp2x00_devices, ARRAY_SIZE(ixdp2x00_devices)); + ixp2000_uart_init(); } --- linux-2.6.13-rc2/arch/arm/mach-ixp2000/ixdp2x01.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mach-ixp2000/ixdp2x01.c 2005-07-12 00:26:30.000000000 -0700 @@ -370,6 +370,7 @@ static void __init ixdp2x01_init_machine ((*IXDP2X01_CPLD_FLASH_REG & IXDP2X01_CPLD_FLASH_BANK_MASK) + 1); platform_add_devices(ixdp2x01_devices, ARRAY_SIZE(ixdp2x01_devices)); + ixp2000_uart_init(); } --- linux-2.6.13-rc2/arch/arm/mach-ixp4xx/common-pci.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/arm/mach-ixp4xx/common-pci.c 2005-07-12 00:26:30.000000000 -0700 @@ -453,8 +453,8 @@ int ixp4xx_setup(int nr, struct pci_sys_ local_write_config(PCI_COMMAND, 2, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); res[0].name = "PCI I/O Space"; - res[0].start = 0x00001000; - res[0].end = 0xffff0000; + res[0].start = 0x00000000; + res[0].end = 0x0000ffff; res[0].flags = IORESOURCE_IO; res[1].name = "PCI Memory Space"; --- linux-2.6.13-rc2/arch/arm/mach-ixp4xx/coyote-setup.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mach-ixp4xx/coyote-setup.c 2005-07-12 00:26:30.000000000 -0700 @@ -56,21 +56,24 @@ static struct resource coyote_uart_resou .flags = IORESOURCE_MEM, }; -static struct plat_serial8250_port coyote_uart_data = { - .mapbase = IXP4XX_UART2_BASE_PHYS, - .membase = (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET, - .irq = IRQ_IXP4XX_UART2, - .flags = UPF_BOOT_AUTOCONF, - .iotype = UPIO_MEM, - .regshift = 2, - .uartclk = IXP4XX_UART_XTAL, +static struct plat_serial8250_port coyote_uart_data[] = { + { + .mapbase = IXP4XX_UART2_BASE_PHYS, + .membase = (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET, + .irq = IRQ_IXP4XX_UART2, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = IXP4XX_UART_XTAL, + }, + { }, }; static struct platform_device coyote_uart = { .name = "serial8250", .id = 0, .dev = { - .platform_data = &coyote_uart_data, + .platform_data = coyote_uart_data, }, .num_resources = 1, .resource = &coyote_uart_resource, @@ -87,10 +90,10 @@ static void __init coyote_init(void) *IXP4XX_EXP_CS1 = *IXP4XX_EXP_CS0; if (machine_is_ixdpg425()) { - coyote_uart_data.membase = + coyote_uart_data[0].membase = (char*)(IXP4XX_UART1_BASE_VIRT + REG_OFFSET); - coyote_uart_data.mapbase = IXP4XX_UART1_BASE_PHYS; - coyote_uart_data.irq = IRQ_IXP4XX_UART1; + coyote_uart_data[0].mapbase = IXP4XX_UART1_BASE_PHYS; + coyote_uart_data[0].irq = IRQ_IXP4XX_UART1; } --- linux-2.6.13-rc2/arch/arm/mach-ixp4xx/ixdp425-setup.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mach-ixp4xx/ixdp425-setup.c 2005-07-12 00:26:30.000000000 -0700 @@ -95,7 +95,8 @@ static struct plat_serial8250_port ixdp4 .iotype = UPIO_MEM, .regshift = 2, .uartclk = IXP4XX_UART_XTAL, - } + }, + { }, }; static struct platform_device ixdp425_uart = { --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/board-generic.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,99 @@ +/* + * linux/arch/arm/mach-omap1/board-generic.c + * + * Modified from board-innovator1510.c + * + * Code for generic OMAP board. Should work on many OMAP systems where + * the device drivers take care of all the necessary hardware initialization. + * Do not put any board specific code to this file; create a new machine + * type if you need custom low-level initializations. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static int __initdata generic_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; + +static void __init omap_generic_init_irq(void) +{ + omap_init_irq(); +} + +/* assume no Mini-AB port */ + +#ifdef CONFIG_ARCH_OMAP1510 +static struct omap_usb_config generic1510_usb_config __initdata = { + .register_host = 1, + .register_dev = 1, + .hmc_mode = 16, + .pins[0] = 3, +}; +#endif + +#if defined(CONFIG_ARCH_OMAP16XX) +static struct omap_usb_config generic1610_usb_config __initdata = { + .register_host = 1, + .register_dev = 1, + .hmc_mode = 16, + .pins[0] = 6, +}; +#endif + +static struct omap_board_config_kernel generic_config[] = { + { OMAP_TAG_USB, NULL }, +}; + +static void __init omap_generic_init(void) +{ + /* + * Make sure the serial ports are muxed on at this point. + * You have to mux them off in device drivers later on + * if not needed. + */ +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + generic_config[0].data = &generic1510_usb_config; + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (!cpu_is_omap1510()) { + generic_config[0].data = &generic1610_usb_config; + } +#endif + omap_board_config = generic_config; + omap_board_config_size = ARRAY_SIZE(generic_config); + omap_serial_init(generic_serial_ports); +} + +static void __init omap_generic_map_io(void) +{ + omap_map_common_io() +} + +MACHINE_START(OMAP_GENERIC, "Generic OMAP1510/1610/1710") + /* Maintainer: Tony Lindgren */ + .phys_ram = 0x10000000, + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = omap_generic_map_io, + .init_irq = omap_generic_init_irq, + .init_machine = omap_generic_init, + .timer = &omap_timer, +MACHINE_END --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/board-h2.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,188 @@ +/* + * linux/arch/arm/mach-omap1/board-h2.c + * + * Board specific inits for OMAP-1610 H2 + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: Greg Lonnon + * + * Copyright (C) 2002 MontaVista Software, Inc. + * + * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 + * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen + * + * H2 specific changes and cleanup + * Copyright (C) 2004 Nokia Corporation by Imre Deak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern int omap_gpio_init(void); + +static int __initdata h2_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; + +static struct mtd_partition h2_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, + /* file system */ + { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + } +}; + +static struct flash_platform_data h2_flash_data = { + .map_name = "cfi_probe", + .width = 2, + .parts = h2_partitions, + .nr_parts = ARRAY_SIZE(h2_partitions), +}; + +static struct resource h2_flash_resource = { + .start = OMAP_CS2B_PHYS, + .end = OMAP_CS2B_PHYS + OMAP_CS2B_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device h2_flash_device = { + .name = "omapflash", + .id = 0, + .dev = { + .platform_data = &h2_flash_data, + }, + .num_resources = 1, + .resource = &h2_flash_resource, +}; + +static struct resource h2_smc91x_resources[] = { + [0] = { + .start = OMAP1610_ETHR_START, /* Physical */ + .end = OMAP1610_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP_GPIO_IRQ(0), + .end = OMAP_GPIO_IRQ(0), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device h2_smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(h2_smc91x_resources), + .resource = h2_smc91x_resources, +}; + +static struct platform_device *h2_devices[] __initdata = { + &h2_flash_device, + &h2_smc91x_device, +}; + +static void __init h2_init_smc91x(void) +{ + if ((omap_request_gpio(0)) < 0) { + printk("Error requesting gpio 0 for smc91x irq\n"); + return; + } + omap_set_gpio_edge_ctrl(0, OMAP_GPIO_FALLING_EDGE); +} + +void h2_init_irq(void) +{ + omap_init_irq(); + omap_gpio_init(); + h2_init_smc91x(); +} + +static struct omap_usb_config h2_usb_config __initdata = { + /* usb1 has a Mini-AB port and external isp1301 transceiver */ + .otg = 2, + +#ifdef CONFIG_USB_GADGET_OMAP + .hmc_mode = 19, // 0:host(off) 1:dev|otg 2:disabled + // .hmc_mode = 21, // 0:host(off) 1:dev(loopback) 2:host(loopback) +#elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + /* needs OTG cable, or NONSTANDARD (B-to-MiniB) */ + .hmc_mode = 20, // 1:dev|otg(off) 1:host 2:disabled +#endif + + .pins[1] = 3, +}; + +static struct omap_mmc_config h2_mmc_config __initdata = { + .mmc_blocks = 1, + .mmc1_power_pin = -1, /* tps65010 gpio3 */ + .mmc1_switch_pin = OMAP_MPUIO(1), +}; + +static struct omap_board_config_kernel h2_config[] = { + { OMAP_TAG_USB, &h2_usb_config }, + { OMAP_TAG_MMC, &h2_mmc_config }, +}; + +static void __init h2_init(void) +{ + platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices)); + omap_board_config = h2_config; + omap_board_config_size = ARRAY_SIZE(h2_config); +} + +static void __init h2_map_io(void) +{ + omap_map_common_io(); + omap_serial_init(h2_serial_ports); +} + +MACHINE_START(OMAP_H2, "TI-H2") + /* Maintainer: Imre Deak */ + .phys_ram = 0x10000000, + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = h2_map_io, + .init_irq = h2_init_irq, + .init_machine = h2_init, + .timer = &omap_timer, +MACHINE_END --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/board-h3.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,206 @@ +/* + * linux/arch/arm/mach-omap1/board-h3.c + * + * This file contains OMAP1710 H3 specific code. + * + * Copyright (C) 2004 Texas Instruments, Inc. + * Copyright (C) 2002 MontaVista Software, Inc. + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon (glonnon@ridgerun.com) or info@ridgerun.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +extern int omap_gpio_init(void); + +static int __initdata h3_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; + +static struct mtd_partition h3_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, + /* file system */ + { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + } +}; + +static struct flash_platform_data h3_flash_data = { + .map_name = "cfi_probe", + .width = 2, + .parts = h3_partitions, + .nr_parts = ARRAY_SIZE(h3_partitions), +}; + +static struct resource h3_flash_resource = { + .start = OMAP_CS2B_PHYS, + .end = OMAP_CS2B_PHYS + OMAP_CS2B_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device flash_device = { + .name = "omapflash", + .id = 0, + .dev = { + .platform_data = &h3_flash_data, + }, + .num_resources = 1, + .resource = &h3_flash_resource, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .start = OMAP1710_ETHR_START, /* Physical */ + .end = OMAP1710_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP_GPIO_IRQ(40), + .end = OMAP_GPIO_IRQ(40), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +#define GPTIMER_BASE 0xFFFB1400 +#define GPTIMER_REGS(x) (0xFFFB1400 + (x * 0x800)) +#define GPTIMER_REGS_SIZE 0x46 + +static struct resource intlat_resources[] = { + [0] = { + .start = GPTIMER_REGS(0), /* Physical */ + .end = GPTIMER_REGS(0) + GPTIMER_REGS_SIZE, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = INT_1610_GPTIMER1, + .end = INT_1610_GPTIMER1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device intlat_device = { + .name = "omap_intlat", + .id = 0, + .num_resources = ARRAY_SIZE(intlat_resources), + .resource = intlat_resources, +}; + +static struct platform_device *devices[] __initdata = { + &flash_device, + &smc91x_device, + &intlat_device, +}; + +static struct omap_usb_config h3_usb_config __initdata = { + /* usb1 has a Mini-AB port and external isp1301 transceiver */ + .otg = 2, + +#ifdef CONFIG_USB_GADGET_OMAP + .hmc_mode = 19, /* 0:host(off) 1:dev|otg 2:disabled */ +#elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ + .hmc_mode = 20, /* 1:dev|otg(off) 1:host 2:disabled */ +#endif + + .pins[1] = 3, +}; + +static struct omap_board_config_kernel h3_config[] = { + { OMAP_TAG_USB, &h3_usb_config }, +}; + +static void __init h3_init(void) +{ + (void) platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +static void __init h3_init_smc91x(void) +{ + omap_cfg_reg(W15_1710_GPIO40); + if (omap_request_gpio(40) < 0) { + printk("Error requesting gpio 40 for smc91x irq\n"); + return; + } + omap_set_gpio_edge_ctrl(40, OMAP_GPIO_FALLING_EDGE); +} + +void h3_init_irq(void) +{ + omap_init_irq(); + omap_gpio_init(); + h3_init_smc91x(); +} + +static void __init h3_map_io(void) +{ + omap_map_common_io(); + omap_serial_init(h3_serial_ports); +} + +MACHINE_START(OMAP_H3, "TI OMAP1710 H3 board") + /* Maintainer: Texas Instruments, Inc. */ + .phys_ram = 0x10000000, + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = h3_map_io, + .init_irq = h3_init_irq, + .init_machine = h3_init, + .timer = &omap_timer, +MACHINE_END --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/board-innovator.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,281 @@ +/* + * linux/arch/arm/mach-omap1/board-innovator.c + * + * Board specific inits for OMAP-1510 and OMAP-1610 Innovator + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: Greg Lonnon + * + * Copyright (C) 2002 MontaVista Software, Inc. + * + * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 + * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static int __initdata innovator_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; + +static struct mtd_partition innovator_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, + /* rest of flash1 is a file system */ + { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = SZ_16M - SZ_2M - 2 * SZ_128K, + .mask_flags = 0 + }, + /* file system */ + { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + } +}; + +static struct flash_platform_data innovator_flash_data = { + .map_name = "cfi_probe", + .width = 2, + .parts = innovator_partitions, + .nr_parts = ARRAY_SIZE(innovator_partitions), +}; + +static struct resource innovator_flash_resource = { + .start = OMAP_CS0_PHYS, + .end = OMAP_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device innovator_flash_device = { + .name = "omapflash", + .id = 0, + .dev = { + .platform_data = &innovator_flash_data, + }, + .num_resources = 1, + .resource = &innovator_flash_resource, +}; + +#ifdef CONFIG_ARCH_OMAP1510 + +/* Only FPGA needs to be mapped here. All others are done with ioremap */ +static struct map_desc innovator1510_io_desc[] __initdata = { +{ OMAP1510_FPGA_BASE, OMAP1510_FPGA_START, OMAP1510_FPGA_SIZE, + MT_DEVICE }, +}; + +static struct resource innovator1510_smc91x_resources[] = { + [0] = { + .start = OMAP1510_FPGA_ETHR_START, /* Physical */ + .end = OMAP1510_FPGA_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP1510_INT_ETHER, + .end = OMAP1510_INT_ETHER, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device innovator1510_smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(innovator1510_smc91x_resources), + .resource = innovator1510_smc91x_resources, +}; + +static struct platform_device *innovator1510_devices[] __initdata = { + &innovator_flash_device, + &innovator1510_smc91x_device, +}; + +#endif /* CONFIG_ARCH_OMAP1510 */ + +#ifdef CONFIG_ARCH_OMAP16XX + +static struct resource innovator1610_smc91x_resources[] = { + [0] = { + .start = INNOVATOR1610_ETHR_START, /* Physical */ + .end = INNOVATOR1610_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP_GPIO_IRQ(0), + .end = OMAP_GPIO_IRQ(0), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device innovator1610_smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(innovator1610_smc91x_resources), + .resource = innovator1610_smc91x_resources, +}; + +static struct platform_device *innovator1610_devices[] __initdata = { + &innovator_flash_device, + &innovator1610_smc91x_device, +}; + +#endif /* CONFIG_ARCH_OMAP16XX */ + +static void __init innovator_init_smc91x(void) +{ + if (cpu_is_omap1510()) { + fpga_write(fpga_read(OMAP1510_FPGA_RST) & ~1, + OMAP1510_FPGA_RST); + udelay(750); + } else { + if ((omap_request_gpio(0)) < 0) { + printk("Error requesting gpio 0 for smc91x irq\n"); + return; + } + omap_set_gpio_edge_ctrl(0, OMAP_GPIO_RISING_EDGE); + } +} + +void innovator_init_irq(void) +{ + omap_init_irq(); + omap_gpio_init(); +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + omap1510_fpga_init_irq(); + } +#endif + innovator_init_smc91x(); +} + +#ifdef CONFIG_ARCH_OMAP1510 +static struct omap_usb_config innovator1510_usb_config __initdata = { + /* for bundled non-standard host and peripheral cables */ + .hmc_mode = 4, + + .register_host = 1, + .pins[1] = 6, + .pins[2] = 6, /* Conflicts with UART2 */ + + .register_dev = 1, + .pins[0] = 2, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP16XX +static struct omap_usb_config h2_usb_config __initdata = { + /* usb1 has a Mini-AB port and external isp1301 transceiver */ + .otg = 2, + +#ifdef CONFIG_USB_GADGET_OMAP + .hmc_mode = 19, // 0:host(off) 1:dev|otg 2:disabled + // .hmc_mode = 21, // 0:host(off) 1:dev(loopback) 2:host(loopback) +#elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ + .hmc_mode = 20, // 1:dev|otg(off) 1:host 2:disabled +#endif + + .pins[1] = 3, +}; +#endif + +static struct omap_board_config_kernel innovator_config[] = { + { OMAP_TAG_USB, NULL }, +}; + +static void __init innovator_init(void) +{ +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + platform_add_devices(innovator1510_devices, ARRAY_SIZE(innovator1510_devices)); + } +#endif +#ifdef CONFIG_ARCH_OMAP16XX + if (!cpu_is_omap1510()) { + platform_add_devices(innovator1610_devices, ARRAY_SIZE(innovator1610_devices)); + } +#endif + +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) + innovator_config[0].data = &innovator1510_usb_config; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + if (cpu_is_omap1610()) + innovator_config[0].data = &h2_usb_config; +#endif + omap_board_config = innovator_config; + omap_board_config_size = ARRAY_SIZE(innovator_config); +} + +static void __init innovator_map_io(void) +{ + omap_map_common_io(); + +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + iotable_init(innovator1510_io_desc, ARRAY_SIZE(innovator1510_io_desc)); + udelay(10); /* Delay needed for FPGA */ + + /* Dump the Innovator FPGA rev early - useful info for support. */ + printk("Innovator FPGA Rev %d.%d Board Rev %d\n", + fpga_read(OMAP1510_FPGA_REV_HIGH), + fpga_read(OMAP1510_FPGA_REV_LOW), + fpga_read(OMAP1510_FPGA_BOARD_REV)); + } +#endif + omap_serial_init(innovator_serial_ports); +} + +MACHINE_START(OMAP_INNOVATOR, "TI-Innovator") + /* Maintainer: MontaVista Software, Inc. */ + .phys_ram = 0x10000000, + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = innovator_map_io, + .init_irq = innovator_init_irq, + .init_machine = innovator_init, + .timer = &omap_timer, +MACHINE_END --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/board-netstar.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,152 @@ +/* + * Modified from board-generic.c + * + * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl + * + * Code for Netstar OMAP board. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +extern void __init omap_init_time(void); +extern int omap_gpio_init(void); + +static struct resource netstar_smc91x_resources[] = { + [0] = { + .start = OMAP_CS1_PHYS + 0x300, + .end = OMAP_CS1_PHYS + 0x300 + 16, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP_GPIO_IRQ(8), + .end = OMAP_GPIO_IRQ(8), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device netstar_smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(netstar_smc91x_resources), + .resource = netstar_smc91x_resources, +}; + +static struct platform_device *netstar_devices[] __initdata = { + &netstar_smc91x_device, +}; + +static void __init netstar_init_irq(void) +{ + omap_init_irq(); + omap_gpio_init(); +} + +static void __init netstar_init(void) +{ + /* green LED */ + omap_request_gpio(4); + omap_set_gpio_direction(4, 0); + /* smc91x reset */ + omap_request_gpio(7); + omap_set_gpio_direction(7, 0); + omap_set_gpio_dataout(7, 1); + udelay(2); /* wait at least 100ns */ + omap_set_gpio_dataout(7, 0); + mdelay(50); /* 50ms until PHY ready */ + /* smc91x interrupt pin */ + omap_request_gpio(8); + omap_set_gpio_edge_ctrl(8, OMAP_GPIO_RISING_EDGE); + + omap_request_gpio(12); + omap_request_gpio(13); + omap_request_gpio(14); + omap_request_gpio(15); + omap_set_gpio_edge_ctrl(12, OMAP_GPIO_FALLING_EDGE); + omap_set_gpio_edge_ctrl(13, OMAP_GPIO_FALLING_EDGE); + omap_set_gpio_edge_ctrl(14, OMAP_GPIO_FALLING_EDGE); + omap_set_gpio_edge_ctrl(15, OMAP_GPIO_FALLING_EDGE); + + platform_add_devices(netstar_devices, ARRAY_SIZE(netstar_devices)); + + /* Switch on green LED */ + omap_set_gpio_dataout(4, 0); + /* Switch off red LED */ + omap_writeb(0x00, OMAP_LPG1_PMR); /* Disable clock */ + omap_writeb(0x80, OMAP_LPG1_LCR); +} + +static int __initdata omap_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; + +static void __init netstar_map_io(void) +{ + omap_map_common_io(); + omap_serial_init(omap_serial_ports); +} + +#define MACHINE_PANICED 1 +#define MACHINE_REBOOTING 2 +#define MACHINE_REBOOT 4 +static unsigned long machine_state; + +static int panic_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + if (test_and_set_bit(MACHINE_PANICED, &machine_state)) + return NOTIFY_DONE; + + /* Switch off green LED */ + omap_set_gpio_dataout(4, 1); + /* Flash red LED */ + omap_writeb(0x78, OMAP_LPG1_LCR); + omap_writeb(0x01, OMAP_LPG1_PMR); /* Enable clock */ + + return NOTIFY_DONE; +} + +static struct notifier_block panic_block = { + .notifier_call = panic_event, +}; + +static int __init netstar_late_init(void) +{ + /* TODO: Setup front panel switch here */ + + /* Setup panic notifier */ + notifier_chain_register(&panic_notifier_list, &panic_block); + + return 0; +} + +postcore_initcall(netstar_late_init); + +MACHINE_START(NETSTAR, "NetStar OMAP5910") + /* Maintainer: Ladislav Michl */ + .phys_ram = 0x10000000, + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = netstar_map_io, + .init_irq = netstar_init_irq, + .init_machine = netstar_init, + .timer = &omap_timer, +MACHINE_END --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/board-osk.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,170 @@ +/* + * linux/arch/arm/mach-omap1/board-osk.c + * + * Board specific init for OMAP5912 OSK + * + * Written by Dirk Behme + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static struct map_desc osk5912_io_desc[] __initdata = { +{ OMAP_OSK_NOR_FLASH_BASE, OMAP_OSK_NOR_FLASH_START, OMAP_OSK_NOR_FLASH_SIZE, + MT_DEVICE }, +}; + +static int __initdata osk_serial_ports[OMAP_MAX_NR_PORTS] = {1, 0, 0}; + +static struct resource osk5912_smc91x_resources[] = { + [0] = { + .start = OMAP_OSK_ETHR_START, /* Physical */ + .end = OMAP_OSK_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP_GPIO_IRQ(0), + .end = OMAP_GPIO_IRQ(0), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device osk5912_smc91x_device = { + .name = "smc91x", + .id = -1, + .num_resources = ARRAY_SIZE(osk5912_smc91x_resources), + .resource = osk5912_smc91x_resources, +}; + +static struct resource osk5912_cf_resources[] = { + [0] = { + .start = OMAP_GPIO_IRQ(62), + .end = OMAP_GPIO_IRQ(62), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device osk5912_cf_device = { + .name = "omap_cf", + .id = -1, + .dev = { + .platform_data = (void *) 2 /* CS2 */, + }, + .num_resources = ARRAY_SIZE(osk5912_cf_resources), + .resource = osk5912_cf_resources, +}; + +static struct platform_device *osk5912_devices[] __initdata = { + &osk5912_smc91x_device, + &osk5912_cf_device, +}; + +static void __init osk_init_smc91x(void) +{ + if ((omap_request_gpio(0)) < 0) { + printk("Error requesting gpio 0 for smc91x irq\n"); + return; + } + omap_set_gpio_edge_ctrl(0, OMAP_GPIO_RISING_EDGE); + + /* Check EMIFS wait states to fix errors with SMC_GET_PKT_HDR */ + EMIFS_CCS(1) |= 0x2; +} + +static void __init osk_init_cf(void) +{ + omap_cfg_reg(M7_1610_GPIO62); + if ((omap_request_gpio(62)) < 0) { + printk("Error requesting gpio 62 for CF irq\n"); + return; + } + /* it's really active-low */ + omap_set_gpio_edge_ctrl(62, OMAP_GPIO_FALLING_EDGE); +} + +void osk_init_irq(void) +{ + omap_init_irq(); + omap_gpio_init(); + osk_init_smc91x(); + osk_init_cf(); +} + +static struct omap_usb_config osk_usb_config __initdata = { + /* has usb host connector (A) ... for development it can also + * be used, with a NONSTANDARD gender-bending cable/dongle, as + * a peripheral. + */ +#ifdef CONFIG_USB_GADGET_OMAP + .register_dev = 1, + .hmc_mode = 0, +#else + .register_host = 1, + .hmc_mode = 16, + .rwc = 1, +#endif + .pins[0] = 2, +}; + +static struct omap_board_config_kernel osk_config[] = { + { OMAP_TAG_USB, &osk_usb_config }, +}; + +static void __init osk_init(void) +{ + platform_add_devices(osk5912_devices, ARRAY_SIZE(osk5912_devices)); + omap_board_config = osk_config; + omap_board_config_size = ARRAY_SIZE(osk_config); + USB_TRANSCEIVER_CTRL_REG |= (3 << 1); +} + +static void __init osk_map_io(void) +{ + omap_map_common_io(); + iotable_init(osk5912_io_desc, ARRAY_SIZE(osk5912_io_desc)); + omap_serial_init(osk_serial_ports); +} + +MACHINE_START(OMAP_OSK, "TI-OSK") + /* Maintainer: Dirk Behme */ + .phys_ram = 0x10000000, + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = osk_map_io, + .init_irq = osk_init_irq, + .init_machine = osk_init, + .timer = &omap_timer, +MACHINE_END --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/board-perseus2.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,190 @@ +/* + * linux/arch/arm/mach-omap1/board-perseus2.c + * + * Modified from board-generic.c + * + * Original OMAP730 support by Jean Pihet + * Updated for 2.6 by Kevin Hilman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static struct resource smc91x_resources[] = { + [0] = { + .start = H2P2_DBG_FPGA_ETHR_START, /* Physical */ + .end = H2P2_DBG_FPGA_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = INT_730_MPU_EXT_NIRQ, + .end = 0, + .flags = IORESOURCE_IRQ, + }, +}; + +static int __initdata p2_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 0}; + +static struct mtd_partition p2_partitions[] = { + /* bootloader (U-Boot, etc) in first sector */ + { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader params in the next sector */ + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + .mask_flags = 0, + }, + /* kernel */ + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + .mask_flags = 0 + }, + /* rest of flash is a file system */ + { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0 + }, +}; + +static struct flash_platform_data p2_flash_data = { + .map_name = "cfi_probe", + .width = 2, + .parts = p2_partitions, + .nr_parts = ARRAY_SIZE(p2_partitions), +}; + +static struct resource p2_flash_resource = { + .start = OMAP_FLASH_0_START, + .end = OMAP_FLASH_0_START + OMAP_FLASH_0_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device p2_flash_device = { + .name = "omapflash", + .id = 0, + .dev = { + .platform_data = &p2_flash_data, + }, + .num_resources = 1, + .resource = &p2_flash_resource, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct platform_device *devices[] __initdata = { + &p2_flash_device, + &smc91x_device, +}; + +static void __init omap_perseus2_init(void) +{ + (void) platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +static void __init perseus2_init_smc91x(void) +{ + fpga_write(1, H2P2_DBG_FPGA_LAN_RESET); + mdelay(50); + fpga_write(fpga_read(H2P2_DBG_FPGA_LAN_RESET) & ~1, + H2P2_DBG_FPGA_LAN_RESET); + mdelay(50); +} + +void omap_perseus2_init_irq(void) +{ + omap_init_irq(); + omap_gpio_init(); + perseus2_init_smc91x(); +} + +/* Only FPGA needs to be mapped here. All others are done with ioremap */ +static struct map_desc omap_perseus2_io_desc[] __initdata = { + {H2P2_DBG_FPGA_BASE, H2P2_DBG_FPGA_START, H2P2_DBG_FPGA_SIZE, + MT_DEVICE}, +}; + +static void __init omap_perseus2_map_io(void) +{ + omap_map_common_io(); + iotable_init(omap_perseus2_io_desc, + ARRAY_SIZE(omap_perseus2_io_desc)); + + /* Early, board-dependent init */ + + /* + * Hold GSM Reset until needed + */ + omap_writew(omap_readw(OMAP730_DSP_M_CTL) & ~1, OMAP730_DSP_M_CTL); + + /* + * UARTs -> done automagically by 8250 driver + */ + + /* + * CSx timings, GPIO Mux ... setup + */ + + /* Flash: CS0 timings setup */ + omap_writel(0x0000fff3, OMAP730_FLASH_CFG_0); + omap_writel(0x00000088, OMAP730_FLASH_ACFG_0); + + /* + * Ethernet support trough the debug board + * CS1 timings setup + */ + omap_writel(0x0000fff3, OMAP730_FLASH_CFG_1); + omap_writel(0x00000000, OMAP730_FLASH_ACFG_1); + + /* + * Configure MPU_EXT_NIRQ IO in IO_CONF9 register, + * It is used as the Ethernet controller interrupt + */ + omap_writel(omap_readl(OMAP730_IO_CONF_9) & 0x1FFFFFFF, OMAP730_IO_CONF_9); + omap_serial_init(p2_serial_ports); +} + +MACHINE_START(OMAP_PERSEUS2, "OMAP730 Perseus2") + /* Maintainer: Kevin Hilman */ + .phys_ram = 0x10000000, + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = omap_perseus2_map_io, + .init_irq = omap_perseus2_init_irq, + .init_machine = omap_perseus2_init, + .timer = &omap_timer, +MACHINE_END --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/board-voiceblue.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,257 @@ +/* + * linux/arch/arm/mach-omap1/board-voiceblue.c + * + * Modified from board-generic.c + * + * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl + * + * Code for OMAP5910 based VoiceBlue board (VoIP to GSM gateway). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern void omap_init_time(void); +extern int omap_gpio_init(void); + +static struct plat_serial8250_port voiceblue_ports[] = { + { + .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x40000), + .irq = OMAP_GPIO_IRQ(12), + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + .iotype = UPIO_MEM, + .regshift = 1, + .uartclk = 3686400, + }, + { + .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x50000), + .irq = OMAP_GPIO_IRQ(13), + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + .iotype = UPIO_MEM, + .regshift = 1, + .uartclk = 3686400, + }, + { + .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x60000), + .irq = OMAP_GPIO_IRQ(14), + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + .iotype = UPIO_MEM, + .regshift = 1, + .uartclk = 3686400, + }, + { + .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x70000), + .irq = OMAP_GPIO_IRQ(15), + .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, + .iotype = UPIO_MEM, + .regshift = 1, + .uartclk = 3686400, + }, + { }, +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = 1, + .dev = { + .platform_data = voiceblue_ports, + }, +}; + +static int __init ext_uart_init(void) +{ + return platform_device_register(&serial_device); +} +arch_initcall(ext_uart_init); + +static struct resource voiceblue_smc91x_resources[] = { + [0] = { + .start = OMAP_CS2_PHYS + 0x300, + .end = OMAP_CS2_PHYS + 0x300 + 16, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP_GPIO_IRQ(8), + .end = OMAP_GPIO_IRQ(8), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device voiceblue_smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(voiceblue_smc91x_resources), + .resource = voiceblue_smc91x_resources, +}; + +static struct platform_device *voiceblue_devices[] __initdata = { + &voiceblue_smc91x_device, +}; + +static struct omap_usb_config voiceblue_usb_config __initdata = { + .hmc_mode = 3, + .register_host = 1, + .register_dev = 1, + .pins[0] = 2, + .pins[1] = 6, + .pins[2] = 6, +}; + +static struct omap_board_config_kernel voiceblue_config[] = { + { OMAP_TAG_USB, &voiceblue_usb_config }, +}; + +static void __init voiceblue_init_irq(void) +{ + omap_init_irq(); + omap_gpio_init(); +} + +static void __init voiceblue_init(void) +{ + /* There is a good chance board is going up, so enable Power LED + * (it is connected through invertor) */ + omap_writeb(0x00, OMAP_LPG1_LCR); + /* Watchdog */ + omap_request_gpio(0); + /* smc91x reset */ + omap_request_gpio(7); + omap_set_gpio_direction(7, 0); + omap_set_gpio_dataout(7, 1); + udelay(2); /* wait at least 100ns */ + omap_set_gpio_dataout(7, 0); + mdelay(50); /* 50ms until PHY ready */ + /* smc91x interrupt pin */ + omap_request_gpio(8); + omap_set_gpio_edge_ctrl(8, OMAP_GPIO_RISING_EDGE); + /* 16C554 reset*/ + omap_request_gpio(6); + omap_set_gpio_direction(6, 0); + omap_set_gpio_dataout(6, 0); + /* 16C554 interrupt pins */ + omap_request_gpio(12); + omap_request_gpio(13); + omap_request_gpio(14); + omap_request_gpio(15); + omap_set_gpio_edge_ctrl(12, OMAP_GPIO_RISING_EDGE); + omap_set_gpio_edge_ctrl(13, OMAP_GPIO_RISING_EDGE); + omap_set_gpio_edge_ctrl(14, OMAP_GPIO_RISING_EDGE); + omap_set_gpio_edge_ctrl(15, OMAP_GPIO_RISING_EDGE); + + platform_add_devices(voiceblue_devices, ARRAY_SIZE(voiceblue_devices)); + omap_board_config = voiceblue_config; + omap_board_config_size = ARRAY_SIZE(voiceblue_config); +} + +static int __initdata omap_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; + +static void __init voiceblue_map_io(void) +{ + omap_map_common_io(); + omap_serial_init(omap_serial_ports); +} + +#define MACHINE_PANICED 1 +#define MACHINE_REBOOTING 2 +#define MACHINE_REBOOT 4 +static unsigned long machine_state; + +static int panic_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + if (test_and_set_bit(MACHINE_PANICED, &machine_state)) + return NOTIFY_DONE; + + /* Flash Power LED + * (TODO: Enable clock right way (enabled in bootloader already)) */ + omap_writeb(0x78, OMAP_LPG1_LCR); + + return NOTIFY_DONE; +} + +static struct notifier_block panic_block = { + .notifier_call = panic_event, +}; + +static int __init setup_notifier(void) +{ + /* Setup panic notifier */ + notifier_chain_register(&panic_notifier_list, &panic_block); + + return 0; +} + +postcore_initcall(setup_notifier); + +static int wdt_gpio_state; + +void voiceblue_wdt_enable(void) +{ + omap_set_gpio_direction(0, 0); + omap_set_gpio_dataout(0, 0); + omap_set_gpio_dataout(0, 1); + omap_set_gpio_dataout(0, 0); + wdt_gpio_state = 0; +} + +void voiceblue_wdt_disable(void) +{ + omap_set_gpio_dataout(0, 0); + omap_set_gpio_dataout(0, 1); + omap_set_gpio_dataout(0, 0); + omap_set_gpio_direction(0, 1); +} + +void voiceblue_wdt_ping(void) +{ + if (test_bit(MACHINE_REBOOT, &machine_state)) + return; + + wdt_gpio_state = !wdt_gpio_state; + omap_set_gpio_dataout(0, wdt_gpio_state); +} + +void voiceblue_reset(void) +{ + set_bit(MACHINE_REBOOT, &machine_state); + voiceblue_wdt_enable(); + while (1) ; +} + +EXPORT_SYMBOL(voiceblue_wdt_enable); +EXPORT_SYMBOL(voiceblue_wdt_disable); +EXPORT_SYMBOL(voiceblue_wdt_ping); + +MACHINE_START(VOICEBLUE, "VoiceBlue OMAP5910") + /* Maintainer: Ladislav Michl */ + .phys_ram = 0x10000000, + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = voiceblue_map_io, + .init_irq = voiceblue_init_irq, + .init_machine = voiceblue_init, + .timer = &omap_timer, +MACHINE_END --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/fpga.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,188 @@ +/* + * linux/arch/arm/mach-omap/fpga.c + * + * Interrupt handler for OMAP-1510 Innovator FPGA + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: Greg Lonnon + * + * Copyright (C) 2002 MontaVista Software, Inc. + * + * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 + * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +static void fpga_mask_irq(unsigned int irq) +{ + irq -= OMAP1510_IH_FPGA_BASE; + + if (irq < 8) + __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) + & ~(1 << irq)), OMAP1510_FPGA_IMR_LO); + else if (irq < 16) + __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) + & ~(1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); + else + __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) + & ~(1 << (irq - 16))), INNOVATOR_FPGA_IMR2); +} + + +static inline u32 get_fpga_unmasked_irqs(void) +{ + return + ((__raw_readb(OMAP1510_FPGA_ISR_LO) & + __raw_readb(OMAP1510_FPGA_IMR_LO))) | + ((__raw_readb(OMAP1510_FPGA_ISR_HI) & + __raw_readb(OMAP1510_FPGA_IMR_HI)) << 8) | + ((__raw_readb(INNOVATOR_FPGA_ISR2) & + __raw_readb(INNOVATOR_FPGA_IMR2)) << 16); +} + + +static void fpga_ack_irq(unsigned int irq) +{ + /* Don't need to explicitly ACK FPGA interrupts */ +} + +static void fpga_unmask_irq(unsigned int irq) +{ + irq -= OMAP1510_IH_FPGA_BASE; + + if (irq < 8) + __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | (1 << irq)), + OMAP1510_FPGA_IMR_LO); + else if (irq < 16) + __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) + | (1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); + else + __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) + | (1 << (irq - 16))), INNOVATOR_FPGA_IMR2); +} + +static void fpga_mask_ack_irq(unsigned int irq) +{ + fpga_mask_irq(irq); + fpga_ack_irq(irq); +} + +void innovator_fpga_IRQ_demux(unsigned int irq, struct irqdesc *desc, + struct pt_regs *regs) +{ + struct irqdesc *d; + u32 stat; + int fpga_irq; + + stat = get_fpga_unmasked_irqs(); + + if (!stat) + return; + + for (fpga_irq = OMAP1510_IH_FPGA_BASE; + (fpga_irq < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS)) && stat; + fpga_irq++, stat >>= 1) { + if (stat & 1) { + d = irq_desc + fpga_irq; + d->handle(fpga_irq, d, regs); + } + } +} + +static struct irqchip omap_fpga_irq_ack = { + .ack = fpga_mask_ack_irq, + .mask = fpga_mask_irq, + .unmask = fpga_unmask_irq, +}; + + +static struct irqchip omap_fpga_irq = { + .ack = fpga_ack_irq, + .mask = fpga_mask_irq, + .unmask = fpga_unmask_irq, +}; + +/* + * All of the FPGA interrupt request inputs except for the touchscreen are + * edge-sensitive; the touchscreen is level-sensitive. The edge-sensitive + * interrupts are acknowledged as a side-effect of reading the interrupt + * status register from the FPGA. The edge-sensitive interrupt inputs + * cause a problem with level interrupt requests, such as Ethernet. The + * problem occurs when a level interrupt request is asserted while its + * interrupt input is masked in the FPGA, which results in a missed + * interrupt. + * + * In an attempt to workaround the problem with missed interrupts, the + * mask_ack routine for all of the FPGA interrupts has been changed from + * fpga_mask_ack_irq() to fpga_ack_irq() so that the specific FPGA interrupt + * being serviced is left unmasked. We can do this because the FPGA cascade + * interrupt is installed with the SA_INTERRUPT flag, which leaves all + * interrupts masked at the CPU while an FPGA interrupt handler executes. + * + * Limited testing indicates that this workaround appears to be effective + * for the smc9194 Ethernet driver used on the Innovator. It should work + * on other FPGA interrupts as well, but any drivers that explicitly mask + * interrupts at the interrupt controller via disable_irq/enable_irq + * could pose a problem. + */ +void omap1510_fpga_init_irq(void) +{ + int i; + + __raw_writeb(0, OMAP1510_FPGA_IMR_LO); + __raw_writeb(0, OMAP1510_FPGA_IMR_HI); + __raw_writeb(0, INNOVATOR_FPGA_IMR2); + + for (i = OMAP1510_IH_FPGA_BASE; i < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS); i++) { + + if (i == OMAP1510_INT_FPGA_TS) { + /* + * The touchscreen interrupt is level-sensitive, so + * we'll use the regular mask_ack routine for it. + */ + set_irq_chip(i, &omap_fpga_irq_ack); + } + else { + /* + * All FPGA interrupts except the touchscreen are + * edge-sensitive, so we won't mask them. + */ + set_irq_chip(i, &omap_fpga_irq); + } + + set_irq_handler(i, do_edge_IRQ); + set_irq_flags(i, IRQF_VALID); + } + + /* + * The FPGA interrupt line is connected to GPIO13. Claim this pin for + * the ARM. + * + * NOTE: For general GPIO/MPUIO access and interrupts, please see + * gpio.[ch] + */ + omap_request_gpio(13); + omap_set_gpio_direction(13, 1); + omap_set_gpio_edge_ctrl(13, OMAP_GPIO_RISING_EDGE); + set_irq_chained_handler(OMAP1510_INT_FPGA, innovator_fpga_IRQ_demux); +} + +EXPORT_SYMBOL(omap1510_fpga_init_irq); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/id.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,188 @@ +/* + * linux/arch/arm/mach-omap1/id.c + * + * OMAP1 CPU identification code + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tony Lindgren + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include + +struct omap_id { + u16 jtag_id; /* Used to determine OMAP type */ + u8 die_rev; /* Processor revision */ + u32 omap_id; /* OMAP revision */ + u32 type; /* Cpu id bits [31:08], cpu class bits [07:00] */ +}; + +/* Register values to detect the OMAP version */ +static struct omap_id omap_ids[] __initdata = { + { .jtag_id = 0x355f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300100}, + { .jtag_id = 0xb55f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300300}, + { .jtag_id = 0xb470, .die_rev = 0x0, .omap_id = 0x03310100, .type = 0x15100000}, + { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x16100000}, + { .jtag_id = 0xb576, .die_rev = 0x2, .omap_id = 0x03320100, .type = 0x16110000}, + { .jtag_id = 0xb576, .die_rev = 0x3, .omap_id = 0x03320100, .type = 0x16100c00}, + { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320200, .type = 0x16100d00}, + { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00}, + { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00}, + { .jtag_id = 0xb576, .die_rev = 0x1, .omap_id = 0x03320100, .type = 0x16110000}, + { .jtag_id = 0xb58c, .die_rev = 0x2, .omap_id = 0x03320200, .type = 0x16110b00}, + { .jtag_id = 0xb58c, .die_rev = 0x3, .omap_id = 0x03320200, .type = 0x16110c00}, + { .jtag_id = 0xb65f, .die_rev = 0x0, .omap_id = 0x03320400, .type = 0x16212300}, + { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320400, .type = 0x16212300}, + { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320500, .type = 0x16212300}, + { .jtag_id = 0xb5f7, .die_rev = 0x0, .omap_id = 0x03330000, .type = 0x17100000}, + { .jtag_id = 0xb5f7, .die_rev = 0x1, .omap_id = 0x03330100, .type = 0x17100000}, + { .jtag_id = 0xb5f7, .die_rev = 0x2, .omap_id = 0x03330100, .type = 0x17100000}, +}; + +/* + * Get OMAP type from PROD_ID. + * 1710 has the PROD_ID in bits 15:00, not in 16:01 as documented in TRM. + * 1510 PROD_ID is empty, and 1610 PROD_ID does not make sense. + * Undocumented register in TEST BLOCK is used as fallback; This seems to + * work on 1510, 1610 & 1710. The official way hopefully will work in future + * processors. + */ +static u16 __init omap_get_jtag_id(void) +{ + u32 prod_id, omap_id; + + prod_id = omap_readl(OMAP_PRODUCTION_ID_1); + omap_id = omap_readl(OMAP32_ID_1); + + /* Check for unusable OMAP_PRODUCTION_ID_1 on 1611B/5912 and 730 */ + if (((prod_id >> 20) == 0) || (prod_id == omap_id)) + prod_id = 0; + else + prod_id &= 0xffff; + + if (prod_id) + return prod_id; + + /* Use OMAP32_ID_1 as fallback */ + prod_id = ((omap_id >> 12) & 0xffff); + + return prod_id; +} + +/* + * Get OMAP revision from DIE_REV. + * Early 1710 processors may have broken OMAP_DIE_ID, it contains PROD_ID. + * Undocumented register in the TEST BLOCK is used as fallback. + * REVISIT: This does not seem to work on 1510 + */ +static u8 __init omap_get_die_rev(void) +{ + u32 die_rev; + + die_rev = omap_readl(OMAP_DIE_ID_1); + + /* Check for broken OMAP_DIE_ID on early 1710 */ + if (((die_rev >> 12) & 0xffff) == omap_get_jtag_id()) + die_rev = 0; + + die_rev = (die_rev >> 17) & 0xf; + if (die_rev) + return die_rev; + + die_rev = (omap_readl(OMAP32_ID_1) >> 28) & 0xf; + + return die_rev; +} + +void __init omap_check_revision(void) +{ + int i; + u16 jtag_id; + u8 die_rev; + u32 omap_id; + u8 cpu_type; + + jtag_id = omap_get_jtag_id(); + die_rev = omap_get_die_rev(); + omap_id = omap_readl(OMAP32_ID_0); + +#ifdef DEBUG + printk("OMAP_DIE_ID_0: 0x%08x\n", omap_readl(OMAP_DIE_ID_0)); + printk("OMAP_DIE_ID_1: 0x%08x DIE_REV: %i\n", + omap_readl(OMAP_DIE_ID_1), + (omap_readl(OMAP_DIE_ID_1) >> 17) & 0xf); + printk("OMAP_PRODUCTION_ID_0: 0x%08x\n", omap_readl(OMAP_PRODUCTION_ID_0)); + printk("OMAP_PRODUCTION_ID_1: 0x%08x JTAG_ID: 0x%04x\n", + omap_readl(OMAP_PRODUCTION_ID_1), + omap_readl(OMAP_PRODUCTION_ID_1) & 0xffff); + printk("OMAP32_ID_0: 0x%08x\n", omap_readl(OMAP32_ID_0)); + printk("OMAP32_ID_1: 0x%08x\n", omap_readl(OMAP32_ID_1)); + printk("JTAG_ID: 0x%04x DIE_REV: %i\n", jtag_id, die_rev); +#endif + + system_serial_high = omap_readl(OMAP_DIE_ID_0); + system_serial_low = omap_readl(OMAP_DIE_ID_1); + + /* First check only the major version in a safe way */ + for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { + if (jtag_id == (omap_ids[i].jtag_id)) { + system_rev = omap_ids[i].type; + break; + } + } + + /* Check if we can find the die revision */ + for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { + if (jtag_id == omap_ids[i].jtag_id && die_rev == omap_ids[i].die_rev) { + system_rev = omap_ids[i].type; + break; + } + } + + /* Finally check also the omap_id */ + for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { + if (jtag_id == omap_ids[i].jtag_id + && die_rev == omap_ids[i].die_rev + && omap_id == omap_ids[i].omap_id) { + system_rev = omap_ids[i].type; + break; + } + } + + /* Add the cpu class info (7xx, 15xx, 16xx, 24xx) */ + cpu_type = system_rev >> 24; + + switch (cpu_type) { + case 0x07: + system_rev |= 0x07; + break; + case 0x15: + system_rev |= 0x15; + break; + case 0x16: + case 0x17: + system_rev |= 0x16; + break; + case 0x24: + system_rev |= 0x24; + break; + default: + printk("Unknown OMAP cpu type: 0x%02x\n", cpu_type); + } + + printk("OMAP%04x", system_rev >> 16); + if ((system_rev >> 8) & 0xff) + printk("%x", (system_rev >> 8) & 0xff); + printk(" revision %i handled as %02xxx id: %08x%08x\n", + die_rev, system_rev & 0xff, system_serial_low, + system_serial_high); +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/io.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,115 @@ +/* + * linux/arch/arm/mach-omap1/io.c + * + * OMAP1 I/O mapping code + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include + +extern int clk_init(void); +extern void omap_check_revision(void); + +/* + * The machine specific code may provide the extra mapping besides the + * default mapping provided here. + */ +static struct map_desc omap_io_desc[] __initdata = { + { IO_VIRT, IO_PHYS, IO_SIZE, MT_DEVICE }, +}; + +#ifdef CONFIG_ARCH_OMAP730 +static struct map_desc omap730_io_desc[] __initdata = { + { OMAP730_DSP_BASE, OMAP730_DSP_START, OMAP730_DSP_SIZE, MT_DEVICE }, + { OMAP730_DSPREG_BASE, OMAP730_DSPREG_START, OMAP730_DSPREG_SIZE, MT_DEVICE }, + { OMAP730_SRAM_BASE, OMAP730_SRAM_START, OMAP730_SRAM_SIZE, MT_DEVICE } +}; +#endif + +#ifdef CONFIG_ARCH_OMAP1510 +static struct map_desc omap1510_io_desc[] __initdata = { + { OMAP1510_DSP_BASE, OMAP1510_DSP_START, OMAP1510_DSP_SIZE, MT_DEVICE }, + { OMAP1510_DSPREG_BASE, OMAP1510_DSPREG_START, OMAP1510_DSPREG_SIZE, MT_DEVICE }, + { OMAP1510_SRAM_BASE, OMAP1510_SRAM_START, OMAP1510_SRAM_SIZE, MT_DEVICE } +}; +#endif + +#if defined(CONFIG_ARCH_OMAP16XX) +static struct map_desc omap1610_io_desc[] __initdata = { + { OMAP16XX_DSP_BASE, OMAP16XX_DSP_START, OMAP16XX_DSP_SIZE, MT_DEVICE }, + { OMAP16XX_DSPREG_BASE, OMAP16XX_DSPREG_START, OMAP16XX_DSPREG_SIZE, MT_DEVICE }, + { OMAP16XX_SRAM_BASE, OMAP16XX_SRAM_START, OMAP1610_SRAM_SIZE, MT_DEVICE } +}; + +static struct map_desc omap5912_io_desc[] __initdata = { + { OMAP16XX_DSP_BASE, OMAP16XX_DSP_START, OMAP16XX_DSP_SIZE, MT_DEVICE }, + { OMAP16XX_DSPREG_BASE, OMAP16XX_DSPREG_START, OMAP16XX_DSPREG_SIZE, MT_DEVICE }, +/* + * The OMAP5912 has 250kByte internal SRAM. Because the mapping is baseed on page + * size (4kByte), it seems that the last 2kByte (=0x800) of the 250kByte are not mapped. + * Add additional 2kByte (0x800) so that the last page is mapped and the last 2kByte + * can be used. + */ + { OMAP16XX_SRAM_BASE, OMAP16XX_SRAM_START, OMAP5912_SRAM_SIZE + 0x800, MT_DEVICE } +}; +#endif + +static int initialized = 0; + +static void __init _omap_map_io(void) +{ + initialized = 1; + + /* We have to initialize the IO space mapping before we can run + * cpu_is_omapxxx() macros. */ + iotable_init(omap_io_desc, ARRAY_SIZE(omap_io_desc)); + omap_check_revision(); + +#ifdef CONFIG_ARCH_OMAP730 + if (cpu_is_omap730()) { + iotable_init(omap730_io_desc, ARRAY_SIZE(omap730_io_desc)); + } +#endif +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc)); + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (cpu_is_omap1610() || cpu_is_omap1710()) { + iotable_init(omap1610_io_desc, ARRAY_SIZE(omap1610_io_desc)); + } + if (cpu_is_omap5912()) { + iotable_init(omap5912_io_desc, ARRAY_SIZE(omap5912_io_desc)); + } +#endif + + /* REVISIT: Refer to OMAP5910 Errata, Advisory SYS_1: "Timeout Abort + * on a Posted Write in the TIPB Bridge". + */ + omap_writew(0x0, MPU_PUBLIC_TIPB_CNTL); + omap_writew(0x0, MPU_PRIVATE_TIPB_CNTL); + + /* Must init clocks early to assure that timer interrupt works + */ + clk_init(); +} + +/* + * This should only get called from board specific init + */ +void omap_map_common_io(void) +{ + if (!initialized) + _omap_map_io(); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/irq.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,234 @@ +/* + * linux/arch/arm/mach-omap/irq.c + * + * Interrupt handler for all OMAP boards + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tony Lindgren + * Major cleanups by Juha Yrjölä + * + * Completely re-written to support various OMAP chips with bank specific + * interrupt handlers. + * + * Some snippets of the code taken from the older OMAP interrupt handler + * Copyright (C) 2001 RidgeRun, Inc. Greg Lonnon + * + * GPIO interrupt handler moved to gpio.c by Juha Yrjola + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define IRQ_BANK(irq) ((irq) >> 5) +#define IRQ_BIT(irq) ((irq) & 0x1f) + +struct omap_irq_bank { + unsigned long base_reg; + unsigned long trigger_map; + unsigned long wake_enable; +}; + +static unsigned int irq_bank_count = 0; +static struct omap_irq_bank *irq_banks; + +static inline unsigned int irq_bank_readl(int bank, int offset) +{ + return omap_readl(irq_banks[bank].base_reg + offset); +} + +static inline void irq_bank_writel(unsigned long value, int bank, int offset) +{ + omap_writel(value, irq_banks[bank].base_reg + offset); +} + +static void omap_ack_irq(unsigned int irq) +{ + if (irq > 31) + omap_writel(0x1, OMAP_IH2_BASE + IRQ_CONTROL_REG_OFFSET); + + omap_writel(0x1, OMAP_IH1_BASE + IRQ_CONTROL_REG_OFFSET); +} + +static void omap_mask_irq(unsigned int irq) +{ + int bank = IRQ_BANK(irq); + u32 l; + + l = omap_readl(irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); + l |= 1 << IRQ_BIT(irq); + omap_writel(l, irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); +} + +static void omap_unmask_irq(unsigned int irq) +{ + int bank = IRQ_BANK(irq); + u32 l; + + l = omap_readl(irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); + l &= ~(1 << IRQ_BIT(irq)); + omap_writel(l, irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); +} + +static void omap_mask_ack_irq(unsigned int irq) +{ + omap_mask_irq(irq); + omap_ack_irq(irq); +} + +static int omap_wake_irq(unsigned int irq, unsigned int enable) +{ + int bank = IRQ_BANK(irq); + + if (enable) + irq_banks[bank].wake_enable |= IRQ_BIT(irq); + else + irq_banks[bank].wake_enable &= ~IRQ_BIT(irq); + + return 0; +} + + +/* + * Allows tuning the IRQ type and priority + * + * NOTE: There is currently no OMAP fiq handler for Linux. Read the + * mailing list threads on FIQ handlers if you are planning to + * add a FIQ handler for OMAP. + */ +static void omap_irq_set_cfg(int irq, int fiq, int priority, int trigger) +{ + signed int bank; + unsigned long val, offset; + + bank = IRQ_BANK(irq); + /* FIQ is only available on bank 0 interrupts */ + fiq = bank ? 0 : (fiq & 0x1); + val = fiq | ((priority & 0x1f) << 2) | ((trigger & 0x1) << 1); + offset = IRQ_ILR0_REG_OFFSET + IRQ_BIT(irq) * 0x4; + irq_bank_writel(val, bank, offset); +} + +#ifdef CONFIG_ARCH_OMAP730 +static struct omap_irq_bank omap730_irq_banks[] = { + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3f8e22f }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb9c1f2 }, + { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0x800040f3 }, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP1510 +static struct omap_irq_bank omap1510_irq_banks[] = { + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3febfff }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xffbfffed }, +}; +#endif + +#if defined(CONFIG_ARCH_OMAP16XX) + +static struct omap_irq_bank omap1610_irq_banks[] = { + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3fefe8f }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb7c1fd }, + { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0xffffb7ff }, + { .base_reg = OMAP_IH2_BASE + 0x200, .trigger_map = 0xffffffff }, +}; +#endif + +static struct irqchip omap_irq_chip = { + .ack = omap_mask_ack_irq, + .mask = omap_mask_irq, + .unmask = omap_unmask_irq, + .wake = omap_wake_irq, +}; + +void __init omap_init_irq(void) +{ + int i, j; + +#ifdef CONFIG_ARCH_OMAP730 + if (cpu_is_omap730()) { + irq_banks = omap730_irq_banks; + irq_bank_count = ARRAY_SIZE(omap730_irq_banks); + } +#endif +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + irq_banks = omap1510_irq_banks; + irq_bank_count = ARRAY_SIZE(omap1510_irq_banks); + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (cpu_is_omap16xx()) { + irq_banks = omap1610_irq_banks; + irq_bank_count = ARRAY_SIZE(omap1610_irq_banks); + } +#endif + printk("Total of %i interrupts in %i interrupt banks\n", + irq_bank_count * 32, irq_bank_count); + + /* Mask and clear all interrupts */ + for (i = 0; i < irq_bank_count; i++) { + irq_bank_writel(~0x0, i, IRQ_MIR_REG_OFFSET); + irq_bank_writel(0x0, i, IRQ_ITR_REG_OFFSET); + } + + /* Clear any pending interrupts */ + irq_bank_writel(0x03, 0, IRQ_CONTROL_REG_OFFSET); + irq_bank_writel(0x03, 1, IRQ_CONTROL_REG_OFFSET); + + /* Enable interrupts in global mask */ + if (cpu_is_omap730()) { + irq_bank_writel(0x0, 0, IRQ_GMR_REG_OFFSET); + } + + /* Install the interrupt handlers for each bank */ + for (i = 0; i < irq_bank_count; i++) { + for (j = i * 32; j < (i + 1) * 32; j++) { + int irq_trigger; + + irq_trigger = irq_banks[i].trigger_map >> IRQ_BIT(j); + omap_irq_set_cfg(j, 0, 0, irq_trigger); + + set_irq_chip(j, &omap_irq_chip); + set_irq_handler(j, do_level_IRQ); + set_irq_flags(j, IRQF_VALID); + } + } + + /* Unmask level 2 handler */ + if (cpu_is_omap730()) { + omap_unmask_irq(INT_730_IH2_IRQ); + } else { + omap_unmask_irq(INT_IH2_IRQ); + } +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/Kconfig 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,144 @@ +comment "OMAP Core Type" + depends on ARCH_OMAP1 + +config ARCH_OMAP730 + depends on ARCH_OMAP1 + bool "OMAP730 Based System" + select ARCH_OMAP_OTG + +config ARCH_OMAP1510 + depends on ARCH_OMAP1 + default y + bool "OMAP1510 Based System" + +config ARCH_OMAP16XX + depends on ARCH_OMAP1 + bool "OMAP16xx Based System" + select ARCH_OMAP_OTG + +comment "OMAP Board Type" + depends on ARCH_OMAP1 + +config MACH_OMAP_INNOVATOR + bool "TI Innovator" + depends on ARCH_OMAP1 && (ARCH_OMAP1510 || ARCH_OMAP16XX) + help + TI OMAP 1510 or 1610 Innovator board support. Say Y here if you + have such a board. + +config MACH_OMAP_H2 + bool "TI H2 Support" + depends on ARCH_OMAP1 && ARCH_OMAP16XX + help + TI OMAP 1610/1611B H2 board support. Say Y here if you have such + a board. + +config MACH_OMAP_H3 + bool "TI H3 Support" + depends on ARCH_OMAP1 && ARCH_OMAP16XX + help + TI OMAP 1710 H3 board support. Say Y here if you have such + a board. + +config MACH_OMAP_OSK + bool "TI OSK Support" + depends on ARCH_OMAP1 && ARCH_OMAP16XX + help + TI OMAP 5912 OSK (OMAP Starter Kit) board support. Say Y here + if you have such a board. + +config MACH_OMAP_PERSEUS2 + bool "TI Perseus2" + depends on ARCH_OMAP1 && ARCH_OMAP730 + help + Support for TI OMAP 730 Perseus2 board. Say Y here if you have such + a board. + +config MACH_VOICEBLUE + bool "Voiceblue" + depends on ARCH_OMAP1 && ARCH_OMAP1510 + help + Support for Voiceblue GSM/VoIP gateway. Say Y here if you have + such a board. + +config MACH_NETSTAR + bool "NetStar" + depends on ARCH_OMAP1 && ARCH_OMAP1510 + help + Support for NetStar PBX. Say Y here if you have such a board. + +config MACH_OMAP_GENERIC + bool "Generic OMAP board" + depends on ARCH_OMAP1 && (ARCH_OMAP1510 || ARCH_OMAP16XX) + help + Support for generic OMAP-1510, 1610 or 1710 board with + no FPGA. Can be used as template for porting Linux to + custom OMAP boards. Say Y here if you have a custom + board. + +comment "OMAP CPU Speed" + depends on ARCH_OMAP1 + +config OMAP_CLOCKS_SET_BY_BOOTLOADER + bool "OMAP clocks set by bootloader" + depends on ARCH_OMAP1 + help + Enable this option to prevent the kernel from overriding the clock + frequencies programmed by bootloader for MPU, DSP, MMUs, TC, + internal LCD controller and MPU peripherals. + +config OMAP_ARM_216MHZ + bool "OMAP ARM 216 MHz CPU (1710 only)" + depends on ARCH_OMAP1 && ARCH_OMAP16XX + help + Enable 216 MHz clock for OMAP1710 CPU. If unsure, say N. + +config OMAP_ARM_195MHZ + bool "OMAP ARM 195 MHz CPU" + depends on ARCH_OMAP1 && ARCH_OMAP730 + help + Enable 195MHz clock for OMAP CPU. If unsure, say N. + +config OMAP_ARM_192MHZ + bool "OMAP ARM 192 MHz CPU" + depends on ARCH_OMAP1 && ARCH_OMAP16XX + help + Enable 192MHz clock for OMAP CPU. If unsure, say N. + +config OMAP_ARM_182MHZ + bool "OMAP ARM 182 MHz CPU" + depends on ARCH_OMAP1 && ARCH_OMAP730 + help + Enable 182MHz clock for OMAP CPU. If unsure, say N. + +config OMAP_ARM_168MHZ + bool "OMAP ARM 168 MHz CPU" + depends on ARCH_OMAP1 && (ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730) + help + Enable 168MHz clock for OMAP CPU. If unsure, say N. + +config OMAP_ARM_150MHZ + bool "OMAP ARM 150 MHz CPU" + depends on ARCH_OMAP1 && ARCH_OMAP1510 + help + Enable 150MHz clock for OMAP CPU. If unsure, say N. + +config OMAP_ARM_120MHZ + bool "OMAP ARM 120 MHz CPU" + depends on ARCH_OMAP1 && (ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730) + help + Enable 120MHz clock for OMAP CPU. If unsure, say N. + +config OMAP_ARM_60MHZ + bool "OMAP ARM 60 MHz CPU" + depends on ARCH_OMAP1 && (ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730) + default y + help + Enable 60MHz clock for OMAP CPU. If unsure, say Y. + +config OMAP_ARM_30MHZ + bool "OMAP ARM 30 MHz CPU" + depends on ARCH_OMAP1 && (ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730) + help + Enable 30MHz clock for OMAP CPU. If unsure, say N. + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/leds.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,61 @@ +/* + * linux/arch/arm/mach-omap/leds.c + * + * OMAP LEDs dispatcher + */ +#include +#include + +#include +#include + +#include +#include + +#include "leds.h" + +static int __init +omap_leds_init(void) +{ + if (machine_is_omap_innovator()) + leds_event = innovator_leds_event; + + else if (machine_is_omap_h2() || machine_is_omap_perseus2()) + leds_event = h2p2_dbg_leds_event; + + else if (machine_is_omap_osk()) + leds_event = osk_leds_event; + + else + return -1; + + if (machine_is_omap_h2() + || machine_is_omap_perseus2() + || machine_is_omap_osk()) { + + /* LED1/LED2 pins can be used as GPIO (as done here), or by + * the LPG (works even in deep sleep!), to drive a bicolor + * LED on the H2 sample board, and another on the H2/P2 + * "surfer" expansion board. + * + * The same pins drive a LED on the OSK Mistral board, but + * that's a different kind of LED (just one color at a time). + */ + omap_cfg_reg(P18_1610_GPIO3); + if (omap_request_gpio(3) == 0) + omap_set_gpio_direction(3, 0); + else + printk(KERN_WARNING "LED: can't get GPIO3/red?\n"); + + omap_cfg_reg(MPUIO4); + if (omap_request_gpio(OMAP_MPUIO(4)) == 0) + omap_set_gpio_direction(OMAP_MPUIO(4), 0); + else + printk(KERN_WARNING "LED: can't get MPUIO4/green?\n"); + } + + leds_event(led_start); + return 0; +} + +__initcall(omap_leds_init); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/leds.h 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,3 @@ +extern void innovator_leds_event(led_event_t evt); +extern void h2p2_dbg_leds_event(led_event_t evt); +extern void osk_leds_event(led_event_t evt); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/leds-h2p2-debug.c 2005-07-12 00:26:40.000000000 -0700 @@ -0,0 +1,143 @@ +/* + * linux/arch/arm/mach-omap/leds-h2p2-debug.c + * + * Copyright 2003 by Texas Instruments Incorporated + * + * There are 16 LEDs on the debug board (all green); four may be used + * for logical 'green', 'amber', 'red', and 'blue' (after "claiming"). + * + * The "surfer" expansion board and H2 sample board also have two-color + * green+red LEDs (in parallel), used here for timer and idle indicators. + */ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "leds.h" + + +#define GPIO_LED_RED 3 +#define GPIO_LED_GREEN OMAP_MPUIO(4) + + +#define LED_STATE_ENABLED 0x01 +#define LED_STATE_CLAIMED 0x02 +#define LED_TIMER_ON 0x04 + +#define GPIO_IDLE GPIO_LED_GREEN +#define GPIO_TIMER GPIO_LED_RED + + +void h2p2_dbg_leds_event(led_event_t evt) +{ + unsigned long flags; + + static struct h2p2_dbg_fpga __iomem *fpga; + static u16 led_state, hw_led_state; + + local_irq_save(flags); + + if (!(led_state & LED_STATE_ENABLED) && evt != led_start) + goto done; + + switch (evt) { + case led_start: + if (!fpga) + fpga = ioremap(H2P2_DBG_FPGA_START, + H2P2_DBG_FPGA_SIZE); + if (fpga) { + led_state |= LED_STATE_ENABLED; + __raw_writew(~0, &fpga->leds); + } + break; + + case led_stop: + case led_halted: + /* all leds off during suspend or shutdown */ + omap_set_gpio_dataout(GPIO_TIMER, 0); + omap_set_gpio_dataout(GPIO_IDLE, 0); + __raw_writew(~0, &fpga->leds); + led_state &= ~LED_STATE_ENABLED; + if (evt == led_halted) { + iounmap(fpga); + fpga = NULL; + } + goto done; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = 0; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + led_state ^= LED_TIMER_ON; + omap_set_gpio_dataout(GPIO_TIMER, led_state & LED_TIMER_ON); + goto done; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + omap_set_gpio_dataout(GPIO_IDLE, 1); + goto done; + + case led_idle_end: + omap_set_gpio_dataout(GPIO_IDLE, 0); + goto done; +#endif + + case led_green_on: + hw_led_state |= H2P2_DBG_FPGA_LED_GREEN; + break; + case led_green_off: + hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN; + break; + + case led_amber_on: + hw_led_state |= H2P2_DBG_FPGA_LED_AMBER; + break; + case led_amber_off: + hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER; + break; + + case led_red_on: + hw_led_state |= H2P2_DBG_FPGA_LED_RED; + break; + case led_red_off: + hw_led_state &= ~H2P2_DBG_FPGA_LED_RED; + break; + + case led_blue_on: + hw_led_state |= H2P2_DBG_FPGA_LED_BLUE; + break; + case led_blue_off: + hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE; + break; + + default: + break; + } + + + /* + * Actually burn the LEDs + */ + if (led_state & LED_STATE_CLAIMED) + __raw_writew(~hw_led_state, &fpga->leds); + +done: + local_irq_restore(flags); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/leds-innovator.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,103 @@ +/* + * linux/arch/arm/mach-omap/leds-innovator.c + */ +#include +#include + +#include +#include +#include + +#include "leds.h" + + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 + +static unsigned int led_state; +static unsigned int hw_led_state; + +void innovator_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (evt) { + case led_start: + hw_led_state = 0; + led_state = LED_STATE_ENABLED; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + hw_led_state = 0; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = 0; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = 0; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= 0; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= 0; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~0; + break; +#endif + + case led_halted: + break; + + case led_green_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~0; + break; + + case led_green_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= 0; + break; + + case led_amber_on: + break; + + case led_amber_off: + break; + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~0; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= 0; + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) + ; + + local_irq_restore(flags); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/leds-osk.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,194 @@ +/* + * linux/arch/arm/mach-omap/leds-osk.c + * + * LED driver for OSK, and optionally Mistral QVGA, boards + */ +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "leds.h" + + +#define LED_STATE_ENABLED (1 << 0) +#define LED_STATE_CLAIMED (1 << 1) +static u8 led_state; + +#define GREEN_LED (1 << 0) /* TPS65010 LED1 */ +#define AMBER_LED (1 << 1) /* TPS65010 LED2 */ +#define RED_LED (1 << 2) /* TPS65010 GPIO2 */ +#define TIMER_LED (1 << 3) /* Mistral board */ +#define IDLE_LED (1 << 4) /* Mistral board */ +static u8 hw_led_state; + + +/* TPS65010 leds are changed using i2c -- from a task context. + * Using one of these for the "idle" LED would be impractical... + */ +#define TPS_LEDS (GREEN_LED | RED_LED | AMBER_LED) + +static u8 tps_leds_change; + +static void tps_work(void *unused) +{ + for (;;) { + u8 leds; + + local_irq_disable(); + leds = tps_leds_change; + tps_leds_change = 0; + local_irq_enable(); + + if (!leds) + break; + + /* careful: the set_led() value is on/off/blink */ + if (leds & GREEN_LED) + tps65010_set_led(LED1, !!(hw_led_state & GREEN_LED)); + if (leds & AMBER_LED) + tps65010_set_led(LED2, !!(hw_led_state & AMBER_LED)); + + /* the gpio led doesn't have that issue */ + if (leds & RED_LED) + tps65010_set_gpio_out_value(GPIO2, + !(hw_led_state & RED_LED)); + } +} + +static DECLARE_WORK(work, tps_work, NULL); + +#ifdef CONFIG_FB_OMAP + +/* For now, all system indicators require the Mistral board, since that + * LED can be manipulated without a task context. This LED is either red, + * or green, but not both; it can't give the full "disco led" effect. + */ + +#define GPIO_LED_RED 3 +#define GPIO_LED_GREEN OMAP_MPUIO(4) + +static void mistral_setled(void) +{ + int red = 0; + int green = 0; + + if (hw_led_state & TIMER_LED) + red = 1; + else if (hw_led_state & IDLE_LED) + green = 1; + // else both sides are disabled + + omap_set_gpio_dataout(GPIO_LED_GREEN, green); + omap_set_gpio_dataout(GPIO_LED_RED, red); +} + +#endif + +void osk_leds_event(led_event_t evt) +{ + unsigned long flags; + u16 leds; + + local_irq_save(flags); + + if (!(led_state & LED_STATE_ENABLED) && evt != led_start) + goto done; + + leds = hw_led_state; + switch (evt) { + case led_start: + led_state |= LED_STATE_ENABLED; + hw_led_state = 0; + leds = ~0; + break; + + case led_halted: + case led_stop: + led_state &= ~LED_STATE_ENABLED; + hw_led_state = 0; + // NOTE: work may still be pending!! + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = 0; + leds = ~0; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = 0; + break; + +#ifdef CONFIG_FB_OMAP + + case led_timer: + hw_led_state ^= TIMER_LED; + mistral_setled(); + break; + + case led_idle_start: + hw_led_state |= IDLE_LED; + mistral_setled(); + break; + + case led_idle_end: + hw_led_state &= ~IDLE_LED; + mistral_setled(); + break; + +#endif /* CONFIG_FB_OMAP */ + + /* "green" == tps LED1 (leftmost, normally power-good) + * works only with DC adapter, not on battery power! + */ + case led_green_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= GREEN_LED; + break; + case led_green_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~GREEN_LED; + break; + + /* "amber" == tps LED2 (middle) */ + case led_amber_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= AMBER_LED; + break; + case led_amber_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~AMBER_LED; + break; + + /* "red" == LED on tps gpio3 (rightmost) */ + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= RED_LED; + break; + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~RED_LED; + break; + + default: + break; + } + + leds ^= hw_led_state; + leds &= TPS_LEDS; + if (leds && (led_state & LED_STATE_CLAIMED)) { + tps_leds_change |= leds; + schedule_work(&work); + } + +done: + local_irq_restore(flags); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/Makefile 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,30 @@ +# +# Makefile for the linux kernel. +# + +# Common support +obj-y := io.o id.o irq.o time.o serial.o +led-y := leds.o + +# Specific board support +obj-$(CONFIG_MACH_OMAP_H2) += board-h2.o +obj-$(CONFIG_MACH_OMAP_INNOVATOR) += board-innovator.o +obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o +obj-$(CONFIG_MACH_OMAP_PERSEUS2) += board-perseus2.o +obj-$(CONFIG_MACH_OMAP_OSK) += board-osk.o +obj-$(CONFIG_MACH_OMAP_H3) += board-h3.o +obj-$(CONFIG_MACH_VOICEBLUE) += board-voiceblue.o +obj-$(CONFIG_MACH_NETSTAR) += board-netstar.o + +ifeq ($(CONFIG_ARCH_OMAP1510),y) +# Innovator-1510 FPGA +obj-$(CONFIG_MACH_OMAP_INNOVATOR) += fpga.o +endif + +# LEDs support +led-$(CONFIG_MACH_OMAP_H2) += leds-h2p2-debug.o +led-$(CONFIG_MACH_OMAP_INNOVATOR) += leds-innovator.o +led-$(CONFIG_MACH_OMAP_PERSEUS2) += leds-h2p2-debug.o +led-$(CONFIG_MACH_OMAP_OSK) += leds-osk.o +obj-$(CONFIG_LEDS) += $(led-y) + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/Makefile.boot 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,3 @@ + zreladdr-y := 0x10008000 +params_phys-y := 0x10000100 +initrd_phys-y := 0x10800000 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/serial.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,200 @@ +/* + * linux/arch/arm/mach-omap1/id.c + * + * OMAP1 CPU identification code + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +static struct clk * uart1_ck = NULL; +static struct clk * uart2_ck = NULL; +static struct clk * uart3_ck = NULL; + +static inline unsigned int omap_serial_in(struct plat_serial8250_port *up, + int offset) +{ + offset <<= up->regshift; + return (unsigned int)__raw_readb(up->membase + offset); +} + +static inline void omap_serial_outp(struct plat_serial8250_port *p, int offset, + int value) +{ + offset <<= p->regshift; + __raw_writeb(value, p->membase + offset); +} + +/* + * Internal UARTs need to be initialized for the 8250 autoconfig to work + * properly. Note that the TX watermark initialization may not be needed + * once the 8250.c watermark handling code is merged. + */ +static void __init omap_serial_reset(struct plat_serial8250_port *p) +{ + omap_serial_outp(p, UART_OMAP_MDR1, 0x07); /* disable UART */ + omap_serial_outp(p, UART_OMAP_SCR, 0x08); /* TX watermark */ + omap_serial_outp(p, UART_OMAP_MDR1, 0x00); /* enable UART */ + + if (!cpu_is_omap1510()) { + omap_serial_outp(p, UART_OMAP_SYSC, 0x01); + while (!(omap_serial_in(p, UART_OMAP_SYSC) & 0x01)); + } +} + +static struct plat_serial8250_port serial_platform_data[] = { + { + .membase = (char*)IO_ADDRESS(OMAP_UART1_BASE), + .mapbase = (unsigned long)OMAP_UART1_BASE, + .irq = INT_UART1, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = OMAP16XX_BASE_BAUD * 16, + }, + { + .membase = (char*)IO_ADDRESS(OMAP_UART2_BASE), + .mapbase = (unsigned long)OMAP_UART2_BASE, + .irq = INT_UART2, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = OMAP16XX_BASE_BAUD * 16, + }, + { + .membase = (char*)IO_ADDRESS(OMAP_UART3_BASE), + .mapbase = (unsigned long)OMAP_UART3_BASE, + .irq = INT_UART3, + .flags = UPF_BOOT_AUTOCONF, + .iotype = UPIO_MEM, + .regshift = 2, + .uartclk = OMAP16XX_BASE_BAUD * 16, + }, + { }, +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = 0, + .dev = { + .platform_data = serial_platform_data, + }, +}; + +/* + * Note that on Innovator-1510 UART2 pins conflict with USB2. + * By default UART2 does not work on Innovator-1510 if you have + * USB OHCI enabled. To use UART2, you must disable USB2 first. + */ +void __init omap_serial_init(int ports[OMAP_MAX_NR_PORTS]) +{ + int i; + + if (cpu_is_omap730()) { + serial_platform_data[0].regshift = 0; + serial_platform_data[1].regshift = 0; + serial_platform_data[0].irq = INT_730_UART_MODEM_1; + serial_platform_data[1].irq = INT_730_UART_MODEM_IRDA_2; + } + + if (cpu_is_omap1510()) { + serial_platform_data[0].uartclk = OMAP1510_BASE_BAUD * 16; + serial_platform_data[1].uartclk = OMAP1510_BASE_BAUD * 16; + serial_platform_data[2].uartclk = OMAP1510_BASE_BAUD * 16; + } + + for (i = 0; i < OMAP_MAX_NR_PORTS; i++) { + unsigned char reg; + + if (ports[i] == 0) { + serial_platform_data[i].membase = NULL; + serial_platform_data[i].mapbase = 0; + continue; + } + + switch (i) { + case 0: + uart1_ck = clk_get(NULL, "uart1_ck"); + if (IS_ERR(uart1_ck)) + printk("Could not get uart1_ck\n"); + else { + clk_use(uart1_ck); + if (cpu_is_omap1510()) + clk_set_rate(uart1_ck, 12000000); + } + if (cpu_is_omap1510()) { + omap_cfg_reg(UART1_TX); + omap_cfg_reg(UART1_RTS); + if (machine_is_omap_innovator()) { + reg = fpga_read(OMAP1510_FPGA_POWER); + reg |= OMAP1510_FPGA_PCR_COM1_EN; + fpga_write(reg, OMAP1510_FPGA_POWER); + udelay(10); + } + } + break; + case 1: + uart2_ck = clk_get(NULL, "uart2_ck"); + if (IS_ERR(uart2_ck)) + printk("Could not get uart2_ck\n"); + else { + clk_use(uart2_ck); + if (cpu_is_omap1510()) + clk_set_rate(uart2_ck, 12000000); + else + clk_set_rate(uart2_ck, 48000000); + } + if (cpu_is_omap1510()) { + omap_cfg_reg(UART2_TX); + omap_cfg_reg(UART2_RTS); + if (machine_is_omap_innovator()) { + reg = fpga_read(OMAP1510_FPGA_POWER); + reg |= OMAP1510_FPGA_PCR_COM2_EN; + fpga_write(reg, OMAP1510_FPGA_POWER); + udelay(10); + } + } + break; + case 2: + uart3_ck = clk_get(NULL, "uart3_ck"); + if (IS_ERR(uart3_ck)) + printk("Could not get uart3_ck\n"); + else { + clk_use(uart3_ck); + if (cpu_is_omap1510()) + clk_set_rate(uart3_ck, 12000000); + } + if (cpu_is_omap1510()) { + omap_cfg_reg(UART3_TX); + omap_cfg_reg(UART3_RX); + } + break; + } + omap_serial_reset(&serial_platform_data[i]); + } +} + +static int __init omap_init(void) +{ + return platform_device_register(&serial_device); +} +arch_initcall(omap_init); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/mach-omap1/time.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,436 @@ +/* + * linux/arch/arm/mach-omap1/time.c + * + * OMAP Timers + * + * Copyright (C) 2004 Nokia Corporation + * Partial timer rewrite and additional dynamic tick timer support by + * Tony Lindgen and + * Tuukka Tikkanen + * + * MPU timer code based on the older MPU timer code for OMAP + * Copyright (C) 2000 RidgeRun, Inc. + * Author: Greg Lonnon + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct sys_timer omap_timer; + +#ifdef CONFIG_OMAP_MPU_TIMER + +/* + * --------------------------------------------------------------------------- + * MPU timer + * --------------------------------------------------------------------------- + */ +#define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE +#define OMAP_MPU_TIMER_OFFSET 0x100 + +/* cycles to nsec conversions taken from arch/i386/kernel/timers/timer_tsc.c, + * converted to use kHz by Kevin Hilman */ +/* convert from cycles(64bits) => nanoseconds (64bits) + * basic equation: + * ns = cycles / (freq / ns_per_sec) + * ns = cycles * (ns_per_sec / freq) + * ns = cycles * (10^9 / (cpu_khz * 10^3)) + * ns = cycles * (10^6 / cpu_khz) + * + * Then we use scaling math (suggested by george at mvista.com) to get: + * ns = cycles * (10^6 * SC / cpu_khz / SC + * ns = cycles * cyc2ns_scale / SC + * + * And since SC is a constant power of two, we can convert the div + * into a shift. + * -johnstul at us.ibm.com "math is hard, lets go shopping!" + */ +static unsigned long cyc2ns_scale; +#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ + +static inline void set_cyc2ns_scale(unsigned long cpu_khz) +{ + cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz; +} + +static inline unsigned long long cycles_2_ns(unsigned long long cyc) +{ + return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; +} + +/* + * MPU_TICKS_PER_SEC must be an even number, otherwise machinecycles_to_usecs + * will break. On P2, the timer count rate is 6.5 MHz after programming PTV + * with 0. This divides the 13MHz input by 2, and is undocumented. + */ +#ifdef CONFIG_MACH_OMAP_PERSEUS2 +/* REVISIT: This ifdef construct should be replaced by a query to clock + * framework to see if timer base frequency is 12.0, 13.0 or 19.2 MHz. + */ +#define MPU_TICKS_PER_SEC (13000000 / 2) +#else +#define MPU_TICKS_PER_SEC (12000000 / 2) +#endif + +#define MPU_TIMER_TICK_PERIOD ((MPU_TICKS_PER_SEC / HZ) - 1) + +typedef struct { + u32 cntl; /* CNTL_TIMER, R/W */ + u32 load_tim; /* LOAD_TIM, W */ + u32 read_tim; /* READ_TIM, R */ +} omap_mpu_timer_regs_t; + +#define omap_mpu_timer_base(n) \ +((volatile omap_mpu_timer_regs_t*)IO_ADDRESS(OMAP_MPU_TIMER_BASE + \ + (n)*OMAP_MPU_TIMER_OFFSET)) + +static inline unsigned long omap_mpu_timer_read(int nr) +{ + volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr); + return timer->read_tim; +} + +static inline void omap_mpu_timer_start(int nr, unsigned long load_val) +{ + volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr); + + timer->cntl = MPU_TIMER_CLOCK_ENABLE; + udelay(1); + timer->load_tim = load_val; + udelay(1); + timer->cntl = (MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_AR | MPU_TIMER_ST); +} + +unsigned long omap_mpu_timer_ticks_to_usecs(unsigned long nr_ticks) +{ + unsigned long long nsec; + + nsec = cycles_2_ns((unsigned long long)nr_ticks); + return (unsigned long)nsec / 1000; +} + +/* + * Last processed system timer interrupt + */ +static unsigned long omap_mpu_timer_last = 0; + +/* + * Returns elapsed usecs since last system timer interrupt + */ +static unsigned long omap_mpu_timer_gettimeoffset(void) +{ + unsigned long now = 0 - omap_mpu_timer_read(0); + unsigned long elapsed = now - omap_mpu_timer_last; + + return omap_mpu_timer_ticks_to_usecs(elapsed); +} + +/* + * Elapsed time between interrupts is calculated using timer0. + * Latency during the interrupt is calculated using timer1. + * Both timer0 and timer1 are counting at 6MHz (P2 6.5MHz). + */ +static irqreturn_t omap_mpu_timer_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + unsigned long now, latency; + + write_seqlock(&xtime_lock); + now = 0 - omap_mpu_timer_read(0); + latency = MPU_TICKS_PER_SEC / HZ - omap_mpu_timer_read(1); + omap_mpu_timer_last = now - latency; + timer_tick(regs); + write_sequnlock(&xtime_lock); + + return IRQ_HANDLED; +} + +static struct irqaction omap_mpu_timer_irq = { + .name = "mpu timer", + .flags = SA_INTERRUPT | SA_TIMER, + .handler = omap_mpu_timer_interrupt, +}; + +static unsigned long omap_mpu_timer1_overflows; +static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + omap_mpu_timer1_overflows++; + return IRQ_HANDLED; +} + +static struct irqaction omap_mpu_timer1_irq = { + .name = "mpu timer1 overflow", + .flags = SA_INTERRUPT, + .handler = omap_mpu_timer1_interrupt, +}; + +static __init void omap_init_mpu_timer(void) +{ + set_cyc2ns_scale(MPU_TICKS_PER_SEC / 1000); + omap_timer.offset = omap_mpu_timer_gettimeoffset; + setup_irq(INT_TIMER1, &omap_mpu_timer1_irq); + setup_irq(INT_TIMER2, &omap_mpu_timer_irq); + omap_mpu_timer_start(0, 0xffffffff); + omap_mpu_timer_start(1, MPU_TIMER_TICK_PERIOD); +} + +/* + * Scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + unsigned long ticks = 0 - omap_mpu_timer_read(0); + unsigned long long ticks64; + + ticks64 = omap_mpu_timer1_overflows; + ticks64 <<= 32; + ticks64 |= ticks; + + return cycles_2_ns(ticks64); +} +#endif /* CONFIG_OMAP_MPU_TIMER */ + +#ifdef CONFIG_OMAP_32K_TIMER + +#ifdef CONFIG_ARCH_OMAP1510 +#error OMAP 32KHz timer does not currently work on 1510! +#endif + +/* + * --------------------------------------------------------------------------- + * 32KHz OS timer + * + * This currently works only on 16xx, as 1510 does not have the continuous + * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track + * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer + * on 1510 would be possible, but the timer would not be as accurate as + * with the 32KHz synchronized timer. + * --------------------------------------------------------------------------- + */ +#define OMAP_32K_TIMER_BASE 0xfffb9000 +#define OMAP_32K_TIMER_CR 0x08 +#define OMAP_32K_TIMER_TVR 0x00 +#define OMAP_32K_TIMER_TCR 0x04 + +#define OMAP_32K_TICKS_PER_HZ (32768 / HZ) +#if (32768 % HZ) != 0 +/* We cannot ignore modulo. + * Potential error can be as high as several percent. + */ +#define OMAP_32K_TICK_MODULO (32768 % HZ) +static unsigned modulo_count = 0; /* Counts 1/HZ units */ +#endif + +/* + * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 + * so with HZ = 100, TVR = 327.68. + */ +#define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) +#define TIMER_32K_SYNCHRONIZED 0xfffbc410 + +#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ + (((nr_jiffies) * (clock_rate)) / HZ) + +static inline void omap_32k_timer_write(int val, int reg) +{ + omap_writew(val, reg + OMAP_32K_TIMER_BASE); +} + +static inline unsigned long omap_32k_timer_read(int reg) +{ + return omap_readl(reg + OMAP_32K_TIMER_BASE) & 0xffffff; +} + +/* + * The 32KHz synchronized timer is an additional timer on 16xx. + * It is always running. + */ +static inline unsigned long omap_32k_sync_timer_read(void) +{ + return omap_readl(TIMER_32K_SYNCHRONIZED); +} + +static inline void omap_32k_timer_start(unsigned long load_val) +{ + omap_32k_timer_write(load_val, OMAP_32K_TIMER_TVR); + omap_32k_timer_write(0x0f, OMAP_32K_TIMER_CR); +} + +static inline void omap_32k_timer_stop(void) +{ + omap_32k_timer_write(0x0, OMAP_32K_TIMER_CR); +} + +/* + * Rounds down to nearest usec + */ +static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k) +{ + return (ticks_32k * 5*5*5*5*5*5) >> 9; +} + +static unsigned long omap_32k_last_tick = 0; + +/* + * Returns elapsed usecs since last 32k timer interrupt + */ +static unsigned long omap_32k_timer_gettimeoffset(void) +{ + unsigned long now = omap_32k_sync_timer_read(); + return omap_32k_ticks_to_usecs(now - omap_32k_last_tick); +} + +/* + * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this + * function is also called from other interrupts to remove latency + * issues with dynamic tick. In the dynamic tick case, we need to lock + * with irqsave. + */ +static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + unsigned long flags; + unsigned long now; + + write_seqlock_irqsave(&xtime_lock, flags); + now = omap_32k_sync_timer_read(); + + while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) { +#ifdef OMAP_32K_TICK_MODULO + /* Modulo addition may put omap_32k_last_tick ahead of now + * and cause unwanted repetition of the while loop. + */ + if (unlikely(now - omap_32k_last_tick == ~0)) + break; + + modulo_count += OMAP_32K_TICK_MODULO; + if (modulo_count > HZ) { + ++omap_32k_last_tick; + modulo_count -= HZ; + } +#endif + omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ; + timer_tick(regs); + } + + /* Restart timer so we don't drift off due to modulo or dynamic tick. + * By default we program the next timer to be continuous to avoid + * latencies during high system load. During dynamic tick operation the + * continuous timer can be overridden from pm_idle to be longer. + */ + omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now); + write_sequnlock_irqrestore(&xtime_lock, flags); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NO_IDLE_HZ +/* + * Programs the next timer interrupt needed. Called when dynamic tick is + * enabled, and to reprogram the ticks to skip from pm_idle. Note that + * we can keep the timer continuous, and don't need to set it to run in + * one-shot mode. This is because the timer will get reprogrammed again + * after next interrupt. + */ +void omap_32k_timer_reprogram(unsigned long next_tick) +{ + omap_32k_timer_start(JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1); +} + +static struct irqaction omap_32k_timer_irq; +extern struct timer_update_handler timer_update; + +static int omap_32k_timer_enable_dyn_tick(void) +{ + /* No need to reprogram timer, just use the next interrupt */ + return 0; +} + +static int omap_32k_timer_disable_dyn_tick(void) +{ + omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); + return 0; +} + +static struct dyn_tick_timer omap_dyn_tick_timer = { + .enable = omap_32k_timer_enable_dyn_tick, + .disable = omap_32k_timer_disable_dyn_tick, + .reprogram = omap_32k_timer_reprogram, + .handler = omap_32k_timer_interrupt, +}; +#endif /* CONFIG_NO_IDLE_HZ */ + +static struct irqaction omap_32k_timer_irq = { + .name = "32KHz timer", + .flags = SA_INTERRUPT | SA_TIMER, + .handler = omap_32k_timer_interrupt, +}; + +static __init void omap_init_32k_timer(void) +{ + +#ifdef CONFIG_NO_IDLE_HZ + omap_timer.dyn_tick = &omap_dyn_tick_timer; +#endif + + setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); + omap_timer.offset = omap_32k_timer_gettimeoffset; + omap_32k_last_tick = omap_32k_sync_timer_read(); + omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); +} +#endif /* CONFIG_OMAP_32K_TIMER */ + +/* + * --------------------------------------------------------------------------- + * Timer initialization + * --------------------------------------------------------------------------- + */ +static void __init omap_timer_init(void) +{ +#if defined(CONFIG_OMAP_MPU_TIMER) + omap_init_mpu_timer(); +#elif defined(CONFIG_OMAP_32K_TIMER) + omap_init_32k_timer(); +#else +#error No system timer selected in Kconfig! +#endif +} + +struct sys_timer omap_timer = { + .init = omap_timer_init, + .offset = NULL, /* Initialized later */ +}; --- linux-2.6.13-rc2/arch/arm/mach-omap/board-generic.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,100 +0,0 @@ -/* - * linux/arch/arm/mach-omap/board-generic.c - * - * Modified from board-innovator1510.c - * - * Code for generic OMAP board. Should work on many OMAP systems where - * the device drivers take care of all the necessary hardware initialization. - * Do not put any board specific code to this file; create a new machine - * type if you need custom low-level initializations. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "common.h" - -static int __initdata generic_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; - -static void __init omap_generic_init_irq(void) -{ - omap_init_irq(); -} - -/* assume no Mini-AB port */ - -#ifdef CONFIG_ARCH_OMAP1510 -static struct omap_usb_config generic1510_usb_config __initdata = { - .register_host = 1, - .register_dev = 1, - .hmc_mode = 16, - .pins[0] = 3, -}; -#endif - -#if defined(CONFIG_ARCH_OMAP16XX) -static struct omap_usb_config generic1610_usb_config __initdata = { - .register_host = 1, - .register_dev = 1, - .hmc_mode = 16, - .pins[0] = 6, -}; -#endif - -static struct omap_board_config_kernel generic_config[] = { - { OMAP_TAG_USB, NULL }, -}; - -static void __init omap_generic_init(void) -{ - /* - * Make sure the serial ports are muxed on at this point. - * You have to mux them off in device drivers later on - * if not needed. - */ -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - generic_config[0].data = &generic1510_usb_config; - } -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (!cpu_is_omap1510()) { - generic_config[0].data = &generic1610_usb_config; - } -#endif - omap_board_config = generic_config; - omap_board_config_size = ARRAY_SIZE(generic_config); - omap_serial_init(generic_serial_ports); -} - -static void __init omap_generic_map_io(void) -{ - omap_map_io(); -} - -MACHINE_START(OMAP_GENERIC, "Generic OMAP1510/1610/1710") - /* Maintainer: Tony Lindgren */ - .phys_ram = 0x10000000, - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = omap_generic_map_io, - .init_irq = omap_generic_init_irq, - .init_machine = omap_generic_init, - .timer = &omap_timer, -MACHINE_END --- linux-2.6.13-rc2/arch/arm/mach-omap/board-h2.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,189 +0,0 @@ -/* - * linux/arch/arm/mach-omap/board-h2.c - * - * Board specific inits for OMAP-1610 H2 - * - * Copyright (C) 2001 RidgeRun, Inc. - * Author: Greg Lonnon - * - * Copyright (C) 2002 MontaVista Software, Inc. - * - * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 - * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen - * - * H2 specific changes and cleanup - * Copyright (C) 2004 Nokia Corporation by Imre Deak - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "common.h" - -extern int omap_gpio_init(void); - -static int __initdata h2_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; - -static struct mtd_partition h2_partitions[] = { - /* bootloader (U-Boot, etc) in first sector */ - { - .name = "bootloader", - .offset = 0, - .size = SZ_128K, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, - /* bootloader params in the next sector */ - { - .name = "params", - .offset = MTDPART_OFS_APPEND, - .size = SZ_128K, - .mask_flags = 0, - }, - /* kernel */ - { - .name = "kernel", - .offset = MTDPART_OFS_APPEND, - .size = SZ_2M, - .mask_flags = 0 - }, - /* file system */ - { - .name = "filesystem", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL, - .mask_flags = 0 - } -}; - -static struct flash_platform_data h2_flash_data = { - .map_name = "cfi_probe", - .width = 2, - .parts = h2_partitions, - .nr_parts = ARRAY_SIZE(h2_partitions), -}; - -static struct resource h2_flash_resource = { - .start = OMAP_CS2B_PHYS, - .end = OMAP_CS2B_PHYS + OMAP_CS2B_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device h2_flash_device = { - .name = "omapflash", - .id = 0, - .dev = { - .platform_data = &h2_flash_data, - }, - .num_resources = 1, - .resource = &h2_flash_resource, -}; - -static struct resource h2_smc91x_resources[] = { - [0] = { - .start = OMAP1610_ETHR_START, /* Physical */ - .end = OMAP1610_ETHR_START + 0xf, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = OMAP_GPIO_IRQ(0), - .end = OMAP_GPIO_IRQ(0), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device h2_smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(h2_smc91x_resources), - .resource = h2_smc91x_resources, -}; - -static struct platform_device *h2_devices[] __initdata = { - &h2_flash_device, - &h2_smc91x_device, -}; - -static void __init h2_init_smc91x(void) -{ - if ((omap_request_gpio(0)) < 0) { - printk("Error requesting gpio 0 for smc91x irq\n"); - return; - } - omap_set_gpio_edge_ctrl(0, OMAP_GPIO_FALLING_EDGE); -} - -void h2_init_irq(void) -{ - omap_init_irq(); - omap_gpio_init(); - h2_init_smc91x(); -} - -static struct omap_usb_config h2_usb_config __initdata = { - /* usb1 has a Mini-AB port and external isp1301 transceiver */ - .otg = 2, - -#ifdef CONFIG_USB_GADGET_OMAP - .hmc_mode = 19, // 0:host(off) 1:dev|otg 2:disabled - // .hmc_mode = 21, // 0:host(off) 1:dev(loopback) 2:host(loopback) -#elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) - /* needs OTG cable, or NONSTANDARD (B-to-MiniB) */ - .hmc_mode = 20, // 1:dev|otg(off) 1:host 2:disabled -#endif - - .pins[1] = 3, -}; - -static struct omap_mmc_config h2_mmc_config __initdata = { - .mmc_blocks = 1, - .mmc1_power_pin = -1, /* tps65010 gpio3 */ - .mmc1_switch_pin = OMAP_MPUIO(1), -}; - -static struct omap_board_config_kernel h2_config[] = { - { OMAP_TAG_USB, &h2_usb_config }, - { OMAP_TAG_MMC, &h2_mmc_config }, -}; - -static void __init h2_init(void) -{ - platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices)); - omap_board_config = h2_config; - omap_board_config_size = ARRAY_SIZE(h2_config); -} - -static void __init h2_map_io(void) -{ - omap_map_io(); - omap_serial_init(h2_serial_ports); -} - -MACHINE_START(OMAP_H2, "TI-H2") - /* Maintainer: Imre Deak */ - .phys_ram = 0x10000000, - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = h2_map_io, - .init_irq = h2_init_irq, - .init_machine = h2_init, - .timer = &omap_timer, -MACHINE_END --- linux-2.6.13-rc2/arch/arm/mach-omap/board-h3.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,207 +0,0 @@ -/* - * linux/arch/arm/mach-omap/board-h3.c - * - * This file contains OMAP1710 H3 specific code. - * - * Copyright (C) 2004 Texas Instruments, Inc. - * Copyright (C) 2002 MontaVista Software, Inc. - * Copyright (C) 2001 RidgeRun, Inc. - * Author: RidgeRun, Inc. - * Greg Lonnon (glonnon@ridgerun.com) or info@ridgerun.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "common.h" - -extern int omap_gpio_init(void); - -static int __initdata h3_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; - -static struct mtd_partition h3_partitions[] = { - /* bootloader (U-Boot, etc) in first sector */ - { - .name = "bootloader", - .offset = 0, - .size = SZ_128K, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, - /* bootloader params in the next sector */ - { - .name = "params", - .offset = MTDPART_OFS_APPEND, - .size = SZ_128K, - .mask_flags = 0, - }, - /* kernel */ - { - .name = "kernel", - .offset = MTDPART_OFS_APPEND, - .size = SZ_2M, - .mask_flags = 0 - }, - /* file system */ - { - .name = "filesystem", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL, - .mask_flags = 0 - } -}; - -static struct flash_platform_data h3_flash_data = { - .map_name = "cfi_probe", - .width = 2, - .parts = h3_partitions, - .nr_parts = ARRAY_SIZE(h3_partitions), -}; - -static struct resource h3_flash_resource = { - .start = OMAP_CS2B_PHYS, - .end = OMAP_CS2B_PHYS + OMAP_CS2B_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device flash_device = { - .name = "omapflash", - .id = 0, - .dev = { - .platform_data = &h3_flash_data, - }, - .num_resources = 1, - .resource = &h3_flash_resource, -}; - -static struct resource smc91x_resources[] = { - [0] = { - .start = OMAP1710_ETHR_START, /* Physical */ - .end = OMAP1710_ETHR_START + 0xf, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = OMAP_GPIO_IRQ(40), - .end = OMAP_GPIO_IRQ(40), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(smc91x_resources), - .resource = smc91x_resources, -}; - -#define GPTIMER_BASE 0xFFFB1400 -#define GPTIMER_REGS(x) (0xFFFB1400 + (x * 0x800)) -#define GPTIMER_REGS_SIZE 0x46 - -static struct resource intlat_resources[] = { - [0] = { - .start = GPTIMER_REGS(0), /* Physical */ - .end = GPTIMER_REGS(0) + GPTIMER_REGS_SIZE, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = INT_1610_GPTIMER1, - .end = INT_1610_GPTIMER1, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device intlat_device = { - .name = "omap_intlat", - .id = 0, - .num_resources = ARRAY_SIZE(intlat_resources), - .resource = intlat_resources, -}; - -static struct platform_device *devices[] __initdata = { - &flash_device, - &smc91x_device, - &intlat_device, -}; - -static struct omap_usb_config h3_usb_config __initdata = { - /* usb1 has a Mini-AB port and external isp1301 transceiver */ - .otg = 2, - -#ifdef CONFIG_USB_GADGET_OMAP - .hmc_mode = 19, /* 0:host(off) 1:dev|otg 2:disabled */ -#elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) - /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ - .hmc_mode = 20, /* 1:dev|otg(off) 1:host 2:disabled */ -#endif - - .pins[1] = 3, -}; - -static struct omap_board_config_kernel h3_config[] = { - { OMAP_TAG_USB, &h3_usb_config }, -}; - -static void __init h3_init(void) -{ - (void) platform_add_devices(devices, ARRAY_SIZE(devices)); -} - -static void __init h3_init_smc91x(void) -{ - omap_cfg_reg(W15_1710_GPIO40); - if (omap_request_gpio(40) < 0) { - printk("Error requesting gpio 40 for smc91x irq\n"); - return; - } - omap_set_gpio_edge_ctrl(40, OMAP_GPIO_FALLING_EDGE); -} - -void h3_init_irq(void) -{ - omap_init_irq(); - omap_gpio_init(); - h3_init_smc91x(); -} - -static void __init h3_map_io(void) -{ - omap_map_io(); - omap_serial_init(h3_serial_ports); -} - -MACHINE_START(OMAP_H3, "TI OMAP1710 H3 board") - /* Maintainer: Texas Instruments, Inc. */ - .phys_ram = 0x10000000, - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = h3_map_io, - .init_irq = h3_init_irq, - .init_machine = h3_init, - .timer = &omap_timer, -MACHINE_END --- linux-2.6.13-rc2/arch/arm/mach-omap/board-innovator.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,282 +0,0 @@ -/* - * linux/arch/arm/mach-omap/board-innovator.c - * - * Board specific inits for OMAP-1510 and OMAP-1610 Innovator - * - * Copyright (C) 2001 RidgeRun, Inc. - * Author: Greg Lonnon - * - * Copyright (C) 2002 MontaVista Software, Inc. - * - * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 - * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "common.h" - -static int __initdata innovator_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; - -static struct mtd_partition innovator_partitions[] = { - /* bootloader (U-Boot, etc) in first sector */ - { - .name = "bootloader", - .offset = 0, - .size = SZ_128K, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, - /* bootloader params in the next sector */ - { - .name = "params", - .offset = MTDPART_OFS_APPEND, - .size = SZ_128K, - .mask_flags = 0, - }, - /* kernel */ - { - .name = "kernel", - .offset = MTDPART_OFS_APPEND, - .size = SZ_2M, - .mask_flags = 0 - }, - /* rest of flash1 is a file system */ - { - .name = "rootfs", - .offset = MTDPART_OFS_APPEND, - .size = SZ_16M - SZ_2M - 2 * SZ_128K, - .mask_flags = 0 - }, - /* file system */ - { - .name = "filesystem", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL, - .mask_flags = 0 - } -}; - -static struct flash_platform_data innovator_flash_data = { - .map_name = "cfi_probe", - .width = 2, - .parts = innovator_partitions, - .nr_parts = ARRAY_SIZE(innovator_partitions), -}; - -static struct resource innovator_flash_resource = { - .start = OMAP_CS0_PHYS, - .end = OMAP_CS0_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device innovator_flash_device = { - .name = "omapflash", - .id = 0, - .dev = { - .platform_data = &innovator_flash_data, - }, - .num_resources = 1, - .resource = &innovator_flash_resource, -}; - -#ifdef CONFIG_ARCH_OMAP1510 - -/* Only FPGA needs to be mapped here. All others are done with ioremap */ -static struct map_desc innovator1510_io_desc[] __initdata = { -{ OMAP1510_FPGA_BASE, OMAP1510_FPGA_START, OMAP1510_FPGA_SIZE, - MT_DEVICE }, -}; - -static struct resource innovator1510_smc91x_resources[] = { - [0] = { - .start = OMAP1510_FPGA_ETHR_START, /* Physical */ - .end = OMAP1510_FPGA_ETHR_START + 0xf, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = OMAP1510_INT_ETHER, - .end = OMAP1510_INT_ETHER, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device innovator1510_smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(innovator1510_smc91x_resources), - .resource = innovator1510_smc91x_resources, -}; - -static struct platform_device *innovator1510_devices[] __initdata = { - &innovator_flash_device, - &innovator1510_smc91x_device, -}; - -#endif /* CONFIG_ARCH_OMAP1510 */ - -#ifdef CONFIG_ARCH_OMAP16XX - -static struct resource innovator1610_smc91x_resources[] = { - [0] = { - .start = INNOVATOR1610_ETHR_START, /* Physical */ - .end = INNOVATOR1610_ETHR_START + 0xf, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = OMAP_GPIO_IRQ(0), - .end = OMAP_GPIO_IRQ(0), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device innovator1610_smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(innovator1610_smc91x_resources), - .resource = innovator1610_smc91x_resources, -}; - -static struct platform_device *innovator1610_devices[] __initdata = { - &innovator_flash_device, - &innovator1610_smc91x_device, -}; - -#endif /* CONFIG_ARCH_OMAP16XX */ - -static void __init innovator_init_smc91x(void) -{ - if (cpu_is_omap1510()) { - fpga_write(fpga_read(OMAP1510_FPGA_RST) & ~1, - OMAP1510_FPGA_RST); - udelay(750); - } else { - if ((omap_request_gpio(0)) < 0) { - printk("Error requesting gpio 0 for smc91x irq\n"); - return; - } - omap_set_gpio_edge_ctrl(0, OMAP_GPIO_RISING_EDGE); - } -} - -void innovator_init_irq(void) -{ - omap_init_irq(); - omap_gpio_init(); -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - omap1510_fpga_init_irq(); - } -#endif - innovator_init_smc91x(); -} - -#ifdef CONFIG_ARCH_OMAP1510 -static struct omap_usb_config innovator1510_usb_config __initdata = { - /* for bundled non-standard host and peripheral cables */ - .hmc_mode = 4, - - .register_host = 1, - .pins[1] = 6, - .pins[2] = 6, /* Conflicts with UART2 */ - - .register_dev = 1, - .pins[0] = 2, -}; -#endif - -#ifdef CONFIG_ARCH_OMAP16XX -static struct omap_usb_config h2_usb_config __initdata = { - /* usb1 has a Mini-AB port and external isp1301 transceiver */ - .otg = 2, - -#ifdef CONFIG_USB_GADGET_OMAP - .hmc_mode = 19, // 0:host(off) 1:dev|otg 2:disabled - // .hmc_mode = 21, // 0:host(off) 1:dev(loopback) 2:host(loopback) -#elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) - /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ - .hmc_mode = 20, // 1:dev|otg(off) 1:host 2:disabled -#endif - - .pins[1] = 3, -}; -#endif - -static struct omap_board_config_kernel innovator_config[] = { - { OMAP_TAG_USB, NULL }, -}; - -static void __init innovator_init(void) -{ -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - platform_add_devices(innovator1510_devices, ARRAY_SIZE(innovator1510_devices)); - } -#endif -#ifdef CONFIG_ARCH_OMAP16XX - if (!cpu_is_omap1510()) { - platform_add_devices(innovator1610_devices, ARRAY_SIZE(innovator1610_devices)); - } -#endif - -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) - innovator_config[0].data = &innovator1510_usb_config; -#endif -#ifdef CONFIG_ARCH_OMAP16XX - if (cpu_is_omap1610()) - innovator_config[0].data = &h2_usb_config; -#endif - omap_board_config = innovator_config; - omap_board_config_size = ARRAY_SIZE(innovator_config); -} - -static void __init innovator_map_io(void) -{ - omap_map_io(); - -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - iotable_init(innovator1510_io_desc, ARRAY_SIZE(innovator1510_io_desc)); - udelay(10); /* Delay needed for FPGA */ - - /* Dump the Innovator FPGA rev early - useful info for support. */ - printk("Innovator FPGA Rev %d.%d Board Rev %d\n", - fpga_read(OMAP1510_FPGA_REV_HIGH), - fpga_read(OMAP1510_FPGA_REV_LOW), - fpga_read(OMAP1510_FPGA_BOARD_REV)); - } -#endif - omap_serial_init(innovator_serial_ports); -} - -MACHINE_START(OMAP_INNOVATOR, "TI-Innovator") - /* Maintainer: MontaVista Software, Inc. */ - .phys_ram = 0x10000000, - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = innovator_map_io, - .init_irq = innovator_init_irq, - .init_machine = innovator_init, - .timer = &omap_timer, -MACHINE_END --- linux-2.6.13-rc2/arch/arm/mach-omap/board-netstar.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,153 +0,0 @@ -/* - * Modified from board-generic.c - * - * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl - * - * Code for Netstar OMAP board. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include "common.h" - -extern void __init omap_init_time(void); -extern int omap_gpio_init(void); - -static struct resource netstar_smc91x_resources[] = { - [0] = { - .start = OMAP_CS1_PHYS + 0x300, - .end = OMAP_CS1_PHYS + 0x300 + 16, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = OMAP_GPIO_IRQ(8), - .end = OMAP_GPIO_IRQ(8), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device netstar_smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(netstar_smc91x_resources), - .resource = netstar_smc91x_resources, -}; - -static struct platform_device *netstar_devices[] __initdata = { - &netstar_smc91x_device, -}; - -static void __init netstar_init_irq(void) -{ - omap_init_irq(); - omap_gpio_init(); -} - -static void __init netstar_init(void) -{ - /* green LED */ - omap_request_gpio(4); - omap_set_gpio_direction(4, 0); - /* smc91x reset */ - omap_request_gpio(7); - omap_set_gpio_direction(7, 0); - omap_set_gpio_dataout(7, 1); - udelay(2); /* wait at least 100ns */ - omap_set_gpio_dataout(7, 0); - mdelay(50); /* 50ms until PHY ready */ - /* smc91x interrupt pin */ - omap_request_gpio(8); - omap_set_gpio_edge_ctrl(8, OMAP_GPIO_RISING_EDGE); - - omap_request_gpio(12); - omap_request_gpio(13); - omap_request_gpio(14); - omap_request_gpio(15); - omap_set_gpio_edge_ctrl(12, OMAP_GPIO_FALLING_EDGE); - omap_set_gpio_edge_ctrl(13, OMAP_GPIO_FALLING_EDGE); - omap_set_gpio_edge_ctrl(14, OMAP_GPIO_FALLING_EDGE); - omap_set_gpio_edge_ctrl(15, OMAP_GPIO_FALLING_EDGE); - - platform_add_devices(netstar_devices, ARRAY_SIZE(netstar_devices)); - - /* Switch on green LED */ - omap_set_gpio_dataout(4, 0); - /* Switch off red LED */ - omap_writeb(0x00, OMAP_LPG1_PMR); /* Disable clock */ - omap_writeb(0x80, OMAP_LPG1_LCR); -} - -static int __initdata omap_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; - -static void __init netstar_map_io(void) -{ - omap_map_io(); - omap_serial_init(omap_serial_ports); -} - -#define MACHINE_PANICED 1 -#define MACHINE_REBOOTING 2 -#define MACHINE_REBOOT 4 -static unsigned long machine_state; - -static int panic_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - if (test_and_set_bit(MACHINE_PANICED, &machine_state)) - return NOTIFY_DONE; - - /* Switch off green LED */ - omap_set_gpio_dataout(4, 1); - /* Flash red LED */ - omap_writeb(0x78, OMAP_LPG1_LCR); - omap_writeb(0x01, OMAP_LPG1_PMR); /* Enable clock */ - - return NOTIFY_DONE; -} - -static struct notifier_block panic_block = { - .notifier_call = panic_event, -}; - -static int __init netstar_late_init(void) -{ - /* TODO: Setup front panel switch here */ - - /* Setup panic notifier */ - notifier_chain_register(&panic_notifier_list, &panic_block); - - return 0; -} - -postcore_initcall(netstar_late_init); - -MACHINE_START(NETSTAR, "NetStar OMAP5910") - /* Maintainer: Ladislav Michl */ - .phys_ram = 0x10000000, - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = netstar_map_io, - .init_irq = netstar_init_irq, - .init_machine = netstar_init, - .timer = &omap_timer, -MACHINE_END --- linux-2.6.13-rc2/arch/arm/mach-omap/board-osk.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,171 +0,0 @@ -/* - * linux/arch/arm/mach-omap/board-osk.c - * - * Board specific init for OMAP5912 OSK - * - * Written by Dirk Behme - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "common.h" - -static struct map_desc osk5912_io_desc[] __initdata = { -{ OMAP_OSK_NOR_FLASH_BASE, OMAP_OSK_NOR_FLASH_START, OMAP_OSK_NOR_FLASH_SIZE, - MT_DEVICE }, -}; - -static int __initdata osk_serial_ports[OMAP_MAX_NR_PORTS] = {1, 0, 0}; - -static struct resource osk5912_smc91x_resources[] = { - [0] = { - .start = OMAP_OSK_ETHR_START, /* Physical */ - .end = OMAP_OSK_ETHR_START + 0xf, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = OMAP_GPIO_IRQ(0), - .end = OMAP_GPIO_IRQ(0), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device osk5912_smc91x_device = { - .name = "smc91x", - .id = -1, - .num_resources = ARRAY_SIZE(osk5912_smc91x_resources), - .resource = osk5912_smc91x_resources, -}; - -static struct resource osk5912_cf_resources[] = { - [0] = { - .start = OMAP_GPIO_IRQ(62), - .end = OMAP_GPIO_IRQ(62), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device osk5912_cf_device = { - .name = "omap_cf", - .id = -1, - .dev = { - .platform_data = (void *) 2 /* CS2 */, - }, - .num_resources = ARRAY_SIZE(osk5912_cf_resources), - .resource = osk5912_cf_resources, -}; - -static struct platform_device *osk5912_devices[] __initdata = { - &osk5912_smc91x_device, - &osk5912_cf_device, -}; - -static void __init osk_init_smc91x(void) -{ - if ((omap_request_gpio(0)) < 0) { - printk("Error requesting gpio 0 for smc91x irq\n"); - return; - } - omap_set_gpio_edge_ctrl(0, OMAP_GPIO_RISING_EDGE); - - /* Check EMIFS wait states to fix errors with SMC_GET_PKT_HDR */ - EMIFS_CCS(1) |= 0x2; -} - -static void __init osk_init_cf(void) -{ - omap_cfg_reg(M7_1610_GPIO62); - if ((omap_request_gpio(62)) < 0) { - printk("Error requesting gpio 62 for CF irq\n"); - return; - } - /* it's really active-low */ - omap_set_gpio_edge_ctrl(62, OMAP_GPIO_FALLING_EDGE); -} - -void osk_init_irq(void) -{ - omap_init_irq(); - omap_gpio_init(); - osk_init_smc91x(); - osk_init_cf(); -} - -static struct omap_usb_config osk_usb_config __initdata = { - /* has usb host connector (A) ... for development it can also - * be used, with a NONSTANDARD gender-bending cable/dongle, as - * a peripheral. - */ -#ifdef CONFIG_USB_GADGET_OMAP - .register_dev = 1, - .hmc_mode = 0, -#else - .register_host = 1, - .hmc_mode = 16, - .rwc = 1, -#endif - .pins[0] = 2, -}; - -static struct omap_board_config_kernel osk_config[] = { - { OMAP_TAG_USB, &osk_usb_config }, -}; - -static void __init osk_init(void) -{ - platform_add_devices(osk5912_devices, ARRAY_SIZE(osk5912_devices)); - omap_board_config = osk_config; - omap_board_config_size = ARRAY_SIZE(osk_config); - USB_TRANSCEIVER_CTRL_REG |= (3 << 1); -} - -static void __init osk_map_io(void) -{ - omap_map_io(); - iotable_init(osk5912_io_desc, ARRAY_SIZE(osk5912_io_desc)); - omap_serial_init(osk_serial_ports); -} - -MACHINE_START(OMAP_OSK, "TI-OSK") - /* Maintainer: Dirk Behme */ - .phys_ram = 0x10000000, - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = osk_map_io, - .init_irq = osk_init_irq, - .init_machine = osk_init, - .timer = &omap_timer, -MACHINE_END --- linux-2.6.13-rc2/arch/arm/mach-omap/board-perseus2.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,191 +0,0 @@ -/* - * linux/arch/arm/mach-omap/board-perseus2.c - * - * Modified from board-generic.c - * - * Original OMAP730 support by Jean Pihet - * Updated for 2.6 by Kevin Hilman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "common.h" - -static struct resource smc91x_resources[] = { - [0] = { - .start = H2P2_DBG_FPGA_ETHR_START, /* Physical */ - .end = H2P2_DBG_FPGA_ETHR_START + 0xf, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = INT_730_MPU_EXT_NIRQ, - .end = 0, - .flags = IORESOURCE_IRQ, - }, -}; - -static int __initdata p2_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 0}; - -static struct mtd_partition p2_partitions[] = { - /* bootloader (U-Boot, etc) in first sector */ - { - .name = "bootloader", - .offset = 0, - .size = SZ_128K, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, - /* bootloader params in the next sector */ - { - .name = "params", - .offset = MTDPART_OFS_APPEND, - .size = SZ_128K, - .mask_flags = 0, - }, - /* kernel */ - { - .name = "kernel", - .offset = MTDPART_OFS_APPEND, - .size = SZ_2M, - .mask_flags = 0 - }, - /* rest of flash is a file system */ - { - .name = "rootfs", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL, - .mask_flags = 0 - }, -}; - -static struct flash_platform_data p2_flash_data = { - .map_name = "cfi_probe", - .width = 2, - .parts = p2_partitions, - .nr_parts = ARRAY_SIZE(p2_partitions), -}; - -static struct resource p2_flash_resource = { - .start = OMAP_FLASH_0_START, - .end = OMAP_FLASH_0_START + OMAP_FLASH_0_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device p2_flash_device = { - .name = "omapflash", - .id = 0, - .dev = { - .platform_data = &p2_flash_data, - }, - .num_resources = 1, - .resource = &p2_flash_resource, -}; - -static struct platform_device smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(smc91x_resources), - .resource = smc91x_resources, -}; - -static struct platform_device *devices[] __initdata = { - &p2_flash_device, - &smc91x_device, -}; - -static void __init omap_perseus2_init(void) -{ - (void) platform_add_devices(devices, ARRAY_SIZE(devices)); -} - -static void __init perseus2_init_smc91x(void) -{ - fpga_write(1, H2P2_DBG_FPGA_LAN_RESET); - mdelay(50); - fpga_write(fpga_read(H2P2_DBG_FPGA_LAN_RESET) & ~1, - H2P2_DBG_FPGA_LAN_RESET); - mdelay(50); -} - -void omap_perseus2_init_irq(void) -{ - omap_init_irq(); - omap_gpio_init(); - perseus2_init_smc91x(); -} - -/* Only FPGA needs to be mapped here. All others are done with ioremap */ -static struct map_desc omap_perseus2_io_desc[] __initdata = { - {H2P2_DBG_FPGA_BASE, H2P2_DBG_FPGA_START, H2P2_DBG_FPGA_SIZE, - MT_DEVICE}, -}; - -static void __init omap_perseus2_map_io(void) -{ - omap_map_io(); - iotable_init(omap_perseus2_io_desc, - ARRAY_SIZE(omap_perseus2_io_desc)); - - /* Early, board-dependent init */ - - /* - * Hold GSM Reset until needed - */ - omap_writew(omap_readw(OMAP730_DSP_M_CTL) & ~1, OMAP730_DSP_M_CTL); - - /* - * UARTs -> done automagically by 8250 driver - */ - - /* - * CSx timings, GPIO Mux ... setup - */ - - /* Flash: CS0 timings setup */ - omap_writel(0x0000fff3, OMAP730_FLASH_CFG_0); - omap_writel(0x00000088, OMAP730_FLASH_ACFG_0); - - /* - * Ethernet support trough the debug board - * CS1 timings setup - */ - omap_writel(0x0000fff3, OMAP730_FLASH_CFG_1); - omap_writel(0x00000000, OMAP730_FLASH_ACFG_1); - - /* - * Configure MPU_EXT_NIRQ IO in IO_CONF9 register, - * It is used as the Ethernet controller interrupt - */ - omap_writel(omap_readl(OMAP730_IO_CONF_9) & 0x1FFFFFFF, OMAP730_IO_CONF_9); - omap_serial_init(p2_serial_ports); -} - -MACHINE_START(OMAP_PERSEUS2, "OMAP730 Perseus2") - /* Maintainer: Kevin Hilman */ - .phys_ram = 0x10000000, - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = omap_perseus2_map_io, - .init_irq = omap_perseus2_init_irq, - .init_machine = omap_perseus2_init, - .timer = &omap_timer, -MACHINE_END --- linux-2.6.13-rc2/arch/arm/mach-omap/board-voiceblue.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,258 +0,0 @@ -/* - * linux/arch/arm/mach-omap/board-voiceblue.c - * - * Modified from board-generic.c - * - * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl - * - * Code for OMAP5910 based VoiceBlue board (VoIP to GSM gateway). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "common.h" - -extern void omap_init_time(void); -extern int omap_gpio_init(void); - -static struct plat_serial8250_port voiceblue_ports[] = { - { - .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x40000), - .irq = OMAP_GPIO_IRQ(12), - .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, - .iotype = UPIO_MEM, - .regshift = 1, - .uartclk = 3686400, - }, - { - .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x50000), - .irq = OMAP_GPIO_IRQ(13), - .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, - .iotype = UPIO_MEM, - .regshift = 1, - .uartclk = 3686400, - }, - { - .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x60000), - .irq = OMAP_GPIO_IRQ(14), - .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, - .iotype = UPIO_MEM, - .regshift = 1, - .uartclk = 3686400, - }, - { - .mapbase = (unsigned long)(OMAP_CS1_PHYS + 0x70000), - .irq = OMAP_GPIO_IRQ(15), - .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP, - .iotype = UPIO_MEM, - .regshift = 1, - .uartclk = 3686400, - }, - { }, -}; - -static struct platform_device serial_device = { - .name = "serial8250", - .id = 1, - .dev = { - .platform_data = voiceblue_ports, - }, -}; - -static int __init ext_uart_init(void) -{ - return platform_device_register(&serial_device); -} -arch_initcall(ext_uart_init); - -static struct resource voiceblue_smc91x_resources[] = { - [0] = { - .start = OMAP_CS2_PHYS + 0x300, - .end = OMAP_CS2_PHYS + 0x300 + 16, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = OMAP_GPIO_IRQ(8), - .end = OMAP_GPIO_IRQ(8), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device voiceblue_smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(voiceblue_smc91x_resources), - .resource = voiceblue_smc91x_resources, -}; - -static struct platform_device *voiceblue_devices[] __initdata = { - &voiceblue_smc91x_device, -}; - -static struct omap_usb_config voiceblue_usb_config __initdata = { - .hmc_mode = 3, - .register_host = 1, - .register_dev = 1, - .pins[0] = 2, - .pins[1] = 6, - .pins[2] = 6, -}; - -static struct omap_board_config_kernel voiceblue_config[] = { - { OMAP_TAG_USB, &voiceblue_usb_config }, -}; - -static void __init voiceblue_init_irq(void) -{ - omap_init_irq(); - omap_gpio_init(); -} - -static void __init voiceblue_init(void) -{ - /* There is a good chance board is going up, so enable Power LED - * (it is connected through invertor) */ - omap_writeb(0x00, OMAP_LPG1_LCR); - /* Watchdog */ - omap_request_gpio(0); - /* smc91x reset */ - omap_request_gpio(7); - omap_set_gpio_direction(7, 0); - omap_set_gpio_dataout(7, 1); - udelay(2); /* wait at least 100ns */ - omap_set_gpio_dataout(7, 0); - mdelay(50); /* 50ms until PHY ready */ - /* smc91x interrupt pin */ - omap_request_gpio(8); - omap_set_gpio_edge_ctrl(8, OMAP_GPIO_RISING_EDGE); - /* 16C554 reset*/ - omap_request_gpio(6); - omap_set_gpio_direction(6, 0); - omap_set_gpio_dataout(6, 0); - /* 16C554 interrupt pins */ - omap_request_gpio(12); - omap_request_gpio(13); - omap_request_gpio(14); - omap_request_gpio(15); - omap_set_gpio_edge_ctrl(12, OMAP_GPIO_RISING_EDGE); - omap_set_gpio_edge_ctrl(13, OMAP_GPIO_RISING_EDGE); - omap_set_gpio_edge_ctrl(14, OMAP_GPIO_RISING_EDGE); - omap_set_gpio_edge_ctrl(15, OMAP_GPIO_RISING_EDGE); - - platform_add_devices(voiceblue_devices, ARRAY_SIZE(voiceblue_devices)); - omap_board_config = voiceblue_config; - omap_board_config_size = ARRAY_SIZE(voiceblue_config); -} - -static int __initdata omap_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1}; - -static void __init voiceblue_map_io(void) -{ - omap_map_io(); - omap_serial_init(omap_serial_ports); -} - -#define MACHINE_PANICED 1 -#define MACHINE_REBOOTING 2 -#define MACHINE_REBOOT 4 -static unsigned long machine_state; - -static int panic_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - if (test_and_set_bit(MACHINE_PANICED, &machine_state)) - return NOTIFY_DONE; - - /* Flash Power LED - * (TODO: Enable clock right way (enabled in bootloader already)) */ - omap_writeb(0x78, OMAP_LPG1_LCR); - - return NOTIFY_DONE; -} - -static struct notifier_block panic_block = { - .notifier_call = panic_event, -}; - -static int __init setup_notifier(void) -{ - /* Setup panic notifier */ - notifier_chain_register(&panic_notifier_list, &panic_block); - - return 0; -} - -postcore_initcall(setup_notifier); - -static int wdt_gpio_state; - -void voiceblue_wdt_enable(void) -{ - omap_set_gpio_direction(0, 0); - omap_set_gpio_dataout(0, 0); - omap_set_gpio_dataout(0, 1); - omap_set_gpio_dataout(0, 0); - wdt_gpio_state = 0; -} - -void voiceblue_wdt_disable(void) -{ - omap_set_gpio_dataout(0, 0); - omap_set_gpio_dataout(0, 1); - omap_set_gpio_dataout(0, 0); - omap_set_gpio_direction(0, 1); -} - -void voiceblue_wdt_ping(void) -{ - if (test_bit(MACHINE_REBOOT, &machine_state)) - return; - - wdt_gpio_state = !wdt_gpio_state; - omap_set_gpio_dataout(0, wdt_gpio_state); -} - -void voiceblue_reset(void) -{ - set_bit(MACHINE_REBOOT, &machine_state); - voiceblue_wdt_enable(); - while (1) ; -} - -EXPORT_SYMBOL(voiceblue_wdt_enable); -EXPORT_SYMBOL(voiceblue_wdt_disable); -EXPORT_SYMBOL(voiceblue_wdt_ping); - -MACHINE_START(VOICEBLUE, "VoiceBlue OMAP5910") - /* Maintainer: Ladislav Michl */ - .phys_ram = 0x10000000, - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = voiceblue_map_io, - .init_irq = voiceblue_init_irq, - .init_machine = voiceblue_init, - .timer = &omap_timer, -MACHINE_END --- linux-2.6.13-rc2/arch/arm/mach-omap/clock.c 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,1076 +0,0 @@ -/* - * linux/arch/arm/mach-omap/clock.c - * - * Copyright (C) 2004 Nokia corporation - * Written by Tuukka Tikkanen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "clock.h" - -static LIST_HEAD(clocks); -static DECLARE_MUTEX(clocks_sem); -static DEFINE_SPINLOCK(clockfw_lock); -static void propagate_rate(struct clk * clk); -/* External clock (MCLK & BCLK) functions */ -static int set_ext_clk_rate(struct clk * clk, unsigned long rate); -static long round_ext_clk_rate(struct clk * clk, unsigned long rate); -static void init_ext_clk(struct clk * clk); -/* MPU virtual clock functions */ -static int select_table_rate(struct clk * clk, unsigned long rate); -static long round_to_table_rate(struct clk * clk, unsigned long rate); -void clk_setdpll(__u16, __u16); - -struct mpu_rate rate_table[] = { - /* MPU MHz, xtal MHz, dpll1 MHz, CKCTL, DPLL_CTL - * armdiv, dspdiv, dspmmu, tcdiv, perdiv, lcddiv - */ -#if defined(CONFIG_OMAP_ARM_216MHZ) - { 216000000, 12000000, 216000000, 0x050d, 0x2910 }, /* 1/1/2/2/2/8 */ -#endif -#if defined(CONFIG_OMAP_ARM_195MHZ) - { 195000000, 13000000, 195000000, 0x050e, 0x2790 }, /* 1/1/2/2/4/8 */ -#endif -#if defined(CONFIG_OMAP_ARM_192MHZ) - { 192000000, 19200000, 192000000, 0x050f, 0x2510 }, /* 1/1/2/2/8/8 */ - { 192000000, 12000000, 192000000, 0x050f, 0x2810 }, /* 1/1/2/2/8/8 */ - { 96000000, 12000000, 192000000, 0x055f, 0x2810 }, /* 2/2/2/2/8/8 */ - { 48000000, 12000000, 192000000, 0x0ccf, 0x2810 }, /* 4/4/4/4/8/8 */ - { 24000000, 12000000, 192000000, 0x0fff, 0x2810 }, /* 8/8/8/8/8/8 */ -#endif -#if defined(CONFIG_OMAP_ARM_182MHZ) - { 182000000, 13000000, 182000000, 0x050e, 0x2710 }, /* 1/1/2/2/4/8 */ -#endif -#if defined(CONFIG_OMAP_ARM_168MHZ) - { 168000000, 12000000, 168000000, 0x010f, 0x2710 }, /* 1/1/1/2/8/8 */ -#endif -#if defined(CONFIG_OMAP_ARM_150MHZ) - { 150000000, 12000000, 150000000, 0x150a, 0x2cb0 }, /* 0/0/1/1/2/2 */ -#endif -#if defined(CONFIG_OMAP_ARM_120MHZ) - { 120000000, 12000000, 120000000, 0x010a, 0x2510 }, /* 1/1/1/2/4/4 */ -#endif -#if defined(CONFIG_OMAP_ARM_96MHZ) - { 96000000, 12000000, 96000000, 0x0005, 0x2410 }, /* 1/1/1/1/2/2 */ -#endif -#if defined(CONFIG_OMAP_ARM_60MHZ) - { 60000000, 12000000, 60000000, 0x0005, 0x2290 }, /* 1/1/1/1/2/2 */ -#endif -#if defined(CONFIG_OMAP_ARM_30MHZ) - { 30000000, 12000000, 60000000, 0x0555, 0x2290 }, /* 2/2/2/2/2/2 */ -#endif - { 0, 0, 0, 0, 0 }, -}; - - -static void ckctl_recalc(struct clk * clk) -{ - int dsor; - - /* Calculate divisor encoded as 2-bit exponent */ - dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset)); - if (unlikely(clk->rate == clk->parent->rate / dsor)) - return; /* No change, quick exit */ - clk->rate = clk->parent->rate / dsor; - - if (unlikely(clk->flags & RATE_PROPAGATES)) - propagate_rate(clk); -} - - -static void followparent_recalc(struct clk * clk) -{ - clk->rate = clk->parent->rate; -} - - -static void watchdog_recalc(struct clk * clk) -{ - clk->rate = clk->parent->rate / 14; -} - - -static struct clk ck_ref = { - .name = "ck_ref", - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - ALWAYS_ENABLED, -}; - -static struct clk ck_dpll1 = { - .name = "ck_dpll1", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_PROPAGATES | ALWAYS_ENABLED, -}; - -static struct clk ck_dpll1out = { - .name = "ck_dpll1out", - .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP16XX, - .enable_reg = ARM_IDLECT2, - .enable_bit = EN_CKOUT_ARM, - .recalc = &followparent_recalc, -}; - -static struct clk arm_ck = { - .name = "arm_ck", - .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, - .rate_offset = CKCTL_ARMDIV_OFFSET, - .recalc = &ckctl_recalc, -}; - -static struct clk armper_ck = { - .name = "armper_ck", - .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_CKCTL, - .enable_reg = ARM_IDLECT2, - .enable_bit = EN_PERCK, - .rate_offset = CKCTL_PERDIV_OFFSET, - .recalc = &ckctl_recalc, -}; - -static struct clk arm_gpio_ck = { - .name = "arm_gpio_ck", - .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510, - .enable_reg = ARM_IDLECT2, - .enable_bit = EN_GPIOCK, - .recalc = &followparent_recalc, -}; - -static struct clk armxor_ck = { - .name = "armxor_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, - .enable_reg = ARM_IDLECT2, - .enable_bit = EN_XORPCK, - .recalc = &followparent_recalc, -}; - -static struct clk armtim_ck = { - .name = "armtim_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, - .enable_reg = ARM_IDLECT2, - .enable_bit = EN_TIMCK, - .recalc = &followparent_recalc, -}; - -static struct clk armwdt_ck = { - .name = "armwdt_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, - .enable_reg = ARM_IDLECT2, - .enable_bit = EN_WDTCK, - .recalc = &watchdog_recalc, -}; - -static struct clk arminth_ck16xx = { - .name = "arminth_ck", - .parent = &arm_ck, - .flags = CLOCK_IN_OMAP16XX, - .recalc = &followparent_recalc, - /* Note: On 16xx the frequency can be divided by 2 by programming - * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1 - * - * 1510 version is in TC clocks. - */ -}; - -static struct clk dsp_ck = { - .name = "dsp_ck", - .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_CKCTL, - .enable_reg = ARM_CKCTL, - .enable_bit = EN_DSPCK, - .rate_offset = CKCTL_DSPDIV_OFFSET, - .recalc = &ckctl_recalc, -}; - -static struct clk dspmmu_ck = { - .name = "dspmmu_ck", - .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_CKCTL | ALWAYS_ENABLED, - .rate_offset = CKCTL_DSPMMUDIV_OFFSET, - .recalc = &ckctl_recalc, -}; - -static struct clk tc_ck = { - .name = "tc_ck", - .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | - RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, - .rate_offset = CKCTL_TCDIV_OFFSET, - .recalc = &ckctl_recalc, -}; - -static struct clk arminth_ck1510 = { - .name = "arminth_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510, - .recalc = &followparent_recalc, - /* Note: On 1510 the frequency follows TC_CK - * - * 16xx version is in MPU clocks. - */ -}; - -static struct clk tipb_ck = { - .name = "tibp_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510, - .recalc = &followparent_recalc, -}; - -static struct clk l3_ocpi_ck = { - .name = "l3_ocpi_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, - .enable_reg = ARM_IDLECT3, - .enable_bit = EN_OCPI_CK, - .recalc = &followparent_recalc, -}; - -static struct clk tc1_ck = { - .name = "tc1_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, - .enable_reg = ARM_IDLECT3, - .enable_bit = EN_TC1_CK, - .recalc = &followparent_recalc, -}; - -static struct clk tc2_ck = { - .name = "tc2_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, - .enable_reg = ARM_IDLECT3, - .enable_bit = EN_TC2_CK, - .recalc = &followparent_recalc, -}; - -static struct clk dma_ck = { - .name = "dma_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, - .recalc = &followparent_recalc, -}; - -static struct clk dma_lcdfree_ck = { - .name = "dma_lcdfree_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, - .recalc = &followparent_recalc, -}; - -static struct clk api_ck = { - .name = "api_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, - .enable_reg = ARM_IDLECT2, - .enable_bit = EN_APICK, - .recalc = &followparent_recalc, -}; - -static struct clk lb_ck = { - .name = "lb_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510, - .enable_reg = ARM_IDLECT2, - .enable_bit = EN_LBCK, - .recalc = &followparent_recalc, -}; - -static struct clk rhea1_ck = { - .name = "rhea1_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, - .recalc = &followparent_recalc, -}; - -static struct clk rhea2_ck = { - .name = "rhea2_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, - .recalc = &followparent_recalc, -}; - -static struct clk lcd_ck = { - .name = "lcd_ck", - .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | - RATE_CKCTL, - .enable_reg = ARM_IDLECT2, - .enable_bit = EN_LCDCK, - .rate_offset = CKCTL_LCDDIV_OFFSET, - .recalc = &ckctl_recalc, -}; - -static struct clk uart1_ck = { - .name = "uart1_ck", - /* Direct from ULPD, no parent */ - .rate = 48000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT, - .enable_reg = MOD_CONF_CTRL_0, - .enable_bit = 29, - /* (Only on 1510) - * The "enable bit" actually chooses between 48MHz and 12MHz. - */ -}; - -static struct clk uart2_ck = { - .name = "uart2_ck", - /* Direct from ULPD, no parent */ - .rate = 48000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT, - .enable_reg = MOD_CONF_CTRL_0, - .enable_bit = 30, - /* (for both 1510 and 16xx) - * The "enable bit" actually chooses between 48MHz and 12MHz/32kHz. - */ -}; - -static struct clk uart3_ck = { - .name = "uart3_ck", - /* Direct from ULPD, no parent */ - .rate = 48000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT, - .enable_reg = MOD_CONF_CTRL_0, - .enable_bit = 31, - /* (Only on 1510) - * The "enable bit" actually chooses between 48MHz and 12MHz. - */ -}; - -static struct clk usb_clko = { /* 6 MHz output on W4_USB_CLKO */ - .name = "usb_clko", - /* Direct from ULPD, no parent */ - .rate = 6000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT, - .enable_reg = ULPD_CLOCK_CTRL, - .enable_bit = USB_MCLK_EN_BIT, -}; - -static struct clk usb_hhc_ck1510 = { - .name = "usb_hhc_ck", - /* Direct from ULPD, no parent */ - .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ - .flags = CLOCK_IN_OMAP1510 | - RATE_FIXED | ENABLE_REG_32BIT, - .enable_reg = MOD_CONF_CTRL_0, - .enable_bit = USB_HOST_HHC_UHOST_EN, -}; - -static struct clk usb_hhc_ck16xx = { - .name = "usb_hhc_ck", - /* Direct from ULPD, no parent */ - .rate = 48000000, - /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */ - .flags = CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT, - .enable_reg = OTG_BASE + 0x08 /* OTG_SYSCON_2 */, - .enable_bit = 8 /* UHOST_EN */, -}; - -static struct clk mclk_1510 = { - .name = "mclk", - /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, -}; - -static struct clk mclk_16xx = { - .name = "mclk", - /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .flags = CLOCK_IN_OMAP16XX, - .enable_reg = COM_CLK_DIV_CTRL_SEL, - .enable_bit = COM_ULPD_PLL_CLK_REQ, - .set_rate = &set_ext_clk_rate, - .round_rate = &round_ext_clk_rate, - .init = &init_ext_clk, -}; - -static struct clk bclk_1510 = { - .name = "bclk", - /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, -}; - -static struct clk bclk_16xx = { - .name = "bclk", - /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .flags = CLOCK_IN_OMAP16XX, - .enable_reg = SWD_CLK_DIV_CTRL_SEL, - .enable_bit = SWD_ULPD_PLL_CLK_REQ, - .set_rate = &set_ext_clk_rate, - .round_rate = &round_ext_clk_rate, - .init = &init_ext_clk, -}; - -static struct clk mmc1_ck = { - .name = "mmc1_ck", - /* Functional clock is direct from ULPD, interface clock is ARMPER */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT, - .enable_reg = MOD_CONF_CTRL_0, - .enable_bit = 23, -}; - -static struct clk mmc2_ck = { - .name = "mmc2_ck", - /* Functional clock is direct from ULPD, interface clock is ARMPER */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT, - .enable_reg = MOD_CONF_CTRL_0, - .enable_bit = 20, -}; - -static struct clk virtual_ck_mpu = { - .name = "mpu", - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - VIRTUAL_CLOCK | ALWAYS_ENABLED, - .parent = &arm_ck, /* Is smarter alias for */ - .recalc = &followparent_recalc, - .set_rate = &select_table_rate, - .round_rate = &round_to_table_rate, -}; - - -static struct clk * onchip_clks[] = { - /* non-ULPD clocks */ - &ck_ref, - &ck_dpll1, - /* CK_GEN1 clocks */ - &ck_dpll1out, - &arm_ck, - &armper_ck, - &arm_gpio_ck, - &armxor_ck, - &armtim_ck, - &armwdt_ck, - &arminth_ck1510, &arminth_ck16xx, - /* CK_GEN2 clocks */ - &dsp_ck, - &dspmmu_ck, - /* CK_GEN3 clocks */ - &tc_ck, - &tipb_ck, - &l3_ocpi_ck, - &tc1_ck, - &tc2_ck, - &dma_ck, - &dma_lcdfree_ck, - &api_ck, - &lb_ck, - &rhea1_ck, - &rhea2_ck, - &lcd_ck, - /* ULPD clocks */ - &uart1_ck, - &uart2_ck, - &uart3_ck, - &usb_clko, - &usb_hhc_ck1510, &usb_hhc_ck16xx, - &mclk_1510, &mclk_16xx, - &bclk_1510, &bclk_16xx, - &mmc1_ck, - &mmc2_ck, - /* Virtual clocks */ - &virtual_ck_mpu, -}; - -struct clk *clk_get(struct device *dev, const char *id) -{ - struct clk *p, *clk = ERR_PTR(-ENOENT); - - down(&clocks_sem); - list_for_each_entry(p, &clocks, node) { - if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { - clk = p; - break; - } - } - up(&clocks_sem); - - return clk; -} -EXPORT_SYMBOL(clk_get); - - -void clk_put(struct clk *clk) -{ - if (clk && !IS_ERR(clk)) - module_put(clk->owner); -} -EXPORT_SYMBOL(clk_put); - - -int __clk_enable(struct clk *clk) -{ - __u16 regval16; - __u32 regval32; - - if (clk->flags & ALWAYS_ENABLED) - return 0; - - if (unlikely(clk->enable_reg == 0)) { - printk(KERN_ERR "clock.c: Enable for %s without enable code\n", - clk->name); - return 0; - } - - if (clk->flags & ENABLE_REG_32BIT) { - regval32 = omap_readl(clk->enable_reg); - regval32 |= (1 << clk->enable_bit); - omap_writel(regval32, clk->enable_reg); - } else { - regval16 = omap_readw(clk->enable_reg); - regval16 |= (1 << clk->enable_bit); - omap_writew(regval16, clk->enable_reg); - } - - return 0; -} - - -void __clk_disable(struct clk *clk) -{ - __u16 regval16; - __u32 regval32; - - if (clk->enable_reg == 0) - return; - - if (clk->flags & ENABLE_REG_32BIT) { - regval32 = omap_readl(clk->enable_reg); - regval32 &= ~(1 << clk->enable_bit); - omap_writel(regval32, clk->enable_reg); - } else { - regval16 = omap_readw(clk->enable_reg); - regval16 &= ~(1 << clk->enable_bit); - omap_writew(regval16, clk->enable_reg); - } -} - - -void __clk_unuse(struct clk *clk) -{ - if (clk->usecount > 0 && !(--clk->usecount)) { - __clk_disable(clk); - if (likely(clk->parent)) - __clk_unuse(clk->parent); - } -} - - -int __clk_use(struct clk *clk) -{ - int ret = 0; - if (clk->usecount++ == 0) { - if (likely(clk->parent)) - ret = __clk_use(clk->parent); - - if (unlikely(ret != 0)) { - clk->usecount--; - return ret; - } - - ret = __clk_enable(clk); - - if (unlikely(ret != 0) && clk->parent) { - __clk_unuse(clk->parent); - clk->usecount--; - } - } - - return ret; -} - - -int clk_enable(struct clk *clk) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&clockfw_lock, flags); - ret = __clk_enable(clk); - spin_unlock_irqrestore(&clockfw_lock, flags); - return ret; -} -EXPORT_SYMBOL(clk_enable); - - -void clk_disable(struct clk *clk) -{ - unsigned long flags; - - spin_lock_irqsave(&clockfw_lock, flags); - __clk_disable(clk); - spin_unlock_irqrestore(&clockfw_lock, flags); -} -EXPORT_SYMBOL(clk_disable); - - -int clk_use(struct clk *clk) -{ - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&clockfw_lock, flags); - ret = __clk_use(clk); - spin_unlock_irqrestore(&clockfw_lock, flags); - return ret; -} -EXPORT_SYMBOL(clk_use); - - -void clk_unuse(struct clk *clk) -{ - unsigned long flags; - - spin_lock_irqsave(&clockfw_lock, flags); - __clk_unuse(clk); - spin_unlock_irqrestore(&clockfw_lock, flags); -} -EXPORT_SYMBOL(clk_unuse); - - -int clk_get_usecount(struct clk *clk) -{ - return clk->usecount; -} -EXPORT_SYMBOL(clk_get_usecount); - - -unsigned long clk_get_rate(struct clk *clk) -{ - return clk->rate; -} -EXPORT_SYMBOL(clk_get_rate); - - -static __u16 verify_ckctl_value(__u16 newval) -{ - /* This function checks for following limitations set - * by the hardware (all conditions must be true): - * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 - * ARM_CK >= TC_CK - * DSP_CK >= TC_CK - * DSPMMU_CK >= TC_CK - * - * In addition following rules are enforced: - * LCD_CK <= TC_CK - * ARMPER_CK <= TC_CK - * - * However, maximum frequencies are not checked for! - */ - __u8 per_exp; - __u8 lcd_exp; - __u8 arm_exp; - __u8 dsp_exp; - __u8 tc_exp; - __u8 dspmmu_exp; - - per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3; - lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3; - arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3; - dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3; - tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3; - dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3; - - if (dspmmu_exp < dsp_exp) - dspmmu_exp = dsp_exp; - if (dspmmu_exp > dsp_exp+1) - dspmmu_exp = dsp_exp+1; - if (tc_exp < arm_exp) - tc_exp = arm_exp; - if (tc_exp < dspmmu_exp) - tc_exp = dspmmu_exp; - if (tc_exp > lcd_exp) - lcd_exp = tc_exp; - if (tc_exp > per_exp) - per_exp = tc_exp; - - newval &= 0xf000; - newval |= per_exp << CKCTL_PERDIV_OFFSET; - newval |= lcd_exp << CKCTL_LCDDIV_OFFSET; - newval |= arm_exp << CKCTL_ARMDIV_OFFSET; - newval |= dsp_exp << CKCTL_DSPDIV_OFFSET; - newval |= tc_exp << CKCTL_TCDIV_OFFSET; - newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET; - - return newval; -} - - -static int calc_dsor_exp(struct clk *clk, unsigned long rate) -{ - /* Note: If target frequency is too low, this function will return 4, - * which is invalid value. Caller must check for this value and act - * accordingly. - * - * Note: This function does not check for following limitations set - * by the hardware (all conditions must be true): - * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 - * ARM_CK >= TC_CK - * DSP_CK >= TC_CK - * DSPMMU_CK >= TC_CK - */ - unsigned long realrate; - struct clk * parent; - unsigned dsor_exp; - - if (unlikely(!(clk->flags & RATE_CKCTL))) - return -EINVAL; - - parent = clk->parent; - if (unlikely(parent == 0)) - return -EIO; - - realrate = parent->rate; - for (dsor_exp=0; dsor_exp<4; dsor_exp++) { - if (realrate <= rate) - break; - - realrate /= 2; - } - - return dsor_exp; -} - -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - int dsor_exp; - - if (clk->flags & RATE_FIXED) - return clk->rate; - - if (clk->flags & RATE_CKCTL) { - dsor_exp = calc_dsor_exp(clk, rate); - if (dsor_exp < 0) - return dsor_exp; - if (dsor_exp > 3) - dsor_exp = 3; - return clk->parent->rate / (1 << dsor_exp); - } - - if(clk->round_rate != 0) - return clk->round_rate(clk, rate); - - return clk->rate; -} -EXPORT_SYMBOL(clk_round_rate); - - -static void propagate_rate(struct clk * clk) -{ - struct clk ** clkp; - - for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) { - if (likely((*clkp)->parent != clk)) continue; - if (likely((*clkp)->recalc)) - (*clkp)->recalc(*clkp); - } -} - - -static int select_table_rate(struct clk * clk, unsigned long rate) -{ - /* Find the highest supported frequency <= rate and switch to it */ - struct mpu_rate * ptr; - - if (clk != &virtual_ck_mpu) - return -EINVAL; - - for (ptr = rate_table; ptr->rate; ptr++) { - if (ptr->xtal != ck_ref.rate) - continue; - - /* DPLL1 cannot be reprogrammed without risking system crash */ - if (likely(ck_dpll1.rate!=0) && ptr->pll_rate != ck_dpll1.rate) - continue; - - /* Can check only after xtal frequency check */ - if (ptr->rate <= rate) - break; - } - - if (!ptr->rate) - return -EINVAL; - - if (unlikely(ck_dpll1.rate == 0)) { - omap_writew(ptr->dpllctl_val, DPLL_CTL); - ck_dpll1.rate = ptr->pll_rate; - } - omap_writew(ptr->ckctl_val, ARM_CKCTL); - propagate_rate(&ck_dpll1); - return 0; -} - - -static long round_to_table_rate(struct clk * clk, unsigned long rate) -{ - /* Find the highest supported frequency <= rate */ - struct mpu_rate * ptr; - long highest_rate; - - if (clk != &virtual_ck_mpu) - return -EINVAL; - - highest_rate = -EINVAL; - - for (ptr = rate_table; ptr->rate; ptr++) { - if (ptr->xtal != ck_ref.rate) - continue; - - highest_rate = ptr->rate; - - /* Can check only after xtal frequency check */ - if (ptr->rate <= rate) - break; - } - - return highest_rate; -} - - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - int ret = -EINVAL; - int dsor_exp; - __u16 regval; - unsigned long flags; - - if (clk->flags & RATE_CKCTL) { - dsor_exp = calc_dsor_exp(clk, rate); - if (dsor_exp > 3) - dsor_exp = -EINVAL; - if (dsor_exp < 0) - return dsor_exp; - - spin_lock_irqsave(&clockfw_lock, flags); - regval = omap_readw(ARM_CKCTL); - regval &= ~(3 << clk->rate_offset); - regval |= dsor_exp << clk->rate_offset; - regval = verify_ckctl_value(regval); - omap_writew(regval, ARM_CKCTL); - clk->rate = clk->parent->rate / (1 << dsor_exp); - spin_unlock_irqrestore(&clockfw_lock, flags); - ret = 0; - } else if(clk->set_rate != 0) { - spin_lock_irqsave(&clockfw_lock, flags); - ret = clk->set_rate(clk, rate); - spin_unlock_irqrestore(&clockfw_lock, flags); - } - - if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES))) - propagate_rate(clk); - - return ret; -} -EXPORT_SYMBOL(clk_set_rate); - - -static unsigned calc_ext_dsor(unsigned long rate) -{ - unsigned dsor; - - /* MCLK and BCLK divisor selection is not linear: - * freq = 96MHz / dsor - * - * RATIO_SEL range: dsor <-> RATIO_SEL - * 0..6: (RATIO_SEL+2) <-> (dsor-2) - * 6..48: (8+(RATIO_SEL-6)*2) <-> ((dsor-8)/2+6) - * Minimum dsor is 2 and maximum is 96. Odd divisors starting from 9 - * can not be used. - */ - for (dsor = 2; dsor < 96; ++dsor) { - if ((dsor & 1) && dsor > 8) - continue; - if (rate >= 96000000 / dsor) - break; - } - return dsor; -} - - -static int set_ext_clk_rate(struct clk * clk, unsigned long rate) -{ - unsigned dsor; - __u16 ratio_bits; - - dsor = calc_ext_dsor(rate); - clk->rate = 96000000 / dsor; - if (dsor > 8) - ratio_bits = ((dsor - 8) / 2 + 6) << 2; - else - ratio_bits = (dsor - 2) << 2; - - ratio_bits |= omap_readw(clk->enable_reg) & ~0xfd; - omap_writew(ratio_bits, clk->enable_reg); - - return 0; -} - - -static long round_ext_clk_rate(struct clk * clk, unsigned long rate) -{ - return 96000000 / calc_ext_dsor(rate); -} - - -static void init_ext_clk(struct clk * clk) -{ - unsigned dsor; - __u16 ratio_bits; - - /* Determine current rate and ensure clock is based on 96MHz APLL */ - ratio_bits = omap_readw(clk->enable_reg) & ~1; - omap_writew(ratio_bits, clk->enable_reg); - - ratio_bits = (ratio_bits & 0xfc) >> 2; - if (ratio_bits > 6) - dsor = (ratio_bits - 6) * 2 + 8; - else - dsor = ratio_bits + 2; - - clk-> rate = 96000000 / dsor; -} - - -int clk_register(struct clk *clk) -{ - down(&clocks_sem); - list_add(&clk->node, &clocks); - if (clk->init) - clk->init(clk); - up(&clocks_sem); - return 0; -} -EXPORT_SYMBOL(clk_register); - -void clk_unregister(struct clk *clk) -{ - down(&clocks_sem); - list_del(&clk->node); - up(&clocks_sem); -} -EXPORT_SYMBOL(clk_unregister); - - - -int __init clk_init(void) -{ - struct clk ** clkp; - const struct omap_clock_config *info; - int crystal_type = 0; /* Default 12 MHz */ - - for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) { - if (((*clkp)->flags &CLOCK_IN_OMAP1510) && cpu_is_omap1510()) { - clk_register(*clkp); - continue; - } - - if (((*clkp)->flags &CLOCK_IN_OMAP16XX) && cpu_is_omap16xx()) { - clk_register(*clkp); - continue; - } - - if (((*clkp)->flags &CLOCK_IN_OMAP730) && cpu_is_omap730()) { - clk_register(*clkp); - continue; - } - } - - info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config); - if (info != NULL) { - if (!cpu_is_omap1510()) - crystal_type = info->system_clock_type; - } - -#if defined(CONFIG_ARCH_OMAP730) - ck_ref.rate = 13000000; -#elif defined(CONFIG_ARCH_OMAP16XX) - if (crystal_type == 2) - ck_ref.rate = 19200000; -#endif - - /* We want to be in syncronous scalable mode */ - omap_writew(0x1000, ARM_SYSST); - - /* Find the highest supported frequency and enable it */ - if (select_table_rate(&virtual_ck_mpu, ~0)) { - printk(KERN_ERR "System frequencies not set. Check your config.\n"); - /* Guess sane values (60MHz) */ - omap_writew(0x2290, DPLL_CTL); - omap_writew(0x1005, ARM_CKCTL); - ck_dpll1.rate = 60000000; - propagate_rate(&ck_dpll1); - printk(KERN_INFO "Clocking rate (xtal/DPLL1/MPU): %ld/%ld/%ld\n", - ck_ref.rate, ck_dpll1.rate, arm_ck.rate); - } - - /* Cache rates for clocks connected to ck_ref (not dpll1) */ - propagate_rate(&ck_ref); - -#ifdef CONFIG_MACH_OMAP_PERSEUS2 - /* Select slicer output as OMAP input clock */ - omap_writew(omap_readw(OMAP730_PCC_UPLD_CTRL) & ~0x1, OMAP730_PCC_UPLD_CTRL); -#endif - - /* Turn off DSP and ARM_TIMXO. Make sure ARM_INTHCK is not divided */ - omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL); - - /* Put DSP/MPUI into reset until needed */ - omap_writew(0, ARM_RSTCT1); - omap_writew(1, ARM_RSTCT2); - omap_writew(0x400, ARM_IDLECT1); - - /* - * According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8) - * of the ARM_IDLECT2 register must be set to zero. The power-on - * default value of this bit is one. - */ - omap_writew(0x0000, ARM_IDLECT2); /* Turn LCD clock off also */ - - /* - * Only enable those clocks we will need, let the drivers - * enable other clocks as necessary - */ - clk_use(&armper_ck); - clk_use(&armxor_ck); - clk_use(&armtim_ck); - - if (cpu_is_omap1510()) - clk_enable(&arm_gpio_ck); - - return 0; -} --- linux-2.6.13-rc2/arch/arm/mach-omap/clock.h 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,112 +0,0 @@ -/* - * linux/arch/arm/mach-omap/clock.h - * - * Copyright (C) 2004 Nokia corporation - * Written by Tuukka Tikkanen - * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __ARCH_ARM_OMAP_CLOCK_H -#define __ARCH_ARM_OMAP_CLOCK_H - -struct module; - -struct clk { - struct list_head node; - struct module *owner; - const char *name; - struct clk *parent; - unsigned long rate; - __s8 usecount; - __u16 flags; - __u32 enable_reg; - __u8 enable_bit; - __u8 rate_offset; - void (*recalc)(struct clk *); - int (*set_rate)(struct clk *, unsigned long); - long (*round_rate)(struct clk *, unsigned long); - void (*init)(struct clk *); -}; - - -struct mpu_rate { - unsigned long rate; - unsigned long xtal; - unsigned long pll_rate; - __u16 ckctl_val; - __u16 dpllctl_val; -}; - - -/* Clock flags */ -#define RATE_CKCTL 1 -#define RATE_FIXED 2 -#define RATE_PROPAGATES 4 -#define VIRTUAL_CLOCK 8 -#define ALWAYS_ENABLED 16 -#define ENABLE_REG_32BIT 32 -#define CLOCK_IN_OMAP16XX 64 -#define CLOCK_IN_OMAP1510 128 -#define CLOCK_IN_OMAP730 256 - -/* ARM_CKCTL bit shifts */ -#define CKCTL_PERDIV_OFFSET 0 -#define CKCTL_LCDDIV_OFFSET 2 -#define CKCTL_ARMDIV_OFFSET 4 -#define CKCTL_DSPDIV_OFFSET 6 -#define CKCTL_TCDIV_OFFSET 8 -#define CKCTL_DSPMMUDIV_OFFSET 10 -/*#define ARM_TIMXO 12*/ -#define EN_DSPCK 13 -/*#define ARM_INTHCK_SEL 14*/ /* Divide-by-2 for mpu inth_ck */ - -/* ARM_IDLECT1 bit shifts */ -/*#define IDLWDT_ARM 0*/ -/*#define IDLXORP_ARM 1*/ -/*#define IDLPER_ARM 2*/ -/*#define IDLLCD_ARM 3*/ -/*#define IDLLB_ARM 4*/ -/*#define IDLHSAB_ARM 5*/ -/*#define IDLIF_ARM 6*/ -/*#define IDLDPLL_ARM 7*/ -/*#define IDLAPI_ARM 8*/ -/*#define IDLTIM_ARM 9*/ -/*#define SETARM_IDLE 11*/ - -/* ARM_IDLECT2 bit shifts */ -#define EN_WDTCK 0 -#define EN_XORPCK 1 -#define EN_PERCK 2 -#define EN_LCDCK 3 -#define EN_LBCK 4 /* Not on 1610/1710 */ -/*#define EN_HSABCK 5*/ -#define EN_APICK 6 -#define EN_TIMCK 7 -#define DMACK_REQ 8 -#define EN_GPIOCK 9 /* Not on 1610/1710 */ -/*#define EN_LBFREECK 10*/ -#define EN_CKOUT_ARM 11 - -/* ARM_IDLECT3 bit shifts */ -#define EN_OCPI_CK 0 -#define EN_TC1_CK 2 -#define EN_TC2_CK 4 - -/* Various register defines for clock controls scattered around OMAP chip */ -#define USB_MCLK_EN_BIT 4 /* In ULPD_CLKC_CTRL */ -#define USB_HOST_HHC_UHOST_EN 9 /* In MOD_CONF_CTRL_0 */ -#define SWD_ULPD_PLL_CLK_REQ 1 /* In SWD_CLK_DIV_CTRL_SEL */ -#define COM_ULPD_PLL_CLK_REQ 1 /* In COM_CLK_DIV_CTRL_SEL */ -#define SWD_CLK_DIV_CTRL_SEL 0xfffe0874 -#define COM_CLK_DIV_CTRL_SEL 0xfffe0878 - - -int clk_register(struct clk *clk); -void clk_unregister(struct clk *clk); -int clk_init(void); - -#endif --- linux-2.6.13-rc2/arch/arm/mach-omap/common.c 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,549 +0,0 @@ -/* - * linux/arch/arm/mach-omap/common.c - * - * Code common to all OMAP machines. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "clock.h" - -#define DEBUG 1 - -struct omap_id { - u16 jtag_id; /* Used to determine OMAP type */ - u8 die_rev; /* Processor revision */ - u32 omap_id; /* OMAP revision */ - u32 type; /* Cpu id bits [31:08], cpu class bits [07:00] */ -}; - -/* Register values to detect the OMAP version */ -static struct omap_id omap_ids[] __initdata = { - { .jtag_id = 0x355f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300100}, - { .jtag_id = 0xb55f, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x07300300}, - { .jtag_id = 0xb470, .die_rev = 0x0, .omap_id = 0x03310100, .type = 0x15100000}, - { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320000, .type = 0x16100000}, - { .jtag_id = 0xb576, .die_rev = 0x2, .omap_id = 0x03320100, .type = 0x16110000}, - { .jtag_id = 0xb576, .die_rev = 0x3, .omap_id = 0x03320100, .type = 0x16100c00}, - { .jtag_id = 0xb576, .die_rev = 0x0, .omap_id = 0x03320200, .type = 0x16100d00}, - { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00}, - { .jtag_id = 0xb613, .die_rev = 0x0, .omap_id = 0x03320300, .type = 0x1610ef00}, - { .jtag_id = 0xb576, .die_rev = 0x1, .omap_id = 0x03320100, .type = 0x16110000}, - { .jtag_id = 0xb58c, .die_rev = 0x2, .omap_id = 0x03320200, .type = 0x16110b00}, - { .jtag_id = 0xb58c, .die_rev = 0x3, .omap_id = 0x03320200, .type = 0x16110c00}, - { .jtag_id = 0xb65f, .die_rev = 0x0, .omap_id = 0x03320400, .type = 0x16212300}, - { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320400, .type = 0x16212300}, - { .jtag_id = 0xb65f, .die_rev = 0x1, .omap_id = 0x03320500, .type = 0x16212300}, - { .jtag_id = 0xb5f7, .die_rev = 0x0, .omap_id = 0x03330000, .type = 0x17100000}, - { .jtag_id = 0xb5f7, .die_rev = 0x1, .omap_id = 0x03330100, .type = 0x17100000}, - { .jtag_id = 0xb5f7, .die_rev = 0x2, .omap_id = 0x03330100, .type = 0x17100000}, -}; - -/* - * Get OMAP type from PROD_ID. - * 1710 has the PROD_ID in bits 15:00, not in 16:01 as documented in TRM. - * 1510 PROD_ID is empty, and 1610 PROD_ID does not make sense. - * Undocumented register in TEST BLOCK is used as fallback; This seems to - * work on 1510, 1610 & 1710. The official way hopefully will work in future - * processors. - */ -static u16 __init omap_get_jtag_id(void) -{ - u32 prod_id, omap_id; - - prod_id = omap_readl(OMAP_PRODUCTION_ID_1); - omap_id = omap_readl(OMAP32_ID_1); - - /* Check for unusable OMAP_PRODUCTION_ID_1 on 1611B/5912 and 730 */ - if (((prod_id >> 20) == 0) || (prod_id == omap_id)) - prod_id = 0; - else - prod_id &= 0xffff; - - if (prod_id) - return prod_id; - - /* Use OMAP32_ID_1 as fallback */ - prod_id = ((omap_id >> 12) & 0xffff); - - return prod_id; -} - -/* - * Get OMAP revision from DIE_REV. - * Early 1710 processors may have broken OMAP_DIE_ID, it contains PROD_ID. - * Undocumented register in the TEST BLOCK is used as fallback. - * REVISIT: This does not seem to work on 1510 - */ -static u8 __init omap_get_die_rev(void) -{ - u32 die_rev; - - die_rev = omap_readl(OMAP_DIE_ID_1); - - /* Check for broken OMAP_DIE_ID on early 1710 */ - if (((die_rev >> 12) & 0xffff) == omap_get_jtag_id()) - die_rev = 0; - - die_rev = (die_rev >> 17) & 0xf; - if (die_rev) - return die_rev; - - die_rev = (omap_readl(OMAP32_ID_1) >> 28) & 0xf; - - return die_rev; -} - -static void __init omap_check_revision(void) -{ - int i; - u16 jtag_id; - u8 die_rev; - u32 omap_id; - u8 cpu_type; - - jtag_id = omap_get_jtag_id(); - die_rev = omap_get_die_rev(); - omap_id = omap_readl(OMAP32_ID_0); - -#ifdef DEBUG - printk("OMAP_DIE_ID_0: 0x%08x\n", omap_readl(OMAP_DIE_ID_0)); - printk("OMAP_DIE_ID_1: 0x%08x DIE_REV: %i\n", - omap_readl(OMAP_DIE_ID_1), - (omap_readl(OMAP_DIE_ID_1) >> 17) & 0xf); - printk("OMAP_PRODUCTION_ID_0: 0x%08x\n", omap_readl(OMAP_PRODUCTION_ID_0)); - printk("OMAP_PRODUCTION_ID_1: 0x%08x JTAG_ID: 0x%04x\n", - omap_readl(OMAP_PRODUCTION_ID_1), - omap_readl(OMAP_PRODUCTION_ID_1) & 0xffff); - printk("OMAP32_ID_0: 0x%08x\n", omap_readl(OMAP32_ID_0)); - printk("OMAP32_ID_1: 0x%08x\n", omap_readl(OMAP32_ID_1)); - printk("JTAG_ID: 0x%04x DIE_REV: %i\n", jtag_id, die_rev); -#endif - - system_serial_high = omap_readl(OMAP_DIE_ID_0); - system_serial_low = omap_readl(OMAP_DIE_ID_1); - - /* First check only the major version in a safe way */ - for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { - if (jtag_id == (omap_ids[i].jtag_id)) { - system_rev = omap_ids[i].type; - break; - } - } - - /* Check if we can find the die revision */ - for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { - if (jtag_id == omap_ids[i].jtag_id && die_rev == omap_ids[i].die_rev) { - system_rev = omap_ids[i].type; - break; - } - } - - /* Finally check also the omap_id */ - for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { - if (jtag_id == omap_ids[i].jtag_id - && die_rev == omap_ids[i].die_rev - && omap_id == omap_ids[i].omap_id) { - system_rev = omap_ids[i].type; - break; - } - } - - /* Add the cpu class info (7xx, 15xx, 16xx, 24xx) */ - cpu_type = system_rev >> 24; - - switch (cpu_type) { - case 0x07: - system_rev |= 0x07; - break; - case 0x15: - system_rev |= 0x15; - break; - case 0x16: - case 0x17: - system_rev |= 0x16; - break; - case 0x24: - system_rev |= 0x24; - break; - default: - printk("Unknown OMAP cpu type: 0x%02x\n", cpu_type); - } - - printk("OMAP%04x", system_rev >> 16); - if ((system_rev >> 8) & 0xff) - printk("%x", (system_rev >> 8) & 0xff); - printk(" revision %i handled as %02xxx id: %08x%08x\n", - die_rev, system_rev & 0xff, system_serial_low, - system_serial_high); -} - -/* - * ---------------------------------------------------------------------------- - * OMAP I/O mapping - * - * The machine specific code may provide the extra mapping besides the - * default mapping provided here. - * ---------------------------------------------------------------------------- - */ - -static struct map_desc omap_io_desc[] __initdata = { - { IO_VIRT, IO_PHYS, IO_SIZE, MT_DEVICE }, -}; - -#ifdef CONFIG_ARCH_OMAP730 -static struct map_desc omap730_io_desc[] __initdata = { - { OMAP730_DSP_BASE, OMAP730_DSP_START, OMAP730_DSP_SIZE, MT_DEVICE }, - { OMAP730_DSPREG_BASE, OMAP730_DSPREG_START, OMAP730_DSPREG_SIZE, MT_DEVICE }, - { OMAP730_SRAM_BASE, OMAP730_SRAM_START, OMAP730_SRAM_SIZE, MT_DEVICE } -}; -#endif - -#ifdef CONFIG_ARCH_OMAP1510 -static struct map_desc omap1510_io_desc[] __initdata = { - { OMAP1510_DSP_BASE, OMAP1510_DSP_START, OMAP1510_DSP_SIZE, MT_DEVICE }, - { OMAP1510_DSPREG_BASE, OMAP1510_DSPREG_START, OMAP1510_DSPREG_SIZE, MT_DEVICE }, - { OMAP1510_SRAM_BASE, OMAP1510_SRAM_START, OMAP1510_SRAM_SIZE, MT_DEVICE } -}; -#endif - -#if defined(CONFIG_ARCH_OMAP16XX) -static struct map_desc omap1610_io_desc[] __initdata = { - { OMAP16XX_DSP_BASE, OMAP16XX_DSP_START, OMAP16XX_DSP_SIZE, MT_DEVICE }, - { OMAP16XX_DSPREG_BASE, OMAP16XX_DSPREG_START, OMAP16XX_DSPREG_SIZE, MT_DEVICE }, - { OMAP16XX_SRAM_BASE, OMAP16XX_SRAM_START, OMAP1610_SRAM_SIZE, MT_DEVICE } -}; - -static struct map_desc omap5912_io_desc[] __initdata = { - { OMAP16XX_DSP_BASE, OMAP16XX_DSP_START, OMAP16XX_DSP_SIZE, MT_DEVICE }, - { OMAP16XX_DSPREG_BASE, OMAP16XX_DSPREG_START, OMAP16XX_DSPREG_SIZE, MT_DEVICE }, -/* - * The OMAP5912 has 250kByte internal SRAM. Because the mapping is baseed on page - * size (4kByte), it seems that the last 2kByte (=0x800) of the 250kByte are not mapped. - * Add additional 2kByte (0x800) so that the last page is mapped and the last 2kByte - * can be used. - */ - { OMAP16XX_SRAM_BASE, OMAP16XX_SRAM_START, OMAP5912_SRAM_SIZE + 0x800, MT_DEVICE } -}; -#endif - -static int initialized = 0; - -static void __init _omap_map_io(void) -{ - initialized = 1; - - /* We have to initialize the IO space mapping before we can run - * cpu_is_omapxxx() macros. */ - iotable_init(omap_io_desc, ARRAY_SIZE(omap_io_desc)); - omap_check_revision(); - -#ifdef CONFIG_ARCH_OMAP730 - if (cpu_is_omap730()) { - iotable_init(omap730_io_desc, ARRAY_SIZE(omap730_io_desc)); - } -#endif -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc)); - } -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (cpu_is_omap1610() || cpu_is_omap1710()) { - iotable_init(omap1610_io_desc, ARRAY_SIZE(omap1610_io_desc)); - } - if (cpu_is_omap5912()) { - iotable_init(omap5912_io_desc, ARRAY_SIZE(omap5912_io_desc)); - } -#endif - - /* REVISIT: Refer to OMAP5910 Errata, Advisory SYS_1: "Timeout Abort - * on a Posted Write in the TIPB Bridge". - */ - omap_writew(0x0, MPU_PUBLIC_TIPB_CNTL); - omap_writew(0x0, MPU_PRIVATE_TIPB_CNTL); - - /* Must init clocks early to assure that timer interrupt works - */ - clk_init(); -} - -/* - * This should only get called from board specific init - */ -void omap_map_io(void) -{ - if (!initialized) - _omap_map_io(); -} - -static inline unsigned int omap_serial_in(struct plat_serial8250_port *up, - int offset) -{ - offset <<= up->regshift; - return (unsigned int)__raw_readb(up->membase + offset); -} - -static inline void omap_serial_outp(struct plat_serial8250_port *p, int offset, - int value) -{ - offset <<= p->regshift; - __raw_writeb(value, p->membase + offset); -} - -/* - * Internal UARTs need to be initialized for the 8250 autoconfig to work - * properly. Note that the TX watermark initialization may not be needed - * once the 8250.c watermark handling code is merged. - */ -static void __init omap_serial_reset(struct plat_serial8250_port *p) -{ - omap_serial_outp(p, UART_OMAP_MDR1, 0x07); /* disable UART */ - omap_serial_outp(p, UART_OMAP_SCR, 0x08); /* TX watermark */ - omap_serial_outp(p, UART_OMAP_MDR1, 0x00); /* enable UART */ - - if (!cpu_is_omap1510()) { - omap_serial_outp(p, UART_OMAP_SYSC, 0x01); - while (!(omap_serial_in(p, UART_OMAP_SYSC) & 0x01)); - } -} - -static struct plat_serial8250_port serial_platform_data[] = { - { - .membase = (char*)IO_ADDRESS(OMAP_UART1_BASE), - .mapbase = (unsigned long)OMAP_UART1_BASE, - .irq = INT_UART1, - .flags = UPF_BOOT_AUTOCONF, - .iotype = UPIO_MEM, - .regshift = 2, - .uartclk = OMAP16XX_BASE_BAUD * 16, - }, - { - .membase = (char*)IO_ADDRESS(OMAP_UART2_BASE), - .mapbase = (unsigned long)OMAP_UART2_BASE, - .irq = INT_UART2, - .flags = UPF_BOOT_AUTOCONF, - .iotype = UPIO_MEM, - .regshift = 2, - .uartclk = OMAP16XX_BASE_BAUD * 16, - }, - { - .membase = (char*)IO_ADDRESS(OMAP_UART3_BASE), - .mapbase = (unsigned long)OMAP_UART3_BASE, - .irq = INT_UART3, - .flags = UPF_BOOT_AUTOCONF, - .iotype = UPIO_MEM, - .regshift = 2, - .uartclk = OMAP16XX_BASE_BAUD * 16, - }, - { }, -}; - -static struct platform_device serial_device = { - .name = "serial8250", - .id = 0, - .dev = { - .platform_data = serial_platform_data, - }, -}; - -/* - * Note that on Innovator-1510 UART2 pins conflict with USB2. - * By default UART2 does not work on Innovator-1510 if you have - * USB OHCI enabled. To use UART2, you must disable USB2 first. - */ -void __init omap_serial_init(int ports[OMAP_MAX_NR_PORTS]) -{ - int i; - - if (cpu_is_omap730()) { - serial_platform_data[0].regshift = 0; - serial_platform_data[1].regshift = 0; - serial_platform_data[0].irq = INT_730_UART_MODEM_1; - serial_platform_data[1].irq = INT_730_UART_MODEM_IRDA_2; - } - - if (cpu_is_omap1510()) { - serial_platform_data[0].uartclk = OMAP1510_BASE_BAUD * 16; - serial_platform_data[1].uartclk = OMAP1510_BASE_BAUD * 16; - serial_platform_data[2].uartclk = OMAP1510_BASE_BAUD * 16; - } - - for (i = 0; i < OMAP_MAX_NR_PORTS; i++) { - unsigned char reg; - - if (ports[i] == 0) { - serial_platform_data[i].membase = 0; - serial_platform_data[i].mapbase = 0; - continue; - } - - switch (i) { - case 0: - if (cpu_is_omap1510()) { - omap_cfg_reg(UART1_TX); - omap_cfg_reg(UART1_RTS); - if (machine_is_omap_innovator()) { - reg = fpga_read(OMAP1510_FPGA_POWER); - reg |= OMAP1510_FPGA_PCR_COM1_EN; - fpga_write(reg, OMAP1510_FPGA_POWER); - udelay(10); - } - } - break; - case 1: - if (cpu_is_omap1510()) { - omap_cfg_reg(UART2_TX); - omap_cfg_reg(UART2_RTS); - if (machine_is_omap_innovator()) { - reg = fpga_read(OMAP1510_FPGA_POWER); - reg |= OMAP1510_FPGA_PCR_COM2_EN; - fpga_write(reg, OMAP1510_FPGA_POWER); - udelay(10); - } - } - break; - case 2: - if (cpu_is_omap1510()) { - omap_cfg_reg(UART3_TX); - omap_cfg_reg(UART3_RX); - } - if (cpu_is_omap1710()) { - clk_enable(clk_get(0, "uart3_ck")); - } - break; - } - omap_serial_reset(&serial_platform_data[i]); - } -} - -static int __init omap_init(void) -{ - return platform_device_register(&serial_device); -} -arch_initcall(omap_init); - -#define NO_LENGTH_CHECK 0xffffffff - -extern int omap_bootloader_tag_len; -extern u8 omap_bootloader_tag[]; - -struct omap_board_config_kernel *omap_board_config; -int omap_board_config_size = 0; - -static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out) -{ - struct omap_board_config_kernel *kinfo = NULL; - int i; - -#ifdef CONFIG_OMAP_BOOT_TAG - struct omap_board_config_entry *info = NULL; - - if (omap_bootloader_tag_len > 4) - info = (struct omap_board_config_entry *) omap_bootloader_tag; - while (info != NULL) { - u8 *next; - - if (info->tag == tag) { - if (skip == 0) - break; - skip--; - } - - if ((info->len & 0x03) != 0) { - /* We bail out to avoid an alignment fault */ - printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n", - info->len, info->tag); - return NULL; - } - next = (u8 *) info + sizeof(*info) + info->len; - if (next >= omap_bootloader_tag + omap_bootloader_tag_len) - info = NULL; - else - info = (struct omap_board_config_entry *) next; - } - if (info != NULL) { - /* Check the length as a lame attempt to check for - * binary inconsistancy. */ - if (len != NO_LENGTH_CHECK) { - /* Word-align len */ - if (len & 0x03) - len = (len + 3) & ~0x03; - if (info->len != len) { - printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n", - tag, len, info->len); - return NULL; - } - } - if (len_out != NULL) - *len_out = info->len; - return info->data; - } -#endif - /* Try to find the config from the board-specific structures - * in the kernel. */ - for (i = 0; i < omap_board_config_size; i++) { - if (omap_board_config[i].tag == tag) { - kinfo = &omap_board_config[i]; - break; - } - } - if (kinfo == NULL) - return NULL; - return kinfo->data; -} - -const void *__omap_get_config(u16 tag, size_t len, int nr) -{ - return get_config(tag, len, nr, NULL); -} -EXPORT_SYMBOL(__omap_get_config); - -const void *omap_get_var_config(u16 tag, size_t *len) -{ - return get_config(tag, NO_LENGTH_CHECK, 0, len); -} -EXPORT_SYMBOL(omap_get_var_config); - -static int __init omap_add_serial_console(void) -{ - const struct omap_uart_config *info; - - info = omap_get_config(OMAP_TAG_UART, struct omap_uart_config); - if (info != NULL && info->console_uart) { - static char speed[11], *opt = NULL; - - if (info->console_speed) { - snprintf(speed, sizeof(speed), "%u", info->console_speed); - opt = speed; - } - return add_preferred_console("ttyS", info->console_uart - 1, opt); - } - return 0; -} -console_initcall(omap_add_serial_console); --- linux-2.6.13-rc2/arch/arm/mach-omap/common.h 2005-03-01 23:38:10.000000000 -0800 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,36 +0,0 @@ -/* - * linux/arch/arm/mach-omap/common.h - * - * Header for code common to all OMAP machines. - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef __ARCH_ARM_MACH_OMAP_COMMON_H -#define __ARCH_ARM_MACH_OMAP_COMMON_H - -struct sys_timer; - -extern void omap_map_io(void); -extern struct sys_timer omap_timer; -extern void omap_serial_init(int ports[]); - -#endif /* __ARCH_ARM_MACH_OMAP_COMMON_H */ --- linux-2.6.13-rc2/arch/arm/mach-omap/dma.c 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,1086 +0,0 @@ -/* - * linux/arch/arm/omap/dma.c - * - * Copyright (C) 2003 Nokia Corporation - * Author: Juha Yrjölä - * DMA channel linking for 1610 by Samuel Ortiz - * Graphics DMA and LCD DMA graphics tranformations - * by Imre Deak - * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. - * - * Support functions for the OMAP internal DMA channels. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#define OMAP_DMA_ACTIVE 0x01 - -#define OMAP_DMA_CCR_EN (1 << 7) - -#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec) - -static int enable_1510_mode = 0; - -struct omap_dma_lch { - int next_lch; - int dev_id; - u16 saved_csr; - u16 enabled_irqs; - const char *dev_name; - void (* callback)(int lch, u16 ch_status, void *data); - void *data; - long flags; -}; - -static int dma_chan_count; - -static spinlock_t dma_chan_lock; -static struct omap_dma_lch dma_chan[OMAP_LOGICAL_DMA_CH_COUNT]; - -const static u8 dma_irq[OMAP_LOGICAL_DMA_CH_COUNT] = { - INT_DMA_CH0_6, INT_DMA_CH1_7, INT_DMA_CH2_8, INT_DMA_CH3, - INT_DMA_CH4, INT_DMA_CH5, INT_1610_DMA_CH6, INT_1610_DMA_CH7, - INT_1610_DMA_CH8, INT_1610_DMA_CH9, INT_1610_DMA_CH10, - INT_1610_DMA_CH11, INT_1610_DMA_CH12, INT_1610_DMA_CH13, - INT_1610_DMA_CH14, INT_1610_DMA_CH15, INT_DMA_LCD -}; - -static inline int get_gdma_dev(int req) -{ - u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; - int shift = ((req - 1) % 5) * 6; - - return ((omap_readl(reg) >> shift) & 0x3f) + 1; -} - -static inline void set_gdma_dev(int req, int dev) -{ - u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; - int shift = ((req - 1) % 5) * 6; - u32 l; - - l = omap_readl(reg); - l &= ~(0x3f << shift); - l |= (dev - 1) << shift; - omap_writel(l, reg); -} - -static void clear_lch_regs(int lch) -{ - int i; - u32 lch_base = OMAP_DMA_BASE + lch * 0x40; - - for (i = 0; i < 0x2c; i += 2) - omap_writew(0, lch_base + i); -} - -void omap_set_dma_priority(int dst_port, int priority) -{ - unsigned long reg; - u32 l; - - switch (dst_port) { - case OMAP_DMA_PORT_OCP_T1: /* FFFECC00 */ - reg = OMAP_TC_OCPT1_PRIOR; - break; - case OMAP_DMA_PORT_OCP_T2: /* FFFECCD0 */ - reg = OMAP_TC_OCPT2_PRIOR; - break; - case OMAP_DMA_PORT_EMIFF: /* FFFECC08 */ - reg = OMAP_TC_EMIFF_PRIOR; - break; - case OMAP_DMA_PORT_EMIFS: /* FFFECC04 */ - reg = OMAP_TC_EMIFS_PRIOR; - break; - default: - BUG(); - return; - } - l = omap_readl(reg); - l &= ~(0xf << 8); - l |= (priority & 0xf) << 8; - omap_writel(l, reg); -} - -void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, - int frame_count, int sync_mode) -{ - u16 w; - - w = omap_readw(OMAP_DMA_CSDP(lch)); - w &= ~0x03; - w |= data_type; - omap_writew(w, OMAP_DMA_CSDP(lch)); - - w = omap_readw(OMAP_DMA_CCR(lch)); - w &= ~(1 << 5); - if (sync_mode == OMAP_DMA_SYNC_FRAME) - w |= 1 << 5; - omap_writew(w, OMAP_DMA_CCR(lch)); - - w = omap_readw(OMAP_DMA_CCR2(lch)); - w &= ~(1 << 2); - if (sync_mode == OMAP_DMA_SYNC_BLOCK) - w |= 1 << 2; - omap_writew(w, OMAP_DMA_CCR2(lch)); - - omap_writew(elem_count, OMAP_DMA_CEN(lch)); - omap_writew(frame_count, OMAP_DMA_CFN(lch)); - -} -void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) -{ - u16 w; - - BUG_ON(omap_dma_in_1510_mode()); - - w = omap_readw(OMAP_DMA_CCR2(lch)) & ~0x03; - switch (mode) { - case OMAP_DMA_CONSTANT_FILL: - w |= 0x01; - break; - case OMAP_DMA_TRANSPARENT_COPY: - w |= 0x02; - break; - case OMAP_DMA_COLOR_DIS: - break; - default: - BUG(); - } - omap_writew(w, OMAP_DMA_CCR2(lch)); - - w = omap_readw(OMAP_DMA_LCH_CTRL(lch)) & ~0x0f; - /* Default is channel type 2D */ - if (mode) { - omap_writew((u16)color, OMAP_DMA_COLOR_L(lch)); - omap_writew((u16)(color >> 16), OMAP_DMA_COLOR_U(lch)); - w |= 1; /* Channel type G */ - } - omap_writew(w, OMAP_DMA_LCH_CTRL(lch)); -} - - -void omap_set_dma_src_params(int lch, int src_port, int src_amode, - unsigned long src_start) -{ - u16 w; - - w = omap_readw(OMAP_DMA_CSDP(lch)); - w &= ~(0x1f << 2); - w |= src_port << 2; - omap_writew(w, OMAP_DMA_CSDP(lch)); - - w = omap_readw(OMAP_DMA_CCR(lch)); - w &= ~(0x03 << 12); - w |= src_amode << 12; - omap_writew(w, OMAP_DMA_CCR(lch)); - - omap_writew(src_start >> 16, OMAP_DMA_CSSA_U(lch)); - omap_writew(src_start, OMAP_DMA_CSSA_L(lch)); -} - -void omap_set_dma_src_index(int lch, int eidx, int fidx) -{ - omap_writew(eidx, OMAP_DMA_CSEI(lch)); - omap_writew(fidx, OMAP_DMA_CSFI(lch)); -} - -void omap_set_dma_src_data_pack(int lch, int enable) -{ - u16 w; - - w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 6); - w |= enable ? (1 << 6) : 0; - omap_writew(w, OMAP_DMA_CSDP(lch)); -} - -void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) -{ - u16 w; - - w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 7); - switch (burst_mode) { - case OMAP_DMA_DATA_BURST_DIS: - break; - case OMAP_DMA_DATA_BURST_4: - w |= (0x01 << 7); - break; - case OMAP_DMA_DATA_BURST_8: - /* not supported by current hardware - * w |= (0x03 << 7); - * fall through - */ - default: - BUG(); - } - omap_writew(w, OMAP_DMA_CSDP(lch)); -} - -void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, - unsigned long dest_start) -{ - u16 w; - - w = omap_readw(OMAP_DMA_CSDP(lch)); - w &= ~(0x1f << 9); - w |= dest_port << 9; - omap_writew(w, OMAP_DMA_CSDP(lch)); - - w = omap_readw(OMAP_DMA_CCR(lch)); - w &= ~(0x03 << 14); - w |= dest_amode << 14; - omap_writew(w, OMAP_DMA_CCR(lch)); - - omap_writew(dest_start >> 16, OMAP_DMA_CDSA_U(lch)); - omap_writew(dest_start, OMAP_DMA_CDSA_L(lch)); -} - -void omap_set_dma_dest_index(int lch, int eidx, int fidx) -{ - omap_writew(eidx, OMAP_DMA_CDEI(lch)); - omap_writew(fidx, OMAP_DMA_CDFI(lch)); -} - -void omap_set_dma_dest_data_pack(int lch, int enable) -{ - u16 w; - - w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 13); - w |= enable ? (1 << 13) : 0; - omap_writew(w, OMAP_DMA_CSDP(lch)); -} - -void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) -{ - u16 w; - - w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 14); - switch (burst_mode) { - case OMAP_DMA_DATA_BURST_DIS: - break; - case OMAP_DMA_DATA_BURST_4: - w |= (0x01 << 14); - break; - case OMAP_DMA_DATA_BURST_8: - w |= (0x03 << 14); - break; - default: - printk(KERN_ERR "Invalid DMA burst mode\n"); - BUG(); - return; - } - omap_writew(w, OMAP_DMA_CSDP(lch)); -} - -static inline void init_intr(int lch) -{ - u16 w; - - /* Read CSR to make sure it's cleared. */ - w = omap_readw(OMAP_DMA_CSR(lch)); - /* Enable some nice interrupts. */ - omap_writew(dma_chan[lch].enabled_irqs, OMAP_DMA_CICR(lch)); - dma_chan[lch].flags |= OMAP_DMA_ACTIVE; -} - -static inline void enable_lnk(int lch) -{ - u16 w; - - /* Clear the STOP_LNK bits */ - w = omap_readw(OMAP_DMA_CLNK_CTRL(lch)); - w &= ~(1 << 14); - omap_writew(w, OMAP_DMA_CLNK_CTRL(lch)); - - /* And set the ENABLE_LNK bits */ - if (dma_chan[lch].next_lch != -1) - omap_writew(dma_chan[lch].next_lch | (1 << 15), - OMAP_DMA_CLNK_CTRL(lch)); -} - -static inline void disable_lnk(int lch) -{ - u16 w; - - /* Disable interrupts */ - omap_writew(0, OMAP_DMA_CICR(lch)); - - /* Set the STOP_LNK bit */ - w = omap_readw(OMAP_DMA_CLNK_CTRL(lch)); - w |= (1 << 14); - w = omap_writew(w, OMAP_DMA_CLNK_CTRL(lch)); - - dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; -} - -void omap_start_dma(int lch) -{ - u16 w; - - if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { - int next_lch, cur_lch; - char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT]; - - dma_chan_link_map[lch] = 1; - /* Set the link register of the first channel */ - enable_lnk(lch); - - memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); - cur_lch = dma_chan[lch].next_lch; - do { - next_lch = dma_chan[cur_lch].next_lch; - - /* The loop case: we've been here already */ - if (dma_chan_link_map[cur_lch]) - break; - /* Mark the current channel */ - dma_chan_link_map[cur_lch] = 1; - - enable_lnk(cur_lch); - init_intr(cur_lch); - - cur_lch = next_lch; - } while (next_lch != -1); - } - - init_intr(lch); - - w = omap_readw(OMAP_DMA_CCR(lch)); - w |= OMAP_DMA_CCR_EN; - omap_writew(w, OMAP_DMA_CCR(lch)); - dma_chan[lch].flags |= OMAP_DMA_ACTIVE; -} - -void omap_stop_dma(int lch) -{ - u16 w; - - if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { - int next_lch, cur_lch = lch; - char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT]; - - memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); - do { - /* The loop case: we've been here already */ - if (dma_chan_link_map[cur_lch]) - break; - /* Mark the current channel */ - dma_chan_link_map[cur_lch] = 1; - - disable_lnk(cur_lch); - - next_lch = dma_chan[cur_lch].next_lch; - cur_lch = next_lch; - } while (next_lch != -1); - - return; - } - /* Disable all interrupts on the channel */ - omap_writew(0, OMAP_DMA_CICR(lch)); - - w = omap_readw(OMAP_DMA_CCR(lch)); - w &= ~OMAP_DMA_CCR_EN; - omap_writew(w, OMAP_DMA_CCR(lch)); - dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; -} - -void omap_enable_dma_irq(int lch, u16 bits) -{ - dma_chan[lch].enabled_irqs |= bits; -} - -void omap_disable_dma_irq(int lch, u16 bits) -{ - dma_chan[lch].enabled_irqs &= ~bits; -} - -static int dma_handle_ch(int ch) -{ - u16 csr; - - if (enable_1510_mode && ch >= 6) { - csr = dma_chan[ch].saved_csr; - dma_chan[ch].saved_csr = 0; - } else - csr = omap_readw(OMAP_DMA_CSR(ch)); - if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) { - dma_chan[ch + 6].saved_csr = csr >> 7; - csr &= 0x7f; - } - if (!csr) - return 0; - if (unlikely(dma_chan[ch].dev_id == -1)) { - printk(KERN_WARNING "Spurious interrupt from DMA channel %d (CSR %04x)\n", - ch, csr); - return 0; - } - if (unlikely(csr & OMAP_DMA_TOUT_IRQ)) - printk(KERN_WARNING "DMA timeout with device %d\n", dma_chan[ch].dev_id); - if (unlikely(csr & OMAP_DMA_DROP_IRQ)) - printk(KERN_WARNING "DMA synchronization event drop occurred with device %d\n", - dma_chan[ch].dev_id); - if (likely(csr & OMAP_DMA_BLOCK_IRQ)) - dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; - if (likely(dma_chan[ch].callback != NULL)) - dma_chan[ch].callback(ch, csr, dma_chan[ch].data); - return 1; -} - -static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) -{ - int ch = ((int) dev_id) - 1; - int handled = 0; - - for (;;) { - int handled_now = 0; - - handled_now += dma_handle_ch(ch); - if (enable_1510_mode && dma_chan[ch + 6].saved_csr) - handled_now += dma_handle_ch(ch + 6); - if (!handled_now) - break; - handled += handled_now; - } - - return handled ? IRQ_HANDLED : IRQ_NONE; -} - -int omap_request_dma(int dev_id, const char *dev_name, - void (* callback)(int lch, u16 ch_status, void *data), - void *data, int *dma_ch_out) -{ - int ch, free_ch = -1; - unsigned long flags; - struct omap_dma_lch *chan; - - spin_lock_irqsave(&dma_chan_lock, flags); - for (ch = 0; ch < dma_chan_count; ch++) { - if (free_ch == -1 && dma_chan[ch].dev_id == -1) { - free_ch = ch; - if (dev_id == 0) - break; - } - } - if (free_ch == -1) { - spin_unlock_irqrestore(&dma_chan_lock, flags); - return -EBUSY; - } - chan = dma_chan + free_ch; - chan->dev_id = dev_id; - clear_lch_regs(free_ch); - spin_unlock_irqrestore(&dma_chan_lock, flags); - - chan->dev_id = dev_id; - chan->dev_name = dev_name; - chan->callback = callback; - chan->data = data; - chan->enabled_irqs = OMAP_DMA_TOUT_IRQ | OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ; - - if (cpu_is_omap16xx()) { - /* If the sync device is set, configure it dynamically. */ - if (dev_id != 0) { - set_gdma_dev(free_ch + 1, dev_id); - dev_id = free_ch + 1; - } - /* Disable the 1510 compatibility mode and set the sync device - * id. */ - omap_writew(dev_id | (1 << 10), OMAP_DMA_CCR(free_ch)); - } else { - omap_writew(dev_id, OMAP_DMA_CCR(free_ch)); - } - *dma_ch_out = free_ch; - - return 0; -} - -void omap_free_dma(int ch) -{ - unsigned long flags; - - spin_lock_irqsave(&dma_chan_lock, flags); - if (dma_chan[ch].dev_id == -1) { - printk("omap_dma: trying to free nonallocated DMA channel %d\n", ch); - spin_unlock_irqrestore(&dma_chan_lock, flags); - return; - } - dma_chan[ch].dev_id = -1; - spin_unlock_irqrestore(&dma_chan_lock, flags); - - /* Disable all DMA interrupts for the channel. */ - omap_writew(0, OMAP_DMA_CICR(ch)); - /* Make sure the DMA transfer is stopped. */ - omap_writew(0, OMAP_DMA_CCR(ch)); -} - -int omap_dma_in_1510_mode(void) -{ - return enable_1510_mode; -} - -/* - * lch_queue DMA will start right after lch_head one is finished. - * For this DMA link to start, you still need to start (see omap_start_dma) - * the first one. That will fire up the entire queue. - */ -void omap_dma_link_lch (int lch_head, int lch_queue) -{ - if (omap_dma_in_1510_mode()) { - printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); - BUG(); - return; - } - - if ((dma_chan[lch_head].dev_id == -1) || - (dma_chan[lch_queue].dev_id == -1)) { - printk(KERN_ERR "omap_dma: trying to link non requested channels\n"); - dump_stack(); - } - - dma_chan[lch_head].next_lch = lch_queue; -} - -/* - * Once the DMA queue is stopped, we can destroy it. - */ -void omap_dma_unlink_lch (int lch_head, int lch_queue) -{ - if (omap_dma_in_1510_mode()) { - printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); - BUG(); - return; - } - - if (dma_chan[lch_head].next_lch != lch_queue || - dma_chan[lch_head].next_lch == -1) { - printk(KERN_ERR "omap_dma: trying to unlink non linked channels\n"); - dump_stack(); - } - - - if ((dma_chan[lch_head].flags & OMAP_DMA_ACTIVE) || - (dma_chan[lch_head].flags & OMAP_DMA_ACTIVE)) { - printk(KERN_ERR "omap_dma: You need to stop the DMA channels before unlinking\n"); - dump_stack(); - } - - dma_chan[lch_head].next_lch = -1; -} - - -static struct lcd_dma_info { - spinlock_t lock; - int reserved; - void (* callback)(u16 status, void *data); - void *cb_data; - - int active; - unsigned long addr, size; - int rotate, data_type, xres, yres; - int vxres; - int mirror; - int xscale, yscale; - int ext_ctrl; - int src_port; - int single_transfer; -} lcd_dma; - -void omap_set_lcd_dma_b1(unsigned long addr, u16 fb_xres, u16 fb_yres, - int data_type) -{ - lcd_dma.addr = addr; - lcd_dma.data_type = data_type; - lcd_dma.xres = fb_xres; - lcd_dma.yres = fb_yres; -} - -void omap_set_lcd_dma_src_port(int port) -{ - lcd_dma.src_port = port; -} - -void omap_set_lcd_dma_ext_controller(int external) -{ - lcd_dma.ext_ctrl = external; -} - -void omap_set_lcd_dma_single_transfer(int single) -{ - lcd_dma.single_transfer = single; -} - - -void omap_set_lcd_dma_b1_rotation(int rotate) -{ - if (omap_dma_in_1510_mode()) { - printk(KERN_ERR "DMA rotation is not supported in 1510 mode\n"); - BUG(); - return; - } - lcd_dma.rotate = rotate; -} - -void omap_set_lcd_dma_b1_mirror(int mirror) -{ - if (omap_dma_in_1510_mode()) { - printk(KERN_ERR "DMA mirror is not supported in 1510 mode\n"); - BUG(); - } - lcd_dma.mirror = mirror; -} - -void omap_set_lcd_dma_b1_vxres(unsigned long vxres) -{ - if (omap_dma_in_1510_mode()) { - printk(KERN_ERR "DMA virtual resulotion is not supported " - "in 1510 mode\n"); - BUG(); - } - lcd_dma.vxres = vxres; -} - -void omap_set_lcd_dma_b1_scale(unsigned int xscale, unsigned int yscale) -{ - if (omap_dma_in_1510_mode()) { - printk(KERN_ERR "DMA scale is not supported in 1510 mode\n"); - BUG(); - } - lcd_dma.xscale = xscale; - lcd_dma.yscale = yscale; -} - -static void set_b1_regs(void) -{ - unsigned long top, bottom; - int es; - u16 w; - unsigned long en, fn; - long ei, fi; - unsigned long vxres; - unsigned int xscale, yscale; - - switch (lcd_dma.data_type) { - case OMAP_DMA_DATA_TYPE_S8: - es = 1; - break; - case OMAP_DMA_DATA_TYPE_S16: - es = 2; - break; - case OMAP_DMA_DATA_TYPE_S32: - es = 4; - break; - default: - BUG(); - return; - } - - vxres = lcd_dma.vxres ? lcd_dma.vxres : lcd_dma.xres; - xscale = lcd_dma.xscale ? lcd_dma.xscale : 1; - yscale = lcd_dma.yscale ? lcd_dma.yscale : 1; - BUG_ON(vxres < lcd_dma.xres); -#define PIXADDR(x,y) (lcd_dma.addr + ((y) * vxres * yscale + (x) * xscale) * es) -#define PIXSTEP(sx, sy, dx, dy) (PIXADDR(dx, dy) - PIXADDR(sx, sy) - es + 1) - switch (lcd_dma.rotate) { - case 0: - if (!lcd_dma.mirror) { - top = PIXADDR(0, 0); - bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); - /* 1510 DMA requires the bottom address to be 2 more - * than the actual last memory access location. */ - if (omap_dma_in_1510_mode() && - lcd_dma.data_type == OMAP_DMA_DATA_TYPE_S32) - bottom += 2; - ei = PIXSTEP(0, 0, 1, 0); - fi = PIXSTEP(lcd_dma.xres - 1, 0, 0, 1); - } else { - top = PIXADDR(lcd_dma.xres - 1, 0); - bottom = PIXADDR(0, lcd_dma.yres - 1); - ei = PIXSTEP(1, 0, 0, 0); - fi = PIXSTEP(0, 0, lcd_dma.xres - 1, 1); - } - en = lcd_dma.xres; - fn = lcd_dma.yres; - break; - case 90: - if (!lcd_dma.mirror) { - top = PIXADDR(0, lcd_dma.yres - 1); - bottom = PIXADDR(lcd_dma.xres - 1, 0); - ei = PIXSTEP(0, 1, 0, 0); - fi = PIXSTEP(0, 0, 1, lcd_dma.yres - 1); - } else { - top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); - bottom = PIXADDR(0, 0); - ei = PIXSTEP(0, 1, 0, 0); - fi = PIXSTEP(1, 0, 0, lcd_dma.yres - 1); - } - en = lcd_dma.yres; - fn = lcd_dma.xres; - break; - case 180: - if (!lcd_dma.mirror) { - top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); - bottom = PIXADDR(0, 0); - ei = PIXSTEP(1, 0, 0, 0); - fi = PIXSTEP(0, 1, lcd_dma.xres - 1, 0); - } else { - top = PIXADDR(0, lcd_dma.yres - 1); - bottom = PIXADDR(lcd_dma.xres - 1, 0); - ei = PIXSTEP(0, 0, 1, 0); - fi = PIXSTEP(lcd_dma.xres - 1, 1, 0, 0); - } - en = lcd_dma.xres; - fn = lcd_dma.yres; - break; - case 270: - if (!lcd_dma.mirror) { - top = PIXADDR(lcd_dma.xres - 1, 0); - bottom = PIXADDR(0, lcd_dma.yres - 1); - ei = PIXSTEP(0, 0, 0, 1); - fi = PIXSTEP(1, lcd_dma.yres - 1, 0, 0); - } else { - top = PIXADDR(0, 0); - bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); - ei = PIXSTEP(0, 0, 0, 1); - fi = PIXSTEP(0, lcd_dma.yres - 1, 1, 0); - } - en = lcd_dma.yres; - fn = lcd_dma.xres; - break; - default: - BUG(); - return; /* Supress warning about uninitialized vars */ - } - - if (omap_dma_in_1510_mode()) { - omap_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U); - omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L); - omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U); - omap_writew(bottom, OMAP1510_DMA_LCD_BOT_F1_L); - - return; - } - - /* 1610 regs */ - omap_writew(top >> 16, OMAP1610_DMA_LCD_TOP_B1_U); - omap_writew(top, OMAP1610_DMA_LCD_TOP_B1_L); - omap_writew(bottom >> 16, OMAP1610_DMA_LCD_BOT_B1_U); - omap_writew(bottom, OMAP1610_DMA_LCD_BOT_B1_L); - - omap_writew(en, OMAP1610_DMA_LCD_SRC_EN_B1); - omap_writew(fn, OMAP1610_DMA_LCD_SRC_FN_B1); - - w = omap_readw(OMAP1610_DMA_LCD_CSDP); - w &= ~0x03; - w |= lcd_dma.data_type; - omap_writew(w, OMAP1610_DMA_LCD_CSDP); - - w = omap_readw(OMAP1610_DMA_LCD_CTRL); - /* Always set the source port as SDRAM for now*/ - w &= ~(0x03 << 6); - if (lcd_dma.ext_ctrl) - w |= 1 << 8; - else - w &= ~(1 << 8); - if (lcd_dma.callback != NULL) - w |= 1 << 1; /* Block interrupt enable */ - else - w &= ~(1 << 1); - omap_writew(w, OMAP1610_DMA_LCD_CTRL); - - if (!(lcd_dma.rotate || lcd_dma.mirror || - lcd_dma.vxres || lcd_dma.xscale || lcd_dma.yscale)) - return; - - w = omap_readw(OMAP1610_DMA_LCD_CCR); - /* Set the double-indexed addressing mode */ - w |= (0x03 << 12); - omap_writew(w, OMAP1610_DMA_LCD_CCR); - - omap_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1); - omap_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U); - omap_writew(fi, OMAP1610_DMA_LCD_SRC_FI_B1_L); -} - -static irqreturn_t lcd_dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) -{ - u16 w; - - w = omap_readw(OMAP1610_DMA_LCD_CTRL); - if (unlikely(!(w & (1 << 3)))) { - printk(KERN_WARNING "Spurious LCD DMA IRQ\n"); - return IRQ_NONE; - } - /* Ack the IRQ */ - w |= (1 << 3); - omap_writew(w, OMAP1610_DMA_LCD_CTRL); - lcd_dma.active = 0; - if (lcd_dma.callback != NULL) - lcd_dma.callback(w, lcd_dma.cb_data); - - return IRQ_HANDLED; -} - -int omap_request_lcd_dma(void (* callback)(u16 status, void *data), - void *data) -{ - spin_lock_irq(&lcd_dma.lock); - if (lcd_dma.reserved) { - spin_unlock_irq(&lcd_dma.lock); - printk(KERN_ERR "LCD DMA channel already reserved\n"); - BUG(); - return -EBUSY; - } - lcd_dma.reserved = 1; - spin_unlock_irq(&lcd_dma.lock); - lcd_dma.callback = callback; - lcd_dma.cb_data = data; - lcd_dma.active = 0; - lcd_dma.single_transfer = 0; - lcd_dma.rotate = 0; - lcd_dma.vxres = 0; - lcd_dma.mirror = 0; - lcd_dma.xscale = 0; - lcd_dma.yscale = 0; - lcd_dma.ext_ctrl = 0; - lcd_dma.src_port = 0; - - return 0; -} - -void omap_free_lcd_dma(void) -{ - spin_lock(&lcd_dma.lock); - if (!lcd_dma.reserved) { - spin_unlock(&lcd_dma.lock); - printk(KERN_ERR "LCD DMA is not reserved\n"); - BUG(); - return; - } - if (!enable_1510_mode) - omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1, OMAP1610_DMA_LCD_CCR); - lcd_dma.reserved = 0; - spin_unlock(&lcd_dma.lock); -} - -void omap_enable_lcd_dma(void) -{ - u16 w; - - /* Set the Enable bit only if an external controller is - * connected. Otherwise the OMAP internal controller will - * start the transfer when it gets enabled. - */ - if (enable_1510_mode || !lcd_dma.ext_ctrl) - return; - w = omap_readw(OMAP1610_DMA_LCD_CCR); - w |= 1 << 7; - omap_writew(w, OMAP1610_DMA_LCD_CCR); - lcd_dma.active = 1; -} - -void omap_setup_lcd_dma(void) -{ - BUG_ON(lcd_dma.active); - if (!enable_1510_mode) { - /* Set some reasonable defaults */ - omap_writew(0x5440, OMAP1610_DMA_LCD_CCR); - omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP); - omap_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL); - } - set_b1_regs(); - if (!enable_1510_mode) { - u16 w; - - w = omap_readw(OMAP1610_DMA_LCD_CCR); - /* If DMA was already active set the end_prog bit to have - * the programmed register set loaded into the active - * register set. - */ - w |= 1 << 11; /* End_prog */ - if (!lcd_dma.single_transfer) - w |= (3 << 8); /* Auto_init, repeat */ - omap_writew(w, OMAP1610_DMA_LCD_CCR); - } -} - -void omap_stop_lcd_dma(void) -{ - lcd_dma.active = 0; - if (!enable_1510_mode && lcd_dma.ext_ctrl) - omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~(1 << 7), - OMAP1610_DMA_LCD_CCR); -} - -/* - * Clears any DMA state so the DMA engine is ready to restart with new buffers - * through omap_start_dma(). Any buffers in flight are discarded. - */ -void omap_clear_dma(int lch) -{ - unsigned long flags; - int status; - - local_irq_save(flags); - omap_writew(omap_readw(OMAP_DMA_CCR(lch)) & ~OMAP_DMA_CCR_EN, - OMAP_DMA_CCR(lch)); - status = OMAP_DMA_CSR(lch); /* clear pending interrupts */ - local_irq_restore(flags); -} - -/* - * Returns current physical source address for the given DMA channel. - * If the channel is running the caller must disable interrupts prior calling - * this function and process the returned value before re-enabling interrupt to - * prevent races with the interrupt handler. Note that in continuous mode there - * is a chance for CSSA_L register overflow inbetween the two reads resulting - * in incorrect return value. - */ -dma_addr_t omap_get_dma_src_pos(int lch) -{ - return (dma_addr_t) (OMAP_DMA_CSSA_L(lch) | - (OMAP_DMA_CSSA_U(lch) << 16)); -} - -/* - * Returns current physical destination address for the given DMA channel. - * If the channel is running the caller must disable interrupts prior calling - * this function and process the returned value before re-enabling interrupt to - * prevent races with the interrupt handler. Note that in continuous mode there - * is a chance for CDSA_L register overflow inbetween the two reads resulting - * in incorrect return value. - */ -dma_addr_t omap_get_dma_dst_pos(int lch) -{ - return (dma_addr_t) (OMAP_DMA_CDSA_L(lch) | - (OMAP_DMA_CDSA_U(lch) << 16)); -} - -static int __init omap_init_dma(void) -{ - int ch, r; - - if (cpu_is_omap1510()) { - printk(KERN_INFO "DMA support for OMAP1510 initialized\n"); - dma_chan_count = 9; - enable_1510_mode = 1; - } else if (cpu_is_omap16xx() || cpu_is_omap730()) { - printk(KERN_INFO "OMAP DMA hardware version %d\n", - omap_readw(OMAP_DMA_HW_ID)); - printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n", - (omap_readw(OMAP_DMA_CAPS_0_U) << 16) | omap_readw(OMAP_DMA_CAPS_0_L), - (omap_readw(OMAP_DMA_CAPS_1_U) << 16) | omap_readw(OMAP_DMA_CAPS_1_L), - omap_readw(OMAP_DMA_CAPS_2), omap_readw(OMAP_DMA_CAPS_3), - omap_readw(OMAP_DMA_CAPS_4)); - if (!enable_1510_mode) { - u16 w; - - /* Disable OMAP 3.0/3.1 compatibility mode. */ - w = omap_readw(OMAP_DMA_GSCR); - w |= 1 << 3; - omap_writew(w, OMAP_DMA_GSCR); - dma_chan_count = 16; - } else - dma_chan_count = 9; - } else { - dma_chan_count = 0; - return 0; - } - - memset(&lcd_dma, 0, sizeof(lcd_dma)); - spin_lock_init(&lcd_dma.lock); - spin_lock_init(&dma_chan_lock); - memset(&dma_chan, 0, sizeof(dma_chan)); - - for (ch = 0; ch < dma_chan_count; ch++) { - dma_chan[ch].dev_id = -1; - dma_chan[ch].next_lch = -1; - - if (ch >= 6 && enable_1510_mode) - continue; - - /* request_irq() doesn't like dev_id (ie. ch) being zero, - * so we have to kludge around this. */ - r = request_irq(dma_irq[ch], dma_irq_handler, 0, "DMA", - (void *) (ch + 1)); - if (r != 0) { - int i; - - printk(KERN_ERR "unable to request IRQ %d for DMA (error %d)\n", - dma_irq[ch], r); - for (i = 0; i < ch; i++) - free_irq(dma_irq[i], (void *) (i + 1)); - return r; - } - } - r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0, "LCD DMA", NULL); - if (r != 0) { - int i; - - printk(KERN_ERR "unable to request IRQ for LCD DMA (error %d)\n", r); - for (i = 0; i < dma_chan_count; i++) - free_irq(dma_irq[i], (void *) (i + 1)); - return r; - } - return 0; -} - -arch_initcall(omap_init_dma); - - -EXPORT_SYMBOL(omap_get_dma_src_pos); -EXPORT_SYMBOL(omap_get_dma_dst_pos); -EXPORT_SYMBOL(omap_clear_dma); -EXPORT_SYMBOL(omap_set_dma_priority); -EXPORT_SYMBOL(omap_request_dma); -EXPORT_SYMBOL(omap_free_dma); -EXPORT_SYMBOL(omap_start_dma); -EXPORT_SYMBOL(omap_stop_dma); -EXPORT_SYMBOL(omap_enable_dma_irq); -EXPORT_SYMBOL(omap_disable_dma_irq); - -EXPORT_SYMBOL(omap_set_dma_transfer_params); -EXPORT_SYMBOL(omap_set_dma_color_mode); - -EXPORT_SYMBOL(omap_set_dma_src_params); -EXPORT_SYMBOL(omap_set_dma_src_index); -EXPORT_SYMBOL(omap_set_dma_src_data_pack); -EXPORT_SYMBOL(omap_set_dma_src_burst_mode); - -EXPORT_SYMBOL(omap_set_dma_dest_params); -EXPORT_SYMBOL(omap_set_dma_dest_index); -EXPORT_SYMBOL(omap_set_dma_dest_data_pack); -EXPORT_SYMBOL(omap_set_dma_dest_burst_mode); - -EXPORT_SYMBOL(omap_dma_link_lch); -EXPORT_SYMBOL(omap_dma_unlink_lch); - -EXPORT_SYMBOL(omap_request_lcd_dma); -EXPORT_SYMBOL(omap_free_lcd_dma); -EXPORT_SYMBOL(omap_enable_lcd_dma); -EXPORT_SYMBOL(omap_setup_lcd_dma); -EXPORT_SYMBOL(omap_stop_lcd_dma); -EXPORT_SYMBOL(omap_set_lcd_dma_b1); -EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer); -EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller); -EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation); -EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres); -EXPORT_SYMBOL(omap_set_lcd_dma_b1_scale); -EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror); - --- linux-2.6.13-rc2/arch/arm/mach-omap/fpga.c 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,188 +0,0 @@ -/* - * linux/arch/arm/mach-omap/fpga.c - * - * Interrupt handler for OMAP-1510 Innovator FPGA - * - * Copyright (C) 2001 RidgeRun, Inc. - * Author: Greg Lonnon - * - * Copyright (C) 2002 MontaVista Software, Inc. - * - * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 - * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -static void fpga_mask_irq(unsigned int irq) -{ - irq -= OMAP1510_IH_FPGA_BASE; - - if (irq < 8) - __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) - & ~(1 << irq)), OMAP1510_FPGA_IMR_LO); - else if (irq < 16) - __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) - & ~(1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); - else - __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) - & ~(1 << (irq - 16))), INNOVATOR_FPGA_IMR2); -} - - -static inline u32 get_fpga_unmasked_irqs(void) -{ - return - ((__raw_readb(OMAP1510_FPGA_ISR_LO) & - __raw_readb(OMAP1510_FPGA_IMR_LO))) | - ((__raw_readb(OMAP1510_FPGA_ISR_HI) & - __raw_readb(OMAP1510_FPGA_IMR_HI)) << 8) | - ((__raw_readb(INNOVATOR_FPGA_ISR2) & - __raw_readb(INNOVATOR_FPGA_IMR2)) << 16); -} - - -static void fpga_ack_irq(unsigned int irq) -{ - /* Don't need to explicitly ACK FPGA interrupts */ -} - -static void fpga_unmask_irq(unsigned int irq) -{ - irq -= OMAP1510_IH_FPGA_BASE; - - if (irq < 8) - __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | (1 << irq)), - OMAP1510_FPGA_IMR_LO); - else if (irq < 16) - __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI) - | (1 << (irq - 8))), OMAP1510_FPGA_IMR_HI); - else - __raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2) - | (1 << (irq - 16))), INNOVATOR_FPGA_IMR2); -} - -static void fpga_mask_ack_irq(unsigned int irq) -{ - fpga_mask_irq(irq); - fpga_ack_irq(irq); -} - -void innovator_fpga_IRQ_demux(unsigned int irq, struct irqdesc *desc, - struct pt_regs *regs) -{ - struct irqdesc *d; - u32 stat; - int fpga_irq; - - stat = get_fpga_unmasked_irqs(); - - if (!stat) - return; - - for (fpga_irq = OMAP1510_IH_FPGA_BASE; - (fpga_irq < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS)) && stat; - fpga_irq++, stat >>= 1) { - if (stat & 1) { - d = irq_desc + fpga_irq; - d->handle(fpga_irq, d, regs); - } - } -} - -static struct irqchip omap_fpga_irq_ack = { - .ack = fpga_mask_ack_irq, - .mask = fpga_mask_irq, - .unmask = fpga_unmask_irq, -}; - - -static struct irqchip omap_fpga_irq = { - .ack = fpga_ack_irq, - .mask = fpga_mask_irq, - .unmask = fpga_unmask_irq, -}; - -/* - * All of the FPGA interrupt request inputs except for the touchscreen are - * edge-sensitive; the touchscreen is level-sensitive. The edge-sensitive - * interrupts are acknowledged as a side-effect of reading the interrupt - * status register from the FPGA. The edge-sensitive interrupt inputs - * cause a problem with level interrupt requests, such as Ethernet. The - * problem occurs when a level interrupt request is asserted while its - * interrupt input is masked in the FPGA, which results in a missed - * interrupt. - * - * In an attempt to workaround the problem with missed interrupts, the - * mask_ack routine for all of the FPGA interrupts has been changed from - * fpga_mask_ack_irq() to fpga_ack_irq() so that the specific FPGA interrupt - * being serviced is left unmasked. We can do this because the FPGA cascade - * interrupt is installed with the SA_INTERRUPT flag, which leaves all - * interrupts masked at the CPU while an FPGA interrupt handler executes. - * - * Limited testing indicates that this workaround appears to be effective - * for the smc9194 Ethernet driver used on the Innovator. It should work - * on other FPGA interrupts as well, but any drivers that explicitly mask - * interrupts at the interrupt controller via disable_irq/enable_irq - * could pose a problem. - */ -void omap1510_fpga_init_irq(void) -{ - int i; - - __raw_writeb(0, OMAP1510_FPGA_IMR_LO); - __raw_writeb(0, OMAP1510_FPGA_IMR_HI); - __raw_writeb(0, INNOVATOR_FPGA_IMR2); - - for (i = OMAP1510_IH_FPGA_BASE; i < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS); i++) { - - if (i == OMAP1510_INT_FPGA_TS) { - /* - * The touchscreen interrupt is level-sensitive, so - * we'll use the regular mask_ack routine for it. - */ - set_irq_chip(i, &omap_fpga_irq_ack); - } - else { - /* - * All FPGA interrupts except the touchscreen are - * edge-sensitive, so we won't mask them. - */ - set_irq_chip(i, &omap_fpga_irq); - } - - set_irq_handler(i, do_edge_IRQ); - set_irq_flags(i, IRQF_VALID); - } - - /* - * The FPGA interrupt line is connected to GPIO13. Claim this pin for - * the ARM. - * - * NOTE: For general GPIO/MPUIO access and interrupts, please see - * gpio.[ch] - */ - omap_request_gpio(13); - omap_set_gpio_direction(13, 1); - omap_set_gpio_edge_ctrl(13, OMAP_GPIO_RISING_EDGE); - set_irq_chained_handler(OMAP1510_INT_FPGA, innovator_fpga_IRQ_demux); -} - -EXPORT_SYMBOL(omap1510_fpga_init_irq); --- linux-2.6.13-rc2/arch/arm/mach-omap/gpio.c 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,762 +0,0 @@ -/* - * linux/arch/arm/mach-omap/gpio.c - * - * Support functions for OMAP GPIO - * - * Copyright (C) 2003 Nokia Corporation - * Written by Juha Yrjölä - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -/* - * OMAP1510 GPIO registers - */ -#define OMAP1510_GPIO_BASE 0xfffce000 -#define OMAP1510_GPIO_DATA_INPUT 0x00 -#define OMAP1510_GPIO_DATA_OUTPUT 0x04 -#define OMAP1510_GPIO_DIR_CONTROL 0x08 -#define OMAP1510_GPIO_INT_CONTROL 0x0c -#define OMAP1510_GPIO_INT_MASK 0x10 -#define OMAP1510_GPIO_INT_STATUS 0x14 -#define OMAP1510_GPIO_PIN_CONTROL 0x18 - -#define OMAP1510_IH_GPIO_BASE 64 - -/* - * OMAP1610 specific GPIO registers - */ -#define OMAP1610_GPIO1_BASE 0xfffbe400 -#define OMAP1610_GPIO2_BASE 0xfffbec00 -#define OMAP1610_GPIO3_BASE 0xfffbb400 -#define OMAP1610_GPIO4_BASE 0xfffbbc00 -#define OMAP1610_GPIO_REVISION 0x0000 -#define OMAP1610_GPIO_SYSCONFIG 0x0010 -#define OMAP1610_GPIO_SYSSTATUS 0x0014 -#define OMAP1610_GPIO_IRQSTATUS1 0x0018 -#define OMAP1610_GPIO_IRQENABLE1 0x001c -#define OMAP1610_GPIO_DATAIN 0x002c -#define OMAP1610_GPIO_DATAOUT 0x0030 -#define OMAP1610_GPIO_DIRECTION 0x0034 -#define OMAP1610_GPIO_EDGE_CTRL1 0x0038 -#define OMAP1610_GPIO_EDGE_CTRL2 0x003c -#define OMAP1610_GPIO_CLEAR_IRQENABLE1 0x009c -#define OMAP1610_GPIO_CLEAR_DATAOUT 0x00b0 -#define OMAP1610_GPIO_SET_IRQENABLE1 0x00dc -#define OMAP1610_GPIO_SET_DATAOUT 0x00f0 - -/* - * OMAP730 specific GPIO registers - */ -#define OMAP730_GPIO1_BASE 0xfffbc000 -#define OMAP730_GPIO2_BASE 0xfffbc800 -#define OMAP730_GPIO3_BASE 0xfffbd000 -#define OMAP730_GPIO4_BASE 0xfffbd800 -#define OMAP730_GPIO5_BASE 0xfffbe000 -#define OMAP730_GPIO6_BASE 0xfffbe800 -#define OMAP730_GPIO_DATA_INPUT 0x00 -#define OMAP730_GPIO_DATA_OUTPUT 0x04 -#define OMAP730_GPIO_DIR_CONTROL 0x08 -#define OMAP730_GPIO_INT_CONTROL 0x0c -#define OMAP730_GPIO_INT_MASK 0x10 -#define OMAP730_GPIO_INT_STATUS 0x14 - -#define OMAP_MPUIO_MASK (~OMAP_MAX_GPIO_LINES & 0xff) - -struct gpio_bank { - u32 base; - u16 irq; - u16 virtual_irq_start; - u8 method; - u32 reserved_map; - spinlock_t lock; -}; - -#define METHOD_MPUIO 0 -#define METHOD_GPIO_1510 1 -#define METHOD_GPIO_1610 2 -#define METHOD_GPIO_730 3 - -#if defined(CONFIG_ARCH_OMAP16XX) -static struct gpio_bank gpio_bank_1610[5] = { - { OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO}, - { OMAP1610_GPIO1_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1610 }, - { OMAP1610_GPIO2_BASE, INT_1610_GPIO_BANK2, IH_GPIO_BASE + 16, METHOD_GPIO_1610 }, - { OMAP1610_GPIO3_BASE, INT_1610_GPIO_BANK3, IH_GPIO_BASE + 32, METHOD_GPIO_1610 }, - { OMAP1610_GPIO4_BASE, INT_1610_GPIO_BANK4, IH_GPIO_BASE + 48, METHOD_GPIO_1610 }, -}; -#endif - -#ifdef CONFIG_ARCH_OMAP1510 -static struct gpio_bank gpio_bank_1510[2] = { - { OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO }, - { OMAP1510_GPIO_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1510 } -}; -#endif - -#ifdef CONFIG_ARCH_OMAP730 -static struct gpio_bank gpio_bank_730[7] = { - { OMAP_MPUIO_BASE, INT_730_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO }, - { OMAP730_GPIO1_BASE, INT_730_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_730 }, - { OMAP730_GPIO2_BASE, INT_730_GPIO_BANK2, IH_GPIO_BASE + 32, METHOD_GPIO_730 }, - { OMAP730_GPIO3_BASE, INT_730_GPIO_BANK3, IH_GPIO_BASE + 64, METHOD_GPIO_730 }, - { OMAP730_GPIO4_BASE, INT_730_GPIO_BANK4, IH_GPIO_BASE + 96, METHOD_GPIO_730 }, - { OMAP730_GPIO5_BASE, INT_730_GPIO_BANK5, IH_GPIO_BASE + 128, METHOD_GPIO_730 }, - { OMAP730_GPIO6_BASE, INT_730_GPIO_BANK6, IH_GPIO_BASE + 160, METHOD_GPIO_730 }, -}; -#endif - -static struct gpio_bank *gpio_bank; -static int gpio_bank_count; - -static inline struct gpio_bank *get_gpio_bank(int gpio) -{ -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - if (OMAP_GPIO_IS_MPUIO(gpio)) - return &gpio_bank[0]; - return &gpio_bank[1]; - } -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (cpu_is_omap16xx()) { - if (OMAP_GPIO_IS_MPUIO(gpio)) - return &gpio_bank[0]; - return &gpio_bank[1 + (gpio >> 4)]; - } -#endif -#ifdef CONFIG_ARCH_OMAP730 - if (cpu_is_omap730()) { - if (OMAP_GPIO_IS_MPUIO(gpio)) - return &gpio_bank[0]; - return &gpio_bank[1 + (gpio >> 5)]; - } -#endif -} - -static inline int get_gpio_index(int gpio) -{ - if (cpu_is_omap730()) - return gpio & 0x1f; - else - return gpio & 0x0f; -} - -static inline int gpio_valid(int gpio) -{ - if (gpio < 0) - return -1; - if (OMAP_GPIO_IS_MPUIO(gpio)) { - if ((gpio & OMAP_MPUIO_MASK) > 16) - return -1; - return 0; - } -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510() && gpio < 16) - return 0; -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if ((cpu_is_omap16xx()) && gpio < 64) - return 0; -#endif -#ifdef CONFIG_ARCH_OMAP730 - if (cpu_is_omap730() && gpio < 192) - return 0; -#endif - return -1; -} - -static int check_gpio(int gpio) -{ - if (unlikely(gpio_valid(gpio)) < 0) { - printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio); - dump_stack(); - return -1; - } - return 0; -} - -static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) -{ - u32 reg = bank->base; - u32 l; - - switch (bank->method) { - case METHOD_MPUIO: - reg += OMAP_MPUIO_IO_CNTL; - break; - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_DIR_CONTROL; - break; - case METHOD_GPIO_1610: - reg += OMAP1610_GPIO_DIRECTION; - break; - case METHOD_GPIO_730: - reg += OMAP730_GPIO_DIR_CONTROL; - break; - } - l = __raw_readl(reg); - if (is_input) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - __raw_writel(l, reg); -} - -void omap_set_gpio_direction(int gpio, int is_input) -{ - struct gpio_bank *bank; - - if (check_gpio(gpio) < 0) - return; - bank = get_gpio_bank(gpio); - spin_lock(&bank->lock); - _set_gpio_direction(bank, get_gpio_index(gpio), is_input); - spin_unlock(&bank->lock); -} - -static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable) -{ - u32 reg = bank->base; - u32 l = 0; - - switch (bank->method) { - case METHOD_MPUIO: - reg += OMAP_MPUIO_OUTPUT; - l = __raw_readl(reg); - if (enable) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - break; - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_DATA_OUTPUT; - l = __raw_readl(reg); - if (enable) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - break; - case METHOD_GPIO_1610: - if (enable) - reg += OMAP1610_GPIO_SET_DATAOUT; - else - reg += OMAP1610_GPIO_CLEAR_DATAOUT; - l = 1 << gpio; - break; - case METHOD_GPIO_730: - reg += OMAP730_GPIO_DATA_OUTPUT; - l = __raw_readl(reg); - if (enable) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - break; - default: - BUG(); - return; - } - __raw_writel(l, reg); -} - -void omap_set_gpio_dataout(int gpio, int enable) -{ - struct gpio_bank *bank; - - if (check_gpio(gpio) < 0) - return; - bank = get_gpio_bank(gpio); - spin_lock(&bank->lock); - _set_gpio_dataout(bank, get_gpio_index(gpio), enable); - spin_unlock(&bank->lock); -} - -int omap_get_gpio_datain(int gpio) -{ - struct gpio_bank *bank; - u32 reg; - - if (check_gpio(gpio) < 0) - return -1; - bank = get_gpio_bank(gpio); - reg = bank->base; - switch (bank->method) { - case METHOD_MPUIO: - reg += OMAP_MPUIO_INPUT_LATCH; - break; - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_DATA_INPUT; - break; - case METHOD_GPIO_1610: - reg += OMAP1610_GPIO_DATAIN; - break; - case METHOD_GPIO_730: - reg += OMAP730_GPIO_DATA_INPUT; - break; - default: - BUG(); - return -1; - } - return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0; -} - -static void _set_gpio_edge_ctrl(struct gpio_bank *bank, int gpio, int edge) -{ - u32 reg = bank->base; - u32 l; - - switch (bank->method) { - case METHOD_MPUIO: - reg += OMAP_MPUIO_GPIO_INT_EDGE; - l = __raw_readl(reg); - if (edge == OMAP_GPIO_RISING_EDGE) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - __raw_writel(l, reg); - break; - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_CONTROL; - l = __raw_readl(reg); - if (edge == OMAP_GPIO_RISING_EDGE) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - __raw_writel(l, reg); - break; - case METHOD_GPIO_1610: - edge &= 0x03; - if (gpio & 0x08) - reg += OMAP1610_GPIO_EDGE_CTRL2; - else - reg += OMAP1610_GPIO_EDGE_CTRL1; - gpio &= 0x07; - l = __raw_readl(reg); - l &= ~(3 << (gpio << 1)); - l |= edge << (gpio << 1); - __raw_writel(l, reg); - break; - case METHOD_GPIO_730: - reg += OMAP730_GPIO_INT_CONTROL; - l = __raw_readl(reg); - if (edge == OMAP_GPIO_RISING_EDGE) - l |= 1 << gpio; - else - l &= ~(1 << gpio); - __raw_writel(l, reg); - break; - default: - BUG(); - return; - } -} - -void omap_set_gpio_edge_ctrl(int gpio, int edge) -{ - struct gpio_bank *bank; - - if (check_gpio(gpio) < 0) - return; - bank = get_gpio_bank(gpio); - spin_lock(&bank->lock); - _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), edge); - spin_unlock(&bank->lock); -} - - -static int _get_gpio_edge_ctrl(struct gpio_bank *bank, int gpio) -{ - u32 reg = bank->base, l; - - switch (bank->method) { - case METHOD_MPUIO: - l = __raw_readl(reg + OMAP_MPUIO_GPIO_INT_EDGE); - return (l & (1 << gpio)) ? - OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; - case METHOD_GPIO_1510: - l = __raw_readl(reg + OMAP1510_GPIO_INT_CONTROL); - return (l & (1 << gpio)) ? - OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; - case METHOD_GPIO_1610: - if (gpio & 0x08) - reg += OMAP1610_GPIO_EDGE_CTRL2; - else - reg += OMAP1610_GPIO_EDGE_CTRL1; - return (__raw_readl(reg) >> ((gpio & 0x07) << 1)) & 0x03; - case METHOD_GPIO_730: - l = __raw_readl(reg + OMAP730_GPIO_INT_CONTROL); - return (l & (1 << gpio)) ? - OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; - default: - BUG(); - return -1; - } -} - -static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) -{ - u32 reg = bank->base; - - switch (bank->method) { - case METHOD_MPUIO: - /* MPUIO irqstatus is reset by reading the status register, - * so do nothing here */ - return; - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_STATUS; - break; - case METHOD_GPIO_1610: - reg += OMAP1610_GPIO_IRQSTATUS1; - break; - case METHOD_GPIO_730: - reg += OMAP730_GPIO_INT_STATUS; - break; - default: - BUG(); - return; - } - __raw_writel(gpio_mask, reg); -} - -static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio) -{ - _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio)); -} - -static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable) -{ - u32 reg = bank->base; - u32 l; - - switch (bank->method) { - case METHOD_MPUIO: - reg += OMAP_MPUIO_GPIO_MASKIT; - l = __raw_readl(reg); - if (enable) - l &= ~(gpio_mask); - else - l |= gpio_mask; - break; - case METHOD_GPIO_1510: - reg += OMAP1510_GPIO_INT_MASK; - l = __raw_readl(reg); - if (enable) - l &= ~(gpio_mask); - else - l |= gpio_mask; - break; - case METHOD_GPIO_1610: - if (enable) - reg += OMAP1610_GPIO_SET_IRQENABLE1; - else - reg += OMAP1610_GPIO_CLEAR_IRQENABLE1; - l = gpio_mask; - break; - case METHOD_GPIO_730: - reg += OMAP730_GPIO_INT_MASK; - l = __raw_readl(reg); - if (enable) - l &= ~(gpio_mask); - else - l |= gpio_mask; - break; - default: - BUG(); - return; - } - __raw_writel(l, reg); -} - -static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable) -{ - _enable_gpio_irqbank(bank, 1 << get_gpio_index(gpio), enable); -} - -int omap_request_gpio(int gpio) -{ - struct gpio_bank *bank; - - if (check_gpio(gpio) < 0) - return -EINVAL; - - bank = get_gpio_bank(gpio); - spin_lock(&bank->lock); - if (unlikely(bank->reserved_map & (1 << get_gpio_index(gpio)))) { - printk(KERN_ERR "omap-gpio: GPIO %d is already reserved!\n", gpio); - dump_stack(); - spin_unlock(&bank->lock); - return -1; - } - bank->reserved_map |= (1 << get_gpio_index(gpio)); -#ifdef CONFIG_ARCH_OMAP1510 - if (bank->method == METHOD_GPIO_1510) { - u32 reg; - - /* Claim the pin for the ARM */ - reg = bank->base + OMAP1510_GPIO_PIN_CONTROL; - __raw_writel(__raw_readl(reg) | (1 << get_gpio_index(gpio)), reg); - } -#endif - spin_unlock(&bank->lock); - - return 0; -} - -void omap_free_gpio(int gpio) -{ - struct gpio_bank *bank; - - if (check_gpio(gpio) < 0) - return; - bank = get_gpio_bank(gpio); - spin_lock(&bank->lock); - if (unlikely(!(bank->reserved_map & (1 << get_gpio_index(gpio))))) { - printk(KERN_ERR "omap-gpio: GPIO %d wasn't reserved!\n", gpio); - dump_stack(); - spin_unlock(&bank->lock); - return; - } - bank->reserved_map &= ~(1 << get_gpio_index(gpio)); - _set_gpio_direction(bank, get_gpio_index(gpio), 1); - _set_gpio_irqenable(bank, gpio, 0); - _clear_gpio_irqstatus(bank, gpio); - spin_unlock(&bank->lock); -} - -/* - * We need to unmask the GPIO bank interrupt as soon as possible to - * avoid missing GPIO interrupts for other lines in the bank. - * Then we need to mask-read-clear-unmask the triggered GPIO lines - * in the bank to avoid missing nested interrupts for a GPIO line. - * If we wait to unmask individual GPIO lines in the bank after the - * line's interrupt handler has been run, we may miss some nested - * interrupts. - */ -static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc, - struct pt_regs *regs) -{ - u32 isr_reg = 0; - u32 isr; - unsigned int gpio_irq; - struct gpio_bank *bank; - - desc->chip->ack(irq); - - bank = (struct gpio_bank *) desc->data; - if (bank->method == METHOD_MPUIO) - isr_reg = bank->base + OMAP_MPUIO_GPIO_INT; -#ifdef CONFIG_ARCH_OMAP1510 - if (bank->method == METHOD_GPIO_1510) - isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS; -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (bank->method == METHOD_GPIO_1610) - isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1; -#endif -#ifdef CONFIG_ARCH_OMAP730 - if (bank->method == METHOD_GPIO_730) - isr_reg = bank->base + OMAP730_GPIO_INT_STATUS; -#endif - - isr = __raw_readl(isr_reg); - _enable_gpio_irqbank(bank, isr, 0); - _clear_gpio_irqbank(bank, isr); - _enable_gpio_irqbank(bank, isr, 1); - desc->chip->unmask(irq); - - if (unlikely(!isr)) - return; - - gpio_irq = bank->virtual_irq_start; - for (; isr != 0; isr >>= 1, gpio_irq++) { - struct irqdesc *d; - if (!(isr & 1)) - continue; - d = irq_desc + gpio_irq; - d->handle(gpio_irq, d, regs); - } -} - -static void gpio_ack_irq(unsigned int irq) -{ - unsigned int gpio = irq - IH_GPIO_BASE; - struct gpio_bank *bank = get_gpio_bank(gpio); - - _clear_gpio_irqstatus(bank, gpio); -} - -static void gpio_mask_irq(unsigned int irq) -{ - unsigned int gpio = irq - IH_GPIO_BASE; - struct gpio_bank *bank = get_gpio_bank(gpio); - - _set_gpio_irqenable(bank, gpio, 0); -} - -static void gpio_unmask_irq(unsigned int irq) -{ - unsigned int gpio = irq - IH_GPIO_BASE; - struct gpio_bank *bank = get_gpio_bank(gpio); - - if (_get_gpio_edge_ctrl(bank, get_gpio_index(gpio)) == OMAP_GPIO_NO_EDGE) { - printk(KERN_ERR "OMAP GPIO %d: trying to enable GPIO IRQ while no edge is set\n", - gpio); - _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), OMAP_GPIO_RISING_EDGE); - } - _set_gpio_irqenable(bank, gpio, 1); -} - -static void mpuio_ack_irq(unsigned int irq) -{ - /* The ISR is reset automatically, so do nothing here. */ -} - -static void mpuio_mask_irq(unsigned int irq) -{ - unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE); - struct gpio_bank *bank = get_gpio_bank(gpio); - - _set_gpio_irqenable(bank, gpio, 0); -} - -static void mpuio_unmask_irq(unsigned int irq) -{ - unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE); - struct gpio_bank *bank = get_gpio_bank(gpio); - - _set_gpio_irqenable(bank, gpio, 1); -} - -static struct irqchip gpio_irq_chip = { - .ack = gpio_ack_irq, - .mask = gpio_mask_irq, - .unmask = gpio_unmask_irq, -}; - -static struct irqchip mpuio_irq_chip = { - .ack = mpuio_ack_irq, - .mask = mpuio_mask_irq, - .unmask = mpuio_unmask_irq -}; - -static int initialized = 0; - -static int __init _omap_gpio_init(void) -{ - int i; - struct gpio_bank *bank; - - initialized = 1; - -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - printk(KERN_INFO "OMAP1510 GPIO hardware\n"); - gpio_bank_count = 2; - gpio_bank = gpio_bank_1510; - } -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (cpu_is_omap16xx()) { - int rev; - - gpio_bank_count = 5; - gpio_bank = gpio_bank_1610; - rev = omap_readw(gpio_bank[1].base + OMAP1610_GPIO_REVISION); - printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n", - (rev >> 4) & 0x0f, rev & 0x0f); - } -#endif -#ifdef CONFIG_ARCH_OMAP730 - if (cpu_is_omap730()) { - printk(KERN_INFO "OMAP730 GPIO hardware\n"); - gpio_bank_count = 7; - gpio_bank = gpio_bank_730; - } -#endif - for (i = 0; i < gpio_bank_count; i++) { - int j, gpio_count = 16; - - bank = &gpio_bank[i]; - bank->reserved_map = 0; - bank->base = IO_ADDRESS(bank->base); - spin_lock_init(&bank->lock); - if (bank->method == METHOD_MPUIO) { - omap_writew(0xFFFF, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_MASKIT); - } -#ifdef CONFIG_ARCH_OMAP1510 - if (bank->method == METHOD_GPIO_1510) { - __raw_writew(0xffff, bank->base + OMAP1510_GPIO_INT_MASK); - __raw_writew(0x0000, bank->base + OMAP1510_GPIO_INT_STATUS); - } -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (bank->method == METHOD_GPIO_1610) { - __raw_writew(0x0000, bank->base + OMAP1610_GPIO_IRQENABLE1); - __raw_writew(0xffff, bank->base + OMAP1610_GPIO_IRQSTATUS1); - } -#endif -#ifdef CONFIG_ARCH_OMAP730 - if (bank->method == METHOD_GPIO_730) { - __raw_writel(0xffffffff, bank->base + OMAP730_GPIO_INT_MASK); - __raw_writel(0x00000000, bank->base + OMAP730_GPIO_INT_STATUS); - - gpio_count = 32; /* 730 has 32-bit GPIOs */ - } -#endif - for (j = bank->virtual_irq_start; - j < bank->virtual_irq_start + gpio_count; j++) { - if (bank->method == METHOD_MPUIO) - set_irq_chip(j, &mpuio_irq_chip); - else - set_irq_chip(j, &gpio_irq_chip); - set_irq_handler(j, do_simple_IRQ); - set_irq_flags(j, IRQF_VALID); - } - set_irq_chained_handler(bank->irq, gpio_irq_handler); - set_irq_data(bank->irq, bank); - } - - /* Enable system clock for GPIO module. - * The CAM_CLK_CTRL *is* really the right place. */ - if (cpu_is_omap1610() || cpu_is_omap1710()) - omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, ULPD_CAM_CLK_CTRL); - - return 0; -} - -/* - * This may get called early from board specific init - */ -int omap_gpio_init(void) -{ - if (!initialized) - return _omap_gpio_init(); - else - return 0; -} - -EXPORT_SYMBOL(omap_request_gpio); -EXPORT_SYMBOL(omap_free_gpio); -EXPORT_SYMBOL(omap_set_gpio_direction); -EXPORT_SYMBOL(omap_set_gpio_dataout); -EXPORT_SYMBOL(omap_get_gpio_datain); -EXPORT_SYMBOL(omap_set_gpio_edge_ctrl); - -arch_initcall(omap_gpio_init); --- linux-2.6.13-rc2/arch/arm/mach-omap/irq.c 2005-03-01 23:37:49.000000000 -0800 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,219 +0,0 @@ -/* - * linux/arch/arm/mach-omap/irq.c - * - * Interrupt handler for all OMAP boards - * - * Copyright (C) 2004 Nokia Corporation - * Written by Tony Lindgren - * Major cleanups by Juha Yrjölä - * - * Completely re-written to support various OMAP chips with bank specific - * interrupt handlers. - * - * Some snippets of the code taken from the older OMAP interrupt handler - * Copyright (C) 2001 RidgeRun, Inc. Greg Lonnon - * - * GPIO interrupt handler moved to gpio.c by Juha Yrjola - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define IRQ_BANK(irq) ((irq) >> 5) -#define IRQ_BIT(irq) ((irq) & 0x1f) - -struct omap_irq_bank { - unsigned long base_reg; - unsigned long trigger_map; -}; - -static unsigned int irq_bank_count = 0; -static struct omap_irq_bank *irq_banks; - -static inline unsigned int irq_bank_readl(int bank, int offset) -{ - return omap_readl(irq_banks[bank].base_reg + offset); -} - -static inline void irq_bank_writel(unsigned long value, int bank, int offset) -{ - omap_writel(value, irq_banks[bank].base_reg + offset); -} - -static void omap_ack_irq(unsigned int irq) -{ - if (irq > 31) - omap_writel(0x1, OMAP_IH2_BASE + IRQ_CONTROL_REG_OFFSET); - - omap_writel(0x1, OMAP_IH1_BASE + IRQ_CONTROL_REG_OFFSET); -} - -static void omap_mask_irq(unsigned int irq) -{ - int bank = IRQ_BANK(irq); - u32 l; - - l = omap_readl(irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); - l |= 1 << IRQ_BIT(irq); - omap_writel(l, irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); -} - -static void omap_unmask_irq(unsigned int irq) -{ - int bank = IRQ_BANK(irq); - u32 l; - - l = omap_readl(irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); - l &= ~(1 << IRQ_BIT(irq)); - omap_writel(l, irq_banks[bank].base_reg + IRQ_MIR_REG_OFFSET); -} - -static void omap_mask_ack_irq(unsigned int irq) -{ - omap_mask_irq(irq); - omap_ack_irq(irq); -} - -/* - * Allows tuning the IRQ type and priority - * - * NOTE: There is currently no OMAP fiq handler for Linux. Read the - * mailing list threads on FIQ handlers if you are planning to - * add a FIQ handler for OMAP. - */ -static void omap_irq_set_cfg(int irq, int fiq, int priority, int trigger) -{ - signed int bank; - unsigned long val, offset; - - bank = IRQ_BANK(irq); - /* FIQ is only available on bank 0 interrupts */ - fiq = bank ? 0 : (fiq & 0x1); - val = fiq | ((priority & 0x1f) << 2) | ((trigger & 0x1) << 1); - offset = IRQ_ILR0_REG_OFFSET + IRQ_BIT(irq) * 0x4; - irq_bank_writel(val, bank, offset); -} - -#ifdef CONFIG_ARCH_OMAP730 -static struct omap_irq_bank omap730_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3f8e22f }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb9c1f2 }, - { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0x800040f3 }, -}; -#endif - -#ifdef CONFIG_ARCH_OMAP1510 -static struct omap_irq_bank omap1510_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3febfff }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xffbfffed }, -}; -#endif - -#if defined(CONFIG_ARCH_OMAP16XX) - -static struct omap_irq_bank omap1610_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3fefe8f }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb7c1fd }, - { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0xfffff7ff }, - { .base_reg = OMAP_IH2_BASE + 0x200, .trigger_map = 0xffffffff }, -}; -#endif - -static struct irqchip omap_irq_chip = { - .ack = omap_mask_ack_irq, - .mask = omap_mask_irq, - .unmask = omap_unmask_irq, -}; - -void __init omap_init_irq(void) -{ - int i, j; - -#ifdef CONFIG_ARCH_OMAP730 - if (cpu_is_omap730()) { - irq_banks = omap730_irq_banks; - irq_bank_count = ARRAY_SIZE(omap730_irq_banks); - } -#endif -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - irq_banks = omap1510_irq_banks; - irq_bank_count = ARRAY_SIZE(omap1510_irq_banks); - } -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (cpu_is_omap16xx()) { - irq_banks = omap1610_irq_banks; - irq_bank_count = ARRAY_SIZE(omap1610_irq_banks); - } -#endif - printk("Total of %i interrupts in %i interrupt banks\n", - irq_bank_count * 32, irq_bank_count); - - /* Mask and clear all interrupts */ - for (i = 0; i < irq_bank_count; i++) { - irq_bank_writel(~0x0, i, IRQ_MIR_REG_OFFSET); - irq_bank_writel(0x0, i, IRQ_ITR_REG_OFFSET); - } - - /* Clear any pending interrupts */ - irq_bank_writel(0x03, 0, IRQ_CONTROL_REG_OFFSET); - irq_bank_writel(0x03, 1, IRQ_CONTROL_REG_OFFSET); - - /* Enable interrupts in global mask */ - if (cpu_is_omap730()) { - irq_bank_writel(0x0, 0, IRQ_GMR_REG_OFFSET); - } - - /* Install the interrupt handlers for each bank */ - for (i = 0; i < irq_bank_count; i++) { - for (j = i * 32; j < (i + 1) * 32; j++) { - int irq_trigger; - - irq_trigger = irq_banks[i].trigger_map >> IRQ_BIT(j); - omap_irq_set_cfg(j, 0, 0, irq_trigger); - - set_irq_chip(j, &omap_irq_chip); - set_irq_handler(j, do_level_IRQ); - set_irq_flags(j, IRQF_VALID); - } - } - - /* Unmask level 2 handler */ - if (cpu_is_omap730()) { - omap_unmask_irq(INT_730_IH2_IRQ); - } else { - omap_unmask_irq(INT_IH2_IRQ); - } -} --- linux-2.6.13-rc2/arch/arm/mach-omap/Kconfig 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,221 +0,0 @@ -if ARCH_OMAP - -menu "TI OMAP Implementations" - -comment "OMAP Core Type" - -config ARCH_OMAP730 - depends on ARCH_OMAP - bool "OMAP730 Based System" - select ARCH_OMAP_OTG - -config ARCH_OMAP1510 - depends on ARCH_OMAP - default y - bool "OMAP1510 Based System" - -config ARCH_OMAP16XX - depends on ARCH_OMAP - bool "OMAP16XX Based System" - select ARCH_OMAP_OTG - -config ARCH_OMAP_OTG - bool - -comment "OMAP Board Type" - -config MACH_OMAP_INNOVATOR - bool "TI Innovator" - depends on ARCH_OMAP1510 || ARCH_OMAP16XX - help - TI OMAP 1510 or 1610 Innovator board support. Say Y here if you - have such a board. - -config MACH_OMAP_H2 - bool "TI H2 Support" - depends on ARCH_OMAP16XX - help - TI OMAP 1610/1611B H2 board support. Say Y here if you have such - a board. - -config MACH_OMAP_H3 - bool "TI H3 Support" - depends on ARCH_OMAP16XX - help - TI OMAP 1710 H3 board support. Say Y here if you have such - a board. - -config MACH_OMAP_H4 - bool "TI H4 Support" - depends on ARCH_OMAP16XX - help - TI OMAP 1610 H4 board support. Say Y here if you have such - a board. - -config MACH_OMAP_OSK - bool "TI OSK Support" - depends on ARCH_OMAP16XX - help - TI OMAP 5912 OSK (OMAP Starter Kit) board support. Say Y here - if you have such a board. - -config MACH_OMAP_PERSEUS2 - bool "TI Perseus2" - depends on ARCH_OMAP730 - help - Support for TI OMAP 730 Perseus2 board. Say Y here if you have such - a board. - -config MACH_VOICEBLUE - bool "Voiceblue" - depends on ARCH_OMAP1510 - help - Support for Voiceblue GSM/VoIP gateway. Say Y here if you have such - board. - -config MACH_NETSTAR - bool "NetStar" - depends on ARCH_OMAP1510 - help - Support for NetStar PBX. Say Y here if you have such a board. - -config MACH_OMAP_GENERIC - bool "Generic OMAP board" - depends on ARCH_OMAP1510 || ARCH_OMAP16XX - help - Support for generic OMAP-1510, 1610 or 1710 board with - no FPGA. Can be used as template for porting Linux to - custom OMAP boards. Say Y here if you have a custom - board. - -comment "OMAP Feature Selections" - -#config OMAP_BOOT_TAG -# bool "OMAP bootloader information passing" -# depends on ARCH_OMAP -# default n -# help -# Say Y, if you have a bootloader which passes information -# about your board and its peripheral configuration. - -config OMAP_MUX - bool "OMAP multiplexing support" - depends on ARCH_OMAP - default y - help - Pin multiplexing support for OMAP boards. If your bootloader - sets the multiplexing correctly, say N. Otherwise, or if unsure, - say Y. - -config OMAP_MUX_DEBUG - bool "Multiplexing debug output" - depends on OMAP_MUX - default n - help - Makes the multiplexing functions print out a lot of debug info. - This is useful if you want to find out the correct values of the - multiplexing registers. - -config OMAP_MUX_WARNINGS - bool "Warn about pins the bootloader didn't set up" - depends on OMAP_MUX - default y - help - Choose Y here to warn whenever driver initialization logic needs - to change the pin multiplexing setup. When there are no warnings - printed, it's safe to deselect OMAP_MUX for your product. - -choice - prompt "System timer" - default OMAP_MPU_TIMER - -config OMAP_MPU_TIMER - bool "Use mpu timer" - help - Select this option if you want to use the OMAP mpu timer. This - timer provides more intra-tick resolution than the 32KHz timer, - but consumes more power. - -config OMAP_32K_TIMER - bool "Use 32KHz timer" - depends on ARCH_OMAP16XX - help - Select this option if you want to enable the OMAP 32KHz timer. - This timer saves power compared to the OMAP_MPU_TIMER, and has - support for no tick during idle. The 32KHz timer provides less - intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is - currently only available for OMAP-16xx. - -endchoice - -config OMAP_32K_TIMER_HZ - int "Kernel internal timer frequency for 32KHz timer" - range 32 1024 - depends on OMAP_32K_TIMER - default "128" - help - Kernel internal timer frequency should be a divisor of 32768, - such as 64 or 128. - -choice - prompt "Low-level debug console UART" - depends on ARCH_OMAP - default OMAP_LL_DEBUG_UART1 - -config OMAP_LL_DEBUG_UART1 - bool "UART1" - -config OMAP_LL_DEBUG_UART2 - bool "UART2" - -config OMAP_LL_DEBUG_UART3 - bool "UART3" - -endchoice - -config OMAP_ARM_195MHZ - bool "OMAP ARM 195 MHz CPU" - depends on ARCH_OMAP730 - help - Enable 195MHz clock for OMAP CPU. If unsure, say N. - -config OMAP_ARM_192MHZ - bool "OMAP ARM 192 MHz CPU" - depends on ARCH_OMAP16XX - help - Enable 192MHz clock for OMAP CPU. If unsure, say N. - -config OMAP_ARM_182MHZ - bool "OMAP ARM 182 MHz CPU" - depends on ARCH_OMAP730 - help - Enable 182MHz clock for OMAP CPU. If unsure, say N. - -config OMAP_ARM_168MHZ - bool "OMAP ARM 168 MHz CPU" - depends on ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730 - help - Enable 168MHz clock for OMAP CPU. If unsure, say N. - -config OMAP_ARM_120MHZ - bool "OMAP ARM 120 MHz CPU" - depends on ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730 - help - Enable 120MHz clock for OMAP CPU. If unsure, say N. - -config OMAP_ARM_60MHZ - bool "OMAP ARM 60 MHz CPU" - depends on ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730 - default y - help - Enable 60MHz clock for OMAP CPU. If unsure, say Y. - -config OMAP_ARM_30MHZ - bool "OMAP ARM 30 MHz CPU" - depends on ARCH_OMAP1510 || ARCH_OMAP16XX || ARCH_OMAP730 - help - Enable 30MHz clock for OMAP CPU. If unsure, say N. - -endmenu - -endif --- linux-2.6.13-rc2/arch/arm/mach-omap/leds.c 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,61 +0,0 @@ -/* - * linux/arch/arm/mach-omap/leds.c - * - * OMAP LEDs dispatcher - */ -#include -#include - -#include -#include - -#include -#include - -#include "leds.h" - -static int __init -omap_leds_init(void) -{ - if (machine_is_omap_innovator()) - leds_event = innovator_leds_event; - - else if (machine_is_omap_h2() || machine_is_omap_perseus2()) - leds_event = h2p2_dbg_leds_event; - - else if (machine_is_omap_osk()) - leds_event = osk_leds_event; - - else - return -1; - - if (machine_is_omap_h2() - || machine_is_omap_perseus2() - || machine_is_omap_osk()) { - - /* LED1/LED2 pins can be used as GPIO (as done here), or by - * the LPG (works even in deep sleep!), to drive a bicolor - * LED on the H2 sample board, and another on the H2/P2 - * "surfer" expansion board. - * - * The same pins drive a LED on the OSK Mistral board, but - * that's a different kind of LED (just one color at a time). - */ - omap_cfg_reg(P18_1610_GPIO3); - if (omap_request_gpio(3) == 0) - omap_set_gpio_direction(3, 0); - else - printk(KERN_WARNING "LED: can't get GPIO3/red?\n"); - - omap_cfg_reg(MPUIO4); - if (omap_request_gpio(OMAP_MPUIO(4)) == 0) - omap_set_gpio_direction(OMAP_MPUIO(4), 0); - else - printk(KERN_WARNING "LED: can't get MPUIO4/green?\n"); - } - - leds_event(led_start); - return 0; -} - -__initcall(omap_leds_init); --- linux-2.6.13-rc2/arch/arm/mach-omap/leds.h 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,3 +0,0 @@ -extern void innovator_leds_event(led_event_t evt); -extern void h2p2_dbg_leds_event(led_event_t evt); -extern void osk_leds_event(led_event_t evt); --- linux-2.6.13-rc2/arch/arm/mach-omap/leds-h2p2-debug.c 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,144 +0,0 @@ -/* - * linux/arch/arm/mach-omap/leds-h2p2-debug.c - * - * Copyright 2003 by Texas Instruments Incorporated - * - * There are 16 LEDs on the debug board (all green); four may be used - * for logical 'green', 'amber', 'red', and 'blue' (after "claiming"). - * - * The "surfer" expansion board and H2 sample board also have two-color - * green+red LEDs (in parallel), used here for timer and idle indicators. - */ -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "leds.h" - - -#define GPIO_LED_RED 3 -#define GPIO_LED_GREEN OMAP_MPUIO(4) - - -#define LED_STATE_ENABLED 0x01 -#define LED_STATE_CLAIMED 0x02 -#define LED_TIMER_ON 0x04 - -#define GPIO_IDLE GPIO_LED_GREEN -#define GPIO_TIMER GPIO_LED_RED - - -void h2p2_dbg_leds_event(led_event_t evt) -{ - unsigned long flags; - - static struct h2p2_dbg_fpga __iomem *fpga; - static u16 led_state, hw_led_state; - - local_irq_save(flags); - - if (!(led_state & LED_STATE_ENABLED) && evt != led_start) - goto done; - - switch (evt) { - case led_start: - if (!fpga) - fpga = ioremap(H2P2_DBG_FPGA_START, - H2P2_DBG_FPGA_SIZE); - if (fpga) { - led_state |= LED_STATE_ENABLED; - __raw_writew(~0, &fpga->leds); - } - break; - - case led_stop: - case led_halted: - /* all leds off during suspend or shutdown */ - omap_set_gpio_dataout(GPIO_TIMER, 0); - omap_set_gpio_dataout(GPIO_IDLE, 0); - __raw_writew(~0, &fpga->leds); - led_state &= ~LED_STATE_ENABLED; - if (evt == led_halted) { - iounmap(fpga); - fpga = NULL; - } - goto done; - - case led_claim: - led_state |= LED_STATE_CLAIMED; - hw_led_state = 0; - break; - - case led_release: - led_state &= ~LED_STATE_CLAIMED; - break; - -#ifdef CONFIG_LEDS_TIMER - case led_timer: - led_state ^= LED_TIMER_ON; - omap_set_gpio_dataout(GPIO_TIMER, led_state & LED_TIMER_ON); - goto done; -#endif - -#ifdef CONFIG_LEDS_CPU - case led_idle_start: - omap_set_gpio_dataout(GPIO_IDLE, 1); - goto done; - - case led_idle_end: - omap_set_gpio_dataout(GPIO_IDLE, 0); - goto done; -#endif - - case led_green_on: - hw_led_state |= H2P2_DBG_FPGA_LED_GREEN; - break; - case led_green_off: - hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN; - break; - - case led_amber_on: - hw_led_state |= H2P2_DBG_FPGA_LED_AMBER; - break; - case led_amber_off: - hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER; - break; - - case led_red_on: - hw_led_state |= H2P2_DBG_FPGA_LED_RED; - break; - case led_red_off: - hw_led_state &= ~H2P2_DBG_FPGA_LED_RED; - break; - - case led_blue_on: - hw_led_state |= H2P2_DBG_FPGA_LED_BLUE; - break; - case led_blue_off: - hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE; - break; - - default: - break; - } - - - /* - * Actually burn the LEDs - */ - if (led_state & LED_STATE_CLAIMED) - __raw_writew(~hw_led_state, &fpga->leds); - -done: - local_irq_restore(flags); -} --- linux-2.6.13-rc2/arch/arm/mach-omap/leds-innovator.c 2005-03-01 23:38:34.000000000 -0800 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,103 +0,0 @@ -/* - * linux/arch/arm/mach-omap/leds-innovator.c - */ -#include -#include - -#include -#include -#include - -#include "leds.h" - - -#define LED_STATE_ENABLED 1 -#define LED_STATE_CLAIMED 2 - -static unsigned int led_state; -static unsigned int hw_led_state; - -void innovator_leds_event(led_event_t evt) -{ - unsigned long flags; - - local_irq_save(flags); - - switch (evt) { - case led_start: - hw_led_state = 0; - led_state = LED_STATE_ENABLED; - break; - - case led_stop: - led_state &= ~LED_STATE_ENABLED; - hw_led_state = 0; - break; - - case led_claim: - led_state |= LED_STATE_CLAIMED; - hw_led_state = 0; - break; - - case led_release: - led_state &= ~LED_STATE_CLAIMED; - hw_led_state = 0; - break; - -#ifdef CONFIG_LEDS_TIMER - case led_timer: - if (!(led_state & LED_STATE_CLAIMED)) - hw_led_state ^= 0; - break; -#endif - -#ifdef CONFIG_LEDS_CPU - case led_idle_start: - if (!(led_state & LED_STATE_CLAIMED)) - hw_led_state |= 0; - break; - - case led_idle_end: - if (!(led_state & LED_STATE_CLAIMED)) - hw_led_state &= ~0; - break; -#endif - - case led_halted: - break; - - case led_green_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~0; - break; - - case led_green_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= 0; - break; - - case led_amber_on: - break; - - case led_amber_off: - break; - - case led_red_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~0; - break; - - case led_red_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= 0; - break; - - default: - break; - } - - if (led_state & LED_STATE_ENABLED) - ; - - local_irq_restore(flags); -} --- linux-2.6.13-rc2/arch/arm/mach-omap/leds-osk.c 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,198 +0,0 @@ -/* - * linux/arch/arm/mach-omap/leds-osk.c - * - * LED driver for OSK, and optionally Mistral QVGA, boards - */ -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "leds.h" - - -#define LED_STATE_ENABLED (1 << 0) -#define LED_STATE_CLAIMED (1 << 1) -static u8 led_state; - -#define GREEN_LED (1 << 0) /* TPS65010 LED1 */ -#define AMBER_LED (1 << 1) /* TPS65010 LED2 */ -#define RED_LED (1 << 2) /* TPS65010 GPIO2 */ -#define TIMER_LED (1 << 3) /* Mistral board */ -#define IDLE_LED (1 << 4) /* Mistral board */ -static u8 hw_led_state; - - -/* TPS65010 leds are changed using i2c -- from a task context. - * Using one of these for the "idle" LED would be impractical... - */ -#define TPS_LEDS (GREEN_LED | RED_LED | AMBER_LED) - -static u8 tps_leds_change; - -static void tps_work(void *unused) -{ - for (;;) { - u8 leds; - - local_irq_disable(); - leds = tps_leds_change; - tps_leds_change = 0; - local_irq_enable(); - - if (!leds) - break; - - /* careful: the set_led() value is on/off/blink */ - if (leds & GREEN_LED) - tps65010_set_led(LED1, !!(hw_led_state & GREEN_LED)); - if (leds & AMBER_LED) - tps65010_set_led(LED2, !!(hw_led_state & AMBER_LED)); - - /* the gpio led doesn't have that issue */ - if (leds & RED_LED) - tps65010_set_gpio_out_value(GPIO2, - !(hw_led_state & RED_LED)); - } -} - -static DECLARE_WORK(work, tps_work, NULL); - -#ifdef CONFIG_FB_OMAP - -/* For now, all system indicators require the Mistral board, since that - * LED can be manipulated without a task context. This LED is either red, - * or green, but not both; it can't give the full "disco led" effect. - */ - -#define GPIO_LED_RED 3 -#define GPIO_LED_GREEN OMAP_MPUIO(4) - -static void mistral_setled(void) -{ - int red = 0; - int green = 0; - - if (hw_led_state & TIMER_LED) - red = 1; - else if (hw_led_state & IDLE_LED) - green = 1; - // else both sides are disabled - - omap_set_gpio_dataout(GPIO_LED_GREEN, green); - omap_set_gpio_dataout(GPIO_LED_RED, red); -} - -#endif - -void osk_leds_event(led_event_t evt) -{ - unsigned long flags; - u16 leds; - - local_irq_save(flags); - - if (!(led_state & LED_STATE_ENABLED) && evt != led_start) - goto done; - - leds = hw_led_state; - switch (evt) { - case led_start: - led_state |= LED_STATE_ENABLED; - hw_led_state = 0; - leds = ~0; - break; - - case led_halted: - case led_stop: - led_state &= ~LED_STATE_ENABLED; - hw_led_state = 0; - // NOTE: work may still be pending!! - break; - - case led_claim: - led_state |= LED_STATE_CLAIMED; - hw_led_state = 0; - leds = ~0; - break; - - case led_release: - led_state &= ~LED_STATE_CLAIMED; - hw_led_state = 0; - break; - -#ifdef CONFIG_FB_OMAP - -#ifdef CONFIG_LEDS_TIMER - case led_timer: - hw_led_state ^= TIMER_LED; - mistral_setled(); - break; -#endif - -#ifdef CONFIG_LEDS_CPU - case led_idle_start: - hw_led_state |= IDLE_LED; - mistral_setled(); - break; - - case led_idle_end: - hw_led_state &= ~IDLE_LED; - mistral_setled(); - break; -#endif - -#endif /* CONFIG_FB_OMAP */ - - /* "green" == tps LED1 (leftmost, normally power-good) - * works only with DC adapter, not on battery power! - */ - case led_green_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= GREEN_LED; - break; - case led_green_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~GREEN_LED; - break; - - /* "amber" == tps LED2 (middle) */ - case led_amber_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= AMBER_LED; - break; - case led_amber_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~AMBER_LED; - break; - - /* "red" == LED on tps gpio3 (rightmost) */ - case led_red_on: - if (led_state & LED_STATE_CLAIMED) - hw_led_state |= RED_LED; - break; - case led_red_off: - if (led_state & LED_STATE_CLAIMED) - hw_led_state &= ~RED_LED; - break; - - default: - break; - } - - leds ^= hw_led_state; - leds &= TPS_LEDS; - if (leds && (led_state & LED_STATE_CLAIMED)) { - tps_leds_change |= leds; - schedule_work(&work); - } - -done: - local_irq_restore(flags); -} --- linux-2.6.13-rc2/arch/arm/mach-omap/Makefile 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,40 +0,0 @@ -# -# Makefile for the linux kernel. -# - -# Common support -obj-y := common.o time.o irq.o dma.o clock.o mux.o gpio.o mcbsp.o usb.o -obj-m := -obj-n := -obj- := -led-y := leds.o - -# Specific board support -obj-$(CONFIG_MACH_OMAP_H2) += board-h2.o -obj-$(CONFIG_MACH_OMAP_INNOVATOR) += board-innovator.o -obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o -obj-$(CONFIG_MACH_OMAP_PERSEUS2) += board-perseus2.o -obj-$(CONFIG_MACH_OMAP_OSK) += board-osk.o -obj-$(CONFIG_MACH_OMAP_H3) += board-h3.o -obj-$(CONFIG_MACH_VOICEBLUE) += board-voiceblue.o -obj-$(CONFIG_MACH_NETSTAR) += board-netstar.o - -# OCPI interconnect support for 1710, 1610 and 5912 -obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o - -# LEDs support -led-$(CONFIG_MACH_OMAP_H2) += leds-h2p2-debug.o -led-$(CONFIG_MACH_OMAP_INNOVATOR) += leds-innovator.o -led-$(CONFIG_MACH_OMAP_PERSEUS2) += leds-h2p2-debug.o -obj-$(CONFIG_LEDS) += $(led-y) - -# Power Management -obj-$(CONFIG_PM) += pm.o sleep.o - -ifeq ($(CONFIG_ARCH_OMAP1510),y) -# Innovator-1510 FPGA -obj-$(CONFIG_MACH_OMAP_INNOVATOR) += fpga.o -endif - -# kgdb support -obj-$(CONFIG_KGDB_SERIAL) += kgdb-serial.o --- linux-2.6.13-rc2/arch/arm/mach-omap/Makefile.boot 2005-03-01 23:38:08.000000000 -0800 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,4 +0,0 @@ - zreladdr-y := 0x10008000 -params_phys-y := 0x10000100 -initrd_phys-y := 0x10800000 - --- linux-2.6.13-rc2/arch/arm/mach-omap/mcbsp.c 2005-03-01 23:38:09.000000000 -0800 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,685 +0,0 @@ -/* - * linux/arch/arm/omap/mcbsp.c - * - * Copyright (C) 2004 Nokia Corporation - * Author: Samuel Ortiz - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Multichannel mode not supported. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#ifdef CONFIG_MCBSP_DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) do { } while (0) -#endif - -struct omap_mcbsp { - u32 io_base; - u8 id; - u8 free; - omap_mcbsp_word_length rx_word_length; - omap_mcbsp_word_length tx_word_length; - - /* IRQ based TX/RX */ - int rx_irq; - int tx_irq; - - /* DMA stuff */ - u8 dma_rx_sync; - short dma_rx_lch; - u8 dma_tx_sync; - short dma_tx_lch; - - /* Completion queues */ - struct completion tx_irq_completion; - struct completion rx_irq_completion; - struct completion tx_dma_completion; - struct completion rx_dma_completion; - - spinlock_t lock; -}; - -static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT]; -static struct clk *mcbsp_dsp_ck = 0; -static struct clk *mcbsp_api_ck = 0; - - -static void omap_mcbsp_dump_reg(u8 id) -{ - DBG("**** MCBSP%d regs ****\n", mcbsp[id].id); - DBG("DRR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR2)); - DBG("DRR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR1)); - DBG("DXR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR2)); - DBG("DXR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR1)); - DBG("SPCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR2)); - DBG("SPCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR1)); - DBG("RCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR2)); - DBG("RCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR1)); - DBG("XCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR2)); - DBG("XCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR1)); - DBG("SRGR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR2)); - DBG("SRGR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR1)); - DBG("PCR0: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, PCR0)); - DBG("***********************\n"); -} - - -static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) -{ - struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id); - - DBG("TX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2)); - - complete(&mcbsp_tx->tx_irq_completion); - return IRQ_HANDLED; -} - -static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) -{ - struct omap_mcbsp * mcbsp_rx = (struct omap_mcbsp *)(dev_id); - - DBG("RX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR2)); - - complete(&mcbsp_rx->rx_irq_completion); - return IRQ_HANDLED; -} - - -static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) -{ - struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data); - - DBG("TX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2)); - - /* We can free the channels */ - omap_free_dma(mcbsp_dma_tx->dma_tx_lch); - mcbsp_dma_tx->dma_tx_lch = -1; - - complete(&mcbsp_dma_tx->tx_dma_completion); -} - -static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data) -{ - struct omap_mcbsp * mcbsp_dma_rx = (struct omap_mcbsp *)(data); - - DBG("RX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2)); - - /* We can free the channels */ - omap_free_dma(mcbsp_dma_rx->dma_rx_lch); - mcbsp_dma_rx->dma_rx_lch = -1; - - complete(&mcbsp_dma_rx->rx_dma_completion); -} - - -/* - * omap_mcbsp_config simply write a config to the - * appropriate McBSP. - * You either call this function or set the McBSP registers - * by yourself before calling omap_mcbsp_start(). - */ - -void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config) -{ - u32 io_base = mcbsp[id].io_base; - - DBG("OMAP-McBSP: McBSP%d io_base: 0x%8x\n", id+1, io_base); - - /* We write the given config */ - OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2); - OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1); - OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2); - OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1); - OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2); - OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1); - OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2); - OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1); - OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2); - OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1); - OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0); -} - - - -static int omap_mcbsp_check(unsigned int id) -{ - if (cpu_is_omap730()) { - if (id > OMAP_MAX_MCBSP_COUNT - 1) { - printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); - return -1; - } - return 0; - } - - if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) { - if (id > OMAP_MAX_MCBSP_COUNT) { - printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); - return -1; - } - return 0; - } - - return -1; -} - -#define EN_XORPCK 1 -#define DSP_RSTCT2 0xe1008014 - -static void omap_mcbsp_dsp_request(void) -{ - if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) { - omap_writew((omap_readw(ARM_RSTCT1) | (1 << 1) | (1 << 2)), - ARM_RSTCT1); - clk_enable(mcbsp_dsp_ck); - clk_enable(mcbsp_api_ck); - - /* enable 12MHz clock to mcbsp 1 & 3 */ - __raw_writew(__raw_readw(DSP_IDLECT2) | (1 << EN_XORPCK), - DSP_IDLECT2); - __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1 << 1, - DSP_RSTCT2); - } -} - -static void omap_mcbsp_dsp_free(void) -{ - /* Useless for now */ -} - - -int omap_mcbsp_request(unsigned int id) -{ - int err; - - if (omap_mcbsp_check(id) < 0) - return -EINVAL; - - /* - * On 1510, 1610 and 1710, McBSP1 and McBSP3 - * are DSP public peripherals. - */ - if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) - omap_mcbsp_dsp_request(); - - spin_lock(&mcbsp[id].lock); - if (!mcbsp[id].free) { - printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); - spin_unlock(&mcbsp[id].lock); - return -1; - } - - mcbsp[id].free = 0; - spin_unlock(&mcbsp[id].lock); - - /* We need to get IRQs here */ - err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, - "McBSP", - (void *) (&mcbsp[id])); - if (err != 0) { - printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", - mcbsp[id].tx_irq, mcbsp[id].id); - return err; - } - - init_completion(&(mcbsp[id].tx_irq_completion)); - - - err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, - "McBSP", - (void *) (&mcbsp[id])); - if (err != 0) { - printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", - mcbsp[id].rx_irq, mcbsp[id].id); - free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); - return err; - } - - init_completion(&(mcbsp[id].rx_irq_completion)); - return 0; - -} - -void omap_mcbsp_free(unsigned int id) -{ - if (omap_mcbsp_check(id) < 0) - return; - - if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) - omap_mcbsp_dsp_free(); - - spin_lock(&mcbsp[id].lock); - if (mcbsp[id].free) { - printk (KERN_ERR "OMAP-McBSP: McBSP%d was not reserved\n", id + 1); - spin_unlock(&mcbsp[id].lock); - return; - } - - mcbsp[id].free = 1; - spin_unlock(&mcbsp[id].lock); - - /* Free IRQs */ - free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); - free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); -} - -/* - * Here we start the McBSP, by enabling the sample - * generator, both transmitter and receivers, - * and the frame sync. - */ -void omap_mcbsp_start(unsigned int id) -{ - u32 io_base; - u16 w; - - if (omap_mcbsp_check(id) < 0) - return; - - io_base = mcbsp[id].io_base; - - mcbsp[id].rx_word_length = ((OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7); - mcbsp[id].tx_word_length = ((OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7); - - /* Start the sample generator */ - w = OMAP_MCBSP_READ(io_base, SPCR2); - OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); - - /* Enable transmitter and receiver */ - w = OMAP_MCBSP_READ(io_base, SPCR2); - OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); - - w = OMAP_MCBSP_READ(io_base, SPCR1); - OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); - - udelay(100); - - /* Start frame sync */ - w = OMAP_MCBSP_READ(io_base, SPCR2); - OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); - - /* Dump McBSP Regs */ - omap_mcbsp_dump_reg(id); - -} - -void omap_mcbsp_stop(unsigned int id) -{ - u32 io_base; - u16 w; - - if (omap_mcbsp_check(id) < 0) - return; - - io_base = mcbsp[id].io_base; - - /* Reset transmitter */ - w = OMAP_MCBSP_READ(io_base, SPCR2); - OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); - - /* Reset receiver */ - w = OMAP_MCBSP_READ(io_base, SPCR1); - OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); - - /* Reset the sample rate generator */ - w = OMAP_MCBSP_READ(io_base, SPCR2); - OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); -} - - -/* - * IRQ based word transmission. - */ -void omap_mcbsp_xmit_word(unsigned int id, u32 word) -{ - u32 io_base; - omap_mcbsp_word_length word_length = mcbsp[id].tx_word_length; - - if (omap_mcbsp_check(id) < 0) - return; - - io_base = mcbsp[id].io_base; - - wait_for_completion(&(mcbsp[id].tx_irq_completion)); - - if (word_length > OMAP_MCBSP_WORD_16) - OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); - OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); -} - -u32 omap_mcbsp_recv_word(unsigned int id) -{ - u32 io_base; - u16 word_lsb, word_msb = 0; - omap_mcbsp_word_length word_length = mcbsp[id].rx_word_length; - - if (omap_mcbsp_check(id) < 0) - return -EINVAL; - - io_base = mcbsp[id].io_base; - - wait_for_completion(&(mcbsp[id].rx_irq_completion)); - - if (word_length > OMAP_MCBSP_WORD_16) - word_msb = OMAP_MCBSP_READ(io_base, DRR2); - word_lsb = OMAP_MCBSP_READ(io_base, DRR1); - - return (word_lsb | (word_msb << 16)); -} - - -/* - * Simple DMA based buffer rx/tx routines. - * Nothing fancy, just a single buffer tx/rx through DMA. - * The DMA resources are released once the transfer is done. - * For anything fancier, you should use your own customized DMA - * routines and callbacks. - */ -int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) -{ - int dma_tx_ch; - - if (omap_mcbsp_check(id) < 0) - return -EINVAL; - - if (omap_request_dma(mcbsp[id].dma_tx_sync, "McBSP TX", omap_mcbsp_tx_dma_callback, - &mcbsp[id], - &dma_tx_ch)) { - printk("OMAP-McBSP: Unable to request DMA channel for McBSP%d TX. Trying IRQ based TX\n", id+1); - return -EAGAIN; - } - mcbsp[id].dma_tx_lch = dma_tx_ch; - - DBG("TX DMA on channel %d\n", dma_tx_ch); - - init_completion(&(mcbsp[id].tx_dma_completion)); - - omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, - OMAP_DMA_DATA_TYPE_S16, - length >> 1, 1, - OMAP_DMA_SYNC_ELEMENT); - - omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, - OMAP_DMA_PORT_TIPB, - OMAP_DMA_AMODE_CONSTANT, - mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1); - - omap_set_dma_src_params(mcbsp[id].dma_tx_lch, - OMAP_DMA_PORT_EMIFF, - OMAP_DMA_AMODE_POST_INC, - buffer); - - omap_start_dma(mcbsp[id].dma_tx_lch); - wait_for_completion(&(mcbsp[id].tx_dma_completion)); - return 0; -} - - -int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) -{ - int dma_rx_ch; - - if (omap_mcbsp_check(id) < 0) - return -EINVAL; - - if (omap_request_dma(mcbsp[id].dma_rx_sync, "McBSP RX", omap_mcbsp_rx_dma_callback, - &mcbsp[id], - &dma_rx_ch)) { - printk("Unable to request DMA channel for McBSP%d RX. Trying IRQ based RX\n", id+1); - return -EAGAIN; - } - mcbsp[id].dma_rx_lch = dma_rx_ch; - - DBG("RX DMA on channel %d\n", dma_rx_ch); - - init_completion(&(mcbsp[id].rx_dma_completion)); - - omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, - OMAP_DMA_DATA_TYPE_S16, - length >> 1, 1, - OMAP_DMA_SYNC_ELEMENT); - - omap_set_dma_src_params(mcbsp[id].dma_rx_lch, - OMAP_DMA_PORT_TIPB, - OMAP_DMA_AMODE_CONSTANT, - mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1); - - omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, - OMAP_DMA_PORT_EMIFF, - OMAP_DMA_AMODE_POST_INC, - buffer); - - omap_start_dma(mcbsp[id].dma_rx_lch); - wait_for_completion(&(mcbsp[id].rx_dma_completion)); - return 0; -} - - -/* - * SPI wrapper. - * Since SPI setup is much simpler than the generic McBSP one, - * this wrapper just need an omap_mcbsp_spi_cfg structure as an input. - * Once this is done, you can call omap_mcbsp_start(). - */ -void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg) -{ - struct omap_mcbsp_reg_cfg mcbsp_cfg; - - if (omap_mcbsp_check(id) < 0) - return; - - memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); - - /* SPI has only one frame */ - mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0)); - mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0)); - - /* Clock stop mode */ - if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY) - mcbsp_cfg.spcr1 |= (1 << 12); - else - mcbsp_cfg.spcr1 |= (3 << 11); - - /* Set clock parities */ - if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING) - mcbsp_cfg.pcr0 |= CLKRP; - else - mcbsp_cfg.pcr0 &= ~CLKRP; - - if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING) - mcbsp_cfg.pcr0 &= ~CLKXP; - else - mcbsp_cfg.pcr0 |= CLKXP; - - /* Set SCLKME to 0 and CLKSM to 1 */ - mcbsp_cfg.pcr0 &= ~SCLKME; - mcbsp_cfg.srgr2 |= CLKSM; - - /* Set FSXP */ - if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH) - mcbsp_cfg.pcr0 &= ~FSXP; - else - mcbsp_cfg.pcr0 |= FSXP; - - if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) { - mcbsp_cfg.pcr0 |= CLKXM; - mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div -1); - mcbsp_cfg.pcr0 |= FSXM; - mcbsp_cfg.srgr2 &= ~FSGM; - mcbsp_cfg.xcr2 |= XDATDLY(1); - mcbsp_cfg.rcr2 |= RDATDLY(1); - } - else { - mcbsp_cfg.pcr0 &= ~CLKXM; - mcbsp_cfg.srgr1 |= CLKGDV(1); - mcbsp_cfg.pcr0 &= ~FSXM; - mcbsp_cfg.xcr2 &= ~XDATDLY(3); - mcbsp_cfg.rcr2 &= ~RDATDLY(3); - } - - mcbsp_cfg.xcr2 &= ~XPHASE; - mcbsp_cfg.rcr2 &= ~RPHASE; - - omap_mcbsp_config(id, &mcbsp_cfg); -} - - -/* - * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. - * 730 has only 2 McBSP, and both of them are MPU peripherals. - */ -struct omap_mcbsp_info { - u32 virt_base; - u8 dma_rx_sync, dma_tx_sync; - u16 rx_irq, tx_irq; -}; - -#ifdef CONFIG_ARCH_OMAP730 -static const struct omap_mcbsp_info mcbsp_730[] = { - [0] = { .virt_base = io_p2v(OMAP730_MCBSP1_BASE), - .dma_rx_sync = OMAP_DMA_MCBSP1_RX, - .dma_tx_sync = OMAP_DMA_MCBSP1_TX, - .rx_irq = INT_730_McBSP1RX, - .tx_irq = INT_730_McBSP1TX }, - [1] = { .virt_base = io_p2v(OMAP730_MCBSP2_BASE), - .dma_rx_sync = OMAP_DMA_MCBSP3_RX, - .dma_tx_sync = OMAP_DMA_MCBSP3_TX, - .rx_irq = INT_730_McBSP2RX, - .tx_irq = INT_730_McBSP2TX }, -}; -#endif - -#ifdef CONFIG_ARCH_OMAP1510 -static const struct omap_mcbsp_info mcbsp_1510[] = { - [0] = { .virt_base = OMAP1510_MCBSP1_BASE, - .dma_rx_sync = OMAP_DMA_MCBSP1_RX, - .dma_tx_sync = OMAP_DMA_MCBSP1_TX, - .rx_irq = INT_McBSP1RX, - .tx_irq = INT_McBSP1TX }, - [1] = { .virt_base = io_p2v(OMAP1510_MCBSP2_BASE), - .dma_rx_sync = OMAP_DMA_MCBSP2_RX, - .dma_tx_sync = OMAP_DMA_MCBSP2_TX, - .rx_irq = INT_1510_SPI_RX, - .tx_irq = INT_1510_SPI_TX }, - [2] = { .virt_base = OMAP1510_MCBSP3_BASE, - .dma_rx_sync = OMAP_DMA_MCBSP3_RX, - .dma_tx_sync = OMAP_DMA_MCBSP3_TX, - .rx_irq = INT_McBSP3RX, - .tx_irq = INT_McBSP3TX }, -}; -#endif - -#if defined(CONFIG_ARCH_OMAP16XX) -static const struct omap_mcbsp_info mcbsp_1610[] = { - [0] = { .virt_base = OMAP1610_MCBSP1_BASE, - .dma_rx_sync = OMAP_DMA_MCBSP1_RX, - .dma_tx_sync = OMAP_DMA_MCBSP1_TX, - .rx_irq = INT_McBSP1RX, - .tx_irq = INT_McBSP1TX }, - [1] = { .virt_base = io_p2v(OMAP1610_MCBSP2_BASE), - .dma_rx_sync = OMAP_DMA_MCBSP2_RX, - .dma_tx_sync = OMAP_DMA_MCBSP2_TX, - .rx_irq = INT_1610_McBSP2_RX, - .tx_irq = INT_1610_McBSP2_TX }, - [2] = { .virt_base = OMAP1610_MCBSP3_BASE, - .dma_rx_sync = OMAP_DMA_MCBSP3_RX, - .dma_tx_sync = OMAP_DMA_MCBSP3_TX, - .rx_irq = INT_McBSP3RX, - .tx_irq = INT_McBSP3TX }, -}; -#endif - -static int __init omap_mcbsp_init(void) -{ - int mcbsp_count = 0, i; - static const struct omap_mcbsp_info *mcbsp_info; - - printk("Initializing OMAP McBSP system\n"); - - mcbsp_dsp_ck = clk_get(0, "dsp_ck"); - if (IS_ERR(mcbsp_dsp_ck)) { - printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n"); - return PTR_ERR(mcbsp_dsp_ck); - } - mcbsp_api_ck = clk_get(0, "api_ck"); - if (IS_ERR(mcbsp_dsp_ck)) { - printk(KERN_ERR "mcbsp: could not acquire api_ck handle.\n"); - return PTR_ERR(mcbsp_api_ck); - } - -#ifdef CONFIG_ARCH_OMAP730 - if (cpu_is_omap730()) { - mcbsp_info = mcbsp_730; - mcbsp_count = ARRAY_SIZE(mcbsp_730); - } -#endif -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - mcbsp_info = mcbsp_1510; - mcbsp_count = ARRAY_SIZE(mcbsp_1510); - } -#endif -#if defined(CONFIG_ARCH_OMAP16XX) - if (cpu_is_omap1610() || cpu_is_omap1710()) { - mcbsp_info = mcbsp_1610; - mcbsp_count = ARRAY_SIZE(mcbsp_1610); - } -#endif - for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { - if (i >= mcbsp_count) { - mcbsp[i].io_base = 0; - mcbsp[i].free = 0; - continue; - } - mcbsp[i].id = i + 1; - mcbsp[i].free = 1; - mcbsp[i].dma_tx_lch = -1; - mcbsp[i].dma_rx_lch = -1; - - mcbsp[i].io_base = mcbsp_info[i].virt_base; - mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; - mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; - mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; - mcbsp[i].dma_tx_sync = mcbsp_info[i].dma_tx_sync; - spin_lock_init(&mcbsp[i].lock); - } - - return 0; -} - - -arch_initcall(omap_mcbsp_init); - -EXPORT_SYMBOL(omap_mcbsp_config); -EXPORT_SYMBOL(omap_mcbsp_request); -EXPORT_SYMBOL(omap_mcbsp_free); -EXPORT_SYMBOL(omap_mcbsp_start); -EXPORT_SYMBOL(omap_mcbsp_stop); -EXPORT_SYMBOL(omap_mcbsp_xmit_word); -EXPORT_SYMBOL(omap_mcbsp_recv_word); -EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); -EXPORT_SYMBOL(omap_mcbsp_recv_buffer); -EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); --- linux-2.6.13-rc2/arch/arm/mach-omap/mux.c 2005-03-01 23:38:25.000000000 -0800 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,163 +0,0 @@ -/* - * linux/arch/arm/mach-omap/mux.c - * - * Utility to set the Omap MUX and PULL_DWN registers from a table in mux.h - * - * Copyright (C) 2003 Nokia Corporation - * - * Written by Tony Lindgren - * - * 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 - * - */ -#include -#include -#include -#include -#include -#include - -#define __MUX_C__ -#include - -#ifdef CONFIG_OMAP_MUX - -/* - * Sets the Omap MUX and PULL_DWN registers based on the table - */ -int __init_or_module -omap_cfg_reg(const reg_cfg_t reg_cfg) -{ - static DEFINE_SPINLOCK(mux_spin_lock); - - unsigned long flags; - reg_cfg_set *cfg; - unsigned int reg_orig = 0, reg = 0, pu_pd_orig = 0, pu_pd = 0, - pull_orig = 0, pull = 0; - unsigned int mask, warn = 0; - - if (reg_cfg > ARRAY_SIZE(reg_cfg_table)) { - printk(KERN_ERR "MUX: reg_cfg %d\n", reg_cfg); - return -EINVAL; - } - - cfg = ®_cfg_table[reg_cfg]; - - /* - * We do a pretty long section here with lock on, but pin muxing - * should only happen on driver init for each driver, so it's not time - * critical. - */ - spin_lock_irqsave(&mux_spin_lock, flags); - - /* Check the mux register in question */ - if (cfg->mux_reg) { - unsigned tmp1, tmp2; - - reg_orig = omap_readl(cfg->mux_reg); - - /* The mux registers always seem to be 3 bits long */ - mask = (0x7 << cfg->mask_offset); - tmp1 = reg_orig & mask; - reg = reg_orig & ~mask; - - tmp2 = (cfg->mask << cfg->mask_offset); - reg |= tmp2; - - if (tmp1 != tmp2) - warn = 1; - - omap_writel(reg, cfg->mux_reg); - } - - /* Check for pull up or pull down selection on 1610 */ - if (!cpu_is_omap1510()) { - if (cfg->pu_pd_reg && cfg->pull_val) { - pu_pd_orig = omap_readl(cfg->pu_pd_reg); - mask = 1 << cfg->pull_bit; - - if (cfg->pu_pd_val) { - if (!(pu_pd_orig & mask)) - warn = 1; - /* Use pull up */ - pu_pd = pu_pd_orig | mask; - } else { - if (pu_pd_orig & mask) - warn = 1; - /* Use pull down */ - pu_pd = pu_pd_orig & ~mask; - } - omap_writel(pu_pd, cfg->pu_pd_reg); - } - } - - /* Check for an associated pull down register */ - if (cfg->pull_reg) { - pull_orig = omap_readl(cfg->pull_reg); - mask = 1 << cfg->pull_bit; - - if (cfg->pull_val) { - if (pull_orig & mask) - warn = 1; - /* Low bit = pull enabled */ - pull = pull_orig & ~mask; - } else { - if (!(pull_orig & mask)) - warn = 1; - /* High bit = pull disabled */ - pull = pull_orig | mask; - } - - omap_writel(pull, cfg->pull_reg); - } - - if (warn) { -#ifdef CONFIG_OMAP_MUX_WARNINGS - printk(KERN_WARNING "MUX: initialized %s\n", cfg->name); -#endif - } - -#ifdef CONFIG_OMAP_MUX_DEBUG - if (cfg->debug || warn) { - printk("MUX: Setting register %s\n", cfg->name); - printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", - cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg); - - if (!cpu_is_omap1510()) { - if (cfg->pu_pd_reg && cfg->pull_val) { - printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", - cfg->pu_pd_name, cfg->pu_pd_reg, - pu_pd_orig, pu_pd); - } - } - - if (cfg->pull_reg) - printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", - cfg->pull_name, cfg->pull_reg, pull_orig, pull); - } -#endif - - spin_unlock_irqrestore(&mux_spin_lock, flags); - -#ifdef CONFIG_OMAP_MUX_ERRORS - return warn ? -ETXTBSY : 0; -#else - return 0; -#endif -} - -EXPORT_SYMBOL(omap_cfg_reg); - -#endif /* CONFIG_OMAP_MUX */ --- linux-2.6.13-rc2/arch/arm/mach-omap/ocpi.c 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,114 +0,0 @@ -/* - * linux/arch/arm/mach-omap/ocpi.c - * - * Minimal OCP bus support for omap16xx - * - * Copyright (C) 2003 - 2005 Nokia Corporation - * Written by Tony Lindgren - * - * Modified for clock framework by Paul Mundt . - * - * 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 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define OCPI_BASE 0xfffec320 -#define OCPI_FAULT (OCPI_BASE + 0x00) -#define OCPI_CMD_FAULT (OCPI_BASE + 0x04) -#define OCPI_SINT0 (OCPI_BASE + 0x08) -#define OCPI_TABORT (OCPI_BASE + 0x0c) -#define OCPI_SINT1 (OCPI_BASE + 0x10) -#define OCPI_PROT (OCPI_BASE + 0x14) -#define OCPI_SEC (OCPI_BASE + 0x18) - -/* USB OHCI OCPI access error registers */ -#define HOSTUEADDR 0xfffba0e0 -#define HOSTUESTATUS 0xfffba0e4 - -static struct clk *ocpi_ck; - -/* - * Enables device access to OMAP buses via the OCPI bridge - * FIXME: Add locking - */ -int ocpi_enable(void) -{ - unsigned int val; - - if (!cpu_is_omap16xx()) - return -ENODEV; - - /* Make sure there's clock for OCPI */ - clk_enable(ocpi_ck); - - /* Enable access for OHCI in OCPI */ - val = omap_readl(OCPI_PROT); - val &= ~0xff; - //val &= (1 << 0); /* Allow access only to EMIFS */ - omap_writel(val, OCPI_PROT); - - val = omap_readl(OCPI_SEC); - val &= ~0xff; - omap_writel(val, OCPI_SEC); - - return 0; -} -EXPORT_SYMBOL(ocpi_enable); - -static int __init omap_ocpi_init(void) -{ - if (!cpu_is_omap16xx()) - return -ENODEV; - - ocpi_ck = clk_get(NULL, "l3_ocpi_ck"); - if (IS_ERR(ocpi_ck)) - return PTR_ERR(ocpi_ck); - - clk_use(ocpi_ck); - ocpi_enable(); - printk("OMAP OCPI interconnect driver loaded\n"); - - return 0; -} - -static void __exit omap_ocpi_exit(void) -{ - /* REVISIT: Disable OCPI */ - - if (!cpu_is_omap16xx()) - return; - - clk_unuse(ocpi_ck); - clk_put(ocpi_ck); -} - -MODULE_AUTHOR("Tony Lindgren "); -MODULE_DESCRIPTION("OMAP OCPI bus controller module"); -MODULE_LICENSE("GPL"); -module_init(omap_ocpi_init); -module_exit(omap_ocpi_exit); --- linux-2.6.13-rc2/arch/arm/mach-omap/pm.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,632 +0,0 @@ -/* - * linux/arch/arm/mach-omap/pm.c - * - * OMAP Power Management Routines - * - * Original code for the SA11x0: - * Copyright (c) 2001 Cliff Brake - * - * Modified for the PXA250 by Nicolas Pitre: - * Copyright (c) 2002 Monta Vista Software, Inc. - * - * Modified for the OMAP1510 by David Singleton: - * Copyright (c) 2002 Monta Vista Software, Inc. - * - * Cleanup 2004 for OMAP1510/1610 by Dirk Behme - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "clock.h" - -static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE]; -static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE]; -static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE]; -static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE]; - -/* - * Let's power down on idle, but only if we are really - * idle, because once we start down the path of - * going idle we continue to do idle even if we get - * a clock tick interrupt . . - */ -void omap_pm_idle(void) -{ - int (*func_ptr)(void) = 0; - unsigned int mask32 = 0; - - /* - * If the DSP is being used let's just idle the CPU, the overhead - * to wake up from Big Sleep is big, milliseconds versus micro - * seconds for wait for interrupt. - */ - - local_irq_disable(); - local_fiq_disable(); - if (need_resched()) { - local_fiq_enable(); - local_irq_enable(); - return; - } - mask32 = omap_readl(ARM_SYSST); - - /* - * Since an interrupt may set up a timer, we don't want to - * reprogram the hardware timer with interrupts enabled. - * Re-enable interrupts only after returning from idle. - */ - timer_dyn_reprogram(); - - if ((mask32 & DSP_IDLE) == 0) { - __asm__ volatile ("mcr p15, 0, r0, c7, c0, 4"); - } else { - - if (cpu_is_omap1510()) { - func_ptr = (void *)(OMAP1510_SRAM_IDLE_SUSPEND); - } else if (cpu_is_omap1610() || cpu_is_omap1710()) { - func_ptr = (void *)(OMAP1610_SRAM_IDLE_SUSPEND); - } else if (cpu_is_omap5912()) { - func_ptr = (void *)(OMAP5912_SRAM_IDLE_SUSPEND); - } - - func_ptr(); - } - local_fiq_enable(); - local_irq_enable(); -} - -/* - * Configuration of the wakeup event is board specific. For the - * moment we put it into this helper function. Later it may move - * to board specific files. - */ -static void omap_pm_wakeup_setup(void) -{ - /* - * Enable ARM XOR clock and release peripheral from reset by - * writing 1 to PER_EN bit in ARM_RSTCT2, this is required - * for UART configuration to use UART2 to wake up. - */ - - omap_writel(omap_readl(ARM_IDLECT2) | ENABLE_XORCLK, ARM_IDLECT2); - omap_writel(omap_readl(ARM_RSTCT2) | PER_EN, ARM_RSTCT2); - omap_writew(MODEM_32K_EN, ULPD_CLOCK_CTRL); - - /* - * Turn off all interrupts except L1-2nd level cascade, - * and the L2 wakeup interrupts: keypad and UART2. - */ - - omap_writel(~IRQ_LEVEL2, OMAP_IH1_MIR); - - if (cpu_is_omap1510()) { - omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_MIR); - } - - if (cpu_is_omap16xx()) { - omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_0_MIR); - - omap_writel(~0x0, OMAP_IH2_1_MIR); - omap_writel(~0x0, OMAP_IH2_2_MIR); - omap_writel(~0x0, OMAP_IH2_3_MIR); - } - - /* New IRQ agreement */ - omap_writel(1, OMAP_IH1_CONTROL); - - /* external PULL to down, bit 22 = 0 */ - omap_writel(omap_readl(PULL_DWN_CTRL_2) & ~(1<<22), PULL_DWN_CTRL_2); -} - -void omap_pm_suspend(void) -{ - unsigned int mask32 = 0; - unsigned long arg0 = 0, arg1 = 0; - int (*func_ptr)(unsigned short, unsigned short) = 0; - unsigned short save_dsp_idlect2; - - printk("PM: OMAP%x is entering deep sleep now ...\n", system_rev); - - if (machine_is_omap_osk()) { - /* Stop LED1 (D9) blink */ - tps65010_set_led(LED1, OFF); - } - - /* - * Step 1: turn off interrupts - */ - - local_irq_disable(); - local_fiq_disable(); - - /* - * Step 2: save registers - * - * The omap is a strange/beautiful device. The caches, memory - * and register state are preserved across power saves. - * We have to save and restore very little register state to - * idle the omap. - * - * Save interrupt, MPUI, ARM and UPLD control registers. - */ - - if (cpu_is_omap1510()) { - MPUI1510_SAVE(OMAP_IH1_MIR); - MPUI1510_SAVE(OMAP_IH2_MIR); - MPUI1510_SAVE(MPUI_CTRL); - MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); - MPUI1510_SAVE(MPUI_DSP_API_CONFIG); - MPUI1510_SAVE(EMIFS_CONFIG); - MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); - } else if (cpu_is_omap16xx()) { - MPUI1610_SAVE(OMAP_IH1_MIR); - MPUI1610_SAVE(OMAP_IH2_0_MIR); - MPUI1610_SAVE(OMAP_IH2_1_MIR); - MPUI1610_SAVE(OMAP_IH2_2_MIR); - MPUI1610_SAVE(OMAP_IH2_3_MIR); - MPUI1610_SAVE(MPUI_CTRL); - MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); - MPUI1610_SAVE(MPUI_DSP_API_CONFIG); - MPUI1610_SAVE(EMIFS_CONFIG); - MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); - } - - ARM_SAVE(ARM_CKCTL); - ARM_SAVE(ARM_IDLECT1); - ARM_SAVE(ARM_IDLECT2); - ARM_SAVE(ARM_EWUPCT); - ARM_SAVE(ARM_RSTCT1); - ARM_SAVE(ARM_RSTCT2); - ARM_SAVE(ARM_SYSST); - ULPD_SAVE(ULPD_CLOCK_CTRL); - ULPD_SAVE(ULPD_STATUS_REQ); - - /* - * Step 3: LOW_PWR signal enabling - * - * Allow the LOW_PWR signal to be visible on MPUIO5 ball. - */ - if (cpu_is_omap1510()) { - /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ - omap_writew(omap_readw(ULPD_POWER_CTRL) | - OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); - } else if (cpu_is_omap16xx()) { - /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ - omap_writew(omap_readw(ULPD_POWER_CTRL) | - OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); - } - - /* configure LOW_PWR pin */ - omap_cfg_reg(T20_1610_LOW_PWR); - - /* - * Step 4: OMAP DSP Shutdown - */ - - /* Set DSP_RST = 1 and DSP_EN = 0, put DSP block into reset */ - omap_writel((omap_readl(ARM_RSTCT1) | DSP_RST) & ~DSP_ENABLE, - ARM_RSTCT1); - - /* Set DSP boot mode to DSP-IDLE, DSP_BOOT_MODE = 0x2 */ - omap_writel(DSP_IDLE_MODE, MPUI_DSP_BOOT_CONFIG); - - /* Set EN_DSPCK = 0, stop DSP block clock */ - omap_writel(omap_readl(ARM_CKCTL) & ~DSP_CLOCK_ENABLE, ARM_CKCTL); - - /* Stop any DSP domain clocks */ - omap_writel(omap_readl(ARM_IDLECT2) | (1< 0 (WDT clock) */ - mask32 |= (1< 1 (XORPCK clock) */ - mask32 &= ~(1< 0 (MPUPER_CK clock) */ - mask32 &= ~(1< 0 (LCDC clock) */ - mask32 &= ~(1< 0 (local bus clock) */ - mask32 |= (1< 1 (MPUI clock) */ - mask32 &= ~(1< 0 (MPU timer clock) */ - mask32 &= ~(1< 0 (DMAC clock) */ - mask32 &= ~(1< 0 (GPIO clock) */ - omap_writel(mask32, ARM_IDLECT2); - - /* disable ARM watchdog */ - omap_writel(0x00F5, OMAP_WDT_TIMER_MODE); - omap_writel(0x00A0, OMAP_WDT_TIMER_MODE); - - /* - * Step 6b: ARM and Traffic controller shutdown - * - * Step 6 continues here. Prepare jump to power management - * assembly code in internal SRAM. - * - * Since the omap_cpu_suspend routine has been copied to - * SRAM, we'll do an indirect procedure call to it and pass the - * contents of arm_idlect1 and arm_idlect2 so it can restore - * them when it wakes up and it will return. - */ - - arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1]; - arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2]; - - if (cpu_is_omap1510()) { - func_ptr = (void *)(OMAP1510_SRAM_API_SUSPEND); - } else if (cpu_is_omap1610() || cpu_is_omap1710()) { - func_ptr = (void *)(OMAP1610_SRAM_API_SUSPEND); - } else if (cpu_is_omap5912()) { - func_ptr = (void *)(OMAP5912_SRAM_API_SUSPEND); - } - - /* - * Step 6c: ARM and Traffic controller shutdown - * - * Jump to assembly code. The processor will stay there - * until wake up. - */ - - func_ptr(arg0, arg1); - - /* - * If we are here, processor is woken up! - */ - - if (cpu_is_omap1510()) { - /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ - omap_writew(omap_readw(ULPD_POWER_CTRL) & - ~OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); - } else if (cpu_is_omap16xx()) { - /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ - omap_writew(omap_readw(ULPD_POWER_CTRL) & - ~OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); - } - - - /* Restore DSP clocks */ - omap_writel(omap_readl(ARM_IDLECT2) | (1<= 1) { - *eof = 1; - return 0; - } - g_read_completed++; - - *my_first_byte = page_buffer; - return my_buffer_offset; -} - -static void omap_pm_init_proc(void) -{ - struct proc_dir_entry *entry; - - entry = create_proc_read_entry("driver/omap_pm", - S_IWUSR | S_IRUGO, NULL, - omap_pm_read_proc, 0); -} - -#endif /* DEBUG && CONFIG_PROC_FS */ - -/* - * omap_pm_prepare - Do preliminary suspend work. - * @state: suspend state we're entering. - * - */ -//#include - -static int omap_pm_prepare(suspend_state_t state) -{ - int error = 0; - - switch (state) - { - case PM_SUSPEND_STANDBY: - case PM_SUSPEND_MEM: - break; - - case PM_SUSPEND_DISK: - return -ENOTSUPP; - - default: - return -EINVAL; - } - - return error; -} - - -/* - * omap_pm_enter - Actually enter a sleep state. - * @state: State we're entering. - * - */ - -static int omap_pm_enter(suspend_state_t state) -{ - switch (state) - { - case PM_SUSPEND_STANDBY: - case PM_SUSPEND_MEM: - omap_pm_suspend(); - break; - - case PM_SUSPEND_DISK: - return -ENOTSUPP; - - default: - return -EINVAL; - } - - return 0; -} - - -/** - * omap_pm_finish - Finish up suspend sequence. - * @state: State we're coming out of. - * - * This is called after we wake back up (or if entering the sleep state - * failed). - */ - -static int omap_pm_finish(suspend_state_t state) -{ - return 0; -} - - -struct pm_ops omap_pm_ops ={ - .pm_disk_mode = 0, - .prepare = omap_pm_prepare, - .enter = omap_pm_enter, - .finish = omap_pm_finish, -}; - -static int __init omap_pm_init(void) -{ - printk("Power Management for TI OMAP.\n"); - pm_idle = omap_pm_idle; - /* - * We copy the assembler sleep/wakeup routines to SRAM. - * These routines need to be in SRAM as that's the only - * memory the MPU can see when it wakes up. - */ - -#ifdef CONFIG_ARCH_OMAP1510 - if (cpu_is_omap1510()) { - memcpy((void *)OMAP1510_SRAM_IDLE_SUSPEND, - omap1510_idle_loop_suspend, - omap1510_idle_loop_suspend_sz); - memcpy((void *)OMAP1510_SRAM_API_SUSPEND, omap1510_cpu_suspend, - omap1510_cpu_suspend_sz); - } else -#endif - if (cpu_is_omap1610() || cpu_is_omap1710()) { - memcpy((void *)OMAP1610_SRAM_IDLE_SUSPEND, - omap1610_idle_loop_suspend, - omap1610_idle_loop_suspend_sz); - memcpy((void *)OMAP1610_SRAM_API_SUSPEND, omap1610_cpu_suspend, - omap1610_cpu_suspend_sz); - } else if (cpu_is_omap5912()) { - memcpy((void *)OMAP5912_SRAM_IDLE_SUSPEND, - omap1610_idle_loop_suspend, - omap1610_idle_loop_suspend_sz); - memcpy((void *)OMAP5912_SRAM_API_SUSPEND, omap1610_cpu_suspend, - omap1610_cpu_suspend_sz); - } - - pm_set_ops(&omap_pm_ops); - -#if defined(DEBUG) && defined(CONFIG_PROC_FS) - omap_pm_init_proc(); -#endif - - return 0; -} -__initcall(omap_pm_init); - --- linux-2.6.13-rc2/arch/arm/mach-omap/sleep.S 2005-03-01 23:38:09.000000000 -0800 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,314 +0,0 @@ -/* - * linux/arch/arm/mach-omap/sleep.S - * - * Low-level OMAP1510/1610 sleep/wakeUp support - * - * Initial SA1110 code: - * Copyright (c) 2001 Cliff Brake - * - * Adapted for PXA by Nicolas Pitre: - * Copyright (c) 2002 Monta Vista Software, Inc. - * - * Support for OMAP1510/1610 by Dirk Behme - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include - - .text - -/* - * Forces OMAP into idle state - * - * omapXXXX_idle_loop_suspend() - * - * Note: This code get's copied to internal SRAM at boot. When the OMAP - * wakes up it continues execution at the point it went to sleep. - * - * Note: Because of slightly different configuration values we have - * processor specific functions here. - */ - -#ifdef CONFIG_ARCH_OMAP1510 -ENTRY(omap1510_idle_loop_suspend) - - stmfd sp!, {r0 - r12, lr} @ save registers on stack - - @ load base address of ARM_IDLECT1 and ARM_IDLECT2 - mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 - orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 - orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 - - @ turn off clock domains - @ get ARM_IDLECT2 into r2 - ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff - orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 - strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - - @ request ARM idle - @ get ARM_IDLECT1 into r1 - ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - orr r3, r1, #OMAP1510_IDLE_LOOP_REQUEST & 0xffff - strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - mov r5, #IDLE_WAIT_CYCLES & 0xff - orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 -l_1510: subs r5, r5, #1 - bne l_1510 -/* - * Let's wait for the next clock tick to wake us up. - */ - mov r0, #0 - mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt -/* - * omap1510_idle_loop_suspend()'s resume point. - * - * It will just start executing here, so we'll restore stuff from the - * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. - */ - - @ restore ARM_IDLECT1 and ARM_IDLECT2 and return - @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 - strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - ldmfd sp!, {r0 - r12, pc} @ restore regs and return - -ENTRY(omap1510_idle_loop_suspend_sz) - .word . - omap1510_idle_loop_suspend -#endif /* CONFIG_ARCH_OMAP1510 */ - -#if defined(CONFIG_ARCH_OMAP16XX) -ENTRY(omap1610_idle_loop_suspend) - - stmfd sp!, {r0 - r12, lr} @ save registers on stack - - @ load base address of ARM_IDLECT1 and ARM_IDLECT2 - mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 - orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 - orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 - - @ turn off clock domains - @ get ARM_IDLECT2 into r2 - ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff - orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00 - strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - - @ request ARM idle - @ get ARM_IDLECT1 into r1 - ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - orr r3, r1, #OMAP1610_IDLE_LOOP_REQUEST & 0xffff - strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - mov r5, #IDLE_WAIT_CYCLES & 0xff - orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 -l_1610: subs r5, r5, #1 - bne l_1610 -/* - * Let's wait for the next clock tick to wake us up. - */ - mov r0, #0 - mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt -/* - * omap1610_idle_loop_suspend()'s resume point. - * - * It will just start executing here, so we'll restore stuff from the - * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. - */ - - @ restore ARM_IDLECT1 and ARM_IDLECT2 and return - @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 - strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - ldmfd sp!, {r0 - r12, pc} @ restore regs and return - -ENTRY(omap1610_idle_loop_suspend_sz) - .word . - omap1610_idle_loop_suspend -#endif /* CONFIG_ARCH_OMAP16XX */ - -/* - * Forces OMAP into deep sleep state - * - * omapXXXX_cpu_suspend() - * - * The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed - * as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1 - * in register r1. - * - * Note: This code get's copied to internal SRAM at boot. When the OMAP - * wakes up it continues execution at the point it went to sleep. - * - * Note: Because of errata work arounds we have processor specific functions - * here. They are mostly the same, but slightly different. - * - */ - -#ifdef CONFIG_ARCH_OMAP1510 -ENTRY(omap1510_cpu_suspend) - - @ save registers on stack - stmfd sp!, {r0 - r12, lr} - - @ load base address of Traffic Controller - mov r4, #TCMIF_ASM_BASE & 0xff000000 - orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 - orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 - - @ work around errata of OMAP1510 PDE bit for TC shut down - @ clear PDE bit - ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - bic r5, r5, #PDE_BIT & 0xff - str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - - @ set PWD_EN bit - and r5, r5, #PWD_EN_BIT & 0xff - str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - - @ prepare to put SDRAM into self-refresh manually - ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] - orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 - orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff - str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] - - @ prepare to put EMIFS to Sleep - ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff - str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - - @ load base address of ARM_IDLECT1 and ARM_IDLECT2 - mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 - orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 - orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 - - @ turn off clock domains - mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff - orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 - strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - - @ request ARM idle - mov r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff - orr r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00 - strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - mov r5, #IDLE_WAIT_CYCLES & 0xff - orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 -l_1510_2: - subs r5, r5, #1 - bne l_1510_2 -/* - * Let's wait for the next wake up event to wake us up. r0 can't be - * used here because r0 holds ARM_IDLECT1 - */ - mov r2, #0 - mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt -/* - * omap1510_cpu_suspend()'s resume point. - * - * It will just start executing here, so we'll restore stuff from the - * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. - */ - strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - @ restore regs and return - ldmfd sp!, {r0 - r12, pc} - -ENTRY(omap1510_cpu_suspend_sz) - .word . - omap1510_cpu_suspend -#endif /* CONFIG_ARCH_OMAP1510 */ - -#if defined(CONFIG_ARCH_OMAP16XX) -ENTRY(omap1610_cpu_suspend) - - @ save registers on stack - stmfd sp!, {r0 - r12, lr} - - @ load base address of Traffic Controller - mov r4, #TCMIF_ASM_BASE & 0xff000000 - orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 - orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 - - @ prepare to put SDRAM into self-refresh manually - ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] - orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 - orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff - str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] - - @ prepare to put EMIFS to Sleep - ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff - str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - - @ load base address of ARM_IDLECT1 and ARM_IDLECT2 - mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 - orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 - orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 - - @ turn off clock domains - mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff - orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00 - strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - - @ work around errata of OMAP1610/5912. Enable (!) peripheral - @ clock to let the chip go into deep sleep - ldrh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - orr r5,r5, #EN_PERCK_BIT & 0xff - strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - - @ request ARM idle - mov r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff - orr r3, r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff00 - strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - mov r5, #IDLE_WAIT_CYCLES & 0xff - orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 -l_1610_2: - subs r5, r5, #1 - bne l_1610_2 -/* - * Let's wait for the next wake up event to wake us up. r0 can't be - * used here because r0 holds ARM_IDLECT1 - */ - mov r2, #0 - mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt -/* - * omap1610_cpu_suspend()'s resume point. - * - * It will just start executing here, so we'll restore stuff from the - * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. - */ - strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - - @ restore regs and return - ldmfd sp!, {r0 - r12, pc} - -ENTRY(omap1610_cpu_suspend_sz) - .word . - omap1610_cpu_suspend -#endif /* CONFIG_ARCH_OMAP16XX */ --- linux-2.6.13-rc2/arch/arm/mach-omap/time.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,424 +0,0 @@ -/* - * linux/arch/arm/mach-omap/time.c - * - * OMAP Timers - * - * Copyright (C) 2004 Nokia Corporation - * Partial timer rewrite and additional dynamic tick timer support by - * Tony Lindgen and - * Tuukka Tikkanen - * - * MPU timer code based on the older MPU timer code for OMAP - * Copyright (C) 2000 RidgeRun, Inc. - * Author: Greg Lonnon - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -struct sys_timer omap_timer; - -#ifdef CONFIG_OMAP_MPU_TIMER - -/* - * --------------------------------------------------------------------------- - * MPU timer - * --------------------------------------------------------------------------- - */ -#define OMAP_MPU_TIMER1_BASE (0xfffec500) -#define OMAP_MPU_TIMER2_BASE (0xfffec600) -#define OMAP_MPU_TIMER3_BASE (0xfffec700) -#define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE -#define OMAP_MPU_TIMER_OFFSET 0x100 - -#define MPU_TIMER_FREE (1 << 6) -#define MPU_TIMER_CLOCK_ENABLE (1 << 5) -#define MPU_TIMER_AR (1 << 1) -#define MPU_TIMER_ST (1 << 0) - -/* cycles to nsec conversions taken from arch/i386/kernel/timers/timer_tsc.c, - * converted to use kHz by Kevin Hilman */ -/* convert from cycles(64bits) => nanoseconds (64bits) - * basic equation: - * ns = cycles / (freq / ns_per_sec) - * ns = cycles * (ns_per_sec / freq) - * ns = cycles * (10^9 / (cpu_khz * 10^3)) - * ns = cycles * (10^6 / cpu_khz) - * - * Then we use scaling math (suggested by george at mvista.com) to get: - * ns = cycles * (10^6 * SC / cpu_khz / SC - * ns = cycles * cyc2ns_scale / SC - * - * And since SC is a constant power of two, we can convert the div - * into a shift. - * -johnstul at us.ibm.com "math is hard, lets go shopping!" - */ -static unsigned long cyc2ns_scale; -#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ - -static inline void set_cyc2ns_scale(unsigned long cpu_khz) -{ - cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz; -} - -static inline unsigned long long cycles_2_ns(unsigned long long cyc) -{ - return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; -} - -/* - * MPU_TICKS_PER_SEC must be an even number, otherwise machinecycles_to_usecs - * will break. On P2, the timer count rate is 6.5 MHz after programming PTV - * with 0. This divides the 13MHz input by 2, and is undocumented. - */ -#ifdef CONFIG_MACH_OMAP_PERSEUS2 -/* REVISIT: This ifdef construct should be replaced by a query to clock - * framework to see if timer base frequency is 12.0, 13.0 or 19.2 MHz. - */ -#define MPU_TICKS_PER_SEC (13000000 / 2) -#else -#define MPU_TICKS_PER_SEC (12000000 / 2) -#endif - -#define MPU_TIMER_TICK_PERIOD ((MPU_TICKS_PER_SEC / HZ) - 1) - -typedef struct { - u32 cntl; /* CNTL_TIMER, R/W */ - u32 load_tim; /* LOAD_TIM, W */ - u32 read_tim; /* READ_TIM, R */ -} omap_mpu_timer_regs_t; - -#define omap_mpu_timer_base(n) \ -((volatile omap_mpu_timer_regs_t*)IO_ADDRESS(OMAP_MPU_TIMER_BASE + \ - (n)*OMAP_MPU_TIMER_OFFSET)) - -static inline unsigned long omap_mpu_timer_read(int nr) -{ - volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr); - return timer->read_tim; -} - -static inline void omap_mpu_timer_start(int nr, unsigned long load_val) -{ - volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr); - - timer->cntl = MPU_TIMER_CLOCK_ENABLE; - udelay(1); - timer->load_tim = load_val; - udelay(1); - timer->cntl = (MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_AR | MPU_TIMER_ST); -} - -unsigned long omap_mpu_timer_ticks_to_usecs(unsigned long nr_ticks) -{ - unsigned long long nsec; - - nsec = cycles_2_ns((unsigned long long)nr_ticks); - return (unsigned long)nsec / 1000; -} - -/* - * Last processed system timer interrupt - */ -static unsigned long omap_mpu_timer_last = 0; - -/* - * Returns elapsed usecs since last system timer interrupt - */ -static unsigned long omap_mpu_timer_gettimeoffset(void) -{ - unsigned long now = 0 - omap_mpu_timer_read(0); - unsigned long elapsed = now - omap_mpu_timer_last; - - return omap_mpu_timer_ticks_to_usecs(elapsed); -} - -/* - * Elapsed time between interrupts is calculated using timer0. - * Latency during the interrupt is calculated using timer1. - * Both timer0 and timer1 are counting at 6MHz (P2 6.5MHz). - */ -static irqreturn_t omap_mpu_timer_interrupt(int irq, void *dev_id, - struct pt_regs *regs) -{ - unsigned long now, latency; - - write_seqlock(&xtime_lock); - now = 0 - omap_mpu_timer_read(0); - latency = MPU_TICKS_PER_SEC / HZ - omap_mpu_timer_read(1); - omap_mpu_timer_last = now - latency; - timer_tick(regs); - write_sequnlock(&xtime_lock); - - return IRQ_HANDLED; -} - -static struct irqaction omap_mpu_timer_irq = { - .name = "mpu timer", - .flags = SA_INTERRUPT | SA_TIMER, - .handler = omap_mpu_timer_interrupt, -}; - -static unsigned long omap_mpu_timer1_overflows; -static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id, - struct pt_regs *regs) -{ - omap_mpu_timer1_overflows++; - return IRQ_HANDLED; -} - -static struct irqaction omap_mpu_timer1_irq = { - .name = "mpu timer1 overflow", - .flags = SA_INTERRUPT, - .handler = omap_mpu_timer1_interrupt, -}; - -static __init void omap_init_mpu_timer(void) -{ - set_cyc2ns_scale(MPU_TICKS_PER_SEC / 1000); - omap_timer.offset = omap_mpu_timer_gettimeoffset; - setup_irq(INT_TIMER1, &omap_mpu_timer1_irq); - setup_irq(INT_TIMER2, &omap_mpu_timer_irq); - omap_mpu_timer_start(0, 0xffffffff); - omap_mpu_timer_start(1, MPU_TIMER_TICK_PERIOD); -} - -/* - * Scheduler clock - returns current time in nanosec units. - */ -unsigned long long sched_clock(void) -{ - unsigned long ticks = 0 - omap_mpu_timer_read(0); - unsigned long long ticks64; - - ticks64 = omap_mpu_timer1_overflows; - ticks64 <<= 32; - ticks64 |= ticks; - - return cycles_2_ns(ticks64); -} -#endif /* CONFIG_OMAP_MPU_TIMER */ - -#ifdef CONFIG_OMAP_32K_TIMER - -#ifdef CONFIG_ARCH_OMAP1510 -#error OMAP 32KHz timer does not currently work on 1510! -#endif - -/* - * --------------------------------------------------------------------------- - * 32KHz OS timer - * - * This currently works only on 16xx, as 1510 does not have the continuous - * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track - * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer - * on 1510 would be possible, but the timer would not be as accurate as - * with the 32KHz synchronized timer. - * --------------------------------------------------------------------------- - */ -#define OMAP_32K_TIMER_BASE 0xfffb9000 -#define OMAP_32K_TIMER_CR 0x08 -#define OMAP_32K_TIMER_TVR 0x00 -#define OMAP_32K_TIMER_TCR 0x04 - -#define OMAP_32K_TICKS_PER_HZ (32768 / HZ) - -/* - * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 - * so with HZ = 100, TVR = 327.68. - */ -#define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) -#define TIMER_32K_SYNCHRONIZED 0xfffbc410 - -#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ - (((nr_jiffies) * (clock_rate)) / HZ) - -static inline void omap_32k_timer_write(int val, int reg) -{ - omap_writew(val, reg + OMAP_32K_TIMER_BASE); -} - -static inline unsigned long omap_32k_timer_read(int reg) -{ - return omap_readl(reg + OMAP_32K_TIMER_BASE) & 0xffffff; -} - -/* - * The 32KHz synchronized timer is an additional timer on 16xx. - * It is always running. - */ -static inline unsigned long omap_32k_sync_timer_read(void) -{ - return omap_readl(TIMER_32K_SYNCHRONIZED); -} - -static inline void omap_32k_timer_start(unsigned long load_val) -{ - omap_32k_timer_write(load_val, OMAP_32K_TIMER_TVR); - omap_32k_timer_write(0x0f, OMAP_32K_TIMER_CR); -} - -static inline void omap_32k_timer_stop(void) -{ - omap_32k_timer_write(0x0, OMAP_32K_TIMER_CR); -} - -/* - * Rounds down to nearest usec - */ -static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k) -{ - return (ticks_32k * 5*5*5*5*5*5) >> 9; -} - -static unsigned long omap_32k_last_tick = 0; - -/* - * Returns elapsed usecs since last 32k timer interrupt - */ -static unsigned long omap_32k_timer_gettimeoffset(void) -{ - unsigned long now = omap_32k_sync_timer_read(); - return omap_32k_ticks_to_usecs(now - omap_32k_last_tick); -} - -/* - * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this - * function is also called from other interrupts to remove latency - * issues with dynamic tick. In the dynamic tick case, we need to lock - * with irqsave. - */ -static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id, - struct pt_regs *regs) -{ - unsigned long flags; - unsigned long now; - - write_seqlock_irqsave(&xtime_lock, flags); - now = omap_32k_sync_timer_read(); - - while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) { - omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ; - timer_tick(regs); - } - - /* Restart timer so we don't drift off due to modulo or dynamic tick. - * By default we program the next timer to be continuous to avoid - * latencies during high system load. During dynamic tick operation the - * continuous timer can be overridden from pm_idle to be longer. - */ - omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now); - write_sequnlock_irqrestore(&xtime_lock, flags); - - return IRQ_HANDLED; -} - -#ifdef CONFIG_NO_IDLE_HZ -/* - * Programs the next timer interrupt needed. Called when dynamic tick is - * enabled, and to reprogram the ticks to skip from pm_idle. Note that - * we can keep the timer continuous, and don't need to set it to run in - * one-shot mode. This is because the timer will get reprogrammed again - * after next interrupt. - */ -void omap_32k_timer_reprogram(unsigned long next_tick) -{ - omap_32k_timer_start(JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1); -} - -static struct irqaction omap_32k_timer_irq; -extern struct timer_update_handler timer_update; - -static int omap_32k_timer_enable_dyn_tick(void) -{ - /* No need to reprogram timer, just use the next interrupt */ - return 0; -} - -static int omap_32k_timer_disable_dyn_tick(void) -{ - omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); - return 0; -} - -static struct dyn_tick_timer omap_dyn_tick_timer = { - .enable = omap_32k_timer_enable_dyn_tick, - .disable = omap_32k_timer_disable_dyn_tick, - .reprogram = omap_32k_timer_reprogram, - .handler = omap_32k_timer_interrupt, -}; -#endif /* CONFIG_NO_IDLE_HZ */ - -static struct irqaction omap_32k_timer_irq = { - .name = "32KHz timer", - .flags = SA_INTERRUPT | SA_TIMER, - .handler = omap_32k_timer_interrupt, -}; - -static __init void omap_init_32k_timer(void) -{ - -#ifdef CONFIG_NO_IDLE_HZ - omap_timer.dyn_tick = &omap_dyn_tick_timer; -#endif - - setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); - omap_timer.offset = omap_32k_timer_gettimeoffset; - omap_32k_last_tick = omap_32k_sync_timer_read(); - omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); -} -#endif /* CONFIG_OMAP_32K_TIMER */ - -/* - * --------------------------------------------------------------------------- - * Timer initialization - * --------------------------------------------------------------------------- - */ -void __init omap_timer_init(void) -{ -#if defined(CONFIG_OMAP_MPU_TIMER) - omap_init_mpu_timer(); -#elif defined(CONFIG_OMAP_32K_TIMER) - omap_init_32k_timer(); -#else -#error No system timer selected in Kconfig! -#endif -} - -struct sys_timer omap_timer = { - .init = omap_timer_init, - .offset = NULL, /* Initialized later */ -}; --- linux-2.6.13-rc2/arch/arm/mach-omap/usb.c 2005-07-05 22:22:07.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,593 +0,0 @@ -/* - * arch/arm/mach-omap/usb.c -- platform level USB initialization - * - * Copyright (C) 2004 Texas Instruments, Inc. - * - * 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 - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -/* These routines should handle the standard chip-specific modes - * for usb0/1/2 ports, covering basic mux and transceiver setup. - * - * Some board-*.c files will need to set up additional mux options, - * like for suspend handling, vbus sensing, GPIOs, and the D+ pullup. - */ - -/* TESTED ON: - * - 1611B H2 (with usb1 mini-AB) using standard Mini-B or OTG cables - * - 5912 OSK OHCI (with usb0 standard-A), standard A-to-B cables - * - 5912 OSK UDC, with *nonstandard* A-to-A cable - * - 1510 Innovator UDC with bundled usb0 cable - * - 1510 Innovator OHCI with bundled usb1/usb2 cable - * - 1510 Innovator OHCI with custom usb0 cable, feeding 5V VBUS - * - 1710 custom development board using alternate pin group - * - 1710 H3 (with usb1 mini-AB) using standard Mini-B or OTG cables - */ - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_ARCH_OMAP_OTG - -static struct otg_transceiver *xceiv; - -/** - * otg_get_transceiver - find the (single) OTG transceiver driver - * - * Returns the transceiver driver, after getting a refcount to it; or - * null if there is no such transceiver. The caller is responsible for - * releasing that count. - */ -struct otg_transceiver *otg_get_transceiver(void) -{ - if (xceiv) - get_device(xceiv->dev); - return xceiv; -} -EXPORT_SYMBOL(otg_get_transceiver); - -int otg_set_transceiver(struct otg_transceiver *x) -{ - if (xceiv && x) - return -EBUSY; - xceiv = x; - return 0; -} -EXPORT_SYMBOL(otg_set_transceiver); - -#endif - -/*-------------------------------------------------------------------------*/ - -static u32 __init omap_usb0_init(unsigned nwires, unsigned is_device) -{ - u32 syscon1 = 0; - - if (nwires == 0) { - if (!cpu_is_omap15xx()) { - /* pulldown D+/D- */ - USB_TRANSCEIVER_CTRL_REG &= ~(3 << 1); - } - return 0; - } - - if (is_device) - omap_cfg_reg(W4_USB_PUEN); - - /* internal transceiver */ - if (nwires == 2) { - // omap_cfg_reg(P9_USB_DP); - // omap_cfg_reg(R8_USB_DM); - - if (cpu_is_omap15xx()) { - /* This works on 1510-Innovator */ - return 0; - } - - /* NOTES: - * - peripheral should configure VBUS detection! - * - only peripherals may use the internal D+/D- pulldowns - * - OTG support on this port not yet written - */ - - USB_TRANSCEIVER_CTRL_REG &= ~(7 << 4); - if (!is_device) - USB_TRANSCEIVER_CTRL_REG |= (3 << 1); - - return 3 << 16; - } - - /* alternate pin config, external transceiver */ - if (cpu_is_omap15xx()) { - printk(KERN_ERR "no usb0 alt pin config on 15xx\n"); - return 0; - } - - omap_cfg_reg(V6_USB0_TXD); - omap_cfg_reg(W9_USB0_TXEN); - omap_cfg_reg(W5_USB0_SE0); - - /* NOTE: SPEED and SUSP aren't configured here */ - - if (nwires != 3) - omap_cfg_reg(Y5_USB0_RCV); - if (nwires != 6) - USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; - - switch (nwires) { - case 3: - syscon1 = 2; - break; - case 4: - syscon1 = 1; - break; - case 6: - syscon1 = 3; - omap_cfg_reg(AA9_USB0_VP); - omap_cfg_reg(R9_USB0_VM); - USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; - break; - default: - printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", - 0, nwires); - } - return syscon1 << 16; -} - -static u32 __init omap_usb1_init(unsigned nwires) -{ - u32 syscon1 = 0; - - if (nwires != 6 && !cpu_is_omap15xx()) - USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB1_UNI_R; - if (nwires == 0) - return 0; - - /* external transceiver */ - omap_cfg_reg(USB1_TXD); - omap_cfg_reg(USB1_TXEN); - if (cpu_is_omap15xx()) { - omap_cfg_reg(USB1_SEO); - omap_cfg_reg(USB1_SPEED); - // SUSP - } else if (cpu_is_omap1610() || cpu_is_omap5912()) { - omap_cfg_reg(W13_1610_USB1_SE0); - omap_cfg_reg(R13_1610_USB1_SPEED); - // SUSP - } else if (cpu_is_omap1710()) { - omap_cfg_reg(R13_1710_USB1_SE0); - // SUSP - } else { - pr_debug("usb unrecognized\n"); - } - if (nwires != 3) - omap_cfg_reg(USB1_RCV); - - switch (nwires) { - case 3: - syscon1 = 2; - break; - case 4: - syscon1 = 1; - break; - case 6: - syscon1 = 3; - omap_cfg_reg(USB1_VP); - omap_cfg_reg(USB1_VM); - if (!cpu_is_omap15xx()) - USB_TRANSCEIVER_CTRL_REG |= CONF_USB1_UNI_R; - break; - default: - printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", - 1, nwires); - } - return syscon1 << 20; -} - -static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup) -{ - u32 syscon1 = 0; - - /* NOTE erratum: must leave USB2_UNI_R set if usb0 in use */ - if (alt_pingroup || nwires == 0) - return 0; - if (nwires != 6 && !cpu_is_omap15xx()) - USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; - - /* external transceiver */ - if (cpu_is_omap15xx()) { - omap_cfg_reg(USB2_TXD); - omap_cfg_reg(USB2_TXEN); - omap_cfg_reg(USB2_SEO); - if (nwires != 3) - omap_cfg_reg(USB2_RCV); - /* there is no USB2_SPEED */ - } else if (cpu_is_omap16xx()) { - omap_cfg_reg(V6_USB2_TXD); - omap_cfg_reg(W9_USB2_TXEN); - omap_cfg_reg(W5_USB2_SE0); - if (nwires != 3) - omap_cfg_reg(Y5_USB2_RCV); - // FIXME omap_cfg_reg(USB2_SPEED); - } else { - pr_debug("usb unrecognized\n"); - } - // omap_cfg_reg(USB2_SUSP); - - switch (nwires) { - case 3: - syscon1 = 2; - break; - case 4: - syscon1 = 1; - break; - case 6: - syscon1 = 3; - if (cpu_is_omap15xx()) { - omap_cfg_reg(USB2_VP); - omap_cfg_reg(USB2_VM); - } else { - omap_cfg_reg(AA9_USB2_VP); - omap_cfg_reg(R9_USB2_VM); - USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; - } - break; - default: - printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", - 2, nwires); - } - return syscon1 << 24; -} - -/*-------------------------------------------------------------------------*/ - -#if defined(CONFIG_USB_GADGET_OMAP) || \ - defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) || \ - (defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG)) -static void usb_release(struct device *dev) -{ - /* normally not freed */ -} -#endif - -#ifdef CONFIG_USB_GADGET_OMAP - -static struct resource udc_resources[] = { - /* order is significant! */ - { /* registers */ - .start = UDC_BASE, - .end = UDC_BASE + 0xff, - .flags = IORESOURCE_MEM, - }, { /* general IRQ */ - .start = IH2_BASE + 20, - .flags = IORESOURCE_IRQ, - }, { /* PIO IRQ */ - .start = IH2_BASE + 30, - .flags = IORESOURCE_IRQ, - }, { /* SOF IRQ */ - .start = IH2_BASE + 29, - .flags = IORESOURCE_IRQ, - }, -}; - -static u64 udc_dmamask = ~(u32)0; - -static struct platform_device udc_device = { - .name = "omap_udc", - .id = -1, - .dev = { - .release = usb_release, - .dma_mask = &udc_dmamask, - .coherent_dma_mask = 0xffffffff, - }, - .num_resources = ARRAY_SIZE(udc_resources), - .resource = udc_resources, -}; - -#endif - -#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) - -/* The dmamask must be set for OHCI to work */ -static u64 ohci_dmamask = ~(u32)0; - -static struct resource ohci_resources[] = { - { - .start = OMAP_OHCI_BASE, - .end = OMAP_OHCI_BASE + 4096, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_USB_HHC_1, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device ohci_device = { - .name = "ohci", - .id = -1, - .dev = { - .release = usb_release, - .dma_mask = &ohci_dmamask, - .coherent_dma_mask = 0xffffffff, - }, - .num_resources = ARRAY_SIZE(ohci_resources), - .resource = ohci_resources, -}; - -#endif - -#if defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG) - -static struct resource otg_resources[] = { - /* order is significant! */ - { - .start = OTG_BASE, - .end = OTG_BASE + 0xff, - .flags = IORESOURCE_MEM, - }, { - .start = IH2_BASE + 8, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device otg_device = { - .name = "omap_otg", - .id = -1, - .dev = { - .release = usb_release, - }, - .num_resources = ARRAY_SIZE(otg_resources), - .resource = otg_resources, -}; - -#endif - -/*-------------------------------------------------------------------------*/ - -#define ULPD_CLOCK_CTRL_REG __REG16(ULPD_CLOCK_CTRL) -#define ULPD_SOFT_REQ_REG __REG16(ULPD_SOFT_REQ) - - -// FIXME correct answer depends on hmc_mode, -// as does any nonzero value for config->otg port number -#ifdef CONFIG_USB_GADGET_OMAP -#define is_usb0_device(config) 1 -#else -#define is_usb0_device(config) 0 -#endif - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_ARCH_OMAP_OTG - -void __init -omap_otg_init(struct omap_usb_config *config) -{ - u32 syscon = OTG_SYSCON_1_REG & 0xffff; - int status; - int alt_pingroup = 0; - - /* NOTE: no bus or clock setup (yet?) */ - - syscon = OTG_SYSCON_1_REG & 0xffff; - if (!(syscon & OTG_RESET_DONE)) - pr_debug("USB resets not complete?\n"); - - // OTG_IRQ_EN_REG = 0; - - /* pin muxing and transceiver pinouts */ - if (config->pins[0] > 2) /* alt pingroup 2 */ - alt_pingroup = 1; - syscon |= omap_usb0_init(config->pins[0], is_usb0_device(config)); - syscon |= omap_usb1_init(config->pins[1]); - syscon |= omap_usb2_init(config->pins[2], alt_pingroup); - pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); - OTG_SYSCON_1_REG = syscon; - - syscon = config->hmc_mode; - syscon |= USBX_SYNCHRO | (4 << 16) /* B_ASE0_BRST */; -#ifdef CONFIG_USB_OTG - if (config->otg) - syscon |= OTG_EN; -#endif - pr_debug("USB_TRANSCEIVER_CTRL_REG = %03x\n", USB_TRANSCEIVER_CTRL_REG); - pr_debug("OTG_SYSCON_2_REG = %08x\n", syscon); - OTG_SYSCON_2_REG = syscon; - - printk("USB: hmc %d", config->hmc_mode); - if (alt_pingroup) - printk(", usb2 alt %d wires", config->pins[2]); - else if (config->pins[0]) - printk(", usb0 %d wires%s", config->pins[0], - is_usb0_device(config) ? " (dev)" : ""); - if (config->pins[1]) - printk(", usb1 %d wires", config->pins[1]); - if (!alt_pingroup && config->pins[2]) - printk(", usb2 %d wires", config->pins[2]); - if (config->otg) - printk(", Mini-AB on usb%d", config->otg - 1); - printk("\n"); - - /* leave USB clocks/controllers off until needed */ - ULPD_SOFT_REQ_REG &= ~SOFT_USB_CLK_REQ; - ULPD_CLOCK_CTRL_REG &= ~USB_MCLK_EN; - ULPD_CLOCK_CTRL_REG |= DIS_USB_PVCI_CLK; - syscon = OTG_SYSCON_1_REG; - syscon |= HST_IDLE_EN|DEV_IDLE_EN|OTG_IDLE_EN; - -#ifdef CONFIG_USB_GADGET_OMAP - if (config->otg || config->register_dev) { - syscon &= ~DEV_IDLE_EN; - udc_device.dev.platform_data = config; - /* FIXME patch IRQ numbers for omap730 */ - status = platform_device_register(&udc_device); - if (status) - pr_debug("can't register UDC device, %d\n", status); - } -#endif - -#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) - if (config->otg || config->register_host) { - syscon &= ~HST_IDLE_EN; - ohci_device.dev.platform_data = config; - if (cpu_is_omap730()) - ohci_resources[1].start = INT_730_USB_HHC_1; - status = platform_device_register(&ohci_device); - if (status) - pr_debug("can't register OHCI device, %d\n", status); - } -#endif - -#ifdef CONFIG_USB_OTG - if (config->otg) { - syscon &= ~OTG_IDLE_EN; - otg_device.dev.platform_data = config; - if (cpu_is_omap730()) - otg_resources[1].start = INT_730_USB_OTG; - status = platform_device_register(&otg_device); - if (status) - pr_debug("can't register OTG device, %d\n", status); - } -#endif - pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); - OTG_SYSCON_1_REG = syscon; - - status = 0; -} - -#else -static inline void omap_otg_init(struct omap_usb_config *config) {} -#endif - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_ARCH_OMAP1510 - -#define ULPD_DPLL_CTRL_REG __REG16(ULPD_DPLL_CTRL) -#define DPLL_IOB (1 << 13) -#define DPLL_PLL_ENABLE (1 << 4) -#define DPLL_LOCK (1 << 0) - -#define ULPD_APLL_CTRL_REG __REG16(ULPD_APLL_CTRL) -#define APLL_NDPLL_SWITCH (1 << 0) - - -static void __init omap_1510_usb_init(struct omap_usb_config *config) -{ - int status; - unsigned int val; - - omap_usb0_init(config->pins[0], is_usb0_device(config)); - omap_usb1_init(config->pins[1]); - omap_usb2_init(config->pins[2], 0); - - val = omap_readl(MOD_CONF_CTRL_0) & ~(0x3f << 1); - val |= (config->hmc_mode << 1); - omap_writel(val, MOD_CONF_CTRL_0); - - printk("USB: hmc %d", config->hmc_mode); - if (config->pins[0]) - printk(", usb0 %d wires%s", config->pins[0], - is_usb0_device(config) ? " (dev)" : ""); - if (config->pins[1]) - printk(", usb1 %d wires", config->pins[1]); - if (config->pins[2]) - printk(", usb2 %d wires", config->pins[2]); - printk("\n"); - - /* use DPLL for 48 MHz function clock */ - pr_debug("APLL %04x DPLL %04x REQ %04x\n", ULPD_APLL_CTRL_REG, - ULPD_DPLL_CTRL_REG, ULPD_SOFT_REQ_REG); - ULPD_APLL_CTRL_REG &= ~APLL_NDPLL_SWITCH; - ULPD_DPLL_CTRL_REG |= DPLL_IOB | DPLL_PLL_ENABLE; - ULPD_SOFT_REQ_REG |= SOFT_UDC_REQ | SOFT_DPLL_REQ; - while (!(ULPD_DPLL_CTRL_REG & DPLL_LOCK)) - cpu_relax(); - -#ifdef CONFIG_USB_GADGET_OMAP - if (config->register_dev) { - udc_device.dev.platform_data = config; - status = platform_device_register(&udc_device); - if (status) - pr_debug("can't register UDC device, %d\n", status); - /* udc driver gates 48MHz by D+ pullup */ - } -#endif - -#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) - if (config->register_host) { - ohci_device.dev.platform_data = config; - status = platform_device_register(&ohci_device); - if (status) - pr_debug("can't register OHCI device, %d\n", status); - /* hcd explicitly gates 48MHz */ - } -#endif -} - -#else -static inline void omap_1510_usb_init(struct omap_usb_config *config) {} -#endif - -/*-------------------------------------------------------------------------*/ - -static struct omap_usb_config platform_data; - -static int __init -omap_usb_init(void) -{ - const struct omap_usb_config *config; - - config = omap_get_config(OMAP_TAG_USB, struct omap_usb_config); - if (config == NULL) { - printk(KERN_ERR "USB: No board-specific " - "platform config found\n"); - return -ENODEV; - } - platform_data = *config; - - if (cpu_is_omap730() || cpu_is_omap16xx()) - omap_otg_init(&platform_data); - else if (cpu_is_omap15xx()) - omap_1510_usb_init(&platform_data); - else { - printk(KERN_ERR "USB: No init for your chip yet\n"); - return -ENODEV; - } - return 0; -} - -subsys_initcall(omap_usb_init); --- linux-2.6.13-rc2/arch/arm/mach-pxa/corgi_ssp.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/arm/mach-pxa/corgi_ssp.c 2005-07-12 00:27:34.000000000 -0700 @@ -22,7 +22,7 @@ #include #include -static spinlock_t corgi_ssp_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(corgi_ssp_lock); static struct ssp_dev corgi_ssp_dev; static struct ssp_state corgi_ssp_state; --- linux-2.6.13-rc2/arch/arm/Makefile 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/Makefile 2005-07-12 00:26:30.000000000 -0700 @@ -91,7 +91,8 @@ textaddr-$(CONFIG_ARCH_FORTUNET) := 0x machine-$(CONFIG_ARCH_IOP3XX) := iop3xx machine-$(CONFIG_ARCH_IXP4XX) := ixp4xx machine-$(CONFIG_ARCH_IXP2000) := ixp2000 - machine-$(CONFIG_ARCH_OMAP) := omap + machine-$(CONFIG_ARCH_OMAP1) := omap1 + incdir-$(CONFIG_ARCH_OMAP) := omap machine-$(CONFIG_ARCH_S3C2410) := s3c2410 machine-$(CONFIG_ARCH_LH7A40X) := lh7a40x machine-$(CONFIG_ARCH_VERSATILE) := versatile @@ -142,6 +143,9 @@ core-$(CONFIG_FPE_NWFPE) += arch/arm/nwf core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ) core-$(CONFIG_VFP) += arch/arm/vfp/ +# If we have a common platform directory, then include it in the build. +core-$(CONFIG_ARCH_OMAP) += arch/arm/plat-omap/ + drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ drivers-$(CONFIG_ARCH_CLPS7500) += drivers/acorn/char/ drivers-$(CONFIG_ARCH_L7200) += drivers/acorn/char/ --- linux-2.6.13-rc2/arch/arm/mm/Kconfig 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mm/Kconfig 2005-07-12 00:26:30.000000000 -0700 @@ -101,7 +101,7 @@ config CPU_ARM922T # ARM925T config CPU_ARM925T - bool "Support ARM925T processor" if ARCH_OMAP + bool "Support ARM925T processor" if ARCH_OMAP1 depends on ARCH_OMAP1510 default y if ARCH_OMAP1510 select CPU_32v4 --- linux-2.6.13-rc2/arch/arm/mm/mm-armv.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mm/mm-armv.c 2005-07-12 00:26:30.000000000 -0700 @@ -399,7 +399,7 @@ static void __init build_mem_type_table( ecc_mask = 0; } - if (cpu_arch <= CPU_ARCH_ARMv5) { + if (cpu_arch <= CPU_ARCH_ARMv5TEJ) { for (i = 0; i < ARRAY_SIZE(mem_types); i++) { if (mem_types[i].prot_l1) mem_types[i].prot_l1 |= PMD_BIT4; @@ -584,7 +584,7 @@ void setup_mm_for_reboot(char mode) pmdval = (i << PGDIR_SHIFT) | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT; - if (cpu_arch <= CPU_ARCH_ARMv5) + if (cpu_arch <= CPU_ARCH_ARMv5TEJ) pmdval |= PMD_BIT4; pmd = pmd_off(pgd, i << PGDIR_SHIFT); pmd[0] = __pmd(pmdval); --- linux-2.6.13-rc2/arch/arm/mm/proc-v6.S 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/arm/mm/proc-v6.S 2005-07-12 00:26:30.000000000 -0700 @@ -200,7 +200,7 @@ __v6_setup: mcr p15, 0, r4, c2, c0, 1 @ load TTB1 #ifdef CONFIG_VFP mrc p15, 0, r0, c1, c0, 2 - orr r0, r0, #(3 << 20) + orr r0, r0, #(0xf << 20) mcr p15, 0, r0, c1, c0, 2 @ Enable full access to VFP #endif mrc p15, 0, r0, c1, c0, 0 @ read control register --- linux-2.6.13-rc2/arch/arm/nwfpe/fpmodule.c 2005-03-01 23:38:08.000000000 -0800 +++ devel/arch/arm/nwfpe/fpmodule.c 2005-07-12 00:26:40.000000000 -0700 @@ -24,7 +24,6 @@ #include "fpa11.h" #include -#include #include /* XXX */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/clock.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,1323 @@ +/* + * linux/arch/arm/plat-omap/clock.c + * + * Copyright (C) 2004 Nokia corporation + * Written by Tuukka Tikkanen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "clock.h" + +static LIST_HEAD(clocks); +static DECLARE_MUTEX(clocks_sem); +static DEFINE_SPINLOCK(clockfw_lock); +static void propagate_rate(struct clk * clk); +/* UART clock function */ +static int set_uart_rate(struct clk * clk, unsigned long rate); +/* External clock (MCLK & BCLK) functions */ +static int set_ext_clk_rate(struct clk * clk, unsigned long rate); +static long round_ext_clk_rate(struct clk * clk, unsigned long rate); +static void init_ext_clk(struct clk * clk); +/* MPU virtual clock functions */ +static int select_table_rate(struct clk * clk, unsigned long rate); +static long round_to_table_rate(struct clk * clk, unsigned long rate); +void clk_setdpll(__u16, __u16); + +static struct mpu_rate rate_table[] = { + /* MPU MHz, xtal MHz, dpll1 MHz, CKCTL, DPLL_CTL + * armdiv, dspdiv, dspmmu, tcdiv, perdiv, lcddiv + */ +#if defined(CONFIG_OMAP_ARM_216MHZ) + { 216000000, 12000000, 216000000, 0x050d, 0x2910 }, /* 1/1/2/2/2/8 */ +#endif +#if defined(CONFIG_OMAP_ARM_195MHZ) + { 195000000, 13000000, 195000000, 0x050e, 0x2790 }, /* 1/1/2/2/4/8 */ +#endif +#if defined(CONFIG_OMAP_ARM_192MHZ) + { 192000000, 19200000, 192000000, 0x050f, 0x2510 }, /* 1/1/2/2/8/8 */ + { 192000000, 12000000, 192000000, 0x050f, 0x2810 }, /* 1/1/2/2/8/8 */ + { 96000000, 12000000, 192000000, 0x055f, 0x2810 }, /* 2/2/2/2/8/8 */ + { 48000000, 12000000, 192000000, 0x0baf, 0x2810 }, /* 4/8/4/4/8/8 */ + { 24000000, 12000000, 192000000, 0x0fff, 0x2810 }, /* 8/8/8/8/8/8 */ +#endif +#if defined(CONFIG_OMAP_ARM_182MHZ) + { 182000000, 13000000, 182000000, 0x050e, 0x2710 }, /* 1/1/2/2/4/8 */ +#endif +#if defined(CONFIG_OMAP_ARM_168MHZ) + { 168000000, 12000000, 168000000, 0x010f, 0x2710 }, /* 1/1/1/2/8/8 */ +#endif +#if defined(CONFIG_OMAP_ARM_150MHZ) + { 150000000, 12000000, 150000000, 0x010a, 0x2cb0 }, /* 1/1/1/2/4/4 */ +#endif +#if defined(CONFIG_OMAP_ARM_120MHZ) + { 120000000, 12000000, 120000000, 0x010a, 0x2510 }, /* 1/1/1/2/4/4 */ +#endif +#if defined(CONFIG_OMAP_ARM_96MHZ) + { 96000000, 12000000, 96000000, 0x0005, 0x2410 }, /* 1/1/1/1/2/2 */ +#endif +#if defined(CONFIG_OMAP_ARM_60MHZ) + { 60000000, 12000000, 60000000, 0x0005, 0x2290 }, /* 1/1/1/1/2/2 */ +#endif +#if defined(CONFIG_OMAP_ARM_30MHZ) + { 30000000, 12000000, 60000000, 0x0555, 0x2290 }, /* 2/2/2/2/2/2 */ +#endif + { 0, 0, 0, 0, 0 }, +}; + + +static void ckctl_recalc(struct clk * clk); +int __clk_enable(struct clk *clk); +void __clk_disable(struct clk *clk); +void __clk_unuse(struct clk *clk); +int __clk_use(struct clk *clk); + + +static void followparent_recalc(struct clk * clk) +{ + clk->rate = clk->parent->rate; +} + + +static void watchdog_recalc(struct clk * clk) +{ + clk->rate = clk->parent->rate / 14; +} + +static void uart_recalc(struct clk * clk) +{ + unsigned int val = omap_readl(clk->enable_reg); + if (val & clk->enable_bit) + clk->rate = 48000000; + else + clk->rate = 12000000; +} + +static struct clk ck_ref = { + .name = "ck_ref", + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + ALWAYS_ENABLED, +}; + +static struct clk ck_dpll1 = { + .name = "ck_dpll1", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + RATE_PROPAGATES | ALWAYS_ENABLED, +}; + +static struct clk ck_dpll1out = { + .name = "ck_dpll1out", + .parent = &ck_dpll1, + .flags = CLOCK_IN_OMAP16XX, + .enable_reg = ARM_IDLECT2, + .enable_bit = EN_CKOUT_ARM, + .recalc = &followparent_recalc, +}; + +static struct clk arm_ck = { + .name = "arm_ck", + .parent = &ck_dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, + .rate_offset = CKCTL_ARMDIV_OFFSET, + .recalc = &ckctl_recalc, +}; + +static struct clk armper_ck = { + .name = "armper_ck", + .parent = &ck_dpll1, + .flags = CLOCK_IN_OMAP730 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + RATE_CKCTL, + .enable_reg = ARM_IDLECT2, + .enable_bit = EN_PERCK, + .rate_offset = CKCTL_PERDIV_OFFSET, + .recalc = &ckctl_recalc, +}; + +static struct clk arm_gpio_ck = { + .name = "arm_gpio_ck", + .parent = &ck_dpll1, + .flags = CLOCK_IN_OMAP1510, + .enable_reg = ARM_IDLECT2, + .enable_bit = EN_GPIOCK, + .recalc = &followparent_recalc, +}; + +static struct clk armxor_ck = { + .name = "armxor_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .enable_reg = ARM_IDLECT2, + .enable_bit = EN_XORPCK, + .recalc = &followparent_recalc, +}; + +static struct clk armtim_ck = { + .name = "armtim_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .enable_reg = ARM_IDLECT2, + .enable_bit = EN_TIMCK, + .recalc = &followparent_recalc, +}; + +static struct clk armwdt_ck = { + .name = "armwdt_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .enable_reg = ARM_IDLECT2, + .enable_bit = EN_WDTCK, + .recalc = &watchdog_recalc, +}; + +static struct clk arminth_ck16xx = { + .name = "arminth_ck", + .parent = &arm_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .recalc = &followparent_recalc, + /* Note: On 16xx the frequency can be divided by 2 by programming + * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1 + * + * 1510 version is in TC clocks. + */ +}; + +static struct clk dsp_ck = { + .name = "dsp_ck", + .parent = &ck_dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + RATE_CKCTL, + .enable_reg = ARM_CKCTL, + .enable_bit = EN_DSPCK, + .rate_offset = CKCTL_DSPDIV_OFFSET, + .recalc = &ckctl_recalc, +}; + +static struct clk dspmmu_ck = { + .name = "dspmmu_ck", + .parent = &ck_dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + RATE_CKCTL | ALWAYS_ENABLED, + .rate_offset = CKCTL_DSPMMUDIV_OFFSET, + .recalc = &ckctl_recalc, +}; + +static struct clk dspper_ck = { + .name = "dspper_ck", + .parent = &ck_dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + RATE_CKCTL | DSP_DOMAIN_CLOCK | VIRTUAL_IO_ADDRESS, + .enable_reg = DSP_IDLECT2, + .enable_bit = EN_PERCK, + .rate_offset = CKCTL_PERDIV_OFFSET, + .recalc = &followparent_recalc, + //.recalc = &ckctl_recalc, +}; + +static struct clk dspxor_ck = { + .name = "dspxor_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + DSP_DOMAIN_CLOCK | VIRTUAL_IO_ADDRESS, + .enable_reg = DSP_IDLECT2, + .enable_bit = EN_XORPCK, + .recalc = &followparent_recalc, +}; + +static struct clk dsptim_ck = { + .name = "dsptim_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + DSP_DOMAIN_CLOCK | VIRTUAL_IO_ADDRESS, + .enable_reg = DSP_IDLECT2, + .enable_bit = EN_DSPTIMCK, + .recalc = &followparent_recalc, +}; + +static struct clk tc_ck = { + .name = "tc_ck", + .parent = &ck_dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | + RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, + .rate_offset = CKCTL_TCDIV_OFFSET, + .recalc = &ckctl_recalc, +}; + +static struct clk arminth_ck1510 = { + .name = "arminth_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED, + .recalc = &followparent_recalc, + /* Note: On 1510 the frequency follows TC_CK + * + * 16xx version is in MPU clocks. + */ +}; + +static struct clk tipb_ck = { + .name = "tibp_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED, + .recalc = &followparent_recalc, +}; + +static struct clk l3_ocpi_ck = { + .name = "l3_ocpi_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX, + .enable_reg = ARM_IDLECT3, + .enable_bit = EN_OCPI_CK, + .recalc = &followparent_recalc, +}; + +static struct clk tc1_ck = { + .name = "tc1_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX, + .enable_reg = ARM_IDLECT3, + .enable_bit = EN_TC1_CK, + .recalc = &followparent_recalc, +}; + +static struct clk tc2_ck = { + .name = "tc2_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX, + .enable_reg = ARM_IDLECT3, + .enable_bit = EN_TC2_CK, + .recalc = &followparent_recalc, +}; + +static struct clk dma_ck = { + .name = "dma_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + ALWAYS_ENABLED, + .recalc = &followparent_recalc, +}; + +static struct clk dma_lcdfree_ck = { + .name = "dma_lcdfree_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .recalc = &followparent_recalc, +}; + +static struct clk api_ck = { + .name = "api_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .enable_reg = ARM_IDLECT2, + .enable_bit = EN_APICK, + .recalc = &followparent_recalc, +}; + +static struct clk lb_ck = { + .name = "lb_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510, + .enable_reg = ARM_IDLECT2, + .enable_bit = EN_LBCK, + .recalc = &followparent_recalc, +}; + +static struct clk rhea1_ck = { + .name = "rhea1_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .recalc = &followparent_recalc, +}; + +static struct clk rhea2_ck = { + .name = "rhea2_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .recalc = &followparent_recalc, +}; + +static struct clk lcd_ck = { + .name = "lcd_ck", + .parent = &ck_dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | + RATE_CKCTL, + .enable_reg = ARM_IDLECT2, + .enable_bit = EN_LCDCK, + .rate_offset = CKCTL_LCDDIV_OFFSET, + .recalc = &ckctl_recalc, +}; + +static struct clk uart1_1510 = { + .name = "uart1_ck", + /* Direct from ULPD, no parent */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | ALWAYS_ENABLED, + .enable_reg = MOD_CONF_CTRL_0, + .enable_bit = 29, /* Chooses between 12MHz and 48MHz */ + .set_rate = &set_uart_rate, + .recalc = &uart_recalc, +}; + +static struct clk uart1_16xx = { + .name = "uart1_ck", + /* Direct from ULPD, no parent */ + .rate = 48000000, + .flags = CLOCK_IN_OMAP16XX | RATE_FIXED | ENABLE_REG_32BIT, + .enable_reg = MOD_CONF_CTRL_0, + .enable_bit = 29, +}; + +static struct clk uart2_ck = { + .name = "uart2_ck", + /* Direct from ULPD, no parent */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | ENABLE_REG_32BIT, + .enable_reg = MOD_CONF_CTRL_0, + .enable_bit = 30, /* Chooses between 12MHz and 48MHz */ + .set_rate = &set_uart_rate, + .recalc = &uart_recalc, +}; + +static struct clk uart3_1510 = { + .name = "uart3_ck", + /* Direct from ULPD, no parent */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | ALWAYS_ENABLED, + .enable_reg = MOD_CONF_CTRL_0, + .enable_bit = 31, /* Chooses between 12MHz and 48MHz */ + .set_rate = &set_uart_rate, + .recalc = &uart_recalc, +}; + +static struct clk uart3_16xx = { + .name = "uart3_ck", + /* Direct from ULPD, no parent */ + .rate = 48000000, + .flags = CLOCK_IN_OMAP16XX | RATE_FIXED | ENABLE_REG_32BIT, + .enable_reg = MOD_CONF_CTRL_0, + .enable_bit = 31, +}; + +static struct clk usb_clko = { /* 6 MHz output on W4_USB_CLKO */ + .name = "usb_clko", + /* Direct from ULPD, no parent */ + .rate = 6000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + RATE_FIXED | ENABLE_REG_32BIT, + .enable_reg = ULPD_CLOCK_CTRL, + .enable_bit = USB_MCLK_EN_BIT, +}; + +static struct clk usb_hhc_ck1510 = { + .name = "usb_hhc_ck", + /* Direct from ULPD, no parent */ + .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ + .flags = CLOCK_IN_OMAP1510 | + RATE_FIXED | ENABLE_REG_32BIT, + .enable_reg = MOD_CONF_CTRL_0, + .enable_bit = USB_HOST_HHC_UHOST_EN, +}; + +static struct clk usb_hhc_ck16xx = { + .name = "usb_hhc_ck", + /* Direct from ULPD, no parent */ + .rate = 48000000, + /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */ + .flags = CLOCK_IN_OMAP16XX | + RATE_FIXED | ENABLE_REG_32BIT, + .enable_reg = OTG_BASE + 0x08 /* OTG_SYSCON_2 */, + .enable_bit = 8 /* UHOST_EN */, +}; + +static struct clk mclk_1510 = { + .name = "mclk", + /* Direct from ULPD, no parent. May be enabled by ext hardware. */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, +}; + +static struct clk mclk_16xx = { + .name = "mclk", + /* Direct from ULPD, no parent. May be enabled by ext hardware. */ + .flags = CLOCK_IN_OMAP16XX, + .enable_reg = COM_CLK_DIV_CTRL_SEL, + .enable_bit = COM_ULPD_PLL_CLK_REQ, + .set_rate = &set_ext_clk_rate, + .round_rate = &round_ext_clk_rate, + .init = &init_ext_clk, +}; + +static struct clk bclk_1510 = { + .name = "bclk", + /* Direct from ULPD, no parent. May be enabled by ext hardware. */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, +}; + +static struct clk bclk_16xx = { + .name = "bclk", + /* Direct from ULPD, no parent. May be enabled by ext hardware. */ + .flags = CLOCK_IN_OMAP16XX, + .enable_reg = SWD_CLK_DIV_CTRL_SEL, + .enable_bit = SWD_ULPD_PLL_CLK_REQ, + .set_rate = &set_ext_clk_rate, + .round_rate = &round_ext_clk_rate, + .init = &init_ext_clk, +}; + +static struct clk mmc1_ck = { + .name = "mmc1_ck", + /* Functional clock is direct from ULPD, interface clock is ARMPER */ + .parent = &armper_ck, + .rate = 48000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + RATE_FIXED | ENABLE_REG_32BIT, + .enable_reg = MOD_CONF_CTRL_0, + .enable_bit = 23, +}; + +static struct clk mmc2_ck = { + .name = "mmc2_ck", + /* Functional clock is direct from ULPD, interface clock is ARMPER */ + .parent = &armper_ck, + .rate = 48000000, + .flags = CLOCK_IN_OMAP16XX | + RATE_FIXED | ENABLE_REG_32BIT, + .enable_reg = MOD_CONF_CTRL_0, + .enable_bit = 20, +}; + +static struct clk virtual_ck_mpu = { + .name = "mpu", + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + VIRTUAL_CLOCK | ALWAYS_ENABLED, + .parent = &arm_ck, /* Is smarter alias for */ + .recalc = &followparent_recalc, + .set_rate = &select_table_rate, + .round_rate = &round_to_table_rate, +}; + + +static struct clk * onchip_clks[] = { + /* non-ULPD clocks */ + &ck_ref, + &ck_dpll1, + /* CK_GEN1 clocks */ + &ck_dpll1out, + &arm_ck, + &armper_ck, + &arm_gpio_ck, + &armxor_ck, + &armtim_ck, + &armwdt_ck, + &arminth_ck1510, &arminth_ck16xx, + /* CK_GEN2 clocks */ + &dsp_ck, + &dspmmu_ck, + &dspper_ck, + &dspxor_ck, + &dsptim_ck, + /* CK_GEN3 clocks */ + &tc_ck, + &tipb_ck, + &l3_ocpi_ck, + &tc1_ck, + &tc2_ck, + &dma_ck, + &dma_lcdfree_ck, + &api_ck, + &lb_ck, + &rhea1_ck, + &rhea2_ck, + &lcd_ck, + /* ULPD clocks */ + &uart1_1510, + &uart1_16xx, + &uart2_ck, + &uart3_1510, + &uart3_16xx, + &usb_clko, + &usb_hhc_ck1510, &usb_hhc_ck16xx, + &mclk_1510, &mclk_16xx, + &bclk_1510, &bclk_16xx, + &mmc1_ck, + &mmc2_ck, + /* Virtual clocks */ + &virtual_ck_mpu, +}; + +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *p, *clk = ERR_PTR(-ENOENT); + + down(&clocks_sem); + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { + clk = p; + break; + } + } + up(&clocks_sem); + + return clk; +} +EXPORT_SYMBOL(clk_get); + + +void clk_put(struct clk *clk) +{ + if (clk && !IS_ERR(clk)) + module_put(clk->owner); +} +EXPORT_SYMBOL(clk_put); + + +int __clk_enable(struct clk *clk) +{ + __u16 regval16; + __u32 regval32; + + if (clk->flags & ALWAYS_ENABLED) + return 0; + + if (unlikely(clk->enable_reg == 0)) { + printk(KERN_ERR "clock.c: Enable for %s without enable code\n", + clk->name); + return 0; + } + + if (clk->flags & DSP_DOMAIN_CLOCK) { + __clk_use(&api_ck); + } + + if (clk->flags & ENABLE_REG_32BIT) { + if (clk->flags & VIRTUAL_IO_ADDRESS) { + regval32 = __raw_readl(clk->enable_reg); + regval32 |= (1 << clk->enable_bit); + __raw_writel(regval32, clk->enable_reg); + } else { + regval32 = omap_readl(clk->enable_reg); + regval32 |= (1 << clk->enable_bit); + omap_writel(regval32, clk->enable_reg); + } + } else { + if (clk->flags & VIRTUAL_IO_ADDRESS) { + regval16 = __raw_readw(clk->enable_reg); + regval16 |= (1 << clk->enable_bit); + __raw_writew(regval16, clk->enable_reg); + } else { + regval16 = omap_readw(clk->enable_reg); + regval16 |= (1 << clk->enable_bit); + omap_writew(regval16, clk->enable_reg); + } + } + + if (clk->flags & DSP_DOMAIN_CLOCK) { + __clk_unuse(&api_ck); + } + + return 0; +} + + +void __clk_disable(struct clk *clk) +{ + __u16 regval16; + __u32 regval32; + + if (clk->enable_reg == 0) + return; + + if (clk->flags & DSP_DOMAIN_CLOCK) { + __clk_use(&api_ck); + } + + if (clk->flags & ENABLE_REG_32BIT) { + if (clk->flags & VIRTUAL_IO_ADDRESS) { + regval32 = __raw_readl(clk->enable_reg); + regval32 &= ~(1 << clk->enable_bit); + __raw_writel(regval32, clk->enable_reg); + } else { + regval32 = omap_readl(clk->enable_reg); + regval32 &= ~(1 << clk->enable_bit); + omap_writel(regval32, clk->enable_reg); + } + } else { + if (clk->flags & VIRTUAL_IO_ADDRESS) { + regval16 = __raw_readw(clk->enable_reg); + regval16 &= ~(1 << clk->enable_bit); + __raw_writew(regval16, clk->enable_reg); + } else { + regval16 = omap_readw(clk->enable_reg); + regval16 &= ~(1 << clk->enable_bit); + omap_writew(regval16, clk->enable_reg); + } + } + + if (clk->flags & DSP_DOMAIN_CLOCK) { + __clk_unuse(&api_ck); + } +} + + +void __clk_unuse(struct clk *clk) +{ + if (clk->usecount > 0 && !(--clk->usecount)) { + __clk_disable(clk); + if (likely(clk->parent)) + __clk_unuse(clk->parent); + } +} + + +int __clk_use(struct clk *clk) +{ + int ret = 0; + if (clk->usecount++ == 0) { + if (likely(clk->parent)) + ret = __clk_use(clk->parent); + + if (unlikely(ret != 0)) { + clk->usecount--; + return ret; + } + + ret = __clk_enable(clk); + + if (unlikely(ret != 0) && clk->parent) { + __clk_unuse(clk->parent); + clk->usecount--; + } + } + + return ret; +} + + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&clockfw_lock, flags); + ret = __clk_enable(clk); + spin_unlock_irqrestore(&clockfw_lock, flags); + return ret; +} +EXPORT_SYMBOL(clk_enable); + + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + spin_lock_irqsave(&clockfw_lock, flags); + __clk_disable(clk); + spin_unlock_irqrestore(&clockfw_lock, flags); +} +EXPORT_SYMBOL(clk_disable); + + +int clk_use(struct clk *clk) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&clockfw_lock, flags); + ret = __clk_use(clk); + spin_unlock_irqrestore(&clockfw_lock, flags); + return ret; +} +EXPORT_SYMBOL(clk_use); + + +void clk_unuse(struct clk *clk) +{ + unsigned long flags; + + spin_lock_irqsave(&clockfw_lock, flags); + __clk_unuse(clk); + spin_unlock_irqrestore(&clockfw_lock, flags); +} +EXPORT_SYMBOL(clk_unuse); + + +int clk_get_usecount(struct clk *clk) +{ + return clk->usecount; +} +EXPORT_SYMBOL(clk_get_usecount); + + +unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} +EXPORT_SYMBOL(clk_get_rate); + + +static __u16 verify_ckctl_value(__u16 newval) +{ + /* This function checks for following limitations set + * by the hardware (all conditions must be true): + * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 + * ARM_CK >= TC_CK + * DSP_CK >= TC_CK + * DSPMMU_CK >= TC_CK + * + * In addition following rules are enforced: + * LCD_CK <= TC_CK + * ARMPER_CK <= TC_CK + * + * However, maximum frequencies are not checked for! + */ + __u8 per_exp; + __u8 lcd_exp; + __u8 arm_exp; + __u8 dsp_exp; + __u8 tc_exp; + __u8 dspmmu_exp; + + per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3; + lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3; + arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3; + dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3; + tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3; + dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3; + + if (dspmmu_exp < dsp_exp) + dspmmu_exp = dsp_exp; + if (dspmmu_exp > dsp_exp+1) + dspmmu_exp = dsp_exp+1; + if (tc_exp < arm_exp) + tc_exp = arm_exp; + if (tc_exp < dspmmu_exp) + tc_exp = dspmmu_exp; + if (tc_exp > lcd_exp) + lcd_exp = tc_exp; + if (tc_exp > per_exp) + per_exp = tc_exp; + + newval &= 0xf000; + newval |= per_exp << CKCTL_PERDIV_OFFSET; + newval |= lcd_exp << CKCTL_LCDDIV_OFFSET; + newval |= arm_exp << CKCTL_ARMDIV_OFFSET; + newval |= dsp_exp << CKCTL_DSPDIV_OFFSET; + newval |= tc_exp << CKCTL_TCDIV_OFFSET; + newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET; + + return newval; +} + + +static int calc_dsor_exp(struct clk *clk, unsigned long rate) +{ + /* Note: If target frequency is too low, this function will return 4, + * which is invalid value. Caller must check for this value and act + * accordingly. + * + * Note: This function does not check for following limitations set + * by the hardware (all conditions must be true): + * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 + * ARM_CK >= TC_CK + * DSP_CK >= TC_CK + * DSPMMU_CK >= TC_CK + */ + unsigned long realrate; + struct clk * parent; + unsigned dsor_exp; + + if (unlikely(!(clk->flags & RATE_CKCTL))) + return -EINVAL; + + parent = clk->parent; + if (unlikely(parent == 0)) + return -EIO; + + realrate = parent->rate; + for (dsor_exp=0; dsor_exp<4; dsor_exp++) { + if (realrate <= rate) + break; + + realrate /= 2; + } + + return dsor_exp; +} + + +static void ckctl_recalc(struct clk * clk) +{ + int dsor; + + /* Calculate divisor encoded as 2-bit exponent */ + if (clk->flags & DSP_DOMAIN_CLOCK) { + /* The clock control bits are in DSP domain, + * so api_ck is needed for access. + * Note that DSP_CKCTL virt addr = phys addr, so + * we must use __raw_readw() instead of omap_readw(). + */ + __clk_use(&api_ck); + dsor = 1 << (3 & (__raw_readw(DSP_CKCTL) >> clk->rate_offset)); + __clk_unuse(&api_ck); + } else { + dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset)); + } + if (unlikely(clk->rate == clk->parent->rate / dsor)) + return; /* No change, quick exit */ + clk->rate = clk->parent->rate / dsor; + + if (unlikely(clk->flags & RATE_PROPAGATES)) + propagate_rate(clk); +} + + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + int dsor_exp; + + if (clk->flags & RATE_FIXED) + return clk->rate; + + if (clk->flags & RATE_CKCTL) { + dsor_exp = calc_dsor_exp(clk, rate); + if (dsor_exp < 0) + return dsor_exp; + if (dsor_exp > 3) + dsor_exp = 3; + return clk->parent->rate / (1 << dsor_exp); + } + + if(clk->round_rate != 0) + return clk->round_rate(clk, rate); + + return clk->rate; +} +EXPORT_SYMBOL(clk_round_rate); + + +static void propagate_rate(struct clk * clk) +{ + struct clk ** clkp; + + for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) { + if (likely((*clkp)->parent != clk)) continue; + if (likely((*clkp)->recalc)) + (*clkp)->recalc(*clkp); + } +} + + +static int select_table_rate(struct clk * clk, unsigned long rate) +{ + /* Find the highest supported frequency <= rate and switch to it */ + struct mpu_rate * ptr; + + if (clk != &virtual_ck_mpu) + return -EINVAL; + + for (ptr = rate_table; ptr->rate; ptr++) { + if (ptr->xtal != ck_ref.rate) + continue; + + /* DPLL1 cannot be reprogrammed without risking system crash */ + if (likely(ck_dpll1.rate!=0) && ptr->pll_rate != ck_dpll1.rate) + continue; + + /* Can check only after xtal frequency check */ + if (ptr->rate <= rate) + break; + } + + if (!ptr->rate) + return -EINVAL; + + if (!ptr->rate) + return -EINVAL; + + if (unlikely(ck_dpll1.rate == 0)) { + omap_writew(ptr->dpllctl_val, DPLL_CTL); + ck_dpll1.rate = ptr->pll_rate; + } + omap_writew(ptr->ckctl_val, ARM_CKCTL); + propagate_rate(&ck_dpll1); + return 0; +} + + +static long round_to_table_rate(struct clk * clk, unsigned long rate) +{ + /* Find the highest supported frequency <= rate */ + struct mpu_rate * ptr; + long highest_rate; + + if (clk != &virtual_ck_mpu) + return -EINVAL; + + highest_rate = -EINVAL; + + for (ptr = rate_table; ptr->rate; ptr++) { + if (ptr->xtal != ck_ref.rate) + continue; + + highest_rate = ptr->rate; + + /* Can check only after xtal frequency check */ + if (ptr->rate <= rate) + break; + } + + return highest_rate; +} + + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = -EINVAL; + int dsor_exp; + __u16 regval; + unsigned long flags; + + if (clk->flags & RATE_CKCTL) { + dsor_exp = calc_dsor_exp(clk, rate); + if (dsor_exp > 3) + dsor_exp = -EINVAL; + if (dsor_exp < 0) + return dsor_exp; + + spin_lock_irqsave(&clockfw_lock, flags); + regval = omap_readw(ARM_CKCTL); + regval &= ~(3 << clk->rate_offset); + regval |= dsor_exp << clk->rate_offset; + regval = verify_ckctl_value(regval); + omap_writew(regval, ARM_CKCTL); + clk->rate = clk->parent->rate / (1 << dsor_exp); + spin_unlock_irqrestore(&clockfw_lock, flags); + ret = 0; + } else if(clk->set_rate != 0) { + spin_lock_irqsave(&clockfw_lock, flags); + ret = clk->set_rate(clk, rate); + spin_unlock_irqrestore(&clockfw_lock, flags); + } + + if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES))) + propagate_rate(clk); + + return ret; +} +EXPORT_SYMBOL(clk_set_rate); + + +static unsigned calc_ext_dsor(unsigned long rate) +{ + unsigned dsor; + + /* MCLK and BCLK divisor selection is not linear: + * freq = 96MHz / dsor + * + * RATIO_SEL range: dsor <-> RATIO_SEL + * 0..6: (RATIO_SEL+2) <-> (dsor-2) + * 6..48: (8+(RATIO_SEL-6)*2) <-> ((dsor-8)/2+6) + * Minimum dsor is 2 and maximum is 96. Odd divisors starting from 9 + * can not be used. + */ + for (dsor = 2; dsor < 96; ++dsor) { + if ((dsor & 1) && dsor > 8) + continue; + if (rate >= 96000000 / dsor) + break; + } + return dsor; +} + +/* Only needed on 1510 */ +static int set_uart_rate(struct clk * clk, unsigned long rate) +{ + unsigned int val; + + val = omap_readl(clk->enable_reg); + if (rate == 12000000) + val &= ~(1 << clk->enable_bit); + else if (rate == 48000000) + val |= (1 << clk->enable_bit); + else + return -EINVAL; + omap_writel(val, clk->enable_reg); + clk->rate = rate; + + return 0; +} + +static int set_ext_clk_rate(struct clk * clk, unsigned long rate) +{ + unsigned dsor; + __u16 ratio_bits; + + dsor = calc_ext_dsor(rate); + clk->rate = 96000000 / dsor; + if (dsor > 8) + ratio_bits = ((dsor - 8) / 2 + 6) << 2; + else + ratio_bits = (dsor - 2) << 2; + + ratio_bits |= omap_readw(clk->enable_reg) & ~0xfd; + omap_writew(ratio_bits, clk->enable_reg); + + return 0; +} + + +static long round_ext_clk_rate(struct clk * clk, unsigned long rate) +{ + return 96000000 / calc_ext_dsor(rate); +} + + +static void init_ext_clk(struct clk * clk) +{ + unsigned dsor; + __u16 ratio_bits; + + /* Determine current rate and ensure clock is based on 96MHz APLL */ + ratio_bits = omap_readw(clk->enable_reg) & ~1; + omap_writew(ratio_bits, clk->enable_reg); + + ratio_bits = (ratio_bits & 0xfc) >> 2; + if (ratio_bits > 6) + dsor = (ratio_bits - 6) * 2 + 8; + else + dsor = ratio_bits + 2; + + clk-> rate = 96000000 / dsor; +} + + +int clk_register(struct clk *clk) +{ + down(&clocks_sem); + list_add(&clk->node, &clocks); + if (clk->init) + clk->init(clk); + up(&clocks_sem); + return 0; +} +EXPORT_SYMBOL(clk_register); + +void clk_unregister(struct clk *clk) +{ + down(&clocks_sem); + list_del(&clk->node); + up(&clocks_sem); +} +EXPORT_SYMBOL(clk_unregister); + +#ifdef CONFIG_OMAP_RESET_CLOCKS +/* + * Resets some clocks that may be left on from bootloader, + * but leaves serial clocks on. See also omap_late_clk_reset(). + */ +static inline void omap_early_clk_reset(void) +{ + //omap_writel(0x3 << 29, MOD_CONF_CTRL_0); +} +#else +#define omap_early_clk_reset() {} +#endif + +int __init clk_init(void) +{ + struct clk ** clkp; + const struct omap_clock_config *info; + int crystal_type = 0; /* Default 12 MHz */ + + omap_early_clk_reset(); + + for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) { + if (((*clkp)->flags &CLOCK_IN_OMAP1510) && cpu_is_omap1510()) { + clk_register(*clkp); + continue; + } + + if (((*clkp)->flags &CLOCK_IN_OMAP16XX) && cpu_is_omap16xx()) { + clk_register(*clkp); + continue; + } + + if (((*clkp)->flags &CLOCK_IN_OMAP730) && cpu_is_omap730()) { + clk_register(*clkp); + continue; + } + } + + info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config); + if (info != NULL) { + if (!cpu_is_omap1510()) + crystal_type = info->system_clock_type; + } + +#if defined(CONFIG_ARCH_OMAP730) + ck_ref.rate = 13000000; +#elif defined(CONFIG_ARCH_OMAP16XX) + if (crystal_type == 2) + ck_ref.rate = 19200000; +#endif + + printk("Clocks: ARM_SYSST: 0x%04x DPLL_CTL: 0x%04x ARM_CKCTL: 0x%04x\n", + omap_readw(ARM_SYSST), omap_readw(DPLL_CTL), + omap_readw(ARM_CKCTL)); + + /* We want to be in syncronous scalable mode */ + omap_writew(0x1000, ARM_SYSST); + +#ifdef CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER + /* Use values set by bootloader. Determine PLL rate and recalculate + * dependent clocks as if kernel had changed PLL or divisors. + */ + { + unsigned pll_ctl_val = omap_readw(DPLL_CTL); + + ck_dpll1.rate = ck_ref.rate; /* Base xtal rate */ + if (pll_ctl_val & 0x10) { + /* PLL enabled, apply multiplier and divisor */ + if (pll_ctl_val & 0xf80) + ck_dpll1.rate *= (pll_ctl_val & 0xf80) >> 7; + ck_dpll1.rate /= ((pll_ctl_val & 0x60) >> 5) + 1; + } else { + /* PLL disabled, apply bypass divisor */ + switch (pll_ctl_val & 0xc) { + case 0: + break; + case 0x4: + ck_dpll1.rate /= 2; + break; + default: + ck_dpll1.rate /= 4; + break; + } + } + } + propagate_rate(&ck_dpll1); +#else + /* Find the highest supported frequency and enable it */ + if (select_table_rate(&virtual_ck_mpu, ~0)) { + printk(KERN_ERR "System frequencies not set. Check your config.\n"); + /* Guess sane values (60MHz) */ + omap_writew(0x2290, DPLL_CTL); + omap_writew(0x1005, ARM_CKCTL); + ck_dpll1.rate = 60000000; + propagate_rate(&ck_dpll1); + } +#endif + /* Cache rates for clocks connected to ck_ref (not dpll1) */ + propagate_rate(&ck_ref); + printk(KERN_INFO "Clocking rate (xtal/DPLL1/MPU): %ld.%01ld/%ld/%ld MHz\n", + ck_ref.rate / 1000000, (ck_ref.rate / 100000) % 10, + ck_dpll1.rate, arm_ck.rate); + +#ifdef CONFIG_MACH_OMAP_PERSEUS2 + /* Select slicer output as OMAP input clock */ + omap_writew(omap_readw(OMAP730_PCC_UPLD_CTRL) & ~0x1, OMAP730_PCC_UPLD_CTRL); +#endif + + /* Turn off DSP and ARM_TIMXO. Make sure ARM_INTHCK is not divided */ + omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL); + + /* Put DSP/MPUI into reset until needed */ + omap_writew(0, ARM_RSTCT1); + omap_writew(1, ARM_RSTCT2); + omap_writew(0x400, ARM_IDLECT1); + + /* + * According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8) + * of the ARM_IDLECT2 register must be set to zero. The power-on + * default value of this bit is one. + */ + omap_writew(0x0000, ARM_IDLECT2); /* Turn LCD clock off also */ + + /* + * Only enable those clocks we will need, let the drivers + * enable other clocks as necessary + */ + clk_use(&armper_ck); + clk_use(&armxor_ck); + clk_use(&armtim_ck); + + if (cpu_is_omap1510()) + clk_enable(&arm_gpio_ck); + + return 0; +} + + +#ifdef CONFIG_OMAP_RESET_CLOCKS + +static int __init omap_late_clk_reset(void) +{ + /* Turn off all unused clocks */ + struct clk *p; + __u32 regval32; + + omap_writew(0, SOFT_REQ_REG); + omap_writew(0, SOFT_REQ_REG2); + + list_for_each_entry(p, &clocks, node) { + if (p->usecount > 0 || (p->flags & ALWAYS_ENABLED) || + p->enable_reg == 0) + continue; + + /* Assume no DSP clocks have been activated by bootloader */ + if (p->flags & DSP_DOMAIN_CLOCK) + continue; + + /* Is the clock already disabled? */ + if (p->flags & ENABLE_REG_32BIT) { + if (p->flags & VIRTUAL_IO_ADDRESS) + regval32 = __raw_readl(p->enable_reg); + else + regval32 = omap_readl(p->enable_reg); + } else { + if (p->flags & VIRTUAL_IO_ADDRESS) + regval32 = __raw_readw(p->enable_reg); + else + regval32 = omap_readw(p->enable_reg); + } + + if ((regval32 & (1 << p->enable_bit)) == 0) + continue; + + /* FIXME: This clock seems to be necessary but no-one + * has asked for its activation. */ + if (p == &tc2_ck // FIX: pm.c (SRAM), CCP, Camera + || p == &ck_dpll1out // FIX: SoSSI, SSR + || p == &arm_gpio_ck // FIX: GPIO code for 1510 + ) { + printk(KERN_INFO "FIXME: Clock \"%s\" seems unused\n", + p->name); + continue; + } + + printk(KERN_INFO "Disabling unused clock \"%s\"... ", p->name); + __clk_disable(p); + printk(" done\n"); + } + + return 0; +} + +late_initcall(omap_late_clk_reset); + +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/clock.h 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,120 @@ +/* + * linux/arch/arm/plat-omap/clock.h + * + * Copyright (C) 2004 Nokia corporation + * Written by Tuukka Tikkanen + * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ARCH_ARM_OMAP_CLOCK_H +#define __ARCH_ARM_OMAP_CLOCK_H + +struct module; + +struct clk { + struct list_head node; + struct module *owner; + const char *name; + struct clk *parent; + unsigned long rate; + __s8 usecount; + __u16 flags; + __u32 enable_reg; + __u8 enable_bit; + __u8 rate_offset; + void (*recalc)(struct clk *); + int (*set_rate)(struct clk *, unsigned long); + long (*round_rate)(struct clk *, unsigned long); + void (*init)(struct clk *); +}; + + +struct mpu_rate { + unsigned long rate; + unsigned long xtal; + unsigned long pll_rate; + __u16 ckctl_val; + __u16 dpllctl_val; +}; + + +/* Clock flags */ +#define RATE_CKCTL 1 +#define RATE_FIXED 2 +#define RATE_PROPAGATES 4 +#define VIRTUAL_CLOCK 8 +#define ALWAYS_ENABLED 16 +#define ENABLE_REG_32BIT 32 +#define CLOCK_IN_OMAP16XX 64 +#define CLOCK_IN_OMAP1510 128 +#define CLOCK_IN_OMAP730 256 +#define DSP_DOMAIN_CLOCK 512 +#define VIRTUAL_IO_ADDRESS 1024 + +/* ARM_CKCTL bit shifts */ +#define CKCTL_PERDIV_OFFSET 0 +#define CKCTL_LCDDIV_OFFSET 2 +#define CKCTL_ARMDIV_OFFSET 4 +#define CKCTL_DSPDIV_OFFSET 6 +#define CKCTL_TCDIV_OFFSET 8 +#define CKCTL_DSPMMUDIV_OFFSET 10 +/*#define ARM_TIMXO 12*/ +#define EN_DSPCK 13 +/*#define ARM_INTHCK_SEL 14*/ /* Divide-by-2 for mpu inth_ck */ +/* DSP_CKCTL bit shifts */ +#define CKCTL_DSPPERDIV_OFFSET 0 + +/* ARM_IDLECT1 bit shifts */ +/*#define IDLWDT_ARM 0*/ +/*#define IDLXORP_ARM 1*/ +/*#define IDLPER_ARM 2*/ +/*#define IDLLCD_ARM 3*/ +/*#define IDLLB_ARM 4*/ +/*#define IDLHSAB_ARM 5*/ +/*#define IDLIF_ARM 6*/ +/*#define IDLDPLL_ARM 7*/ +/*#define IDLAPI_ARM 8*/ +/*#define IDLTIM_ARM 9*/ +/*#define SETARM_IDLE 11*/ + +/* ARM_IDLECT2 bit shifts */ +#define EN_WDTCK 0 +#define EN_XORPCK 1 +#define EN_PERCK 2 +#define EN_LCDCK 3 +#define EN_LBCK 4 /* Not on 1610/1710 */ +/*#define EN_HSABCK 5*/ +#define EN_APICK 6 +#define EN_TIMCK 7 +#define DMACK_REQ 8 +#define EN_GPIOCK 9 /* Not on 1610/1710 */ +/*#define EN_LBFREECK 10*/ +#define EN_CKOUT_ARM 11 + +/* ARM_IDLECT3 bit shifts */ +#define EN_OCPI_CK 0 +#define EN_TC1_CK 2 +#define EN_TC2_CK 4 + +/* DSP_IDLECT2 bit shifts (0,1,2 are same as for ARM_IDLECT2) */ +#define EN_DSPTIMCK 5 + +/* Various register defines for clock controls scattered around OMAP chip */ +#define USB_MCLK_EN_BIT 4 /* In ULPD_CLKC_CTRL */ +#define USB_HOST_HHC_UHOST_EN 9 /* In MOD_CONF_CTRL_0 */ +#define SWD_ULPD_PLL_CLK_REQ 1 /* In SWD_CLK_DIV_CTRL_SEL */ +#define COM_ULPD_PLL_CLK_REQ 1 /* In COM_CLK_DIV_CTRL_SEL */ +#define SWD_CLK_DIV_CTRL_SEL 0xfffe0874 +#define COM_CLK_DIV_CTRL_SEL 0xfffe0878 +#define SOFT_REQ_REG 0xfffe0834 +#define SOFT_REQ_REG2 0xfffe0880 + +int clk_register(struct clk *clk); +void clk_unregister(struct clk *clk); +int clk_init(void); + +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/common.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,135 @@ +/* + * linux/arch/arm/plat-omap/common.c + * + * Code common to all OMAP machines. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "clock.h" + +#define NO_LENGTH_CHECK 0xffffffff + +extern int omap_bootloader_tag_len; +extern u8 omap_bootloader_tag[]; + +struct omap_board_config_kernel *omap_board_config; +int omap_board_config_size = 0; + +static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out) +{ + struct omap_board_config_kernel *kinfo = NULL; + int i; + +#ifdef CONFIG_OMAP_BOOT_TAG + struct omap_board_config_entry *info = NULL; + + if (omap_bootloader_tag_len > 4) + info = (struct omap_board_config_entry *) omap_bootloader_tag; + while (info != NULL) { + u8 *next; + + if (info->tag == tag) { + if (skip == 0) + break; + skip--; + } + + if ((info->len & 0x03) != 0) { + /* We bail out to avoid an alignment fault */ + printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n", + info->len, info->tag); + return NULL; + } + next = (u8 *) info + sizeof(*info) + info->len; + if (next >= omap_bootloader_tag + omap_bootloader_tag_len) + info = NULL; + else + info = (struct omap_board_config_entry *) next; + } + if (info != NULL) { + /* Check the length as a lame attempt to check for + * binary inconsistancy. */ + if (len != NO_LENGTH_CHECK) { + /* Word-align len */ + if (len & 0x03) + len = (len + 3) & ~0x03; + if (info->len != len) { + printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n", + tag, len, info->len); + return NULL; + } + } + if (len_out != NULL) + *len_out = info->len; + return info->data; + } +#endif + /* Try to find the config from the board-specific structures + * in the kernel. */ + for (i = 0; i < omap_board_config_size; i++) { + if (omap_board_config[i].tag == tag) { + kinfo = &omap_board_config[i]; + break; + } + } + if (kinfo == NULL) + return NULL; + return kinfo->data; +} + +const void *__omap_get_config(u16 tag, size_t len, int nr) +{ + return get_config(tag, len, nr, NULL); +} +EXPORT_SYMBOL(__omap_get_config); + +const void *omap_get_var_config(u16 tag, size_t *len) +{ + return get_config(tag, NO_LENGTH_CHECK, 0, len); +} +EXPORT_SYMBOL(omap_get_var_config); + +static int __init omap_add_serial_console(void) +{ + const struct omap_serial_console_config *info; + + info = omap_get_config(OMAP_TAG_SERIAL_CONSOLE, + struct omap_serial_console_config); + if (info != NULL && info->console_uart) { + static char speed[11], *opt = NULL; + + if (info->console_speed) { + snprintf(speed, sizeof(speed), "%u", info->console_speed); + opt = speed; + } + return add_preferred_console("ttyS", info->console_uart - 1, opt); + } + return 0; +} +console_initcall(omap_add_serial_console); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/cpu-omap.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,128 @@ +/* + * linux/arch/arm/plat-omap/cpu-omap.c + * + * CPU frequency scaling for OMAP + * + * Copyright (C) 2005 Nokia Corporation + * Written by Tony Lindgren + * + * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +/* TODO: Add support for SDRAM timing changes */ + +int omap_verify_speed(struct cpufreq_policy *policy) +{ + struct clk * mpu_clk; + + if (policy->cpu) + return -EINVAL; + + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + mpu_clk = clk_get(NULL, "mpu"); + if (IS_ERR(mpu_clk)) + return PTR_ERR(mpu_clk); + policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000; + policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000; + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + clk_put(mpu_clk); + + return 0; +} + +unsigned int omap_getspeed(unsigned int cpu) +{ + struct clk * mpu_clk; + unsigned long rate; + + if (cpu) + return 0; + + mpu_clk = clk_get(NULL, "mpu"); + if (IS_ERR(mpu_clk)) + return 0; + rate = clk_get_rate(mpu_clk) / 1000; + clk_put(mpu_clk); + + return rate; +} + +static int omap_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct clk * mpu_clk; + struct cpufreq_freqs freqs; + int ret = 0; + + mpu_clk = clk_get(NULL, "mpu"); + if (IS_ERR(mpu_clk)) + return PTR_ERR(mpu_clk); + + freqs.old = omap_getspeed(0); + freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; + freqs.cpu = 0; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + ret = clk_set_rate(mpu_clk, target_freq * 1000); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + clk_put(mpu_clk); + + return ret; +} + +static int __init omap_cpu_init(struct cpufreq_policy *policy) +{ + struct clk * mpu_clk; + + mpu_clk = clk_get(NULL, "mpu"); + if (IS_ERR(mpu_clk)) + return PTR_ERR(mpu_clk); + + if (policy->cpu != 0) + return -EINVAL; + policy->cur = policy->min = policy->max = omap_getspeed(0); + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000; + policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, 216000000) / 1000; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + clk_put(mpu_clk); + + return 0; +} + +static struct cpufreq_driver omap_driver = { + .flags = CPUFREQ_STICKY, + .verify = omap_verify_speed, + .target = omap_target, + .get = omap_getspeed, + .init = omap_cpu_init, + .name = "omap", +}; + +static int __init omap_cpufreq_init(void) +{ + return cpufreq_register_driver(&omap_driver); +} + +arch_initcall(omap_cpufreq_init); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/dma.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,1116 @@ +/* + * linux/arch/arm/plat-omap/dma.c + * + * Copyright (C) 2003 Nokia Corporation + * Author: Juha Yrjölä + * DMA channel linking for 1610 by Samuel Ortiz + * Graphics DMA and LCD DMA graphics tranformations + * by Imre Deak + * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. + * + * Support functions for the OMAP internal DMA channels. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define OMAP_DMA_ACTIVE 0x01 + +#define OMAP_DMA_CCR_EN (1 << 7) + +#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec) + +static int enable_1510_mode = 0; + +struct omap_dma_lch { + int next_lch; + int dev_id; + u16 saved_csr; + u16 enabled_irqs; + const char *dev_name; + void (* callback)(int lch, u16 ch_status, void *data); + void *data; + long flags; +}; + +static int dma_chan_count; + +static spinlock_t dma_chan_lock; +static struct omap_dma_lch dma_chan[OMAP_LOGICAL_DMA_CH_COUNT]; + +const static u8 dma_irq[OMAP_LOGICAL_DMA_CH_COUNT] = { + INT_DMA_CH0_6, INT_DMA_CH1_7, INT_DMA_CH2_8, INT_DMA_CH3, + INT_DMA_CH4, INT_DMA_CH5, INT_1610_DMA_CH6, INT_1610_DMA_CH7, + INT_1610_DMA_CH8, INT_1610_DMA_CH9, INT_1610_DMA_CH10, + INT_1610_DMA_CH11, INT_1610_DMA_CH12, INT_1610_DMA_CH13, + INT_1610_DMA_CH14, INT_1610_DMA_CH15, INT_DMA_LCD +}; + +static inline int get_gdma_dev(int req) +{ + u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; + int shift = ((req - 1) % 5) * 6; + + return ((omap_readl(reg) >> shift) & 0x3f) + 1; +} + +static inline void set_gdma_dev(int req, int dev) +{ + u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; + int shift = ((req - 1) % 5) * 6; + u32 l; + + l = omap_readl(reg); + l &= ~(0x3f << shift); + l |= (dev - 1) << shift; + omap_writel(l, reg); +} + +static void clear_lch_regs(int lch) +{ + int i; + u32 lch_base = OMAP_DMA_BASE + lch * 0x40; + + for (i = 0; i < 0x2c; i += 2) + omap_writew(0, lch_base + i); +} + +void omap_set_dma_priority(int dst_port, int priority) +{ + unsigned long reg; + u32 l; + + switch (dst_port) { + case OMAP_DMA_PORT_OCP_T1: /* FFFECC00 */ + reg = OMAP_TC_OCPT1_PRIOR; + break; + case OMAP_DMA_PORT_OCP_T2: /* FFFECCD0 */ + reg = OMAP_TC_OCPT2_PRIOR; + break; + case OMAP_DMA_PORT_EMIFF: /* FFFECC08 */ + reg = OMAP_TC_EMIFF_PRIOR; + break; + case OMAP_DMA_PORT_EMIFS: /* FFFECC04 */ + reg = OMAP_TC_EMIFS_PRIOR; + break; + default: + BUG(); + return; + } + l = omap_readl(reg); + l &= ~(0xf << 8); + l |= (priority & 0xf) << 8; + omap_writel(l, reg); +} + +void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, + int frame_count, int sync_mode) +{ + u16 w; + + w = omap_readw(OMAP_DMA_CSDP(lch)); + w &= ~0x03; + w |= data_type; + omap_writew(w, OMAP_DMA_CSDP(lch)); + + w = omap_readw(OMAP_DMA_CCR(lch)); + w &= ~(1 << 5); + if (sync_mode == OMAP_DMA_SYNC_FRAME) + w |= 1 << 5; + omap_writew(w, OMAP_DMA_CCR(lch)); + + w = omap_readw(OMAP_DMA_CCR2(lch)); + w &= ~(1 << 2); + if (sync_mode == OMAP_DMA_SYNC_BLOCK) + w |= 1 << 2; + omap_writew(w, OMAP_DMA_CCR2(lch)); + + omap_writew(elem_count, OMAP_DMA_CEN(lch)); + omap_writew(frame_count, OMAP_DMA_CFN(lch)); + +} +void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) +{ + u16 w; + + BUG_ON(omap_dma_in_1510_mode()); + + w = omap_readw(OMAP_DMA_CCR2(lch)) & ~0x03; + switch (mode) { + case OMAP_DMA_CONSTANT_FILL: + w |= 0x01; + break; + case OMAP_DMA_TRANSPARENT_COPY: + w |= 0x02; + break; + case OMAP_DMA_COLOR_DIS: + break; + default: + BUG(); + } + omap_writew(w, OMAP_DMA_CCR2(lch)); + + w = omap_readw(OMAP_DMA_LCH_CTRL(lch)) & ~0x0f; + /* Default is channel type 2D */ + if (mode) { + omap_writew((u16)color, OMAP_DMA_COLOR_L(lch)); + omap_writew((u16)(color >> 16), OMAP_DMA_COLOR_U(lch)); + w |= 1; /* Channel type G */ + } + omap_writew(w, OMAP_DMA_LCH_CTRL(lch)); +} + + +void omap_set_dma_src_params(int lch, int src_port, int src_amode, + unsigned long src_start) +{ + u16 w; + + w = omap_readw(OMAP_DMA_CSDP(lch)); + w &= ~(0x1f << 2); + w |= src_port << 2; + omap_writew(w, OMAP_DMA_CSDP(lch)); + + w = omap_readw(OMAP_DMA_CCR(lch)); + w &= ~(0x03 << 12); + w |= src_amode << 12; + omap_writew(w, OMAP_DMA_CCR(lch)); + + omap_writew(src_start >> 16, OMAP_DMA_CSSA_U(lch)); + omap_writew(src_start, OMAP_DMA_CSSA_L(lch)); +} + +void omap_set_dma_src_index(int lch, int eidx, int fidx) +{ + omap_writew(eidx, OMAP_DMA_CSEI(lch)); + omap_writew(fidx, OMAP_DMA_CSFI(lch)); +} + +void omap_set_dma_src_data_pack(int lch, int enable) +{ + u16 w; + + w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 6); + w |= enable ? (1 << 6) : 0; + omap_writew(w, OMAP_DMA_CSDP(lch)); +} + +void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) +{ + u16 w; + + w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 7); + switch (burst_mode) { + case OMAP_DMA_DATA_BURST_DIS: + break; + case OMAP_DMA_DATA_BURST_4: + w |= (0x01 << 7); + break; + case OMAP_DMA_DATA_BURST_8: + /* not supported by current hardware + * w |= (0x03 << 7); + * fall through + */ + default: + BUG(); + } + omap_writew(w, OMAP_DMA_CSDP(lch)); +} + +void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, + unsigned long dest_start) +{ + u16 w; + + w = omap_readw(OMAP_DMA_CSDP(lch)); + w &= ~(0x1f << 9); + w |= dest_port << 9; + omap_writew(w, OMAP_DMA_CSDP(lch)); + + w = omap_readw(OMAP_DMA_CCR(lch)); + w &= ~(0x03 << 14); + w |= dest_amode << 14; + omap_writew(w, OMAP_DMA_CCR(lch)); + + omap_writew(dest_start >> 16, OMAP_DMA_CDSA_U(lch)); + omap_writew(dest_start, OMAP_DMA_CDSA_L(lch)); +} + +void omap_set_dma_dest_index(int lch, int eidx, int fidx) +{ + omap_writew(eidx, OMAP_DMA_CDEI(lch)); + omap_writew(fidx, OMAP_DMA_CDFI(lch)); +} + +void omap_set_dma_dest_data_pack(int lch, int enable) +{ + u16 w; + + w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(1 << 13); + w |= enable ? (1 << 13) : 0; + omap_writew(w, OMAP_DMA_CSDP(lch)); +} + +void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) +{ + u16 w; + + w = omap_readw(OMAP_DMA_CSDP(lch)) & ~(0x03 << 14); + switch (burst_mode) { + case OMAP_DMA_DATA_BURST_DIS: + break; + case OMAP_DMA_DATA_BURST_4: + w |= (0x01 << 14); + break; + case OMAP_DMA_DATA_BURST_8: + w |= (0x03 << 14); + break; + default: + printk(KERN_ERR "Invalid DMA burst mode\n"); + BUG(); + return; + } + omap_writew(w, OMAP_DMA_CSDP(lch)); +} + +static inline void init_intr(int lch) +{ + u16 w; + + /* Read CSR to make sure it's cleared. */ + w = omap_readw(OMAP_DMA_CSR(lch)); + /* Enable some nice interrupts. */ + omap_writew(dma_chan[lch].enabled_irqs, OMAP_DMA_CICR(lch)); + dma_chan[lch].flags |= OMAP_DMA_ACTIVE; +} + +static inline void enable_lnk(int lch) +{ + u16 w; + + /* Clear the STOP_LNK bits */ + w = omap_readw(OMAP_DMA_CLNK_CTRL(lch)); + w &= ~(1 << 14); + omap_writew(w, OMAP_DMA_CLNK_CTRL(lch)); + + /* And set the ENABLE_LNK bits */ + if (dma_chan[lch].next_lch != -1) + omap_writew(dma_chan[lch].next_lch | (1 << 15), + OMAP_DMA_CLNK_CTRL(lch)); +} + +static inline void disable_lnk(int lch) +{ + u16 w; + + /* Disable interrupts */ + omap_writew(0, OMAP_DMA_CICR(lch)); + + /* Set the STOP_LNK bit */ + w = omap_readw(OMAP_DMA_CLNK_CTRL(lch)); + w |= (1 << 14); + w = omap_writew(w, OMAP_DMA_CLNK_CTRL(lch)); + + dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; +} + +void omap_start_dma(int lch) +{ + u16 w; + + if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { + int next_lch, cur_lch; + char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT]; + + dma_chan_link_map[lch] = 1; + /* Set the link register of the first channel */ + enable_lnk(lch); + + memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); + cur_lch = dma_chan[lch].next_lch; + do { + next_lch = dma_chan[cur_lch].next_lch; + + /* The loop case: we've been here already */ + if (dma_chan_link_map[cur_lch]) + break; + /* Mark the current channel */ + dma_chan_link_map[cur_lch] = 1; + + enable_lnk(cur_lch); + init_intr(cur_lch); + + cur_lch = next_lch; + } while (next_lch != -1); + } + + init_intr(lch); + + w = omap_readw(OMAP_DMA_CCR(lch)); + w |= OMAP_DMA_CCR_EN; + omap_writew(w, OMAP_DMA_CCR(lch)); + dma_chan[lch].flags |= OMAP_DMA_ACTIVE; +} + +void omap_stop_dma(int lch) +{ + u16 w; + + if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { + int next_lch, cur_lch = lch; + char dma_chan_link_map[OMAP_LOGICAL_DMA_CH_COUNT]; + + memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); + do { + /* The loop case: we've been here already */ + if (dma_chan_link_map[cur_lch]) + break; + /* Mark the current channel */ + dma_chan_link_map[cur_lch] = 1; + + disable_lnk(cur_lch); + + next_lch = dma_chan[cur_lch].next_lch; + cur_lch = next_lch; + } while (next_lch != -1); + + return; + } + /* Disable all interrupts on the channel */ + omap_writew(0, OMAP_DMA_CICR(lch)); + + w = omap_readw(OMAP_DMA_CCR(lch)); + w &= ~OMAP_DMA_CCR_EN; + omap_writew(w, OMAP_DMA_CCR(lch)); + dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; +} + +void omap_enable_dma_irq(int lch, u16 bits) +{ + dma_chan[lch].enabled_irqs |= bits; +} + +void omap_disable_dma_irq(int lch, u16 bits) +{ + dma_chan[lch].enabled_irqs &= ~bits; +} + +static int dma_handle_ch(int ch) +{ + u16 csr; + + if (enable_1510_mode && ch >= 6) { + csr = dma_chan[ch].saved_csr; + dma_chan[ch].saved_csr = 0; + } else + csr = omap_readw(OMAP_DMA_CSR(ch)); + if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) { + dma_chan[ch + 6].saved_csr = csr >> 7; + csr &= 0x7f; + } + if (!csr) + return 0; + if (unlikely(dma_chan[ch].dev_id == -1)) { + printk(KERN_WARNING "Spurious interrupt from DMA channel %d (CSR %04x)\n", + ch, csr); + return 0; + } + if (unlikely(csr & OMAP_DMA_TOUT_IRQ)) + printk(KERN_WARNING "DMA timeout with device %d\n", dma_chan[ch].dev_id); + if (unlikely(csr & OMAP_DMA_DROP_IRQ)) + printk(KERN_WARNING "DMA synchronization event drop occurred with device %d\n", + dma_chan[ch].dev_id); + if (likely(csr & OMAP_DMA_BLOCK_IRQ)) + dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; + if (likely(dma_chan[ch].callback != NULL)) + dma_chan[ch].callback(ch, csr, dma_chan[ch].data); + return 1; +} + +static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + int ch = ((int) dev_id) - 1; + int handled = 0; + + for (;;) { + int handled_now = 0; + + handled_now += dma_handle_ch(ch); + if (enable_1510_mode && dma_chan[ch + 6].saved_csr) + handled_now += dma_handle_ch(ch + 6); + if (!handled_now) + break; + handled += handled_now; + } + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +int omap_request_dma(int dev_id, const char *dev_name, + void (* callback)(int lch, u16 ch_status, void *data), + void *data, int *dma_ch_out) +{ + int ch, free_ch = -1; + unsigned long flags; + struct omap_dma_lch *chan; + + spin_lock_irqsave(&dma_chan_lock, flags); + for (ch = 0; ch < dma_chan_count; ch++) { + if (free_ch == -1 && dma_chan[ch].dev_id == -1) { + free_ch = ch; + if (dev_id == 0) + break; + } + } + if (free_ch == -1) { + spin_unlock_irqrestore(&dma_chan_lock, flags); + return -EBUSY; + } + chan = dma_chan + free_ch; + chan->dev_id = dev_id; + clear_lch_regs(free_ch); + spin_unlock_irqrestore(&dma_chan_lock, flags); + + chan->dev_id = dev_id; + chan->dev_name = dev_name; + chan->callback = callback; + chan->data = data; + chan->enabled_irqs = OMAP_DMA_TOUT_IRQ | OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ; + + if (cpu_is_omap16xx()) { + /* If the sync device is set, configure it dynamically. */ + if (dev_id != 0) { + set_gdma_dev(free_ch + 1, dev_id); + dev_id = free_ch + 1; + } + /* Disable the 1510 compatibility mode and set the sync device + * id. */ + omap_writew(dev_id | (1 << 10), OMAP_DMA_CCR(free_ch)); + } else { + omap_writew(dev_id, OMAP_DMA_CCR(free_ch)); + } + *dma_ch_out = free_ch; + + return 0; +} + +void omap_free_dma(int ch) +{ + unsigned long flags; + + spin_lock_irqsave(&dma_chan_lock, flags); + if (dma_chan[ch].dev_id == -1) { + printk("omap_dma: trying to free nonallocated DMA channel %d\n", ch); + spin_unlock_irqrestore(&dma_chan_lock, flags); + return; + } + dma_chan[ch].dev_id = -1; + spin_unlock_irqrestore(&dma_chan_lock, flags); + + /* Disable all DMA interrupts for the channel. */ + omap_writew(0, OMAP_DMA_CICR(ch)); + /* Make sure the DMA transfer is stopped. */ + omap_writew(0, OMAP_DMA_CCR(ch)); +} + +int omap_dma_in_1510_mode(void) +{ + return enable_1510_mode; +} + +/* + * lch_queue DMA will start right after lch_head one is finished. + * For this DMA link to start, you still need to start (see omap_start_dma) + * the first one. That will fire up the entire queue. + */ +void omap_dma_link_lch (int lch_head, int lch_queue) +{ + if (omap_dma_in_1510_mode()) { + printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); + BUG(); + return; + } + + if ((dma_chan[lch_head].dev_id == -1) || + (dma_chan[lch_queue].dev_id == -1)) { + printk(KERN_ERR "omap_dma: trying to link non requested channels\n"); + dump_stack(); + } + + dma_chan[lch_head].next_lch = lch_queue; +} + +/* + * Once the DMA queue is stopped, we can destroy it. + */ +void omap_dma_unlink_lch (int lch_head, int lch_queue) +{ + if (omap_dma_in_1510_mode()) { + printk(KERN_ERR "DMA linking is not supported in 1510 mode\n"); + BUG(); + return; + } + + if (dma_chan[lch_head].next_lch != lch_queue || + dma_chan[lch_head].next_lch == -1) { + printk(KERN_ERR "omap_dma: trying to unlink non linked channels\n"); + dump_stack(); + } + + + if ((dma_chan[lch_head].flags & OMAP_DMA_ACTIVE) || + (dma_chan[lch_head].flags & OMAP_DMA_ACTIVE)) { + printk(KERN_ERR "omap_dma: You need to stop the DMA channels before unlinking\n"); + dump_stack(); + } + + dma_chan[lch_head].next_lch = -1; +} + + +static struct lcd_dma_info { + spinlock_t lock; + int reserved; + void (* callback)(u16 status, void *data); + void *cb_data; + + int active; + unsigned long addr, size; + int rotate, data_type, xres, yres; + int vxres; + int mirror; + int xscale, yscale; + int ext_ctrl; + int src_port; + int single_transfer; +} lcd_dma; + +void omap_set_lcd_dma_b1(unsigned long addr, u16 fb_xres, u16 fb_yres, + int data_type) +{ + lcd_dma.addr = addr; + lcd_dma.data_type = data_type; + lcd_dma.xres = fb_xres; + lcd_dma.yres = fb_yres; +} + +void omap_set_lcd_dma_src_port(int port) +{ + lcd_dma.src_port = port; +} + +void omap_set_lcd_dma_ext_controller(int external) +{ + lcd_dma.ext_ctrl = external; +} + +void omap_set_lcd_dma_single_transfer(int single) +{ + lcd_dma.single_transfer = single; +} + + +void omap_set_lcd_dma_b1_rotation(int rotate) +{ + if (omap_dma_in_1510_mode()) { + printk(KERN_ERR "DMA rotation is not supported in 1510 mode\n"); + BUG(); + return; + } + lcd_dma.rotate = rotate; +} + +void omap_set_lcd_dma_b1_mirror(int mirror) +{ + if (omap_dma_in_1510_mode()) { + printk(KERN_ERR "DMA mirror is not supported in 1510 mode\n"); + BUG(); + } + lcd_dma.mirror = mirror; +} + +void omap_set_lcd_dma_b1_vxres(unsigned long vxres) +{ + if (omap_dma_in_1510_mode()) { + printk(KERN_ERR "DMA virtual resulotion is not supported " + "in 1510 mode\n"); + BUG(); + } + lcd_dma.vxres = vxres; +} + +void omap_set_lcd_dma_b1_scale(unsigned int xscale, unsigned int yscale) +{ + if (omap_dma_in_1510_mode()) { + printk(KERN_ERR "DMA scale is not supported in 1510 mode\n"); + BUG(); + } + lcd_dma.xscale = xscale; + lcd_dma.yscale = yscale; +} + +static void set_b1_regs(void) +{ + unsigned long top, bottom; + int es; + u16 w; + unsigned long en, fn; + long ei, fi; + unsigned long vxres; + unsigned int xscale, yscale; + + switch (lcd_dma.data_type) { + case OMAP_DMA_DATA_TYPE_S8: + es = 1; + break; + case OMAP_DMA_DATA_TYPE_S16: + es = 2; + break; + case OMAP_DMA_DATA_TYPE_S32: + es = 4; + break; + default: + BUG(); + return; + } + + vxres = lcd_dma.vxres ? lcd_dma.vxres : lcd_dma.xres; + xscale = lcd_dma.xscale ? lcd_dma.xscale : 1; + yscale = lcd_dma.yscale ? lcd_dma.yscale : 1; + BUG_ON(vxres < lcd_dma.xres); +#define PIXADDR(x,y) (lcd_dma.addr + ((y) * vxres * yscale + (x) * xscale) * es) +#define PIXSTEP(sx, sy, dx, dy) (PIXADDR(dx, dy) - PIXADDR(sx, sy) - es + 1) + switch (lcd_dma.rotate) { + case 0: + if (!lcd_dma.mirror) { + top = PIXADDR(0, 0); + bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); + /* 1510 DMA requires the bottom address to be 2 more + * than the actual last memory access location. */ + if (omap_dma_in_1510_mode() && + lcd_dma.data_type == OMAP_DMA_DATA_TYPE_S32) + bottom += 2; + ei = PIXSTEP(0, 0, 1, 0); + fi = PIXSTEP(lcd_dma.xres - 1, 0, 0, 1); + } else { + top = PIXADDR(lcd_dma.xres - 1, 0); + bottom = PIXADDR(0, lcd_dma.yres - 1); + ei = PIXSTEP(1, 0, 0, 0); + fi = PIXSTEP(0, 0, lcd_dma.xres - 1, 1); + } + en = lcd_dma.xres; + fn = lcd_dma.yres; + break; + case 90: + if (!lcd_dma.mirror) { + top = PIXADDR(0, lcd_dma.yres - 1); + bottom = PIXADDR(lcd_dma.xres - 1, 0); + ei = PIXSTEP(0, 1, 0, 0); + fi = PIXSTEP(0, 0, 1, lcd_dma.yres - 1); + } else { + top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); + bottom = PIXADDR(0, 0); + ei = PIXSTEP(0, 1, 0, 0); + fi = PIXSTEP(1, 0, 0, lcd_dma.yres - 1); + } + en = lcd_dma.yres; + fn = lcd_dma.xres; + break; + case 180: + if (!lcd_dma.mirror) { + top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); + bottom = PIXADDR(0, 0); + ei = PIXSTEP(1, 0, 0, 0); + fi = PIXSTEP(0, 1, lcd_dma.xres - 1, 0); + } else { + top = PIXADDR(0, lcd_dma.yres - 1); + bottom = PIXADDR(lcd_dma.xres - 1, 0); + ei = PIXSTEP(0, 0, 1, 0); + fi = PIXSTEP(lcd_dma.xres - 1, 1, 0, 0); + } + en = lcd_dma.xres; + fn = lcd_dma.yres; + break; + case 270: + if (!lcd_dma.mirror) { + top = PIXADDR(lcd_dma.xres - 1, 0); + bottom = PIXADDR(0, lcd_dma.yres - 1); + ei = PIXSTEP(0, 0, 0, 1); + fi = PIXSTEP(1, lcd_dma.yres - 1, 0, 0); + } else { + top = PIXADDR(0, 0); + bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); + ei = PIXSTEP(0, 0, 0, 1); + fi = PIXSTEP(0, lcd_dma.yres - 1, 1, 0); + } + en = lcd_dma.yres; + fn = lcd_dma.xres; + break; + default: + BUG(); + return; /* Supress warning about uninitialized vars */ + } + + if (omap_dma_in_1510_mode()) { + omap_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U); + omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L); + omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U); + omap_writew(bottom, OMAP1510_DMA_LCD_BOT_F1_L); + + return; + } + + /* 1610 regs */ + omap_writew(top >> 16, OMAP1610_DMA_LCD_TOP_B1_U); + omap_writew(top, OMAP1610_DMA_LCD_TOP_B1_L); + omap_writew(bottom >> 16, OMAP1610_DMA_LCD_BOT_B1_U); + omap_writew(bottom, OMAP1610_DMA_LCD_BOT_B1_L); + + omap_writew(en, OMAP1610_DMA_LCD_SRC_EN_B1); + omap_writew(fn, OMAP1610_DMA_LCD_SRC_FN_B1); + + w = omap_readw(OMAP1610_DMA_LCD_CSDP); + w &= ~0x03; + w |= lcd_dma.data_type; + omap_writew(w, OMAP1610_DMA_LCD_CSDP); + + w = omap_readw(OMAP1610_DMA_LCD_CTRL); + /* Always set the source port as SDRAM for now*/ + w &= ~(0x03 << 6); + if (lcd_dma.callback != NULL) + w |= 1 << 1; /* Block interrupt enable */ + else + w &= ~(1 << 1); + omap_writew(w, OMAP1610_DMA_LCD_CTRL); + + if (!(lcd_dma.rotate || lcd_dma.mirror || + lcd_dma.vxres || lcd_dma.xscale || lcd_dma.yscale)) + return; + + w = omap_readw(OMAP1610_DMA_LCD_CCR); + /* Set the double-indexed addressing mode */ + w |= (0x03 << 12); + omap_writew(w, OMAP1610_DMA_LCD_CCR); + + omap_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1); + omap_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U); + omap_writew(fi, OMAP1610_DMA_LCD_SRC_FI_B1_L); +} + +static irqreturn_t lcd_dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + u16 w; + + w = omap_readw(OMAP1610_DMA_LCD_CTRL); + if (unlikely(!(w & (1 << 3)))) { + printk(KERN_WARNING "Spurious LCD DMA IRQ\n"); + return IRQ_NONE; + } + /* Ack the IRQ */ + w |= (1 << 3); + omap_writew(w, OMAP1610_DMA_LCD_CTRL); + lcd_dma.active = 0; + if (lcd_dma.callback != NULL) + lcd_dma.callback(w, lcd_dma.cb_data); + + return IRQ_HANDLED; +} + +int omap_request_lcd_dma(void (* callback)(u16 status, void *data), + void *data) +{ + spin_lock_irq(&lcd_dma.lock); + if (lcd_dma.reserved) { + spin_unlock_irq(&lcd_dma.lock); + printk(KERN_ERR "LCD DMA channel already reserved\n"); + BUG(); + return -EBUSY; + } + lcd_dma.reserved = 1; + spin_unlock_irq(&lcd_dma.lock); + lcd_dma.callback = callback; + lcd_dma.cb_data = data; + lcd_dma.active = 0; + lcd_dma.single_transfer = 0; + lcd_dma.rotate = 0; + lcd_dma.vxres = 0; + lcd_dma.mirror = 0; + lcd_dma.xscale = 0; + lcd_dma.yscale = 0; + lcd_dma.ext_ctrl = 0; + lcd_dma.src_port = 0; + + return 0; +} + +void omap_free_lcd_dma(void) +{ + spin_lock(&lcd_dma.lock); + if (!lcd_dma.reserved) { + spin_unlock(&lcd_dma.lock); + printk(KERN_ERR "LCD DMA is not reserved\n"); + BUG(); + return; + } + if (!enable_1510_mode) + omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1, OMAP1610_DMA_LCD_CCR); + lcd_dma.reserved = 0; + spin_unlock(&lcd_dma.lock); +} + +void omap_enable_lcd_dma(void) +{ + u16 w; + + /* Set the Enable bit only if an external controller is + * connected. Otherwise the OMAP internal controller will + * start the transfer when it gets enabled. + */ + if (enable_1510_mode || !lcd_dma.ext_ctrl) + return; + + w = omap_readw(OMAP1610_DMA_LCD_CTRL); + w |= 1 << 8; + omap_writew(w, OMAP1610_DMA_LCD_CTRL); + + w = omap_readw(OMAP1610_DMA_LCD_CCR); + w |= 1 << 7; + omap_writew(w, OMAP1610_DMA_LCD_CCR); + + lcd_dma.active = 1; +} + +void omap_setup_lcd_dma(void) +{ + BUG_ON(lcd_dma.active); + if (!enable_1510_mode) { + /* Set some reasonable defaults */ + omap_writew(0x5440, OMAP1610_DMA_LCD_CCR); + omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP); + omap_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL); + } + set_b1_regs(); + if (!enable_1510_mode) { + u16 w; + + w = omap_readw(OMAP1610_DMA_LCD_CCR); + /* If DMA was already active set the end_prog bit to have + * the programmed register set loaded into the active + * register set. + */ + w |= 1 << 11; /* End_prog */ + if (!lcd_dma.single_transfer) + w |= (3 << 8); /* Auto_init, repeat */ + omap_writew(w, OMAP1610_DMA_LCD_CCR); + } +} + +void omap_stop_lcd_dma(void) +{ + u16 w; + + lcd_dma.active = 0; + if (enable_1510_mode || !lcd_dma.ext_ctrl) + return; + + w = omap_readw(OMAP1610_DMA_LCD_CCR); + w &= ~(1 << 7); + omap_writew(w, OMAP1610_DMA_LCD_CCR); + + w = omap_readw(OMAP1610_DMA_LCD_CTRL); + w &= ~(1 << 8); + omap_writew(w, OMAP1610_DMA_LCD_CTRL); +} + +/* + * Clears any DMA state so the DMA engine is ready to restart with new buffers + * through omap_start_dma(). Any buffers in flight are discarded. + */ +void omap_clear_dma(int lch) +{ + unsigned long flags; + int status; + + local_irq_save(flags); + omap_writew(omap_readw(OMAP_DMA_CCR(lch)) & ~OMAP_DMA_CCR_EN, + OMAP_DMA_CCR(lch)); + status = OMAP_DMA_CSR(lch); /* clear pending interrupts */ + local_irq_restore(flags); +} + +/* + * Returns current physical source address for the given DMA channel. + * If the channel is running the caller must disable interrupts prior calling + * this function and process the returned value before re-enabling interrupt to + * prevent races with the interrupt handler. Note that in continuous mode there + * is a chance for CSSA_L register overflow inbetween the two reads resulting + * in incorrect return value. + */ +dma_addr_t omap_get_dma_src_pos(int lch) +{ + return (dma_addr_t) (OMAP_DMA_CSSA_L(lch) | + (OMAP_DMA_CSSA_U(lch) << 16)); +} + +/* + * Returns current physical destination address for the given DMA channel. + * If the channel is running the caller must disable interrupts prior calling + * this function and process the returned value before re-enabling interrupt to + * prevent races with the interrupt handler. Note that in continuous mode there + * is a chance for CDSA_L register overflow inbetween the two reads resulting + * in incorrect return value. + */ +dma_addr_t omap_get_dma_dst_pos(int lch) +{ + return (dma_addr_t) (OMAP_DMA_CDSA_L(lch) | + (OMAP_DMA_CDSA_U(lch) << 16)); +} + +int omap_dma_running(void) +{ + int lch; + + /* Check if LCD DMA is running */ + if (cpu_is_omap16xx()) + if (omap_readw(OMAP1610_DMA_LCD_CCR) & OMAP_DMA_CCR_EN) + return 1; + + for (lch = 0; lch < dma_chan_count; lch++) { + u16 w; + + w = omap_readw(OMAP_DMA_CCR(lch)); + if (w & OMAP_DMA_CCR_EN) + return 1; + } + return 0; +} + +static int __init omap_init_dma(void) +{ + int ch, r; + + if (cpu_is_omap1510()) { + printk(KERN_INFO "DMA support for OMAP1510 initialized\n"); + dma_chan_count = 9; + enable_1510_mode = 1; + } else if (cpu_is_omap16xx() || cpu_is_omap730()) { + printk(KERN_INFO "OMAP DMA hardware version %d\n", + omap_readw(OMAP_DMA_HW_ID)); + printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n", + (omap_readw(OMAP_DMA_CAPS_0_U) << 16) | omap_readw(OMAP_DMA_CAPS_0_L), + (omap_readw(OMAP_DMA_CAPS_1_U) << 16) | omap_readw(OMAP_DMA_CAPS_1_L), + omap_readw(OMAP_DMA_CAPS_2), omap_readw(OMAP_DMA_CAPS_3), + omap_readw(OMAP_DMA_CAPS_4)); + if (!enable_1510_mode) { + u16 w; + + /* Disable OMAP 3.0/3.1 compatibility mode. */ + w = omap_readw(OMAP_DMA_GSCR); + w |= 1 << 3; + omap_writew(w, OMAP_DMA_GSCR); + dma_chan_count = 16; + } else + dma_chan_count = 9; + } else { + dma_chan_count = 0; + return 0; + } + + memset(&lcd_dma, 0, sizeof(lcd_dma)); + spin_lock_init(&lcd_dma.lock); + spin_lock_init(&dma_chan_lock); + memset(&dma_chan, 0, sizeof(dma_chan)); + + for (ch = 0; ch < dma_chan_count; ch++) { + dma_chan[ch].dev_id = -1; + dma_chan[ch].next_lch = -1; + + if (ch >= 6 && enable_1510_mode) + continue; + + /* request_irq() doesn't like dev_id (ie. ch) being zero, + * so we have to kludge around this. */ + r = request_irq(dma_irq[ch], dma_irq_handler, 0, "DMA", + (void *) (ch + 1)); + if (r != 0) { + int i; + + printk(KERN_ERR "unable to request IRQ %d for DMA (error %d)\n", + dma_irq[ch], r); + for (i = 0; i < ch; i++) + free_irq(dma_irq[i], (void *) (i + 1)); + return r; + } + } + r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0, "LCD DMA", NULL); + if (r != 0) { + int i; + + printk(KERN_ERR "unable to request IRQ for LCD DMA (error %d)\n", r); + for (i = 0; i < dma_chan_count; i++) + free_irq(dma_irq[i], (void *) (i + 1)); + return r; + } + return 0; +} + +arch_initcall(omap_init_dma); + + +EXPORT_SYMBOL(omap_get_dma_src_pos); +EXPORT_SYMBOL(omap_get_dma_dst_pos); +EXPORT_SYMBOL(omap_clear_dma); +EXPORT_SYMBOL(omap_set_dma_priority); +EXPORT_SYMBOL(omap_request_dma); +EXPORT_SYMBOL(omap_free_dma); +EXPORT_SYMBOL(omap_start_dma); +EXPORT_SYMBOL(omap_stop_dma); +EXPORT_SYMBOL(omap_enable_dma_irq); +EXPORT_SYMBOL(omap_disable_dma_irq); + +EXPORT_SYMBOL(omap_set_dma_transfer_params); +EXPORT_SYMBOL(omap_set_dma_color_mode); + +EXPORT_SYMBOL(omap_set_dma_src_params); +EXPORT_SYMBOL(omap_set_dma_src_index); +EXPORT_SYMBOL(omap_set_dma_src_data_pack); +EXPORT_SYMBOL(omap_set_dma_src_burst_mode); + +EXPORT_SYMBOL(omap_set_dma_dest_params); +EXPORT_SYMBOL(omap_set_dma_dest_index); +EXPORT_SYMBOL(omap_set_dma_dest_data_pack); +EXPORT_SYMBOL(omap_set_dma_dest_burst_mode); + +EXPORT_SYMBOL(omap_dma_link_lch); +EXPORT_SYMBOL(omap_dma_unlink_lch); + +EXPORT_SYMBOL(omap_request_lcd_dma); +EXPORT_SYMBOL(omap_free_lcd_dma); +EXPORT_SYMBOL(omap_enable_lcd_dma); +EXPORT_SYMBOL(omap_setup_lcd_dma); +EXPORT_SYMBOL(omap_stop_lcd_dma); +EXPORT_SYMBOL(omap_set_lcd_dma_b1); +EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer); +EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller); +EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation); +EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres); +EXPORT_SYMBOL(omap_set_lcd_dma_b1_scale); +EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror); + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/gpio.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,762 @@ +/* + * linux/arch/arm/plat-omap/gpio.c + * + * Support functions for OMAP GPIO + * + * Copyright (C) 2003 Nokia Corporation + * Written by Juha Yrjölä + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* + * OMAP1510 GPIO registers + */ +#define OMAP1510_GPIO_BASE 0xfffce000 +#define OMAP1510_GPIO_DATA_INPUT 0x00 +#define OMAP1510_GPIO_DATA_OUTPUT 0x04 +#define OMAP1510_GPIO_DIR_CONTROL 0x08 +#define OMAP1510_GPIO_INT_CONTROL 0x0c +#define OMAP1510_GPIO_INT_MASK 0x10 +#define OMAP1510_GPIO_INT_STATUS 0x14 +#define OMAP1510_GPIO_PIN_CONTROL 0x18 + +#define OMAP1510_IH_GPIO_BASE 64 + +/* + * OMAP1610 specific GPIO registers + */ +#define OMAP1610_GPIO1_BASE 0xfffbe400 +#define OMAP1610_GPIO2_BASE 0xfffbec00 +#define OMAP1610_GPIO3_BASE 0xfffbb400 +#define OMAP1610_GPIO4_BASE 0xfffbbc00 +#define OMAP1610_GPIO_REVISION 0x0000 +#define OMAP1610_GPIO_SYSCONFIG 0x0010 +#define OMAP1610_GPIO_SYSSTATUS 0x0014 +#define OMAP1610_GPIO_IRQSTATUS1 0x0018 +#define OMAP1610_GPIO_IRQENABLE1 0x001c +#define OMAP1610_GPIO_DATAIN 0x002c +#define OMAP1610_GPIO_DATAOUT 0x0030 +#define OMAP1610_GPIO_DIRECTION 0x0034 +#define OMAP1610_GPIO_EDGE_CTRL1 0x0038 +#define OMAP1610_GPIO_EDGE_CTRL2 0x003c +#define OMAP1610_GPIO_CLEAR_IRQENABLE1 0x009c +#define OMAP1610_GPIO_CLEAR_DATAOUT 0x00b0 +#define OMAP1610_GPIO_SET_IRQENABLE1 0x00dc +#define OMAP1610_GPIO_SET_DATAOUT 0x00f0 + +/* + * OMAP730 specific GPIO registers + */ +#define OMAP730_GPIO1_BASE 0xfffbc000 +#define OMAP730_GPIO2_BASE 0xfffbc800 +#define OMAP730_GPIO3_BASE 0xfffbd000 +#define OMAP730_GPIO4_BASE 0xfffbd800 +#define OMAP730_GPIO5_BASE 0xfffbe000 +#define OMAP730_GPIO6_BASE 0xfffbe800 +#define OMAP730_GPIO_DATA_INPUT 0x00 +#define OMAP730_GPIO_DATA_OUTPUT 0x04 +#define OMAP730_GPIO_DIR_CONTROL 0x08 +#define OMAP730_GPIO_INT_CONTROL 0x0c +#define OMAP730_GPIO_INT_MASK 0x10 +#define OMAP730_GPIO_INT_STATUS 0x14 + +#define OMAP_MPUIO_MASK (~OMAP_MAX_GPIO_LINES & 0xff) + +struct gpio_bank { + u32 base; + u16 irq; + u16 virtual_irq_start; + u8 method; + u32 reserved_map; + spinlock_t lock; +}; + +#define METHOD_MPUIO 0 +#define METHOD_GPIO_1510 1 +#define METHOD_GPIO_1610 2 +#define METHOD_GPIO_730 3 + +#if defined(CONFIG_ARCH_OMAP16XX) +static struct gpio_bank gpio_bank_1610[5] = { + { OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO}, + { OMAP1610_GPIO1_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1610 }, + { OMAP1610_GPIO2_BASE, INT_1610_GPIO_BANK2, IH_GPIO_BASE + 16, METHOD_GPIO_1610 }, + { OMAP1610_GPIO3_BASE, INT_1610_GPIO_BANK3, IH_GPIO_BASE + 32, METHOD_GPIO_1610 }, + { OMAP1610_GPIO4_BASE, INT_1610_GPIO_BANK4, IH_GPIO_BASE + 48, METHOD_GPIO_1610 }, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP1510 +static struct gpio_bank gpio_bank_1510[2] = { + { OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO }, + { OMAP1510_GPIO_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1510 } +}; +#endif + +#ifdef CONFIG_ARCH_OMAP730 +static struct gpio_bank gpio_bank_730[7] = { + { OMAP_MPUIO_BASE, INT_730_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO }, + { OMAP730_GPIO1_BASE, INT_730_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_730 }, + { OMAP730_GPIO2_BASE, INT_730_GPIO_BANK2, IH_GPIO_BASE + 32, METHOD_GPIO_730 }, + { OMAP730_GPIO3_BASE, INT_730_GPIO_BANK3, IH_GPIO_BASE + 64, METHOD_GPIO_730 }, + { OMAP730_GPIO4_BASE, INT_730_GPIO_BANK4, IH_GPIO_BASE + 96, METHOD_GPIO_730 }, + { OMAP730_GPIO5_BASE, INT_730_GPIO_BANK5, IH_GPIO_BASE + 128, METHOD_GPIO_730 }, + { OMAP730_GPIO6_BASE, INT_730_GPIO_BANK6, IH_GPIO_BASE + 160, METHOD_GPIO_730 }, +}; +#endif + +static struct gpio_bank *gpio_bank; +static int gpio_bank_count; + +static inline struct gpio_bank *get_gpio_bank(int gpio) +{ +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1]; + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (cpu_is_omap16xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1 + (gpio >> 4)]; + } +#endif +#ifdef CONFIG_ARCH_OMAP730 + if (cpu_is_omap730()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1 + (gpio >> 5)]; + } +#endif +} + +static inline int get_gpio_index(int gpio) +{ + if (cpu_is_omap730()) + return gpio & 0x1f; + else + return gpio & 0x0f; +} + +static inline int gpio_valid(int gpio) +{ + if (gpio < 0) + return -1; + if (OMAP_GPIO_IS_MPUIO(gpio)) { + if ((gpio & OMAP_MPUIO_MASK) > 16) + return -1; + return 0; + } +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510() && gpio < 16) + return 0; +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if ((cpu_is_omap16xx()) && gpio < 64) + return 0; +#endif +#ifdef CONFIG_ARCH_OMAP730 + if (cpu_is_omap730() && gpio < 192) + return 0; +#endif + return -1; +} + +static int check_gpio(int gpio) +{ + if (unlikely(gpio_valid(gpio)) < 0) { + printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio); + dump_stack(); + return -1; + } + return 0; +} + +static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) +{ + u32 reg = bank->base; + u32 l; + + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_IO_CNTL; + break; + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DIR_CONTROL; + break; + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_DIRECTION; + break; + case METHOD_GPIO_730: + reg += OMAP730_GPIO_DIR_CONTROL; + break; + } + l = __raw_readl(reg); + if (is_input) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + __raw_writel(l, reg); +} + +void omap_set_gpio_direction(int gpio, int is_input) +{ + struct gpio_bank *bank; + + if (check_gpio(gpio) < 0) + return; + bank = get_gpio_bank(gpio); + spin_lock(&bank->lock); + _set_gpio_direction(bank, get_gpio_index(gpio), is_input); + spin_unlock(&bank->lock); +} + +static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable) +{ + u32 reg = bank->base; + u32 l = 0; + + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_OUTPUT; + l = __raw_readl(reg); + if (enable) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + break; + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DATA_OUTPUT; + l = __raw_readl(reg); + if (enable) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + break; + case METHOD_GPIO_1610: + if (enable) + reg += OMAP1610_GPIO_SET_DATAOUT; + else + reg += OMAP1610_GPIO_CLEAR_DATAOUT; + l = 1 << gpio; + break; + case METHOD_GPIO_730: + reg += OMAP730_GPIO_DATA_OUTPUT; + l = __raw_readl(reg); + if (enable) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + break; + default: + BUG(); + return; + } + __raw_writel(l, reg); +} + +void omap_set_gpio_dataout(int gpio, int enable) +{ + struct gpio_bank *bank; + + if (check_gpio(gpio) < 0) + return; + bank = get_gpio_bank(gpio); + spin_lock(&bank->lock); + _set_gpio_dataout(bank, get_gpio_index(gpio), enable); + spin_unlock(&bank->lock); +} + +int omap_get_gpio_datain(int gpio) +{ + struct gpio_bank *bank; + u32 reg; + + if (check_gpio(gpio) < 0) + return -1; + bank = get_gpio_bank(gpio); + reg = bank->base; + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_INPUT_LATCH; + break; + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DATA_INPUT; + break; + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_DATAIN; + break; + case METHOD_GPIO_730: + reg += OMAP730_GPIO_DATA_INPUT; + break; + default: + BUG(); + return -1; + } + return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0; +} + +static void _set_gpio_edge_ctrl(struct gpio_bank *bank, int gpio, int edge) +{ + u32 reg = bank->base; + u32 l; + + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_INT_EDGE; + l = __raw_readl(reg); + if (edge == OMAP_GPIO_RISING_EDGE) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + __raw_writel(l, reg); + break; + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_CONTROL; + l = __raw_readl(reg); + if (edge == OMAP_GPIO_RISING_EDGE) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + __raw_writel(l, reg); + break; + case METHOD_GPIO_1610: + edge &= 0x03; + if (gpio & 0x08) + reg += OMAP1610_GPIO_EDGE_CTRL2; + else + reg += OMAP1610_GPIO_EDGE_CTRL1; + gpio &= 0x07; + l = __raw_readl(reg); + l &= ~(3 << (gpio << 1)); + l |= edge << (gpio << 1); + __raw_writel(l, reg); + break; + case METHOD_GPIO_730: + reg += OMAP730_GPIO_INT_CONTROL; + l = __raw_readl(reg); + if (edge == OMAP_GPIO_RISING_EDGE) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + __raw_writel(l, reg); + break; + default: + BUG(); + return; + } +} + +void omap_set_gpio_edge_ctrl(int gpio, int edge) +{ + struct gpio_bank *bank; + + if (check_gpio(gpio) < 0) + return; + bank = get_gpio_bank(gpio); + spin_lock(&bank->lock); + _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), edge); + spin_unlock(&bank->lock); +} + + +static int _get_gpio_edge_ctrl(struct gpio_bank *bank, int gpio) +{ + u32 reg = bank->base, l; + + switch (bank->method) { + case METHOD_MPUIO: + l = __raw_readl(reg + OMAP_MPUIO_GPIO_INT_EDGE); + return (l & (1 << gpio)) ? + OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; + case METHOD_GPIO_1510: + l = __raw_readl(reg + OMAP1510_GPIO_INT_CONTROL); + return (l & (1 << gpio)) ? + OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; + case METHOD_GPIO_1610: + if (gpio & 0x08) + reg += OMAP1610_GPIO_EDGE_CTRL2; + else + reg += OMAP1610_GPIO_EDGE_CTRL1; + return (__raw_readl(reg) >> ((gpio & 0x07) << 1)) & 0x03; + case METHOD_GPIO_730: + l = __raw_readl(reg + OMAP730_GPIO_INT_CONTROL); + return (l & (1 << gpio)) ? + OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE; + default: + BUG(); + return -1; + } +} + +static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) +{ + u32 reg = bank->base; + + switch (bank->method) { + case METHOD_MPUIO: + /* MPUIO irqstatus is reset by reading the status register, + * so do nothing here */ + return; + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_STATUS; + break; + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_IRQSTATUS1; + break; + case METHOD_GPIO_730: + reg += OMAP730_GPIO_INT_STATUS; + break; + default: + BUG(); + return; + } + __raw_writel(gpio_mask, reg); +} + +static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio) +{ + _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio)); +} + +static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable) +{ + u32 reg = bank->base; + u32 l; + + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_MASKIT; + l = __raw_readl(reg); + if (enable) + l &= ~(gpio_mask); + else + l |= gpio_mask; + break; + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_MASK; + l = __raw_readl(reg); + if (enable) + l &= ~(gpio_mask); + else + l |= gpio_mask; + break; + case METHOD_GPIO_1610: + if (enable) + reg += OMAP1610_GPIO_SET_IRQENABLE1; + else + reg += OMAP1610_GPIO_CLEAR_IRQENABLE1; + l = gpio_mask; + break; + case METHOD_GPIO_730: + reg += OMAP730_GPIO_INT_MASK; + l = __raw_readl(reg); + if (enable) + l &= ~(gpio_mask); + else + l |= gpio_mask; + break; + default: + BUG(); + return; + } + __raw_writel(l, reg); +} + +static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable) +{ + _enable_gpio_irqbank(bank, 1 << get_gpio_index(gpio), enable); +} + +int omap_request_gpio(int gpio) +{ + struct gpio_bank *bank; + + if (check_gpio(gpio) < 0) + return -EINVAL; + + bank = get_gpio_bank(gpio); + spin_lock(&bank->lock); + if (unlikely(bank->reserved_map & (1 << get_gpio_index(gpio)))) { + printk(KERN_ERR "omap-gpio: GPIO %d is already reserved!\n", gpio); + dump_stack(); + spin_unlock(&bank->lock); + return -1; + } + bank->reserved_map |= (1 << get_gpio_index(gpio)); +#ifdef CONFIG_ARCH_OMAP1510 + if (bank->method == METHOD_GPIO_1510) { + u32 reg; + + /* Claim the pin for the ARM */ + reg = bank->base + OMAP1510_GPIO_PIN_CONTROL; + __raw_writel(__raw_readl(reg) | (1 << get_gpio_index(gpio)), reg); + } +#endif + spin_unlock(&bank->lock); + + return 0; +} + +void omap_free_gpio(int gpio) +{ + struct gpio_bank *bank; + + if (check_gpio(gpio) < 0) + return; + bank = get_gpio_bank(gpio); + spin_lock(&bank->lock); + if (unlikely(!(bank->reserved_map & (1 << get_gpio_index(gpio))))) { + printk(KERN_ERR "omap-gpio: GPIO %d wasn't reserved!\n", gpio); + dump_stack(); + spin_unlock(&bank->lock); + return; + } + bank->reserved_map &= ~(1 << get_gpio_index(gpio)); + _set_gpio_direction(bank, get_gpio_index(gpio), 1); + _set_gpio_irqenable(bank, gpio, 0); + _clear_gpio_irqstatus(bank, gpio); + spin_unlock(&bank->lock); +} + +/* + * We need to unmask the GPIO bank interrupt as soon as possible to + * avoid missing GPIO interrupts for other lines in the bank. + * Then we need to mask-read-clear-unmask the triggered GPIO lines + * in the bank to avoid missing nested interrupts for a GPIO line. + * If we wait to unmask individual GPIO lines in the bank after the + * line's interrupt handler has been run, we may miss some nested + * interrupts. + */ +static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc, + struct pt_regs *regs) +{ + u32 isr_reg = 0; + u32 isr; + unsigned int gpio_irq; + struct gpio_bank *bank; + + desc->chip->ack(irq); + + bank = (struct gpio_bank *) desc->data; + if (bank->method == METHOD_MPUIO) + isr_reg = bank->base + OMAP_MPUIO_GPIO_INT; +#ifdef CONFIG_ARCH_OMAP1510 + if (bank->method == METHOD_GPIO_1510) + isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS; +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (bank->method == METHOD_GPIO_1610) + isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1; +#endif +#ifdef CONFIG_ARCH_OMAP730 + if (bank->method == METHOD_GPIO_730) + isr_reg = bank->base + OMAP730_GPIO_INT_STATUS; +#endif + + isr = __raw_readl(isr_reg); + _enable_gpio_irqbank(bank, isr, 0); + _clear_gpio_irqbank(bank, isr); + _enable_gpio_irqbank(bank, isr, 1); + desc->chip->unmask(irq); + + if (unlikely(!isr)) + return; + + gpio_irq = bank->virtual_irq_start; + for (; isr != 0; isr >>= 1, gpio_irq++) { + struct irqdesc *d; + if (!(isr & 1)) + continue; + d = irq_desc + gpio_irq; + d->handle(gpio_irq, d, regs); + } +} + +static void gpio_ack_irq(unsigned int irq) +{ + unsigned int gpio = irq - IH_GPIO_BASE; + struct gpio_bank *bank = get_gpio_bank(gpio); + + _clear_gpio_irqstatus(bank, gpio); +} + +static void gpio_mask_irq(unsigned int irq) +{ + unsigned int gpio = irq - IH_GPIO_BASE; + struct gpio_bank *bank = get_gpio_bank(gpio); + + _set_gpio_irqenable(bank, gpio, 0); +} + +static void gpio_unmask_irq(unsigned int irq) +{ + unsigned int gpio = irq - IH_GPIO_BASE; + struct gpio_bank *bank = get_gpio_bank(gpio); + + if (_get_gpio_edge_ctrl(bank, get_gpio_index(gpio)) == OMAP_GPIO_NO_EDGE) { + printk(KERN_ERR "OMAP GPIO %d: trying to enable GPIO IRQ while no edge is set\n", + gpio); + _set_gpio_edge_ctrl(bank, get_gpio_index(gpio), OMAP_GPIO_RISING_EDGE); + } + _set_gpio_irqenable(bank, gpio, 1); +} + +static void mpuio_ack_irq(unsigned int irq) +{ + /* The ISR is reset automatically, so do nothing here. */ +} + +static void mpuio_mask_irq(unsigned int irq) +{ + unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE); + struct gpio_bank *bank = get_gpio_bank(gpio); + + _set_gpio_irqenable(bank, gpio, 0); +} + +static void mpuio_unmask_irq(unsigned int irq) +{ + unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE); + struct gpio_bank *bank = get_gpio_bank(gpio); + + _set_gpio_irqenable(bank, gpio, 1); +} + +static struct irqchip gpio_irq_chip = { + .ack = gpio_ack_irq, + .mask = gpio_mask_irq, + .unmask = gpio_unmask_irq, +}; + +static struct irqchip mpuio_irq_chip = { + .ack = mpuio_ack_irq, + .mask = mpuio_mask_irq, + .unmask = mpuio_unmask_irq +}; + +static int initialized = 0; + +static int __init _omap_gpio_init(void) +{ + int i; + struct gpio_bank *bank; + + initialized = 1; + +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + printk(KERN_INFO "OMAP1510 GPIO hardware\n"); + gpio_bank_count = 2; + gpio_bank = gpio_bank_1510; + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (cpu_is_omap16xx()) { + int rev; + + gpio_bank_count = 5; + gpio_bank = gpio_bank_1610; + rev = omap_readw(gpio_bank[1].base + OMAP1610_GPIO_REVISION); + printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n", + (rev >> 4) & 0x0f, rev & 0x0f); + } +#endif +#ifdef CONFIG_ARCH_OMAP730 + if (cpu_is_omap730()) { + printk(KERN_INFO "OMAP730 GPIO hardware\n"); + gpio_bank_count = 7; + gpio_bank = gpio_bank_730; + } +#endif + for (i = 0; i < gpio_bank_count; i++) { + int j, gpio_count = 16; + + bank = &gpio_bank[i]; + bank->reserved_map = 0; + bank->base = IO_ADDRESS(bank->base); + spin_lock_init(&bank->lock); + if (bank->method == METHOD_MPUIO) { + omap_writew(0xFFFF, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_MASKIT); + } +#ifdef CONFIG_ARCH_OMAP1510 + if (bank->method == METHOD_GPIO_1510) { + __raw_writew(0xffff, bank->base + OMAP1510_GPIO_INT_MASK); + __raw_writew(0x0000, bank->base + OMAP1510_GPIO_INT_STATUS); + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (bank->method == METHOD_GPIO_1610) { + __raw_writew(0x0000, bank->base + OMAP1610_GPIO_IRQENABLE1); + __raw_writew(0xffff, bank->base + OMAP1610_GPIO_IRQSTATUS1); + } +#endif +#ifdef CONFIG_ARCH_OMAP730 + if (bank->method == METHOD_GPIO_730) { + __raw_writel(0xffffffff, bank->base + OMAP730_GPIO_INT_MASK); + __raw_writel(0x00000000, bank->base + OMAP730_GPIO_INT_STATUS); + + gpio_count = 32; /* 730 has 32-bit GPIOs */ + } +#endif + for (j = bank->virtual_irq_start; + j < bank->virtual_irq_start + gpio_count; j++) { + if (bank->method == METHOD_MPUIO) + set_irq_chip(j, &mpuio_irq_chip); + else + set_irq_chip(j, &gpio_irq_chip); + set_irq_handler(j, do_simple_IRQ); + set_irq_flags(j, IRQF_VALID); + } + set_irq_chained_handler(bank->irq, gpio_irq_handler); + set_irq_data(bank->irq, bank); + } + + /* Enable system clock for GPIO module. + * The CAM_CLK_CTRL *is* really the right place. */ + if (cpu_is_omap1610() || cpu_is_omap1710()) + omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04, ULPD_CAM_CLK_CTRL); + + return 0; +} + +/* + * This may get called early from board specific init + */ +int omap_gpio_init(void) +{ + if (!initialized) + return _omap_gpio_init(); + else + return 0; +} + +EXPORT_SYMBOL(omap_request_gpio); +EXPORT_SYMBOL(omap_free_gpio); +EXPORT_SYMBOL(omap_set_gpio_direction); +EXPORT_SYMBOL(omap_set_gpio_dataout); +EXPORT_SYMBOL(omap_get_gpio_datain); +EXPORT_SYMBOL(omap_set_gpio_edge_ctrl); + +arch_initcall(omap_gpio_init); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/Kconfig 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,112 @@ +if ARCH_OMAP + +menu "TI OMAP Implementations" + +config ARCH_OMAP_OTG + bool + +choice + prompt "OMAP System Type" + default ARCH_OMAP1 + +config ARCH_OMAP1 + bool "TI OMAP1" + +config ARCH_OMAP2 + bool "TI OMAP2" + +endchoice + +comment "OMAP Feature Selections" + +config OMAP_RESET_CLOCKS + bool "Reset unused clocks during boot" + depends on ARCH_OMAP + default n + help + Say Y if you want to reset unused clocks during boot. + This option saves power, but assumes all drivers are + using the clock framework. Broken drivers that do not + yet use clock framework may not work with this option. + If you are booting from another operating system, you + probably do not want this option enabled until your + device drivers work properly. + +config OMAP_MUX + bool "OMAP multiplexing support" + depends on ARCH_OMAP + default y + help + Pin multiplexing support for OMAP boards. If your bootloader + sets the multiplexing correctly, say N. Otherwise, or if unsure, + say Y. + +config OMAP_MUX_DEBUG + bool "Multiplexing debug output" + depends on OMAP_MUX + default n + help + Makes the multiplexing functions print out a lot of debug info. + This is useful if you want to find out the correct values of the + multiplexing registers. + +config OMAP_MUX_WARNINGS + bool "Warn about pins the bootloader didn't set up" + depends on OMAP_MUX + default y + help + Choose Y here to warn whenever driver initialization logic needs + to change the pin multiplexing setup. When there are no warnings + printed, it's safe to deselect OMAP_MUX for your product. + +choice + prompt "System timer" + default OMAP_MPU_TIMER + +config OMAP_MPU_TIMER + bool "Use mpu timer" + help + Select this option if you want to use the OMAP mpu timer. This + timer provides more intra-tick resolution than the 32KHz timer, + but consumes more power. + +config OMAP_32K_TIMER + bool "Use 32KHz timer" + depends on ARCH_OMAP16XX + help + Select this option if you want to enable the OMAP 32KHz timer. + This timer saves power compared to the OMAP_MPU_TIMER, and has + support for no tick during idle. The 32KHz timer provides less + intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is + currently only available for OMAP-16xx. + +endchoice + +config OMAP_32K_TIMER_HZ + int "Kernel internal timer frequency for 32KHz timer" + range 32 1024 + depends on OMAP_32K_TIMER + default "128" + help + Kernel internal timer frequency should be a divisor of 32768, + such as 64 or 128. + +choice + prompt "Low-level debug console UART" + depends on ARCH_OMAP + default OMAP_LL_DEBUG_UART1 + +config OMAP_LL_DEBUG_UART1 + bool "UART1" + +config OMAP_LL_DEBUG_UART2 + bool "UART2" + +config OMAP_LL_DEBUG_UART3 + bool "UART3" + +endchoice + +endmenu + +endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/Makefile 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,17 @@ +# +# Makefile for the linux kernel. +# + +# Common support +obj-y := common.o dma.o clock.o mux.o gpio.o mcbsp.o usb.o +obj-m := +obj-n := +obj- := + +# OCPI interconnect support for 1710, 1610 and 5912 +obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o + +# Power Management +obj-$(CONFIG_PM) += pm.o sleep.o + +obj-$(CONFIG_CPU_FREQ) += cpu-omap.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/mcbsp.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,758 @@ +/* + * linux/arch/arm/plat-omap/mcbsp.c + * + * Copyright (C) 2004 Nokia Corporation + * Author: Samuel Ortiz + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Multichannel mode not supported. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef CONFIG_MCBSP_DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) do { } while (0) +#endif + +struct omap_mcbsp { + u32 io_base; + u8 id; + u8 free; + omap_mcbsp_word_length rx_word_length; + omap_mcbsp_word_length tx_word_length; + + /* IRQ based TX/RX */ + int rx_irq; + int tx_irq; + + /* DMA stuff */ + u8 dma_rx_sync; + short dma_rx_lch; + u8 dma_tx_sync; + short dma_tx_lch; + + /* Completion queues */ + struct completion tx_irq_completion; + struct completion rx_irq_completion; + struct completion tx_dma_completion; + struct completion rx_dma_completion; + + spinlock_t lock; +}; + +static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT]; +static struct clk *mcbsp_dsp_ck = 0; +static struct clk *mcbsp_api_ck = 0; +static struct clk *mcbsp_dspxor_ck = 0; + + +static void omap_mcbsp_dump_reg(u8 id) +{ + DBG("**** MCBSP%d regs ****\n", mcbsp[id].id); + DBG("DRR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR2)); + DBG("DRR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR1)); + DBG("DXR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR2)); + DBG("DXR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR1)); + DBG("SPCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR2)); + DBG("SPCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR1)); + DBG("RCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR2)); + DBG("RCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR1)); + DBG("XCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR2)); + DBG("XCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR1)); + DBG("SRGR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR2)); + DBG("SRGR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR1)); + DBG("PCR0: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, PCR0)); + DBG("***********************\n"); +} + + +static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id); + + DBG("TX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2)); + + complete(&mcbsp_tx->tx_irq_completion); + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct omap_mcbsp * mcbsp_rx = (struct omap_mcbsp *)(dev_id); + + DBG("RX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR2)); + + complete(&mcbsp_rx->rx_irq_completion); + return IRQ_HANDLED; +} + + +static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data); + + DBG("TX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2)); + + /* We can free the channels */ + omap_free_dma(mcbsp_dma_tx->dma_tx_lch); + mcbsp_dma_tx->dma_tx_lch = -1; + + complete(&mcbsp_dma_tx->tx_dma_completion); +} + +static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct omap_mcbsp * mcbsp_dma_rx = (struct omap_mcbsp *)(data); + + DBG("RX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2)); + + /* We can free the channels */ + omap_free_dma(mcbsp_dma_rx->dma_rx_lch); + mcbsp_dma_rx->dma_rx_lch = -1; + + complete(&mcbsp_dma_rx->rx_dma_completion); +} + + +/* + * omap_mcbsp_config simply write a config to the + * appropriate McBSP. + * You either call this function or set the McBSP registers + * by yourself before calling omap_mcbsp_start(). + */ + +void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config) +{ + u32 io_base = mcbsp[id].io_base; + + DBG("OMAP-McBSP: McBSP%d io_base: 0x%8x\n", id+1, io_base); + + /* We write the given config */ + OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2); + OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1); + OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2); + OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1); + OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2); + OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1); + OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2); + OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1); + OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2); + OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1); + OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0); +} + + + +static int omap_mcbsp_check(unsigned int id) +{ + if (cpu_is_omap730()) { + if (id > OMAP_MAX_MCBSP_COUNT - 1) { + printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); + return -1; + } + return 0; + } + + if (cpu_is_omap1510() || cpu_is_omap16xx()) { + if (id > OMAP_MAX_MCBSP_COUNT) { + printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); + return -1; + } + return 0; + } + + return -1; +} + +#define EN_XORPCK 1 +#define DSP_RSTCT2 0xe1008014 + +static void omap_mcbsp_dsp_request(void) +{ + if (cpu_is_omap1510() || cpu_is_omap16xx()) { + clk_use(mcbsp_dsp_ck); + clk_use(mcbsp_api_ck); + + /* enable 12MHz clock to mcbsp 1 & 3 */ + clk_use(mcbsp_dspxor_ck); + __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1 << 1, + DSP_RSTCT2); + } +} + +static void omap_mcbsp_dsp_free(void) +{ + if (cpu_is_omap1510() || cpu_is_omap16xx()) { + clk_unuse(mcbsp_dspxor_ck); + clk_unuse(mcbsp_dsp_ck); + clk_unuse(mcbsp_api_ck); + } +} + +int omap_mcbsp_request(unsigned int id) +{ + int err; + + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + /* + * On 1510, 1610 and 1710, McBSP1 and McBSP3 + * are DSP public peripherals. + */ + if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) + omap_mcbsp_dsp_request(); + + spin_lock(&mcbsp[id].lock); + if (!mcbsp[id].free) { + printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); + spin_unlock(&mcbsp[id].lock); + return -1; + } + + mcbsp[id].free = 0; + spin_unlock(&mcbsp[id].lock); + + /* We need to get IRQs here */ + err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, + "McBSP", + (void *) (&mcbsp[id])); + if (err != 0) { + printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", + mcbsp[id].tx_irq, mcbsp[id].id); + return err; + } + + init_completion(&(mcbsp[id].tx_irq_completion)); + + + err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, + "McBSP", + (void *) (&mcbsp[id])); + if (err != 0) { + printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", + mcbsp[id].rx_irq, mcbsp[id].id); + free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); + return err; + } + + init_completion(&(mcbsp[id].rx_irq_completion)); + return 0; + +} + +void omap_mcbsp_free(unsigned int id) +{ + if (omap_mcbsp_check(id) < 0) + return; + + if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) + omap_mcbsp_dsp_free(); + + spin_lock(&mcbsp[id].lock); + if (mcbsp[id].free) { + printk (KERN_ERR "OMAP-McBSP: McBSP%d was not reserved\n", id + 1); + spin_unlock(&mcbsp[id].lock); + return; + } + + mcbsp[id].free = 1; + spin_unlock(&mcbsp[id].lock); + + /* Free IRQs */ + free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); + free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); +} + +/* + * Here we start the McBSP, by enabling the sample + * generator, both transmitter and receivers, + * and the frame sync. + */ +void omap_mcbsp_start(unsigned int id) +{ + u32 io_base; + u16 w; + + if (omap_mcbsp_check(id) < 0) + return; + + io_base = mcbsp[id].io_base; + + mcbsp[id].rx_word_length = ((OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7); + mcbsp[id].tx_word_length = ((OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7); + + /* Start the sample generator */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); + + /* Enable transmitter and receiver */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); + + w = OMAP_MCBSP_READ(io_base, SPCR1); + OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); + + udelay(100); + + /* Start frame sync */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); + + /* Dump McBSP Regs */ + omap_mcbsp_dump_reg(id); + +} + +void omap_mcbsp_stop(unsigned int id) +{ + u32 io_base; + u16 w; + + if (omap_mcbsp_check(id) < 0) + return; + + io_base = mcbsp[id].io_base; + + /* Reset transmitter */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); + + /* Reset receiver */ + w = OMAP_MCBSP_READ(io_base, SPCR1); + OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); + + /* Reset the sample rate generator */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); +} + + +/* polled mcbsp i/o operations */ +int omap_mcbsp_pollwrite(unsigned int id, u16 buf) +{ + u32 base = mcbsp[id].io_base; + writew(buf, base + OMAP_MCBSP_REG_DXR1); + /* if frame sync error - clear the error */ + if (readw(base + OMAP_MCBSP_REG_SPCR2) & XSYNC_ERR) { + /* clear error */ + writew(readw(base + OMAP_MCBSP_REG_SPCR2) & (~XSYNC_ERR), + base + OMAP_MCBSP_REG_SPCR2); + /* resend */ + return -1; + } else { + /* wait for transmit confirmation */ + int attemps = 0; + while (!(readw(base + OMAP_MCBSP_REG_SPCR2) & XRDY)) { + if (attemps++ > 1000) { + writew(readw(base + OMAP_MCBSP_REG_SPCR2) & + (~XRST), + base + OMAP_MCBSP_REG_SPCR2); + udelay(10); + writew(readw(base + OMAP_MCBSP_REG_SPCR2) | + (XRST), + base + OMAP_MCBSP_REG_SPCR2); + udelay(10); + printk(KERN_ERR + " Could not write to McBSP Register\n"); + return -2; + } + } + } + return 0; +} + +int omap_mcbsp_pollread(unsigned int id, u16 * buf) +{ + u32 base = mcbsp[id].io_base; + /* if frame sync error - clear the error */ + if (readw(base + OMAP_MCBSP_REG_SPCR1) & RSYNC_ERR) { + /* clear error */ + writew(readw(base + OMAP_MCBSP_REG_SPCR1) & (~RSYNC_ERR), + base + OMAP_MCBSP_REG_SPCR1); + /* resend */ + return -1; + } else { + /* wait for recieve confirmation */ + int attemps = 0; + while (!(readw(base + OMAP_MCBSP_REG_SPCR1) & RRDY)) { + if (attemps++ > 1000) { + writew(readw(base + OMAP_MCBSP_REG_SPCR1) & + (~RRST), + base + OMAP_MCBSP_REG_SPCR1); + udelay(10); + writew(readw(base + OMAP_MCBSP_REG_SPCR1) | + (RRST), + base + OMAP_MCBSP_REG_SPCR1); + udelay(10); + printk(KERN_ERR + " Could not read from McBSP Register\n"); + return -2; + } + } + } + *buf = readw(base + OMAP_MCBSP_REG_DRR1); + return 0; +} + +/* + * IRQ based word transmission. + */ +void omap_mcbsp_xmit_word(unsigned int id, u32 word) +{ + u32 io_base; + omap_mcbsp_word_length word_length = mcbsp[id].tx_word_length; + + if (omap_mcbsp_check(id) < 0) + return; + + io_base = mcbsp[id].io_base; + + wait_for_completion(&(mcbsp[id].tx_irq_completion)); + + if (word_length > OMAP_MCBSP_WORD_16) + OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); + OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); +} + +u32 omap_mcbsp_recv_word(unsigned int id) +{ + u32 io_base; + u16 word_lsb, word_msb = 0; + omap_mcbsp_word_length word_length = mcbsp[id].rx_word_length; + + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + io_base = mcbsp[id].io_base; + + wait_for_completion(&(mcbsp[id].rx_irq_completion)); + + if (word_length > OMAP_MCBSP_WORD_16) + word_msb = OMAP_MCBSP_READ(io_base, DRR2); + word_lsb = OMAP_MCBSP_READ(io_base, DRR1); + + return (word_lsb | (word_msb << 16)); +} + + +/* + * Simple DMA based buffer rx/tx routines. + * Nothing fancy, just a single buffer tx/rx through DMA. + * The DMA resources are released once the transfer is done. + * For anything fancier, you should use your own customized DMA + * routines and callbacks. + */ +int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) +{ + int dma_tx_ch; + + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + if (omap_request_dma(mcbsp[id].dma_tx_sync, "McBSP TX", omap_mcbsp_tx_dma_callback, + &mcbsp[id], + &dma_tx_ch)) { + printk("OMAP-McBSP: Unable to request DMA channel for McBSP%d TX. Trying IRQ based TX\n", id+1); + return -EAGAIN; + } + mcbsp[id].dma_tx_lch = dma_tx_ch; + + DBG("TX DMA on channel %d\n", dma_tx_ch); + + init_completion(&(mcbsp[id].tx_dma_completion)); + + omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, + OMAP_DMA_DATA_TYPE_S16, + length >> 1, 1, + OMAP_DMA_SYNC_ELEMENT); + + omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, + mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1); + + omap_set_dma_src_params(mcbsp[id].dma_tx_lch, + OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, + buffer); + + omap_start_dma(mcbsp[id].dma_tx_lch); + wait_for_completion(&(mcbsp[id].tx_dma_completion)); + return 0; +} + + +int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) +{ + int dma_rx_ch; + + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + if (omap_request_dma(mcbsp[id].dma_rx_sync, "McBSP RX", omap_mcbsp_rx_dma_callback, + &mcbsp[id], + &dma_rx_ch)) { + printk("Unable to request DMA channel for McBSP%d RX. Trying IRQ based RX\n", id+1); + return -EAGAIN; + } + mcbsp[id].dma_rx_lch = dma_rx_ch; + + DBG("RX DMA on channel %d\n", dma_rx_ch); + + init_completion(&(mcbsp[id].rx_dma_completion)); + + omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, + OMAP_DMA_DATA_TYPE_S16, + length >> 1, 1, + OMAP_DMA_SYNC_ELEMENT); + + omap_set_dma_src_params(mcbsp[id].dma_rx_lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, + mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1); + + omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, + OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, + buffer); + + omap_start_dma(mcbsp[id].dma_rx_lch); + wait_for_completion(&(mcbsp[id].rx_dma_completion)); + return 0; +} + + +/* + * SPI wrapper. + * Since SPI setup is much simpler than the generic McBSP one, + * this wrapper just need an omap_mcbsp_spi_cfg structure as an input. + * Once this is done, you can call omap_mcbsp_start(). + */ +void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg) +{ + struct omap_mcbsp_reg_cfg mcbsp_cfg; + + if (omap_mcbsp_check(id) < 0) + return; + + memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); + + /* SPI has only one frame */ + mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0)); + mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0)); + + /* Clock stop mode */ + if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY) + mcbsp_cfg.spcr1 |= (1 << 12); + else + mcbsp_cfg.spcr1 |= (3 << 11); + + /* Set clock parities */ + if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING) + mcbsp_cfg.pcr0 |= CLKRP; + else + mcbsp_cfg.pcr0 &= ~CLKRP; + + if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING) + mcbsp_cfg.pcr0 &= ~CLKXP; + else + mcbsp_cfg.pcr0 |= CLKXP; + + /* Set SCLKME to 0 and CLKSM to 1 */ + mcbsp_cfg.pcr0 &= ~SCLKME; + mcbsp_cfg.srgr2 |= CLKSM; + + /* Set FSXP */ + if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH) + mcbsp_cfg.pcr0 &= ~FSXP; + else + mcbsp_cfg.pcr0 |= FSXP; + + if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) { + mcbsp_cfg.pcr0 |= CLKXM; + mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div -1); + mcbsp_cfg.pcr0 |= FSXM; + mcbsp_cfg.srgr2 &= ~FSGM; + mcbsp_cfg.xcr2 |= XDATDLY(1); + mcbsp_cfg.rcr2 |= RDATDLY(1); + } + else { + mcbsp_cfg.pcr0 &= ~CLKXM; + mcbsp_cfg.srgr1 |= CLKGDV(1); + mcbsp_cfg.pcr0 &= ~FSXM; + mcbsp_cfg.xcr2 &= ~XDATDLY(3); + mcbsp_cfg.rcr2 &= ~RDATDLY(3); + } + + mcbsp_cfg.xcr2 &= ~XPHASE; + mcbsp_cfg.rcr2 &= ~RPHASE; + + omap_mcbsp_config(id, &mcbsp_cfg); +} + + +/* + * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. + * 730 has only 2 McBSP, and both of them are MPU peripherals. + */ +struct omap_mcbsp_info { + u32 virt_base; + u8 dma_rx_sync, dma_tx_sync; + u16 rx_irq, tx_irq; +}; + +#ifdef CONFIG_ARCH_OMAP730 +static const struct omap_mcbsp_info mcbsp_730[] = { + [0] = { .virt_base = io_p2v(OMAP730_MCBSP1_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP_DMA_MCBSP1_TX, + .rx_irq = INT_730_McBSP1RX, + .tx_irq = INT_730_McBSP1TX }, + [1] = { .virt_base = io_p2v(OMAP730_MCBSP2_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP3_RX, + .dma_tx_sync = OMAP_DMA_MCBSP3_TX, + .rx_irq = INT_730_McBSP2RX, + .tx_irq = INT_730_McBSP2TX }, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP1510 +static const struct omap_mcbsp_info mcbsp_1510[] = { + [0] = { .virt_base = OMAP1510_MCBSP1_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP_DMA_MCBSP1_TX, + .rx_irq = INT_McBSP1RX, + .tx_irq = INT_McBSP1TX }, + [1] = { .virt_base = io_p2v(OMAP1510_MCBSP2_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP2_RX, + .dma_tx_sync = OMAP_DMA_MCBSP2_TX, + .rx_irq = INT_1510_SPI_RX, + .tx_irq = INT_1510_SPI_TX }, + [2] = { .virt_base = OMAP1510_MCBSP3_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP3_RX, + .dma_tx_sync = OMAP_DMA_MCBSP3_TX, + .rx_irq = INT_McBSP3RX, + .tx_irq = INT_McBSP3TX }, +}; +#endif + +#if defined(CONFIG_ARCH_OMAP16XX) +static const struct omap_mcbsp_info mcbsp_1610[] = { + [0] = { .virt_base = OMAP1610_MCBSP1_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP_DMA_MCBSP1_TX, + .rx_irq = INT_McBSP1RX, + .tx_irq = INT_McBSP1TX }, + [1] = { .virt_base = io_p2v(OMAP1610_MCBSP2_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP2_RX, + .dma_tx_sync = OMAP_DMA_MCBSP2_TX, + .rx_irq = INT_1610_McBSP2_RX, + .tx_irq = INT_1610_McBSP2_TX }, + [2] = { .virt_base = OMAP1610_MCBSP3_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP3_RX, + .dma_tx_sync = OMAP_DMA_MCBSP3_TX, + .rx_irq = INT_McBSP3RX, + .tx_irq = INT_McBSP3TX }, +}; +#endif + +static int __init omap_mcbsp_init(void) +{ + int mcbsp_count = 0, i; + static const struct omap_mcbsp_info *mcbsp_info; + + printk("Initializing OMAP McBSP system\n"); + + mcbsp_dsp_ck = clk_get(0, "dsp_ck"); + if (IS_ERR(mcbsp_dsp_ck)) { + printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n"); + return PTR_ERR(mcbsp_dsp_ck); + } + mcbsp_api_ck = clk_get(0, "api_ck"); + if (IS_ERR(mcbsp_api_ck)) { + printk(KERN_ERR "mcbsp: could not acquire api_ck handle.\n"); + return PTR_ERR(mcbsp_api_ck); + } + mcbsp_dspxor_ck = clk_get(0, "dspxor_ck"); + if (IS_ERR(mcbsp_dspxor_ck)) { + printk(KERN_ERR "mcbsp: could not acquire dspxor_ck handle.\n"); + return PTR_ERR(mcbsp_dspxor_ck); + } + +#ifdef CONFIG_ARCH_OMAP730 + if (cpu_is_omap730()) { + mcbsp_info = mcbsp_730; + mcbsp_count = ARRAY_SIZE(mcbsp_730); + } +#endif +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + mcbsp_info = mcbsp_1510; + mcbsp_count = ARRAY_SIZE(mcbsp_1510); + } +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (cpu_is_omap16xx()) { + mcbsp_info = mcbsp_1610; + mcbsp_count = ARRAY_SIZE(mcbsp_1610); + } +#endif + for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { + if (i >= mcbsp_count) { + mcbsp[i].io_base = 0; + mcbsp[i].free = 0; + continue; + } + mcbsp[i].id = i + 1; + mcbsp[i].free = 1; + mcbsp[i].dma_tx_lch = -1; + mcbsp[i].dma_rx_lch = -1; + + mcbsp[i].io_base = mcbsp_info[i].virt_base; + mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; + mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; + mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; + mcbsp[i].dma_tx_sync = mcbsp_info[i].dma_tx_sync; + spin_lock_init(&mcbsp[i].lock); + } + + return 0; +} + + +arch_initcall(omap_mcbsp_init); + +EXPORT_SYMBOL(omap_mcbsp_config); +EXPORT_SYMBOL(omap_mcbsp_request); +EXPORT_SYMBOL(omap_mcbsp_free); +EXPORT_SYMBOL(omap_mcbsp_start); +EXPORT_SYMBOL(omap_mcbsp_stop); +EXPORT_SYMBOL(omap_mcbsp_xmit_word); +EXPORT_SYMBOL(omap_mcbsp_recv_word); +EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); +EXPORT_SYMBOL(omap_mcbsp_recv_buffer); +EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/mux.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,160 @@ +/* + * linux/arch/arm/plat-omap/mux.c + * + * Utility to set the Omap MUX and PULL_DWN registers from a table in mux.h + * + * Copyright (C) 2003 Nokia Corporation + * + * Written by Tony Lindgren + * + * 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 + * + */ +#include +#include +#include +#include +#include +#include + +#define __MUX_C__ +#include + +#ifdef CONFIG_OMAP_MUX + +/* + * Sets the Omap MUX and PULL_DWN registers based on the table + */ +int __init_or_module +omap_cfg_reg(const reg_cfg_t reg_cfg) +{ + static DEFINE_SPINLOCK(mux_spin_lock); + + unsigned long flags; + reg_cfg_set *cfg; + unsigned int reg_orig = 0, reg = 0, pu_pd_orig = 0, pu_pd = 0, + pull_orig = 0, pull = 0; + unsigned int mask, warn = 0; + + if (reg_cfg > ARRAY_SIZE(reg_cfg_table)) { + printk(KERN_ERR "MUX: reg_cfg %d\n", reg_cfg); + return -EINVAL; + } + + cfg = (reg_cfg_set *)®_cfg_table[reg_cfg]; + + /* Check the mux register in question */ + if (cfg->mux_reg) { + unsigned tmp1, tmp2; + + spin_lock_irqsave(&mux_spin_lock, flags); + reg_orig = omap_readl(cfg->mux_reg); + + /* The mux registers always seem to be 3 bits long */ + mask = (0x7 << cfg->mask_offset); + tmp1 = reg_orig & mask; + reg = reg_orig & ~mask; + + tmp2 = (cfg->mask << cfg->mask_offset); + reg |= tmp2; + + if (tmp1 != tmp2) + warn = 1; + + omap_writel(reg, cfg->mux_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + + /* Check for pull up or pull down selection on 1610 */ + if (!cpu_is_omap1510()) { + if (cfg->pu_pd_reg && cfg->pull_val) { + spin_lock_irqsave(&mux_spin_lock, flags); + pu_pd_orig = omap_readl(cfg->pu_pd_reg); + mask = 1 << cfg->pull_bit; + + if (cfg->pu_pd_val) { + if (!(pu_pd_orig & mask)) + warn = 1; + /* Use pull up */ + pu_pd = pu_pd_orig | mask; + } else { + if (pu_pd_orig & mask) + warn = 1; + /* Use pull down */ + pu_pd = pu_pd_orig & ~mask; + } + omap_writel(pu_pd, cfg->pu_pd_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + } + + /* Check for an associated pull down register */ + if (cfg->pull_reg) { + spin_lock_irqsave(&mux_spin_lock, flags); + pull_orig = omap_readl(cfg->pull_reg); + mask = 1 << cfg->pull_bit; + + if (cfg->pull_val) { + if (pull_orig & mask) + warn = 1; + /* Low bit = pull enabled */ + pull = pull_orig & ~mask; + } else { + if (!(pull_orig & mask)) + warn = 1; + /* High bit = pull disabled */ + pull = pull_orig | mask; + } + + omap_writel(pull, cfg->pull_reg); + spin_unlock_irqrestore(&mux_spin_lock, flags); + } + + if (warn) { +#ifdef CONFIG_OMAP_MUX_WARNINGS + printk(KERN_WARNING "MUX: initialized %s\n", cfg->name); +#endif + } + +#ifdef CONFIG_OMAP_MUX_DEBUG + if (cfg->debug || warn) { + printk("MUX: Setting register %s\n", cfg->name); + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg); + + if (!cpu_is_omap1510()) { + if (cfg->pu_pd_reg && cfg->pull_val) { + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->pu_pd_name, cfg->pu_pd_reg, + pu_pd_orig, pu_pd); + } + } + + if (cfg->pull_reg) + printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n", + cfg->pull_name, cfg->pull_reg, pull_orig, pull); + } +#endif + +#ifdef CONFIG_OMAP_MUX_ERRORS + return warn ? -ETXTBSY : 0; +#else + return 0; +#endif +} + +EXPORT_SYMBOL(omap_cfg_reg); + +#endif /* CONFIG_OMAP_MUX */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/ocpi.c 2005-07-12 00:26:40.000000000 -0700 @@ -0,0 +1,113 @@ +/* + * linux/arch/arm/plat-omap/ocpi.c + * + * Minimal OCP bus support for omap16xx + * + * Copyright (C) 2003 - 2005 Nokia Corporation + * Written by Tony Lindgren + * + * Modified for clock framework by Paul Mundt . + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define OCPI_BASE 0xfffec320 +#define OCPI_FAULT (OCPI_BASE + 0x00) +#define OCPI_CMD_FAULT (OCPI_BASE + 0x04) +#define OCPI_SINT0 (OCPI_BASE + 0x08) +#define OCPI_TABORT (OCPI_BASE + 0x0c) +#define OCPI_SINT1 (OCPI_BASE + 0x10) +#define OCPI_PROT (OCPI_BASE + 0x14) +#define OCPI_SEC (OCPI_BASE + 0x18) + +/* USB OHCI OCPI access error registers */ +#define HOSTUEADDR 0xfffba0e0 +#define HOSTUESTATUS 0xfffba0e4 + +static struct clk *ocpi_ck; + +/* + * Enables device access to OMAP buses via the OCPI bridge + * FIXME: Add locking + */ +int ocpi_enable(void) +{ + unsigned int val; + + if (!cpu_is_omap16xx()) + return -ENODEV; + + /* Make sure there's clock for OCPI */ + clk_enable(ocpi_ck); + + /* Enable access for OHCI in OCPI */ + val = omap_readl(OCPI_PROT); + val &= ~0xff; + //val &= (1 << 0); /* Allow access only to EMIFS */ + omap_writel(val, OCPI_PROT); + + val = omap_readl(OCPI_SEC); + val &= ~0xff; + omap_writel(val, OCPI_SEC); + + return 0; +} +EXPORT_SYMBOL(ocpi_enable); + +static int __init omap_ocpi_init(void) +{ + if (!cpu_is_omap16xx()) + return -ENODEV; + + ocpi_ck = clk_get(NULL, "l3_ocpi_ck"); + if (IS_ERR(ocpi_ck)) + return PTR_ERR(ocpi_ck); + + clk_use(ocpi_ck); + ocpi_enable(); + printk("OMAP OCPI interconnect driver loaded\n"); + + return 0; +} + +static void __exit omap_ocpi_exit(void) +{ + /* REVISIT: Disable OCPI */ + + if (!cpu_is_omap16xx()) + return; + + clk_unuse(ocpi_ck); + clk_put(ocpi_ck); +} + +MODULE_AUTHOR("Tony Lindgren "); +MODULE_DESCRIPTION("OMAP OCPI bus controller module"); +MODULE_LICENSE("GPL"); +module_init(omap_ocpi_init); +module_exit(omap_ocpi_exit); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/pm.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,632 @@ +/* + * linux/arch/arm/plat-omap/pm.c + * + * OMAP Power Management Routines + * + * Original code for the SA11x0: + * Copyright (c) 2001 Cliff Brake + * + * Modified for the PXA250 by Nicolas Pitre: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Modified for the OMAP1510 by David Singleton: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Cleanup 2004 for OMAP1510/1610 by Dirk Behme + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "clock.h" + +static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE]; +static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE]; +static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE]; +static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE]; + +/* + * Let's power down on idle, but only if we are really + * idle, because once we start down the path of + * going idle we continue to do idle even if we get + * a clock tick interrupt . . + */ +void omap_pm_idle(void) +{ + int (*func_ptr)(void) = 0; + unsigned int mask32 = 0; + + /* + * If the DSP is being used let's just idle the CPU, the overhead + * to wake up from Big Sleep is big, milliseconds versus micro + * seconds for wait for interrupt. + */ + + local_irq_disable(); + local_fiq_disable(); + if (need_resched()) { + local_fiq_enable(); + local_irq_enable(); + return; + } + mask32 = omap_readl(ARM_SYSST); + + /* + * Since an interrupt may set up a timer, we don't want to + * reprogram the hardware timer with interrupts enabled. + * Re-enable interrupts only after returning from idle. + */ + timer_dyn_reprogram(); + + if ((mask32 & DSP_IDLE) == 0) { + __asm__ volatile ("mcr p15, 0, r0, c7, c0, 4"); + } else { + + if (cpu_is_omap1510()) { + func_ptr = (void *)(OMAP1510_SRAM_IDLE_SUSPEND); + } else if (cpu_is_omap1610() || cpu_is_omap1710()) { + func_ptr = (void *)(OMAP1610_SRAM_IDLE_SUSPEND); + } else if (cpu_is_omap5912()) { + func_ptr = (void *)(OMAP5912_SRAM_IDLE_SUSPEND); + } + + func_ptr(); + } + local_fiq_enable(); + local_irq_enable(); +} + +/* + * Configuration of the wakeup event is board specific. For the + * moment we put it into this helper function. Later it may move + * to board specific files. + */ +static void omap_pm_wakeup_setup(void) +{ + /* + * Enable ARM XOR clock and release peripheral from reset by + * writing 1 to PER_EN bit in ARM_RSTCT2, this is required + * for UART configuration to use UART2 to wake up. + */ + + omap_writel(omap_readl(ARM_IDLECT2) | ENABLE_XORCLK, ARM_IDLECT2); + omap_writel(omap_readl(ARM_RSTCT2) | PER_EN, ARM_RSTCT2); + omap_writew(MODEM_32K_EN, ULPD_CLOCK_CTRL); + + /* + * Turn off all interrupts except L1-2nd level cascade, + * and the L2 wakeup interrupts: keypad and UART2. + */ + + omap_writel(~IRQ_LEVEL2, OMAP_IH1_MIR); + + if (cpu_is_omap1510()) { + omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_MIR); + } + + if (cpu_is_omap16xx()) { + omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_0_MIR); + + omap_writel(~0x0, OMAP_IH2_1_MIR); + omap_writel(~0x0, OMAP_IH2_2_MIR); + omap_writel(~0x0, OMAP_IH2_3_MIR); + } + + /* New IRQ agreement */ + omap_writel(1, OMAP_IH1_CONTROL); + + /* external PULL to down, bit 22 = 0 */ + omap_writel(omap_readl(PULL_DWN_CTRL_2) & ~(1<<22), PULL_DWN_CTRL_2); +} + +void omap_pm_suspend(void) +{ + unsigned int mask32 = 0; + unsigned long arg0 = 0, arg1 = 0; + int (*func_ptr)(unsigned short, unsigned short) = 0; + unsigned short save_dsp_idlect2; + + printk("PM: OMAP%x is entering deep sleep now ...\n", system_rev); + + if (machine_is_omap_osk()) { + /* Stop LED1 (D9) blink */ + tps65010_set_led(LED1, OFF); + } + + /* + * Step 1: turn off interrupts + */ + + local_irq_disable(); + local_fiq_disable(); + + /* + * Step 2: save registers + * + * The omap is a strange/beautiful device. The caches, memory + * and register state are preserved across power saves. + * We have to save and restore very little register state to + * idle the omap. + * + * Save interrupt, MPUI, ARM and UPLD control registers. + */ + + if (cpu_is_omap1510()) { + MPUI1510_SAVE(OMAP_IH1_MIR); + MPUI1510_SAVE(OMAP_IH2_MIR); + MPUI1510_SAVE(MPUI_CTRL); + MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_SAVE(MPUI_DSP_API_CONFIG); + MPUI1510_SAVE(EMIFS_CONFIG); + MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); + } else if (cpu_is_omap16xx()) { + MPUI1610_SAVE(OMAP_IH1_MIR); + MPUI1610_SAVE(OMAP_IH2_0_MIR); + MPUI1610_SAVE(OMAP_IH2_1_MIR); + MPUI1610_SAVE(OMAP_IH2_2_MIR); + MPUI1610_SAVE(OMAP_IH2_3_MIR); + MPUI1610_SAVE(MPUI_CTRL); + MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_SAVE(MPUI_DSP_API_CONFIG); + MPUI1610_SAVE(EMIFS_CONFIG); + MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); + } + + ARM_SAVE(ARM_CKCTL); + ARM_SAVE(ARM_IDLECT1); + ARM_SAVE(ARM_IDLECT2); + ARM_SAVE(ARM_EWUPCT); + ARM_SAVE(ARM_RSTCT1); + ARM_SAVE(ARM_RSTCT2); + ARM_SAVE(ARM_SYSST); + ULPD_SAVE(ULPD_CLOCK_CTRL); + ULPD_SAVE(ULPD_STATUS_REQ); + + /* + * Step 3: LOW_PWR signal enabling + * + * Allow the LOW_PWR signal to be visible on MPUIO5 ball. + */ + if (cpu_is_omap1510()) { + /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ + omap_writew(omap_readw(ULPD_POWER_CTRL) | + OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); + } else if (cpu_is_omap16xx()) { + /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ + omap_writew(omap_readw(ULPD_POWER_CTRL) | + OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); + } + + /* configure LOW_PWR pin */ + omap_cfg_reg(T20_1610_LOW_PWR); + + /* + * Step 4: OMAP DSP Shutdown + */ + + /* Set DSP_RST = 1 and DSP_EN = 0, put DSP block into reset */ + omap_writel((omap_readl(ARM_RSTCT1) | DSP_RST) & ~DSP_ENABLE, + ARM_RSTCT1); + + /* Set DSP boot mode to DSP-IDLE, DSP_BOOT_MODE = 0x2 */ + omap_writel(DSP_IDLE_MODE, MPUI_DSP_BOOT_CONFIG); + + /* Set EN_DSPCK = 0, stop DSP block clock */ + omap_writel(omap_readl(ARM_CKCTL) & ~DSP_CLOCK_ENABLE, ARM_CKCTL); + + /* Stop any DSP domain clocks */ + omap_writel(omap_readl(ARM_IDLECT2) | (1< 0 (WDT clock) */ + mask32 |= (1< 1 (XORPCK clock) */ + mask32 &= ~(1< 0 (MPUPER_CK clock) */ + mask32 &= ~(1< 0 (LCDC clock) */ + mask32 &= ~(1< 0 (local bus clock) */ + mask32 |= (1< 1 (MPUI clock) */ + mask32 &= ~(1< 0 (MPU timer clock) */ + mask32 &= ~(1< 0 (DMAC clock) */ + mask32 &= ~(1< 0 (GPIO clock) */ + omap_writel(mask32, ARM_IDLECT2); + + /* disable ARM watchdog */ + omap_writel(0x00F5, OMAP_WDT_TIMER_MODE); + omap_writel(0x00A0, OMAP_WDT_TIMER_MODE); + + /* + * Step 6b: ARM and Traffic controller shutdown + * + * Step 6 continues here. Prepare jump to power management + * assembly code in internal SRAM. + * + * Since the omap_cpu_suspend routine has been copied to + * SRAM, we'll do an indirect procedure call to it and pass the + * contents of arm_idlect1 and arm_idlect2 so it can restore + * them when it wakes up and it will return. + */ + + arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1]; + arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2]; + + if (cpu_is_omap1510()) { + func_ptr = (void *)(OMAP1510_SRAM_API_SUSPEND); + } else if (cpu_is_omap1610() || cpu_is_omap1710()) { + func_ptr = (void *)(OMAP1610_SRAM_API_SUSPEND); + } else if (cpu_is_omap5912()) { + func_ptr = (void *)(OMAP5912_SRAM_API_SUSPEND); + } + + /* + * Step 6c: ARM and Traffic controller shutdown + * + * Jump to assembly code. The processor will stay there + * until wake up. + */ + + func_ptr(arg0, arg1); + + /* + * If we are here, processor is woken up! + */ + + if (cpu_is_omap1510()) { + /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ + omap_writew(omap_readw(ULPD_POWER_CTRL) & + ~OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); + } else if (cpu_is_omap16xx()) { + /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ + omap_writew(omap_readw(ULPD_POWER_CTRL) & + ~OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); + } + + + /* Restore DSP clocks */ + omap_writel(omap_readl(ARM_IDLECT2) | (1<= 1) { + *eof = 1; + return 0; + } + g_read_completed++; + + *my_first_byte = page_buffer; + return my_buffer_offset; +} + +static void omap_pm_init_proc(void) +{ + struct proc_dir_entry *entry; + + entry = create_proc_read_entry("driver/omap_pm", + S_IWUSR | S_IRUGO, NULL, + omap_pm_read_proc, 0); +} + +#endif /* DEBUG && CONFIG_PROC_FS */ + +/* + * omap_pm_prepare - Do preliminary suspend work. + * @state: suspend state we're entering. + * + */ +//#include + +static int omap_pm_prepare(suspend_state_t state) +{ + int error = 0; + + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return error; +} + + +/* + * omap_pm_enter - Actually enter a sleep state. + * @state: State we're entering. + * + */ + +static int omap_pm_enter(suspend_state_t state) +{ + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + omap_pm_suspend(); + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return 0; +} + + +/** + * omap_pm_finish - Finish up suspend sequence. + * @state: State we're coming out of. + * + * This is called after we wake back up (or if entering the sleep state + * failed). + */ + +static int omap_pm_finish(suspend_state_t state) +{ + return 0; +} + + +struct pm_ops omap_pm_ops ={ + .pm_disk_mode = 0, + .prepare = omap_pm_prepare, + .enter = omap_pm_enter, + .finish = omap_pm_finish, +}; + +static int __init omap_pm_init(void) +{ + printk("Power Management for TI OMAP.\n"); + pm_idle = omap_pm_idle; + /* + * We copy the assembler sleep/wakeup routines to SRAM. + * These routines need to be in SRAM as that's the only + * memory the MPU can see when it wakes up. + */ + +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + memcpy((void *)OMAP1510_SRAM_IDLE_SUSPEND, + omap1510_idle_loop_suspend, + omap1510_idle_loop_suspend_sz); + memcpy((void *)OMAP1510_SRAM_API_SUSPEND, omap1510_cpu_suspend, + omap1510_cpu_suspend_sz); + } else +#endif + if (cpu_is_omap1610() || cpu_is_omap1710()) { + memcpy((void *)OMAP1610_SRAM_IDLE_SUSPEND, + omap1610_idle_loop_suspend, + omap1610_idle_loop_suspend_sz); + memcpy((void *)OMAP1610_SRAM_API_SUSPEND, omap1610_cpu_suspend, + omap1610_cpu_suspend_sz); + } else if (cpu_is_omap5912()) { + memcpy((void *)OMAP5912_SRAM_IDLE_SUSPEND, + omap1610_idle_loop_suspend, + omap1610_idle_loop_suspend_sz); + memcpy((void *)OMAP5912_SRAM_API_SUSPEND, omap1610_cpu_suspend, + omap1610_cpu_suspend_sz); + } + + pm_set_ops(&omap_pm_ops); + +#if defined(DEBUG) && defined(CONFIG_PROC_FS) + omap_pm_init_proc(); +#endif + + return 0; +} +__initcall(omap_pm_init); + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/sleep.S 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,314 @@ +/* + * linux/arch/arm/plat-omap/sleep.S + * + * Low-level OMAP1510/1610 sleep/wakeUp support + * + * Initial SA1110 code: + * Copyright (c) 2001 Cliff Brake + * + * Adapted for PXA by Nicolas Pitre: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Support for OMAP1510/1610 by Dirk Behme + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + + .text + +/* + * Forces OMAP into idle state + * + * omapXXXX_idle_loop_suspend() + * + * Note: This code get's copied to internal SRAM at boot. When the OMAP + * wakes up it continues execution at the point it went to sleep. + * + * Note: Because of slightly different configuration values we have + * processor specific functions here. + */ + +#ifdef CONFIG_ARCH_OMAP1510 +ENTRY(omap1510_idle_loop_suspend) + + stmfd sp!, {r0 - r12, lr} @ save registers on stack + + @ load base address of ARM_IDLECT1 and ARM_IDLECT2 + mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 + + @ turn off clock domains + @ get ARM_IDLECT2 into r2 + ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff + orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 + strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + + @ request ARM idle + @ get ARM_IDLECT1 into r1 + ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + orr r3, r1, #OMAP1510_IDLE_LOOP_REQUEST & 0xffff + strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + mov r5, #IDLE_WAIT_CYCLES & 0xff + orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 +l_1510: subs r5, r5, #1 + bne l_1510 +/* + * Let's wait for the next clock tick to wake us up. + */ + mov r0, #0 + mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt +/* + * omap1510_idle_loop_suspend()'s resume point. + * + * It will just start executing here, so we'll restore stuff from the + * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. + */ + + @ restore ARM_IDLECT1 and ARM_IDLECT2 and return + @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 + strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + ldmfd sp!, {r0 - r12, pc} @ restore regs and return + +ENTRY(omap1510_idle_loop_suspend_sz) + .word . - omap1510_idle_loop_suspend +#endif /* CONFIG_ARCH_OMAP1510 */ + +#if defined(CONFIG_ARCH_OMAP16XX) +ENTRY(omap1610_idle_loop_suspend) + + stmfd sp!, {r0 - r12, lr} @ save registers on stack + + @ load base address of ARM_IDLECT1 and ARM_IDLECT2 + mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 + + @ turn off clock domains + @ get ARM_IDLECT2 into r2 + ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff + orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00 + strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + + @ request ARM idle + @ get ARM_IDLECT1 into r1 + ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + orr r3, r1, #OMAP1610_IDLE_LOOP_REQUEST & 0xffff + strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + mov r5, #IDLE_WAIT_CYCLES & 0xff + orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 +l_1610: subs r5, r5, #1 + bne l_1610 +/* + * Let's wait for the next clock tick to wake us up. + */ + mov r0, #0 + mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt +/* + * omap1610_idle_loop_suspend()'s resume point. + * + * It will just start executing here, so we'll restore stuff from the + * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. + */ + + @ restore ARM_IDLECT1 and ARM_IDLECT2 and return + @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2 + strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + ldmfd sp!, {r0 - r12, pc} @ restore regs and return + +ENTRY(omap1610_idle_loop_suspend_sz) + .word . - omap1610_idle_loop_suspend +#endif /* CONFIG_ARCH_OMAP16XX */ + +/* + * Forces OMAP into deep sleep state + * + * omapXXXX_cpu_suspend() + * + * The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed + * as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1 + * in register r1. + * + * Note: This code get's copied to internal SRAM at boot. When the OMAP + * wakes up it continues execution at the point it went to sleep. + * + * Note: Because of errata work arounds we have processor specific functions + * here. They are mostly the same, but slightly different. + * + */ + +#ifdef CONFIG_ARCH_OMAP1510 +ENTRY(omap1510_cpu_suspend) + + @ save registers on stack + stmfd sp!, {r0 - r12, lr} + + @ load base address of Traffic Controller + mov r4, #TCMIF_ASM_BASE & 0xff000000 + orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 + orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 + + @ work around errata of OMAP1510 PDE bit for TC shut down + @ clear PDE bit + ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + bic r5, r5, #PDE_BIT & 0xff + str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ set PWD_EN bit + and r5, r5, #PWD_EN_BIT & 0xff + str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ prepare to put SDRAM into self-refresh manually + ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 + orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff + str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + + @ prepare to put EMIFS to Sleep + ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff + str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ load base address of ARM_IDLECT1 and ARM_IDLECT2 + mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 + + @ turn off clock domains + mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff + orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 + strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + + @ request ARM idle + mov r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff + orr r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00 + strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + mov r5, #IDLE_WAIT_CYCLES & 0xff + orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 +l_1510_2: + subs r5, r5, #1 + bne l_1510_2 +/* + * Let's wait for the next wake up event to wake us up. r0 can't be + * used here because r0 holds ARM_IDLECT1 + */ + mov r2, #0 + mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt +/* + * omap1510_cpu_suspend()'s resume point. + * + * It will just start executing here, so we'll restore stuff from the + * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. + */ + strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + @ restore regs and return + ldmfd sp!, {r0 - r12, pc} + +ENTRY(omap1510_cpu_suspend_sz) + .word . - omap1510_cpu_suspend +#endif /* CONFIG_ARCH_OMAP1510 */ + +#if defined(CONFIG_ARCH_OMAP16XX) +ENTRY(omap1610_cpu_suspend) + + @ save registers on stack + stmfd sp!, {r0 - r12, lr} + + @ load base address of Traffic Controller + mov r4, #TCMIF_ASM_BASE & 0xff000000 + orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 + orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 + + @ prepare to put SDRAM into self-refresh manually + ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 + orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff + str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] + + @ prepare to put EMIFS to Sleep + ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff + str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] + + @ load base address of ARM_IDLECT1 and ARM_IDLECT2 + mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 + orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 + + @ turn off clock domains + mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff + orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00 + strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + + @ work around errata of OMAP1610/5912. Enable (!) peripheral + @ clock to let the chip go into deep sleep + ldrh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + orr r5,r5, #EN_PERCK_BIT & 0xff + strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + + @ request ARM idle + mov r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff + orr r3, r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff00 + strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + mov r5, #IDLE_WAIT_CYCLES & 0xff + orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 +l_1610_2: + subs r5, r5, #1 + bne l_1610_2 +/* + * Let's wait for the next wake up event to wake us up. r0 can't be + * used here because r0 holds ARM_IDLECT1 + */ + mov r2, #0 + mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt +/* + * omap1610_cpu_suspend()'s resume point. + * + * It will just start executing here, so we'll restore stuff from the + * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. + */ + strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] + strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] + + @ restore regs and return + ldmfd sp!, {r0 - r12, pc} + +ENTRY(omap1610_cpu_suspend_sz) + .word . - omap1610_cpu_suspend +#endif /* CONFIG_ARCH_OMAP16XX */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/arm/plat-omap/usb.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,593 @@ +/* + * arch/arm/plat-omap/usb.c -- platform level USB initialization + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * 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 + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* These routines should handle the standard chip-specific modes + * for usb0/1/2 ports, covering basic mux and transceiver setup. + * + * Some board-*.c files will need to set up additional mux options, + * like for suspend handling, vbus sensing, GPIOs, and the D+ pullup. + */ + +/* TESTED ON: + * - 1611B H2 (with usb1 mini-AB) using standard Mini-B or OTG cables + * - 5912 OSK OHCI (with usb0 standard-A), standard A-to-B cables + * - 5912 OSK UDC, with *nonstandard* A-to-A cable + * - 1510 Innovator UDC with bundled usb0 cable + * - 1510 Innovator OHCI with bundled usb1/usb2 cable + * - 1510 Innovator OHCI with custom usb0 cable, feeding 5V VBUS + * - 1710 custom development board using alternate pin group + * - 1710 H3 (with usb1 mini-AB) using standard Mini-B or OTG cables + */ + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_OMAP_OTG + +static struct otg_transceiver *xceiv; + +/** + * otg_get_transceiver - find the (single) OTG transceiver driver + * + * Returns the transceiver driver, after getting a refcount to it; or + * null if there is no such transceiver. The caller is responsible for + * releasing that count. + */ +struct otg_transceiver *otg_get_transceiver(void) +{ + if (xceiv) + get_device(xceiv->dev); + return xceiv; +} +EXPORT_SYMBOL(otg_get_transceiver); + +int otg_set_transceiver(struct otg_transceiver *x) +{ + if (xceiv && x) + return -EBUSY; + xceiv = x; + return 0; +} +EXPORT_SYMBOL(otg_set_transceiver); + +#endif + +/*-------------------------------------------------------------------------*/ + +static u32 __init omap_usb0_init(unsigned nwires, unsigned is_device) +{ + u32 syscon1 = 0; + + if (nwires == 0) { + if (!cpu_is_omap15xx()) { + /* pulldown D+/D- */ + USB_TRANSCEIVER_CTRL_REG &= ~(3 << 1); + } + return 0; + } + + if (is_device) + omap_cfg_reg(W4_USB_PUEN); + + /* internal transceiver */ + if (nwires == 2) { + // omap_cfg_reg(P9_USB_DP); + // omap_cfg_reg(R8_USB_DM); + + if (cpu_is_omap15xx()) { + /* This works on 1510-Innovator */ + return 0; + } + + /* NOTES: + * - peripheral should configure VBUS detection! + * - only peripherals may use the internal D+/D- pulldowns + * - OTG support on this port not yet written + */ + + USB_TRANSCEIVER_CTRL_REG &= ~(7 << 4); + if (!is_device) + USB_TRANSCEIVER_CTRL_REG |= (3 << 1); + + return 3 << 16; + } + + /* alternate pin config, external transceiver */ + if (cpu_is_omap15xx()) { + printk(KERN_ERR "no usb0 alt pin config on 15xx\n"); + return 0; + } + + omap_cfg_reg(V6_USB0_TXD); + omap_cfg_reg(W9_USB0_TXEN); + omap_cfg_reg(W5_USB0_SE0); + + /* NOTE: SPEED and SUSP aren't configured here */ + + if (nwires != 3) + omap_cfg_reg(Y5_USB0_RCV); + if (nwires != 6) + USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; + + switch (nwires) { + case 3: + syscon1 = 2; + break; + case 4: + syscon1 = 1; + break; + case 6: + syscon1 = 3; + omap_cfg_reg(AA9_USB0_VP); + omap_cfg_reg(R9_USB0_VM); + USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; + break; + default: + printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", + 0, nwires); + } + return syscon1 << 16; +} + +static u32 __init omap_usb1_init(unsigned nwires) +{ + u32 syscon1 = 0; + + if (nwires != 6 && !cpu_is_omap15xx()) + USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB1_UNI_R; + if (nwires == 0) + return 0; + + /* external transceiver */ + omap_cfg_reg(USB1_TXD); + omap_cfg_reg(USB1_TXEN); + if (cpu_is_omap15xx()) { + omap_cfg_reg(USB1_SEO); + omap_cfg_reg(USB1_SPEED); + // SUSP + } else if (cpu_is_omap1610() || cpu_is_omap5912()) { + omap_cfg_reg(W13_1610_USB1_SE0); + omap_cfg_reg(R13_1610_USB1_SPEED); + // SUSP + } else if (cpu_is_omap1710()) { + omap_cfg_reg(R13_1710_USB1_SE0); + // SUSP + } else { + pr_debug("usb unrecognized\n"); + } + if (nwires != 3) + omap_cfg_reg(USB1_RCV); + + switch (nwires) { + case 3: + syscon1 = 2; + break; + case 4: + syscon1 = 1; + break; + case 6: + syscon1 = 3; + omap_cfg_reg(USB1_VP); + omap_cfg_reg(USB1_VM); + if (!cpu_is_omap15xx()) + USB_TRANSCEIVER_CTRL_REG |= CONF_USB1_UNI_R; + break; + default: + printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", + 1, nwires); + } + return syscon1 << 20; +} + +static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup) +{ + u32 syscon1 = 0; + + /* NOTE erratum: must leave USB2_UNI_R set if usb0 in use */ + if (alt_pingroup || nwires == 0) + return 0; + if (nwires != 6 && !cpu_is_omap15xx()) + USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; + + /* external transceiver */ + if (cpu_is_omap15xx()) { + omap_cfg_reg(USB2_TXD); + omap_cfg_reg(USB2_TXEN); + omap_cfg_reg(USB2_SEO); + if (nwires != 3) + omap_cfg_reg(USB2_RCV); + /* there is no USB2_SPEED */ + } else if (cpu_is_omap16xx()) { + omap_cfg_reg(V6_USB2_TXD); + omap_cfg_reg(W9_USB2_TXEN); + omap_cfg_reg(W5_USB2_SE0); + if (nwires != 3) + omap_cfg_reg(Y5_USB2_RCV); + // FIXME omap_cfg_reg(USB2_SPEED); + } else { + pr_debug("usb unrecognized\n"); + } + // omap_cfg_reg(USB2_SUSP); + + switch (nwires) { + case 3: + syscon1 = 2; + break; + case 4: + syscon1 = 1; + break; + case 6: + syscon1 = 3; + if (cpu_is_omap15xx()) { + omap_cfg_reg(USB2_VP); + omap_cfg_reg(USB2_VM); + } else { + omap_cfg_reg(AA9_USB2_VP); + omap_cfg_reg(R9_USB2_VM); + USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; + } + break; + default: + printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", + 2, nwires); + } + return syscon1 << 24; +} + +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_USB_GADGET_OMAP) || \ + defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) || \ + (defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG)) +static void usb_release(struct device *dev) +{ + /* normally not freed */ +} +#endif + +#ifdef CONFIG_USB_GADGET_OMAP + +static struct resource udc_resources[] = { + /* order is significant! */ + { /* registers */ + .start = UDC_BASE, + .end = UDC_BASE + 0xff, + .flags = IORESOURCE_MEM, + }, { /* general IRQ */ + .start = IH2_BASE + 20, + .flags = IORESOURCE_IRQ, + }, { /* PIO IRQ */ + .start = IH2_BASE + 30, + .flags = IORESOURCE_IRQ, + }, { /* SOF IRQ */ + .start = IH2_BASE + 29, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 udc_dmamask = ~(u32)0; + +static struct platform_device udc_device = { + .name = "omap_udc", + .id = -1, + .dev = { + .release = usb_release, + .dma_mask = &udc_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(udc_resources), + .resource = udc_resources, +}; + +#endif + +#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + +/* The dmamask must be set for OHCI to work */ +static u64 ohci_dmamask = ~(u32)0; + +static struct resource ohci_resources[] = { + { + .start = OMAP_OHCI_BASE, + .end = OMAP_OHCI_BASE + 4096 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HHC_1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ohci_device = { + .name = "ohci", + .id = -1, + .dev = { + .release = usb_release, + .dma_mask = &ohci_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(ohci_resources), + .resource = ohci_resources, +}; + +#endif + +#if defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG) + +static struct resource otg_resources[] = { + /* order is significant! */ + { + .start = OTG_BASE, + .end = OTG_BASE + 0xff, + .flags = IORESOURCE_MEM, + }, { + .start = IH2_BASE + 8, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device otg_device = { + .name = "omap_otg", + .id = -1, + .dev = { + .release = usb_release, + }, + .num_resources = ARRAY_SIZE(otg_resources), + .resource = otg_resources, +}; + +#endif + +/*-------------------------------------------------------------------------*/ + +#define ULPD_CLOCK_CTRL_REG __REG16(ULPD_CLOCK_CTRL) +#define ULPD_SOFT_REQ_REG __REG16(ULPD_SOFT_REQ) + + +// FIXME correct answer depends on hmc_mode, +// as does any nonzero value for config->otg port number +#ifdef CONFIG_USB_GADGET_OMAP +#define is_usb0_device(config) 1 +#else +#define is_usb0_device(config) 0 +#endif + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_OMAP_OTG + +void __init +omap_otg_init(struct omap_usb_config *config) +{ + u32 syscon = OTG_SYSCON_1_REG & 0xffff; + int status; + int alt_pingroup = 0; + + /* NOTE: no bus or clock setup (yet?) */ + + syscon = OTG_SYSCON_1_REG & 0xffff; + if (!(syscon & OTG_RESET_DONE)) + pr_debug("USB resets not complete?\n"); + + // OTG_IRQ_EN_REG = 0; + + /* pin muxing and transceiver pinouts */ + if (config->pins[0] > 2) /* alt pingroup 2 */ + alt_pingroup = 1; + syscon |= omap_usb0_init(config->pins[0], is_usb0_device(config)); + syscon |= omap_usb1_init(config->pins[1]); + syscon |= omap_usb2_init(config->pins[2], alt_pingroup); + pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); + OTG_SYSCON_1_REG = syscon; + + syscon = config->hmc_mode; + syscon |= USBX_SYNCHRO | (4 << 16) /* B_ASE0_BRST */; +#ifdef CONFIG_USB_OTG + if (config->otg) + syscon |= OTG_EN; +#endif + pr_debug("USB_TRANSCEIVER_CTRL_REG = %03x\n", USB_TRANSCEIVER_CTRL_REG); + pr_debug("OTG_SYSCON_2_REG = %08x\n", syscon); + OTG_SYSCON_2_REG = syscon; + + printk("USB: hmc %d", config->hmc_mode); + if (alt_pingroup) + printk(", usb2 alt %d wires", config->pins[2]); + else if (config->pins[0]) + printk(", usb0 %d wires%s", config->pins[0], + is_usb0_device(config) ? " (dev)" : ""); + if (config->pins[1]) + printk(", usb1 %d wires", config->pins[1]); + if (!alt_pingroup && config->pins[2]) + printk(", usb2 %d wires", config->pins[2]); + if (config->otg) + printk(", Mini-AB on usb%d", config->otg - 1); + printk("\n"); + + /* leave USB clocks/controllers off until needed */ + ULPD_SOFT_REQ_REG &= ~SOFT_USB_CLK_REQ; + ULPD_CLOCK_CTRL_REG &= ~USB_MCLK_EN; + ULPD_CLOCK_CTRL_REG |= DIS_USB_PVCI_CLK; + syscon = OTG_SYSCON_1_REG; + syscon |= HST_IDLE_EN|DEV_IDLE_EN|OTG_IDLE_EN; + +#ifdef CONFIG_USB_GADGET_OMAP + if (config->otg || config->register_dev) { + syscon &= ~DEV_IDLE_EN; + udc_device.dev.platform_data = config; + /* FIXME patch IRQ numbers for omap730 */ + status = platform_device_register(&udc_device); + if (status) + pr_debug("can't register UDC device, %d\n", status); + } +#endif + +#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + if (config->otg || config->register_host) { + syscon &= ~HST_IDLE_EN; + ohci_device.dev.platform_data = config; + if (cpu_is_omap730()) + ohci_resources[1].start = INT_730_USB_HHC_1; + status = platform_device_register(&ohci_device); + if (status) + pr_debug("can't register OHCI device, %d\n", status); + } +#endif + +#ifdef CONFIG_USB_OTG + if (config->otg) { + syscon &= ~OTG_IDLE_EN; + otg_device.dev.platform_data = config; + if (cpu_is_omap730()) + otg_resources[1].start = INT_730_USB_OTG; + status = platform_device_register(&otg_device); + if (status) + pr_debug("can't register OTG device, %d\n", status); + } +#endif + pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); + OTG_SYSCON_1_REG = syscon; + + status = 0; +} + +#else +static inline void omap_otg_init(struct omap_usb_config *config) {} +#endif + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_OMAP1510 + +#define ULPD_DPLL_CTRL_REG __REG16(ULPD_DPLL_CTRL) +#define DPLL_IOB (1 << 13) +#define DPLL_PLL_ENABLE (1 << 4) +#define DPLL_LOCK (1 << 0) + +#define ULPD_APLL_CTRL_REG __REG16(ULPD_APLL_CTRL) +#define APLL_NDPLL_SWITCH (1 << 0) + + +static void __init omap_1510_usb_init(struct omap_usb_config *config) +{ + int status; + unsigned int val; + + omap_usb0_init(config->pins[0], is_usb0_device(config)); + omap_usb1_init(config->pins[1]); + omap_usb2_init(config->pins[2], 0); + + val = omap_readl(MOD_CONF_CTRL_0) & ~(0x3f << 1); + val |= (config->hmc_mode << 1); + omap_writel(val, MOD_CONF_CTRL_0); + + printk("USB: hmc %d", config->hmc_mode); + if (config->pins[0]) + printk(", usb0 %d wires%s", config->pins[0], + is_usb0_device(config) ? " (dev)" : ""); + if (config->pins[1]) + printk(", usb1 %d wires", config->pins[1]); + if (config->pins[2]) + printk(", usb2 %d wires", config->pins[2]); + printk("\n"); + + /* use DPLL for 48 MHz function clock */ + pr_debug("APLL %04x DPLL %04x REQ %04x\n", ULPD_APLL_CTRL_REG, + ULPD_DPLL_CTRL_REG, ULPD_SOFT_REQ_REG); + ULPD_APLL_CTRL_REG &= ~APLL_NDPLL_SWITCH; + ULPD_DPLL_CTRL_REG |= DPLL_IOB | DPLL_PLL_ENABLE; + ULPD_SOFT_REQ_REG |= SOFT_UDC_REQ | SOFT_DPLL_REQ; + while (!(ULPD_DPLL_CTRL_REG & DPLL_LOCK)) + cpu_relax(); + +#ifdef CONFIG_USB_GADGET_OMAP + if (config->register_dev) { + udc_device.dev.platform_data = config; + status = platform_device_register(&udc_device); + if (status) + pr_debug("can't register UDC device, %d\n", status); + /* udc driver gates 48MHz by D+ pullup */ + } +#endif + +#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + if (config->register_host) { + ohci_device.dev.platform_data = config; + status = platform_device_register(&ohci_device); + if (status) + pr_debug("can't register OHCI device, %d\n", status); + /* hcd explicitly gates 48MHz */ + } +#endif +} + +#else +static inline void omap_1510_usb_init(struct omap_usb_config *config) {} +#endif + +/*-------------------------------------------------------------------------*/ + +static struct omap_usb_config platform_data; + +static int __init +omap_usb_init(void) +{ + const struct omap_usb_config *config; + + config = omap_get_config(OMAP_TAG_USB, struct omap_usb_config); + if (config == NULL) { + printk(KERN_ERR "USB: No board-specific " + "platform config found\n"); + return -ENODEV; + } + platform_data = *config; + + if (cpu_is_omap730() || cpu_is_omap16xx()) + omap_otg_init(&platform_data); + else if (cpu_is_omap15xx()) + omap_1510_usb_init(&platform_data); + else { + printk(KERN_ERR "USB: No init for your chip yet\n"); + return -ENODEV; + } + return 0; +} + +subsys_initcall(omap_usb_init); --- linux-2.6.13-rc2/arch/cris/arch-v10/boot/compressed/head.S 2005-03-01 23:38:09.000000000 -0800 +++ devel/arch/cris/arch-v10/boot/compressed/head.S 2005-07-12 00:26:59.000000000 -0700 @@ -13,7 +13,8 @@ #include #define RAM_INIT_MAGIC 0x56902387 - +#define COMMAND_LINE_MAGIC 0x87109563 + ;; Exported symbols .globl _input_data @@ -88,6 +89,12 @@ basse: move.d pc, r5 cmp.d r2, r1 bcs 1b nop + + ;; Save command line magic and address. + move.d _cmd_line_magic, $r12 + move.d $r10, [$r12] + move.d _cmd_line_addr, $r12 + move.d $r11, [$r12] ;; Do the decompression and save compressed size in _inptr @@ -98,7 +105,13 @@ basse: move.d pc, r5 move.d [_input_data], r9 ; flash address of compressed kernel add.d [_inptr], r9 ; size of compressed kernel - + + ;; Restore command line magic and address. + move.d _cmd_line_magic, $r10 + move.d [$r10], $r10 + move.d _cmd_line_addr, $r11 + move.d [$r11], $r11 + ;; Enter the decompressed kernel move.d RAM_INIT_MAGIC, r8 ; Tell kernel that DRAM is initialized jump 0x40004000 ; kernel is linked to this address @@ -107,5 +120,8 @@ basse: move.d pc, r5 _input_data: .dword 0 ; used by the decompressor - +_cmd_line_magic: + .dword 0 +_cmd_line_addr: + .dword 0 #include "../../lib/hw_settings.S" --- linux-2.6.13-rc2/arch/cris/arch-v10/boot/compressed/Makefile 2005-03-01 23:37:31.000000000 -0800 +++ devel/arch/cris/arch-v10/boot/compressed/Makefile 2005-07-12 00:26:58.000000000 -0700 @@ -1,40 +1,45 @@ # -# linux/arch/etrax100/boot/compressed/Makefile -# -# create a compressed vmlinux image from the original vmlinux files and romfs +# create a compressed vmlinuz image from the binary vmlinux.bin file # +target = $(target_compressed_dir) +src = $(src_compressed_dir) -CC = gcc-cris -melf -I $(TOPDIR)/include +CC = gcc-cris -melf $(LINUXINCLUDE) CFLAGS = -O2 LD = ld-cris OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss -OBJECTS = head.o misc.o +OBJECTS = $(target)/head.o $(target)/misc.o # files to compress -SYSTEM = $(TOPDIR)/vmlinux.bin +SYSTEM = $(objtree)/vmlinux.bin -all: vmlinuz +all: $(target_compressed_dir)/vmlinuz -decompress.bin: $(OBJECTS) - $(LD) -T decompress.ld -o decompress.o $(OBJECTS) - $(OBJCOPY) $(OBJCOPYFLAGS) decompress.o decompress.bin -# save it for mkprod in the topdir. - cp decompress.bin $(TOPDIR) +$(target)/decompress.bin: $(OBJECTS) + $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS) + $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin +# Create vmlinuz image in top-level build directory +$(target_compressed_dir)/vmlinuz: $(target) piggy.img $(target)/decompress.bin + @echo " COMPR vmlinux.bin --> vmlinuz" + @cat $(target)/decompress.bin piggy.img > $(target_compressed_dir)/vmlinuz + @rm -f piggy.img -vmlinuz: piggy.img decompress.bin - cat decompress.bin piggy.img > vmlinuz - rm -f piggy.img +$(target)/head.o: $(src)/head.S + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $@ -head.o: head.S - $(CC) -D__ASSEMBLY__ -traditional -c head.S -o head.o +$(target)/misc.o: $(src)/misc.c + $(CC) -D__KERNEL__ -c $< -o $@ # gzip the kernel image piggy.img: $(SYSTEM) - cat $(SYSTEM) | gzip -f -9 > piggy.img + @cat $(SYSTEM) | gzip -f -9 > piggy.img + +$(target): + mkdir -p $(target) clean: - rm -f piggy.img vmlinuz vmlinuz.o + rm -f piggy.img $(objtree)/vmlinuz --- linux-2.6.13-rc2/arch/cris/arch-v10/boot/Makefile 2005-03-01 23:38:09.000000000 -0800 +++ devel/arch/cris/arch-v10/boot/Makefile 2005-07-12 00:26:58.000000000 -0700 @@ -1,12 +1,13 @@ # # arch/cris/boot/Makefile # +target = $(target_boot_dir) +src = $(src_boot_dir) zImage: compressed/vmlinuz -compressed/vmlinuz: $(TOPDIR)/vmlinux - @$(MAKE) -C compressed vmlinuz +compressed/vmlinuz: + @$(MAKE) -f $(src)/compressed/Makefile $(target_compressed_dir)/vmlinuz clean: - rm -f zImage tools/build compressed/vmlinux.out - @$(MAKE) -C compressed clean + @$(MAKE) -f $(src)/compressed/Makefile clean --- linux-2.6.13-rc2/arch/cris/arch-v10/boot/rescue/head.S 2005-03-01 23:38:18.000000000 -0800 +++ devel/arch/cris/arch-v10/boot/rescue/head.S 2005-07-12 00:26:59.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.6 2003/04/09 08:12:43 pkj Exp $ +/* $Id: head.S,v 1.7 2005/03/07 12:11:06 starvik Exp $ * * Rescue code, made to reside at the beginning of the * flash-memory. when it starts, it checks a partition @@ -121,12 +121,13 @@ ;; 0x80000000 if loaded in flash (as it should be) ;; since etrax actually starts at address 2 when booting from flash, we ;; put a nop (2 bytes) here first so we dont accidentally skip the di - + nop di jump in_cache ; enter cached area instead -in_cache: +in_cache: + ;; first put a jump test to give a possibility of upgrading the rescue code ;; without erasing/reflashing the sector. we put a longword of -1 here and if @@ -325,9 +326,29 @@ flash_ok: ;; result will be in r0 checksum: moveq 0, $r0 -1: addu.b [$r1+], $r0 - subq 1, $r2 - bne 1b + moveq CONFIG_ETRAX_FLASH1_SIZE, $r6 + + ;; If the first physical flash memory is exceeded wrap to the second one. + btstq 26, $r1 ; Are we addressing first flash? + bpl 1f + nop + clear.d $r6 + +1: test.d $r6 ; 0 = no wrapping + beq 2f + nop + lslq 20, $r6 ; Convert MB to bytes + sub.d $r1, $r6 + +2: addu.b [$r1+], $r0 + subq 1, $r6 ; Flash memory left + beq 3f + subq 1, $r2 ; Length left + bne 2b nop ret nop + +3: move.d MEM_CSE1_START, $r1 ; wrap to second flash + ba 2b + nop --- linux-2.6.13-rc2/arch/cris/arch-v10/boot/rescue/Makefile 2005-03-01 23:38:37.000000000 -0800 +++ devel/arch/cris/arch-v10/boot/rescue/Makefile 2005-07-12 00:26:58.000000000 -0700 @@ -1,52 +1,53 @@ # # Makefile for rescue code # -ifndef TOPDIR -TOPDIR = ../../../.. -endif -CC = gcc-cris -mlinux -I $(TOPDIR)/include +target = $(target_rescue_dir) +src = $(src_rescue_dir) + +CC = gcc-cris -mlinux $(LINUXINCLUDE) CFLAGS = -O2 LD = gcc-cris -mlinux -nostdlib OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss -all: rescue.bin testrescue.bin kimagerescue.bin - -rescue: rescue.bin - # do nothing +all: $(target)/rescue.bin $(target)/testrescue.bin $(target)/kimagerescue.bin -rescue.bin: head.o - $(LD) -T rescue.ld -o rescue.o head.o - $(OBJCOPY) $(OBJCOPYFLAGS) rescue.o rescue.bin - cp rescue.bin $(TOPDIR) +$(target)/rescue.bin: $(target) $(target)/head.o + $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin +# Place a copy in top-level build directory + cp -p $(target)/rescue.bin $(objtree) -testrescue.bin: testrescue.o - $(OBJCOPY) $(OBJCOPYFLAGS) testrescue.o tr.bin +$(target)/testrescue.bin: $(target) $(target)/testrescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/testrescue.o tr.bin # Pad it to 784 bytes dd if=/dev/zero of=tmp2423 bs=1 count=784 cat tr.bin tmp2423 >testrescue_tmp.bin - dd if=testrescue_tmp.bin of=testrescue.bin bs=1 count=784 + dd if=testrescue_tmp.bin of=$(target)/testrescue.bin bs=1 count=784 rm tr.bin tmp2423 testrescue_tmp.bin -kimagerescue.bin: kimagerescue.o - $(OBJCOPY) $(OBJCOPYFLAGS) kimagerescue.o ktr.bin +$(target)/kimagerescue.bin: $(target) $(target)/kimagerescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/kimagerescue.o ktr.bin # Pad it to 784 bytes, that's what the rescue loader expects dd if=/dev/zero of=tmp2423 bs=1 count=784 cat ktr.bin tmp2423 >kimagerescue_tmp.bin - dd if=kimagerescue_tmp.bin of=kimagerescue.bin bs=1 count=784 + dd if=kimagerescue_tmp.bin of=$(target)/kimagerescue.bin bs=1 count=784 rm ktr.bin tmp2423 kimagerescue_tmp.bin -head.o: head.S +$(target): + mkdir -p $(target) + +$(target)/head.o: $(src)/head.S $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o -testrescue.o: testrescue.S +$(target)/testrescue.o: $(src)/testrescue.S $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o -kimagerescue.o: kimagerescue.S +$(target)/kimagerescue.o: $(src)/kimagerescue.S $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o clean: - rm -f *.o *.bin + rm -f $(target)/*.o $(target)/*.bin fastdep: --- linux-2.6.13-rc2/arch/cris/arch-v10/drivers/axisflashmap.c 2005-03-01 23:38:17.000000000 -0800 +++ devel/arch/cris/arch-v10/drivers/axisflashmap.c 2005-07-12 00:26:59.000000000 -0700 @@ -11,6 +11,9 @@ * partition split defined below. * * $Log: axisflashmap.c,v $ + * Revision 1.11 2004/11/15 10:27:14 starvik + * Corrected typo (Thanks to Milton Miller ). + * * Revision 1.10 2004/08/16 12:37:22 starvik * Merge of Linux 2.6.8 * @@ -161,7 +164,7 @@ #elif CONFIG_ETRAX_FLASH_BUSWIDTH==2 #define flash_data __u16 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==4 -#define flash_data __u16 +#define flash_data __u32 #endif /* From head.S */ --- linux-2.6.13-rc2/arch/cris/arch-v10/drivers/ds1302.c 2005-03-01 23:38:38.000000000 -0800 +++ devel/arch/cris/arch-v10/drivers/ds1302.c 2005-07-12 00:26:59.000000000 -0700 @@ -7,6 +7,15 @@ *! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init *! *! $Log: ds1302.c,v $ +*! Revision 1.18 2005/01/24 09:11:26 mikaelam +*! Minor changes to get DS1302 RTC chip driver to work +*! +*! Revision 1.17 2005/01/05 06:11:22 starvik +*! No need to do local_irq_disable after local_irq_save. +*! +*! Revision 1.16 2004/12/13 12:21:52 starvik +*! Added I/O and DMA allocators from Linux 2.4 +*! *! Revision 1.14 2004/08/24 06:48:43 starvik *! Whitespace cleanup *! @@ -124,9 +133,9 @@ *! *! --------------------------------------------------------------------------- *! -*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN +*! (C) Copyright 1999, 2000, 2001, 2002, 2003, 2004 Axis Communications AB, LUND, SWEDEN *! -*! $Id: ds1302.c,v 1.14 2004/08/24 06:48:43 starvik Exp $ +*! $Id: ds1302.c,v 1.18 2005/01/24 09:11:26 mikaelam Exp $ *! *!***************************************************************************/ @@ -145,6 +154,7 @@ #include #include #include +#include #define RTC_MAJOR_NR 121 /* local major, change later */ @@ -320,7 +330,6 @@ get_rtc_time(struct rtc_time *rtc_tm) unsigned long flags; local_irq_save(flags); - local_irq_disable(); rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); @@ -358,7 +367,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - unsigned long flags; + unsigned long flags; switch(cmd) { case RTC_RD_TIME: /* read the time/date from RTC */ @@ -382,7 +391,7 @@ rtc_ioctl(struct inode *inode, struct fi return -EPERM; if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time))) - return -EFAULT; + return -EFAULT; yrs = rtc_tm.tm_year + 1900; mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ @@ -419,7 +428,6 @@ rtc_ioctl(struct inode *inode, struct fi BIN_TO_BCD(yrs); local_irq_save(flags); - local_irq_disable(); CMOS_WRITE(yrs, RTC_YEAR); CMOS_WRITE(mon, RTC_MONTH); CMOS_WRITE(day, RTC_DAY_OF_MONTH); @@ -438,7 +446,7 @@ rtc_ioctl(struct inode *inode, struct fi case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */ { - int tcs_val; + int tcs_val; if (!capable(CAP_SYS_TIME)) return -EPERM; @@ -492,8 +500,8 @@ print_rtc_status(void) /* The various file operations we support. */ static struct file_operations rtc_fops = { - .owner = THIS_MODULE, - .ioctl = rtc_ioctl, + .owner = THIS_MODULE, + .ioctl = rtc_ioctl, }; /* Probe for the chip by writing something to its RAM and try reading it back. */ @@ -532,7 +540,7 @@ ds1302_probe(void) "PB", #endif CONFIG_ETRAX_DS1302_RSTBIT); - print_rtc_status(); + print_rtc_status(); retval = 1; } else { stop(); @@ -548,7 +556,9 @@ ds1302_probe(void) int __init ds1302_init(void) { +#ifdef CONFIG_ETRAX_I2C i2c_init(); +#endif if (!ds1302_probe()) { #ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT @@ -558,25 +568,42 @@ ds1302_init(void) * * Make sure that R_GEN_CONFIG is setup correct. */ - genconfig_shadow = ((genconfig_shadow & - ~IO_MASK(R_GEN_CONFIG, ata)) | - (IO_STATE(R_GEN_CONFIG, ata, select))); - *R_GEN_CONFIG = genconfig_shadow; + /* Allocating the ATA interface will grab almost all + * pins in I/O groups a, b, c and d. A consequence of + * allocating the ATA interface is that the fixed + * interfaces shared RAM, parallel port 0, parallel + * port 1, parallel port W, SCSI-8 port 0, SCSI-8 port + * 1, SCSI-W, serial port 2, serial port 3, + * synchronous serial port 3 and USB port 2 and almost + * all GPIO pins on port g cannot be used. + */ + if (cris_request_io_interface(if_ata, "ds1302/ATA")) { + printk(KERN_WARNING "ds1302: Failed to get IO interface\n"); + return -1; + } + #elif CONFIG_ETRAX_DS1302_RSTBIT == 0 - - /* Set the direction of this bit to out. */ - genconfig_shadow = ((genconfig_shadow & - ~IO_MASK(R_GEN_CONFIG, g0dir)) | - (IO_STATE(R_GEN_CONFIG, g0dir, out))); - *R_GEN_CONFIG = genconfig_shadow; + if (cris_io_interface_allocate_pins(if_gpio_grp_a, + 'g', + CONFIG_ETRAX_DS1302_RSTBIT, + CONFIG_ETRAX_DS1302_RSTBIT)) { + printk(KERN_WARNING "ds1302: Failed to get IO interface\n"); + return -1; + } + + /* Set the direction of this bit to out. */ + genconfig_shadow = ((genconfig_shadow & + ~IO_MASK(R_GEN_CONFIG, g0dir)) | + (IO_STATE(R_GEN_CONFIG, g0dir, out))); + *R_GEN_CONFIG = genconfig_shadow; #endif if (!ds1302_probe()) { printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name); - return -1; + return -1; } #else printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name); - return -1; + return -1; #endif } /* Initialise trickle charger */ --- linux-2.6.13-rc2/arch/cris/arch-v10/drivers/eeprom.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/cris/arch-v10/drivers/eeprom.c 2005-07-12 00:26:59.000000000 -0700 @@ -20,6 +20,12 @@ *! in the spin-lock. *! *! $Log: eeprom.c,v $ +*! Revision 1.12 2005/06/19 17:06:46 starvik +*! Merge of Linux 2.6.12. +*! +*! Revision 1.11 2005/01/26 07:14:46 starvik +*! Applied diff from kernel janitors (Nish Aravamudan). +*! *! Revision 1.10 2003/09/11 07:29:48 starvik *! Merge of Linux 2.6.0-test5 *! @@ -94,6 +100,7 @@ #include #include #include +#include #include #include "i2c.h" @@ -526,15 +533,10 @@ static ssize_t eeprom_read(struct file * return -EFAULT; } - while(eeprom.busy) - { - interruptible_sleep_on(&eeprom.wait_q); + wait_event_interruptible(eeprom.wait_q, !eeprom.busy); + if (signal_pending(current)) + return -EINTR; - /* bail out if we get interrupted */ - if (signal_pending(current)) - return -EINTR; - - } eeprom.busy++; page = (unsigned char) (p >> 8); @@ -604,13 +606,10 @@ static ssize_t eeprom_write(struct file return -EFAULT; } - while(eeprom.busy) - { - interruptible_sleep_on(&eeprom.wait_q); - /* bail out if we get interrupted */ - if (signal_pending(current)) - return -EINTR; - } + wait_event_interruptible(eeprom.wait_q, !eeprom.busy); + /* bail out if we get interrupted */ + if (signal_pending(current)) + return -EINTR; eeprom.busy++; for(i = 0; (i < EEPROM_RETRIES) && (restart > 0); i++) { --- linux-2.6.13-rc2/arch/cris/arch-v10/drivers/gpio.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/cris/arch-v10/drivers/gpio.c 2005-07-12 00:26:59.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: gpio.c,v 1.12 2004/08/24 07:19:59 starvik Exp $ +/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $ * * Etrax general port I/O device * @@ -9,6 +9,18 @@ * Johan Adolfsson (read/set directions, write, port G) * * $Log: gpio.c,v $ + * Revision 1.17 2005/06/19 17:06:46 starvik + * Merge of Linux 2.6.12. + * + * Revision 1.16 2005/03/07 13:02:29 starvik + * Protect driver global states with spinlock + * + * Revision 1.15 2005/01/05 06:08:55 starvik + * No need to do local_irq_disable after local_irq_save. + * + * Revision 1.14 2004/12/13 12:21:52 starvik + * Added I/O and DMA allocators from Linux 2.4 + * * Revision 1.12 2004/08/24 07:19:59 starvik * Whitespace cleanup * @@ -142,6 +154,7 @@ #include #include #include +#include #define GPIO_MAJOR 120 /* experimental MAJOR number */ @@ -194,6 +207,8 @@ static struct gpio_private *alarmlist = static int gpio_some_alarms = 0; /* Set if someone uses alarm */ static unsigned long gpio_pa_irq_enabled_mask = 0; +static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */ + /* Port A and B use 8 bit access, but Port G is 32 bit */ #define NUM_PORTS (GPIO_MINOR_B+1) @@ -241,6 +256,9 @@ static volatile unsigned char *dir_shado &port_pb_dir_shadow }; +/* All bits in port g that can change dir. */ +static const unsigned long int changeable_dir_g_mask = 0x01FFFF01; + /* Port G is 32 bit, handle it special, some bits are both inputs and outputs at the same time, only some of the bits can change direction and some of them in groups of 8 bit. */ @@ -260,6 +278,7 @@ gpio_poll(struct file *file, unsigned int mask = 0; struct gpio_private *priv = (struct gpio_private *)file->private_data; unsigned long data; + spin_lock(&gpio_lock); poll_wait(file, &priv->alarm_wq, wait); if (priv->minor == GPIO_MINOR_A) { unsigned long flags; @@ -270,10 +289,10 @@ gpio_poll(struct file *file, */ tmp = ~data & priv->highalarm & 0xFF; tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR); - save_flags(flags); cli(); + local_irq_save(flags); gpio_pa_irq_enabled_mask |= tmp; *R_IRQ_MASK1_SET = tmp; - restore_flags(flags); + local_irq_restore(flags); } else if (priv->minor == GPIO_MINOR_B) data = *R_PORT_PB_DATA; @@ -286,8 +305,11 @@ gpio_poll(struct file *file, (~data & priv->lowalarm)) { mask = POLLIN|POLLRDNORM; } + + spin_unlock(&gpio_lock); DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); + return mask; } @@ -296,6 +318,7 @@ int etrax_gpio_wake_up_check(void) struct gpio_private *priv = alarmlist; unsigned long data = 0; int ret = 0; + spin_lock(&gpio_lock); while (priv) { if (USE_PORTS(priv)) { data = *priv->port; @@ -310,6 +333,7 @@ int etrax_gpio_wake_up_check(void) } priv = priv->next; } + spin_unlock(&gpio_lock); return ret; } @@ -327,6 +351,7 @@ static irqreturn_t gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned long tmp; + spin_lock(&gpio_lock); /* Find what PA interrupts are active */ tmp = (*R_IRQ_READ1); @@ -337,6 +362,8 @@ gpio_pa_interrupt(int irq, void *dev_id, *R_IRQ_MASK1_CLR = tmp; gpio_pa_irq_enabled_mask &= ~tmp; + spin_unlock(&gpio_lock); + if (gpio_some_alarms) { return IRQ_RETVAL(etrax_gpio_wake_up_check()); } @@ -350,6 +377,9 @@ static ssize_t gpio_write(struct file * struct gpio_private *priv = (struct gpio_private *)file->private_data; unsigned char data, clk_mask, data_mask, write_msb; unsigned long flags; + + spin_lock(&gpio_lock); + ssize_t retval = count; if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) { return -EFAULT; @@ -372,7 +402,7 @@ static ssize_t gpio_write(struct file * data = *buf++; if (priv->write_msb) { for (i = 7; i >= 0;i--) { - local_irq_save(flags); local_irq_disable(); + local_irq_save(flags); *priv->port = *priv->shadow &= ~clk_mask; if (data & 1<port = *priv->shadow |= data_mask; @@ -384,7 +414,7 @@ static ssize_t gpio_write(struct file * } } else { for (i = 0; i <= 7;i++) { - local_irq_save(flags); local_irq_disable(); + local_irq_save(flags); *priv->port = *priv->shadow &= ~clk_mask; if (data & 1<port = *priv->shadow |= data_mask; @@ -396,6 +426,7 @@ static ssize_t gpio_write(struct file * } } } + spin_unlock(&gpio_lock); return retval; } @@ -452,9 +483,14 @@ gpio_open(struct inode *inode, struct fi static int gpio_release(struct inode *inode, struct file *filp) { - struct gpio_private *p = alarmlist; - struct gpio_private *todel = (struct gpio_private *)filp->private_data; - + struct gpio_private *p; + struct gpio_private *todel; + + spin_lock(&gpio_lock); + + p = alarmlist; + todel = (struct gpio_private *)filp->private_data; + /* unlink from alarmlist and free the private structure */ if (p == todel) { @@ -476,7 +512,7 @@ gpio_release(struct inode *inode, struct p = p->next; } gpio_some_alarms = 0; - + spin_unlock(&gpio_lock); return 0; } @@ -491,14 +527,14 @@ unsigned long inline setget_input(struct */ unsigned long flags; if (USE_PORTS(priv)) { - local_irq_save(flags); local_irq_disable(); + local_irq_save(flags); *priv->dir = *priv->dir_shadow &= ~((unsigned char)arg & priv->changeable_dir); local_irq_restore(flags); return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ } else if (priv->minor == GPIO_MINOR_G) { /* We must fiddle with R_GEN_CONFIG to change dir */ - save_flags(flags); cli(); + local_irq_save(flags); if (((arg & dir_g_in_bits) != arg) && (arg & changeable_dir_g)) { arg &= changeable_dir_g; @@ -533,7 +569,7 @@ unsigned long inline setget_input(struct /* Must be a >120 ns delay before writing this again */ } - restore_flags(flags); + local_irq_restore(flags); return dir_g_in_bits; } return 0; @@ -543,14 +579,14 @@ unsigned long inline setget_output(struc { unsigned long flags; if (USE_PORTS(priv)) { - local_irq_save(flags); local_irq_disable(); + local_irq_save(flags); *priv->dir = *priv->dir_shadow |= ((unsigned char)arg & priv->changeable_dir); local_irq_restore(flags); return *priv->dir_shadow; } else if (priv->minor == GPIO_MINOR_G) { /* We must fiddle with R_GEN_CONFIG to change dir */ - save_flags(flags); cli(); + local_irq_save(flags); if (((arg & dir_g_out_bits) != arg) && (arg & changeable_dir_g)) { /* Set bits in genconfig to set to output */ @@ -583,7 +619,7 @@ unsigned long inline setget_output(struc *R_GEN_CONFIG = genconfig_shadow; /* Must be a >120 ns delay before writing this again */ } - restore_flags(flags); + local_irq_restore(flags); return dir_g_out_bits & 0x7FFFFFFF; } return 0; @@ -598,22 +634,26 @@ gpio_ioctl(struct inode *inode, struct f { unsigned long flags; unsigned long val; + int ret = 0; + struct gpio_private *priv = (struct gpio_private *)file->private_data; if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { return -EINVAL; } + spin_lock(&gpio_lock); + switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ // read the port if (USE_PORTS(priv)) { - return *priv->port; + ret = *priv->port; } else if (priv->minor == GPIO_MINOR_G) { - return (*R_PORT_G_DATA) & 0x7FFFFFFF; + ret = (*R_PORT_G_DATA) & 0x7FFFFFFF; } break; case IO_SETBITS: - local_irq_save(flags); local_irq_disable(); + local_irq_save(flags); // set changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow |= @@ -624,7 +664,7 @@ gpio_ioctl(struct inode *inode, struct f local_irq_restore(flags); break; case IO_CLRBITS: - local_irq_save(flags); local_irq_disable(); + local_irq_save(flags); // clear changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow &= @@ -666,33 +706,34 @@ gpio_ioctl(struct inode *inode, struct f case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ /* Read direction 0=input 1=output */ if (USE_PORTS(priv)) { - return *priv->dir_shadow; + ret = *priv->dir_shadow; } else if (priv->minor == GPIO_MINOR_G) { /* Note: Some bits are both in and out, * Those that are dual is set here as well. */ - return (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; + ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; } + break; case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ /* Set direction 0=unchanged 1=input, * return mask with 1=input */ - return setget_input(priv, arg) & 0x7FFFFFFF; + ret = setget_input(priv, arg) & 0x7FFFFFFF; break; case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ /* Set direction 0=unchanged 1=output, * return mask with 1=output */ - return setget_output(priv, arg) & 0x7FFFFFFF; - + ret = setget_output(priv, arg) & 0x7FFFFFFF; + break; case IO_SHUTDOWN: SOFT_SHUTDOWN(); break; case IO_GET_PWR_BT: #if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) - return (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); + ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); #else - return 0; + ret = 0; #endif break; case IO_CFG_WRITE_MODE: @@ -709,7 +750,7 @@ gpio_ioctl(struct inode *inode, struct f { priv->clk_mask = 0; priv->data_mask = 0; - return -EPERM; + ret = -EPERM; } break; case IO_READ_INBITS: @@ -720,8 +761,7 @@ gpio_ioctl(struct inode *inode, struct f val = *R_PORT_G_DATA; } if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) - return -EFAULT; - return 0; + ret = -EFAULT; break; case IO_READ_OUTBITS: /* *arg is result of reading the output shadow */ @@ -731,36 +771,43 @@ gpio_ioctl(struct inode *inode, struct f val = port_g_data_shadow; } if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) - return -EFAULT; + ret = -EFAULT; break; case IO_SETGET_INPUT: /* bits set in *arg is set to input, * *arg updated with current input pins. */ if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) - return -EFAULT; + { + ret = -EFAULT; + break; + } val = setget_input(priv, val); if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) - return -EFAULT; + ret = -EFAULT; break; case IO_SETGET_OUTPUT: /* bits set in *arg is set to output, * *arg updated with current output pins. */ if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) - return -EFAULT; + { + ret = -EFAULT; + break; + } val = setget_output(priv, val); if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) - return -EFAULT; + ret = -EFAULT; break; default: if (priv->minor == GPIO_MINOR_LEDS) - return gpio_leds_ioctl(cmd, arg); + ret = gpio_leds_ioctl(cmd, arg); else - return -EINVAL; + ret = -EINVAL; } /* switch */ - - return 0; + + spin_unlock(&gpio_lock); + return ret; } static int @@ -802,60 +849,20 @@ struct file_operations gpio_fops = { }; -static void __init gpio_init_port_g(void) -{ -#define GROUPA (0x0000FF3F) -#define GROUPB (1<<6 | 1<<7) -#define GROUPC (1<<30 | 1<<31) -#define GROUPD (0x3FFF0000) -#define GROUPD_LOW (0x00FF0000) - unsigned long used_in_bits = 0; - unsigned long used_out_bits = 0; - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0, select)){ - used_in_bits |= GROUPA | GROUPB | 0 | 0; - used_out_bits |= GROUPA | GROUPB | 0 | 0; - } - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ata, select)) { - used_in_bits |= GROUPA | GROUPB | GROUPC | (GROUPD & ~(1<<25|1<<26)); - used_out_bits |= GROUPA | GROUPB | GROUPC | GROUPD; - } - - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par0, select)) { - used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0; - used_out_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0; - } - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser2, select)) { - used_in_bits |= 0 | GROUPB | 0 | 0; - used_out_bits |= 0 | GROUPB | 0 | 0; - } - /* mio same as shared RAM ? */ - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio, select)) { - used_in_bits |= (GROUPA & ~(1<<0)) | 0 |0 |GROUPD_LOW; - used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 |0 |GROUPD_LOW; - } - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi1, select)) { - used_in_bits |= 0 | 0 | GROUPC | GROUPD; - used_out_bits |= 0 | 0 | GROUPC | GROUPD; - } - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0w, select)) { - used_in_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24); - used_out_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24 | 1<<25|1<<26); - } - - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par1, select)) { - used_in_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24)); - used_out_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24)); - } - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser3, select)) { - used_in_bits |= 0 | 0 | GROUPC | 0; - used_out_bits |= 0 | 0 | GROUPC | 0; - } - /* mio same as shared RAM-W? */ - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio_w, select)) { - used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 |GROUPD_LOW; - used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 | 0 |GROUPD_LOW; - } - /* TODO: USB p2, parw, sync ser3? */ +void ioif_watcher(const unsigned int gpio_in_available, + const unsigned int gpio_out_available, + const unsigned char pa_available, + const unsigned char pb_available) +{ + unsigned long int flags; + D(printk("gpio.c: ioif_watcher called\n")); + D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n", + gpio_in_available, gpio_out_available, pa_available, pb_available)); + + spin_lock_irqsave(&gpio_lock, flags); + + dir_g_in_bits = gpio_in_available; + dir_g_out_bits = gpio_out_available; /* Initialise the dir_g_shadow etc. depending on genconfig */ /* 0=input 1=output */ @@ -868,10 +875,7 @@ static void __init gpio_init_port_g(void if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out)) dir_g_shadow |= (1 << 24); - dir_g_in_bits = ~used_in_bits; - dir_g_out_bits = ~used_out_bits; - - changeable_dir_g = 0x01FFFF01; /* all that can change dir */ + changeable_dir_g = changeable_dir_g_mask; changeable_dir_g &= dir_g_out_bits; changeable_dir_g &= dir_g_in_bits; /* Correct the bits that can change direction */ @@ -880,6 +884,7 @@ static void __init gpio_init_port_g(void dir_g_in_bits &= ~changeable_dir_g; dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g); + spin_unlock_irqrestore(&gpio_lock, flags); printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n", dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA); @@ -896,6 +901,7 @@ gpio_init(void) #if defined (CONFIG_ETRAX_CSP0_LEDS) int i; #endif + printk("gpio init\n"); /* do the formalities */ @@ -919,8 +925,13 @@ gpio_init(void) #endif #endif - gpio_init_port_g(); - printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 Axis Communications AB\n"); + /* The I/O interface allocation watcher will be called when + * registering it. */ + if (cris_io_interface_register_watcher(ioif_watcher)){ + printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n"); + } + + printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002, 2003, 2004 Axis Communications AB\n"); /* We call etrax_gpio_wake_up_check() from timer interrupt and * from cpu_idle() in kernel/process.c * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms --- linux-2.6.13-rc2/arch/cris/arch-v10/drivers/i2c.c 2005-03-01 23:37:48.000000000 -0800 +++ devel/arch/cris/arch-v10/drivers/i2c.c 2005-07-12 00:26:59.000000000 -0700 @@ -12,6 +12,15 @@ *! don't use PB_I2C if DS1302 uses same bits, *! use PB. *! $Log: i2c.c,v $ +*! Revision 1.13 2005/03/07 13:13:07 starvik +*! Added spinlocks to protect states etc +*! +*! Revision 1.12 2005/01/05 06:11:22 starvik +*! No need to do local_irq_disable after local_irq_save. +*! +*! Revision 1.11 2004/12/13 12:21:52 starvik +*! Added I/O and DMA allocators from Linux 2.4 +*! *! Revision 1.9 2004/08/24 06:49:14 starvik *! Whitespace cleanup *! @@ -75,7 +84,7 @@ *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.9 2004/08/24 06:49:14 starvik Exp $ */ +/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ @@ -95,6 +104,7 @@ #include #include #include +#include #include "i2c.h" @@ -184,6 +194,7 @@ static const char i2c_name[] = "i2c"; #define i2c_delay(usecs) udelay(usecs) +static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */ /****************** FUNCTION DEFINITION SECTION *************************/ @@ -488,13 +499,14 @@ i2c_writereg(unsigned char theSlave, uns int error, cntr = 3; unsigned long flags; + spin_lock(&i2c_lock); + do { error = 0; /* * we don't like to be interrupted */ local_irq_save(flags); - local_irq_disable(); i2c_start(); /* @@ -538,6 +550,8 @@ i2c_writereg(unsigned char theSlave, uns i2c_delay(CLOCK_LOW_TIME); + spin_unlock(&i2c_lock); + return -error; } @@ -555,13 +569,14 @@ i2c_readreg(unsigned char theSlave, unsi int error, cntr = 3; unsigned long flags; + spin_lock(&i2c_lock); + do { error = 0; /* * we don't like to be interrupted */ local_irq_save(flags); - local_irq_disable(); /* * generate start condition */ @@ -620,6 +635,8 @@ i2c_readreg(unsigned char theSlave, unsi } while(error && cntr--); + spin_unlock(&i2c_lock); + return b; } @@ -686,15 +703,26 @@ static struct file_operations i2c_fops = int __init i2c_init(void) { + static int res = 0; + static int first = 1; + + if (!first) { + return res; + } + /* Setup and enable the Port B I2C interface */ #ifndef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C + if ((res = cris_request_io_interface(if_i2c, "I2C"))) { + printk(KERN_CRIT "i2c_init: Failed to get IO interface\n"); + return res; + } + *R_PORT_PB_I2C = port_pb_i2c_shadow |= IO_STATE(R_PORT_PB_I2C, i2c_en, on) | IO_FIELD(R_PORT_PB_I2C, i2c_d, 1) | IO_FIELD(R_PORT_PB_I2C, i2c_clk, 1) | IO_STATE(R_PORT_PB_I2C, i2c_oe_, enable); -#endif port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir0); port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir1); @@ -702,8 +730,26 @@ i2c_init(void) *R_PORT_PB_DIR = (port_pb_dir_shadow |= IO_STATE(R_PORT_PB_DIR, dir0, input) | IO_STATE(R_PORT_PB_DIR, dir1, output)); +#else + if ((res = cris_io_interface_allocate_pins(if_i2c, + 'b', + CONFIG_ETRAX_I2C_DATA_PORT, + CONFIG_ETRAX_I2C_DATA_PORT))) { + printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C data port\n"); + return res; + } else if ((res = cris_io_interface_allocate_pins(if_i2c, + 'b', + CONFIG_ETRAX_I2C_CLK_PORT, + CONFIG_ETRAX_I2C_CLK_PORT))) { + cris_io_interface_free_pins(if_i2c, + 'b', + CONFIG_ETRAX_I2C_DATA_PORT, + CONFIG_ETRAX_I2C_DATA_PORT); + printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C clk port\n"); + } +#endif - return 0; + return res; } static int __init @@ -711,14 +757,16 @@ i2c_register(void) { int res; - i2c_init(); + res = i2c_init(); + if (res < 0) + return res; res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); if(res < 0) { printk(KERN_ERR "i2c: couldn't get a major number.\n"); return res; } - printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); + printk(KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n"); return 0; } --- linux-2.6.13-rc2/arch/cris/arch-v10/drivers/Kconfig 2005-03-01 23:37:50.000000000 -0800 +++ devel/arch/cris/arch-v10/drivers/Kconfig 2005-07-12 00:26:58.000000000 -0700 @@ -1,17 +1,11 @@ config ETRAX_ETHERNET bool "Ethernet support" depends on ETRAX_ARCH_V10 + select NET_ETHERNET help This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet controller. -# this is just so that the user does not have to go into the -# normal ethernet driver section just to enable ethernetworking -config NET_ETHERNET - bool - depends on ETRAX_ETHERNET - default y - choice prompt "Network LED behavior" depends on ETRAX_ETHERNET @@ -20,26 +14,26 @@ choice config ETRAX_NETWORK_LED_ON_WHEN_LINK bool "LED_on_when_link" help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. - Selecting LED_on_when_activity will light the LED only when + Selecting LED_on_when_activity will light the LED only when there is activity. - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY bool "LED_on_when_activity" help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. - Selecting LED_on_when_activity will light the LED only when + Selecting LED_on_when_activity will light the LED only when there is activity. - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. endchoice @@ -91,11 +85,11 @@ choice depends on ETRAX_SERIAL_PORT0 default ETRAX_SERIAL_PORT0_DMA6_OUT -config CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT - bool "No DMA out" +config ETRAX_SERIAL_PORT0_NO_DMA_OUT + bool "No DMA out" -config CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT - bool "DMA 6" +config ETRAX_SERIAL_PORT0_DMA6_OUT + bool "DMA 6" endchoice @@ -104,11 +98,11 @@ choice depends on ETRAX_SERIAL_PORT0 default ETRAX_SERIAL_PORT0_DMA7_IN -config CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN - bool "No DMA in" +config ETRAX_SERIAL_PORT0_NO_DMA_IN + bool "No DMA in" -config CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN - bool "DMA 7" +config ETRAX_SERIAL_PORT0_DMA7_IN + bool "DMA 7" endchoice @@ -205,11 +199,11 @@ choice depends on ETRAX_SERIAL_PORT1 default ETRAX_SERIAL_PORT1_DMA8_OUT -config CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_OUT - bool "No DMA out" +config ETRAX_SERIAL_PORT1_NO_DMA_OUT + bool "No DMA out" -config CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT - bool "DMA 8" +config ETRAX_SERIAL_PORT1_DMA8_OUT + bool "DMA 8" endchoice @@ -218,11 +212,11 @@ choice depends on ETRAX_SERIAL_PORT1 default ETRAX_SERIAL_PORT1_DMA9_IN -config CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_IN - bool "No DMA in" +config ETRAX_SERIAL_PORT1_NO_DMA_IN + bool "No DMA in" -config CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN - bool "DMA 9" +config ETRAX_SERIAL_PORT1_DMA9_IN + bool "DMA 9" endchoice @@ -308,7 +302,7 @@ config ETRAX_SER1_CD_ON_PB_BIT Specify the pin of the PB port to carry the CD signal for serial port 1. -comment "Make sure you dont have the same PB bits more than once!" +comment "Make sure you do not have the same PB bits more than once!" depends on ETRAX_SERIAL && ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && ETRAX_SER1_DTR_RI_DSR_CD_ON_PB config ETRAX_SERIAL_PORT2 @@ -322,11 +316,11 @@ choice depends on ETRAX_SERIAL_PORT2 default ETRAX_SERIAL_PORT2_DMA2_OUT -config CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT - bool "No DMA out" +config ETRAX_SERIAL_PORT2_NO_DMA_OUT + bool "No DMA out" -config CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT - bool "DMA 2" +config ETRAX_SERIAL_PORT2_DMA2_OUT + bool "DMA 2" endchoice @@ -335,11 +329,11 @@ choice depends on ETRAX_SERIAL_PORT2 default ETRAX_SERIAL_PORT2_DMA3_IN -config CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN - bool "No DMA in" +config ETRAX_SERIAL_PORT2_NO_DMA_IN + bool "No DMA in" -config CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN - bool "DMA 3" +config ETRAX_SERIAL_PORT2_DMA3_IN + bool "DMA 3" endchoice @@ -436,11 +430,11 @@ choice depends on ETRAX_SERIAL_PORT3 default ETRAX_SERIAL_PORT3_DMA4_OUT -config CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_OUT - bool "No DMA out" +config ETRAX_SERIAL_PORT3_NO_DMA_OUT + bool "No DMA out" -config CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT - bool "DMA 4" +config ETRAX_SERIAL_PORT3_DMA4_OUT + bool "DMA 4" endchoice @@ -449,11 +443,11 @@ choice depends on ETRAX_SERIAL_PORT3 default ETRAX_SERIAL_PORT3_DMA5_IN -config CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_IN - bool "No DMA in" +config ETRAX_SERIAL_PORT3_NO_DMA_IN + bool "No DMA in" -config CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN - bool "DMA 5" +config ETRAX_SERIAL_PORT3_DMA5_IN + bool "DMA 5" endchoice @@ -554,7 +548,6 @@ config ETRAX_IDE select BLK_DEV_IDEDISK select BLK_DEV_IDECD select BLK_DEV_IDEDMA - select DMA_NONPCI help Enable this to get support for ATA/IDE. You can't use paralell ports or SCSI ports @@ -579,7 +572,7 @@ config ETRAX_IDE_PB7_RESET IDE reset on pin 7 on port B config ETRAX_IDE_G27_RESET - bool "Port_G_Bit_27" + bool "Port_G_Bit_27" help IDE reset on pin 27 on port G @@ -588,30 +581,36 @@ endchoice config ETRAX_USB_HOST bool "USB host" + select USB help This option enables the host functionality of the ETRAX 100LX built-in USB controller. In host mode the controller is designed for CTRL and BULK traffic only, INTR traffic may work as well however (depending on the requirements of timeliness). -config USB - tristate - depends on ETRAX_USB_HOST - default y - config ETRAX_USB_HOST_PORT1 - bool " USB port 1 enabled" - depends on ETRAX_USB_HOST - default n + bool "USB port 1 enabled" + depends on ETRAX_USB_HOST + default n config ETRAX_USB_HOST_PORT2 - bool " USB port 2 enabled" - depends on ETRAX_USB_HOST - default n + bool "USB port 2 enabled" + depends on ETRAX_USB_HOST + default n config ETRAX_AXISFLASHMAP bool "Axis flash-map support" depends on ETRAX_ARCH_V10 + select MTD + select MTD_CFI + select MTD_CFI_AMDSTD + select MTD_OBSOLETE_CHIPS + select MTD_AMDSTD + select MTD_CHAR + select MTD_BLOCK + select MTD_PARTITIONS + select MTD_CONCAT + select MTD_COMPLEX_MAPPINGS help This option enables MTD mapping of flash devices. Needed to use flash memories. If unsure, say Y. @@ -627,119 +626,6 @@ config ETRAX_PTABLE_SECTOR for changing this is when the flash block size is bigger than 64kB (e.g. when using two parallel 16 bit flashes). -# here we define the CONFIG_'s necessary to enable MTD support -# for the flash -config MTD - tristate - depends on ETRAX_AXISFLASHMAP - default y - help - Memory Technology Devices are flash, RAM and similar chips, often - used for solid state file systems on embedded devices. This option - will provide the generic support for MTD drivers to register - themselves with the kernel and for potential users of MTD devices - to enumerate the devices which are present and obtain a handle on - them. It will also allow you to select individual drivers for - particular hardware and users of MTD devices. If unsure, say N. - -config MTD_CFI - tristate - depends on ETRAX_AXISFLASHMAP - default y - help - The Common Flash Interface specification was developed by Intel, - AMD and other flash manufactures that provides a universal method - for probing the capabilities of flash devices. If you wish to - support any device that is CFI-compliant, you need to enable this - option. Visit - for more information on CFI. - -config MTD_CFI_AMDSTD - tristate - depends on ETRAX_AXISFLASHMAP - default y - help - The Common Flash Interface defines a number of different command - sets which a CFI-compliant chip may claim to implement. This code - provides support for one of those command sets, used on chips - chips including the AMD Am29LV320. - -config MTD_OBSOLETE_CHIPS - bool - depends on ETRAX_AXISFLASHMAP - default y - help - This option does not enable any code directly, but will allow you to - select some other chip drivers which are now considered obsolete, - because the generic CONFIG_JEDEC_PROBE code above should now detect - the chips which are supported by these drivers, and allow the generic - CFI-compatible drivers to drive the chips. Say 'N' here unless you have - already tried the CONFIG_JEDEC_PROBE method and reported its failure - to the MTD mailing list at - -config MTD_AMDSTD - tristate - depends on ETRAX_AXISFLASHMAP - default y - help - This option enables support for flash chips using AMD-compatible - commands, including some which are not CFI-compatible and hence - cannot be used with the CONFIG_MTD_CFI_AMDSTD option. - - It also works on AMD compatible chips that do conform to CFI. - -config MTD_CHAR - tristate - depends on ETRAX_AXISFLASHMAP - default y - help - This provides a character device for each MTD device present in - the system, allowing the user to read and write directly to the - memory chips, and also use ioctl() to obtain information about - the device, or to erase parts of it. - -config MTD_BLOCK - tristate - depends on ETRAX_AXISFLASHMAP - default y - ---help--- - Although most flash chips have an erase size too large to be useful - as block devices, it is possible to use MTD devices which are based - on RAM chips in this manner. This block device is a user of MTD - devices performing that function. - - At the moment, it is also required for the Journalling Flash File - System(s) to obtain a handle on the MTD device when it's mounted - (although JFFS and JFFS2 don't actually use any of the functionality - of the mtdblock device). - - Later, it may be extended to perform read/erase/modify/write cycles - on flash chips to emulate a smaller block size. Needless to say, - this is very unsafe, but could be useful for file systems which are - almost never written to. - - You do not need this option for use with the DiskOnChip devices. For - those, enable NFTL support (CONFIG_NFTL) instead. - -config MTD_PARTITIONS - tristate - depends on ETRAX_AXISFLASHMAP - default y - help - If you have a device which needs to divide its flash chip(s) up - into multiple 'partitions', each of which appears to the user as - a separate MTD device, you require this option to be enabled. If - unsure, say 'Y'. - - Note, however, that you don't need this option for the DiskOnChip - devices. Partitioning on NFTL 'devices' is a different - that's the - 'normal' form of partitioning used on a block device. - -config MTD_CONCAT - tristate - depends on ETRAX_AXISFLASHMAP - default y - config ETRAX_I2C bool "I2C support" depends on ETRAX_ARCH_V10 @@ -752,7 +638,7 @@ config ETRAX_I2C val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg); # this is true for most products since PB-I2C seems to be somewhat -# flawed.. +# flawed.. config ETRAX_I2C_USES_PB_NOT_PB_I2C bool "I2C uses PB not PB-I2C" depends on ETRAX_I2C @@ -886,7 +772,7 @@ config ETRAX_RTC bool "Real Time Clock support" depends on ETRAX_ARCH_V10 help - Enables drivers for the Real-Time Clock battery-backed chips on + Enables drivers for the Real-Time Clock battery-backed chips on some products. The kernel reads the time when booting, and the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a rtc_time struct (see ) on the /dev/rtc @@ -903,13 +789,13 @@ config ETRAX_DS1302 bool "DS1302" help Enables the driver for the DS1302 Real-Time Clock battery-backed - chip on some products. + chip on some products. config ETRAX_PCF8563 bool "PCF8563" help Enables the driver for the PCF8563 Real-Time Clock battery-backed - chip on some products. + chip on some products. endchoice @@ -954,10 +840,8 @@ config ETRAX_DS1302_TRICKLE_CHARGE help This controls the initial value of the trickle charge register. 0 = disabled (use this if you are unsure or have a non rechargable battery) - Otherwise the following values can be OR:ed together to control the + Otherwise the following values can be OR:ed together to control the charge current: 1 = 2kohm, 2 = 4kohm, 3 = 4kohm 4 = 1 diode, 8 = 2 diodes Allowed values are (increasing current): 0, 11, 10, 9, 7, 6, 5 - - --- linux-2.6.13-rc2/arch/cris/arch-v10/drivers/pcf8563.c 2005-03-01 23:37:54.000000000 -0800 +++ devel/arch/cris/arch-v10/drivers/pcf8563.c 2005-07-12 00:26:59.000000000 -0700 @@ -15,7 +15,7 @@ * * Author: Tobias Anderberg . * - * $Id: pcf8563.c,v 1.8 2004/08/24 06:42:51 starvik Exp $ + * $Id: pcf8563.c,v 1.11 2005/03/07 13:13:07 starvik Exp $ */ #include @@ -40,7 +40,7 @@ #define PCF8563_MAJOR 121 /* Local major number. */ #define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ #define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.8 $" +#define DRIVER_VERSION "$Revision: 1.11 $" /* I2C bus slave registers. */ #define RTC_I2C_READ 0xa3 @@ -49,6 +49,8 @@ /* Two simple wrapper macros, saves a few keystrokes. */ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) + +static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */ static const unsigned char days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; @@ -125,9 +127,12 @@ get_rtc_time(struct rtc_time *tm) int __init pcf8563_init(void) { - unsigned char ret; + int ret; - i2c_init(); + if ((ret = i2c_init())) { + printk(KERN_CRIT "pcf8563_init: failed to init i2c\n"); + return ret; + } /* * First of all we need to reset the chip. This is done by @@ -200,12 +205,15 @@ pcf8563_ioctl(struct inode *inode, struc { struct rtc_time tm; + spin_lock(&rtc_lock); get_rtc_time(&tm); if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) { + spin_unlock(&rtc_lock); return -EFAULT; } + spin_unlock(&rtc_lock); return 0; } break; @@ -250,6 +258,8 @@ pcf8563_ioctl(struct inode *inode, struc BIN_TO_BCD(tm.tm_min); BIN_TO_BCD(tm.tm_sec); tm.tm_mon |= century; + + spin_lock(&rtc_lock); rtc_write(RTC_YEAR, tm.tm_year); rtc_write(RTC_MONTH, tm.tm_mon); @@ -258,6 +268,8 @@ pcf8563_ioctl(struct inode *inode, struc rtc_write(RTC_MINUTES, tm.tm_min); rtc_write(RTC_SECONDS, tm.tm_sec); + spin_unlock(&rtc_lock); + return 0; #endif /* !CONFIG_ETRAX_RTC_READONLY */ } --- linux-2.6.13-rc2/arch/cris/arch-v10/Kconfig 2005-03-01 23:38:33.000000000 -0800 +++ devel/arch/cris/arch-v10/Kconfig 2005-07-12 00:26:58.000000000 -0700 @@ -260,6 +260,37 @@ config ETRAX_DEBUG_PORT_NULL endchoice choice + prompt "Kernel GDB port" + depends on ETRAX_KGDB + default ETRAX_KGDB_PORT0 + help + Choose a serial port for kernel debugging. NOTE: This port should + not be enabled under Drivers for built-in interfaces (as it has its + own initialization code) and should not be the same as the debug port. + +config ETRAX_KGDB_PORT0 + bool "Serial-0" + help + Use serial port 0 for kernel debugging. + +config ETRAX_KGDB_PORT1 + bool "Serial-1" + help + Use serial port 1 for kernel debugging. + +config ETRAX_KGDB_PORT2 + bool "Serial-2" + help + Use serial port 2 for kernel debugging. + +config ETRAX_KGDB_PORT3 + bool "Serial-3" + help + Use serial port 3 for kernel debugging. + +endchoice + +choice prompt "Product rescue-port" depends on ETRAX_ARCH_V10 default ETRAX_RESCUE_SER0 --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/debugport.c 2005-03-01 23:37:45.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/debugport.c 2005-07-12 00:26:58.000000000 -0700 @@ -12,6 +12,31 @@ * init_etrax_debug() * * $Log: debugport.c,v $ + * Revision 1.27 2005/06/10 10:34:14 starvik + * Real console support + * + * Revision 1.26 2005/06/07 07:06:07 starvik + * Added LF->CR translation to make ETRAX customers happy. + * + * Revision 1.25 2005/03/08 08:56:47 mikaelam + * Do only set index as port->index if port is defined, otherwise use the index from the command line + * + * Revision 1.24 2005/01/19 10:26:33 mikaelam + * Return the cris serial driver in console device driver callback function + * + * Revision 1.23 2005/01/14 10:12:17 starvik + * KGDB on separate port. + * Console fixes from 2.4. + * + * Revision 1.22 2005/01/11 16:06:13 starvik + * typo + * + * Revision 1.21 2005/01/11 13:49:14 starvik + * Added raw_printk to be used where we don't trust the console. + * + * Revision 1.20 2004/12/27 11:18:32 starvik + * Merge of Linux 2.6.10 (not functional yet). + * * Revision 1.19 2004/10/21 07:26:16 starvik * Made it possible to specify console settings on kernel command line. * @@ -114,7 +139,11 @@ struct dbg_port ports[]= R_SERIAL0_BAUD, R_SERIAL0_TR_CTRL, R_SERIAL0_REC_CTRL, - IO_STATE(R_IRQ_MASK1_SET, ser0_data, set) + IO_STATE(R_IRQ_MASK1_SET, ser0_data, set), + 0, + 115200, + 'N', + 8 }, { 1, @@ -124,7 +153,11 @@ struct dbg_port ports[]= R_SERIAL1_BAUD, R_SERIAL1_TR_CTRL, R_SERIAL1_REC_CTRL, - IO_STATE(R_IRQ_MASK1_SET, ser1_data, set) + IO_STATE(R_IRQ_MASK1_SET, ser1_data, set), + 0, + 115200, + 'N', + 8 }, { 2, @@ -134,7 +167,11 @@ struct dbg_port ports[]= R_SERIAL2_BAUD, R_SERIAL2_TR_CTRL, R_SERIAL2_REC_CTRL, - IO_STATE(R_IRQ_MASK1_SET, ser2_data, set) + IO_STATE(R_IRQ_MASK1_SET, ser2_data, set), + 0, + 115200, + 'N', + 8 }, { 3, @@ -144,11 +181,15 @@ struct dbg_port ports[]= R_SERIAL3_BAUD, R_SERIAL3_TR_CTRL, R_SERIAL3_REC_CTRL, - IO_STATE(R_IRQ_MASK1_SET, ser3_data, set) + IO_STATE(R_IRQ_MASK1_SET, ser3_data, set), + 0, + 115200, + 'N', + 8 } }; -static struct tty_driver *serial_driver; +extern struct tty_driver *serial_driver; struct dbg_port* port = #if defined(CONFIG_ETRAX_DEBUG_PORT0) @@ -162,37 +203,44 @@ struct dbg_port* port = #else NULL; #endif -/* Used by serial.c to register a debug_write_function so that the normal - * serial driver is used for kernel debug output - */ -typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len); -debugport_write_function debug_write_function = NULL; +static struct dbg_port* kgdb_port = +#if defined(CONFIG_ETRAX_KGDB_PORT0) + &ports[0]; +#elif defined(CONFIG_ETRAX_KGDB_PORT1) + &ports[1]; +#elif defined(CONFIG_ETRAX_KGDB_PORT2) + &ports[2]; +#elif defined(CONFIG_ETRAX_KGDB_PORT3) + &ports[3]; +#else + NULL; +#endif static void -start_port(void) +start_port(struct dbg_port* p) { unsigned long rec_ctrl = 0; unsigned long tr_ctrl = 0; - if (!port) + if (!p) return; - if (port->started) + if (p->started) return; - port->started = 1; + p->started = 1; - if (port->index == 0) + if (p->index == 0) { genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6); genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused); } - else if (port->index == 1) + else if (p->index == 1) { genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8); genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb); } - else if (port->index == 2) + else if (p->index == 2) { genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2); genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0); @@ -211,69 +259,69 @@ start_port(void) *R_GEN_CONFIG = genconfig_shadow; - *port->xoff = + *p->xoff = IO_STATE(R_SERIAL0_XOFF, tx_stop, enable) | IO_STATE(R_SERIAL0_XOFF, auto_xoff, disable) | IO_FIELD(R_SERIAL0_XOFF, xoff_char, 0); - switch (port->baudrate) + switch (p->baudrate) { case 0: case 115200: - *port->baud = + *p->baud = IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) | IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz); break; case 1200: - *port->baud = + *p->baud = IO_STATE(R_SERIAL0_BAUD, tr_baud, c1200Hz) | IO_STATE(R_SERIAL0_BAUD, rec_baud, c1200Hz); break; case 2400: - *port->baud = + *p->baud = IO_STATE(R_SERIAL0_BAUD, tr_baud, c2400Hz) | IO_STATE(R_SERIAL0_BAUD, rec_baud, c2400Hz); break; case 4800: - *port->baud = + *p->baud = IO_STATE(R_SERIAL0_BAUD, tr_baud, c4800Hz) | IO_STATE(R_SERIAL0_BAUD, rec_baud, c4800Hz); break; case 9600: - *port->baud = + *p->baud = IO_STATE(R_SERIAL0_BAUD, tr_baud, c9600Hz) | IO_STATE(R_SERIAL0_BAUD, rec_baud, c9600Hz); break; case 19200: - *port->baud = + *p->baud = IO_STATE(R_SERIAL0_BAUD, tr_baud, c19k2Hz) | IO_STATE(R_SERIAL0_BAUD, rec_baud, c19k2Hz); break; case 38400: - *port->baud = + *p->baud = IO_STATE(R_SERIAL0_BAUD, tr_baud, c38k4Hz) | IO_STATE(R_SERIAL0_BAUD, rec_baud, c38k4Hz); break; case 57600: - *port->baud = + *p->baud = IO_STATE(R_SERIAL0_BAUD, tr_baud, c57k6Hz) | IO_STATE(R_SERIAL0_BAUD, rec_baud, c57k6Hz); break; default: - *port->baud = + *p->baud = IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) | IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz); break; } - if (port->parity == 'E') { + if (p->parity == 'E') { rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_par, even) | IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable); tr_ctrl = IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) | IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable); - } else if (port->parity == 'O') { + } else if (p->parity == 'O') { rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd) | IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable); @@ -288,8 +336,7 @@ start_port(void) IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) | IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, disable); } - - if (port->bits == 7) + if (p->bits == 7) { rec_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_7bit); tr_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_7bit); @@ -300,7 +347,7 @@ start_port(void) tr_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_8bit); } - *port->rec_ctrl = + *p->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, dma_err, stop) | IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable) | IO_STATE(R_SERIAL0_REC_CTRL, rts_, active) | @@ -308,7 +355,7 @@ start_port(void) IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, normal) | rec_ctrl; - *port->tr_ctrl = + *p->tr_ctrl = IO_FIELD(R_SERIAL0_TR_CTRL, txd, 0) | IO_STATE(R_SERIAL0_TR_CTRL, tr_enable, enable) | IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, disabled) | @@ -323,8 +370,18 @@ console_write_direct(struct console *co, int i; unsigned long flags; local_irq_save(flags); + + if (!port) + return; + /* Send data */ for (i = 0; i < len; i++) { + /* LF -> CRLF */ + if (buf[i] == '\n') { + while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready))) + ; + *port->write = '\r'; + } /* Wait until transmitter is ready and send.*/ while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready))) ; @@ -333,6 +390,25 @@ console_write_direct(struct console *co, local_irq_restore(flags); } +int raw_printk(const char *fmt, ...) +{ + static char buf[1024]; + int printed_len; + static int first = 1; + if (first) { + /* Force reinitialization of the port to get manual mode. */ + port->started = 0; + start_port(port); + first = 0; + } + va_list args; + va_start(args, fmt); + printed_len = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + console_write_direct(NULL, buf, strlen(buf)); + return printed_len; +} + static void console_write(struct console *co, const char *buf, unsigned int len) { @@ -345,18 +421,7 @@ console_write(struct console *co, const return; #endif - start_port(); - -#ifdef CONFIG_ETRAX_KGDB - /* kgdb needs to output debug info using the gdb protocol */ - putDebugString(buf, len); - return; -#endif - - if (debug_write_function) - debug_write_function(co->index, buf, len); - else - console_write_direct(co, buf, len); + console_write_direct(co, buf, len); } /* legacy function */ @@ -374,8 +439,11 @@ getDebugChar(void) { unsigned long readval; + if (!kgdb_port) + return 0; + do { - readval = *port->read; + readval = *kgdb_port->read; } while (!(readval & IO_MASK(R_SERIAL0_READ, data_avail))); return (readval & IO_MASK(R_SERIAL0_READ, data_in)); @@ -386,9 +454,12 @@ getDebugChar(void) void putDebugChar(int val) { - while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready))) + if (!kgdb_port) + return; + + while (!(*kgdb_port->read & IO_MASK(R_SERIAL0_READ, tr_ready))) ; - *port->write = val; + *kgdb_port->write = val; } /* Enable irq for receiving chars on the debug port, used by kgdb */ @@ -396,19 +467,16 @@ putDebugChar(int val) void enableDebugIRQ(void) { - *R_IRQ_MASK1_SET = port->irq; + if (!kgdb_port) + return; + + *R_IRQ_MASK1_SET = kgdb_port->irq; /* use R_VECT_MASK directly, since we really bypass Linux normal * IRQ handling in kgdb anyway, we don't need to use enable_irq */ *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set); - *port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable); -} - -static struct tty_driver* -etrax_console_device(struct console* co, int *index) -{ - return serial_driver; + *kgdb_port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable); } static int __init @@ -428,11 +496,69 @@ console_setup(struct console *co, char * if (*s) port->parity = *s++; if (*s) port->bits = *s++ - '0'; port->started = 0; - start_port(); + start_port(0); } return 0; } +/* This is a dummy serial device that throws away anything written to it. + * This is used when no debug output is wanted. + */ +static struct tty_driver dummy_driver; + +static int dummy_open(struct tty_struct *tty, struct file * filp) +{ + return 0; +} + +static void dummy_close(struct tty_struct *tty, struct file * filp) +{ +} + +static int dummy_write(struct tty_struct * tty, + const unsigned char *buf, int count) +{ + return count; +} + +static int +dummy_write_room(struct tty_struct *tty) +{ + return 8192; +} + +void __init +init_dummy_console(void) +{ + memset(&dummy_driver, 0, sizeof(struct tty_driver)); + dummy_driver.driver_name = "serial"; + dummy_driver.name = "ttyS"; + dummy_driver.major = TTY_MAJOR; + dummy_driver.minor_start = 68; + dummy_driver.num = 1; /* etrax100 has 4 serial ports */ + dummy_driver.type = TTY_DRIVER_TYPE_SERIAL; + dummy_driver.subtype = SERIAL_TYPE_NORMAL; + dummy_driver.init_termios = tty_std_termios; + dummy_driver.init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ + dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + + dummy_driver.open = dummy_open; + dummy_driver.close = dummy_close; + dummy_driver.write = dummy_write; + dummy_driver.write_room = dummy_write_room; + if (tty_register_driver(&dummy_driver)) + panic("Couldn't register dummy serial driver\n"); +} + +static struct tty_driver* +etrax_console_device(struct console* co, int *index) +{ + if (port) + *index = port->index; + return port ? serial_driver : &dummy_driver; +} + static struct console sercons = { name : "ttyS", write: console_write, @@ -504,28 +630,21 @@ init_etrax_debug(void) static int first = 1; if (!first) { - if (!port) { - register_console(&sercons0); - register_console(&sercons1); - register_console(&sercons2); - register_console(&sercons3); - unregister_console(&sercons); - } + unregister_console(&sercons); + register_console(&sercons0); + register_console(&sercons1); + register_console(&sercons2); + register_console(&sercons3); + init_dummy_console(); return 0; } - first = 0; - if (port) - register_console(&sercons); - return 0; -} -int __init -init_console(void) -{ - serial_driver = alloc_tty_driver(1); - if (!serial_driver) - return -ENOMEM; + first = 0; + register_console(&sercons); + start_port(port); +#ifdef CONFIG_ETRAX_KGDB + start_port(kgdb_port); +#endif return 0; } - __initcall(init_etrax_debug); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v10/kernel/dma.c 2005-07-12 00:26:59.000000000 -0700 @@ -0,0 +1,287 @@ +/* Wrapper for DMA channel allocator that updates DMA client muxing. + * Copyright 2004, Axis Communications AB + * $Id: dma.c,v 1.1 2004/12/13 12:21:51 starvik Exp $ + */ + +#include +#include +#include + +#include +#include + +/* Macro to access ETRAX 100 registers */ +#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_STATE_(reg##_, field##_, _##val) + + +static char used_dma_channels[MAX_DMA_CHANNELS]; +static const char * used_dma_channels_users[MAX_DMA_CHANNELS]; + +int cris_request_dma(unsigned int dmanr, const char * device_id, + unsigned options, enum dma_owner owner) +{ + unsigned long flags; + unsigned long int gens; + int fail = -EINVAL; + + if ((dmanr < 0) || (dmanr >= MAX_DMA_CHANNELS)) { + printk(KERN_CRIT "cris_request_dma: invalid DMA channel %u\n", dmanr); + return -EINVAL; + } + + local_irq_save(flags); + if (used_dma_channels[dmanr]) { + local_irq_restore(flags); + if (options & DMA_VERBOSE_ON_ERROR) { + printk(KERN_CRIT "Failed to request DMA %i for %s, already allocated by %s\n", dmanr, device_id, used_dma_channels_users[dmanr]); + } + if (options & DMA_PANIC_ON_ERROR) { + panic("request_dma error!"); + } + return -EBUSY; + } + + gens = genconfig_shadow; + + switch(owner) + { + case dma_eth: + if ((dmanr != NETWORK_TX_DMA_NBR) && + (dmanr != NETWORK_RX_DMA_NBR)) { + printk(KERN_CRIT "Invalid DMA channel for eth\n"); + goto bail; + } + break; + case dma_ser0: + if (dmanr == SER0_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma6, serial0); + } else if (dmanr == SER0_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma7, serial0); + } else { + printk(KERN_CRIT "Invalid DMA channel for ser0\n"); + goto bail; + } + break; + case dma_ser1: + if (dmanr == SER1_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma8, serial1); + } else if (dmanr == SER1_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma9, serial1); + } else { + printk(KERN_CRIT "Invalid DMA channel for ser1\n"); + goto bail; + } + break; + case dma_ser2: + if (dmanr == SER2_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma2, serial2); + } else if (dmanr == SER2_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma3, serial2); + } else { + printk(KERN_CRIT "Invalid DMA channel for ser2\n"); + goto bail; + } + break; + case dma_ser3: + if (dmanr == SER3_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma4, serial3); + } else if (dmanr == SER3_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma5, serial3); + } else { + printk(KERN_CRIT "Invalid DMA channel for ser3\n"); + goto bail; + } + break; + case dma_ata: + if (dmanr == ATA_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma2, ata); + } else if (dmanr == ATA_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma3, ata); + } else { + printk(KERN_CRIT "Invalid DMA channel for ata\n"); + goto bail; + } + break; + case dma_ext0: + if (dmanr == EXTDMA0_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma4, extdma0); + } else if (dmanr == EXTDMA0_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma5, extdma0); + } else { + printk(KERN_CRIT "Invalid DMA channel for ext0\n"); + goto bail; + } + break; + case dma_ext1: + if (dmanr == EXTDMA1_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma6, extdma1); + } else if (dmanr == EXTDMA1_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma7, extdma1); + } else { + printk(KERN_CRIT "Invalid DMA channel for ext1\n"); + goto bail; + } + break; + case dma_int6: + if (dmanr == MEM2MEM_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma7, intdma6); + } else { + printk(KERN_CRIT "Invalid DMA channel for int6\n"); + goto bail; + } + break; + case dma_int7: + if (dmanr == MEM2MEM_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma6, intdma7); + } else { + printk(KERN_CRIT "Invalid DMA channel for int7\n"); + goto bail; + } + break; + case dma_usb: + if (dmanr == USB_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma8, usb); + } else if (dmanr == USB_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma9, usb); + } else { + printk(KERN_CRIT "Invalid DMA channel for usb\n"); + goto bail; + } + break; + case dma_scsi0: + if (dmanr == SCSI0_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma2, scsi0); + } else if (dmanr == SCSI0_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma3, scsi0); + } else { + printk(KERN_CRIT "Invalid DMA channel for scsi0\n"); + goto bail; + } + break; + case dma_scsi1: + if (dmanr == SCSI1_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma4, scsi1); + } else if (dmanr == SCSI1_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma5, scsi1); + } else { + printk(KERN_CRIT "Invalid DMA channel for scsi1\n"); + goto bail; + } + break; + case dma_par0: + if (dmanr == PAR0_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma2, par0); + } else if (dmanr == PAR0_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma3, par0); + } else { + printk(KERN_CRIT "Invalid DMA channel for par0\n"); + goto bail; + } + break; + case dma_par1: + if (dmanr == PAR1_TX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma4, par1); + } else if (dmanr == PAR1_RX_DMA_NBR) { + SETS(gens, R_GEN_CONFIG, dma5, par1); + } else { + printk(KERN_CRIT "Invalid DMA channel for par1\n"); + goto bail; + } + break; + default: + printk(KERN_CRIT "Invalid DMA owner.\n"); + goto bail; + } + + used_dma_channels[dmanr] = 1; + used_dma_channels_users[dmanr] = device_id; + + { + volatile int i; + genconfig_shadow = gens; + *R_GEN_CONFIG = genconfig_shadow; + /* Wait 12 cycles before doing any DMA command */ + for(i = 6; i > 0; i--) + nop(); + } + fail = 0; + bail: + local_irq_restore(flags); + return fail; +} + +void cris_free_dma(unsigned int dmanr, const char * device_id) +{ + unsigned long flags; + if ((dmanr < 0) || (dmanr >= MAX_DMA_CHANNELS)) { + printk(KERN_CRIT "cris_free_dma: invalid DMA channel %u\n", dmanr); + return; + } + + local_irq_save(flags); + if (!used_dma_channels[dmanr]) { + printk(KERN_CRIT "cris_free_dma: DMA channel %u not allocated\n", dmanr); + } else if (device_id != used_dma_channels_users[dmanr]) { + printk(KERN_CRIT "cris_free_dma: DMA channel %u not allocated by device\n", dmanr); + } else { + switch(dmanr) + { + case 0: + *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH0_CMD, cmd, *R_DMA_CH0_CMD) == + IO_STATE_VALUE(R_DMA_CH0_CMD, cmd, reset)); + break; + case 1: + *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH1_CMD, cmd, *R_DMA_CH1_CMD) == + IO_STATE_VALUE(R_DMA_CH1_CMD, cmd, reset)); + break; + case 2: + *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH2_CMD, cmd, *R_DMA_CH2_CMD) == + IO_STATE_VALUE(R_DMA_CH2_CMD, cmd, reset)); + break; + case 3: + *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH3_CMD, cmd, *R_DMA_CH3_CMD) == + IO_STATE_VALUE(R_DMA_CH3_CMD, cmd, reset)); + break; + case 4: + *R_DMA_CH4_CMD = IO_STATE(R_DMA_CH4_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH4_CMD, cmd, *R_DMA_CH4_CMD) == + IO_STATE_VALUE(R_DMA_CH4_CMD, cmd, reset)); + break; + case 5: + *R_DMA_CH5_CMD = IO_STATE(R_DMA_CH5_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH5_CMD, cmd, *R_DMA_CH5_CMD) == + IO_STATE_VALUE(R_DMA_CH5_CMD, cmd, reset)); + break; + case 6: + *R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *R_DMA_CH6_CMD) == + IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); + break; + case 7: + *R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH7_CMD, cmd, *R_DMA_CH7_CMD) == + IO_STATE_VALUE(R_DMA_CH7_CMD, cmd, reset)); + break; + case 8: + *R_DMA_CH8_CMD = IO_STATE(R_DMA_CH8_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH8_CMD, cmd, *R_DMA_CH8_CMD) == + IO_STATE_VALUE(R_DMA_CH8_CMD, cmd, reset)); + break; + case 9: + *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH9_CMD, cmd, *R_DMA_CH9_CMD) == + IO_STATE_VALUE(R_DMA_CH9_CMD, cmd, reset)); + break; + } + used_dma_channels[dmanr] = 0; + } + local_irq_restore(flags); +} + +EXPORT_SYMBOL(cris_request_dma); +EXPORT_SYMBOL(cris_free_dma); --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/entry.S 2005-03-01 23:38:07.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/entry.S 2005-07-12 00:26:59.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.23 2004/10/19 13:07:37 starvik Exp $ +/* $Id: entry.S,v 1.28 2005/06/20 05:06:30 starvik Exp $ * * linux/arch/cris/entry.S * @@ -7,6 +7,22 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: entry.S,v $ + * Revision 1.28 2005/06/20 05:06:30 starvik + * Remove unnecessary diff to kernel.org tree + * + * Revision 1.27 2005/03/04 08:16:16 starvik + * Merge of Linux 2.6.11. + * + * Revision 1.26 2005/01/11 13:49:47 starvik + * Added NMI handler. + * + * Revision 1.25 2004/12/27 11:18:32 starvik + * Merge of Linux 2.6.10 (not functional yet). + * + * Revision 1.24 2004/12/22 10:41:23 starvik + * Updates to make v10 compile with the latest SMP aware generic code (even + * though v10 will never have SMP). + * * Revision 1.23 2004/10/19 13:07:37 starvik * Merge of Linux 2.6.9 * @@ -279,6 +295,7 @@ #ifdef CONFIG_PREEMPT ; Check if preemptive kernel scheduling should be done _resume_kernel: + di ; Load current task struct movs.w -8192, $r0 ; THREAD_SIZE = 8192 and.d $sp, $r0 @@ -291,12 +308,7 @@ _need_resched: bpl _Rexit nop ; Ok, lets's do some preemptive kernel scheduling - move.d PREEMPT_ACTIVE, $r10 - move.d $r10, [$r0+TI_preempt_count] ; Mark as active - ei - jsr schedule - clear.d [$r0+TI_preempt_count] ; Mark as inactive - di + jsr preempt_schedule_irq ; Load new task struct movs.w -8192, $r0 ; THREAD_SIZE = 8192 and.d $sp, $r0 @@ -590,15 +602,15 @@ mmu_bus_fault: move.d $r0, [$sp+16] 1: btstq 12, $r1 ; Refill? bpl 2f - lsrq PMD_SHIFT, $r1 ; Get PMD index into PGD (bit 24-31) - move.d [current_pgd], $r0 ; PGD for the current process + lsrq 24, $r1 ; Get PGD index (bit 24-31) + move.d [per_cpu__current_pgd], $r0 ; PGD for the current process move.d [$r0+$r1.d], $r0 ; Get PMD beq 2f nop and.w PAGE_MASK, $r0 ; Remove PMD flags move.d [R_MMU_CAUSE], $r1 lsrq PAGE_SHIFT, $r1 - and.d 0x7ff, $r1 ; Get PTE index into PMD (bit 13-24) + and.d 0x7ff, $r1 ; Get PTE index into PGD (bit 13-23) move.d [$r0+$r1.d], $r1 ; Get PTE beq 2f nop @@ -656,11 +668,6 @@ hwbreakpoint: nop IRQ1_interrupt: - -#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) -;; If we receive a watchdog interrupt while it is not expected, then set -;; up a canonical frame and dump register contents before dying. - ;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!! move $brp,[$sp=$sp-16]; instruction pointer and room for a fake SBFS frame push $srp @@ -672,9 +679,16 @@ IRQ1_interrupt: push $r10 ; push orig_r10 clear.d [$sp=$sp-4] ; frametype == 0, normal frame -;; We don't check that we actually were bit by the watchdog as opposed to -;; an external NMI, since there is currently no handler for external NMI. - + move.d [R_IRQ_MASK0_RD], $r1 ; External NMI or watchdog? + and.d 0x80000000, $r1 + beq wdog + move.d $sp, $r10 + jsr handle_nmi + setf m ; Enable NMI again + retb ; Return from NMI + nop +wdog: +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) ;; Check if we're waiting for reset to happen, as signalled by ;; hard_reset_now setting cause_of_death to a magic value. If so, just ;; get stuck until reset happens. @@ -1118,6 +1132,10 @@ sys_call_table: .long sys_mq_getsetattr .long sys_ni_syscall /* reserved for kexec */ .long sys_waitid + .long sys_ni_syscall /* 285 */ /* available */ + .long sys_add_key + .long sys_request_key + .long sys_keyctl /* * NOTE!! This doesn't have to be exact - we just have --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/fasttimer.c 2005-03-01 23:38:37.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/fasttimer.c 2005-07-12 00:26:59.000000000 -0700 @@ -1,10 +1,20 @@ -/* $Id: fasttimer.c,v 1.6 2004/05/14 10:18:39 starvik Exp $ +/* $Id: fasttimer.c,v 1.9 2005/03/04 08:16:16 starvik Exp $ * linux/arch/cris/kernel/fasttimer.c * * Fast timers for ETRAX100/ETRAX100LX * This may be useful in other OS than Linux so use 2 space indentation... * * $Log: fasttimer.c,v $ + * Revision 1.9 2005/03/04 08:16:16 starvik + * Merge of Linux 2.6.11. + * + * Revision 1.8 2005/01/05 06:09:29 starvik + * cli()/sti() will be obsolete in 2.6.11. + * + * Revision 1.7 2005/01/03 13:35:46 starvik + * Removed obsolete stuff. + * Mark fast timer IRQ as not shared. + * * Revision 1.6 2004/05/14 10:18:39 starvik * Export fast_timer_list * @@ -148,8 +158,7 @@ static int debug_log_cnt_wrapped = 0; #define DEBUG_LOG(string, value) \ { \ unsigned long log_flags; \ - save_flags(log_flags); \ - cli(); \ + local_irq_save(log_flags); \ debug_log_string[debug_log_cnt] = (string); \ debug_log_value[debug_log_cnt] = (unsigned long)(value); \ if (++debug_log_cnt >= DEBUG_LOG_MAX) \ @@ -157,7 +166,7 @@ static int debug_log_cnt_wrapped = 0; debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \ debug_log_cnt_wrapped = 1; \ } \ - restore_flags(log_flags); \ + local_irq_restore(log_flags); \ } #else #define DEBUG_LOG(string, value) @@ -320,8 +329,7 @@ void start_one_shot_timer(struct fast_ti D1(printk("sft %s %d us\n", name, delay_us)); - save_flags(flags); - cli(); + local_irq_save(flags); do_gettimeofday_fast(&t->tv_set); tmp = fast_timer_list; @@ -395,7 +403,7 @@ void start_one_shot_timer(struct fast_ti D2(printk("start_one_shot_timer: %d us done\n", delay_us)); - restore_flags(flags); + local_irq_restore(flags); } /* start_one_shot_timer */ static inline int fast_timer_pending (const struct fast_timer * t) @@ -425,11 +433,10 @@ int del_fast_timer(struct fast_timer * t unsigned long flags; int ret; - save_flags(flags); - cli(); + local_irq_save(flags); ret = detach_fast_timer(t); t->next = t->prev = NULL; - restore_flags(flags); + local_irq_restore(flags); return ret; } /* del_fast_timer */ @@ -444,8 +451,7 @@ timer1_handler(int irq, void *dev_id, st struct fast_timer *t; unsigned long flags; - save_flags(flags); - cli(); + local_irq_save(flags); /* Clear timer1 irq */ *R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, timer1, clr); @@ -462,7 +468,7 @@ timer1_handler(int irq, void *dev_id, st fast_timer_running = 0; fast_timer_ints++; - restore_flags(flags); + local_irq_restore(flags); t = fast_timer_list; while (t) @@ -482,8 +488,7 @@ timer1_handler(int irq, void *dev_id, st fast_timers_expired++; /* Remove this timer before call, since it may reuse the timer */ - save_flags(flags); - cli(); + local_irq_save(flags); if (t->prev) { t->prev->next = t->next; @@ -498,7 +503,7 @@ timer1_handler(int irq, void *dev_id, st } t->prev = NULL; t->next = NULL; - restore_flags(flags); + local_irq_restore(flags); if (t->function != NULL) { @@ -515,8 +520,7 @@ timer1_handler(int irq, void *dev_id, st D1(printk(".\n")); } - save_flags(flags); - cli(); + local_irq_save(flags); if ((t = fast_timer_list) != NULL) { /* Start next timer.. */ @@ -535,7 +539,7 @@ timer1_handler(int irq, void *dev_id, st #endif start_timer1(us); } - restore_flags(flags); + local_irq_restore(flags); break; } else @@ -546,7 +550,7 @@ timer1_handler(int irq, void *dev_id, st D1(printk("e! %d\n", us)); } } - restore_flags(flags); + local_irq_restore(flags); } if (!t) @@ -748,13 +752,12 @@ static int proc_fasttimer_read(char *buf #endif used += sprintf(bigbuf + used, "Active timers:\n"); - save_flags(flags); - cli(); + local_irq_save(flags); t = fast_timer_list; while (t != NULL && (used+100 < BIG_BUF_SIZE)) { nextt = t->next; - restore_flags(flags); + local_irq_restore(flags); used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " "d: %6li us data: 0x%08lX" /* " func: 0x%08lX" */ @@ -768,14 +771,14 @@ static int proc_fasttimer_read(char *buf t->data /* , t->function */ ); - cli(); + local_irq_disable(); if (t->next != nextt) { printk(KERN_WARNING "timer removed!\n"); } t = nextt; } - restore_flags(flags); + local_irq_restore(flags); } if (used - offset < len) @@ -963,7 +966,7 @@ void fast_timer_init(void) if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 ))) fasttimer_proc_entry->read_proc = proc_fasttimer_read; #endif /* PROC_FS */ - if(request_irq(TIMER1_IRQ_NBR, timer1_handler, SA_SHIRQ, + if(request_irq(TIMER1_IRQ_NBR, timer1_handler, 0, "fast timer int", NULL)) { printk("err: timer1 irq\n"); --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/head.S 2005-03-01 23:38:26.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/head.S 2005-07-12 00:26:59.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.7 2004/05/14 07:58:01 starvik Exp $ +/* $Id: head.S,v 1.10 2005/06/20 05:12:54 starvik Exp $ * * Head of the kernel - alter with care * @@ -7,6 +7,16 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: head.S,v $ + * Revision 1.10 2005/06/20 05:12:54 starvik + * Remove unnecessary diff to kernel.org tree + * + * Revision 1.9 2004/12/13 12:21:51 starvik + * Added I/O and DMA allocators from Linux 2.4 + * + * Revision 1.8 2004/11/22 11:41:14 starvik + * Kernel command line may be supplied to kernel. Not used by Axis but may + * be used by customers. + * * Revision 1.7 2004/05/14 07:58:01 starvik * Merge of changes from 2.4 * @@ -181,6 +191,7 @@ #define CRAMFS_MAGIC 0x28cd3d45 #define RAM_INIT_MAGIC 0x56902387 +#define COMMAND_LINE_MAGIC 0x87109563 #define START_ETHERNET_CLOCK IO_STATE(R_NETWORK_GEN_CONFIG, enable, on) |\ IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) @@ -490,6 +501,23 @@ _no_romfs_in_flash: _start_it: + ;; Check if kernel command line is supplied + cmp.d COMMAND_LINE_MAGIC, $r10 + bne no_command_line + nop + + move.d 256, $r13 + move.d cris_command_line, $r10 + or.d 0x80000000, $r11 ; Make it virtual +1: + move.b [$r11+], $r12 + move.b $r12, [$r10+] + subq 1, $r13 + bne 1b + nop + +no_command_line: + ;; the kernel stack is overlayed with the task structure for each ;; task. thus the initial kernel stack is in the same page as the ;; init_task (but starts in the top of the page, size 8192) @@ -567,76 +595,32 @@ _start_it: ;; Etrax product HW genconfig setup moveq 0,$r0 -#if (!defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT0)) \ - && !defined(CONFIG_DMA_MEMCPY) - ; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA - or.d IO_STATE (R_GEN_CONFIG, dma7, serial0) \ - | IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0 -#endif -#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT1) - ; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA - or.d IO_STATE (R_GEN_CONFIG, dma9, serial1) \ - | IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0 -#endif -#ifdef CONFIG_DMA_MEMCPY - ; 6/7 memory-memory DMA - or.d IO_STATE (R_GEN_CONFIG, dma7, intdma6) \ - | IO_STATE (R_GEN_CONFIG, dma6, intdma7),$r0 -#endif -#ifdef CONFIG_ETRAX_SERIAL_PORT2 - ; Enable serial port 2 - or.w IO_STATE (R_GEN_CONFIG, ser2, select),$r0 -#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT2) - ; DMA channels 2 and 3 to ser2, kgdb doesnt want DMA - or.d IO_STATE (R_GEN_CONFIG, dma3, serial2) \ - | IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0 -#endif -#endif -#if defined(CONFIG_ETRAX_SERIAL_PORT3) || defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) - ; Enable serial port 3 - or.w IO_STATE (R_GEN_CONFIG, ser3, select),$r0 -#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT3) - ; DMA channels 4 and 5 to ser3, kgdb doesnt want DMA - or.d IO_STATE (R_GEN_CONFIG, dma5, serial3) \ - | IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0 -#endif -#endif -#if defined(CONFIG_ETRAX_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE) - ; parport 0 enabled using DMA 2/3 - or.w IO_STATE (R_GEN_CONFIG, par0, select),$r0 -#endif -#if defined(CONFIG_ETRAX_PARALLEL_PORT1) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE) - ; parport 1 enabled using DMA 4/5 - or.w IO_STATE (R_GEN_CONFIG, par1, select),$r0 -#endif -#ifdef CONFIG_ETRAX_IDE - ; DMA channels 2 and 3 to ATA, ATA enabled - or.d IO_STATE (R_GEN_CONFIG, dma3, ata) \ - | IO_STATE (R_GEN_CONFIG, dma2, ata) \ - | IO_STATE (R_GEN_CONFIG, ata, select),$r0 -#endif - -#ifdef CONFIG_ETRAX_USB_HOST_PORT1 - ; Set the USB port 1 enable bit - or.d IO_STATE (R_GEN_CONFIG, usb1, select),$r0 -#endif -#ifdef CONFIG_ETRAX_USB_HOST_PORT2 - ; Set the USB port 2 enable bit - or.d IO_STATE (R_GEN_CONFIG, usb2, select),$r0 -#endif -#ifdef CONFIG_ETRAX_USB_HOST - ; Connect DMA channels 8 and 9 to USB - and.d (~(IO_MASK (R_GEN_CONFIG, dma9) \ - | IO_MASK (R_GEN_CONFIG, dma8))) \ - | IO_STATE (R_GEN_CONFIG, dma9, usb) \ - | IO_STATE (R_GEN_CONFIG, dma8, usb),$r0 -#endif - -#ifdef CONFIG_JULIETTE - ; DMA channels 4 and 5 to EXTDMA0, for Juliette - or.d IO_STATE (R_GEN_CONFIG, dma5, extdma0) \ - | IO_STATE (R_GEN_CONFIG, dma4, extdma0),$r0 -#endif + + ;; Init interfaces (disable them). + or.d IO_STATE (R_GEN_CONFIG, scsi0, disable) \ + | IO_STATE (R_GEN_CONFIG, ata, disable) \ + | IO_STATE (R_GEN_CONFIG, par0, disable) \ + | IO_STATE (R_GEN_CONFIG, ser2, disable) \ + | IO_STATE (R_GEN_CONFIG, mio, disable) \ + | IO_STATE (R_GEN_CONFIG, scsi1, disable) \ + | IO_STATE (R_GEN_CONFIG, scsi0w, disable) \ + | IO_STATE (R_GEN_CONFIG, par1, disable) \ + | IO_STATE (R_GEN_CONFIG, ser3, disable) \ + | IO_STATE (R_GEN_CONFIG, mio_w, disable) \ + | IO_STATE (R_GEN_CONFIG, usb1, disable) \ + | IO_STATE (R_GEN_CONFIG, usb2, disable) \ + | IO_STATE (R_GEN_CONFIG, par_w, disable),$r0 + + ;; Init DMA channel muxing (set to unused clients). + or.d IO_STATE (R_GEN_CONFIG, dma2, ata) \ + | IO_STATE (R_GEN_CONFIG, dma3, ata) \ + | IO_STATE (R_GEN_CONFIG, dma4, scsi1) \ + | IO_STATE (R_GEN_CONFIG, dma5, scsi1) \ + | IO_STATE (R_GEN_CONFIG, dma6, unused) \ + | IO_STATE (R_GEN_CONFIG, dma7, unused) \ + | IO_STATE (R_GEN_CONFIG, dma8, usb) \ + | IO_STATE (R_GEN_CONFIG, dma9, usb),$r0 + #if defined(CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT) or.d IO_STATE (R_GEN_CONFIG, g0dir, out),$r0 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v10/kernel/io_interface_mux.c 2005-07-12 00:26:59.000000000 -0700 @@ -0,0 +1,879 @@ +/* IO interface mux allocator for ETRAX100LX. + * Copyright 2004, Axis Communications AB + * $Id: io_interface_mux.c,v 1.2 2004/12/21 12:08:38 starvik Exp $ + */ + + +/* C.f. ETRAX100LX Designer's Reference 20.9 */ + +#include +#include +#include +#include +#include + +#include +#include +#include + + +#define DBG(s) + +/* Macro to access ETRAX 100 registers */ +#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_STATE_(reg##_, field##_, _##val) + +enum io_if_group { + group_a = (1<<0), + group_b = (1<<1), + group_c = (1<<2), + group_d = (1<<3), + group_e = (1<<4), + group_f = (1<<5) +}; + +struct watcher +{ + void (*notify)(const unsigned int gpio_in_available, + const unsigned int gpio_out_available, + const unsigned char pa_available, + const unsigned char pb_available); + struct watcher *next; +}; + + +struct if_group +{ + enum io_if_group group; + unsigned char used; + enum cris_io_interface owner; +}; + + +struct interface +{ + enum cris_io_interface ioif; + unsigned char groups; + unsigned char used; + char *owner; + unsigned int gpio_g_in; + unsigned int gpio_g_out; + unsigned char gpio_b; +}; + +static struct if_group if_groups[6] = { + { + .group = group_a, + .used = 0, + }, + { + .group = group_b, + .used = 0, + }, + { + .group = group_c, + .used = 0, + }, + { + .group = group_d, + .used = 0, + }, + { + .group = group_e, + .used = 0, + }, + { + .group = group_f, + .used = 0, + } +}; + +/* The order in the array must match the order of enum + * cris_io_interface in io_interface_mux.h */ +static struct interface interfaces[] = { + /* Begin Non-multiplexed interfaces */ + { + .ioif = if_eth, + .groups = 0, + .gpio_g_in = 0, + .gpio_g_out = 0, + .gpio_b = 0 + }, + { + .ioif = if_serial_0, + .groups = 0, + .gpio_g_in = 0, + .gpio_g_out = 0, + .gpio_b = 0 + }, + /* End Non-multiplexed interfaces */ + { + .ioif = if_serial_1, + .groups = group_e, + .gpio_g_in = 0x00000000, + .gpio_g_out = 0x00000000, + .gpio_b = 0x00 + }, + { + .ioif = if_serial_2, + .groups = group_b, + .gpio_g_in = 0x000000c0, + .gpio_g_out = 0x000000c0, + .gpio_b = 0x00 + }, + { + .ioif = if_serial_3, + .groups = group_c, + .gpio_g_in = 0xc0000000, + .gpio_g_out = 0xc0000000, + .gpio_b = 0x00 + }, + { + .ioif = if_sync_serial_1, + .groups = group_e | group_f, /* if_sync_serial_1 and if_sync_serial_3 + can be used simultaneously */ + .gpio_g_in = 0x00000000, + .gpio_g_out = 0x00000000, + .gpio_b = 0x10 + }, + { + .ioif = if_sync_serial_3, + .groups = group_c | group_f, + .gpio_g_in = 0xc0000000, + .gpio_g_out = 0xc0000000, + .gpio_b = 0x80 + }, + { + .ioif = if_shared_ram, + .groups = group_a, + .gpio_g_in = 0x0000ff3e, + .gpio_g_out = 0x0000ff38, + .gpio_b = 0x00 + }, + { + .ioif = if_shared_ram_w, + .groups = group_a | group_d, + .gpio_g_in = 0x00ffff3e, + .gpio_g_out = 0x00ffff38, + .gpio_b = 0x00 + }, + { + .ioif = if_par_0, + .groups = group_a, + .gpio_g_in = 0x0000ff3e, + .gpio_g_out = 0x0000ff3e, + .gpio_b = 0x00 + }, + { + .ioif = if_par_1, + .groups = group_d, + .gpio_g_in = 0x3eff0000, + .gpio_g_out = 0x3eff0000, + .gpio_b = 0x00 + }, + { + .ioif = if_par_w, + .groups = group_a | group_d, + .gpio_g_in = 0x00ffff3e, + .gpio_g_out = 0x00ffff3e, + .gpio_b = 0x00 + }, + { + .ioif = if_scsi8_0, + .groups = group_a | group_b | group_f, /* if_scsi8_0 and if_scsi8_1 + can be used simultaneously */ + .gpio_g_in = 0x0000ffff, + .gpio_g_out = 0x0000ffff, + .gpio_b = 0x10 + }, + { + .ioif = if_scsi8_1, + .groups = group_c | group_d | group_f, /* if_scsi8_0 and if_scsi8_1 + can be used simultaneously */ + .gpio_g_in = 0xffff0000, + .gpio_g_out = 0xffff0000, + .gpio_b = 0x80 + }, + { + .ioif = if_scsi_w, + .groups = group_a | group_b | group_d | group_f, + .gpio_g_in = 0x01ffffff, + .gpio_g_out = 0x07ffffff, + .gpio_b = 0x80 + }, + { + .ioif = if_ata, + .groups = group_a | group_b | group_c | group_d, + .gpio_g_in = 0xf9ffffff, + .gpio_g_out = 0xffffffff, + .gpio_b = 0x80 + }, + { + .ioif = if_csp, + .groups = group_f, /* if_csp and if_i2c can be used simultaneously */ + .gpio_g_in = 0x00000000, + .gpio_g_out = 0x00000000, + .gpio_b = 0xfc + }, + { + .ioif = if_i2c, + .groups = group_f, /* if_csp and if_i2c can be used simultaneously */ + .gpio_g_in = 0x00000000, + .gpio_g_out = 0x00000000, + .gpio_b = 0x03 + }, + { + .ioif = if_usb_1, + .groups = group_e | group_f, + .gpio_g_in = 0x00000000, + .gpio_g_out = 0x00000000, + .gpio_b = 0x2c + }, + { + .ioif = if_usb_2, + .groups = group_d, + .gpio_g_in = 0x0e000000, + .gpio_g_out = 0x3c000000, + .gpio_b = 0x00 + }, + /* GPIO pins */ + { + .ioif = if_gpio_grp_a, + .groups = group_a, + .gpio_g_in = 0x0000ff3f, + .gpio_g_out = 0x0000ff3f, + .gpio_b = 0x00 + }, + { + .ioif = if_gpio_grp_b, + .groups = group_b, + .gpio_g_in = 0x000000c0, + .gpio_g_out = 0x000000c0, + .gpio_b = 0x00 + }, + { + .ioif = if_gpio_grp_c, + .groups = group_c, + .gpio_g_in = 0xc0000000, + .gpio_g_out = 0xc0000000, + .gpio_b = 0x00 + }, + { + .ioif = if_gpio_grp_d, + .groups = group_d, + .gpio_g_in = 0x3fff0000, + .gpio_g_out = 0x3fff0000, + .gpio_b = 0x00 + }, + { + .ioif = if_gpio_grp_e, + .groups = group_e, + .gpio_g_in = 0x00000000, + .gpio_g_out = 0x00000000, + .gpio_b = 0x00 + }, + { + .ioif = if_gpio_grp_f, + .groups = group_f, + .gpio_g_in = 0x00000000, + .gpio_g_out = 0x00000000, + .gpio_b = 0xff + } + /* Array end */ +}; + +static struct watcher *watchers = NULL; + +static unsigned int gpio_in_pins = 0xffffffff; +static unsigned int gpio_out_pins = 0xffffffff; +static unsigned char gpio_pb_pins = 0xff; +static unsigned char gpio_pa_pins = 0xff; + +static enum cris_io_interface gpio_pa_owners[8]; +static enum cris_io_interface gpio_pb_owners[8]; +static enum cris_io_interface gpio_pg_owners[32]; + +static int cris_io_interface_init(void); + +static unsigned char clear_group_from_set(const unsigned char groups, struct if_group *group) +{ + return (groups & ~group->group); +} + + +static struct if_group *get_group(const unsigned char groups) +{ + int i; + for (i = 0; i < sizeof(if_groups)/sizeof(struct if_group); i++) { + if (groups & if_groups[i].group) { + return &if_groups[i]; + } + } + return NULL; +} + + +static void notify_watchers(void) +{ + struct watcher *w = watchers; + + DBG(printk("io_interface_mux: notifying watchers\n")); + + while (NULL != w) { + w->notify((const unsigned int)gpio_in_pins, + (const unsigned int)gpio_out_pins, + (const unsigned char)gpio_pa_pins, + (const unsigned char)gpio_pb_pins); + w = w->next; + } +} + + +int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id) +{ + int set_gen_config = 0; + int set_gen_config_ii = 0; + unsigned long int gens; + unsigned long int gens_ii; + struct if_group *grp; + unsigned char group_set; + unsigned long flags; + + (void)cris_io_interface_init(); + + DBG(printk("cris_request_io_interface(%d, \"%s\")\n", ioif, device_id)); + + if ((ioif >= if_max_interfaces) || (ioif < 0)) { + printk(KERN_CRIT "cris_request_io_interface: Bad interface %u submitted for %s\n", + ioif, + device_id); + return -EINVAL; + } + + local_irq_save(flags); + + if (interfaces[ioif].used) { + local_irq_restore(flags); + printk(KERN_CRIT "cris_io_interface: Cannot allocate interface for %s, in use by %s\n", + device_id, + interfaces[ioif].owner); + return -EBUSY; + } + + /* Check that all required groups are free before allocating, */ + group_set = interfaces[ioif].groups; + while (NULL != (grp = get_group(group_set))) { + if (grp->used) { + if (grp->group == group_f) { + if ((if_sync_serial_1 == ioif) || + (if_sync_serial_3 == ioif)) { + if ((grp->owner != if_sync_serial_1) && + (grp->owner != if_sync_serial_3)) { + local_irq_restore(flags); + return -EBUSY; + } + } else if ((if_scsi8_0 == ioif) || + (if_scsi8_1 == ioif)) { + if ((grp->owner != if_scsi8_0) && + (grp->owner != if_scsi8_1)) { + local_irq_restore(flags); + return -EBUSY; + } + } + } else { + local_irq_restore(flags); + return -EBUSY; + } + } + group_set = clear_group_from_set(group_set, grp); + } + + /* Are the required GPIO pins available too? */ + if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != interfaces[ioif].gpio_g_in) || + ((interfaces[ioif].gpio_g_out & gpio_out_pins) != interfaces[ioif].gpio_g_out) || + ((interfaces[ioif].gpio_b & gpio_pb_pins) != interfaces[ioif].gpio_b)) { + printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %u\n", + ioif); + return -EBUSY; + } + + /* All needed I/O pins and pin groups are free, allocate. */ + group_set = interfaces[ioif].groups; + while (NULL != (grp = get_group(group_set))) { + grp->used = 1; + grp->owner = ioif; + group_set = clear_group_from_set(group_set, grp); + } + + gens = genconfig_shadow; + gens_ii = gen_config_ii_shadow; + + set_gen_config = 1; + switch (ioif) + { + /* Begin Non-multiplexed interfaces */ + case if_eth: + /* fall through */ + case if_serial_0: + set_gen_config = 0; + break; + /* End Non-multiplexed interfaces */ + case if_serial_1: + set_gen_config_ii = 1; + SETS(gens_ii, R_GEN_CONFIG_II, sermode1, async); + break; + case if_serial_2: + SETS(gens, R_GEN_CONFIG, ser2, select); + break; + case if_serial_3: + SETS(gens, R_GEN_CONFIG, ser3, select); + set_gen_config_ii = 1; + SETS(gens_ii, R_GEN_CONFIG_II, sermode3, async); + break; + case if_sync_serial_1: + set_gen_config_ii = 1; + SETS(gens_ii, R_GEN_CONFIG_II, sermode1, sync); + break; + case if_sync_serial_3: + SETS(gens, R_GEN_CONFIG, ser3, select); + set_gen_config_ii = 1; + SETS(gens_ii, R_GEN_CONFIG_II, sermode3, sync); + break; + case if_shared_ram: + SETS(gens, R_GEN_CONFIG, mio, select); + break; + case if_shared_ram_w: + SETS(gens, R_GEN_CONFIG, mio_w, select); + break; + case if_par_0: + SETS(gens, R_GEN_CONFIG, par0, select); + break; + case if_par_1: + SETS(gens, R_GEN_CONFIG, par1, select); + break; + case if_par_w: + SETS(gens, R_GEN_CONFIG, par0, select); + SETS(gens, R_GEN_CONFIG, par_w, select); + break; + case if_scsi8_0: + SETS(gens, R_GEN_CONFIG, scsi0, select); + break; + case if_scsi8_1: + SETS(gens, R_GEN_CONFIG, scsi1, select); + break; + case if_scsi_w: + SETS(gens, R_GEN_CONFIG, scsi0, select); + SETS(gens, R_GEN_CONFIG, scsi0w, select); + break; + case if_ata: + SETS(gens, R_GEN_CONFIG, ata, select); + break; + case if_csp: + /* fall through */ + case if_i2c: + set_gen_config = 0; + break; + case if_usb_1: + SETS(gens, R_GEN_CONFIG, usb1, select); + break; + case if_usb_2: + SETS(gens, R_GEN_CONFIG, usb2, select); + break; + case if_gpio_grp_a: + /* GPIO groups are only accounted, don't do configuration changes. */ + /* fall through */ + case if_gpio_grp_b: + /* fall through */ + case if_gpio_grp_c: + /* fall through */ + case if_gpio_grp_d: + /* fall through */ + case if_gpio_grp_e: + /* fall through */ + case if_gpio_grp_f: + set_gen_config = 0; + break; + default: + panic("cris_request_io_interface: Bad interface %u submitted for %s\n", + ioif, + device_id); + } + + interfaces[ioif].used = 1; + interfaces[ioif].owner = (char*)device_id; + + if (set_gen_config) { + volatile int i; + genconfig_shadow = gens; + *R_GEN_CONFIG = genconfig_shadow; + /* Wait 12 cycles before doing any DMA command */ + for(i = 6; i > 0; i--) + nop(); + } + if (set_gen_config_ii) { + gen_config_ii_shadow = gens_ii; + *R_GEN_CONFIG_II = gen_config_ii_shadow; + } + + DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + gpio_in_pins, gpio_out_pins, gpio_pb_pins)); + DBG(printk("grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + interfaces[ioif].gpio_g_in, + interfaces[ioif].gpio_g_out, + interfaces[ioif].gpio_b)); + + gpio_in_pins &= ~interfaces[ioif].gpio_g_in; + gpio_out_pins &= ~interfaces[ioif].gpio_g_out; + gpio_pb_pins &= ~interfaces[ioif].gpio_b; + + DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + gpio_in_pins, gpio_out_pins, gpio_pb_pins)); + + local_irq_restore(flags); + + notify_watchers(); + + return 0; +} + + +void cris_free_io_interface(enum cris_io_interface ioif) +{ + struct if_group *grp; + unsigned char group_set; + unsigned long flags; + + (void)cris_io_interface_init(); + + if ((ioif >= if_max_interfaces) || (ioif < 0)) { + printk(KERN_CRIT "cris_free_io_interface: Bad interface %u\n", + ioif); + return; + } + local_irq_save(flags); + if (!interfaces[ioif].used) { + printk(KERN_CRIT "cris_free_io_interface: Freeing free interface %u\n", + ioif); + local_irq_restore(flags); + return; + } + group_set = interfaces[ioif].groups; + while (NULL != (grp = get_group(group_set))) { + if (grp->group == group_f) { + switch (ioif) + { + case if_sync_serial_1: + if ((grp->owner == if_sync_serial_1) && + interfaces[if_sync_serial_3].used) { + grp->owner = if_sync_serial_3; + } else + grp->used = 0; + break; + case if_sync_serial_3: + if ((grp->owner == if_sync_serial_3) && + interfaces[if_sync_serial_1].used) { + grp->owner = if_sync_serial_1; + } else + grp->used = 0; + break; + case if_scsi8_0: + if ((grp->owner == if_scsi8_0) && + interfaces[if_scsi8_1].used) { + grp->owner = if_scsi8_1; + } else + grp->used = 0; + break; + case if_scsi8_1: + if ((grp->owner == if_scsi8_1) && + interfaces[if_scsi8_0].used) { + grp->owner = if_scsi8_0; + } else + grp->used = 0; + break; + default: + grp->used = 0; + } + } else { + grp->used = 0; + } + group_set = clear_group_from_set(group_set, grp); + } + interfaces[ioif].used = 0; + interfaces[ioif].owner = NULL; + + DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + gpio_in_pins, gpio_out_pins, gpio_pb_pins)); + DBG(printk("freeing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + interfaces[ioif].gpio_g_in, + interfaces[ioif].gpio_g_out, + interfaces[ioif].gpio_b)); + + gpio_in_pins |= interfaces[ioif].gpio_g_in; + gpio_out_pins |= interfaces[ioif].gpio_g_out; + gpio_pb_pins |= interfaces[ioif].gpio_b; + + DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + gpio_in_pins, gpio_out_pins, gpio_pb_pins)); + + local_irq_restore(flags); + + notify_watchers(); +} + +/* Create a bitmask from bit 0 (inclusive) to bit stop_bit + (non-inclusive). stop_bit == 0 returns 0x0 */ +static inline unsigned int create_mask(const unsigned stop_bit) +{ + /* Avoid overflow */ + if (stop_bit >= 32) { + return 0xffffffff; + } + return (1<notify = notify; + w->next = watchers; + watchers = w; + + w->notify((const unsigned int)gpio_in_pins, + (const unsigned int)gpio_out_pins, + (const unsigned char)gpio_pa_pins, + (const unsigned char)gpio_pb_pins); + + return 0; +} + +void cris_io_interface_delete_watcher(void (*notify)(const unsigned int gpio_in_available, + const unsigned int gpio_out_available, + const unsigned char pa_available, + const unsigned char pb_available)) +{ + struct watcher *w = watchers, *prev = NULL; + + (void)cris_io_interface_init(); + + while ((NULL != w) && (w->notify != notify)){ + prev = w; + w = w->next; + } + if (NULL != w) { + if (NULL != prev) { + prev->next = w->next; + } else { + watchers = w->next; + } + kfree(w); + return; + } + printk(KERN_WARNING "cris_io_interface_delete_watcher: Deleting unknown watcher 0x%p\n", notify); +} + + +static int cris_io_interface_init(void) +{ + static int first = 1; + int i; + + if (!first) { + return 0; + } + first = 0; + + for (i = 0; i<8; i++) { + gpio_pa_owners[i] = if_unclaimed; + gpio_pb_owners[i] = if_unclaimed; + gpio_pg_owners[i] = if_unclaimed; + } + for (; i<32; i++) { + gpio_pg_owners[i] = if_unclaimed; + } + return 0; +} + + +module_init(cris_io_interface_init); + + +EXPORT_SYMBOL(cris_request_io_interface); +EXPORT_SYMBOL(cris_free_io_interface); +EXPORT_SYMBOL(cris_io_interface_allocate_pins); +EXPORT_SYMBOL(cris_io_interface_free_pins); +EXPORT_SYMBOL(cris_io_interface_register_watcher); +EXPORT_SYMBOL(cris_io_interface_delete_watcher); --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/irq.c 2005-03-01 23:38:09.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/irq.c 2005-07-12 00:26:59.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.2 2004/06/09 05:30:27 starvik Exp $ +/* $Id: irq.c,v 1.4 2005/01/04 12:22:28 starvik Exp $ * * linux/arch/cris/kernel/irq.c * @@ -12,11 +12,13 @@ */ #include +#include #include #include #include -irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */ +#define mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr)); +#define unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr)); /* don't use set_int_vector, it bypasses the linux interrupt handlers. it is * global just so that the kernel gdb can use it. @@ -102,41 +104,52 @@ static void (*interrupt[NR_IRQS])(void) IRQ31_interrupt }; -static void (*bad_interrupt[NR_IRQS])(void) = { - NULL, NULL, - NULL, bad_IRQ3_interrupt, - bad_IRQ4_interrupt, bad_IRQ5_interrupt, - bad_IRQ6_interrupt, bad_IRQ7_interrupt, - bad_IRQ8_interrupt, bad_IRQ9_interrupt, - bad_IRQ10_interrupt, bad_IRQ11_interrupt, - bad_IRQ12_interrupt, bad_IRQ13_interrupt, - NULL, NULL, - bad_IRQ16_interrupt, bad_IRQ17_interrupt, - bad_IRQ18_interrupt, bad_IRQ19_interrupt, - bad_IRQ20_interrupt, bad_IRQ21_interrupt, - bad_IRQ22_interrupt, bad_IRQ23_interrupt, - bad_IRQ24_interrupt, bad_IRQ25_interrupt, - NULL, NULL, NULL, NULL, NULL, - bad_IRQ31_interrupt -}; +static void enable_crisv10_irq(unsigned int irq); + +static unsigned int startup_crisv10_irq(unsigned int irq) +{ + enable_crisv10_irq(irq); + return 0; +} + +#define shutdown_crisv10_irq disable_crisv10_irq -void arch_setup_irq(int irq) +static void enable_crisv10_irq(unsigned int irq) { - set_int_vector(irq, interrupt[irq]); + unmask_irq(irq); } -void arch_free_irq(int irq) +static void disable_crisv10_irq(unsigned int irq) { - set_int_vector(irq, bad_interrupt[irq]); + mask_irq(irq); } +static void ack_crisv10_irq(unsigned int irq) +{ +} + +static void end_crisv10_irq(unsigned int irq) +{ +} + +static struct hw_interrupt_type crisv10_irq_type = { + .typename = "CRISv10", + .startup = startup_crisv10_irq, + .shutdown = shutdown_crisv10_irq, + .enable = enable_crisv10_irq, + .disable = disable_crisv10_irq, + .ack = ack_crisv10_irq, + .end = end_crisv10_irq, + .set_affinity = NULL +}; + void weird_irq(void); void system_call(void); /* from entry.S */ void do_sigtrap(void); /* from entry.S */ void gdb_handle_breakpoint(void); /* from entry.S */ /* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and - setting the irq vector table to point to bad_interrupt ptrs. + setting the irq vector table. */ void __init @@ -154,14 +167,15 @@ init_IRQ(void) *R_VECT_MASK_CLR = 0xffffffff; - /* clear the shortcut entry points */ - - for(i = 0; i < NR_IRQS; i++) - irq_shortcuts[i] = NULL; - for (i = 0; i < 256; i++) etrax_irv->v[i] = weird_irq; + /* Initialize IRQ handler descriptiors. */ + for(i = 2; i < NR_IRQS; i++) { + irq_desc[i].handler = &crisv10_irq_type; + set_int_vector(i, interrupt[i]); + } + /* the entries in the break vector contain actual code to be executed by the associated break handler, rather than just a jump address. therefore we need to setup a default breakpoint handler @@ -170,10 +184,6 @@ init_IRQ(void) for (i = 0; i < 16; i++) set_break_vector(i, do_sigtrap); - /* set all etrax irq's to the bad handlers */ - for (i = 2; i < NR_IRQS; i++) - set_int_vector(i, bad_interrupt[i]); - /* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */ set_int_vector(15, multiple_interrupt); --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/kgdb.c 2005-03-01 23:38:26.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/kgdb.c 2005-07-12 00:26:59.000000000 -0700 @@ -18,6 +18,10 @@ *! Jul 21 1999 Bjorn Wesen eLinux port *! *! $Log: kgdb.c,v $ +*! Revision 1.6 2005/01/14 10:12:17 starvik +*! KGDB on separate port. +*! Console fixes from 2.4. +*! *! Revision 1.5 2004/10/07 13:59:08 starvik *! Corrected call to set_int_vector *! @@ -71,7 +75,7 @@ *! *!--------------------------------------------------------------------------- *! -*! $Id: kgdb.c,v 1.5 2004/10/07 13:59:08 starvik Exp $ +*! $Id: kgdb.c,v 1.6 2005/01/14 10:12:17 starvik Exp $ *! *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN *! @@ -225,6 +229,7 @@ #include #include #include +#include #include #include @@ -1344,12 +1349,11 @@ handle_exception (int sigval) } } -/* The jump is to the address 0x00000002. Performs a complete re-start - from scratch. */ +/* Performs a complete re-start from scratch. */ static void kill_restart () { - __asm__ volatile ("jump 2"); + machine_restart(""); } /********************************** Breakpoint *******************************/ @@ -1506,6 +1510,11 @@ kgdb_handle_serial: bne goback nop + move.d [reg+0x5E], $r10 ; Get DCCR + btstq 8, $r10 ; Test the U-flag. + bmi goback + nop + ;; ;; Handle the communication ;; --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/Makefile 2005-03-01 23:37:30.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/Makefile 2005-07-12 00:26:59.000000000 -0700 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.5 2004/06/02 08:24:38 starvik Exp $ +# $Id: Makefile,v 1.6 2004/12/13 12:21:51 starvik Exp $ # # Makefile for the linux kernel. # @@ -7,7 +7,8 @@ extra-y := head.o obj-y := entry.o traps.o shadows.o debugport.o irq.o \ - process.o setup.o signal.o traps.o time.o ptrace.o + process.o setup.o signal.o traps.o time.o ptrace.o \ + dma.o io_interface_mux.o obj-$(CONFIG_ETRAX_KGDB) += kgdb.o obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/process.c 2005-03-01 23:38:10.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/process.c 2005-07-12 00:26:59.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.9 2004/10/19 13:07:37 starvik Exp $ +/* $Id: process.c,v 1.12 2004/12/27 11:18:32 starvik Exp $ * * linux/arch/cris/kernel/process.c * @@ -101,6 +101,7 @@ int kernel_thread(int (*fn)(void *), voi regs.r11 = (unsigned long)fn; regs.r12 = (unsigned long)arg; regs.irp = (unsigned long)kernel_thread_helper; + regs.dccr = 1 << I_DCCR_BITNR; /* Ok, create the new process.. */ return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/ptrace.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/cris/arch-v10/kernel/ptrace.c 2005-07-12 00:27:00.000000000 -0700 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -86,9 +87,13 @@ sys_ptrace(long request, long pid, long ret = -EPERM; if (request == PTRACE_TRACEME) { + /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; - + ret = security_ptrace(current->parent, current); + if (ret) + goto out; + /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; ret = 0; goto out; @@ -207,7 +212,7 @@ sys_ptrace(long request, long pid, long case PTRACE_KILL: ret = 0; - if (child->state == TASK_ZOMBIE) + if (child->exit_state == EXIT_ZOMBIE) break; child->exit_code = SIGKILL; --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/shadows.c 2005-03-01 23:38:26.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/shadows.c 2005-07-12 00:26:59.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: shadows.c,v 1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: shadows.c,v 1.2 2004/12/13 12:21:51 starvik Exp $ * * Various shadow registers. Defines for these are in include/asm-etrax100/io.h */ @@ -6,6 +6,7 @@ /* Shadows for internal Etrax-registers */ unsigned long genconfig_shadow; +unsigned long gen_config_ii_shadow; unsigned long port_g_data_shadow; unsigned char port_pa_dir_shadow; unsigned char port_pa_data_shadow; --- linux-2.6.13-rc2/arch/cris/arch-v10/kernel/traps.c 2005-03-01 23:38:25.000000000 -0800 +++ devel/arch/cris/arch-v10/kernel/traps.c 2005-07-12 00:26:58.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.2 2003/07/04 08:27:41 starvik Exp $ +/* $Id: traps.c,v 1.4 2005/04/24 18:47:55 starvik Exp $ * * linux/arch/cris/arch-v10/traps.c * @@ -16,6 +16,8 @@ #include #include +extern int raw_printk(const char *fmt, ...); + void show_registers(struct pt_regs * regs) { @@ -26,18 +28,18 @@ show_registers(struct pt_regs * regs) register. */ unsigned long usp = rdusp(); - printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", + raw_printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", regs->irp, regs->srp, regs->dccr, usp, regs->mof ); - printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", regs->r0, regs->r1, regs->r2, regs->r3); - printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", regs->r4, regs->r5, regs->r6, regs->r7); - printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", regs->r8, regs->r9, regs->r10, regs->r11); - printk("r12: %08lx r13: %08lx oR10: %08lx\n", - regs->r12, regs->r13, regs->orig_r10); - printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); - printk("Process %s (pid: %d, stackpage=%08lx)\n", + raw_printk("r12: %08lx r13: %08lx oR10: %08lx sp: %08lx\n", + regs->r12, regs->r13, regs->orig_r10, regs); + raw_printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); + raw_printk("Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, (unsigned long)current); /* @@ -53,7 +55,7 @@ show_registers(struct pt_regs * regs) if (usp != 0) show_stack (NULL, NULL); - printk("\nCode: "); + raw_printk("\nCode: "); if(regs->irp < PAGE_OFFSET) goto bad; @@ -70,16 +72,16 @@ show_registers(struct pt_regs * regs) unsigned char c; if(__get_user(c, &((unsigned char*)regs->irp)[i])) { bad: - printk(" Bad IP value."); + raw_printk(" Bad IP value."); break; } if (i == 0) - printk("(%02x) ", c); + raw_printk("(%02x) ", c); else - printk("%02x ", c); + raw_printk("%02x ", c); } - printk("\n"); + raw_printk("\n"); } } @@ -121,7 +123,7 @@ die_if_kernel(const char * str, struct p stop_watchdog(); #endif - printk("%s: %04lx\n", str, err & 0xffff); + raw_printk("%s: %04lx\n", str, err & 0xffff); show_registers(regs); @@ -130,3 +132,8 @@ die_if_kernel(const char * str, struct p #endif do_exit(SIGSEGV); } + +void arch_enable_nmi(void) +{ + asm volatile("setf m"); +} --- linux-2.6.13-rc2/arch/cris/arch-v10/mm/fault.c 2005-03-01 23:38:26.000000000 -0800 +++ devel/arch/cris/arch-v10/mm/fault.c 2005-07-12 00:27:00.000000000 -0700 @@ -14,6 +14,7 @@ #include #include #include +#include /* debug of low-level TLB reload */ #undef DEBUG @@ -24,8 +25,6 @@ #define D(x) #endif -extern volatile pgd_t *current_pgd; - extern const struct exception_table_entry *search_exception_tables(unsigned long addr); @@ -46,7 +45,7 @@ handle_mmu_bus_fault(struct pt_regs *reg int page_id; int acc, inv; #endif - pgd_t* pgd = (pgd_t*)current_pgd; + pgd_t* pgd = (pgd_t*)per_cpu(current_pgd, smp_processor_id()); pmd_t *pmd; pte_t pte; int miss, we, writeac; @@ -94,24 +93,3 @@ handle_mmu_bus_fault(struct pt_regs *reg *R_TLB_LO = pte_val(pte); local_irq_restore(flags); } - -/* Called from arch/cris/mm/fault.c to find fixup code. */ -int -find_fixup_code(struct pt_regs *regs) -{ - const struct exception_table_entry *fixup; - - if ((fixup = search_exception_tables(regs->irp)) != 0) { - /* Adjust the instruction pointer in the stackframe. */ - regs->irp = fixup->fixup; - - /* - * Don't return by restoring the CPU state, so switch - * frame-type. - */ - regs->frametype = CRIS_FRAME_NORMAL; - return 1; - } - - return 0; -} --- linux-2.6.13-rc2/arch/cris/arch-v10/mm/init.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/cris/arch-v10/mm/init.c 2005-07-12 00:27:00.000000000 -0700 @@ -42,7 +42,7 @@ paging_init(void) * switch_mm) */ - current_pgd = init_mm.pgd; + per_cpu(current_pgd, smp_processor_id()) = init_mm.pgd; /* initialise the TLB (tlb.c) */ --- linux-2.6.13-rc2/arch/cris/arch-v10/mm/tlb.c 2005-03-01 23:38:33.000000000 -0800 +++ devel/arch/cris/arch-v10/mm/tlb.c 2005-07-12 00:27:00.000000000 -0700 @@ -139,53 +139,6 @@ flush_tlb_page(struct vm_area_struct *vm local_irq_restore(flags); } -/* invalidate a page range */ - -void -flush_tlb_range(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - int page_id = mm->context.page_id; - int i; - unsigned long flags; - - D(printk("tlb: flush range %p<->%p in context %d (%p)\n", - start, end, page_id, mm)); - - if(page_id == NO_CONTEXT) - return; - - start &= PAGE_MASK; /* probably not necessary */ - end &= PAGE_MASK; /* dito */ - - /* invalidate those TLB entries that match both the mm context - * and the virtual address range - */ - - local_save_flags(flags); - local_irq_disable(); - for(i = 0; i < NUM_TLB_ENTRIES; i++) { - unsigned long tlb_hi, vpn; - *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); - tlb_hi = *R_TLB_HI; - vpn = tlb_hi & PAGE_MASK; - if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id && - vpn >= start && vpn < end) { - *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | - IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); - - *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | - IO_STATE(R_TLB_LO, valid, no ) | - IO_STATE(R_TLB_LO, kernel,no ) | - IO_STATE(R_TLB_LO, we, no ) | - IO_FIELD(R_TLB_LO, pfn, 0 ) ); - } - } - local_irq_restore(flags); -} - /* dump the entire TLB for debug purposes */ #if 0 @@ -237,7 +190,7 @@ switch_mm(struct mm_struct *prev, struct * the pgd. */ - current_pgd = next->pgd; + per_cpu(current_pgd, smp_processor_id()) = next->pgd; /* switch context in the MMU */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/boot/compressed/decompress.ld 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,30 @@ +/*#OUTPUT_FORMAT(elf32-us-cris) */ +OUTPUT_ARCH (crisv32) + +MEMORY + { + dram : ORIGIN = 0x40700000, + LENGTH = 0x00100000 + } + +SECTIONS +{ + .text : + { + _stext = . ; + *(.text) + *(.rodata) + *(.rodata.*) + _etext = . ; + } > dram + .data : + { + *(.data) + _edata = . ; + } > dram + .bss : + { + *(.bss) + _end = ALIGN( 0x10 ) ; + } > dram +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/boot/compressed/head.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,193 @@ +/* + * Code that sets up the DRAM registers, calls the + * decompressor to unpack the piggybacked kernel, and jumps. + * + * Copyright (C) 1999 - 2003, Axis Communications AB + */ + +#include +#define ASSEMBLER_MACROS_ONLY +#include +#include +#include + +#define RAM_INIT_MAGIC 0x56902387 +#define COMMAND_LINE_MAGIC 0x87109563 + + ;; Exported symbols + + .globl input_data + + .text +start: + di + + ;; Start clocks for used blocks. + move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 + move.d [$r1], $r0 + or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \ + REG_STATE(config, rw_clk_ctrl, bif, yes) | \ + REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 + move.d $r0, [$r1] + + ;; If booting from NAND flash we first have to copy some + ;; data from NAND flash to internal RAM to get the code + ;; that initializes the SDRAM. Lets copy 20 KB. This + ;; code executes at 0x38010000 if booting from NAND and + ;; we are guaranted that at least 0x200 bytes are good so + ;; lets start from there. The first 8192 bytes in the nand + ;; flash is spliced with zeroes and is thus 16384 bytes. + move.d 0x38010200, $r10 + move.d 0x14200, $r11 ; Start offset in NAND flash 0x10200 + 16384 + move.d 0x5000, $r12 ; Length of copy + + ;; Before this code the tools add a partitiontable so the PC + ;; has an offset from the linked address. +offset1: + lapcq ., $r13 ; get PC + add.d first_copy_complete-offset1, $r13 + +#include "../../lib/nand_init.S" + +first_copy_complete: + ;; Initialze the DRAM registers. + cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized? + beq dram_init_finished + nop + +#include "../../lib/dram_init.S" + +dram_init_finished: + lapcq ., $r13 ; get PC + add.d second_copy_complete-dram_init_finished, $r13 + + move.d REG_ADDR(config, regi_config, r_bootsel), $r0 + move.d [$r0], $r0 + and.d REG_MASK(config, r_bootsel, boot_mode), $r0 + cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 + bne second_copy_complete ; No NAND boot + nop + + ;; Copy 2MB from NAND flash to SDRAM (at 2-4MB into the SDRAM) + move.d 0x40204000, $r10 + move.d 0x8000, $r11 + move.d 0x200000, $r12 + ba copy_nand_to_ram + nop +second_copy_complete: + + ;; Initiate the PA port. + move.d CONFIG_ETRAX_DEF_GIO_PA_OUT, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pa_dout), $r1 + move.d $r0, [$r1] + + move.d CONFIG_ETRAX_DEF_GIO_PA_OE, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r1 + move.d $r0, [$r1] + + ;; Setup the stack to a suitably high address. + ;; We assume 8 MB is the minimum DRAM and put + ;; the SP at the top for now. + + move.d 0x40800000, $sp + + ;; Figure out where the compressed piggyback image is + ;; in the flash (since we wont try to copy it to DRAM + ;; before unpacking). It is at _edata, but in flash. + ;; Use (_edata - herami) as offset to the current PC. + + move.d REG_ADDR(config, regi_config, r_bootsel), $r0 + move.d [$r0], $r0 + and.d REG_MASK(config, r_bootsel, boot_mode), $r0 + cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 + beq hereami2 + nop +hereami: + lapcq ., $r5 ; get PC + and.d 0x7fffffff, $r5 ; strip any non-cache bit + move.d $r5, $r0 ; save for later - flash address of 'herami' + add.d _edata, $r5 + sub.d hereami, $r5 ; r5 = flash address of '_edata' + move.d hereami, $r1 ; destination + ba 2f + nop +hereami2: + lapcq ., $r5 ; get PC + and.d 0x00ffffff, $r5 ; strip any non-cache bit + move.d $r5, $r6 + or.d 0x40200000, $r6 + move.d $r6, $r0 ; save for later - flash address of 'herami' + add.d _edata, $r5 + sub.d hereami2, $r5 ; r5 = flash address of '_edata' + add.d 0x40200000, $r5 + move.d hereami2, $r1 ; destination +2: + ;; Copy text+data to DRAM + + move.d _edata, $r2 ; end destination +1: move.w [$r0+], $r3 + move.w $r3, [$r1+] + cmp.d $r2, $r1 + bcs 1b + nop + + move.d input_data, $r0 ; for the decompressor + move.d $r5, [$r0] ; for the decompressor + + ;; Clear the decompressors BSS (between _edata and _end) + + moveq 0, $r0 + move.d _edata, $r1 + move.d _end, $r2 +1: move.w $r0, [$r1+] + cmp.d $r2, $r1 + bcs 1b + nop + + ;; Save command line magic and address. + move.d _cmd_line_magic, $r12 + move.d $r10, [$r12] + move.d _cmd_line_addr, $r12 + move.d $r11, [$r12] + + ;; Do the decompression and save compressed size in _inptr + + jsr decompress_kernel + nop + + ;; Restore command line magic and address. + move.d _cmd_line_magic, $r10 + move.d [$r10], $r10 + move.d _cmd_line_addr, $r11 + move.d [$r11], $r11 + + ;; Put start address of root partition in r9 so the kernel can use it + ;; when mounting from flash + move.d input_data, $r0 + move.d [$r0], $r9 ; flash address of compressed kernel + move.d inptr, $r0 + add.d [$r0], $r9 ; size of compressed kernel + cmp.d 0x40200000, $r9 + blo enter_kernel + nop + sub.d 0x40200000, $r9 + add.d 0x4000, $r9 + +enter_kernel: + ;; Enter the decompressed kernel + move.d RAM_INIT_MAGIC, $r8 ; Tell kernel that DRAM is initialized + jump 0x40004000 ; kernel is linked to this address + nop + + .data + +input_data: + .dword 0 ; used by the decompressor +_cmd_line_magic: + .dword 0 +_cmd_line_addr: + .dword 0 +is_nand_boot: + .dword 0 + +#include "../../lib/hw_settings.S" --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/boot/compressed/Makefile 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,41 @@ +# +# lx25/arch/cris/arch-v32/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux files and romfs +# + +target = $(target_compressed_dir) +src = $(src_compressed_dir) + +CC = gcc-cris -mlinux -march=v32 -I $(TOPDIR)/include +CFLAGS = -O2 +LD = gcc-cris -mlinux -march=v32 -nostdlib +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary --remove-section=.bss +OBJECTS = $(target)/head.o $(target)/misc.o + +# files to compress +SYSTEM = $(objtree)/vmlinux.bin + +all: vmlinuz + +$(target)/decompress.bin: $(OBJECTS) + $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS) + $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin + +$(objtree)/vmlinuz: $(target) piggy.img $(target)/decompress.bin + cat $(target)/decompress.bin piggy.img > $(objtree)/vmlinuz + rm -f piggy.img + cp $(objtree)/vmlinuz $(src) + +$(target)/head.o: $(src)/head.S + $(CC) -D__ASSEMBLY__ -c $< -o $@ + +# gzip the kernel image + +piggy.img: $(SYSTEM) + cat $(SYSTEM) | gzip -f -9 > piggy.img + +clean: + rm -f piggy.img $(objtree)/vmlinuz vmlinuz.o decompress.o decompress.bin $(OBJECTS) + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/boot/compressed/misc.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,318 @@ +/* + * misc.c + * + * $Id: misc.c,v 1.8 2005/04/24 18:34:29 starvik Exp $ + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * puts by Nick Holloway 1993, better puts by Martin Mares 1995 + * adoptation for Linux/CRIS Axis Communications AB, 1999 + * + */ + +/* where the piggybacked kernel image expects itself to live. + * it is the same address we use when we network load an uncompressed + * image into DRAM, and it is the address the kernel is linked to live + * at by vmlinux.lds.S + */ + +#define KERNEL_LOAD_ADR 0x40004000 + +#include + +#include +#include +#include +#include + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +void* memset(void* s, int c, size_t n); +void* memcpy(void* __dest, __const void* __src, + size_t __n); + +#define memzero(s, n) memset ((s), 0, (n)) + + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* Sliding window buffer */ + +unsigned inptr = 0; /* index of next byte to be processed in inbuf + * After decompression it will contain the + * compressed size, and head.S will read it. + */ + +static unsigned outcnt = 0; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() inbuf[inptr++] + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +extern char *input_data; /* lives in head.S */ + +static long bytes_out = 0; +static uch *output_data; +static unsigned long output_ptr = 0; + +static void *malloc(int size); +static void free(void *where); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +static void puts(const char *); + +/* the "heap" is put directly after the BSS ends, at end */ + +extern int _end; +static long free_mem_ptr = (long)&_end; + +#include "../../../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + return p; +} + +static void free(void *where) +{ /* Don't care */ +} + +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + +/* decompressor info and error messages to serial console */ + +static inline void +serout(const char *s, reg_scope_instances regi_ser) +{ + reg_ser_rs_stat_din rs; + reg_ser_rw_dout dout = {.data = *s}; + + do { + rs = REG_RD(ser, regi_ser, rs_stat_din); + } + while (!rs.tr_rdy);/* Wait for tranceiver. */ + + REG_WR(ser, regi_ser, rw_dout, dout); +} + +static void +puts(const char *s) +{ +#ifndef CONFIG_ETRAX_DEBUG_PORT_NULL + while (*s) { +#ifdef CONFIG_ETRAX_DEBUG_PORT0 + serout(s, regi_ser0); +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT1 + serout(s, regi_ser1); +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT2 + serout(s, regi_ser2); +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT3 + serout(s, regi_ser3); +#endif + *s++; + } +/* CONFIG_ETRAX_DEBUG_PORT_NULL */ +#endif +} + +void* +memset(void* s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void +error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted\n"); + + while(1); /* Halt */ +} + +void +setup_normal_output_buffer() +{ + output_data = (char *)KERNEL_LOAD_ADR; +} + +static inline void +serial_setup(reg_scope_instances regi_ser) +{ + reg_ser_rw_xoff xoff; + reg_ser_rw_tr_ctrl tr_ctrl; + reg_ser_rw_rec_ctrl rec_ctrl; + reg_ser_rw_tr_baud_div tr_baud; + reg_ser_rw_rec_baud_div rec_baud; + + /* Turn off XOFF. */ + xoff = REG_RD(ser, regi_ser, rw_xoff); + + xoff.chr = 0; + xoff.automatic = regk_ser_no; + + REG_WR(ser, regi_ser, rw_xoff, xoff); + + /* Set baudrate and stopbits. */ + tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl); + rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl); + tr_baud = REG_RD(ser, regi_ser, rw_tr_baud_div); + rec_baud = REG_RD(ser, regi_ser, rw_rec_baud_div); + + tr_ctrl.stop_bits = 1; /* 2 stop bits. */ + + /* + * The baudrate setup is a bit fishy, but in the end the tranceiver is + * set to 4800 and the receiver to 115200. The magic value is + * 29.493 MHz. + */ + tr_ctrl.base_freq = regk_ser_f29_493; + rec_ctrl.base_freq = regk_ser_f29_493; + tr_baud.div = (29493000 / 8) / 4800; + rec_baud.div = (29493000 / 8) / 115200; + + REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); + REG_WR(ser, regi_ser, rw_tr_baud_div, tr_baud); + REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl); + REG_WR(ser, regi_ser, rw_rec_baud_div, rec_baud); +} + +void +decompress_kernel() +{ + char revision; + + /* input_data is set in head.S */ + inbuf = input_data; + +#ifdef CONFIG_ETRAX_DEBUG_PORT0 + serial_setup(regi_ser0); +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT1 + serial_setup(regi_ser1); +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT2 + serial_setup(regi_ser2); +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT3 + serial_setup(regi_ser3); +#endif + + setup_normal_output_buffer(); + + makecrc(); + + __asm__ volatile ("move $vr,%0" : "=rm" (revision)); + if (revision < 32) + { + puts("You need an ETRAX FS to run Linux 2.6/crisv32.\n"); + while(1); + } + + puts("Uncompressing Linux...\n"); + gunzip(); + puts("Done. Now booting the kernel.\n"); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/boot/compressed/README 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,25 @@ +Creation of the self-extracting compressed kernel image (vmlinuz) +----------------------------------------------------------------- +$Id: README,v 1.1 2003/08/21 09:37:03 johana Exp $ + +This can be slightly confusing because it's a process with many steps. + +The kernel object built by the arch/etrax100/Makefile, vmlinux, is split +by that makefile into text and data binary files, vmlinux.text and +vmlinux.data. + +Those files together with a ROM filesystem can be catted together and +burned into a flash or executed directly at the DRAM origin. + +They can also be catted together and compressed with gzip, which is what +happens in this makefile. Together they make up piggy.img. + +The decompressor is built into the file decompress.o. It is turned into +the binary file decompress.bin, which is catted together with piggy.img +into the file vmlinuz. It can be executed in an arbitrary place in flash. + +Be careful - it assumes some things about free locations in DRAM. It +assumes the DRAM starts at 0x40000000 and that it is at least 8 MB, +so it puts its code at 0x40700000, and initial stack at 0x40800000. + +-Bjorn --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/boot/Makefile 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,14 @@ +# +# arch/cris/arch-v32/boot/Makefile +# +target = $(target_boot_dir) +src = $(src_boot_dir) + +zImage: compressed/vmlinuz + +compressed/vmlinuz: $(objtree)/vmlinux + @$(MAKE) -f $(src)/compressed/Makefile $(objtree)/vmlinuz + +clean: + rm -f zImage tools/build compressed/vmlinux.out + @$(MAKE) -f $(src)/compressed/Makefile clean --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/boot/rescue/head.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,39 @@ +/* $Id: head.S,v 1.4 2004/11/01 16:10:28 starvik Exp $ + * + * This used to be the rescue code but now that is handled by the + * RedBoot based RFL instead. Nothing to see here, move along. + */ + +#include +#include +#include + + .text + + ;; Start clocks for used blocks. + move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 + move.d [$r1], $r0 + or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \ + REG_STATE(config, rw_clk_ctrl, bif, yes) | \ + REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 + move.d $r0, [$r1] + + ;; Copy 68KB NAND flash to Internal RAM (if NAND boot) + move.d 0x38004000, $r10 + move.d 0x8000, $r11 + move.d 0x11000, $r12 + move.d copy_complete, $r13 + and.d 0x000fffff, $r13 + or.d 0x38000000, $r13 + +#include "../../lib/nand_init.S" + + ;; No NAND found + move.d CONFIG_ETRAX_PTABLE_SECTOR, $r10 + jump $r10 ; Jump to decompresser + nop + +copy_complete: + move.d 0x38000000 + CONFIG_ETRAX_PTABLE_SECTOR, $r10 + jump $r10 ; Jump to decompresser + nop --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/boot/rescue/Makefile 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,36 @@ +# +# Makefile for rescue code +# +target = $(target_rescue_dir) +src = $(src_rescue_dir) + +CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE) +CFLAGS = -O2 +LD = gcc-cris -mlinux -march=v32 -nostdlib +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary --remove-section=.bss + +all: $(target)/rescue.bin + +rescue: rescue.bin + # do nothing + +$(target)/rescue.bin: $(target) $(target)/head.o + $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin + cp -p $(target)/rescue.bin $(objtree) + +$(target): + mkdir -p $(target) + +$(target)/head.o: $(src)/head.S + $(CC) -D__ASSEMBLY__ -c $< -o $*.o + +clean: + rm -f $(target)/*.o $(target)/*.bin + +fastdep: + +modules: + +modules-install: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/boot/rescue/rescue.ld 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,20 @@ +MEMORY + { + flash : ORIGIN = 0x00000000, + LENGTH = 0x00100000 + } + +SECTIONS +{ + .text : + { + stext = . ; + *(.text) + etext = . ; + } > flash + .data : + { + *(.data) + edata = . ; + } > flash +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/axisflashmap.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,455 @@ +/* + * Physical mapping layer for MTD using the Axis partitiontable format + * + * Copyright (c) 2001, 2002, 2003 Axis Communications AB + * + * This file is under the GPL. + * + * First partition is always sector 0 regardless of if we find a partitiontable + * or not. In the start of the next sector, there can be a partitiontable that + * tells us what other partitions to define. If there isn't, we use a default + * partition split defined below. + * + * Copy of os/lx25/arch/cris/arch-v10/drivers/axisflashmap.c 1.5 + * with minor changes. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MEM_CSE0_SIZE (0x04000000) +#define MEM_CSE1_SIZE (0x04000000) + +#define FLASH_UNCACHED_ADDR KSEG_E +#define FLASH_CACHED_ADDR KSEG_F + +#if CONFIG_ETRAX_FLASH_BUSWIDTH==1 +#define flash_data __u8 +#elif CONFIG_ETRAX_FLASH_BUSWIDTH==2 +#define flash_data __u16 +#elif CONFIG_ETRAX_FLASH_BUSWIDTH==4 +#define flash_data __u16 +#endif + +/* From head.S */ +extern unsigned long romfs_start, romfs_length, romfs_in_flash; + +/* The master mtd for the entire flash. */ +struct mtd_info* axisflash_mtd = NULL; + +/* Map driver functions. */ + +static map_word flash_read(struct map_info *map, unsigned long ofs) +{ + map_word tmp; + tmp.x[0] = *(flash_data *)(map->map_priv_1 + ofs); + return tmp; +} + +static void flash_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(map->map_priv_1 + from), len); +} + +static void flash_write(struct map_info *map, map_word d, unsigned long adr) +{ + *(flash_data *)(map->map_priv_1 + adr) = (flash_data)d.x[0]; +} + +/* + * The map for chip select e0. + * + * We run into tricky coherence situations if we mix cached with uncached + * accesses to we only use the uncached version here. + * + * The size field is the total size where the flash chips may be mapped on the + * chip select. MTD probes should find all devices there and it does not matter + * if there are unmapped gaps or aliases (mirrors of flash devices). The MTD + * probes will ignore them. + * + * The start address in map_priv_1 is in virtual memory so we cannot use + * MEM_CSE0_START but must rely on that FLASH_UNCACHED_ADDR is the start + * address of cse0. + */ +static struct map_info map_cse0 = { + .name = "cse0", + .size = MEM_CSE0_SIZE, + .bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH, + .read = flash_read, + .copy_from = flash_copy_from, + .write = flash_write, + .map_priv_1 = FLASH_UNCACHED_ADDR +}; + +/* + * The map for chip select e1. + * + * If there was a gap between cse0 and cse1, map_priv_1 would get the wrong + * address, but there isn't. + */ +static struct map_info map_cse1 = { + .name = "cse1", + .size = MEM_CSE1_SIZE, + .bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH, + .read = flash_read, + .copy_from = flash_copy_from, + .write = flash_write, + .map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE +}; + +/* If no partition-table was found, we use this default-set. */ +#define MAX_PARTITIONS 7 +#define NUM_DEFAULT_PARTITIONS 3 + +/* + * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the + * size of one flash block and "filesystem"-partition needs 5 blocks to be able + * to use JFFS. + */ +static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { + { + .name = "boot firmware", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "kernel", + .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR), + .offset = CONFIG_ETRAX_PTABLE_SECTOR + }, + { + .name = "filesystem", + .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR) + } +}; + +/* Initialize the ones normally used. */ +static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { + { + .name = "part0", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "part1", + .size = 0, + .offset = 0 + }, + { + .name = "part2", + .size = 0, + .offset = 0 + }, + { + .name = "part3", + .size = 0, + .offset = 0 + }, + { + .name = "part4", + .size = 0, + .offset = 0 + }, + { + .name = "part5", + .size = 0, + .offset = 0 + }, + { + .name = "part6", + .size = 0, + .offset = 0 + }, +}; + +/* + * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash + * chips in that order (because the amd_flash-driver is faster). + */ +static struct mtd_info *probe_cs(struct map_info *map_cs) +{ + struct mtd_info *mtd_cs = NULL; + + printk(KERN_INFO + "%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n", + map_cs->name, map_cs->size, map_cs->map_priv_1); + +#ifdef CONFIG_MTD_AMDSTD + mtd_cs = do_map_probe("amd_flash", map_cs); +#endif +#ifdef CONFIG_MTD_CFI + if (!mtd_cs) { + mtd_cs = do_map_probe("cfi_probe", map_cs); + } +#endif + + return mtd_cs; +} + +/* + * Probe each chip select individually for flash chips. If there are chips on + * both cse0 and cse1, the mtd_info structs will be concatenated to one struct + * so that MTD partitions can cross chip boundries. + * + * The only known restriction to how you can mount your chips is that each + * chip select must hold similar flash chips. But you need external hardware + * to do that anyway and you can put totally different chips on cse0 and cse1 + * so it isn't really much of a restriction. + */ +extern struct mtd_info* __init crisv32_nand_flash_probe (void); +static struct mtd_info *flash_probe(void) +{ + struct mtd_info *mtd_cse0; + struct mtd_info *mtd_cse1; + struct mtd_info *mtd_nand = NULL; + struct mtd_info *mtd_total; + struct mtd_info *mtds[3]; + int count = 0; + + if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL) + mtds[count++] = mtd_cse0; + if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL) + mtds[count++] = mtd_cse1; + +#ifdef CONFIG_ETRAX_NANDFLASH + if ((mtd_nand = crisv32_nand_flash_probe()) != NULL) + mtds[count++] = mtd_nand; +#endif + + if (!mtd_cse0 && !mtd_cse1 && !mtd_nand) { + /* No chip found. */ + return NULL; + } + + if (count > 1) { +#ifdef CONFIG_MTD_CONCAT + /* Since the concatenation layer adds a small overhead we + * could try to figure out if the chips in cse0 and cse1 are + * identical and reprobe the whole cse0+cse1 window. But since + * flash chips are slow, the overhead is relatively small. + * So we use the MTD concatenation layer instead of further + * complicating the probing procedure. + */ + mtd_total = mtd_concat_create(mtds, + count, + "cse0+cse1+nand"); +#else + printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel " + "(mis)configuration!\n", map_cse0.name, map_cse1.name); + mtd_toal = NULL; +#endif + if (!mtd_total) { + printk(KERN_ERR "%s and %s: Concatenation failed!\n", + map_cse0.name, map_cse1.name); + + /* The best we can do now is to only use what we found + * at cse0. + */ + mtd_total = mtd_cse0; + map_destroy(mtd_cse1); + } + } else { + mtd_total = mtd_cse0? mtd_cse0 : mtd_cse1 ? mtd_cse1 : mtd_nand; + + } + + return mtd_total; +} + +extern unsigned long crisv32_nand_boot; +extern unsigned long crisv32_nand_cramfs_offset; + +/* + * Probe the flash chip(s) and, if it succeeds, read the partition-table + * and register the partitions with MTD. + */ +static int __init init_axis_flash(void) +{ + struct mtd_info *mymtd; + int err = 0; + int pidx = 0; + struct partitiontable_head *ptable_head = NULL; + struct partitiontable_entry *ptable; + int use_default_ptable = 1; /* Until proven otherwise. */ + const char *pmsg = KERN_INFO " /dev/flash%d at 0x%08x, size 0x%08x\n"; + static char page[512]; + size_t len; + +#ifndef CONFIG_ETRAXFS_SIM + mymtd = flash_probe(); + mymtd->read(mymtd, CONFIG_ETRAX_PTABLE_SECTOR, 512, &len, page); + ptable_head = (struct partitiontable_head *)(page + PARTITION_TABLE_OFFSET); + + if (!mymtd) { + /* There's no reason to use this module if no flash chip can + * be identified. Make sure that's understood. + */ + printk(KERN_INFO "axisflashmap: Found no flash chip.\n"); + } else { + printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n", + mymtd->name, mymtd->size); + axisflash_mtd = mymtd; + } + + if (mymtd) { + mymtd->owner = THIS_MODULE; + } + pidx++; /* First partition is always set to the default. */ + + if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC) + && (ptable_head->size < + (MAX_PARTITIONS * sizeof(struct partitiontable_entry) + + PARTITIONTABLE_END_MARKER_SIZE)) + && (*(unsigned long*)((void*)ptable_head + sizeof(*ptable_head) + + ptable_head->size - + PARTITIONTABLE_END_MARKER_SIZE) + == PARTITIONTABLE_END_MARKER)) { + /* Looks like a start, sane length and end of a + * partition table, lets check csum etc. + */ + int ptable_ok = 0; + struct partitiontable_entry *max_addr = + (struct partitiontable_entry *) + ((unsigned long)ptable_head + sizeof(*ptable_head) + + ptable_head->size); + unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR; + unsigned char *p; + unsigned long csum = 0; + + ptable = (struct partitiontable_entry *) + ((unsigned long)ptable_head + sizeof(*ptable_head)); + + /* Lets be PARANOID, and check the checksum. */ + p = (unsigned char*) ptable; + + while (p <= (unsigned char*)max_addr) { + csum += *p++; + csum += *p++; + csum += *p++; + csum += *p++; + } + ptable_ok = (csum == ptable_head->checksum); + + /* Read the entries and use/show the info. */ + printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n", + (ptable_ok ? " valid" : "n invalid"), ptable_head, + max_addr); + + /* We have found a working bootblock. Now read the + * partition table. Scan the table. It ends when + * there is 0xffffffff, that is, empty flash. + */ + while (ptable_ok + && ptable->offset != 0xffffffff + && ptable < max_addr + && pidx < MAX_PARTITIONS) { + + axis_partitions[pidx].offset = offset + ptable->offset + (crisv32_nand_boot ? 16384 : 0); + axis_partitions[pidx].size = ptable->size; + + printk(pmsg, pidx, axis_partitions[pidx].offset, + axis_partitions[pidx].size); + pidx++; + ptable++; + } + use_default_ptable = !ptable_ok; + } + + if (romfs_in_flash) { + /* Add an overlapping device for the root partition (romfs). */ + + axis_partitions[pidx].name = "romfs"; + if (crisv32_nand_boot) { + char* data = kmalloc(1024, GFP_KERNEL); + int len; + int offset = crisv32_nand_cramfs_offset & ~(1024-1); + char* tmp; + + mymtd->read(mymtd, offset, 1024, &len, data); + tmp = &data[crisv32_nand_cramfs_offset % 512]; + axis_partitions[pidx].size = *(unsigned*)(tmp + 4); + axis_partitions[pidx].offset = crisv32_nand_cramfs_offset; + kfree(data); + } else { + axis_partitions[pidx].size = romfs_length; + axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; + } + + axis_partitions[pidx].mask_flags |= MTD_WRITEABLE; + + printk(KERN_INFO + " Adding readonly flash partition for romfs image:\n"); + printk(pmsg, pidx, axis_partitions[pidx].offset, + axis_partitions[pidx].size); + pidx++; + } + + if (mymtd) { + if (use_default_ptable) { + printk(KERN_INFO " Using default partition table.\n"); + err = add_mtd_partitions(mymtd, axis_default_partitions, + NUM_DEFAULT_PARTITIONS); + } else { + err = add_mtd_partitions(mymtd, axis_partitions, pidx); + } + + if (err) { + panic("axisflashmap could not add MTD partitions!\n"); + } + } +/* CONFIG_EXTRAXFS_SIM */ +#endif + + if (!romfs_in_flash) { + /* Create an RAM device for the root partition (romfs). */ + +#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) + /* No use trying to boot this kernel from RAM. Panic! */ + printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " + "device due to kernel (mis)configuration!\n"); + panic("This kernel cannot boot from RAM!\n"); +#else + struct mtd_info *mtd_ram; + + mtd_ram = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), + GFP_KERNEL); + if (!mtd_ram) { + panic("axisflashmap couldn't allocate memory for " + "mtd_info!\n"); + } + + printk(KERN_INFO " Adding RAM partition for romfs image:\n"); + printk(pmsg, pidx, romfs_start, romfs_length); + + err = mtdram_init_device(mtd_ram, (void*)romfs_start, + romfs_length, "romfs"); + if (err) { + panic("axisflashmap could not initialize MTD RAM " + "device!\n"); + } +#endif + } + + return err; +} + +/* This adds the above to the kernels init-call chain. */ +module_init(init_axis_flash); + +EXPORT_SYMBOL(axisflash_mtd); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/cryptocop.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,3522 @@ +/* $Id: cryptocop.c,v 1.13 2005/04/21 17:27:55 henriken Exp $ + * + * Stream co-processor driver for the ETRAX FS + * + * Copyright (C) 2003-2005 Axis Communications AB + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + + + +#define DESCR_ALLOC_PAD (31) + +struct cryptocop_dma_desc { + char *free_buf; /* If non-null will be kfreed in free_cdesc() */ + dma_descr_data *dma_descr; + + unsigned char dma_descr_buf[sizeof(dma_descr_data) + DESCR_ALLOC_PAD]; + + unsigned int from_pool:1; /* If 1 'allocated' from the descriptor pool. */ + struct cryptocop_dma_desc *next; +}; + + +struct cryptocop_int_operation{ + void *alloc_ptr; + cryptocop_session_id sid; + + dma_descr_context ctx_out; + dma_descr_context ctx_in; + + /* DMA descriptors allocated by driver. */ + struct cryptocop_dma_desc *cdesc_out; + struct cryptocop_dma_desc *cdesc_in; + + /* Strcop config to use. */ + cryptocop_3des_mode tdes_mode; + cryptocop_csum_type csum_mode; + + /* DMA descrs provided by consumer. */ + dma_descr_data *ddesc_out; + dma_descr_data *ddesc_in; +}; + + +struct cryptocop_tfrm_ctx { + cryptocop_tfrm_id tid; + unsigned int blocklength; + + unsigned int start_ix; + + struct cryptocop_tfrm_cfg *tcfg; + struct cryptocop_transform_ctx *tctx; + + unsigned char previous_src; + unsigned char current_src; + + /* Values to use in metadata out. */ + unsigned char hash_conf; + unsigned char hash_mode; + unsigned char ciph_conf; + unsigned char cbcmode; + unsigned char decrypt; + + unsigned int requires_padding:1; + unsigned int strict_block_length:1; + unsigned int active:1; + unsigned int done:1; + size_t consumed; + size_t produced; + + /* Pad (input) descriptors to put in the DMA out list when the transform + * output is put on the DMA in list. */ + struct cryptocop_dma_desc *pad_descs; + + struct cryptocop_tfrm_ctx *prev_src; + struct cryptocop_tfrm_ctx *curr_src; + + /* Mapping to HW. */ + unsigned char unit_no; +}; + + +struct cryptocop_private{ + cryptocop_session_id sid; + struct cryptocop_private *next; +}; + +/* Session list. */ + +struct cryptocop_transform_ctx{ + struct cryptocop_transform_init init; + unsigned char dec_key[CRYPTOCOP_MAX_KEY_LENGTH]; + unsigned int dec_key_set:1; + + struct cryptocop_transform_ctx *next; +}; + + +struct cryptocop_session{ + cryptocop_session_id sid; + + struct cryptocop_transform_ctx *tfrm_ctx; + + struct cryptocop_session *next; +}; + +/* Priority levels for jobs sent to the cryptocop. Checksum operations from + kernel have highest priority since TCPIP stack processing must not + be a bottleneck. */ +typedef enum { + cryptocop_prio_kernel_csum = 0, + cryptocop_prio_kernel = 1, + cryptocop_prio_user = 2, + cryptocop_prio_no_prios = 3 +} cryptocop_queue_priority; + +struct cryptocop_prio_queue{ + struct list_head jobs; + cryptocop_queue_priority prio; +}; + +struct cryptocop_prio_job{ + struct list_head node; + cryptocop_queue_priority prio; + + struct cryptocop_operation *oper; + struct cryptocop_int_operation *iop; +}; + +struct ioctl_job_cb_ctx { + unsigned int processed:1; +}; + + +static struct cryptocop_session *cryptocop_sessions = NULL; +spinlock_t cryptocop_sessions_lock; + +/* Next Session ID to assign. */ +static cryptocop_session_id next_sid = 1; + +/* Pad for checksum. */ +static const char csum_zero_pad[1] = {0x00}; + +/* Trash buffer for mem2mem operations. */ +#define MEM2MEM_DISCARD_BUF_LENGTH (512) +static unsigned char mem2mem_discard_buf[MEM2MEM_DISCARD_BUF_LENGTH]; + +/* Descriptor pool. */ +/* FIXME Tweak this value. */ +#define CRYPTOCOP_DESCRIPTOR_POOL_SIZE (100) +static struct cryptocop_dma_desc descr_pool[CRYPTOCOP_DESCRIPTOR_POOL_SIZE]; +static struct cryptocop_dma_desc *descr_pool_free_list; +static int descr_pool_no_free; +static spinlock_t descr_pool_lock; + +/* Lock to stop cryptocop to start processing of a new operation. The holder + of this lock MUST call cryptocop_start_job() after it is unlocked. */ +spinlock_t cryptocop_process_lock; + +static struct cryptocop_prio_queue cryptocop_job_queues[cryptocop_prio_no_prios]; +static spinlock_t cryptocop_job_queue_lock; +static struct cryptocop_prio_job *cryptocop_running_job = NULL; +static spinlock_t running_job_lock; + +/* The interrupt handler appends completed jobs to this list. The scehduled + * tasklet removes them upon sending the response to the crypto consumer. */ +static struct list_head cryptocop_completed_jobs; +static spinlock_t cryptocop_completed_jobs_lock; + +DECLARE_WAIT_QUEUE_HEAD(cryptocop_ioc_process_wq); + + +/** Local functions. **/ + +static int cryptocop_open(struct inode *, struct file *); + +static int cryptocop_release(struct inode *, struct file *); + +static int cryptocop_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static void cryptocop_start_job(void); + +static int cryptocop_job_queue_insert(cryptocop_queue_priority prio, struct cryptocop_operation *operation); +static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_operation *operation); + +static int cryptocop_job_queue_init(void); +static void cryptocop_job_queue_close(void); + +static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length); + +static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length); + +static int transform_ok(struct cryptocop_transform_init *tinit); + +static struct cryptocop_session *get_session(cryptocop_session_id sid); + +static struct cryptocop_transform_ctx *get_transform_ctx(struct cryptocop_session *sess, cryptocop_tfrm_id tid); + +static void delete_internal_operation(struct cryptocop_int_operation *iop); + +static void get_aes_decrypt_key(unsigned char *dec_key, const unsigned char *key, unsigned int keylength); + +static int init_stream_coprocessor(void); + +static void __exit exit_stream_coprocessor(void); + +/*#define LDEBUG*/ +#ifdef LDEBUG +#define DEBUG(s) s +#define DEBUG_API(s) s +static void print_cryptocop_operation(struct cryptocop_operation *cop); +static void print_dma_descriptors(struct cryptocop_int_operation *iop); +static void print_strcop_crypto_op(struct strcop_crypto_op *cop); +static void print_lock_status(void); +static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op); +#define assert(s) do{if (!(s)) panic(#s);} while(0); +#else +#define DEBUG(s) +#define DEBUG_API(s) +#define assert(s) +#endif + + +/* Transform constants. */ +#define DES_BLOCK_LENGTH (8) +#define AES_BLOCK_LENGTH (16) +#define MD5_BLOCK_LENGTH (64) +#define SHA1_BLOCK_LENGTH (64) +#define CSUM_BLOCK_LENGTH (2) +#define MD5_STATE_LENGTH (16) +#define SHA1_STATE_LENGTH (20) + +/* The device number. */ +#define CRYPTOCOP_MAJOR (254) +#define CRYPTOCOP_MINOR (0) + + + +struct file_operations cryptocop_fops = { + owner: THIS_MODULE, + open: cryptocop_open, + release: cryptocop_release, + ioctl: cryptocop_ioctl +}; + + +static void free_cdesc(struct cryptocop_dma_desc *cdesc) +{ + DEBUG(printk("free_cdesc: cdesc 0x%p, from_pool=%d\n", cdesc, cdesc->from_pool)); + if (cdesc->free_buf) kfree(cdesc->free_buf); + + if (cdesc->from_pool) { + unsigned long int flags; + spin_lock_irqsave(&descr_pool_lock, flags); + cdesc->next = descr_pool_free_list; + descr_pool_free_list = cdesc; + ++descr_pool_no_free; + spin_unlock_irqrestore(&descr_pool_lock, flags); + } else { + kfree(cdesc); + } +} + + +static struct cryptocop_dma_desc *alloc_cdesc(int alloc_flag) +{ + int use_pool = (alloc_flag & GFP_ATOMIC) ? 1 : 0; + struct cryptocop_dma_desc *cdesc; + + if (use_pool) { + unsigned long int flags; + spin_lock_irqsave(&descr_pool_lock, flags); + if (!descr_pool_free_list) { + spin_unlock_irqrestore(&descr_pool_lock, flags); + DEBUG_API(printk("alloc_cdesc: pool is empty\n")); + return NULL; + } + cdesc = descr_pool_free_list; + descr_pool_free_list = descr_pool_free_list->next; + --descr_pool_no_free; + spin_unlock_irqrestore(&descr_pool_lock, flags); + cdesc->from_pool = 1; + } else { + cdesc = kmalloc(sizeof(struct cryptocop_dma_desc), alloc_flag); + if (!cdesc) { + DEBUG_API(printk("alloc_cdesc: kmalloc\n")); + return NULL; + } + cdesc->from_pool = 0; + } + cdesc->dma_descr = (dma_descr_data*)(((unsigned long int)cdesc + offsetof(struct cryptocop_dma_desc, dma_descr_buf) + DESCR_ALLOC_PAD) & ~0x0000001F); + + cdesc->next = NULL; + + cdesc->free_buf = NULL; + cdesc->dma_descr->out_eop = 0; + cdesc->dma_descr->in_eop = 0; + cdesc->dma_descr->intr = 0; + cdesc->dma_descr->eol = 0; + cdesc->dma_descr->wait = 0; + cdesc->dma_descr->buf = NULL; + cdesc->dma_descr->after = NULL; + + DEBUG_API(printk("alloc_cdesc: return 0x%p, cdesc->dma_descr=0x%p, from_pool=%d\n", cdesc, cdesc->dma_descr, cdesc->from_pool)); + return cdesc; +} + + +static void setup_descr_chain(struct cryptocop_dma_desc *cd) +{ + DEBUG(printk("setup_descr_chain: entering\n")); + while (cd) { + if (cd->next) { + cd->dma_descr->next = (dma_descr_data*)virt_to_phys(cd->next->dma_descr); + } else { + cd->dma_descr->next = NULL; + } + cd = cd->next; + } + DEBUG(printk("setup_descr_chain: exit\n")); +} + + +/* Create a pad descriptor for the transform. + * Return -1 for error, 0 if pad created. */ +static int create_pad_descriptor(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **pad_desc, int alloc_flag) +{ + struct cryptocop_dma_desc *cdesc = NULL; + int error = 0; + struct strcop_meta_out mo = { + .ciphsel = src_none, + .hashsel = src_none, + .csumsel = src_none + }; + char *pad; + size_t plen; + + DEBUG(printk("create_pad_descriptor: start.\n")); + /* Setup pad descriptor. */ + + DEBUG(printk("create_pad_descriptor: setting up padding.\n")); + cdesc = alloc_cdesc(alloc_flag); + if (!cdesc){ + DEBUG_API(printk("create_pad_descriptor: alloc pad desc\n")); + goto error_cleanup; + } + switch (tc->unit_no) { + case src_md5: + error = create_md5_pad(alloc_flag, tc->consumed, &pad, &plen); + if (error){ + DEBUG_API(printk("create_pad_descriptor: create_md5_pad_failed\n")); + goto error_cleanup; + } + cdesc->free_buf = pad; + mo.hashsel = src_dma; + mo.hashconf = tc->hash_conf; + mo.hashmode = tc->hash_mode; + break; + case src_sha1: + error = create_sha1_pad(alloc_flag, tc->consumed, &pad, &plen); + if (error){ + DEBUG_API(printk("create_pad_descriptor: create_sha1_pad_failed\n")); + goto error_cleanup; + } + cdesc->free_buf = pad; + mo.hashsel = src_dma; + mo.hashconf = tc->hash_conf; + mo.hashmode = tc->hash_mode; + break; + case src_csum: + if (tc->consumed % tc->blocklength){ + pad = (char*)csum_zero_pad; + plen = 1; + } else { + pad = (char*)cdesc; /* Use any pointer. */ + plen = 0; + } + mo.csumsel = src_dma; + break; + } + cdesc->dma_descr->wait = 1; + cdesc->dma_descr->out_eop = 1; /* Since this is a pad output is pushed. EOP is ok here since the padded unit is the only one active. */ + cdesc->dma_descr->buf = (char*)virt_to_phys((char*)pad); + cdesc->dma_descr->after = cdesc->dma_descr->buf + plen; + + cdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo); + *pad_desc = cdesc; + + return 0; + + error_cleanup: + if (cdesc) free_cdesc(cdesc); + return -1; +} + + +static int setup_key_dl_desc(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **kd, int alloc_flag) +{ + struct cryptocop_dma_desc *key_desc = alloc_cdesc(alloc_flag); + struct strcop_meta_out mo = {0}; + + DEBUG(printk("setup_key_dl_desc\n")); + + if (!key_desc) { + DEBUG_API(printk("setup_key_dl_desc: failed descriptor allocation.\n")); + return -ENOMEM; + } + + /* Download key. */ + if ((tc->tctx->init.alg == cryptocop_alg_aes) && (tc->tcfg->flags & CRYPTOCOP_DECRYPT)) { + /* Precook the AES decrypt key. */ + if (!tc->tctx->dec_key_set){ + get_aes_decrypt_key(tc->tctx->dec_key, tc->tctx->init.key, tc->tctx->init.keylen); + tc->tctx->dec_key_set = 1; + } + key_desc->dma_descr->buf = (char*)virt_to_phys(tc->tctx->dec_key); + key_desc->dma_descr->after = key_desc->dma_descr->buf + tc->tctx->init.keylen/8; + } else { + key_desc->dma_descr->buf = (char*)virt_to_phys(tc->tctx->init.key); + key_desc->dma_descr->after = key_desc->dma_descr->buf + tc->tctx->init.keylen/8; + } + /* Setup metadata. */ + mo.dlkey = 1; + switch (tc->tctx->init.keylen) { + case 64: + mo.decrypt = 0; + mo.hashmode = 0; + break; + case 128: + mo.decrypt = 0; + mo.hashmode = 1; + break; + case 192: + mo.decrypt = 1; + mo.hashmode = 0; + break; + case 256: + mo.decrypt = 1; + mo.hashmode = 1; + break; + default: + break; + } + mo.ciphsel = mo.hashsel = mo.csumsel = src_none; + key_desc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo); + + key_desc->dma_descr->out_eop = 1; + key_desc->dma_descr->wait = 1; + key_desc->dma_descr->intr = 0; + + *kd = key_desc; + return 0; +} + +static int setup_cipher_iv_desc(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **id, int alloc_flag) +{ + struct cryptocop_dma_desc *iv_desc = alloc_cdesc(alloc_flag); + struct strcop_meta_out mo = {0}; + + DEBUG(printk("setup_cipher_iv_desc\n")); + + if (!iv_desc) { + DEBUG_API(printk("setup_cipher_iv_desc: failed CBC IV descriptor allocation.\n")); + return -ENOMEM; + } + /* Download IV. */ + iv_desc->dma_descr->buf = (char*)virt_to_phys(tc->tcfg->iv); + iv_desc->dma_descr->after = iv_desc->dma_descr->buf + tc->blocklength; + + /* Setup metadata. */ + mo.hashsel = mo.csumsel = src_none; + mo.ciphsel = src_dma; + mo.ciphconf = tc->ciph_conf; + mo.cbcmode = tc->cbcmode; + + iv_desc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo); + + iv_desc->dma_descr->out_eop = 0; + iv_desc->dma_descr->wait = 1; + iv_desc->dma_descr->intr = 0; + + *id = iv_desc; + return 0; +} + +/* Map the ouput length of the transform to operation output starting on the inject index. */ +static int create_input_descriptors(struct cryptocop_operation *operation, struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **id, int alloc_flag) +{ + int err = 0; + struct cryptocop_dma_desc head = {0}; + struct cryptocop_dma_desc *outdesc = &head; + size_t iov_offset = 0; + size_t out_ix = 0; + int outiov_ix = 0; + struct strcop_meta_in mi = {0}; + + size_t out_length = tc->produced; + int rem_length; + int dlength; + + assert(out_length != 0); + if (((tc->produced + tc->tcfg->inject_ix) > operation->tfrm_op.outlen) || (tc->produced && (operation->tfrm_op.outlen == 0))) { + DEBUG_API(printk("create_input_descriptors: operation outdata too small\n")); + return -EINVAL; + } + /* Traverse the out iovec until the result inject index is reached. */ + while ((outiov_ix < operation->tfrm_op.outcount) && ((out_ix + operation->tfrm_op.outdata[outiov_ix].iov_len) <= tc->tcfg->inject_ix)){ + out_ix += operation->tfrm_op.outdata[outiov_ix].iov_len; + outiov_ix++; + } + if (outiov_ix >= operation->tfrm_op.outcount){ + DEBUG_API(printk("create_input_descriptors: operation outdata too small\n")); + return -EINVAL; + } + iov_offset = tc->tcfg->inject_ix - out_ix; + mi.dmasel = tc->unit_no; + + /* Setup the output descriptors. */ + while ((out_length > 0) && (outiov_ix < operation->tfrm_op.outcount)) { + outdesc->next = alloc_cdesc(alloc_flag); + if (!outdesc->next) { + DEBUG_API(printk("create_input_descriptors: alloc_cdesc\n")); + err = -ENOMEM; + goto error_cleanup; + } + outdesc = outdesc->next; + rem_length = operation->tfrm_op.outdata[outiov_ix].iov_len - iov_offset; + dlength = (out_length < rem_length) ? out_length : rem_length; + + DEBUG(printk("create_input_descriptors:\n" + "outiov_ix=%d, rem_length=%d, dlength=%d\n" + "iov_offset=%d, outdata[outiov_ix].iov_len=%d\n" + "outcount=%d, outiov_ix=%d\n", + outiov_ix, rem_length, dlength, iov_offset, operation->tfrm_op.outdata[outiov_ix].iov_len, operation->tfrm_op.outcount, outiov_ix)); + + outdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.outdata[outiov_ix].iov_base + iov_offset); + outdesc->dma_descr->after = outdesc->dma_descr->buf + dlength; + outdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi); + + out_length -= dlength; + iov_offset += dlength; + if (iov_offset >= operation->tfrm_op.outdata[outiov_ix].iov_len) { + iov_offset = 0; + ++outiov_ix; + } + } + if (out_length > 0){ + DEBUG_API(printk("create_input_descriptors: not enough room for output, %d remained\n", out_length)); + err = -EINVAL; + goto error_cleanup; + } + /* Set sync in last descriptor. */ + mi.sync = 1; + outdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi); + + *id = head.next; + return 0; + + error_cleanup: + while (head.next) { + outdesc = head.next->next; + free_cdesc(head.next); + head.next = outdesc; + } + return err; +} + + +static int create_output_descriptors(struct cryptocop_operation *operation, int *iniov_ix, int *iniov_offset, size_t desc_len, struct cryptocop_dma_desc **current_out_cdesc, struct strcop_meta_out *meta_out, int alloc_flag) +{ + while (desc_len != 0) { + struct cryptocop_dma_desc *cdesc; + int rem_length = operation->tfrm_op.indata[*iniov_ix].iov_len - *iniov_offset; + int dlength = (desc_len < rem_length) ? desc_len : rem_length; + + cdesc = alloc_cdesc(alloc_flag); + if (!cdesc) { + DEBUG_API(printk("create_output_descriptors: alloc_cdesc\n")); + return -ENOMEM; + } + (*current_out_cdesc)->next = cdesc; + (*current_out_cdesc) = cdesc; + + cdesc->free_buf = NULL; + + cdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.indata[*iniov_ix].iov_base + *iniov_offset); + cdesc->dma_descr->after = cdesc->dma_descr->buf + dlength; + + desc_len -= dlength; + *iniov_offset += dlength; + assert(desc_len >= 0); + if (*iniov_offset >= operation->tfrm_op.indata[*iniov_ix].iov_len) { + *iniov_offset = 0; + ++(*iniov_ix); + if (*iniov_ix > operation->tfrm_op.incount) { + DEBUG_API(printk("create_output_descriptors: not enough indata in operation.")); + return -EINVAL; + } + } + cdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, (*meta_out)); + } /* while (desc_len != 0) */ + /* Last DMA descriptor gets a 'wait' bit to signal expected change in metadata. */ + (*current_out_cdesc)->dma_descr->wait = 1; /* This will set extraneous WAIT in some situations, e.g. when padding hashes and checksums. */ + + return 0; +} + + +static int append_input_descriptors(struct cryptocop_operation *operation, struct cryptocop_dma_desc **current_in_cdesc, struct cryptocop_dma_desc **current_out_cdesc, struct cryptocop_tfrm_ctx *tc, int alloc_flag) +{ + DEBUG(printk("append_input_descriptors, tc=0x%p, unit_no=%d\n", tc, tc->unit_no)); + if (tc->tcfg) { + int failed = 0; + struct cryptocop_dma_desc *idescs = NULL; + DEBUG(printk("append_input_descriptors: pushing output, consumed %d produced %d bytes.\n", tc->consumed, tc->produced)); + if (tc->pad_descs) { + DEBUG(printk("append_input_descriptors: append pad descriptors to DMA out list.\n")); + while (tc->pad_descs) { + DEBUG(printk("append descriptor 0x%p\n", tc->pad_descs)); + (*current_out_cdesc)->next = tc->pad_descs; + tc->pad_descs = tc->pad_descs->next; + (*current_out_cdesc) = (*current_out_cdesc)->next; + } + } + + /* Setup and append output descriptors to DMA in list. */ + if (tc->unit_no == src_dma){ + /* mem2mem. Setup DMA in descriptors to discard all input prior to the requested mem2mem data. */ + struct strcop_meta_in mi = {.sync = 0, .dmasel = src_dma}; + unsigned int start_ix = tc->start_ix; + while (start_ix){ + unsigned int desclen = start_ix < MEM2MEM_DISCARD_BUF_LENGTH ? start_ix : MEM2MEM_DISCARD_BUF_LENGTH; + (*current_in_cdesc)->next = alloc_cdesc(alloc_flag); + if (!(*current_in_cdesc)->next){ + DEBUG_API(printk("append_input_descriptors: alloc_cdesc mem2mem discard failed\n")); + return -ENOMEM; + } + (*current_in_cdesc) = (*current_in_cdesc)->next; + (*current_in_cdesc)->dma_descr->buf = (char*)virt_to_phys(mem2mem_discard_buf); + (*current_in_cdesc)->dma_descr->after = (*current_in_cdesc)->dma_descr->buf + desclen; + (*current_in_cdesc)->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi); + start_ix -= desclen; + } + mi.sync = 1; + (*current_in_cdesc)->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi); + } + + failed = create_input_descriptors(operation, tc, &idescs, alloc_flag); + if (failed){ + DEBUG_API(printk("append_input_descriptors: output descriptor setup failed\n")); + return failed; + } + DEBUG(printk("append_input_descriptors: append output descriptors to DMA in list.\n")); + while (idescs) { + DEBUG(printk("append descriptor 0x%p\n", idescs)); + (*current_in_cdesc)->next = idescs; + idescs = idescs->next; + (*current_in_cdesc) = (*current_in_cdesc)->next; + } + } + return 0; +} + + + +static int cryptocop_setup_dma_list(struct cryptocop_operation *operation, struct cryptocop_int_operation **int_op, int alloc_flag) +{ + struct cryptocop_session *sess; + struct cryptocop_transform_ctx *tctx; + + struct cryptocop_tfrm_ctx digest_ctx = { + .previous_src = src_none, + .current_src = src_none, + .start_ix = 0, + .requires_padding = 1, + .strict_block_length = 0, + .hash_conf = 0, + .hash_mode = 0, + .ciph_conf = 0, + .cbcmode = 0, + .decrypt = 0, + .consumed = 0, + .produced = 0, + .pad_descs = NULL, + .active = 0, + .done = 0, + .prev_src = NULL, + .curr_src = NULL, + .tcfg = NULL}; + struct cryptocop_tfrm_ctx cipher_ctx = { + .previous_src = src_none, + .current_src = src_none, + .start_ix = 0, + .requires_padding = 0, + .strict_block_length = 1, + .hash_conf = 0, + .hash_mode = 0, + .ciph_conf = 0, + .cbcmode = 0, + .decrypt = 0, + .consumed = 0, + .produced = 0, + .pad_descs = NULL, + .active = 0, + .done = 0, + .prev_src = NULL, + .curr_src = NULL, + .tcfg = NULL}; + struct cryptocop_tfrm_ctx csum_ctx = { + .previous_src = src_none, + .current_src = src_none, + .start_ix = 0, + .blocklength = 2, + .requires_padding = 1, + .strict_block_length = 0, + .hash_conf = 0, + .hash_mode = 0, + .ciph_conf = 0, + .cbcmode = 0, + .decrypt = 0, + .consumed = 0, + .produced = 0, + .pad_descs = NULL, + .active = 0, + .done = 0, + .tcfg = NULL, + .prev_src = NULL, + .curr_src = NULL, + .unit_no = src_csum}; + struct cryptocop_tfrm_cfg *tcfg = operation->tfrm_op.tfrm_cfg; + + unsigned int indata_ix = 0; + + /* iovec accounting. */ + int iniov_ix = 0; + int iniov_offset = 0; + + /* Operation descriptor cfg traversal pointer. */ + struct cryptocop_desc *odsc; + + int failed = 0; + /* List heads for allocated descriptors. */ + struct cryptocop_dma_desc out_cdesc_head = {0}; + struct cryptocop_dma_desc in_cdesc_head = {0}; + + struct cryptocop_dma_desc *current_out_cdesc = &out_cdesc_head; + struct cryptocop_dma_desc *current_in_cdesc = &in_cdesc_head; + + struct cryptocop_tfrm_ctx *output_tc = NULL; + void *iop_alloc_ptr; + + assert(operation != NULL); + assert(int_op != NULL); + + DEBUG(printk("cryptocop_setup_dma_list: start\n")); + DEBUG(print_cryptocop_operation(operation)); + + sess = get_session(operation->sid); + if (!sess) { + DEBUG_API(printk("cryptocop_setup_dma_list: no session found for operation.\n")); + failed = -EINVAL; + goto error_cleanup; + } + iop_alloc_ptr = kmalloc(DESCR_ALLOC_PAD + sizeof(struct cryptocop_int_operation), alloc_flag); + if (!iop_alloc_ptr) { + DEBUG_API(printk("cryptocop_setup_dma_list: kmalloc cryptocop_int_operation\n")); + failed = -ENOMEM; + goto error_cleanup; + } + (*int_op) = (struct cryptocop_int_operation*)(((unsigned long int)(iop_alloc_ptr + DESCR_ALLOC_PAD + offsetof(struct cryptocop_int_operation, ctx_out)) & ~0x0000001F) - offsetof(struct cryptocop_int_operation, ctx_out)); + DEBUG(memset((*int_op), 0xff, sizeof(struct cryptocop_int_operation))); + (*int_op)->alloc_ptr = iop_alloc_ptr; + DEBUG(printk("cryptocop_setup_dma_list: *int_op=0x%p, alloc_ptr=0x%p\n", *int_op, (*int_op)->alloc_ptr)); + + (*int_op)->sid = operation->sid; + (*int_op)->cdesc_out = NULL; + (*int_op)->cdesc_in = NULL; + (*int_op)->tdes_mode = cryptocop_3des_ede; + (*int_op)->csum_mode = cryptocop_csum_le; + (*int_op)->ddesc_out = NULL; + (*int_op)->ddesc_in = NULL; + + /* Scan operation->tfrm_op.tfrm_cfg for bad configuration and set up the local contexts. */ + if (!tcfg) { + DEBUG_API(printk("cryptocop_setup_dma_list: no configured transforms in operation.\n")); + failed = -EINVAL; + goto error_cleanup; + } + while (tcfg) { + tctx = get_transform_ctx(sess, tcfg->tid); + if (!tctx) { + DEBUG_API(printk("cryptocop_setup_dma_list: no transform id %d in session.\n", tcfg->tid)); + failed = -EINVAL; + goto error_cleanup; + } + if (tcfg->inject_ix > operation->tfrm_op.outlen){ + DEBUG_API(printk("cryptocop_setup_dma_list: transform id %d inject_ix (%d) > operation->tfrm_op.outlen(%d)", tcfg->tid, tcfg->inject_ix, operation->tfrm_op.outlen)); + failed = -EINVAL; + goto error_cleanup; + } + switch (tctx->init.alg){ + case cryptocop_alg_mem2mem: + if (cipher_ctx.tcfg != NULL){ + DEBUG_API(printk("cryptocop_setup_dma_list: multiple ciphers in operation.\n")); + failed = -EINVAL; + goto error_cleanup; + } + /* mem2mem is handled as a NULL cipher. */ + cipher_ctx.cbcmode = 0; + cipher_ctx.decrypt = 0; + cipher_ctx.blocklength = 1; + cipher_ctx.ciph_conf = 0; + cipher_ctx.unit_no = src_dma; + cipher_ctx.tcfg = tcfg; + cipher_ctx.tctx = tctx; + break; + case cryptocop_alg_des: + case cryptocop_alg_3des: + case cryptocop_alg_aes: + /* cipher */ + if (cipher_ctx.tcfg != NULL){ + DEBUG_API(printk("cryptocop_setup_dma_list: multiple ciphers in operation.\n")); + failed = -EINVAL; + goto error_cleanup; + } + cipher_ctx.tcfg = tcfg; + cipher_ctx.tctx = tctx; + if (cipher_ctx.tcfg->flags & CRYPTOCOP_DECRYPT){ + cipher_ctx.decrypt = 1; + } + switch (tctx->init.cipher_mode) { + case cryptocop_cipher_mode_ecb: + cipher_ctx.cbcmode = 0; + break; + case cryptocop_cipher_mode_cbc: + cipher_ctx.cbcmode = 1; + break; + default: + DEBUG_API(printk("cryptocop_setup_dma_list: cipher_ctx, bad cipher mode==%d\n", tctx->init.cipher_mode)); + failed = -EINVAL; + goto error_cleanup; + } + DEBUG(printk("cryptocop_setup_dma_list: cipher_ctx, set CBC mode==%d\n", cipher_ctx.cbcmode)); + switch (tctx->init.alg){ + case cryptocop_alg_des: + cipher_ctx.ciph_conf = 0; + cipher_ctx.unit_no = src_des; + cipher_ctx.blocklength = DES_BLOCK_LENGTH; + break; + case cryptocop_alg_3des: + cipher_ctx.ciph_conf = 1; + cipher_ctx.unit_no = src_des; + cipher_ctx.blocklength = DES_BLOCK_LENGTH; + break; + case cryptocop_alg_aes: + cipher_ctx.ciph_conf = 2; + cipher_ctx.unit_no = src_aes; + cipher_ctx.blocklength = AES_BLOCK_LENGTH; + break; + default: + panic("cryptocop_setup_dma_list: impossible algorithm %d\n", tctx->init.alg); + } + (*int_op)->tdes_mode = tctx->init.tdes_mode; + break; + case cryptocop_alg_md5: + case cryptocop_alg_sha1: + /* digest */ + if (digest_ctx.tcfg != NULL){ + DEBUG_API(printk("cryptocop_setup_dma_list: multiple digests in operation.\n")); + failed = -EINVAL; + goto error_cleanup; + } + digest_ctx.tcfg = tcfg; + digest_ctx.tctx = tctx; + digest_ctx.hash_mode = 0; /* Don't use explicit IV in this API. */ + switch (tctx->init.alg){ + case cryptocop_alg_md5: + digest_ctx.blocklength = MD5_BLOCK_LENGTH; + digest_ctx.unit_no = src_md5; + digest_ctx.hash_conf = 1; /* 1 => MD-5 */ + break; + case cryptocop_alg_sha1: + digest_ctx.blocklength = SHA1_BLOCK_LENGTH; + digest_ctx.unit_no = src_sha1; + digest_ctx.hash_conf = 0; /* 0 => SHA-1 */ + break; + default: + panic("cryptocop_setup_dma_list: impossible digest algorithm\n"); + } + break; + case cryptocop_alg_csum: + /* digest */ + if (csum_ctx.tcfg != NULL){ + DEBUG_API(printk("cryptocop_setup_dma_list: multiple checksums in operation.\n")); + failed = -EINVAL; + goto error_cleanup; + } + (*int_op)->csum_mode = tctx->init.csum_mode; + csum_ctx.tcfg = tcfg; + csum_ctx.tctx = tctx; + break; + default: + /* no algorithm. */ + DEBUG_API(printk("cryptocop_setup_dma_list: invalid algorithm %d specified in tfrm %d.\n", tctx->init.alg, tcfg->tid)); + failed = -EINVAL; + goto error_cleanup; + } + tcfg = tcfg->next; + } + /* Download key if a cipher is used. */ + if (cipher_ctx.tcfg && (cipher_ctx.tctx->init.alg != cryptocop_alg_mem2mem)){ + struct cryptocop_dma_desc *key_desc = NULL; + + failed = setup_key_dl_desc(&cipher_ctx, &key_desc, alloc_flag); + if (failed) { + DEBUG_API(printk("cryptocop_setup_dma_list: setup key dl\n")); + goto error_cleanup; + } + current_out_cdesc->next = key_desc; + current_out_cdesc = key_desc; + indata_ix += (unsigned int)(key_desc->dma_descr->after - key_desc->dma_descr->buf); + + /* Download explicit IV if a cipher is used and CBC mode and explicit IV selected. */ + if ((cipher_ctx.tctx->init.cipher_mode == cryptocop_cipher_mode_cbc) && (cipher_ctx.tcfg->flags & CRYPTOCOP_EXPLICIT_IV)) { + struct cryptocop_dma_desc *iv_desc = NULL; + + DEBUG(printk("cryptocop_setup_dma_list: setup cipher CBC IV descriptor.\n")); + + failed = setup_cipher_iv_desc(&cipher_ctx, &iv_desc, alloc_flag); + if (failed) { + DEBUG_API(printk("cryptocop_setup_dma_list: CBC IV descriptor.\n")); + goto error_cleanup; + } + current_out_cdesc->next = iv_desc; + current_out_cdesc = iv_desc; + indata_ix += (unsigned int)(iv_desc->dma_descr->after - iv_desc->dma_descr->buf); + } + } + + /* Process descriptors. */ + odsc = operation->tfrm_op.desc; + while (odsc) { + struct cryptocop_desc_cfg *dcfg = odsc->cfg; + struct strcop_meta_out meta_out = {0}; + size_t desc_len = odsc->length; + int active_count, eop_needed_count; + + output_tc = NULL; + + DEBUG(printk("cryptocop_setup_dma_list: parsing an operation descriptor\n")); + + while (dcfg) { + struct cryptocop_tfrm_ctx *tc = NULL; + + DEBUG(printk("cryptocop_setup_dma_list: parsing an operation descriptor configuration.\n")); + /* Get the local context for the transform and mark it as the output unit if it produces output. */ + if (digest_ctx.tcfg && (digest_ctx.tcfg->tid == dcfg->tid)){ + tc = &digest_ctx; + } else if (cipher_ctx.tcfg && (cipher_ctx.tcfg->tid == dcfg->tid)){ + tc = &cipher_ctx; + } else if (csum_ctx.tcfg && (csum_ctx.tcfg->tid == dcfg->tid)){ + tc = &csum_ctx; + } + if (!tc) { + DEBUG_API(printk("cryptocop_setup_dma_list: invalid transform %d specified in descriptor.\n", dcfg->tid)); + failed = -EINVAL; + goto error_cleanup; + } + if (tc->done) { + DEBUG_API(printk("cryptocop_setup_dma_list: completed transform %d reused.\n", dcfg->tid)); + failed = -EINVAL; + goto error_cleanup; + } + if (!tc->active) { + tc->start_ix = indata_ix; + tc->active = 1; + } + + tc->previous_src = tc->current_src; + tc->prev_src = tc->curr_src; + /* Map source unit id to DMA source config. */ + switch (dcfg->src){ + case cryptocop_source_dma: + tc->current_src = src_dma; + break; + case cryptocop_source_des: + tc->current_src = src_des; + break; + case cryptocop_source_3des: + tc->current_src = src_des; + break; + case cryptocop_source_aes: + tc->current_src = src_aes; + break; + case cryptocop_source_md5: + case cryptocop_source_sha1: + case cryptocop_source_csum: + case cryptocop_source_none: + default: + /* We do not allow using accumulating style units (SHA-1, MD5, checksum) as sources to other units. + */ + DEBUG_API(printk("cryptocop_setup_dma_list: bad unit source configured %d.\n", dcfg->src)); + failed = -EINVAL; + goto error_cleanup; + } + if (tc->current_src != src_dma) { + /* Find the unit we are sourcing from. */ + if (digest_ctx.unit_no == tc->current_src){ + tc->curr_src = &digest_ctx; + } else if (cipher_ctx.unit_no == tc->current_src){ + tc->curr_src = &cipher_ctx; + } else if (csum_ctx.unit_no == tc->current_src){ + tc->curr_src = &csum_ctx; + } + if ((tc->curr_src == tc) && (tc->unit_no != src_dma)){ + DEBUG_API(printk("cryptocop_setup_dma_list: unit %d configured to source from itself.\n", tc->unit_no)); + failed = -EINVAL; + goto error_cleanup; + } + } else { + tc->curr_src = NULL; + } + + /* Detect source switch. */ + DEBUG(printk("cryptocop_setup_dma_list: tc->active=%d tc->unit_no=%d tc->current_src=%d tc->previous_src=%d, tc->curr_src=0x%p, tc->prev_srv=0x%p\n", tc->active, tc->unit_no, tc->current_src, tc->previous_src, tc->curr_src, tc->prev_src)); + if (tc->active && (tc->current_src != tc->previous_src)) { + /* Only allow source switch when both the old source unit and the new one have + * no pending data to process (i.e. the consumed length must be a multiple of the + * transform blocklength). */ + /* Note: if the src == NULL we are actually sourcing from DMA out. */ + if (((tc->prev_src != NULL) && (tc->prev_src->consumed % tc->prev_src->blocklength)) || + ((tc->curr_src != NULL) && (tc->curr_src->consumed % tc->curr_src->blocklength))) + { + DEBUG_API(printk("cryptocop_setup_dma_list: can only disconnect from or connect to a unit on a multiple of the blocklength, old: cons=%d, prod=%d, block=%d, new: cons=%d prod=%d, block=%d.\n", tc->prev_src ? tc->prev_src->consumed : INT_MIN, tc->prev_src ? tc->prev_src->produced : INT_MIN, tc->prev_src ? tc->prev_src->blocklength : INT_MIN, tc->curr_src ? tc->curr_src->consumed : INT_MIN, tc->curr_src ? tc->curr_src->produced : INT_MIN, tc->curr_src ? tc->curr_src->blocklength : INT_MIN)); + failed = -EINVAL; + goto error_cleanup; + } + } + /* Detect unit deactivation. */ + if (dcfg->last) { + /* Length check of this is handled below. */ + tc->done = 1; + } + dcfg = dcfg->next; + } /* while (dcfg) */ + DEBUG(printk("cryptocop_setup_dma_list: parsing operation descriptor configuration complete.\n")); + + if (cipher_ctx.active && (cipher_ctx.curr_src != NULL) && !cipher_ctx.curr_src->active){ + DEBUG_API(printk("cryptocop_setup_dma_list: cipher source from inactive unit %d\n", cipher_ctx.curr_src->unit_no)); + failed = -EINVAL; + goto error_cleanup; + } + if (digest_ctx.active && (digest_ctx.curr_src != NULL) && !digest_ctx.curr_src->active){ + DEBUG_API(printk("cryptocop_setup_dma_list: digest source from inactive unit %d\n", digest_ctx.curr_src->unit_no)); + failed = -EINVAL; + goto error_cleanup; + } + if (csum_ctx.active && (csum_ctx.curr_src != NULL) && !csum_ctx.curr_src->active){ + DEBUG_API(printk("cryptocop_setup_dma_list: cipher source from inactive unit %d\n", csum_ctx.curr_src->unit_no)); + failed = -EINVAL; + goto error_cleanup; + } + + /* Update consumed and produced lengths. + + The consumed length accounting here is actually cheating. If a unit source from DMA (or any + other unit that process data in blocks of one octet) it is correct, but if it source from a + block processing unit, i.e. a cipher, it will be temporarily incorrect at some times. However + since it is only allowed--by the HW--to change source to or from a block processing unit at times where that + unit has processed an exact multiple of its block length the end result will be correct. + Beware that if the source change restriction change this code will need to be (much) reworked. + */ + DEBUG(printk("cryptocop_setup_dma_list: desc->length=%d, desc_len=%d.\n", odsc->length, desc_len)); + + if (csum_ctx.active) { + csum_ctx.consumed += desc_len; + if (csum_ctx.done) { + csum_ctx.produced = 2; + } + DEBUG(printk("cryptocop_setup_dma_list: csum_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", csum_ctx.consumed, csum_ctx.produced, csum_ctx.blocklength)); + } + if (digest_ctx.active) { + digest_ctx.consumed += desc_len; + if (digest_ctx.done) { + if (digest_ctx.unit_no == src_md5) { + digest_ctx.produced = MD5_STATE_LENGTH; + } else { + digest_ctx.produced = SHA1_STATE_LENGTH; + } + } + DEBUG(printk("cryptocop_setup_dma_list: digest_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", digest_ctx.consumed, digest_ctx.produced, digest_ctx.blocklength)); + } + if (cipher_ctx.active) { + /* Ciphers are allowed only to source from DMA out. That is filtered above. */ + assert(cipher_ctx.current_src == src_dma); + cipher_ctx.consumed += desc_len; + cipher_ctx.produced = cipher_ctx.blocklength * (cipher_ctx.consumed / cipher_ctx.blocklength); + if (cipher_ctx.cbcmode && !(cipher_ctx.tcfg->flags & CRYPTOCOP_EXPLICIT_IV) && cipher_ctx.produced){ + cipher_ctx.produced -= cipher_ctx.blocklength; /* Compensate for CBC iv. */ + } + DEBUG(printk("cryptocop_setup_dma_list: cipher_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", cipher_ctx.consumed, cipher_ctx.produced, cipher_ctx.blocklength)); + } + + /* Setup the DMA out descriptors. */ + /* Configure the metadata. */ + active_count = 0; + eop_needed_count = 0; + if (cipher_ctx.active) { + ++active_count; + if (cipher_ctx.unit_no == src_dma){ + /* mem2mem */ + meta_out.ciphsel = src_none; + } else { + meta_out.ciphsel = cipher_ctx.current_src; + } + meta_out.ciphconf = cipher_ctx.ciph_conf; + meta_out.cbcmode = cipher_ctx.cbcmode; + meta_out.decrypt = cipher_ctx.decrypt; + DEBUG(printk("set ciphsel=%d ciphconf=%d cbcmode=%d decrypt=%d\n", meta_out.ciphsel, meta_out.ciphconf, meta_out.cbcmode, meta_out.decrypt)); + if (cipher_ctx.done) ++eop_needed_count; + } else { + meta_out.ciphsel = src_none; + } + + if (digest_ctx.active) { + ++active_count; + meta_out.hashsel = digest_ctx.current_src; + meta_out.hashconf = digest_ctx.hash_conf; + meta_out.hashmode = 0; /* Explicit mode is not used here. */ + DEBUG(printk("set hashsel=%d hashconf=%d hashmode=%d\n", meta_out.hashsel, meta_out.hashconf, meta_out.hashmode)); + if (digest_ctx.done) { + assert(digest_ctx.pad_descs == NULL); + failed = create_pad_descriptor(&digest_ctx, &digest_ctx.pad_descs, alloc_flag); + if (failed) { + DEBUG_API(printk("cryptocop_setup_dma_list: failed digest pad creation.\n")); + goto error_cleanup; + } + } + } else { + meta_out.hashsel = src_none; + } + + if (csum_ctx.active) { + ++active_count; + meta_out.csumsel = csum_ctx.current_src; + if (csum_ctx.done) { + assert(csum_ctx.pad_descs == NULL); + failed = create_pad_descriptor(&csum_ctx, &csum_ctx.pad_descs, alloc_flag); + if (failed) { + DEBUG_API(printk("cryptocop_setup_dma_list: failed csum pad creation.\n")); + goto error_cleanup; + } + } + } else { + meta_out.csumsel = src_none; + } + DEBUG(printk("cryptocop_setup_dma_list: %d eop needed, %d active units\n", eop_needed_count, active_count)); + /* Setup DMA out descriptors for the indata. */ + failed = create_output_descriptors(operation, &iniov_ix, &iniov_offset, desc_len, ¤t_out_cdesc, &meta_out, alloc_flag); + if (failed) { + DEBUG_API(printk("cryptocop_setup_dma_list: create_output_descriptors %d\n", failed)); + goto error_cleanup; + } + /* Setup out EOP. If there are active units that are not done here they cannot get an EOP + * so we ust setup a zero length descriptor to DMA to signal EOP only to done units. + * If there is a pad descriptor EOP for the padded unit will be EOPed by it. + */ + assert(active_count >= eop_needed_count); + assert((eop_needed_count == 0) || (eop_needed_count == 1)); + if (eop_needed_count) { + /* This means that the bulk operation (cipeher/m2m) is terminated. */ + if (active_count > 1) { + /* Use zero length EOP descriptor. */ + struct cryptocop_dma_desc *ed = alloc_cdesc(alloc_flag); + struct strcop_meta_out ed_mo = {0}; + if (!ed) { + DEBUG_API(printk("cryptocop_setup_dma_list: alloc EOP descriptor for cipher\n")); + failed = -ENOMEM; + goto error_cleanup; + } + + assert(cipher_ctx.active && cipher_ctx.done); + + if (cipher_ctx.unit_no == src_dma){ + /* mem2mem */ + ed_mo.ciphsel = src_none; + } else { + ed_mo.ciphsel = cipher_ctx.current_src; + } + ed_mo.ciphconf = cipher_ctx.ciph_conf; + ed_mo.cbcmode = cipher_ctx.cbcmode; + ed_mo.decrypt = cipher_ctx.decrypt; + + ed->free_buf = NULL; + ed->dma_descr->wait = 1; + ed->dma_descr->out_eop = 1; + + ed->dma_descr->buf = (char*)virt_to_phys(&ed); /* Use any valid physical address for zero length descriptor. */ + ed->dma_descr->after = ed->dma_descr->buf; + ed->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, ed_mo); + current_out_cdesc->next = ed; + current_out_cdesc = ed; + } else { + /* Set EOP in the current out descriptor since the only active module is + * the one needing the EOP. */ + + current_out_cdesc->dma_descr->out_eop = 1; + } + } + + if (cipher_ctx.done && cipher_ctx.active) cipher_ctx.active = 0; + if (digest_ctx.done && digest_ctx.active) digest_ctx.active = 0; + if (csum_ctx.done && csum_ctx.active) csum_ctx.active = 0; + indata_ix += odsc->length; + odsc = odsc->next; + } /* while (odsc) */ /* Process descriptors. */ + DEBUG(printk("cryptocop_setup_dma_list: done parsing operation descriptors\n")); + if (cipher_ctx.tcfg && (cipher_ctx.active || !cipher_ctx.done)){ + DEBUG_API(printk("cryptocop_setup_dma_list: cipher operation not terminated.\n")); + failed = -EINVAL; + goto error_cleanup; + } + if (digest_ctx.tcfg && (digest_ctx.active || !digest_ctx.done)){ + DEBUG_API(printk("cryptocop_setup_dma_list: digest operation not terminated.\n")); + failed = -EINVAL; + goto error_cleanup; + } + if (csum_ctx.tcfg && (csum_ctx.active || !csum_ctx.done)){ + DEBUG_API(printk("cryptocop_setup_dma_list: csum operation not terminated.\n")); + failed = -EINVAL; + goto error_cleanup; + } + + failed = append_input_descriptors(operation, ¤t_in_cdesc, ¤t_out_cdesc, &cipher_ctx, alloc_flag); + if (failed){ + DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed)); + goto error_cleanup; + } + failed = append_input_descriptors(operation, ¤t_in_cdesc, ¤t_out_cdesc, &digest_ctx, alloc_flag); + if (failed){ + DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed)); + goto error_cleanup; + } + failed = append_input_descriptors(operation, ¤t_in_cdesc, ¤t_out_cdesc, &csum_ctx, alloc_flag); + if (failed){ + DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed)); + goto error_cleanup; + } + + DEBUG(printk("cryptocop_setup_dma_list: int_op=0x%p, *int_op=0x%p\n", int_op, *int_op)); + (*int_op)->cdesc_out = out_cdesc_head.next; + (*int_op)->cdesc_in = in_cdesc_head.next; + DEBUG(printk("cryptocop_setup_dma_list: out_cdesc_head=0x%p in_cdesc_head=0x%p\n", (*int_op)->cdesc_out, (*int_op)->cdesc_in)); + + setup_descr_chain(out_cdesc_head.next); + setup_descr_chain(in_cdesc_head.next); + + /* Last but not least: mark the last DMA in descriptor for a INTR and EOL and the the + * last DMA out descriptor for EOL. + */ + current_in_cdesc->dma_descr->intr = 1; + current_in_cdesc->dma_descr->eol = 1; + current_out_cdesc->dma_descr->eol = 1; + + /* Setup DMA contexts. */ + (*int_op)->ctx_out.next = NULL; + (*int_op)->ctx_out.eol = 1; + (*int_op)->ctx_out.intr = 0; + (*int_op)->ctx_out.store_mode = 0; + (*int_op)->ctx_out.en = 0; + (*int_op)->ctx_out.dis = 0; + (*int_op)->ctx_out.md0 = 0; + (*int_op)->ctx_out.md1 = 0; + (*int_op)->ctx_out.md2 = 0; + (*int_op)->ctx_out.md3 = 0; + (*int_op)->ctx_out.md4 = 0; + (*int_op)->ctx_out.saved_data = (dma_descr_data*)virt_to_phys((*int_op)->cdesc_out->dma_descr); + (*int_op)->ctx_out.saved_data_buf = (*int_op)->cdesc_out->dma_descr->buf; /* Already physical address. */ + + (*int_op)->ctx_in.next = NULL; + (*int_op)->ctx_in.eol = 1; + (*int_op)->ctx_in.intr = 0; + (*int_op)->ctx_in.store_mode = 0; + (*int_op)->ctx_in.en = 0; + (*int_op)->ctx_in.dis = 0; + (*int_op)->ctx_in.md0 = 0; + (*int_op)->ctx_in.md1 = 0; + (*int_op)->ctx_in.md2 = 0; + (*int_op)->ctx_in.md3 = 0; + (*int_op)->ctx_in.md4 = 0; + + (*int_op)->ctx_in.saved_data = (dma_descr_data*)virt_to_phys((*int_op)->cdesc_in->dma_descr); + (*int_op)->ctx_in.saved_data_buf = (*int_op)->cdesc_in->dma_descr->buf; /* Already physical address. */ + + DEBUG(printk("cryptocop_setup_dma_list: done\n")); + return 0; + +error_cleanup: + { + /* Free all allocated resources. */ + struct cryptocop_dma_desc *tmp_cdesc; + while (digest_ctx.pad_descs){ + tmp_cdesc = digest_ctx.pad_descs->next; + free_cdesc(digest_ctx.pad_descs); + digest_ctx.pad_descs = tmp_cdesc; + } + while (csum_ctx.pad_descs){ + tmp_cdesc = csum_ctx.pad_descs->next; + free_cdesc(csum_ctx.pad_descs); + csum_ctx.pad_descs = tmp_cdesc; + } + assert(cipher_ctx.pad_descs == NULL); /* The ciphers are never padded. */ + + if (*int_op != NULL) delete_internal_operation(*int_op); + } + DEBUG_API(printk("cryptocop_setup_dma_list: done with error %d\n", failed)); + return failed; +} + + +static void delete_internal_operation(struct cryptocop_int_operation *iop) +{ + void *ptr = iop->alloc_ptr; + struct cryptocop_dma_desc *cd = iop->cdesc_out; + struct cryptocop_dma_desc *next; + + DEBUG(printk("delete_internal_operation: iop=0x%p, alloc_ptr=0x%p\n", iop, ptr)); + + while (cd) { + next = cd->next; + free_cdesc(cd); + cd = next; + } + cd = iop->cdesc_in; + while (cd) { + next = cd->next; + free_cdesc(cd); + cd = next; + } + kfree(ptr); +} + +#define MD5_MIN_PAD_LENGTH (9) +#define MD5_PAD_LENGTH_FIELD_LENGTH (8) + +static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length) +{ + size_t padlen = MD5_BLOCK_LENGTH - (hashed_length % MD5_BLOCK_LENGTH); + unsigned char *p; + int i; + unsigned long long int bit_length = hashed_length << 3; + + if (padlen < MD5_MIN_PAD_LENGTH) padlen += MD5_BLOCK_LENGTH; + + p = kmalloc(padlen, alloc_flag); + if (!pad) return -ENOMEM; + + *p = 0x80; + memset(p+1, 0, padlen - 1); + + DEBUG(printk("create_md5_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length)); + + i = padlen - MD5_PAD_LENGTH_FIELD_LENGTH; + while (bit_length != 0){ + p[i++] = bit_length % 0x100; + bit_length >>= 8; + } + + *pad = (char*)p; + *pad_length = padlen; + + return 0; +} + +#define SHA1_MIN_PAD_LENGTH (9) +#define SHA1_PAD_LENGTH_FIELD_LENGTH (8) + +static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length) +{ + size_t padlen = SHA1_BLOCK_LENGTH - (hashed_length % SHA1_BLOCK_LENGTH); + unsigned char *p; + int i; + unsigned long long int bit_length = hashed_length << 3; + + if (padlen < SHA1_MIN_PAD_LENGTH) padlen += SHA1_BLOCK_LENGTH; + + p = kmalloc(padlen, alloc_flag); + if (!pad) return -ENOMEM; + + *p = 0x80; + memset(p+1, 0, padlen - 1); + + DEBUG(printk("create_sha1_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length)); + + i = padlen - 1; + while (bit_length != 0){ + p[i--] = bit_length % 0x100; + bit_length >>= 8; + } + + *pad = (char*)p; + *pad_length = padlen; + + return 0; +} + + +static int transform_ok(struct cryptocop_transform_init *tinit) +{ + switch (tinit->alg){ + case cryptocop_alg_csum: + switch (tinit->csum_mode){ + case cryptocop_csum_le: + case cryptocop_csum_be: + break; + default: + DEBUG_API(printk("transform_ok: Bad mode set for csum transform\n")); + return -EINVAL; + } + case cryptocop_alg_mem2mem: + case cryptocop_alg_md5: + case cryptocop_alg_sha1: + if (tinit->keylen != 0) { + DEBUG_API(printk("transform_ok: non-zero keylength, %d, for a digest/csum algorithm\n", tinit->keylen)); + return -EINVAL; /* This check is a bit strict. */ + } + break; + case cryptocop_alg_des: + if (tinit->keylen != 64) { + DEBUG_API(printk("transform_ok: keylen %d invalid for DES\n", tinit->keylen)); + return -EINVAL; + } + break; + case cryptocop_alg_3des: + if (tinit->keylen != 192) { + DEBUG_API(printk("transform_ok: keylen %d invalid for 3DES\n", tinit->keylen)); + return -EINVAL; + } + break; + case cryptocop_alg_aes: + if (tinit->keylen != 128 && tinit->keylen != 192 && tinit->keylen != 256) { + DEBUG_API(printk("transform_ok: keylen %d invalid for AES\n", tinit->keylen)); + return -EINVAL; + } + break; + case cryptocop_no_alg: + default: + DEBUG_API(printk("transform_ok: no such algorithm %d\n", tinit->alg)); + return -EINVAL; + } + + switch (tinit->alg){ + case cryptocop_alg_des: + case cryptocop_alg_3des: + case cryptocop_alg_aes: + if (tinit->cipher_mode != cryptocop_cipher_mode_ecb && tinit->cipher_mode != cryptocop_cipher_mode_cbc) return -EINVAL; + default: + break; + } + return 0; +} + + +int cryptocop_new_session(cryptocop_session_id *sid, struct cryptocop_transform_init *tinit, int alloc_flag) +{ + struct cryptocop_session *sess; + struct cryptocop_transform_init *tfrm_in = tinit; + struct cryptocop_transform_init *tmp_in; + int no_tfrms = 0; + int i; + unsigned long int flags; + + init_stream_coprocessor(); /* For safety if we are called early */ + + while (tfrm_in){ + int err; + ++no_tfrms; + if ((err = transform_ok(tfrm_in))) { + DEBUG_API(printk("cryptocop_new_session, bad transform\n")); + return err; + } + tfrm_in = tfrm_in->next; + } + if (0 == no_tfrms) { + DEBUG_API(printk("cryptocop_new_session, no transforms specified\n")); + return -EINVAL; + } + + sess = kmalloc(sizeof(struct cryptocop_session), alloc_flag); + if (!sess){ + DEBUG_API(printk("cryptocop_new_session, kmalloc cryptocop_session\n")); + return -ENOMEM; + } + + sess->tfrm_ctx = kmalloc(no_tfrms * sizeof(struct cryptocop_transform_ctx), alloc_flag); + if (!sess->tfrm_ctx) { + DEBUG_API(printk("cryptocop_new_session, kmalloc cryptocop_transform_ctx\n")); + kfree(sess); + return -ENOMEM; + } + + tfrm_in = tinit; + for (i = 0; i < no_tfrms; i++){ + tmp_in = tfrm_in->next; + while (tmp_in){ + if (tmp_in->tid == tfrm_in->tid) { + DEBUG_API(printk("cryptocop_new_session, duplicate transform ids\n")); + kfree(sess->tfrm_ctx); + kfree(sess); + return -EINVAL; + } + tmp_in = tmp_in->next; + } + memcpy(&sess->tfrm_ctx[i].init, tfrm_in, sizeof(struct cryptocop_transform_init)); + sess->tfrm_ctx[i].dec_key_set = 0; + sess->tfrm_ctx[i].next = &sess->tfrm_ctx[i] + 1; + + tfrm_in = tfrm_in->next; + } + sess->tfrm_ctx[i-1].next = NULL; + + spin_lock_irqsave(&cryptocop_sessions_lock, flags); + sess->sid = next_sid; + next_sid++; + /* TODO If we are really paranoid we should do duplicate check to handle sid wraparound. + * OTOH 2^64 is a really large number of session. */ + if (next_sid == 0) next_sid = 1; + + /* Prepend to session list. */ + sess->next = cryptocop_sessions; + cryptocop_sessions = sess; + spin_unlock_irqrestore(&cryptocop_sessions_lock, flags); + *sid = sess->sid; + return 0; +} + + +int cryptocop_free_session(cryptocop_session_id sid) +{ + struct cryptocop_transform_ctx *tc; + struct cryptocop_session *sess = NULL; + struct cryptocop_session *psess = NULL; + unsigned long int flags; + int i; + LIST_HEAD(remove_list); + struct list_head *node, *tmp; + struct cryptocop_prio_job *pj; + + DEBUG(printk("cryptocop_free_session: sid=%lld\n", sid)); + + spin_lock_irqsave(&cryptocop_sessions_lock, flags); + sess = cryptocop_sessions; + while (sess && sess->sid != sid){ + psess = sess; + sess = sess->next; + } + if (sess){ + if (psess){ + psess->next = sess->next; + } else { + cryptocop_sessions = sess->next; + } + } + spin_unlock_irqrestore(&cryptocop_sessions_lock, flags); + + if (!sess) return -EINVAL; + + /* Remove queued jobs. */ + spin_lock_irqsave(&cryptocop_job_queue_lock, flags); + + for (i = 0; i < cryptocop_prio_no_prios; i++){ + if (!list_empty(&(cryptocop_job_queues[i].jobs))){ + list_for_each_safe(node, tmp, &(cryptocop_job_queues[i].jobs)) { + pj = list_entry(node, struct cryptocop_prio_job, node); + if (pj->oper->sid == sid) { + list_move_tail(node, &remove_list); + } + } + } + } + spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags); + + list_for_each_safe(node, tmp, &remove_list) { + list_del(node); + pj = list_entry(node, struct cryptocop_prio_job, node); + pj->oper->operation_status = -EAGAIN; /* EAGAIN is not ideal for job/session terminated but it's the best choice I know of. */ + DEBUG(printk("cryptocop_free_session: pj=0x%p, pj->oper=0x%p, pj->iop=0x%p\n", pj, pj->oper, pj->iop)); + pj->oper->cb(pj->oper, pj->oper->cb_data); + delete_internal_operation(pj->iop); + kfree(pj); + } + + tc = sess->tfrm_ctx; + /* Erase keying data. */ + while (tc){ + DEBUG(printk("cryptocop_free_session: memset keys, tfrm id=%d\n", tc->init.tid)); + memset(tc->init.key, 0xff, CRYPTOCOP_MAX_KEY_LENGTH); + memset(tc->dec_key, 0xff, CRYPTOCOP_MAX_KEY_LENGTH); + tc = tc->next; + } + kfree(sess->tfrm_ctx); + kfree(sess); + + return 0; +} + +static struct cryptocop_session *get_session(cryptocop_session_id sid) +{ + struct cryptocop_session *sess; + unsigned long int flags; + + spin_lock_irqsave(&cryptocop_sessions_lock, flags); + sess = cryptocop_sessions; + while (sess && (sess->sid != sid)){ + sess = sess->next; + } + spin_unlock_irqrestore(&cryptocop_sessions_lock, flags); + + return sess; +} + +static struct cryptocop_transform_ctx *get_transform_ctx(struct cryptocop_session *sess, cryptocop_tfrm_id tid) +{ + struct cryptocop_transform_ctx *tc = sess->tfrm_ctx; + + DEBUG(printk("get_transform_ctx, sess=0x%p, tid=%d\n", sess, tid)); + assert(sess != NULL); + while (tc && tc->init.tid != tid){ + DEBUG(printk("tc=0x%p, tc->next=0x%p\n", tc, tc->next)); + tc = tc->next; + } + DEBUG(printk("get_transform_ctx, returning tc=0x%p\n", tc)); + return tc; +} + + + +/* The AES s-transform matrix (s-box). */ +static const u8 aes_sbox[256] = { + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 +}; + +/* AES has a 32 bit word round constants for each round in the + * key schedule. round_constant[i] is really Rcon[i+1] in FIPS187. + */ +static u32 round_constant[11] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, 0x6C000000 +}; + +/* Apply the s-box to each of the four occtets in w. */ +static u32 aes_ks_subword(const u32 w) +{ + u8 bytes[4]; + + *(u32*)(&bytes[0]) = w; + bytes[0] = aes_sbox[bytes[0]]; + bytes[1] = aes_sbox[bytes[1]]; + bytes[2] = aes_sbox[bytes[2]]; + bytes[3] = aes_sbox[bytes[3]]; + return *(u32*)(&bytes[0]); +} + +/* The encrypt (forward) Rijndael key schedule algorithm pseudo code: + * (Note that AES words are 32 bit long) + * + * KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk){ + * word temp + * i = 0 + * while (i < Nk) { + * w[i] = word(key[4*i, 4*i + 1, 4*i + 2, 4*i + 3]) + * i = i + 1 + * } + * i = Nk + * + * while (i < (Nb * (Nr + 1))) { + * temp = w[i - 1] + * if ((i mod Nk) == 0) { + * temp = SubWord(RotWord(temp)) xor Rcon[i/Nk] + * } + * else if ((Nk > 6) && ((i mod Nk) == 4)) { + * temp = SubWord(temp) + * } + * w[i] = w[i - Nk] xor temp + * } + * RotWord(t) does a 8 bit cyclic shift left on a 32 bit word. + * SubWord(t) applies the AES s-box individually to each octet + * in a 32 bit word. + * + * For AES Nk can have the values 4, 6, and 8 (corresponding to + * values for Nr of 10, 12, and 14). Nb is always 4. + * + * To construct w[i], w[i - 1] and w[i - Nk] must be + * available. Consequently we must keep a state of the last Nk words + * to be able to create the last round keys. + */ +static void get_aes_decrypt_key(unsigned char *dec_key, const unsigned char *key, unsigned int keylength) +{ + u32 temp; + u32 w_ring[8]; /* nk is max 8, use elements 0..(nk - 1) as a ringbuffer */ + u8 w_last_ix; + int i; + u8 nr, nk; + + switch (keylength){ + case 128: + nk = 4; + nr = 10; + break; + case 192: + nk = 6; + nr = 12; + break; + case 256: + nk = 8; + nr = 14; + break; + default: + panic("stream co-processor: bad aes key length in get_aes_decrypt_key\n"); + }; + + /* Need to do host byte order correction here since key is byte oriented and the + * kx algorithm is word (u32) oriented. */ + for (i = 0; i < nk; i+=1) { + w_ring[i] = be32_to_cpu(*(u32*)&key[4*i]); + } + + i = (int)nk; + w_last_ix = i - 1; + while (i < (4 * (nr + 2))) { + temp = w_ring[w_last_ix]; + if (!(i % nk)) { + /* RotWord(temp) */ + temp = (temp << 8) | (temp >> 24); + temp = aes_ks_subword(temp); + temp ^= round_constant[i/nk - 1]; + } else if ((nk > 6) && ((i % nk) == 4)) { + temp = aes_ks_subword(temp); + } + w_last_ix = (w_last_ix + 1) % nk; /* This is the same as (i-Nk) mod Nk */ + temp ^= w_ring[w_last_ix]; + w_ring[w_last_ix] = temp; + + /* We need the round keys for round Nr+1 and Nr+2 (round key + * Nr+2 is the round key beyond the last one used when + * encrypting). Rounds are numbered starting from 0, Nr=10 + * implies 11 rounds are used in encryption/decryption. + */ + if (i >= (4 * nr)) { + /* Need to do host byte order correction here, the key + * is byte oriented. */ + *(u32*)dec_key = cpu_to_be32(temp); + dec_key += 4; + } + ++i; + } +} + + +/**** Job/operation management. ****/ + +int cryptocop_job_queue_insert_csum(struct cryptocop_operation *operation) +{ + return cryptocop_job_queue_insert(cryptocop_prio_kernel_csum, operation); +} + +int cryptocop_job_queue_insert_crypto(struct cryptocop_operation *operation) +{ + return cryptocop_job_queue_insert(cryptocop_prio_kernel, operation); +} + +int cryptocop_job_queue_insert_user_job(struct cryptocop_operation *operation) +{ + return cryptocop_job_queue_insert(cryptocop_prio_user, operation); +} + +static int cryptocop_job_queue_insert(cryptocop_queue_priority prio, struct cryptocop_operation *operation) +{ + int ret; + struct cryptocop_prio_job *pj = NULL; + unsigned long int flags; + + DEBUG(printk("cryptocop_job_queue_insert(%d, 0x%p)\n", prio, operation)); + + if (!operation || !operation->cb){ + DEBUG_API(printk("cryptocop_job_queue_insert oper=0x%p, NULL operation or callback\n", operation)); + return -EINVAL; + } + + if ((ret = cryptocop_job_setup(&pj, operation)) != 0){ + DEBUG_API(printk("cryptocop_job_queue_insert: job setup failed\n")); + return ret; + } + assert(pj != NULL); + + spin_lock_irqsave(&cryptocop_job_queue_lock, flags); + list_add_tail(&pj->node, &cryptocop_job_queues[prio].jobs); + spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags); + + /* Make sure a job is running */ + cryptocop_start_job(); + return 0; +} + +static void cryptocop_do_tasklet(unsigned long unused); +DECLARE_TASKLET (cryptocop_tasklet, cryptocop_do_tasklet, 0); + +static void cryptocop_do_tasklet(unsigned long unused) +{ + struct list_head *node; + struct cryptocop_prio_job *pj = NULL; + unsigned long flags; + + DEBUG(printk("cryptocop_do_tasklet: entering\n")); + + do { + spin_lock_irqsave(&cryptocop_completed_jobs_lock, flags); + if (!list_empty(&cryptocop_completed_jobs)){ + node = cryptocop_completed_jobs.next; + list_del(node); + pj = list_entry(node, struct cryptocop_prio_job, node); + } else { + pj = NULL; + } + spin_unlock_irqrestore(&cryptocop_completed_jobs_lock, flags); + if (pj) { + assert(pj->oper != NULL); + + /* Notify consumer of operation completeness. */ + DEBUG(printk("cryptocop_do_tasklet: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data)); + + pj->oper->operation_status = 0; /* Job is completed. */ + pj->oper->cb(pj->oper, pj->oper->cb_data); + delete_internal_operation(pj->iop); + kfree(pj); + } + } while (pj != NULL); + + DEBUG(printk("cryptocop_do_tasklet: exiting\n")); +} + +static irqreturn_t +dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct cryptocop_prio_job *done_job; + reg_dma_rw_ack_intr ack_intr = { + .data = 1, + }; + + REG_WR (dma, regi_dma9, rw_ack_intr, ack_intr); + + DEBUG(printk("cryptocop DMA done\n")); + + spin_lock(&running_job_lock); + if (cryptocop_running_job == NULL){ + printk("stream co-processor got interrupt when not busy\n"); + spin_unlock(&running_job_lock); + return IRQ_HANDLED; + } + done_job = cryptocop_running_job; + cryptocop_running_job = NULL; + spin_unlock(&running_job_lock); + + /* Start processing a job. */ + if (!spin_trylock(&cryptocop_process_lock)){ + DEBUG(printk("cryptocop irq handler, not starting a job\n")); + } else { + cryptocop_start_job(); + spin_unlock(&cryptocop_process_lock); + } + + done_job->oper->operation_status = 0; /* Job is completed. */ + if (done_job->oper->fast_callback){ + /* This operation wants callback from interrupt. */ + done_job->oper->cb(done_job->oper, done_job->oper->cb_data); + delete_internal_operation(done_job->iop); + kfree(done_job); + } else { + spin_lock(&cryptocop_completed_jobs_lock); + list_add_tail(&(done_job->node), &cryptocop_completed_jobs); + spin_unlock(&cryptocop_completed_jobs_lock); + tasklet_schedule(&cryptocop_tasklet); + } + + DEBUG(printk("cryptocop leave irq handler\n")); + return IRQ_HANDLED; +} + + +/* Setup interrupts and DMA channels. */ +static int init_cryptocop(void) +{ + unsigned long flags; + reg_intr_vect_rw_mask intr_mask; + reg_dma_rw_cfg dma_cfg = {.en = 1}; + reg_dma_rw_intr_mask intr_mask_in = {.data = regk_dma_yes}; /* Only want descriptor interrupts from the DMA in channel. */ + reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 }; + reg_strcop_rw_cfg strcop_cfg = { + .ipend = regk_strcop_little, + .td1 = regk_strcop_e, + .td2 = regk_strcop_d, + .td3 = regk_strcop_e, + .ignore_sync = 0, + .en = 1 + }; + + if (request_irq(DMA9_INTR_VECT, dma_done_interrupt, 0, "stream co-processor DMA", NULL)) panic("request_irq stream co-processor irq dma9"); + + (void)crisv32_request_dma(8, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp); + (void)crisv32_request_dma(9, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp); + + local_irq_save(flags); + + /* Reset and enable the cryptocop. */ + strcop_cfg.en = 0; + REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg); + strcop_cfg.en = 1; + REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg); + + /* Enable DMA9 interrupt */ + intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); + intr_mask.dma9 = 1; + REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + + /* Enable DMAs. */ + REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */ + REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */ + + /* Set up wordsize = 4 for DMAs. */ + DMA_WR_CMD (regi_dma8, regk_dma_set_w_size4); + DMA_WR_CMD (regi_dma9, regk_dma_set_w_size4); + + /* Enable interrupts. */ + REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in); + + /* Clear intr ack. */ + REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr); + + local_irq_restore(flags); + + return 0; +} + +/* Free used cryptocop hw resources (interrupt and DMA channels). */ +static void release_cryptocop(void) +{ + unsigned long flags; + reg_intr_vect_rw_mask intr_mask; + reg_dma_rw_cfg dma_cfg = {.en = 0}; + reg_dma_rw_intr_mask intr_mask_in = {0}; + reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 }; + + local_irq_save(flags); + + /* Clear intr ack. */ + REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr); + + /* Disable DMA9 interrupt */ + intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); + intr_mask.dma9 = 0; + REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + + /* Disable DMAs. */ + REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */ + REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */ + + /* Disable interrupts. */ + REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in); + + local_irq_restore(flags); + + free_irq(DMA9_INTR_VECT, NULL); + + (void)crisv32_free_dma(8); + (void)crisv32_free_dma(9); +} + + +/* Init job queue. */ +static int cryptocop_job_queue_init(void) +{ + int i; + + INIT_LIST_HEAD(&cryptocop_completed_jobs); + + for (i = 0; i < cryptocop_prio_no_prios; i++){ + cryptocop_job_queues[i].prio = (cryptocop_queue_priority)i; + INIT_LIST_HEAD(&cryptocop_job_queues[i].jobs); + } + return 0; +} + + +static void cryptocop_job_queue_close(void) +{ + struct list_head *node, *tmp; + struct cryptocop_prio_job *pj = NULL; + unsigned long int process_flags, flags; + int i; + + /* FIXME: This is as yet untested code. */ + + /* Stop strcop from getting an operation to process while we are closing the + module. */ + spin_lock_irqsave(&cryptocop_process_lock, process_flags); + + /* Empty the job queue. */ + spin_lock_irqsave(&cryptocop_process_lock, process_flags); + for (i = 0; i < cryptocop_prio_no_prios; i++){ + if (!list_empty(&(cryptocop_job_queues[i].jobs))){ + list_for_each_safe(node, tmp, &(cryptocop_job_queues[i].jobs)) { + pj = list_entry(node, struct cryptocop_prio_job, node); + list_del(node); + + /* Call callback to notify consumer of job removal. */ + DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data)); + pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */ + pj->oper->cb(pj->oper, pj->oper->cb_data); + + delete_internal_operation(pj->iop); + kfree(pj); + } + } + } + spin_unlock_irqrestore(&cryptocop_process_lock, process_flags); + + /* Remove the running job, if any. */ + spin_lock_irqsave(&running_job_lock, flags); + if (cryptocop_running_job){ + reg_strcop_rw_cfg rw_cfg; + reg_dma_rw_cfg dma_out_cfg, dma_in_cfg; + + /* Stop DMA. */ + dma_out_cfg = REG_RD(dma, regi_dma8, rw_cfg); + dma_out_cfg.en = regk_dma_no; + REG_WR(dma, regi_dma8, rw_cfg, dma_out_cfg); + + dma_in_cfg = REG_RD(dma, regi_dma9, rw_cfg); + dma_in_cfg.en = regk_dma_no; + REG_WR(dma, regi_dma9, rw_cfg, dma_in_cfg); + + /* Disble the cryptocop. */ + rw_cfg = REG_RD(strcop, regi_strcop, rw_cfg); + rw_cfg.en = 0; + REG_WR(strcop, regi_strcop, rw_cfg, rw_cfg); + + pj = cryptocop_running_job; + cryptocop_running_job = NULL; + + /* Call callback to notify consumer of job removal. */ + DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data)); + pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */ + pj->oper->cb(pj->oper, pj->oper->cb_data); + + delete_internal_operation(pj->iop); + kfree(pj); + } + spin_unlock_irqrestore(&running_job_lock, flags); + + /* Remove completed jobs, if any. */ + spin_lock_irqsave(&cryptocop_completed_jobs_lock, flags); + + list_for_each_safe(node, tmp, &cryptocop_completed_jobs) { + pj = list_entry(node, struct cryptocop_prio_job, node); + list_del(node); + /* Call callback to notify consumer of job removal. */ + DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data)); + pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */ + pj->oper->cb(pj->oper, pj->oper->cb_data); + + delete_internal_operation(pj->iop); + kfree(pj); + } + spin_unlock_irqrestore(&cryptocop_completed_jobs_lock, flags); +} + + +static void cryptocop_start_job(void) +{ + int i; + struct cryptocop_prio_job *pj; + unsigned long int flags; + unsigned long int running_job_flags; + reg_strcop_rw_cfg rw_cfg = {.en = 1, .ignore_sync = 0}; + + DEBUG(printk("cryptocop_start_job: entering\n")); + + spin_lock_irqsave(&running_job_lock, running_job_flags); + if (cryptocop_running_job != NULL){ + /* Already running. */ + DEBUG(printk("cryptocop_start_job: already running, exit\n")); + spin_unlock_irqrestore(&running_job_lock, running_job_flags); + return; + } + spin_lock_irqsave(&cryptocop_job_queue_lock, flags); + + /* Check the queues in priority order. */ + for (i = cryptocop_prio_kernel_csum; (i < cryptocop_prio_no_prios) && list_empty(&cryptocop_job_queues[i].jobs); i++); + if (i == cryptocop_prio_no_prios) { + spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags); + spin_unlock_irqrestore(&running_job_lock, running_job_flags); + DEBUG(printk("cryptocop_start_job: no jobs to run\n")); + return; /* No jobs to run */ + } + DEBUG(printk("starting job for prio %d\n", i)); + + /* TODO: Do not starve lower priority jobs. Let in a lower + * prio job for every N-th processed higher prio job or some + * other scheduling policy. This could reasonably be + * tweakable since the optimal balance would depend on the + * type of load on the system. */ + + /* Pull the DMA lists from the job and start the DMA client. */ + pj = list_entry(cryptocop_job_queues[i].jobs.next, struct cryptocop_prio_job, node); + list_del(&pj->node); + spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags); + cryptocop_running_job = pj; + + /* Set config register (3DES and CSUM modes). */ + switch (pj->iop->tdes_mode){ + case cryptocop_3des_eee: + rw_cfg.td1 = regk_strcop_e; + rw_cfg.td2 = regk_strcop_e; + rw_cfg.td3 = regk_strcop_e; + break; + case cryptocop_3des_eed: + rw_cfg.td1 = regk_strcop_e; + rw_cfg.td2 = regk_strcop_e; + rw_cfg.td3 = regk_strcop_d; + break; + case cryptocop_3des_ede: + rw_cfg.td1 = regk_strcop_e; + rw_cfg.td2 = regk_strcop_d; + rw_cfg.td3 = regk_strcop_e; + break; + case cryptocop_3des_edd: + rw_cfg.td1 = regk_strcop_e; + rw_cfg.td2 = regk_strcop_d; + rw_cfg.td3 = regk_strcop_d; + break; + case cryptocop_3des_dee: + rw_cfg.td1 = regk_strcop_d; + rw_cfg.td2 = regk_strcop_e; + rw_cfg.td3 = regk_strcop_e; + break; + case cryptocop_3des_ded: + rw_cfg.td1 = regk_strcop_d; + rw_cfg.td2 = regk_strcop_e; + rw_cfg.td3 = regk_strcop_d; + break; + case cryptocop_3des_dde: + rw_cfg.td1 = regk_strcop_d; + rw_cfg.td2 = regk_strcop_d; + rw_cfg.td3 = regk_strcop_e; + break; + case cryptocop_3des_ddd: + rw_cfg.td1 = regk_strcop_d; + rw_cfg.td2 = regk_strcop_d; + rw_cfg.td3 = regk_strcop_d; + break; + default: + DEBUG(printk("cryptocop_setup_dma_list: bad 3DES mode\n")); + } + switch (pj->iop->csum_mode){ + case cryptocop_csum_le: + rw_cfg.ipend = regk_strcop_little; + break; + case cryptocop_csum_be: + rw_cfg.ipend = regk_strcop_big; + break; + default: + DEBUG(printk("cryptocop_setup_dma_list: bad checksum mode\n")); + } + REG_WR(strcop, regi_strcop, rw_cfg, rw_cfg); + + DEBUG(printk("cryptocop_start_job: starting DMA, new cryptocop_running_job=0x%p\n" + "ctx_in: 0x%p, phys: 0x%p\n" + "ctx_out: 0x%p, phys: 0x%p\n", + pj, + &pj->iop->ctx_in, (char*)virt_to_phys(&pj->iop->ctx_in), + &pj->iop->ctx_out, (char*)virt_to_phys(&pj->iop->ctx_out))); + + /* Start input DMA. */ + DMA_START_CONTEXT(regi_dma9, virt_to_phys(&pj->iop->ctx_in)); + + /* Start output DMA. */ + DMA_START_CONTEXT(regi_dma8, virt_to_phys(&pj->iop->ctx_out)); + + spin_unlock_irqrestore(&running_job_lock, running_job_flags); + DEBUG(printk("cryptocop_start_job: exiting\n")); +} + + +static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_operation *operation) +{ + int err; + int alloc_flag = operation->in_interrupt ? GFP_ATOMIC : GFP_KERNEL; + void *iop_alloc_ptr = NULL; + + *pj = kmalloc(sizeof (struct cryptocop_prio_job), alloc_flag); + if (!*pj) return -ENOMEM; + + DEBUG(printk("cryptocop_job_setup: operation=0x%p\n", operation)); + + (*pj)->oper = operation; + DEBUG(printk("cryptocop_job_setup, cb=0x%p cb_data=0x%p\n", (*pj)->oper->cb, (*pj)->oper->cb_data)); + + if (operation->use_dmalists) { + DEBUG(print_user_dma_lists(&operation->list_op)); + if (!operation->list_op.inlist || !operation->list_op.outlist || !operation->list_op.out_data_buf || !operation->list_op.in_data_buf){ + DEBUG_API(printk("cryptocop_job_setup: bad indata (use_dmalists)\n")); + kfree(*pj); + return -EINVAL; + } + iop_alloc_ptr = kmalloc(DESCR_ALLOC_PAD + sizeof(struct cryptocop_int_operation), alloc_flag); + if (!iop_alloc_ptr) { + DEBUG_API(printk("cryptocop_job_setup: kmalloc cryptocop_int_operation\n")); + kfree(*pj); + return -ENOMEM; + } + (*pj)->iop = (struct cryptocop_int_operation*)(((unsigned long int)(iop_alloc_ptr + DESCR_ALLOC_PAD + offsetof(struct cryptocop_int_operation, ctx_out)) & ~0x0000001F) - offsetof(struct cryptocop_int_operation, ctx_out)); + DEBUG(memset((*pj)->iop, 0xff, sizeof(struct cryptocop_int_operation))); + (*pj)->iop->alloc_ptr = iop_alloc_ptr; + (*pj)->iop->sid = operation->sid; + (*pj)->iop->cdesc_out = NULL; + (*pj)->iop->cdesc_in = NULL; + (*pj)->iop->tdes_mode = operation->list_op.tdes_mode; + (*pj)->iop->csum_mode = operation->list_op.csum_mode; + (*pj)->iop->ddesc_out = operation->list_op.outlist; + (*pj)->iop->ddesc_in = operation->list_op.inlist; + + /* Setup DMA contexts. */ + (*pj)->iop->ctx_out.next = NULL; + (*pj)->iop->ctx_out.eol = 1; + (*pj)->iop->ctx_out.saved_data = operation->list_op.outlist; + (*pj)->iop->ctx_out.saved_data_buf = operation->list_op.out_data_buf; + + (*pj)->iop->ctx_in.next = NULL; + (*pj)->iop->ctx_in.eol = 1; + (*pj)->iop->ctx_in.saved_data = operation->list_op.inlist; + (*pj)->iop->ctx_in.saved_data_buf = operation->list_op.in_data_buf; + } else { + if ((err = cryptocop_setup_dma_list(operation, &(*pj)->iop, alloc_flag))) { + DEBUG_API(printk("cryptocop_job_setup: cryptocop_setup_dma_list failed %d\n", err)); + kfree(*pj); + return err; + } + } + DEBUG(print_dma_descriptors((*pj)->iop)); + + DEBUG(printk("cryptocop_job_setup, DMA list setup successful\n")); + + return 0; +} + + +static int cryptocop_open(struct inode *inode, struct file *filp) +{ + int p = MINOR(inode->i_rdev); + + if (p != CRYPTOCOP_MINOR) return -EINVAL; + + filp->private_data = NULL; + return 0; +} + + +static int cryptocop_release(struct inode *inode, struct file *filp) +{ + struct cryptocop_private *dev = filp->private_data; + struct cryptocop_private *dev_next; + + while (dev){ + dev_next = dev->next; + if (dev->sid != CRYPTOCOP_SESSION_ID_NONE) { + (void)cryptocop_free_session(dev->sid); + } + kfree(dev); + dev = dev_next; + } + + return 0; +} + + +static int cryptocop_ioctl_close_session(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct cryptocop_private *dev = filp->private_data; + struct cryptocop_private *prev_dev = NULL; + struct strcop_session_op *sess_op = (struct strcop_session_op *)arg; + struct strcop_session_op sop; + int err; + + DEBUG(printk("cryptocop_ioctl_close_session\n")); + + if (!access_ok(VERIFY_READ, sess_op, sizeof(struct strcop_session_op))) + return -EFAULT; + err = copy_from_user(&sop, sess_op, sizeof(struct strcop_session_op)); + if (err) return -EFAULT; + + while (dev && (dev->sid != sop.ses_id)) { + prev_dev = dev; + dev = dev->next; + } + if (dev){ + if (prev_dev){ + prev_dev->next = dev->next; + } else { + filp->private_data = dev->next; + } + err = cryptocop_free_session(dev->sid); + if (err) return -EFAULT; + } else { + DEBUG_API(printk("cryptocop_ioctl_close_session: session %lld not found\n", sop.ses_id)); + return -EINVAL; + } + return 0; +} + + +static void ioctl_process_job_callback(struct cryptocop_operation *op, void*cb_data) +{ + struct ioctl_job_cb_ctx *jc = (struct ioctl_job_cb_ctx *)cb_data; + + DEBUG(printk("ioctl_process_job_callback: op=0x%p, cb_data=0x%p\n", op, cb_data)); + + jc->processed = 1; + wake_up(&cryptocop_ioc_process_wq); +} + + +#define CRYPTOCOP_IOCTL_CIPHER_TID (1) +#define CRYPTOCOP_IOCTL_DIGEST_TID (2) +#define CRYPTOCOP_IOCTL_CSUM_TID (3) + +static size_t first_cfg_change_ix(struct strcop_crypto_op *crp_op) +{ + size_t ch_ix = 0; + + if (crp_op->do_cipher) ch_ix = crp_op->cipher_start; + if (crp_op->do_digest && (crp_op->digest_start < ch_ix)) ch_ix = crp_op->digest_start; + if (crp_op->do_csum && (crp_op->csum_start < ch_ix)) ch_ix = crp_op->csum_start; + + DEBUG(printk("first_cfg_change_ix: ix=%d\n", ch_ix)); + return ch_ix; +} + + +static size_t next_cfg_change_ix(struct strcop_crypto_op *crp_op, size_t ix) +{ + size_t ch_ix = INT_MAX; + size_t tmp_ix = 0; + + if (crp_op->do_cipher && ((crp_op->cipher_start + crp_op->cipher_len) > ix)){ + if (crp_op->cipher_start > ix) { + ch_ix = crp_op->cipher_start; + } else { + ch_ix = crp_op->cipher_start + crp_op->cipher_len; + } + } + if (crp_op->do_digest && ((crp_op->digest_start + crp_op->digest_len) > ix)){ + if (crp_op->digest_start > ix) { + tmp_ix = crp_op->digest_start; + } else { + tmp_ix = crp_op->digest_start + crp_op->digest_len; + } + if (tmp_ix < ch_ix) ch_ix = tmp_ix; + } + if (crp_op->do_csum && ((crp_op->csum_start + crp_op->csum_len) > ix)){ + if (crp_op->csum_start > ix) { + tmp_ix = crp_op->csum_start; + } else { + tmp_ix = crp_op->csum_start + crp_op->csum_len; + } + if (tmp_ix < ch_ix) ch_ix = tmp_ix; + } + if (ch_ix == INT_MAX) ch_ix = ix; + DEBUG(printk("next_cfg_change_ix prev ix=%d, next ix=%d\n", ix, ch_ix)); + return ch_ix; +} + + +/* Map map_length bytes from the pages starting on *pageix and *pageoffset to iovecs starting on *iovix. + * Return -1 for ok, 0 for fail. */ +static int map_pages_to_iovec(struct iovec *iov, int iovlen, int *iovix, struct page **pages, int nopages, int *pageix, int *pageoffset, int map_length ) +{ + int tmplen; + + assert(iov != NULL); + assert(iovix != NULL); + assert(pages != NULL); + assert(pageix != NULL); + assert(pageoffset != NULL); + + DEBUG(printk("map_pages_to_iovec, map_length=%d, iovlen=%d, *iovix=%d, nopages=%d, *pageix=%d, *pageoffset=%d\n", map_length, iovlen, *iovix, nopages, *pageix, *pageoffset)); + + while (map_length > 0){ + DEBUG(printk("map_pages_to_iovec, map_length=%d, iovlen=%d, *iovix=%d, nopages=%d, *pageix=%d, *pageoffset=%d\n", map_length, iovlen, *iovix, nopages, *pageix, *pageoffset)); + if (*iovix >= iovlen){ + DEBUG_API(printk("map_page_to_iovec: *iovix=%d >= iovlen=%d\n", *iovix, iovlen)); + return 0; + } + if (*pageix >= nopages){ + DEBUG_API(printk("map_page_to_iovec: *pageix=%d >= nopages=%d\n", *pageix, nopages)); + return 0; + } + iov[*iovix].iov_base = (unsigned char*)page_address(pages[*pageix]) + *pageoffset; + tmplen = PAGE_SIZE - *pageoffset; + if (tmplen < map_length){ + (*pageoffset) = 0; + (*pageix)++; + } else { + tmplen = map_length; + (*pageoffset) += map_length; + } + DEBUG(printk("mapping %d bytes from page %d (or %d) to iovec %d\n", tmplen, *pageix, *pageix-1, *iovix)); + iov[*iovix].iov_len = tmplen; + map_length -= tmplen; + (*iovix)++; + } + DEBUG(printk("map_page_to_iovec, exit, *iovix=%d\n", *iovix)); + return -1; +} + + + +static int cryptocop_ioctl_process(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int i; + struct cryptocop_private *dev = filp->private_data; + struct strcop_crypto_op *crp_oper = (struct strcop_crypto_op *)arg; + struct strcop_crypto_op oper = {0}; + int err = 0; + struct cryptocop_operation *cop = NULL; + + struct ioctl_job_cb_ctx *jc = NULL; + + struct page **inpages = NULL; + struct page **outpages = NULL; + int noinpages = 0; + int nooutpages = 0; + + struct cryptocop_desc descs[5]; /* Max 5 descriptors are needed, there are three transforms that + * can get connected/disconnected on different places in the indata. */ + struct cryptocop_desc_cfg dcfgs[5*3]; + int desc_ix = 0; + int dcfg_ix = 0; + struct cryptocop_tfrm_cfg ciph_tcfg = {0}; + struct cryptocop_tfrm_cfg digest_tcfg = {0}; + struct cryptocop_tfrm_cfg csum_tcfg = {0}; + + unsigned char *digest_result = NULL; + int digest_length = 0; + int cblocklen = 0; + unsigned char csum_result[CSUM_BLOCK_LENGTH]; + struct cryptocop_session *sess; + + int iovlen = 0; + int iovix = 0; + int pageix = 0; + int pageoffset = 0; + + size_t prev_ix = 0; + size_t next_ix; + + int cipher_active, digest_active, csum_active; + int end_digest, end_csum; + int digest_done = 0; + int cipher_done = 0; + int csum_done = 0; + + DEBUG(printk("cryptocop_ioctl_process\n")); + + if (!access_ok(VERIFY_WRITE, crp_oper, sizeof(struct strcop_crypto_op))){ + DEBUG_API(printk("cryptocop_ioctl_process: !access_ok crp_oper!\n")); + return -EFAULT; + } + if (copy_from_user(&oper, crp_oper, sizeof(struct strcop_crypto_op))) { + DEBUG_API(printk("cryptocop_ioctl_process: copy_from_user\n")); + return -EFAULT; + } + DEBUG(print_strcop_crypto_op(&oper)); + + while (dev && dev->sid != oper.ses_id) dev = dev->next; + if (!dev){ + DEBUG_API(printk("cryptocop_ioctl_process: session %lld not found\n", oper.ses_id)); + return -EINVAL; + } + + /* Check buffers. */ + if (((oper.indata + oper.inlen) < oper.indata) || ((oper.cipher_outdata + oper.cipher_outlen) < oper.cipher_outdata)){ + DEBUG_API(printk("cryptocop_ioctl_process: user buffers wrapped around, bad user!\n")); + return -EINVAL; + } + + if (!access_ok(VERIFY_WRITE, oper.cipher_outdata, oper.cipher_outlen)){ + DEBUG_API(printk("cryptocop_ioctl_process: !access_ok out data!\n")); + return -EFAULT; + } + if (!access_ok(VERIFY_READ, oper.indata, oper.inlen)){ + DEBUG_API(printk("cryptocop_ioctl_process: !access_ok in data!\n")); + return -EFAULT; + } + + cop = kmalloc(sizeof(struct cryptocop_operation), GFP_KERNEL); + if (!cop) { + DEBUG_API(printk("cryptocop_ioctl_process: kmalloc\n")); + return -ENOMEM; + } + jc = kmalloc(sizeof(struct ioctl_job_cb_ctx), GFP_KERNEL); + if (!jc) { + DEBUG_API(printk("cryptocop_ioctl_process: kmalloc\n")); + err = -ENOMEM; + goto error_cleanup; + } + jc->processed = 0; + + cop->cb_data = jc; + cop->cb = ioctl_process_job_callback; + cop->operation_status = 0; + cop->use_dmalists = 0; + cop->in_interrupt = 0; + cop->fast_callback = 0; + cop->tfrm_op.tfrm_cfg = NULL; + cop->tfrm_op.desc = NULL; + cop->tfrm_op.indata = NULL; + cop->tfrm_op.incount = 0; + cop->tfrm_op.inlen = 0; + cop->tfrm_op.outdata = NULL; + cop->tfrm_op.outcount = 0; + cop->tfrm_op.outlen = 0; + + sess = get_session(oper.ses_id); + if (!sess){ + DEBUG_API(printk("cryptocop_ioctl_process: bad session id.\n")); + kfree(cop); + kfree(jc); + return -EINVAL; + } + + if (oper.do_cipher) { + unsigned int cipher_outlen = 0; + struct cryptocop_transform_ctx *tc = get_transform_ctx(sess, CRYPTOCOP_IOCTL_CIPHER_TID); + if (!tc) { + DEBUG_API(printk("cryptocop_ioctl_process: no cipher transform in session.\n")); + err = -EINVAL; + goto error_cleanup; + } + ciph_tcfg.tid = CRYPTOCOP_IOCTL_CIPHER_TID; + ciph_tcfg.inject_ix = 0; + ciph_tcfg.flags = 0; + if ((oper.cipher_start < 0) || (oper.cipher_len <= 0) || (oper.cipher_start > oper.inlen) || ((oper.cipher_start + oper.cipher_len) > oper.inlen)){ + DEBUG_API(printk("cryptocop_ioctl_process: bad cipher length\n")); + kfree(cop); + kfree(jc); + return -EINVAL; + } + cblocklen = tc->init.alg == cryptocop_alg_aes ? AES_BLOCK_LENGTH : DES_BLOCK_LENGTH; + if (oper.cipher_len % cblocklen) { + kfree(cop); + kfree(jc); + DEBUG_API(printk("cryptocop_ioctl_process: cipher inlength not multiple of block length.\n")); + return -EINVAL; + } + cipher_outlen = oper.cipher_len; + if (tc->init.cipher_mode == cryptocop_cipher_mode_cbc){ + if (oper.cipher_explicit) { + ciph_tcfg.flags |= CRYPTOCOP_EXPLICIT_IV; + memcpy(ciph_tcfg.iv, oper.cipher_iv, cblocklen); + } else { + cipher_outlen = oper.cipher_len - cblocklen; + } + } else { + if (oper.cipher_explicit){ + kfree(cop); + kfree(jc); + DEBUG_API(printk("cryptocop_ioctl_process: explicit_iv when not CBC mode\n")); + return -EINVAL; + } + } + if (oper.cipher_outlen != cipher_outlen) { + kfree(cop); + kfree(jc); + DEBUG_API(printk("cryptocop_ioctl_process: cipher_outlen incorrect, should be %d not %d.\n", cipher_outlen, oper.cipher_outlen)); + return -EINVAL; + } + + if (oper.decrypt){ + ciph_tcfg.flags |= CRYPTOCOP_DECRYPT; + } else { + ciph_tcfg.flags |= CRYPTOCOP_ENCRYPT; + } + ciph_tcfg.next = cop->tfrm_op.tfrm_cfg; + cop->tfrm_op.tfrm_cfg = &ciph_tcfg; + } + if (oper.do_digest){ + struct cryptocop_transform_ctx *tc = get_transform_ctx(sess, CRYPTOCOP_IOCTL_DIGEST_TID); + if (!tc) { + DEBUG_API(printk("cryptocop_ioctl_process: no digest transform in session.\n")); + err = -EINVAL; + goto error_cleanup; + } + digest_length = tc->init.alg == cryptocop_alg_md5 ? 16 : 20; + digest_result = kmalloc(digest_length, GFP_KERNEL); + if (!digest_result) { + DEBUG_API(printk("cryptocop_ioctl_process: kmalloc digest_result\n")); + err = -EINVAL; + goto error_cleanup; + } + DEBUG(memset(digest_result, 0xff, digest_length)); + + digest_tcfg.tid = CRYPTOCOP_IOCTL_DIGEST_TID; + digest_tcfg.inject_ix = 0; + ciph_tcfg.inject_ix += digest_length; + if ((oper.digest_start < 0) || (oper.digest_len <= 0) || (oper.digest_start > oper.inlen) || ((oper.digest_start + oper.digest_len) > oper.inlen)){ + DEBUG_API(printk("cryptocop_ioctl_process: bad digest length\n")); + err = -EINVAL; + goto error_cleanup; + } + + digest_tcfg.next = cop->tfrm_op.tfrm_cfg; + cop->tfrm_op.tfrm_cfg = &digest_tcfg; + } + if (oper.do_csum){ + csum_tcfg.tid = CRYPTOCOP_IOCTL_CSUM_TID; + csum_tcfg.inject_ix = digest_length; + ciph_tcfg.inject_ix += 2; + + if ((oper.csum_start < 0) || (oper.csum_len <= 0) || (oper.csum_start > oper.inlen) || ((oper.csum_start + oper.csum_len) > oper.inlen)){ + DEBUG_API(printk("cryptocop_ioctl_process: bad csum length\n")); + kfree(cop); + kfree(jc); + return -EINVAL; + } + + csum_tcfg.next = cop->tfrm_op.tfrm_cfg; + cop->tfrm_op.tfrm_cfg = &csum_tcfg; + } + + prev_ix = first_cfg_change_ix(&oper); + if (prev_ix > oper.inlen) { + DEBUG_API(printk("cryptocop_ioctl_process: length mismatch\n")); + nooutpages = noinpages = 0; + err = -EINVAL; + goto error_cleanup; + } + DEBUG(printk("cryptocop_ioctl_process: inlen=%d, cipher_outlen=%d\n", oper.inlen, oper.cipher_outlen)); + + /* Map user pages for in and out data of the operation. */ + noinpages = (((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK) + oper.inlen - 1 - prev_ix + ~PAGE_MASK) >> PAGE_SHIFT; + DEBUG(printk("cryptocop_ioctl_process: noinpages=%d\n", noinpages)); + inpages = kmalloc(noinpages * sizeof(struct page*), GFP_KERNEL); + if (!inpages){ + DEBUG_API(printk("cryptocop_ioctl_process: kmalloc inpages\n")); + nooutpages = noinpages = 0; + err = -ENOMEM; + goto error_cleanup; + } + if (oper.do_cipher){ + nooutpages = (((unsigned long int)oper.cipher_outdata & ~PAGE_MASK) + oper.cipher_outlen - 1 + ~PAGE_MASK) >> PAGE_SHIFT; + DEBUG(printk("cryptocop_ioctl_process: nooutpages=%d\n", nooutpages)); + outpages = kmalloc(nooutpages * sizeof(struct page*), GFP_KERNEL); + if (!outpages){ + DEBUG_API(printk("cryptocop_ioctl_process: kmalloc outpages\n")); + nooutpages = noinpages = 0; + err = -ENOMEM; + goto error_cleanup; + } + } + + /* Acquire the mm page semaphore. */ + down_read(¤t->mm->mmap_sem); + + err = get_user_pages(current, + current->mm, + (unsigned long int)(oper.indata + prev_ix), + noinpages, + 0, /* read access only for in data */ + 0, /* no force */ + inpages, + NULL); + + if (err < 0) { + up_read(¤t->mm->mmap_sem); + nooutpages = noinpages = 0; + DEBUG_API(printk("cryptocop_ioctl_process: get_user_pages indata\n")); + goto error_cleanup; + } + noinpages = err; + if (oper.do_cipher){ + err = get_user_pages(current, + current->mm, + (unsigned long int)oper.cipher_outdata, + nooutpages, + 1, /* write access for out data */ + 0, /* no force */ + outpages, + NULL); + up_read(¤t->mm->mmap_sem); + if (err < 0) { + nooutpages = 0; + DEBUG_API(printk("cryptocop_ioctl_process: get_user_pages outdata\n")); + goto error_cleanup; + } + nooutpages = err; + } else { + up_read(¤t->mm->mmap_sem); + } + + /* Add 6 to nooutpages to make room for possibly inserted buffers for storing digest and + * csum output and splits when units are (dis-)connected. */ + cop->tfrm_op.indata = kmalloc((noinpages) * sizeof(struct iovec), GFP_KERNEL); + cop->tfrm_op.outdata = kmalloc((6 + nooutpages) * sizeof(struct iovec), GFP_KERNEL); + if (!cop->tfrm_op.indata || !cop->tfrm_op.outdata) { + DEBUG_API(printk("cryptocop_ioctl_process: kmalloc iovecs\n")); + err = -ENOMEM; + goto error_cleanup; + } + + cop->tfrm_op.inlen = oper.inlen - prev_ix; + cop->tfrm_op.outlen = 0; + if (oper.do_cipher) cop->tfrm_op.outlen += oper.cipher_outlen; + if (oper.do_digest) cop->tfrm_op.outlen += digest_length; + if (oper.do_csum) cop->tfrm_op.outlen += 2; + + /* Setup the in iovecs. */ + cop->tfrm_op.incount = noinpages; + if (noinpages > 1){ + size_t tmplen = cop->tfrm_op.inlen; + + cop->tfrm_op.indata[0].iov_len = PAGE_SIZE - ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK); + cop->tfrm_op.indata[0].iov_base = (unsigned char*)page_address(inpages[0]) + ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK); + tmplen -= cop->tfrm_op.indata[0].iov_len; + for (i = 1; itfrm_op.indata[i].iov_len = tmplen < PAGE_SIZE ? tmplen : PAGE_SIZE; + cop->tfrm_op.indata[i].iov_base = (unsigned char*)page_address(inpages[i]); + tmplen -= PAGE_SIZE; + } + } else { + cop->tfrm_op.indata[0].iov_len = oper.inlen - prev_ix; + cop->tfrm_op.indata[0].iov_base = (unsigned char*)page_address(inpages[0]) + ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK); + } + + iovlen = nooutpages + 6; + pageoffset = oper.do_cipher ? ((unsigned long int)oper.cipher_outdata & ~PAGE_MASK) : 0; + + next_ix = next_cfg_change_ix(&oper, prev_ix); + if (prev_ix == next_ix){ + DEBUG_API(printk("cryptocop_ioctl_process: length configuration broken.\n")); + err = -EINVAL; /* This should be impossible barring bugs. */ + goto error_cleanup; + } + while (prev_ix != next_ix){ + end_digest = end_csum = cipher_active = digest_active = csum_active = 0; + descs[desc_ix].cfg = NULL; + descs[desc_ix].length = next_ix - prev_ix; + + if (oper.do_cipher && (oper.cipher_start < next_ix) && (prev_ix < (oper.cipher_start + oper.cipher_len))) { + dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_CIPHER_TID; + dcfgs[dcfg_ix].src = cryptocop_source_dma; + cipher_active = 1; + + if (next_ix == (oper.cipher_start + oper.cipher_len)){ + cipher_done = 1; + dcfgs[dcfg_ix].last = 1; + } else { + dcfgs[dcfg_ix].last = 0; + } + dcfgs[dcfg_ix].next = descs[desc_ix].cfg; + descs[desc_ix].cfg = &dcfgs[dcfg_ix]; + ++dcfg_ix; + } + if (oper.do_digest && (oper.digest_start < next_ix) && (prev_ix < (oper.digest_start + oper.digest_len))) { + digest_active = 1; + dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_DIGEST_TID; + dcfgs[dcfg_ix].src = cryptocop_source_dma; + if (next_ix == (oper.digest_start + oper.digest_len)){ + assert(!digest_done); + digest_done = 1; + dcfgs[dcfg_ix].last = 1; + } else { + dcfgs[dcfg_ix].last = 0; + } + dcfgs[dcfg_ix].next = descs[desc_ix].cfg; + descs[desc_ix].cfg = &dcfgs[dcfg_ix]; + ++dcfg_ix; + } + if (oper.do_csum && (oper.csum_start < next_ix) && (prev_ix < (oper.csum_start + oper.csum_len))){ + csum_active = 1; + dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_CSUM_TID; + dcfgs[dcfg_ix].src = cryptocop_source_dma; + if (next_ix == (oper.csum_start + oper.csum_len)){ + csum_done = 1; + dcfgs[dcfg_ix].last = 1; + } else { + dcfgs[dcfg_ix].last = 0; + } + dcfgs[dcfg_ix].next = descs[desc_ix].cfg; + descs[desc_ix].cfg = &dcfgs[dcfg_ix]; + ++dcfg_ix; + } + if (!descs[desc_ix].cfg){ + DEBUG_API(printk("cryptocop_ioctl_process: data segment %d (%d to %d) had no active transforms\n", desc_ix, prev_ix, next_ix)); + err = -EINVAL; + goto error_cleanup; + } + descs[desc_ix].next = &(descs[desc_ix]) + 1; + ++desc_ix; + prev_ix = next_ix; + next_ix = next_cfg_change_ix(&oper, prev_ix); + } + if (desc_ix > 0){ + descs[desc_ix-1].next = NULL; + } else { + descs[0].next = NULL; + } + if (oper.do_digest) { + DEBUG(printk("cryptocop_ioctl_process: mapping %d byte digest output to iovec %d\n", digest_length, iovix)); + /* Add outdata iovec, length == */ + cop->tfrm_op.outdata[iovix].iov_base = digest_result; + cop->tfrm_op.outdata[iovix].iov_len = digest_length; + ++iovix; + } + if (oper.do_csum) { + /* Add outdata iovec, length == 2, the length of csum. */ + DEBUG(printk("cryptocop_ioctl_process: mapping 2 byte csum output to iovec %d\n", iovix)); + /* Add outdata iovec, length == */ + cop->tfrm_op.outdata[iovix].iov_base = csum_result; + cop->tfrm_op.outdata[iovix].iov_len = 2; + ++iovix; + } + if (oper.do_cipher) { + if (!map_pages_to_iovec(cop->tfrm_op.outdata, iovlen, &iovix, outpages, nooutpages, &pageix, &pageoffset, oper.cipher_outlen)){ + DEBUG_API(printk("cryptocop_ioctl_process: failed to map pages to iovec.\n")); + err = -ENOSYS; /* This should be impossible barring bugs. */ + goto error_cleanup; + } + } + DEBUG(printk("cryptocop_ioctl_process: setting cop->tfrm_op.outcount %d\n", iovix)); + cop->tfrm_op.outcount = iovix; + assert(iovix <= (nooutpages + 6)); + + cop->sid = oper.ses_id; + cop->tfrm_op.desc = &descs[0]; + + DEBUG(printk("cryptocop_ioctl_process: inserting job, cb_data=0x%p\n", cop->cb_data)); + + if ((err = cryptocop_job_queue_insert_user_job(cop)) != 0) { + DEBUG_API(printk("cryptocop_ioctl_process: insert job %d\n", err)); + err = -EINVAL; + goto error_cleanup; + } + + DEBUG(printk("cryptocop_ioctl_process: begin wait for result\n")); + + wait_event(cryptocop_ioc_process_wq, (jc->processed != 0)); + DEBUG(printk("cryptocop_ioctl_process: end wait for result\n")); + if (!jc->processed){ + printk(KERN_WARNING "cryptocop_ioctl_process: job not processed at completion\n"); + err = -EIO; + goto error_cleanup; + } + + /* Job process done. Cipher output should already be correct in job so no post processing of outdata. */ + DEBUG(printk("cryptocop_ioctl_process: operation_status = %d\n", cop->operation_status)); + if (cop->operation_status == 0){ + if (oper.do_digest){ + DEBUG(printk("cryptocop_ioctl_process: copy %d bytes digest to user\n", digest_length)); + err = copy_to_user((unsigned char*)crp_oper + offsetof(struct strcop_crypto_op, digest), digest_result, digest_length); + if (0 != err){ + DEBUG_API(printk("cryptocop_ioctl_process: copy_to_user, digest length %d, err %d\n", digest_length, err)); + err = -EFAULT; + goto error_cleanup; + } + } + if (oper.do_csum){ + DEBUG(printk("cryptocop_ioctl_process: copy 2 bytes checksum to user\n")); + err = copy_to_user((unsigned char*)crp_oper + offsetof(struct strcop_crypto_op, csum), csum_result, 2); + if (0 != err){ + DEBUG_API(printk("cryptocop_ioctl_process: copy_to_user, csum, err %d\n", err)); + err = -EFAULT; + goto error_cleanup; + } + } + err = 0; + } else { + DEBUG(printk("cryptocop_ioctl_process: returning err = operation_status = %d\n", cop->operation_status)); + err = cop->operation_status; + } + + error_cleanup: + /* Release page caches. */ + for (i = 0; i < noinpages; i++){ + put_page(inpages[i]); + } + for (i = 0; i < nooutpages; i++){ + int spdl_err; + /* Mark output pages dirty. */ + spdl_err = set_page_dirty_lock(outpages[i]); + DEBUG(if (spdl_err)printk("cryptocop_ioctl_process: set_page_dirty_lock returned %d\n", spdl_err)); + } + for (i = 0; i < nooutpages; i++){ + put_page(outpages[i]); + } + + if (digest_result) kfree(digest_result); + if (inpages) kfree(inpages); + if (outpages) kfree(outpages); + if (cop){ + if (cop->tfrm_op.indata) kfree(cop->tfrm_op.indata); + if (cop->tfrm_op.outdata) kfree(cop->tfrm_op.outdata); + kfree(cop); + } + if (jc) kfree(jc); + + DEBUG(print_lock_status()); + + return err; +} + + +static int cryptocop_ioctl_create_session(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + cryptocop_session_id sid; + int err; + struct cryptocop_private *dev; + struct strcop_session_op *sess_op = (struct strcop_session_op *)arg; + struct strcop_session_op sop; + struct cryptocop_transform_init *tis = NULL; + struct cryptocop_transform_init ti_cipher = {0}; + struct cryptocop_transform_init ti_digest = {0}; + struct cryptocop_transform_init ti_csum = {0}; + + if (!access_ok(VERIFY_WRITE, sess_op, sizeof(struct strcop_session_op))) + return -EFAULT; + err = copy_from_user(&sop, sess_op, sizeof(struct strcop_session_op)); + if (err) return -EFAULT; + if (sop.cipher != cryptocop_cipher_none) { + if (!access_ok(VERIFY_READ, sop.key, sop.keylen)) return -EFAULT; + } + DEBUG(printk("cryptocop_ioctl_create_session, sess_op:\n")); + + DEBUG(printk("\tcipher:%d\n" + "\tcipher_mode:%d\n" + "\tdigest:%d\n" + "\tcsum:%d\n", + (int)sop.cipher, + (int)sop.cmode, + (int)sop.digest, + (int)sop.csum)); + + if (sop.cipher != cryptocop_cipher_none){ + /* Init the cipher. */ + switch (sop.cipher){ + case cryptocop_cipher_des: + ti_cipher.alg = cryptocop_alg_des; + break; + case cryptocop_cipher_3des: + ti_cipher.alg = cryptocop_alg_3des; + break; + case cryptocop_cipher_aes: + ti_cipher.alg = cryptocop_alg_aes; + break; + default: + DEBUG_API(printk("create session, bad cipher algorithm %d\n", sop.cipher)); + return -EINVAL; + }; + DEBUG(printk("setting cipher transform %d\n", ti_cipher.alg)); + copy_from_user(ti_cipher.key, sop.key, sop.keylen/8); + ti_cipher.keylen = sop.keylen; + switch (sop.cmode){ + case cryptocop_cipher_mode_cbc: + case cryptocop_cipher_mode_ecb: + ti_cipher.cipher_mode = sop.cmode; + break; + default: + DEBUG_API(printk("create session, bad cipher mode %d\n", sop.cmode)); + return -EINVAL; + } + DEBUG(printk("cryptocop_ioctl_create_session: setting CBC mode %d\n", ti_cipher.cipher_mode)); + switch (sop.des3_mode){ + case cryptocop_3des_eee: + case cryptocop_3des_eed: + case cryptocop_3des_ede: + case cryptocop_3des_edd: + case cryptocop_3des_dee: + case cryptocop_3des_ded: + case cryptocop_3des_dde: + case cryptocop_3des_ddd: + ti_cipher.tdes_mode = sop.des3_mode; + break; + default: + DEBUG_API(printk("create session, bad 3DES mode %d\n", sop.des3_mode)); + return -EINVAL; + } + ti_cipher.tid = CRYPTOCOP_IOCTL_CIPHER_TID; + ti_cipher.next = tis; + tis = &ti_cipher; + } /* if (sop.cipher != cryptocop_cipher_none) */ + if (sop.digest != cryptocop_digest_none){ + DEBUG(printk("setting digest transform\n")); + switch (sop.digest){ + case cryptocop_digest_md5: + ti_digest.alg = cryptocop_alg_md5; + break; + case cryptocop_digest_sha1: + ti_digest.alg = cryptocop_alg_sha1; + break; + default: + DEBUG_API(printk("create session, bad digest algorithm %d\n", sop.digest)); + return -EINVAL; + } + ti_digest.tid = CRYPTOCOP_IOCTL_DIGEST_TID; + ti_digest.next = tis; + tis = &ti_digest; + } /* if (sop.digest != cryptocop_digest_none) */ + if (sop.csum != cryptocop_csum_none){ + DEBUG(printk("setting csum transform\n")); + switch (sop.csum){ + case cryptocop_csum_le: + case cryptocop_csum_be: + ti_csum.csum_mode = sop.csum; + break; + default: + DEBUG_API(printk("create session, bad checksum algorithm %d\n", sop.csum)); + return -EINVAL; + } + ti_csum.alg = cryptocop_alg_csum; + ti_csum.tid = CRYPTOCOP_IOCTL_CSUM_TID; + ti_csum.next = tis; + tis = &ti_csum; + } /* (sop.csum != cryptocop_csum_none) */ + dev = kmalloc(sizeof(struct cryptocop_private), GFP_KERNEL); + if (!dev){ + DEBUG_API(printk("create session, alloc dev\n")); + return -ENOMEM; + } + + err = cryptocop_new_session(&sid, tis, GFP_KERNEL); + DEBUG({ if (err) printk("create session, cryptocop_new_session %d\n", err);}); + + if (err) { + kfree(dev); + return err; + } + sess_op->ses_id = sid; + dev->sid = sid; + dev->next = filp->private_data; + filp->private_data = dev; + + return 0; +} + +static int cryptocop_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + if (_IOC_TYPE(cmd) != ETRAXCRYPTOCOP_IOCTYPE) { + DEBUG_API(printk("cryptocop_ioctl: wrong type\n")); + return -ENOTTY; + } + if (_IOC_NR(cmd) > CRYPTOCOP_IO_MAXNR){ + return -ENOTTY; + } + /* Access check of the argument. Some commands, e.g. create session and process op, + needs additional checks. Those are handled in the command handling functions. */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); + if (err) return -EFAULT; + + switch (cmd) { + case CRYPTOCOP_IO_CREATE_SESSION: + return cryptocop_ioctl_create_session(inode, filp, cmd, arg); + case CRYPTOCOP_IO_CLOSE_SESSION: + return cryptocop_ioctl_close_session(inode, filp, cmd, arg); + case CRYPTOCOP_IO_PROCESS_OP: + return cryptocop_ioctl_process(inode, filp, cmd, arg); + default: + DEBUG_API(printk("cryptocop_ioctl: unknown command\n")); + return -ENOTTY; + } + return 0; +} + + +#ifdef LDEBUG +static void print_dma_descriptors(struct cryptocop_int_operation *iop) +{ + struct cryptocop_dma_desc *cdesc_out = iop->cdesc_out; + struct cryptocop_dma_desc *cdesc_in = iop->cdesc_in; + int i; + + printk("print_dma_descriptors start\n"); + + printk("iop:\n"); + printk("\tsid: 0x%lld\n", iop->sid); + + printk("\tcdesc_out: 0x%p\n", iop->cdesc_out); + printk("\tcdesc_in: 0x%p\n", iop->cdesc_in); + printk("\tddesc_out: 0x%p\n", iop->ddesc_out); + printk("\tddesc_in: 0x%p\n", iop->ddesc_in); + + printk("\niop->ctx_out: 0x%p phys: 0x%p\n", &iop->ctx_out, (char*)virt_to_phys(&iop->ctx_out)); + printk("\tnext: 0x%p\n" + "\tsaved_data: 0x%p\n" + "\tsaved_data_buf: 0x%p\n", + iop->ctx_out.next, + iop->ctx_out.saved_data, + iop->ctx_out.saved_data_buf); + + printk("\niop->ctx_in: 0x%p phys: 0x%p\n", &iop->ctx_in, (char*)virt_to_phys(&iop->ctx_in)); + printk("\tnext: 0x%p\n" + "\tsaved_data: 0x%p\n" + "\tsaved_data_buf: 0x%p\n", + iop->ctx_in.next, + iop->ctx_in.saved_data, + iop->ctx_in.saved_data_buf); + + i = 0; + while (cdesc_out) { + dma_descr_data *td; + printk("cdesc_out %d, desc=0x%p\n", i, cdesc_out->dma_descr); + printk("\n\tvirt_to_phys(desc): 0x%p\n", (char*)virt_to_phys(cdesc_out->dma_descr)); + td = cdesc_out->dma_descr; + printk("\n\tbuf: 0x%p\n" + "\tafter: 0x%p\n" + "\tmd: 0x%04x\n" + "\tnext: 0x%p\n", + td->buf, + td->after, + td->md, + td->next); + printk("flags:\n" + "\twait:\t%d\n" + "\teol:\t%d\n" + "\touteop:\t%d\n" + "\tineop:\t%d\n" + "\tintr:\t%d\n", + td->wait, + td->eol, + td->out_eop, + td->in_eop, + td->intr); + cdesc_out = cdesc_out->next; + i++; + } + i = 0; + while (cdesc_in) { + dma_descr_data *td; + printk("cdesc_in %d, desc=0x%p\n", i, cdesc_in->dma_descr); + printk("\n\tvirt_to_phys(desc): 0x%p\n", (char*)virt_to_phys(cdesc_in->dma_descr)); + td = cdesc_in->dma_descr; + printk("\n\tbuf: 0x%p\n" + "\tafter: 0x%p\n" + "\tmd: 0x%04x\n" + "\tnext: 0x%p\n", + td->buf, + td->after, + td->md, + td->next); + printk("flags:\n" + "\twait:\t%d\n" + "\teol:\t%d\n" + "\touteop:\t%d\n" + "\tineop:\t%d\n" + "\tintr:\t%d\n", + td->wait, + td->eol, + td->out_eop, + td->in_eop, + td->intr); + cdesc_in = cdesc_in->next; + i++; + } + + printk("print_dma_descriptors end\n"); +} + + +static void print_strcop_crypto_op(struct strcop_crypto_op *cop) +{ + printk("print_strcop_crypto_op, 0x%p\n", cop); + + /* Indata. */ + printk("indata=0x%p\n" + "inlen=%d\n" + "do_cipher=%d\n" + "decrypt=%d\n" + "cipher_explicit=%d\n" + "cipher_start=%d\n" + "cipher_len=%d\n" + "outdata=0x%p\n" + "outlen=%d\n", + cop->indata, + cop->inlen, + cop->do_cipher, + cop->decrypt, + cop->cipher_explicit, + cop->cipher_start, + cop->cipher_len, + cop->cipher_outdata, + cop->cipher_outlen); + + printk("do_digest=%d\n" + "digest_start=%d\n" + "digest_len=%d\n", + cop->do_digest, + cop->digest_start, + cop->digest_len); + + printk("do_csum=%d\n" + "csum_start=%d\n" + "csum_len=%d\n", + cop->do_csum, + cop->csum_start, + cop->csum_len); +} + +static void print_cryptocop_operation(struct cryptocop_operation *cop) +{ + struct cryptocop_desc *d; + struct cryptocop_tfrm_cfg *tc; + struct cryptocop_desc_cfg *dc; + int i; + + printk("print_cryptocop_operation, cop=0x%p\n\n", cop); + printk("sid: %lld\n", cop->sid); + printk("operation_status=%d\n" + "use_dmalists=%d\n" + "in_interrupt=%d\n" + "fast_callback=%d\n", + cop->operation_status, + cop->use_dmalists, + cop->in_interrupt, + cop->fast_callback); + + if (cop->use_dmalists){ + print_user_dma_lists(&cop->list_op); + } else { + printk("cop->tfrm_op\n" + "tfrm_cfg=0x%p\n" + "desc=0x%p\n" + "indata=0x%p\n" + "incount=%d\n" + "inlen=%d\n" + "outdata=0x%p\n" + "outcount=%d\n" + "outlen=%d\n\n", + cop->tfrm_op.tfrm_cfg, + cop->tfrm_op.desc, + cop->tfrm_op.indata, + cop->tfrm_op.incount, + cop->tfrm_op.inlen, + cop->tfrm_op.outdata, + cop->tfrm_op.outcount, + cop->tfrm_op.outlen); + + tc = cop->tfrm_op.tfrm_cfg; + while (tc){ + printk("tfrm_cfg, 0x%p\n" + "tid=%d\n" + "flags=%d\n" + "inject_ix=%d\n" + "next=0x%p\n", + tc, + tc->tid, + tc->flags, + tc->inject_ix, + tc->next); + tc = tc->next; + } + d = cop->tfrm_op.desc; + while (d){ + printk("\n======================desc, 0x%p\n" + "length=%d\n" + "cfg=0x%p\n" + "next=0x%p\n", + d, + d->length, + d->cfg, + d->next); + dc = d->cfg; + while (dc){ + printk("=========desc_cfg, 0x%p\n" + "tid=%d\n" + "src=%d\n" + "last=%d\n" + "next=0x%p\n", + dc, + dc->tid, + dc->src, + dc->last, + dc->next); + dc = dc->next; + } + d = d->next; + } + printk("\n====iniov\n"); + for (i = 0; i < cop->tfrm_op.incount; i++){ + printk("indata[%d]\n" + "base=0x%p\n" + "len=%d\n", + i, + cop->tfrm_op.indata[i].iov_base, + cop->tfrm_op.indata[i].iov_len); + } + printk("\n====outiov\n"); + for (i = 0; i < cop->tfrm_op.outcount; i++){ + printk("outdata[%d]\n" + "base=0x%p\n" + "len=%d\n", + i, + cop->tfrm_op.outdata[i].iov_base, + cop->tfrm_op.outdata[i].iov_len); + } + } + printk("------------end print_cryptocop_operation\n"); +} + + +static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op) +{ + dma_descr_data *dd; + int i; + + printk("print_user_dma_lists, dma_op=0x%p\n", dma_op); + + printk("out_data_buf = 0x%p, phys_to_virt(out_data_buf) = 0x%p\n", dma_op->out_data_buf, phys_to_virt((unsigned long int)dma_op->out_data_buf)); + printk("in_data_buf = 0x%p, phys_to_virt(in_data_buf) = 0x%p\n", dma_op->in_data_buf, phys_to_virt((unsigned long int)dma_op->in_data_buf)); + + printk("##############outlist\n"); + dd = phys_to_virt((unsigned long int)dma_op->outlist); + i = 0; + while (dd != NULL) { + printk("#%d phys_to_virt(desc) 0x%p\n", i, dd); + printk("\n\tbuf: 0x%p\n" + "\tafter: 0x%p\n" + "\tmd: 0x%04x\n" + "\tnext: 0x%p\n", + dd->buf, + dd->after, + dd->md, + dd->next); + printk("flags:\n" + "\twait:\t%d\n" + "\teol:\t%d\n" + "\touteop:\t%d\n" + "\tineop:\t%d\n" + "\tintr:\t%d\n", + dd->wait, + dd->eol, + dd->out_eop, + dd->in_eop, + dd->intr); + if (dd->eol) + dd = NULL; + else + dd = phys_to_virt((unsigned long int)dd->next); + ++i; + } + + printk("##############inlist\n"); + dd = phys_to_virt((unsigned long int)dma_op->inlist); + i = 0; + while (dd != NULL) { + printk("#%d phys_to_virt(desc) 0x%p\n", i, dd); + printk("\n\tbuf: 0x%p\n" + "\tafter: 0x%p\n" + "\tmd: 0x%04x\n" + "\tnext: 0x%p\n", + dd->buf, + dd->after, + dd->md, + dd->next); + printk("flags:\n" + "\twait:\t%d\n" + "\teol:\t%d\n" + "\touteop:\t%d\n" + "\tineop:\t%d\n" + "\tintr:\t%d\n", + dd->wait, + dd->eol, + dd->out_eop, + dd->in_eop, + dd->intr); + if (dd->eol) + dd = NULL; + else + dd = phys_to_virt((unsigned long int)dd->next); + ++i; + } +} + + +static void print_lock_status(void) +{ + printk("**********************print_lock_status\n"); + printk("cryptocop_completed_jobs_lock %d\n", spin_is_locked(&cryptocop_completed_jobs_lock)); + printk("cryptocop_job_queue_lock %d\n", spin_is_locked(&cryptocop_job_queue_lock)); + printk("descr_pool_lock %d\n", spin_is_locked(&descr_pool_lock)); + printk("cryptocop_sessions_lock %d\n", spin_is_locked(cryptocop_sessions_lock)); + printk("running_job_lock %d\n", spin_is_locked(running_job_lock)); + printk("cryptocop_process_lock %d\n", spin_is_locked(cryptocop_process_lock)); +} +#endif /* LDEBUG */ + + +static const char cryptocop_name[] = "ETRAX FS stream co-processor"; + +static int init_stream_coprocessor(void) +{ + int err; + int i; + static int initialized = 0; + + if (initialized) + return 0; + + initialized = 1; + + printk("ETRAX FS stream co-processor driver v0.01, (c) 2003 Axis Communications AB\n"); + + err = register_chrdev(CRYPTOCOP_MAJOR, cryptocop_name, &cryptocop_fops); + if (err < 0) { + printk(KERN_ERR "stream co-processor: could not get major number.\n"); + return err; + } + + err = init_cryptocop(); + if (err) { + (void)unregister_chrdev(CRYPTOCOP_MAJOR, cryptocop_name); + return err; + } + err = cryptocop_job_queue_init(); + if (err) { + release_cryptocop(); + (void)unregister_chrdev(CRYPTOCOP_MAJOR, cryptocop_name); + return err; + } + /* Init the descriptor pool. */ + for (i = 0; i < CRYPTOCOP_DESCRIPTOR_POOL_SIZE - 1; i++) { + descr_pool[i].from_pool = 1; + descr_pool[i].next = &descr_pool[i + 1]; + } + descr_pool[i].from_pool = 1; + descr_pool[i].next = NULL; + descr_pool_free_list = &descr_pool[0]; + descr_pool_no_free = CRYPTOCOP_DESCRIPTOR_POOL_SIZE; + + spin_lock_init(&cryptocop_completed_jobs_lock); + spin_lock_init(&cryptocop_job_queue_lock); + spin_lock_init(&descr_pool_lock); + spin_lock_init(&cryptocop_sessions_lock); + spin_lock_init(&running_job_lock); + spin_lock_init(&cryptocop_process_lock); + + cryptocop_sessions = NULL; + next_sid = 1; + + cryptocop_running_job = NULL; + + printk("stream co-processor: init done.\n"); + return 0; +} + +static void __exit exit_stream_coprocessor(void) +{ + release_cryptocop(); + cryptocop_job_queue_close(); +} + +module_init(init_stream_coprocessor); +module_exit(exit_stream_coprocessor); + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/gpio.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,766 @@ +/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $ + * + * ETRAX CRISv32 general port I/O device + * + * Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB + * + * Authors: Bjorn Wesen (initial version) + * Ola Knutsson (LED handling) + * Johan Adolfsson (read/set directions, write, port G, + * port to ETRAX FS. + * + * $Log: gpio.c,v $ + * Revision 1.16 2005/06/19 17:06:49 starvik + * Merge of Linux 2.6.12. + * + * Revision 1.15 2005/05/25 08:22:20 starvik + * Changed GPIO port order to fit packages/devices/axis-2.4. + * + * Revision 1.14 2005/04/24 18:35:08 starvik + * Updated with final register headers. + * + * Revision 1.13 2005/03/15 15:43:00 starvik + * dev_id needs to be supplied for shared IRQs. + * + * Revision 1.12 2005/03/10 17:12:00 starvik + * Protect alarm list with spinlock. + * + * Revision 1.11 2005/01/05 06:08:59 starvik + * No need to do local_irq_disable after local_irq_save. + * + * Revision 1.10 2004/11/19 08:38:31 starvik + * Removed old crap. + * + * Revision 1.9 2004/05/14 07:58:02 starvik + * Merge of changes from 2.4 + * + * Revision 1.8 2003/09/11 07:29:50 starvik + * Merge of Linux 2.6.0-test5 + * + * Revision 1.7 2003/07/10 13:25:46 starvik + * Compiles for 2.5.74 + * Lindented ethernet.c + * + * Revision 1.6 2003/07/04 08:27:46 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.5 2003/06/10 08:26:37 johana + * Etrax -> ETRAX CRISv32 + * + * Revision 1.4 2003/06/05 14:22:48 johana + * Initialise some_alarms. + * + * Revision 1.3 2003/06/05 10:15:46 johana + * New INTR_VECT macros. + * Enable interrupts in global config. + * + * Revision 1.2 2003/06/03 15:52:50 johana + * Initial CRIS v32 version. + * + * Revision 1.1 2003/06/03 08:53:15 johana + * Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* The following gio ports on ETRAX FS is available: + * pa 8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge + * pb 18 bits + * pc 18 bits + * pd 18 bits + * pe 18 bits + * each port has a rw_px_dout, r_px_din and rw_px_oe register. + */ + +#define GPIO_MAJOR 120 /* experimental MAJOR number */ + +#define D(x) + +#if 0 +static int dp_cnt; +#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0) +#else +#define DP(x) +#endif + +static char gpio_name[] = "etrax gpio"; + +#if 0 +static wait_queue_head_t *gpio_wq; +#endif + +static int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file * file, const char * buf, size_t count, + loff_t *off); +static int gpio_open(struct inode *inode, struct file *filp); +static int gpio_release(struct inode *inode, struct file *filp); +static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); + +/* private data per open() of this driver */ + +struct gpio_private { + struct gpio_private *next; + /* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */ + unsigned char clk_mask; + unsigned char data_mask; + unsigned char write_msb; + unsigned char pad1; + /* These fields are generic */ + unsigned long highalarm, lowalarm; + wait_queue_head_t alarm_wq; + int minor; +}; + +/* linked list of alarms to check for */ + +static struct gpio_private *alarmlist = 0; + +static int gpio_some_alarms = 0; /* Set if someone uses alarm */ +static unsigned long gpio_pa_high_alarms = 0; +static unsigned long gpio_pa_low_alarms = 0; + +static DEFINE_SPINLOCK(alarm_lock); + +#define NUM_PORTS (GPIO_MINOR_LAST+1) +#define GIO_REG_RD_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg ) +#define GIO_REG_WR_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg ) +unsigned long led_dummy; + +static volatile unsigned long *data_out[NUM_PORTS] = { + GIO_REG_WR_ADDR(rw_pa_dout), + GIO_REG_WR_ADDR(rw_pb_dout), + &led_dummy, + GIO_REG_WR_ADDR(rw_pc_dout), + GIO_REG_WR_ADDR(rw_pd_dout), + GIO_REG_WR_ADDR(rw_pe_dout), +}; + +static volatile unsigned long *data_in[NUM_PORTS] = { + GIO_REG_RD_ADDR(r_pa_din), + GIO_REG_RD_ADDR(r_pb_din), + &led_dummy, + GIO_REG_RD_ADDR(r_pc_din), + GIO_REG_RD_ADDR(r_pd_din), + GIO_REG_RD_ADDR(r_pe_din), +}; + +static unsigned long changeable_dir[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_DIR, + CONFIG_ETRAX_PB_CHANGEABLE_DIR, + 0, + CONFIG_ETRAX_PC_CHANGEABLE_DIR, + CONFIG_ETRAX_PD_CHANGEABLE_DIR, + CONFIG_ETRAX_PE_CHANGEABLE_DIR, +}; + +static unsigned long changeable_bits[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_BITS, + CONFIG_ETRAX_PB_CHANGEABLE_BITS, + 0, + CONFIG_ETRAX_PC_CHANGEABLE_BITS, + CONFIG_ETRAX_PD_CHANGEABLE_BITS, + CONFIG_ETRAX_PE_CHANGEABLE_BITS, +}; + +static volatile unsigned long *dir_oe[NUM_PORTS] = { + GIO_REG_WR_ADDR(rw_pa_oe), + GIO_REG_WR_ADDR(rw_pb_oe), + &led_dummy, + GIO_REG_WR_ADDR(rw_pc_oe), + GIO_REG_WR_ADDR(rw_pd_oe), + GIO_REG_WR_ADDR(rw_pe_oe), +}; + + + +static unsigned int +gpio_poll(struct file *file, + poll_table *wait) +{ + unsigned int mask = 0; + struct gpio_private *priv = (struct gpio_private *)file->private_data; + unsigned long data; + poll_wait(file, &priv->alarm_wq, wait); + if (priv->minor == GPIO_MINOR_A) { + reg_gio_rw_intr_cfg intr_cfg; + unsigned long tmp; + unsigned long flags; + + local_irq_save(flags); + data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din, REG_RD(gio, regi_gio, r_pa_din)); + /* PA has support for interrupt + * lets activate high for those low and with highalarm set + */ + intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); + + tmp = ~data & priv->highalarm & 0xFF; + if (tmp & (1 << 0)) { + intr_cfg.pa0 = regk_gio_hi; + } + if (tmp & (1 << 1)) { + intr_cfg.pa1 = regk_gio_hi; + } + if (tmp & (1 << 2)) { + intr_cfg.pa2 = regk_gio_hi; + } + if (tmp & (1 << 3)) { + intr_cfg.pa3 = regk_gio_hi; + } + if (tmp & (1 << 4)) { + intr_cfg.pa4 = regk_gio_hi; + } + if (tmp & (1 << 5)) { + intr_cfg.pa5 = regk_gio_hi; + } + if (tmp & (1 << 6)) { + intr_cfg.pa6 = regk_gio_hi; + } + if (tmp & (1 << 7)) { + intr_cfg.pa7 = regk_gio_hi; + } + /* + * lets activate low for those high and with lowalarm set + */ + tmp = data & priv->lowalarm & 0xFF; + if (tmp & (1 << 0)) { + intr_cfg.pa0 = regk_gio_lo; + } + if (tmp & (1 << 1)) { + intr_cfg.pa1 = regk_gio_lo; + } + if (tmp & (1 << 2)) { + intr_cfg.pa2 = regk_gio_lo; + } + if (tmp & (1 << 3)) { + intr_cfg.pa3 = regk_gio_lo; + } + if (tmp & (1 << 4)) { + intr_cfg.pa4 = regk_gio_lo; + } + if (tmp & (1 << 5)) { + intr_cfg.pa5 = regk_gio_lo; + } + if (tmp & (1 << 6)) { + intr_cfg.pa6 = regk_gio_lo; + } + if (tmp & (1 << 7)) { + intr_cfg.pa7 = regk_gio_lo; + } + + REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); + local_irq_restore(flags); + } else if (priv->minor <= GPIO_MINOR_E) + data = *data_in[priv->minor]; + else + return 0; + + if ((data & priv->highalarm) || + (~data & priv->lowalarm)) { + mask = POLLIN|POLLRDNORM; + } + + DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); + return mask; +} + +int etrax_gpio_wake_up_check(void) +{ + struct gpio_private *priv = alarmlist; + unsigned long data = 0; + int ret = 0; + while (priv) { + data = *data_in[priv->minor]; + if ((data & priv->highalarm) || + (~data & priv->lowalarm)) { + DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); + wake_up_interruptible(&priv->alarm_wq); + ret = 1; + } + priv = priv->next; + } + return ret; +} + +static irqreturn_t +gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + if (gpio_some_alarms) { + return IRQ_RETVAL(etrax_gpio_wake_up_check()); + } + return IRQ_NONE; +} + +static irqreturn_t +gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + reg_gio_rw_intr_mask intr_mask; + reg_gio_r_masked_intr masked_intr; + reg_gio_rw_ack_intr ack_intr; + unsigned long tmp; + unsigned long tmp2; + + /* Find what PA interrupts are active */ + masked_intr = REG_RD(gio, regi_gio, r_masked_intr); + tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr); + + /* Find those that we have enabled */ + spin_lock(&alarm_lock); + tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms); + spin_unlock(&alarm_lock); + + /* Ack them */ + ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); + REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); + + /* Disable those interrupts.. */ + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask); + tmp2 &= ~tmp; + intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); + + if (gpio_some_alarms) { + return IRQ_RETVAL(etrax_gpio_wake_up_check()); + } + return IRQ_NONE; +} + + +static ssize_t gpio_write(struct file * file, const char * buf, size_t count, + loff_t *off) +{ + struct gpio_private *priv = (struct gpio_private *)file->private_data; + unsigned char data, clk_mask, data_mask, write_msb; + unsigned long flags; + unsigned long shadow; + volatile unsigned long *port; + ssize_t retval = count; + /* Only bits 0-7 may be used for write operations but allow all + devices except leds... */ + if (priv->minor == GPIO_MINOR_LEDS) { + return -EFAULT; + } + + if (!access_ok(VERIFY_READ, buf, count)) { + return -EFAULT; + } + clk_mask = priv->clk_mask; + data_mask = priv->data_mask; + /* It must have been configured using the IO_CFG_WRITE_MODE */ + /* Perhaps a better error code? */ + if (clk_mask == 0 || data_mask == 0) { + return -EPERM; + } + write_msb = priv->write_msb; + D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); + port = data_out[priv->minor]; + + while (count--) { + int i; + data = *buf++; + if (priv->write_msb) { + for (i = 7; i >= 0;i--) { + local_irq_save(flags); + shadow = *port; + *port = shadow &= ~clk_mask; + if (data & 1<i_rdev); + + if (p > GPIO_MINOR_LAST) + return -EINVAL; + + priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), + GFP_KERNEL); + + if (!priv) + return -ENOMEM; + + priv->minor = p; + + /* initialize the io/alarm struct and link it into our alarmlist */ + + priv->next = alarmlist; + alarmlist = priv; + priv->clk_mask = 0; + priv->data_mask = 0; + priv->highalarm = 0; + priv->lowalarm = 0; + init_waitqueue_head(&priv->alarm_wq); + + filp->private_data = (void *)priv; + + return 0; +} + +static int +gpio_release(struct inode *inode, struct file *filp) +{ + struct gpio_private *p = alarmlist; + struct gpio_private *todel = (struct gpio_private *)filp->private_data; + /* local copies while updating them: */ + unsigned long a_high, a_low; + unsigned long some_alarms; + + /* unlink from alarmlist and free the private structure */ + + if (p == todel) { + alarmlist = todel->next; + } else { + while (p->next != todel) + p = p->next; + p->next = todel->next; + } + + kfree(todel); + /* Check if there are still any alarms set */ + p = alarmlist; + some_alarms = 0; + a_high = 0; + a_low = 0; + while (p) { + if (p->minor == GPIO_MINOR_A) { + a_high |= p->highalarm; + a_low |= p->lowalarm; + } + + if (p->highalarm | p->lowalarm) { + some_alarms = 1; + } + p = p->next; + } + + spin_lock(&alarm_lock); + gpio_some_alarms = some_alarms; + gpio_pa_high_alarms = a_high; + gpio_pa_low_alarms = a_low; + spin_unlock(&alarm_lock); + + return 0; +} + +/* Main device API. ioctl's to read/set/clear bits, as well as to + * set alarms to wait for using a subsequent select(). + */ + +unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) +{ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + unsigned long flags; + unsigned long dir_shadow; + + local_irq_save(flags); + dir_shadow = *dir_oe[priv->minor]; + dir_shadow &= ~(arg & changeable_dir[priv->minor]); + *dir_oe[priv->minor] = dir_shadow; + local_irq_restore(flags); + + if (priv->minor == GPIO_MINOR_A) + dir_shadow ^= 0xFF; /* Only 8 bits */ + else + dir_shadow ^= 0x3FFFF; /* Only 18 bits */ + return dir_shadow; + +} /* setget_input */ + +unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) +{ + unsigned long flags; + unsigned long dir_shadow; + + local_irq_save(flags); + dir_shadow = *dir_oe[priv->minor]; + dir_shadow |= (arg & changeable_dir[priv->minor]); + *dir_oe[priv->minor] = dir_shadow; + local_irq_restore(flags); + return dir_shadow; +} /* setget_output */ + +static int +gpio_leds_ioctl(unsigned int cmd, unsigned long arg); + +static int +gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned long val; + unsigned long shadow; + struct gpio_private *priv = (struct gpio_private *)file->private_data; + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + return -EINVAL; + } + + switch (_IOC_NR(cmd)) { + case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ + // read the port + return *data_in[priv->minor]; + break; + case IO_SETBITS: + local_irq_save(flags); + if (arg & 0x04) + printk("GPIO SET 2\n"); + // set changeable bits with a 1 in arg + shadow = *data_out[priv->minor]; + shadow |= (arg & changeable_bits[priv->minor]); + *data_out[priv->minor] = shadow; + local_irq_restore(flags); + break; + case IO_CLRBITS: + local_irq_save(flags); + if (arg & 0x04) + printk("GPIO CLR 2\n"); + // clear changeable bits with a 1 in arg + shadow = *data_out[priv->minor]; + shadow &= ~(arg & changeable_bits[priv->minor]); + *data_out[priv->minor] = shadow; + local_irq_restore(flags); + break; + case IO_HIGHALARM: + // set alarm when bits with 1 in arg go high + priv->highalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + if (priv->minor == GPIO_MINOR_A) { + gpio_pa_high_alarms |= arg; + } + spin_unlock(&alarm_lock); + break; + case IO_LOWALARM: + // set alarm when bits with 1 in arg go low + priv->lowalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + if (priv->minor == GPIO_MINOR_A) { + gpio_pa_low_alarms |= arg; + } + spin_unlock(&alarm_lock); + break; + case IO_CLRALARM: + // clear alarm for bits with 1 in arg + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + spin_lock(&alarm_lock); + if (priv->minor == GPIO_MINOR_A) { + if (gpio_pa_high_alarms & arg || + gpio_pa_low_alarms & arg) { + /* Must update the gpio_pa_*alarms masks */ + } + } + spin_unlock(&alarm_lock); + break; + case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ + /* Read direction 0=input 1=output */ + return *dir_oe[priv->minor]; + case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + return setget_input(priv, arg); + break; + case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ + /* Set direction 0=unchanged 1=output, + * return mask with 1=output + */ + return setget_output(priv, arg); + + case IO_CFG_WRITE_MODE: + { + unsigned long dir_shadow; + dir_shadow = *dir_oe[priv->minor]; + + priv->clk_mask = arg & 0xFF; + priv->data_mask = (arg >> 8) & 0xFF; + priv->write_msb = (arg >> 16) & 0x01; + /* Check if we're allowed to change the bits and + * the direction is correct + */ + if (!((priv->clk_mask & changeable_bits[priv->minor]) && + (priv->data_mask & changeable_bits[priv->minor]) && + (priv->clk_mask & dir_shadow) && + (priv->data_mask & dir_shadow))) + { + priv->clk_mask = 0; + priv->data_mask = 0; + return -EPERM; + } + break; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = *data_in[priv->minor]; + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + break; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + val = *data_out[priv->minor]; + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + if (priv->minor == GPIO_MINOR_LEDS) + return gpio_leds_ioctl(cmd, arg); + else + return -EINVAL; + } /* switch */ + + return 0; +} + +static int +gpio_leds_ioctl(unsigned int cmd, unsigned long arg) +{ + unsigned char green; + unsigned char red; + + switch (_IOC_NR(cmd)) { + case IO_LEDACTIVE_SET: + green = ((unsigned char) arg) & 1; + red = (((unsigned char) arg) >> 1) & 1; + LED_ACTIVE_SET_G(green); + LED_ACTIVE_SET_R(red); + break; + + default: + return -EINVAL; + } /* switch */ + + return 0; +} + +struct file_operations gpio_fops = { + .owner = THIS_MODULE, + .poll = gpio_poll, + .ioctl = gpio_ioctl, + .write = gpio_write, + .open = gpio_open, + .release = gpio_release, +}; + + +/* main driver initialization routine, called from mem.c */ + +static __init int +gpio_init(void) +{ + int res; + reg_intr_vect_rw_mask intr_mask; + + /* do the formalities */ + + res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); + if (res < 0) { + printk(KERN_ERR "gpio: couldn't get a major number.\n"); + return res; + } + + /* Clear all leds */ + LED_NETWORK_SET(0); + LED_ACTIVE_SET(0); + LED_DISK_READ(0); + LED_DISK_WRITE(0); + + printk("ETRAX FS GPIO driver v2.5, (c) 2003-2005 Axis Communications AB\n"); + /* We call etrax_gpio_wake_up_check() from timer interrupt and + * from cpu_idle() in kernel/process.c + * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms + * in some tests. + */ + if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt, + SA_SHIRQ | SA_INTERRUPT,"gpio poll", &alarmlist)) { + printk("err: timer0 irq for gpio\n"); + } + if (request_irq(GEN_IO_INTR_VECT, gpio_pa_interrupt, + SA_SHIRQ | SA_INTERRUPT,"gpio PA", &alarmlist)) { + printk("err: PA irq for gpio\n"); + } + /* enable the gio and timer irq in global config */ + intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); + intr_mask.timer = 1; + intr_mask.gen_io = 1; + REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + + return res; +} + +/* this makes sure that gpio_init is called during kernel boot */ + +module_init(gpio_init); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/i2c.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,611 @@ +/*!*************************************************************************** +*! +*! FILE NAME : i2c.c +*! +*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other +*! kernel modules (i2c_writereg/readreg) and from userspace using +*! ioctl()'s +*! +*! Nov 30 1998 Torbjorn Eliasson Initial version. +*! Bjorn Wesen Elinux kernel version. +*! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff - +*! don't use PB_I2C if DS1302 uses same bits, +*! use PB. +*| June 23 2003 Pieter Grimmerink Added 'i2c_sendnack'. i2c_readreg now +*| generates nack on last received byte, +*| instead of ack. +*| i2c_getack changed data level while clock +*| was high, causing DS75 to see a stop condition +*! +*! --------------------------------------------------------------------------- +*! +*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN +*! +*!***************************************************************************/ +/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */ +/****************** INCLUDE FILES SECTION ***********************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "i2c.h" + +/****************** I2C DEFINITION SECTION *************************/ + +#define D(x) + +#define I2C_MAJOR 123 /* LOCAL/EXPERIMENTAL */ +static const char i2c_name[] = "i2c"; + +#define CLOCK_LOW_TIME 8 +#define CLOCK_HIGH_TIME 8 +#define START_CONDITION_HOLD_TIME 8 +#define STOP_CONDITION_HOLD_TIME 8 +#define ENABLE_OUTPUT 0x01 +#define ENABLE_INPUT 0x00 +#define I2C_CLOCK_HIGH 1 +#define I2C_CLOCK_LOW 0 +#define I2C_DATA_HIGH 1 +#define I2C_DATA_LOW 0 + +#define i2c_enable() +#define i2c_disable() + +/* enable or disable output-enable, to select output or input on the i2c bus */ + +#define i2c_dir_out() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_out) +#define i2c_dir_in() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_in) + +/* control the i2c clock and data signals */ + +#define i2c_clk(x) crisv32_io_set(&cris_i2c_clk, x) +#define i2c_data(x) crisv32_io_set(&cris_i2c_data, x) + +/* read a bit from the i2c interface */ + +#define i2c_getbit() crisv32_io_rd(&cris_i2c_data) + +#define i2c_delay(usecs) udelay(usecs) + +/****************** VARIABLE SECTION ************************************/ + +static struct crisv32_iopin cris_i2c_clk; +static struct crisv32_iopin cris_i2c_data; + +/****************** FUNCTION DEFINITION SECTION *************************/ + + +/* generate i2c start condition */ + +void +i2c_start(void) +{ + /* + * SCL=1 SDA=1 + */ + i2c_dir_out(); + i2c_delay(CLOCK_HIGH_TIME/6); + i2c_data(I2C_DATA_HIGH); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + /* + * SCL=1 SDA=0 + */ + i2c_data(I2C_DATA_LOW); + i2c_delay(START_CONDITION_HOLD_TIME); + /* + * SCL=0 SDA=0 + */ + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME); +} + +/* generate i2c stop condition */ + +void +i2c_stop(void) +{ + i2c_dir_out(); + + /* + * SCL=0 SDA=0 + */ + i2c_clk(I2C_CLOCK_LOW); + i2c_data(I2C_DATA_LOW); + i2c_delay(CLOCK_LOW_TIME*2); + /* + * SCL=1 SDA=0 + */ + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME*2); + /* + * SCL=1 SDA=1 + */ + i2c_data(I2C_DATA_HIGH); + i2c_delay(STOP_CONDITION_HOLD_TIME); + + i2c_dir_in(); +} + +/* write a byte to the i2c interface */ + +void +i2c_outbyte(unsigned char x) +{ + int i; + + i2c_dir_out(); + + for (i = 0; i < 8; i++) { + if (x & 0x80) { + i2c_data(I2C_DATA_HIGH); + } else { + i2c_data(I2C_DATA_LOW); + } + + i2c_delay(CLOCK_LOW_TIME/2); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME/2); + x <<= 1; + } + i2c_data(I2C_DATA_LOW); + i2c_delay(CLOCK_LOW_TIME/2); + + /* + * enable input + */ + i2c_dir_in(); +} + +/* read a byte from the i2c interface */ + +unsigned char +i2c_inbyte(void) +{ + unsigned char aBitByte = 0; + int i; + + /* Switch off I2C to get bit */ + i2c_disable(); + i2c_dir_in(); + i2c_delay(CLOCK_HIGH_TIME/2); + + /* Get bit */ + aBitByte |= i2c_getbit(); + + /* Enable I2C */ + i2c_enable(); + i2c_delay(CLOCK_LOW_TIME/2); + + for (i = 1; i < 8; i++) { + aBitByte <<= 1; + /* Clock pulse */ + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME); + + /* Switch off I2C to get bit */ + i2c_disable(); + i2c_dir_in(); + i2c_delay(CLOCK_HIGH_TIME/2); + + /* Get bit */ + aBitByte |= i2c_getbit(); + + /* Enable I2C */ + i2c_enable(); + i2c_delay(CLOCK_LOW_TIME/2); + } + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + + /* + * we leave the clock low, getbyte is usually followed + * by sendack/nack, they assume the clock to be low + */ + i2c_clk(I2C_CLOCK_LOW); + return aBitByte; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_getack +*# +*# DESCRIPTION : checks if ack was received from ic2 +*# +*#--------------------------------------------------------------------------*/ + +int +i2c_getack(void) +{ + int ack = 1; + /* + * enable output + */ + i2c_dir_out(); + /* + * Release data bus by setting + * data high + */ + i2c_data(I2C_DATA_HIGH); + /* + * enable input + */ + i2c_dir_in(); + i2c_delay(CLOCK_HIGH_TIME/4); + /* + * generate ACK clock pulse + */ + i2c_clk(I2C_CLOCK_HIGH); + /* + * Use PORT PB instead of I2C + * for input. (I2C not working) + */ + i2c_clk(1); + i2c_data(1); + /* + * switch off I2C + */ + i2c_data(1); + i2c_disable(); + i2c_dir_in(); + /* + * now wait for ack + */ + i2c_delay(CLOCK_HIGH_TIME/2); + /* + * check for ack + */ + if(i2c_getbit()) + ack = 0; + i2c_delay(CLOCK_HIGH_TIME/2); + if(!ack){ + if(!i2c_getbit()) /* receiver pulld SDA low */ + ack = 1; + i2c_delay(CLOCK_HIGH_TIME/2); + } + + /* + * our clock is high now, make sure data is low + * before we enable our output. If we keep data high + * and enable output, we would generate a stop condition. + */ + i2c_data(I2C_DATA_LOW); + + /* + * end clock pulse + */ + i2c_enable(); + i2c_dir_out(); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_HIGH_TIME/4); + /* + * enable output + */ + i2c_dir_out(); + /* + * remove ACK clock pulse + */ + i2c_data(I2C_DATA_HIGH); + i2c_delay(CLOCK_LOW_TIME/2); + return ack; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: I2C::sendAck +*# +*# DESCRIPTION : Send ACK on received data +*# +*#--------------------------------------------------------------------------*/ +void +i2c_sendack(void) +{ + /* + * enable output + */ + i2c_delay(CLOCK_LOW_TIME); + i2c_dir_out(); + /* + * set ack pulse high + */ + i2c_data(I2C_DATA_LOW); + /* + * generate clock pulse + */ + i2c_delay(CLOCK_HIGH_TIME/6); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME/6); + /* + * reset data out + */ + i2c_data(I2C_DATA_HIGH); + i2c_delay(CLOCK_LOW_TIME); + + i2c_dir_in(); +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_sendnack +*# +*# DESCRIPTION : Sends NACK on received data +*# +*#--------------------------------------------------------------------------*/ +void +i2c_sendnack(void) +{ + /* + * enable output + */ + i2c_delay(CLOCK_LOW_TIME); + i2c_dir_out(); + /* + * set data high + */ + i2c_data(I2C_DATA_HIGH); + /* + * generate clock pulse + */ + i2c_delay(CLOCK_HIGH_TIME/6); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME); + + i2c_dir_in(); +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_writereg +*# +*# DESCRIPTION : Writes a value to an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_writereg(unsigned char theSlave, unsigned char theReg, + unsigned char theValue) +{ + int error, cntr = 3; + unsigned long flags; + + do { + error = 0; + /* + * we don't like to be interrupted + */ + local_irq_save(flags); + + i2c_start(); + /* + * send slave address + */ + i2c_outbyte((theSlave & 0xfe)); + /* + * wait for ack + */ + if(!i2c_getack()) + error = 1; + /* + * now select register + */ + i2c_dir_out(); + i2c_outbyte(theReg); + /* + * now it's time to wait for ack + */ + if(!i2c_getack()) + error |= 2; + /* + * send register register data + */ + i2c_outbyte(theValue); + /* + * now it's time to wait for ack + */ + if(!i2c_getack()) + error |= 4; + /* + * end byte stream + */ + i2c_stop(); + /* + * enable interrupt again + */ + local_irq_restore(flags); + + } while(error && cntr--); + + i2c_delay(CLOCK_LOW_TIME); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_readreg +*# +*# DESCRIPTION : Reads a value from the decoder registers. +*# +*#--------------------------------------------------------------------------*/ +unsigned char +i2c_readreg(unsigned char theSlave, unsigned char theReg) +{ + unsigned char b = 0; + int error, cntr = 3; + unsigned long flags; + + do { + error = 0; + /* + * we don't like to be interrupted + */ + local_irq_save(flags); + /* + * generate start condition + */ + i2c_start(); + + /* + * send slave address + */ + i2c_outbyte((theSlave & 0xfe)); + /* + * wait for ack + */ + if(!i2c_getack()) + error = 1; + /* + * now select register + */ + i2c_dir_out(); + i2c_outbyte(theReg); + /* + * now it's time to wait for ack + */ + if(!i2c_getack()) + error = 1; + /* + * repeat start condition + */ + i2c_delay(CLOCK_LOW_TIME); + i2c_start(); + /* + * send slave address + */ + i2c_outbyte(theSlave | 0x01); + /* + * wait for ack + */ + if(!i2c_getack()) + error = 1; + /* + * fetch register + */ + b = i2c_inbyte(); + /* + * last received byte needs to be nacked + * instead of acked + */ + i2c_sendnack(); + /* + * end sequence + */ + i2c_stop(); + /* + * enable interrupt again + */ + local_irq_restore(flags); + + } while(error && cntr--); + + return b; +} + +static int +i2c_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int +i2c_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +/* Main device API. ioctl's to write or read to/from i2c registers. + */ + +static int +i2c_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) { + return -EINVAL; + } + + switch (_IOC_NR(cmd)) { + case I2C_WRITEREG: + /* write to an i2c slave */ + D(printk("i2cw %d %d %d\n", + I2C_ARGSLAVE(arg), + I2C_ARGREG(arg), + I2C_ARGVALUE(arg))); + + return i2c_writereg(I2C_ARGSLAVE(arg), + I2C_ARGREG(arg), + I2C_ARGVALUE(arg)); + case I2C_READREG: + { + unsigned char val; + /* read from an i2c slave */ + D(printk("i2cr %d %d ", + I2C_ARGSLAVE(arg), + I2C_ARGREG(arg))); + val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg)); + D(printk("= %d\n", val)); + return val; + } + default: + return -EINVAL; + + } + + return 0; +} + +static struct file_operations i2c_fops = { + owner: THIS_MODULE, + ioctl: i2c_ioctl, + open: i2c_open, + release: i2c_release, +}; + +int __init +i2c_init(void) +{ + int res; + + /* Setup and enable the Port B I2C interface */ + + crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT); + crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT); + + /* register char device */ + + res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); + if(res < 0) { + printk(KERN_ERR "i2c: couldn't get a major number.\n"); + return res; + } + + printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); + + return 0; +} + +/* this makes sure that i2c_init is called during boot */ + +module_init(i2c_init); + +/****************** END OF FILE i2c.c ********************************/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/i2c.h 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,15 @@ + +#include + +/* High level I2C actions */ +int __init i2c_init(void); +int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue); +unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg); + +/* Low level I2C */ +void i2c_start(void); +void i2c_stop(void); +void i2c_outbyte(unsigned char x); +unsigned char i2c_inbyte(void); +int i2c_getack(void); +void i2c_sendack(void); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/iop_fw_load.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,219 @@ +/* $Id: iop_fw_load.c,v 1.4 2005/04/07 09:27:46 larsv Exp $ + * + * Firmware loader for ETRAX FS IO-Processor + * + * Copyright (C) 2004 Axis Communications AB + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define IOP_TIMEOUT 100 + +static struct device iop_spu_device[2] = { + { .bus_id = "iop-spu0", }, + { .bus_id = "iop-spu1", }, +}; + +static struct device iop_mpu_device = { + .bus_id = "iop-mpu", +}; + +static int wait_mpu_idle(void) +{ + reg_iop_mpu_r_stat mpu_stat; + unsigned int timeout = IOP_TIMEOUT; + + do { + mpu_stat = REG_RD(iop_mpu, regi_iop_mpu, r_stat); + } while (mpu_stat.instr_reg_busy == regk_iop_mpu_yes && --timeout > 0); + if (timeout == 0) { + printk(KERN_ERR "Timeout waiting for MPU to be idle\n"); + return -EBUSY; + } + return 0; +} + +int iop_fw_load_spu(const unsigned char *fw_name, unsigned int spu_inst) +{ + reg_iop_sw_cpu_rw_mc_ctrl mc_ctrl = { + .wr_spu0_mem = regk_iop_sw_cpu_no, + .wr_spu1_mem = regk_iop_sw_cpu_no, + .size = 4, + .cmd = regk_iop_sw_cpu_reg_copy, + .keep_owner = regk_iop_sw_cpu_yes + }; + reg_iop_spu_rw_ctrl spu_ctrl = { + .en = regk_iop_spu_no, + .fsm = regk_iop_spu_no, + }; + reg_iop_sw_cpu_r_mc_stat mc_stat; + const struct firmware *fw_entry; + u32 *data; + unsigned int timeout; + int retval, i; + + if (spu_inst > 1) + return -ENODEV; + + /* get firmware */ + retval = request_firmware(&fw_entry, + fw_name, + &iop_spu_device[spu_inst]); + if (retval != 0) + { + printk(KERN_ERR + "iop_load_spu: Failed to load firmware \"%s\"\n", + fw_name); + return retval; + } + data = (u32 *) fw_entry->data; + + /* acquire ownership of memory controller */ + switch (spu_inst) { + case 0: + mc_ctrl.wr_spu0_mem = regk_iop_sw_cpu_yes; + REG_WR(iop_spu, regi_iop_spu0, rw_ctrl, spu_ctrl); + break; + case 1: + mc_ctrl.wr_spu1_mem = regk_iop_sw_cpu_yes; + REG_WR(iop_spu, regi_iop_spu1, rw_ctrl, spu_ctrl); + break; + } + timeout = IOP_TIMEOUT; + do { + REG_WR(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_ctrl, mc_ctrl); + mc_stat = REG_RD(iop_sw_cpu, regi_iop_sw_cpu, r_mc_stat); + } while (mc_stat.owned_by_cpu == regk_iop_sw_cpu_no && --timeout > 0); + if (timeout == 0) { + printk(KERN_ERR "Timeout waiting to acquire MC\n"); + retval = -EBUSY; + goto out; + } + + /* write to SPU memory */ + for (i = 0; i < (fw_entry->size/4); i++) { + switch (spu_inst) { + case 0: + REG_WR_INT(iop_spu, regi_iop_spu0, rw_seq_pc, (i*4)); + break; + case 1: + REG_WR_INT(iop_spu, regi_iop_spu1, rw_seq_pc, (i*4)); + break; + } + REG_WR_INT(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_data, *data); + data++; + } + + /* release ownership of memory controller */ + (void) REG_RD(iop_sw_cpu, regi_iop_sw_cpu, rs_mc_data); + + out: + release_firmware(fw_entry); + return retval; +} + +int iop_fw_load_mpu(unsigned char *fw_name) +{ + const unsigned int start_addr = 0; + reg_iop_mpu_rw_ctrl mpu_ctrl; + const struct firmware *fw_entry; + u32 *data; + int retval, i; + + /* get firmware */ + retval = request_firmware(&fw_entry, fw_name, &iop_mpu_device); + if (retval != 0) + { + printk(KERN_ERR + "iop_load_spu: Failed to load firmware \"%s\"\n", + fw_name); + return retval; + } + data = (u32 *) fw_entry->data; + + /* disable MPU */ + mpu_ctrl.en = regk_iop_mpu_no; + REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl); + /* put start address in R0 */ + REG_WR_VECT(iop_mpu, regi_iop_mpu, rw_r, 0, start_addr); + /* write to memory by executing 'SWX i, 4, R0' for each word */ + if ((retval = wait_mpu_idle()) != 0) + goto out; + REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_SWX_IIR_INSTR(0, 4, 0)); + for (i = 0; i < (fw_entry->size / 4); i++) { + REG_WR_INT(iop_mpu, regi_iop_mpu, rw_immediate, *data); + if ((retval = wait_mpu_idle()) != 0) + goto out; + data++; + } + + out: + release_firmware(fw_entry); + return retval; +} + +int iop_start_mpu(unsigned int start_addr) +{ + reg_iop_mpu_rw_ctrl mpu_ctrl = { .en = regk_iop_mpu_yes }; + int retval; + + /* disable MPU */ + if ((retval = wait_mpu_idle()) != 0) + goto out; + REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_HALT()); + if ((retval = wait_mpu_idle()) != 0) + goto out; + /* set PC and wait for it to bite */ + if ((retval = wait_mpu_idle()) != 0) + goto out; + REG_WR_INT(iop_mpu, regi_iop_mpu, rw_instr, MPU_BA_I(start_addr)); + if ((retval = wait_mpu_idle()) != 0) + goto out; + /* make sure the MPU starts executing with interrupts disabled */ + REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_DI()); + if ((retval = wait_mpu_idle()) != 0) + goto out; + /* enable MPU */ + REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl); + out: + return retval; +} + +static int __init iop_fw_load_init(void) +{ + device_initialize(&iop_spu_device[0]); + kobject_set_name(&iop_spu_device[0].kobj, "iop-spu0"); + kobject_add(&iop_spu_device[0].kobj); + device_initialize(&iop_spu_device[1]); + kobject_set_name(&iop_spu_device[1].kobj, "iop-spu1"); + kobject_add(&iop_spu_device[1].kobj); + device_initialize(&iop_mpu_device); + kobject_set_name(&iop_mpu_device.kobj, "iop-mpu"); + kobject_add(&iop_mpu_device.kobj); + return 0; +} + +static void __exit iop_fw_load_exit(void) +{ +} + +module_init(iop_fw_load_init); +module_exit(iop_fw_load_exit); + +MODULE_DESCRIPTION("ETRAX FS IO-Processor Firmware Loader"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(iop_fw_load_spu); +EXPORT_SYMBOL(iop_fw_load_mpu); +EXPORT_SYMBOL(iop_start_mpu); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/Kconfig 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,625 @@ +config ETRAX_ETHERNET + bool "Ethernet support" + depends on ETRAX_ARCH_V32 + select NET_ETHERNET + help + This option enables the ETRAX FS built-in 10/100Mbit Ethernet + controller. + +config ETRAX_ETHERNET_HW_CSUM + bool "Hardware accelerated ethernet checksum and scatter/gather" + depends on ETRAX_ETHERNET + depends on ETRAX_STREAMCOPROC + default y + help + Hardware acceleration of checksumming and scatter/gather + +config ETRAX_ETHERNET_IFACE0 + depends on ETRAX_ETHERNET + bool "Enable network interface 0" + +config ETRAX_ETHERNET_IFACE1 + depends on ETRAX_ETHERNET + bool "Enable network interface 1 (uses DMA6 and DMA7)" + +choice + prompt "Network LED behavior" + depends on ETRAX_ETHERNET + default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + +config ETRAX_NETWORK_LED_ON_WHEN_LINK + bool "LED_on_when_link" + help + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. + + Selecting LED_on_when_activity will light the LED only when + there is activity. + + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. + +config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + bool "LED_on_when_activity" + help + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. + + Selecting LED_on_when_activity will light the LED only when + there is activity. + + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. + +endchoice + +config ETRAXFS_SERIAL + bool "Serial-port support" + depends on ETRAX_ARCH_V32 + help + Enables the ETRAX FS serial driver for ser0 (ttyS0) + You probably want this enabled. + +config ETRAX_SERIAL_PORT0 + bool "Serial port 0 enabled" + depends on ETRAXFS_SERIAL + help + Enables the ETRAX FS serial driver for ser0 (ttyS0) + Normally you want this on. You can control what DMA channels to use + if you do not need DMA to something else. + ser0 can use dma4 or dma6 for output and dma5 or dma7 for input. + +choice + prompt "Ser0 DMA in channel " + depends on ETRAX_SERIAL_PORT0 + default ETRAX_SERIAL_PORT0_NO_DMA_IN + help + What DMA channel to use for ser0. + + +config ETRAX_SERIAL_PORT0_NO_DMA_IN + bool "Ser0 uses no DMA for input" + help + Do not use DMA for ser0 input. + +config ETRAX_SERIAL_PORT0_DMA7_IN + bool "Ser0 uses DMA7 for input" + depends on ETRAX_SERIAL_PORT0 + help + Enables the DMA7 input channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser0 DMA out channel" + depends on ETRAX_SERIAL_PORT0 + default ETRAX_SERIAL_PORT0_NO_DMA_OUT + +config ETRAX_SERIAL_PORT0_NO_DMA_OUT + bool "Ser0 uses no DMA for output" + help + Do not use DMA for ser0 output. + +config ETRAX_SERIAL_PORT0_DMA6_OUT + bool "Ser0 uses DMA6 for output" + depends on ETRAX_SERIAL_PORT0 + help + Enables the DMA6 output channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +config ETRAX_SER0_DTR_BIT + string "Ser 0 DTR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT0 + +config ETRAX_SER0_RI_BIT + string "Ser 0 RI bit (empty = not used)" + depends on ETRAX_SERIAL_PORT0 + +config ETRAX_SER0_DSR_BIT + string "Ser 0 DSR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT0 + +config ETRAX_SER0_CD_BIT + string "Ser 0 CD bit (empty = not used)" + depends on ETRAX_SERIAL_PORT0 + +config ETRAX_SERIAL_PORT1 + bool "Serial port 1 enabled" + depends on ETRAXFS_SERIAL + help + Enables the ETRAX FS serial driver for ser1 (ttyS1). + +choice + prompt "Ser1 DMA in channel " + depends on ETRAX_SERIAL_PORT1 + default ETRAX_SERIAL_PORT1_NO_DMA_IN + help + What DMA channel to use for ser1. + + +config ETRAX_SERIAL_PORT1_NO_DMA_IN + bool "Ser1 uses no DMA for input" + help + Do not use DMA for ser1 input. + +config ETRAX_SERIAL_PORT1_DMA5_IN + bool "Ser1 uses DMA5 for input" + depends on ETRAX_SERIAL_PORT1 + help + Enables the DMA5 input channel for ser1 (ttyS1). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want this on, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser1 DMA out channel " + depends on ETRAX_SERIAL_PORT1 + default ETRAX_SERIAL_PORT1_NO_DMA_OUT + help + What DMA channel to use for ser1. + +config ETRAX_SERIAL_PORT1_NO_DMA_OUT + bool "Ser1 uses no DMA for output" + help + Do not use DMA for ser1 output. + +config ETRAX_SERIAL_PORT1_DMA4_OUT + bool "Ser1 uses DMA4 for output" + depends on ETRAX_SERIAL_PORT1 + help + Enables the DMA4 output channel for ser1 (ttyS1). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want this on, unless you use the DMA channel for + something else. + +endchoice + +config ETRAX_SER1_DTR_BIT + string "Ser 1 DTR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT1 + +config ETRAX_SER1_RI_BIT + string "Ser 1 RI bit (empty = not used)" + depends on ETRAX_SERIAL_PORT1 + +config ETRAX_SER1_DSR_BIT + string "Ser 1 DSR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT1 + +config ETRAX_SER1_CD_BIT + string "Ser 1 CD bit (empty = not used)" + depends on ETRAX_SERIAL_PORT1 + +config ETRAX_SERIAL_PORT2 + bool "Serial port 2 enabled" + depends on ETRAXFS_SERIAL + help + Enables the ETRAX FS serial driver for ser2 (ttyS2). + +choice + prompt "Ser2 DMA in channel " + depends on ETRAX_SERIAL_PORT2 + default ETRAX_SERIAL_PORT2_NO_DMA_IN + help + What DMA channel to use for ser2. + + +config ETRAX_SERIAL_PORT2_NO_DMA_IN + bool "Ser2 uses no DMA for input" + help + Do not use DMA for ser2 input. + +config ETRAX_SERIAL_PORT2_DMA3_IN + bool "Ser2 uses DMA3 for input" + depends on ETRAX_SERIAL_PORT2 + help + Enables the DMA3 input channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser2 DMA out channel" + depends on ETRAX_SERIAL_PORT2 + default ETRAX_SERIAL_PORT2_NO_DMA_OUT + +config ETRAX_SERIAL_PORT2_NO_DMA_OUT + bool "Ser2 uses no DMA for output" + help + Do not use DMA for ser2 output. + +config ETRAX_SERIAL_PORT2_DMA2_OUT + bool "Ser2 uses DMA2 for output" + depends on ETRAX_SERIAL_PORT2 + help + Enables the DMA2 output channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +config ETRAX_SER2_DTR_BIT + string "Ser 2 DTR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT2 + +config ETRAX_SER2_RI_BIT + string "Ser 2 RI bit (empty = not used)" + depends on ETRAX_SERIAL_PORT2 + +config ETRAX_SER2_DSR_BIT + string "Ser 2 DSR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT2 + +config ETRAX_SER2_CD_BIT + string "Ser 2 CD bit (empty = not used)" + depends on ETRAX_SERIAL_PORT2 + +config ETRAX_SERIAL_PORT3 + bool "Serial port 3 enabled" + depends on ETRAXFS_SERIAL + help + Enables the ETRAX FS serial driver for ser3 (ttyS3). + +choice + prompt "Ser3 DMA in channel " + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SERIAL_PORT3_NO_DMA_IN + help + What DMA channel to use for ser3. + + +config ETRAX_SERIAL_PORT3_NO_DMA_IN + bool "Ser3 uses no DMA for input" + help + Do not use DMA for ser3 input. + +config ETRAX_SERIAL_PORT3_DMA9_IN + bool "Ser3 uses DMA9 for input" + depends on ETRAX_SERIAL_PORT3 + help + Enables the DMA9 input channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser3 DMA out channel" + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SERIAL_PORT3_NO_DMA_OUT + +config ETRAX_SERIAL_PORT3_NO_DMA_OUT + bool "Ser3 uses no DMA for output" + help + Do not use DMA for ser3 output. + +config ETRAX_SERIAL_PORT3_DMA8_OUT + bool "Ser3 uses DMA8 for output" + depends on ETRAX_SERIAL_PORT3 + help + Enables the DMA8 output channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +config ETRAX_SER3_DTR_BIT + string "Ser 3 DTR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SER3_RI_BIT + string "Ser 3 RI bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SER3_DSR_BIT + string "Ser 3 DSR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SER3_CD_BIT + string "Ser 3 CD bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_RS485 + bool "RS-485 support" + depends on ETRAX_SERIAL + help + Enables support for RS-485 serial communication. For a primer on + RS-485, see . + +config ETRAX_RS485_DISABLE_RECEIVER + bool "Disable serial receiver" + depends on ETRAX_RS485 + help + It is necessary to disable the serial receiver to avoid serial + loopback. Not all products are able to do this in software only. + Axis 2400/2401 must disable receiver. + +config ETRAX_AXISFLASHMAP + bool "Axis flash-map support" + depends on ETRAX_ARCH_V32 + select MTD + select MTD_CFI + select MTD_CFI_AMDSTD + select MTD_OBSOLETE_CHIPS + select MTD_AMDSTD + select MTD_CHAR + select MTD_BLOCK + select MTD_PARTITIONS + select MTD_CONCAT + select MTD_COMPLEX_MAPPINGS + help + This option enables MTD mapping of flash devices. Needed to use + flash memories. If unsure, say Y. + +config ETRAX_SYNCHRONOUS_SERIAL + bool "Synchronous serial-port support" + depends on ETRAX_ARCH_V32 + help + Enables the ETRAX FS synchronous serial driver. + +config ETRAX_SYNCHRONOUS_SERIAL_PORT0 + bool "Synchronous serial port 0 enabled" + depends on ETRAX_SYNCHRONOUS_SERIAL + help + Enabled synchronous serial port 0. + +config ETRAX_SYNCHRONOUS_SERIAL0_DMA + bool "Enable DMA on synchronous serial port 0." + depends on ETRAX_SYNCHRONOUS_SERIAL_PORT0 + help + A synchronous serial port can run in manual or DMA mode. + Selecting this option will make it run in DMA mode. + +config ETRAX_SYNCHRONOUS_SERIAL_PORT1 + bool "Synchronous serial port 1 enabled" + depends on ETRAX_SYNCHRONOUS_SERIAL + help + Enabled synchronous serial port 1. + +config ETRAX_SYNCHRONOUS_SERIAL1_DMA + bool "Enable DMA on synchronous serial port 1." + depends on ETRAX_SYNCHRONOUS_SERIAL_PORT1 + help + A synchronous serial port can run in manual or DMA mode. + Selecting this option will make it run in DMA mode. + +config ETRAX_PTABLE_SECTOR + int "Byte-offset of partition table sector" + depends on ETRAX_AXISFLASHMAP + default "65536" + help + Byte-offset of the partition table in the first flash chip. + The default value is 64kB and should not be changed unless + you know exactly what you are doing. The only valid reason + for changing this is when the flash block size is bigger + than 64kB (e.g. when using two parallel 16 bit flashes). + +config ETRAX_NANDFLASH + bool "NAND flash support" + depends on ETRAX_ARCH_V32 + select MTD_NAND + select MTD_NAND_IDS + help + This option enables MTD mapping of NAND flash devices. Needed to use + NAND flash memories. If unsure, say Y. + +config ETRAX_I2C + bool "I2C driver" + depends on ETRAX_ARCH_V32 + help + This option enabled the I2C driver used by e.g. the RTC driver. + +config ETRAX_I2C_DATA_PORT + string "I2C data pin" + depends on ETRAX_I2C + help + The pin to use for I2C data. + +config ETRAX_I2C_CLK_PORT + string "I2C clock pin" + depends on ETRAX_I2C + help + The pin to use for I2C clock. + +config ETRAX_RTC + bool "Real Time Clock support" + depends on ETRAX_ARCH_V32 + help + Enabled RTC support. + +choice + prompt "RTC chip" + depends on ETRAX_RTC + default ETRAX_PCF8563 + +config ETRAX_PCF8563 + bool "PCF8563" + help + Philips PCF8563 RTC + +endchoice + +config ETRAX_GPIO + bool "GPIO support" + depends on ETRAX_ARCH_V32 + ---help--- + Enables the ETRAX general port device (major 120, minors 0-4). + You can use this driver to access the general port bits. It supports + these ioctl's: + #include + fd = open("/dev/gpioa", O_RDWR); // or /dev/gpiob + ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), bits_to_set); + ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), bits_to_clear); + err = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READ_INBITS), &val); + Remember that you need to setup the port directions appropriately in + the General configuration. + +config ETRAX_PA_BUTTON_BITMASK + hex "PA-buttons bitmask" + depends on ETRAX_GPIO + default "0x02" + help + This is a bitmask (8 bits) with information about what bits on PA + that are used for buttons. + Most products has a so called TEST button on PA1, if that is true + use 0x02 here. + Use 00 if there are no buttons on PA. + If the bitmask is <> 00 a button driver will be included in the gpio + driver. ETRAX general I/O support must be enabled. + +config ETRAX_PA_CHANGEABLE_DIR + hex "PA user changeable dir mask" + depends on ETRAX_GPIO + default "0x00" + help + This is a bitmask (8 bits) with information of what bits in PA that a + user can change direction on using ioctl's. + Bit set = changeable. + You probably want 0x00 here, but it depends on your hardware. + +config ETRAX_PA_CHANGEABLE_BITS + hex "PA user changeable bits mask" + depends on ETRAX_GPIO + default "0x00" + help + This is a bitmask (8 bits) with information of what bits in PA + that a user can change the value on using ioctl's. + Bit set = changeable. + +config ETRAX_PB_CHANGEABLE_DIR + hex "PB user changeable dir mask" + depends on ETRAX_GPIO + default "0x00000" + help + This is a bitmask (18 bits) with information of what bits in PB + that a user can change direction on using ioctl's. + Bit set = changeable. + You probably want 0x00000 here, but it depends on your hardware. + +config ETRAX_PB_CHANGEABLE_BITS + hex "PB user changeable bits mask" + depends on ETRAX_GPIO + default "0x00000" + help + This is a bitmask (18 bits) with information of what bits in PB + that a user can change the value on using ioctl's. + Bit set = changeable. + +config ETRAX_PC_CHANGEABLE_DIR + hex "PC user changeable dir mask" + depends on ETRAX_GPIO + default "0x00000" + help + This is a bitmask (18 bits) with information of what bits in PC + that a user can change direction on using ioctl's. + Bit set = changeable. + You probably want 0x00000 here, but it depends on your hardware. + +config ETRAX_PC_CHANGEABLE_BITS + hex "PC user changeable bits mask" + depends on ETRAX_GPIO + default "0x00000" + help + This is a bitmask (18 bits) with information of what bits in PC + that a user can change the value on using ioctl's. + Bit set = changeable. + +config ETRAX_PD_CHANGEABLE_DIR + hex "PD user changeable dir mask" + depends on ETRAX_GPIO + default "0x00000" + help + This is a bitmask (18 bits) with information of what bits in PD + that a user can change direction on using ioctl's. + Bit set = changeable. + You probably want 0x00000 here, but it depends on your hardware. + +config ETRAX_PD_CHANGEABLE_BITS + hex "PD user changeable bits mask" + depends on ETRAX_GPIO + default "0x00000" + help + This is a bitmask (18 bits) with information of what bits in PD + that a user can change the value on using ioctl's. + Bit set = changeable. + +config ETRAX_PE_CHANGEABLE_DIR + hex "PE user changeable dir mask" + depends on ETRAX_GPIO + default "0x00000" + help + This is a bitmask (18 bits) with information of what bits in PE + that a user can change direction on using ioctl's. + Bit set = changeable. + You probably want 0x00000 here, but it depends on your hardware. + +config ETRAX_PE_CHANGEABLE_BITS + hex "PE user changeable bits mask" + depends on ETRAX_GPIO + default "0x00000" + help + This is a bitmask (18 bits) with information of what bits in PE + that a user can change the value on using ioctl's. + Bit set = changeable. + +config ETRAX_IDE + bool "ATA/IDE support" + depends on ETRAX_ARCH_V32 + select IDE + select BLK_DEV_IDE + select BLK_DEV_IDEDISK + select BLK_DEV_IDECD + select BLK_DEV_IDEDMA + help + Enables the ETRAX IDE driver. + +config ETRAX_CARDBUS + bool "Cardbus support" + depends on ETRAX_ARCH_V32 + select PCCARD + select CARDBUS + select HOTPLUG + select PCCARD_NONSTATIC + help + Enabled the ETRAX Carbus driver. + +config PCI + bool + depends on ETRAX_CARDBUS + default y + +config ETRAX_IOP_FW_LOAD + tristate "IO-processor hotplug firmware loading support" + depends on ETRAX_ARCH_V32 + select FW_LOADER + help + Enables IO-processor hotplug firmware loading support. + +config ETRAX_STREAMCOPROC + tristate "Stream co-processor driver enabled" + depends on ETRAX_ARCH_V32 + help + This option enables a driver for the stream co-processor + for cryptographic operations. --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/Makefile 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,13 @@ +# +# Makefile for Etrax-specific drivers +# + +obj-$(CONFIG_ETRAX_STREAMCOPROC) += cryptocop.o +obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o +obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o +obj-$(CONFIG_ETRAX_IOP_FW_LOAD) += iop_fw_load.o +obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o +obj-$(CONFIG_ETRAX_I2C) += i2c.o +obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o +obj-$(CONFIG_PCI) += pci/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/nandflash.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,157 @@ +/* + * arch/cris/arch-v32/drivers/nandflash.c + * + * Copyright (c) 2004 + * + * Derived from drivers/mtd/nand/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * $Id: nandflash.c,v 1.3 2005/06/01 10:57:12 starvik Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CE_BIT 4 +#define CLE_BIT 5 +#define ALE_BIT 6 +#define BY_BIT 7 + +static struct mtd_info *crisv32_mtd = NULL; +/* + * hardware specific access to control-lines +*/ +static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd) +{ + unsigned long flags; + reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout); + + local_irq_save(flags); + switch(cmd){ + case NAND_CTL_SETCLE: + dout.data |= (1<> BY_BIT); +} + +/* + * Main initialization routine + */ +struct mtd_info* __init crisv32_nand_flash_probe (void) +{ + void __iomem *read_cs; + void __iomem *write_cs; + + reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, rw_grp3_cfg); + reg_gio_rw_pa_oe pa_oe = REG_RD(gio, regi_gio, rw_pa_oe); + struct nand_chip *this; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + crisv32_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!crisv32_mtd) { + printk ("Unable to allocate CRISv32 NAND MTD device structure.\n"); + err = -ENOMEM; + return NULL; + } + + read_cs = ioremap(MEM_CSP0_START | MEM_NON_CACHEABLE, 8192); + write_cs = ioremap(MEM_CSP1_START | MEM_NON_CACHEABLE, 8192); + + if (!read_cs || !write_cs) { + printk("CRISv32 NAND ioremap failed\n"); + err = -EIO; + goto out_mtd; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&crisv32_mtd[1]); + + pa_oe.oe |= 1 << CE_BIT; + pa_oe.oe |= 1 << ALE_BIT; + pa_oe.oe |= 1 << CLE_BIT; + pa_oe.oe &= ~ (1 << BY_BIT); + REG_WR(gio, regi_gio, rw_pa_oe, pa_oe); + + bif_cfg.gated_csp0 = regk_bif_core_rd; + bif_cfg.gated_csp1 = regk_bif_core_wr; + REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg); + + /* Initialize structures */ + memset((char *) crisv32_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + crisv32_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = read_cs; + this->IO_ADDR_W = write_cs; + this->hwcontrol = crisv32_hwcontrol; + this->dev_ready = crisv32_device_ready; + /* 20 us command delay time */ + this->chip_delay = 20; + this->eccmode = NAND_ECC_SOFT; + + /* Enable the following for a flash based bad block table */ + this->options = NAND_USE_FLASH_BBT; + + /* Scan to find existance of the device */ + if (nand_scan (crisv32_mtd, 1)) { + err = -ENXIO; + goto out_ior; + } + + return crisv32_mtd; + +out_ior: + iounmap((void *)read_cs); + iounmap((void *)write_cs); +out_mtd: + kfree (crisv32_mtd); + return NULL; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/pcf8563.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,341 @@ +/* + * PCF8563 RTC + * + * From Phillips' datasheet: + * + * The PCF8563 is a CMOS real-time clock/calendar optimized for low power + * consumption. A programmable clock output, interupt output and voltage + * low detector are also provided. All address and data are transferred + * serially via two-line bidirectional I2C-bus. Maximum bus speed is + * 400 kbits/s. The built-in word address register is incremented + * automatically after each written or read byte. + * + * Copyright (c) 2002-2003, Axis Communications AB + * All rights reserved. + * + * Author: Tobias Anderberg . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "i2c.h" + +#define PCF8563_MAJOR 121 /* Local major number. */ +#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ +#define PCF8563_NAME "PCF8563" +#define DRIVER_VERSION "$Revision: 1.1 $" + +/* Two simple wrapper macros, saves a few keystrokes. */ +#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) +#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) + +static const unsigned char days_in_month[] = + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +int pcf8563_open(struct inode *, struct file *); +int pcf8563_release(struct inode *, struct file *); + +static struct file_operations pcf8563_fops = { + owner: THIS_MODULE, + ioctl: pcf8563_ioctl, + open: pcf8563_open, + release: pcf8563_release, +}; + +unsigned char +pcf8563_readreg(int reg) +{ + unsigned char res = rtc_read(reg); + + /* The PCF8563 does not return 0 for unimplemented bits */ + switch (reg) { + case RTC_SECONDS: + case RTC_MINUTES: + res &= 0x7F; + break; + case RTC_HOURS: + case RTC_DAY_OF_MONTH: + res &= 0x3F; + break; + case RTC_WEEKDAY: + res &= 0x07; + break; + case RTC_MONTH: + res &= 0x1F; + break; + case RTC_CONTROL1: + res &= 0xA8; + break; + case RTC_CONTROL2: + res &= 0x1F; + break; + case RTC_CLOCKOUT_FREQ: + case RTC_TIMER_CONTROL: + res &= 0x83; + break; + } + return res; +} + +void +pcf8563_writereg(int reg, unsigned char val) +{ +#ifdef CONFIG_ETRAX_RTC_READONLY + if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) + return; +#endif + + rtc_write(reg, val); +} + +void +get_rtc_time(struct rtc_time *tm) +{ + tm->tm_sec = rtc_read(RTC_SECONDS); + tm->tm_min = rtc_read(RTC_MINUTES); + tm->tm_hour = rtc_read(RTC_HOURS); + tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); + tm->tm_wday = rtc_read(RTC_WEEKDAY); + tm->tm_mon = rtc_read(RTC_MONTH); + tm->tm_year = rtc_read(RTC_YEAR); + + if (tm->tm_sec & 0x80) + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + + tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_sec &= 0x7F; + tm->tm_min &= 0x7F; + tm->tm_hour &= 0x3F; + tm->tm_mday &= 0x3F; + tm->tm_wday &= 0x07; /* Not coded in BCD. */ + tm->tm_mon &= 0x1F; + + BCD_TO_BIN(tm->tm_sec); + BCD_TO_BIN(tm->tm_min); + BCD_TO_BIN(tm->tm_hour); + BCD_TO_BIN(tm->tm_mday); + BCD_TO_BIN(tm->tm_mon); + tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */ +} + +int __init +pcf8563_init(void) +{ + /* Initiate the i2c protocol. */ + i2c_init(); + + /* + * First of all we need to reset the chip. This is done by + * clearing control1, control2 and clk freq and resetting + * all alarms. + */ + if (rtc_write(RTC_CONTROL1, 0x00) < 0) + goto err; + + if (rtc_write(RTC_CONTROL2, 0x00) < 0) + goto err; + + if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0) + goto err; + + if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0) + goto err; + + /* Reset the alarms. */ + if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0) + goto err; + + if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0) + goto err; + + if (rtc_write(RTC_DAY_ALARM, 0x80) < 0) + goto err; + + if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) + goto err; + + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { + printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n", + PCF8563_NAME, PCF8563_MAJOR); + return -1; + } + + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + + /* Check for low voltage, and warn about it.. */ + if (rtc_read(RTC_SECONDS) & 0x80) + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + + return 0; + +err: + printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); + return -1; +} + +void __exit +pcf8563_exit(void) +{ + if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) { + printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME); + } +} + +/* + * ioctl calls for this driver. Why return -ENOTTY upon error? Because + * POSIX says so! + */ +int +pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + /* Some sanity checks. */ + if (_IOC_TYPE(cmd) != RTC_MAGIC) + return -ENOTTY; + + if (_IOC_NR(cmd) > RTC_MAX_IOCTL) + return -ENOTTY; + + switch (cmd) { + case RTC_RD_TIME: + { + struct rtc_time tm; + + memset(&tm, 0, sizeof (struct rtc_time)); + get_rtc_time(&tm); + + if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) { + return -EFAULT; + } + + return 0; + } + + case RTC_SET_TIME: + { +#ifdef CONFIG_ETRAX_RTC_READONLY + return -EPERM; +#else + int leap; + int year; + int century; + struct rtc_time tm; + + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) + return -EFAULT; + + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + /* + * Check if tm.tm_year is a leap year. A year is a leap + * year if it is divisible by 4 but not 100, except + * that years divisible by 400 _are_ leap years. + */ + year = tm.tm_year; + leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + + /* Perform some sanity checks. */ + if ((tm.tm_year < 1970) || + (tm.tm_mon > 12) || + (tm.tm_mday == 0) || + (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || + (tm.tm_wday >= 7) || + (tm.tm_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) + return -EINVAL; + + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; + + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + return 0; +#endif /* !CONFIG_ETRAX_RTC_READONLY */ + } + + case RTC_VLOW_RD: + { + int vl_bit = 0; + + if (rtc_read(RTC_SECONDS) & 0x80) { + vl_bit = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); + } + if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) + return -EFAULT; + + return 0; + } + + case RTC_VLOW_SET: + { + /* Clear the VL bit in the seconds register */ + int ret = rtc_read(RTC_SECONDS); + + rtc_write(RTC_SECONDS, (ret & 0x7F)); + + return 0; + } + + default: + return -ENOTTY; + } + + return 0; +} + +int +pcf8563_open(struct inode *inode, struct file *filp) +{ + MOD_INC_USE_COUNT; + return 0; +} + +int +pcf8563_release(struct inode *inode, struct file *filp) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +module_init(pcf8563_init); +module_exit(pcf8563_exit); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/pci/bios.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,131 @@ +#include +#include +#include + +void __devinit pcibios_fixup_bus(struct pci_bus *b) +{ +} + +char * __devinit pcibios_setup(char *str) +{ + return NULL; +} + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n", pci_name(dev), lat); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + unsigned long prot; + + /* Leave vm_pgoff as-is, the PCI space address is the physical + * address on this platform. + */ + vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO); + + prot = pgprot_val(vma->vm_page_prot); + vma->vm_page_prot = __pgprot(prot); + + /* Write-combine setting is ignored, it is changed via the mtrr + * interfaces on this platform. + */ + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +void +pcibios_align_resource(void *data, struct resource *res, + unsigned long size, unsigned long align) +{ + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + +int pcibios_enable_resources(struct pci_dev *dev, int mask) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for(idx=0; idx<6; idx++) { + /* Only set up the requested stuff */ + if (!(mask & (1<resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev)); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +int pcibios_enable_irq(struct pci_dev *dev) +{ + dev->irq = EXT_INTR_VECT; + return 0; +} + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + int err; + + if ((err = pcibios_enable_resources(dev, mask)) < 0) + return err; + + return pcibios_enable_irq(dev); +} + +int pcibios_assign_resources(void) +{ + struct pci_dev *dev = NULL; + int idx; + struct resource *r; + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + int class = dev->class >> 8; + + /* Don't touch classless devices and host bridges */ + if (!class || class == PCI_CLASS_BRIDGE_HOST) + continue; + + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + + if (!r->start && r->end) + pci_assign_resource(dev, idx); + } + } + return 0; +} + +EXPORT_SYMBOL(pcibios_assign_resources); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/pci/dma.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,149 @@ +/* + * Dynamic DMA mapping support. + * + * On cris there is no hardware dynamic DMA address translation, + * so consistent alloc/free are merely page allocation/freeing. + * The rest of the dynamic DMA mapping interface is implemented + * in asm/pci.h. + * + * Borrowed from i386. + */ + +#include +#include +#include +#include +#include + +struct dma_coherent_mem { + void *virt_base; + u32 device_base; + int size; + int flags; + unsigned long *bitmap; +}; + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, unsigned int __nocast gfp) +{ + void *ret; + struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; + int order = get_order(size); + /* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + + if (mem) { + int page = bitmap_find_free_region(mem->bitmap, mem->size, + order); + if (page >= 0) { + *dma_handle = mem->device_base + (page << PAGE_SHIFT); + ret = mem->virt_base + (page << PAGE_SHIFT); + memset(ret, 0, size); + return ret; + } + if (mem->flags & DMA_MEMORY_EXCLUSIVE) + return NULL; + } + + if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) + gfp |= GFP_DMA; + + ret = (void *)__get_free_pages(gfp, order); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_phys(ret); + } + return ret; +} + +void dma_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; + int order = get_order(size); + + if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { + int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; + + bitmap_release_region(mem->bitmap, page, order); + } else + free_pages((unsigned long)vaddr, order); +} + +int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + void __iomem *mem_base; + int pages = size >> PAGE_SHIFT; + int bitmap_size = (pages + 31)/32; + + if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) + goto out; + if (!size) + goto out; + if (dev->dma_mem) + goto out; + + /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ + + mem_base = ioremap(bus_addr, size); + if (!mem_base) + goto out; + + dev->dma_mem = kmalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); + if (!dev->dma_mem) + goto out; + memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem)); + dev->dma_mem->bitmap = kmalloc(bitmap_size, GFP_KERNEL); + if (!dev->dma_mem->bitmap) + goto free1_out; + memset(dev->dma_mem->bitmap, 0, bitmap_size); + + dev->dma_mem->virt_base = mem_base; + dev->dma_mem->device_base = device_addr; + dev->dma_mem->size = pages; + dev->dma_mem->flags = flags; + + if (flags & DMA_MEMORY_MAP) + return DMA_MEMORY_MAP; + + return DMA_MEMORY_IO; + + free1_out: + kfree(dev->dma_mem->bitmap); + out: + return 0; +} +EXPORT_SYMBOL(dma_declare_coherent_memory); + +void dma_release_declared_memory(struct device *dev) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + + if(!mem) + return; + dev->dma_mem = NULL; + iounmap(mem->virt_base); + kfree(mem->bitmap); + kfree(mem); +} +EXPORT_SYMBOL(dma_release_declared_memory); + +void *dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT; + int pos, err; + + if (!mem) + return ERR_PTR(-EINVAL); + + pos = (device_addr - mem->device_base) >> PAGE_SHIFT; + err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages)); + if (err != 0) + return ERR_PTR(err); + return mem->virt_base + (pos << PAGE_SHIFT); +} +EXPORT_SYMBOL(dma_mark_declared_memory_occupied); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/pci/Makefile 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,5 @@ +# +# Makefile for Etrax cardbus driver +# + +obj-$(CONFIG_ETRAX_CARDBUS) += bios.o dma.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/drivers/sync_serial.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,1283 @@ +/* + * Simple synchronous serial port driver for ETRAX FS. + * + * Copyright (c) 2005 Axis Communications AB + * + * Author: Mikael Starvik + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The receiver is a bit tricky beacuse of the continuous stream of data.*/ +/* */ +/* Three DMA descriptors are linked together. Each DMA descriptor is */ +/* responsible for port->bufchunk of a common buffer. */ +/* */ +/* +---------------------------------------------+ */ +/* | +----------+ +----------+ +----------+ | */ +/* +-> | Descr[0] |-->| Descr[1] |-->| Descr[2] |-+ */ +/* +----------+ +----------+ +----------+ */ +/* | | | */ +/* v v v */ +/* +-------------------------------------+ */ +/* | BUFFER | */ +/* +-------------------------------------+ */ +/* |<- data_avail ->| */ +/* readp writep */ +/* */ +/* If the application keeps up the pace readp will be right after writep.*/ +/* If the application can't keep the pace we have to throw away data. */ +/* The idea is that readp should be ready with the data pointed out by */ +/* Descr[i] when the DMA has filled in Descr[i+1]. */ +/* Otherwise we will discard */ +/* the rest of the data pointed out by Descr1 and set readp to the start */ +/* of Descr2 */ + +#define SYNC_SERIAL_MAJOR 125 + +/* IN_BUFFER_SIZE should be a multiple of 6 to make sure that 24 bit */ +/* words can be handled */ +#define IN_BUFFER_SIZE 12288 +#define IN_DESCR_SIZE 256 +#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) +#define OUT_BUFFER_SIZE 4096 + +#define DEFAULT_FRAME_RATE 0 +#define DEFAULT_WORD_RATE 7 + +/* NOTE: Enabling some debug will likely cause overrun or underrun, + * especially if manual mode is use. + */ +#define DEBUG(x) +#define DEBUGREAD(x) +#define DEBUGWRITE(x) +#define DEBUGPOLL(x) +#define DEBUGRXINT(x) +#define DEBUGTXINT(x) + +typedef struct sync_port +{ + reg_scope_instances regi_sser; + reg_scope_instances regi_dmain; + reg_scope_instances regi_dmaout; + + char started; /* 1 if port has been started */ + char port_nbr; /* Port 0 or 1 */ + char busy; /* 1 if port is busy */ + + char enabled; /* 1 if port is enabled */ + char use_dma; /* 1 if port uses dma */ + char tr_running; + + char init_irqs; + int output; + int input; + + volatile unsigned int out_count; /* Remaining bytes for current transfer */ + unsigned char* outp; /* Current position in out_buffer */ + volatile unsigned char* volatile readp; /* Next byte to be read by application */ + volatile unsigned char* volatile writep; /* Next byte to be written by etrax */ + unsigned int in_buffer_size; + unsigned int inbufchunk; + unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32))); + unsigned char in_buffer[IN_BUFFER_SIZE]__attribute__ ((aligned(32))); + unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32))); + struct dma_descr_data* next_rx_desc; + struct dma_descr_data* prev_rx_desc; + int full; + + dma_descr_data in_descr[NUM_IN_DESCR] __attribute__ ((__aligned__(16))); + dma_descr_context in_context __attribute__ ((__aligned__(32))); + dma_descr_data out_descr __attribute__ ((__aligned__(16))); + dma_descr_context out_context __attribute__ ((__aligned__(32))); + wait_queue_head_t out_wait_q; + wait_queue_head_t in_wait_q; + + spinlock_t lock; +} sync_port; + +static int etrax_sync_serial_init(void); +static void initialize_port(int portnbr); +static inline int sync_data_avail(struct sync_port *port); + +static int sync_serial_open(struct inode *, struct file*); +static int sync_serial_release(struct inode*, struct file*); +static unsigned int sync_serial_poll(struct file *filp, poll_table *wait); + +static int sync_serial_ioctl(struct inode*, struct file*, + unsigned int cmd, unsigned long arg); +static ssize_t sync_serial_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t sync_serial_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +#if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA)) +#define SYNC_SER_DMA +#endif + +static void send_word(sync_port* port); +static void start_dma(struct sync_port *port, const char* data, int count); +static void start_dma_in(sync_port* port); +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs); +#endif + +#if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA)) +#define SYNC_SER_MANUAL +#endif +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs); +#endif + +/* The ports */ +static struct sync_port ports[]= +{ + { + .regi_sser = regi_sser0, + .regi_dmaout = regi_dma4, + .regi_dmain = regi_dma5, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + }, + { + .regi_sser = regi_sser1, + .regi_dmaout = regi_dma6, + .regi_dmain = regi_dma7, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + } +}; + +#define NUMBER_OF_PORTS (sizeof(ports)/sizeof(sync_port)) + +static struct file_operations sync_serial_fops = { + .owner = THIS_MODULE, + .write = sync_serial_write, + .read = sync_serial_read, + .poll = sync_serial_poll, + .ioctl = sync_serial_ioctl, + .open = sync_serial_open, + .release = sync_serial_release +}; + +static int __init etrax_sync_serial_init(void) +{ + ports[0].enabled = 0; + ports[1].enabled = 0; + + if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) + { + printk("unable to get major for synchronous serial port\n"); + return -EBUSY; + } + + /* Initialize Ports */ +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + if (crisv32_pinmux_alloc_fixed(pinmux_sser0)) + { + printk("Unable to allocate pins for syncrhronous serial port 0\n"); + return -EIO; + } + ports[0].enabled = 1; + initialize_port(0); +#endif + +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + if (crisv32_pinmux_alloc_fixed(pinmux_sser1)) + { + printk("Unable to allocate pins for syncrhronous serial port 0\n"); + return -EIO; + } + ports[1].enabled = 1; + initialize_port(1); +#endif + + printk("ETRAX FS synchronous serial port driver\n"); + return 0; +} + +static void __init initialize_port(int portnbr) +{ + struct sync_port* port = &ports[portnbr]; + reg_sser_rw_cfg cfg = {0}; + reg_sser_rw_frm_cfg frm_cfg = {0}; + reg_sser_rw_tr_cfg tr_cfg = {0}; + reg_sser_rw_rec_cfg rec_cfg = {0}; + + DEBUG(printk("Init sync serial port %d\n", portnbr)); + + port->port_nbr = portnbr; + port->init_irqs = 1; + + port->outp = port->out_buffer; + port->output = 1; + port->input = 0; + + port->readp = port->flip; + port->writep = port->flip; + port->in_buffer_size = IN_BUFFER_SIZE; + port->inbufchunk = IN_DESCR_SIZE; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1]; + port->prev_rx_desc->eol = 1; + + init_waitqueue_head(&port->out_wait_q); + init_waitqueue_head(&port->in_wait_q); + + spin_lock_init(&port->lock); + + cfg.out_clk_src = regk_sser_intern_clk; + cfg.out_clk_pol = regk_sser_pos; + cfg.clk_od_mode = regk_sser_no; + cfg.clk_dir = regk_sser_out; + cfg.gate_clk = regk_sser_no; + cfg.base_freq = regk_sser_f29_493; + cfg.clk_div = 256; + REG_WR(sser, port->regi_sser, rw_cfg, cfg); + + frm_cfg.wordrate = DEFAULT_WORD_RATE; + frm_cfg.type = regk_sser_edge; + frm_cfg.frame_pin_dir = regk_sser_out; + frm_cfg.frame_pin_use = regk_sser_frm; + frm_cfg.status_pin_dir = regk_sser_in; + frm_cfg.status_pin_use = regk_sser_hold; + frm_cfg.out_on = regk_sser_tr; + frm_cfg.tr_delay = 1; + REG_WR(sser, port->regi_sser, rw_frm_cfg, frm_cfg); + + tr_cfg.urun_stop = regk_sser_no; + tr_cfg.sample_size = 7; + tr_cfg.sh_dir = regk_sser_msbfirst; + tr_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; + tr_cfg.rate_ctrl = regk_sser_bulk; + tr_cfg.data_pin_use = regk_sser_dout; + tr_cfg.bulk_wspace = 1; + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); + + rec_cfg.sample_size = 7; + rec_cfg.sh_dir = regk_sser_msbfirst; + rec_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; + rec_cfg.fifo_thr = regk_sser_inf; + REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); +} + +static inline int sync_data_avail(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char*)port->readp; /* cast away volatile */ + end = (unsigned char*)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- - ----- + * ^rp ^wp ^wp ^rp + */ + + if (end >= start) + avail = end - start; + else + avail = port->in_buffer_size - (start - end); + return avail; +} + +static inline int sync_data_avail_to_end(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char*)port->readp; /* cast away volatile */ + end = (unsigned char*)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- ----- + * ^rp ^wp ^wp ^rp + */ + + if (end >= start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + return avail; +} + +static int sync_serial_open(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + sync_port* port; + reg_dma_rw_cfg cfg = {.en = regk_dma_yes}; + reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes}; + + DEBUG(printk("Open sync serial port %d\n", dev)); + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + /* Allow open this device twice (assuming one reader and one writer) */ + if (port->busy == 2) + { + DEBUG(printk("Device is busy.. \n")); + return -EBUSY; + } + if (port->init_irqs) { + if (port->use_dma) { + if (port == &ports[0]){ +#ifdef SYNC_SER_DMA + if(request_irq(DMA4_INTR_VECT, + tr_interrupt, + 0, + "synchronous serial 0 dma tr", + &ports[0])) { + printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); + return -EBUSY; + } else if(request_irq(DMA5_INTR_VECT, + rx_interrupt, + 0, + "synchronous serial 1 dma rx", + &ports[0])) { + free_irq(DMA4_INTR_VECT, &port[0]); + printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); + return -EBUSY; + } else if (crisv32_request_dma(SYNC_SER0_TX_DMA_NBR, + "synchronous serial 0 dma tr", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser0)) { + free_irq(DMA4_INTR_VECT, &port[0]); + free_irq(DMA5_INTR_VECT, &port[0]); + printk(KERN_CRIT "Can't allocate sync serial port 0 TX DMA channel"); + return -EBUSY; + } else if (crisv32_request_dma(SYNC_SER0_RX_DMA_NBR, + "synchronous serial 0 dma rec", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser0)) { + crisv32_free_dma(SYNC_SER0_TX_DMA_NBR); + free_irq(DMA4_INTR_VECT, &port[0]); + free_irq(DMA5_INTR_VECT, &port[0]); + printk(KERN_CRIT "Can't allocate sync serial port 1 RX DMA channel"); + return -EBUSY; + } +#endif + } + else if (port == &ports[1]){ +#ifdef SYNC_SER_DMA + if (request_irq(DMA6_INTR_VECT, + tr_interrupt, + 0, + "synchronous serial 1 dma tr", + &ports[1])) { + printk(KERN_CRIT "Can't allocate sync serial port 1 IRQ"); + return -EBUSY; + } else if (request_irq(DMA7_INTR_VECT, + rx_interrupt, + 0, + "synchronous serial 1 dma rx", + &ports[1])) { + free_irq(DMA6_INTR_VECT, &ports[1]); + printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ"); + return -EBUSY; + } else if (crisv32_request_dma(SYNC_SER1_TX_DMA_NBR, + "synchronous serial 1 dma tr", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser1)) { + free_irq(21, &ports[1]); + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel"); + return -EBUSY; + } else if (crisv32_request_dma(SYNC_SER1_RX_DMA_NBR, + "synchronous serial 3 dma rec", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser1)) { + crisv32_free_dma(SYNC_SER1_TX_DMA_NBR); + free_irq(DMA6_INTR_VECT, &ports[1]); + free_irq(DMA7_INTR_VECT, &ports[1]); + printk(KERN_CRIT "Can't allocate sync serial port 3 RX DMA channel"); + return -EBUSY; + } +#endif + } + + /* Enable DMAs */ + REG_WR(dma, port->regi_dmain, rw_cfg, cfg); + REG_WR(dma, port->regi_dmaout, rw_cfg, cfg); + /* Enable DMA IRQs */ + REG_WR(dma, port->regi_dmain, rw_intr_mask, intr_mask); + REG_WR(dma, port->regi_dmaout, rw_intr_mask, intr_mask); + /* Set up wordsize = 2 for DMAs. */ + DMA_WR_CMD (port->regi_dmain, regk_dma_set_w_size1); + DMA_WR_CMD (port->regi_dmaout, regk_dma_set_w_size1); + + start_dma_in(port); + port->init_irqs = 0; + } else { /* !port->use_dma */ +#ifdef SYNC_SER_MANUAL + if (port == &ports[0]) { + if (request_irq(SSER0_INTR_VECT, + manual_interrupt, + 0, + "synchronous serial manual irq", + &ports[0])) { + printk("Can't allocate sync serial manual irq"); + return -EBUSY; + } + } else if (port == &ports[1]) { + if (request_irq(SSER1_INTR_VECT, + manual_interrupt, + 0, + "synchronous serial manual irq", + &ports[1])) { + printk(KERN_CRIT "Can't allocate sync serial manual irq"); + return -EBUSY; + } + } + port->init_irqs = 0; +#else + panic("sync_serial: Manual mode not supported.\n"); +#endif /* SYNC_SER_MANUAL */ + } + } /* port->init_irqs */ + + port->busy++; + return 0; +} + +static int sync_serial_release(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + sync_port* port; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + if (port->busy) + port->busy--; + if (!port->busy) + /* XXX */ ; + return 0; +} + +static unsigned int sync_serial_poll(struct file *file, poll_table *wait) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + unsigned int mask = 0; + sync_port* port; + DEBUGPOLL( static unsigned int prev_mask = 0; ); + + port = &ports[dev]; + poll_wait(file, &port->out_wait_q, wait); + poll_wait(file, &port->in_wait_q, wait); + /* Some room to write */ + if (port->out_count < OUT_BUFFER_SIZE) + mask |= POLLOUT | POLLWRNORM; + /* At least an inbufchunk of data */ + if (sync_data_avail(port) >= port->inbufchunk) + mask |= POLLIN | POLLRDNORM; + + DEBUGPOLL(if (mask != prev_mask) + printk("sync_serial_poll: mask 0x%08X %s %s\n", mask, + mask&POLLOUT?"POLLOUT":"", mask&POLLIN?"POLLIN":""); + prev_mask = mask; + ); + return mask; +} + +static int sync_serial_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int return_val = 0; + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + sync_port* port; + reg_sser_rw_tr_cfg tr_cfg; + reg_sser_rw_rec_cfg rec_cfg; + reg_sser_rw_frm_cfg frm_cfg; + reg_sser_rw_cfg gen_cfg; + reg_sser_rw_intr_mask intr_mask; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -1; + } + port = &ports[dev]; + spin_lock_irq(&port->lock); + + tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); + rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg); + frm_cfg = REG_RD(sser, port->regi_sser, rw_frm_cfg); + gen_cfg = REG_RD(sser, port->regi_sser, rw_cfg); + intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); + + switch(cmd) + { + case SSP_SPEED: + if (GET_SPEED(arg) == CODEC) + { + gen_cfg.base_freq = regk_sser_f32; + /* FREQ = 0 => 4 MHz => clk_div = 7*/ + gen_cfg.clk_div = 6 + (1 << GET_FREQ(arg)); + } + else + { + gen_cfg.base_freq = regk_sser_f29_493; + switch (GET_SPEED(arg)) + { + case SSP150: + gen_cfg.clk_div = 29493000 / (150 * 8) - 1; + break; + case SSP300: + gen_cfg.clk_div = 29493000 / (300 * 8) - 1; + break; + case SSP600: + gen_cfg.clk_div = 29493000 / (600 * 8) - 1; + break; + case SSP1200: + gen_cfg.clk_div = 29493000 / (1200 * 8) - 1; + break; + case SSP2400: + gen_cfg.clk_div = 29493000 / (2400 * 8) - 1; + break; + case SSP4800: + gen_cfg.clk_div = 29493000 / (4800 * 8) - 1; + break; + case SSP9600: + gen_cfg.clk_div = 29493000 / (9600 * 8) - 1; + break; + case SSP19200: + gen_cfg.clk_div = 29493000 / (19200 * 8) - 1; + break; + case SSP28800: + gen_cfg.clk_div = 29493000 / (28800 * 8) - 1; + break; + case SSP57600: + gen_cfg.clk_div = 29493000 / (57600 * 8) - 1; + break; + case SSP115200: + gen_cfg.clk_div = 29493000 / (115200 * 8) - 1; + break; + case SSP230400: + gen_cfg.clk_div = 29493000 / (230400 * 8) - 1; + break; + case SSP460800: + gen_cfg.clk_div = 29493000 / (460800 * 8) - 1; + break; + case SSP921600: + gen_cfg.clk_div = 29493000 / (921600 * 8) - 1; + break; + case SSP3125000: + gen_cfg.base_freq = regk_sser_f100; + gen_cfg.clk_div = 100000000 / (3125000 * 8) - 1; + break; + + } + } + frm_cfg.wordrate = GET_WORD_RATE(arg); + + break; + case SSP_MODE: + switch(arg) + { + case MASTER_OUTPUT: + port->output = 1; + port->input = 0; + gen_cfg.clk_dir = regk_sser_out; + break; + case SLAVE_OUTPUT: + port->output = 1; + port->input = 0; + gen_cfg.clk_dir = regk_sser_in; + break; + case MASTER_INPUT: + port->output = 0; + port->input = 1; + gen_cfg.clk_dir = regk_sser_out; + break; + case SLAVE_INPUT: + port->output = 0; + port->input = 1; + gen_cfg.clk_dir = regk_sser_in; + break; + case MASTER_BIDIR: + port->output = 1; + port->input = 1; + gen_cfg.clk_dir = regk_sser_out; + break; + case SLAVE_BIDIR: + port->output = 1; + port->input = 1; + gen_cfg.clk_dir = regk_sser_in; + break; + default: + spin_unlock_irq(&port->lock); + return -EINVAL; + + } + if (!port->use_dma || (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT)) + intr_mask.rdav = regk_sser_yes; + break; + case SSP_FRAME_SYNC: + if (arg & NORMAL_SYNC) + frm_cfg.tr_delay = 1; + else if (arg & EARLY_SYNC) + frm_cfg.tr_delay = 0; + + tr_cfg.bulk_wspace = frm_cfg.tr_delay; + frm_cfg.early_wend = regk_sser_yes; + if (arg & BIT_SYNC) + frm_cfg.type = regk_sser_edge; + else if (arg & WORD_SYNC) + frm_cfg.type = regk_sser_level; + else if (arg & EXTENDED_SYNC) + frm_cfg.early_wend = regk_sser_no; + + if (arg & SYNC_ON) + frm_cfg.frame_pin_use = regk_sser_frm; + else if (arg & SYNC_OFF) + frm_cfg.frame_pin_use = regk_sser_gio0; + + if (arg & WORD_SIZE_8) + rec_cfg.sample_size = tr_cfg.sample_size = 7; + else if (arg & WORD_SIZE_12) + rec_cfg.sample_size = tr_cfg.sample_size = 11; + else if (arg & WORD_SIZE_16) + rec_cfg.sample_size = tr_cfg.sample_size = 15; + else if (arg & WORD_SIZE_24) + rec_cfg.sample_size = tr_cfg.sample_size = 23; + else if (arg & WORD_SIZE_32) + rec_cfg.sample_size = tr_cfg.sample_size = 31; + + if (arg & BIT_ORDER_MSB) + rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_msbfirst; + else if (arg & BIT_ORDER_LSB) + rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_lsbfirst; + + if (arg & FLOW_CONTROL_ENABLE) + rec_cfg.fifo_thr = regk_sser_thr16; + else if (arg & FLOW_CONTROL_DISABLE) + rec_cfg.fifo_thr = regk_sser_inf; + + if (arg & CLOCK_NOT_GATED) + gen_cfg.gate_clk = regk_sser_no; + else if (arg & CLOCK_GATED) + gen_cfg.gate_clk = regk_sser_yes; + + break; + case SSP_IPOLARITY: + /* NOTE!! negedge is considered NORMAL */ + if (arg & CLOCK_NORMAL) + rec_cfg.clk_pol = regk_sser_neg; + else if (arg & CLOCK_INVERT) + rec_cfg.clk_pol = regk_sser_pos; + + if (arg & FRAME_NORMAL) + frm_cfg.level = regk_sser_pos_hi; + else if (arg & FRAME_INVERT) + frm_cfg.level = regk_sser_neg_lo; + + if (arg & STATUS_NORMAL) + gen_cfg.hold_pol = regk_sser_pos; + else if (arg & STATUS_INVERT) + gen_cfg.hold_pol = regk_sser_neg; + break; + case SSP_OPOLARITY: + if (arg & CLOCK_NORMAL) + gen_cfg.out_clk_pol = regk_sser_neg; + else if (arg & CLOCK_INVERT) + gen_cfg.out_clk_pol = regk_sser_pos; + + if (arg & FRAME_NORMAL) + frm_cfg.level = regk_sser_pos_hi; + else if (arg & FRAME_INVERT) + frm_cfg.level = regk_sser_neg_lo; + + if (arg & STATUS_NORMAL) + gen_cfg.hold_pol = regk_sser_pos; + else if (arg & STATUS_INVERT) + gen_cfg.hold_pol = regk_sser_neg; + break; + case SSP_SPI: + rec_cfg.fifo_thr = regk_sser_inf; + rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_msbfirst; + rec_cfg.sample_size = tr_cfg.sample_size = 7; + frm_cfg.frame_pin_use = regk_sser_frm; + frm_cfg.type = regk_sser_level; + frm_cfg.tr_delay = 1; + frm_cfg.level = regk_sser_neg_lo; + if (arg & SPI_SLAVE) + { + rec_cfg.clk_pol = regk_sser_neg; + gen_cfg.clk_dir = regk_sser_in; + port->input = 1; + port->output = 0; + } + else + { + gen_cfg.out_clk_pol = regk_sser_pos; + port->input = 0; + port->output = 1; + gen_cfg.clk_dir = regk_sser_out; + } + break; + case SSP_INBUFCHUNK: + break; + default: + return_val = -1; + } + + + if (port->started) + { + tr_cfg.tr_en = port->output; + rec_cfg.rec_en = port->input; + } + + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); + REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + REG_WR(sser, port->regi_sser, rw_frm_cfg, frm_cfg); + REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); + REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + + spin_unlock_irq(&port->lock); + return return_val; +} + +static ssize_t sync_serial_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + sync_port *port; + unsigned long c, c1; + unsigned long free_outp; + unsigned long outp; + unsigned long out_buffer; + unsigned long flags; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGWRITE(printk("W d%d c %lu (%d/%d)\n", port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE)); + /* Space to end of buffer */ + /* + * out_buffer 012345<- c ->OUT_BUFFER_SIZE + * outp^ +out_count + ^free_outp + * out_buffer 45<- c ->0123OUT_BUFFER_SIZE + * +out_count outp^ + * free_outp + * + */ + + /* Read variables that may be updated by interrupts */ + spin_lock_irqsave(&port->lock, flags); + count = count > OUT_BUFFER_SIZE - port->out_count ? OUT_BUFFER_SIZE - port->out_count : count; + outp = (unsigned long)port->outp; + free_outp = outp + port->out_count; + spin_unlock_irqrestore(&port->lock, flags); + out_buffer = (unsigned long)port->out_buffer; + + /* Find out where and how much to write */ + if (free_outp >= out_buffer + OUT_BUFFER_SIZE) + free_outp -= OUT_BUFFER_SIZE; + if (free_outp >= outp) + c = out_buffer + OUT_BUFFER_SIZE - free_outp; + else + c = outp - free_outp; + if (c > count) + c = count; + +// DEBUGWRITE(printk("w op %08lX fop %08lX c %lu\n", outp, free_outp, c)); + if (copy_from_user((void*)free_outp, buf, c)) + return -EFAULT; + + if (c != count) { + buf += c; + c1 = count - c; + DEBUGWRITE(printk("w2 fi %lu c %lu c1 %lu\n", free_outp-out_buffer, c, c1)); + if (copy_from_user((void*)out_buffer, buf, c1)) + return -EFAULT; + } + spin_lock_irqsave(&port->lock, flags); + port->out_count += count; + spin_unlock_irqrestore(&port->lock, flags); + + /* Make sure transmitter/receiver is running */ + if (!port->started) + { + reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); + reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); + reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg); + cfg.en = regk_sser_yes; + tr_cfg.tr_en = port->output; + rec_cfg.rec_en = port->input; + REG_WR(sser, port->regi_sser, rw_cfg, cfg); + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); + REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + port->started = 1; + } + + if (file->f_flags & O_NONBLOCK) { + spin_lock_irqsave(&port->lock, flags); + if (!port->tr_running) { + if (!port->use_dma) { + reg_sser_rw_intr_mask intr_mask; + intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + intr_mask.trdy = 1; + REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); + } else { + start_dma(port, (unsigned char* volatile )port->outp, c); + } + } + spin_unlock_irqrestore(&port->lock, flags); + DEBUGWRITE(printk("w d%d c %lu NB\n", + port->port_nbr, count)); + return count; + } + + /* Sleep until all sent */ + + add_wait_queue(&port->out_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&port->lock, flags); + if (!port->tr_running) { + if (!port->use_dma) { + reg_sser_rw_intr_mask intr_mask; + intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + intr_mask.trdy = 1; + REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); + } else { + start_dma(port, port->outp, c); + } + } + spin_unlock_irqrestore(&port->lock, flags); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->out_wait_q, &wait); + if (signal_pending(current)) + { + return -EINTR; + } + DEBUGWRITE(printk("w d%d c %lu\n", port->port_nbr, count)); + return count; +} + +static ssize_t sync_serial_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + int avail; + sync_port *port; + unsigned char* start; + unsigned char* end; + unsigned long flags; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGREAD(printk("R%d c %d ri %lu wi %lu /%lu\n", dev, count, port->readp - port->flip, port->writep - port->flip, port->in_buffer_size)); + + if (!port->started) + { + reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); + reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); + reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg); + cfg.en = regk_sser_yes; + tr_cfg.tr_en = regk_sser_yes; + rec_cfg.rec_en = regk_sser_yes; + REG_WR(sser, port->regi_sser, rw_cfg, cfg); + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); + REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + port->started = 1; + } + + + /* Calculate number of available bytes */ + /* Save pointers to avoid that they are modified by interrupt */ + spin_lock_irqsave(&port->lock, flags); + start = (unsigned char*)port->readp; /* cast away volatile */ + end = (unsigned char*)port->writep; /* cast away volatile */ + spin_unlock_irqrestore(&port->lock, flags); + while ((start == end) && !port->full) /* No data */ + { + if (file->f_flags & O_NONBLOCK) + { + return -EAGAIN; + } + + interruptible_sleep_on(&port->in_wait_q); + if (signal_pending(current)) + { + return -EINTR; + } + spin_lock_irqsave(&port->lock, flags); + start = (unsigned char*)port->readp; /* cast away volatile */ + end = (unsigned char*)port->writep; /* cast away volatile */ + spin_unlock_irqrestore(&port->lock, flags); + } + + /* Lazy read, never return wrapped data. */ + if (port->full) + avail = port->in_buffer_size; + else if (end > start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + + count = count > avail ? avail : count; + if (copy_to_user(buf, start, count)) + return -EFAULT; + /* Disable interrupts while updating readp */ + spin_lock_irqsave(&port->lock, flags); + port->readp += count; + if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */ + port->readp = port->flip; + port->full = 0; + spin_unlock_irqrestore(&port->lock, flags); + DEBUGREAD(printk("r %d\n", count)); + return count; +} + +static void send_word(sync_port* port) +{ + reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); + reg_sser_rw_tr_data tr_data = {0}; + + switch(tr_cfg.sample_size) + { + case 8: + port->out_count--; + tr_data.data = *port->outp++; + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case 12: + { + int data = (*port->outp++) << 8; + data |= *port->outp++; + port->out_count-=2; + tr_data.data = data; + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + } + break; + case 16: + port->out_count-=2; + tr_data.data = *(unsigned short *)port->outp; + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + port->outp+=2; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case 24: + port->out_count-=3; + tr_data.data = *(unsigned short *)port->outp; + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + port->outp+=2; + tr_data.data = *port->outp++; + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case 32: + port->out_count-=4; + tr_data.data = *(unsigned short *)port->outp; + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + port->outp+=2; + tr_data.data = *(unsigned short *)port->outp; + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + port->outp+=2; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + } +} + + +static void start_dma(struct sync_port* port, const char* data, int count) +{ + port->tr_running = 1; + port->out_descr.buf = (char*)virt_to_phys((char*)data); + port->out_descr.after = port->out_descr.buf + count; + port->out_descr.eol = port->out_descr.intr = 1; + + port->out_context.saved_data = (dma_descr_data*)virt_to_phys(&port->out_descr); + port->out_context.saved_data_buf = port->out_descr.buf; + + DMA_START_CONTEXT(port->regi_dmaout, virt_to_phys((char*)&port->out_context)); + DEBUGTXINT(printk("dma %08lX c %d\n", (unsigned long)data, count)); +} + +static void start_dma_in(sync_port* port) +{ + int i; + char* buf; + port->writep = port->flip; + + if (port->writep > port->flip + port->in_buffer_size) + { + panic("Offset too large in sync serial driver\n"); + return; + } + buf = (char*)virt_to_phys(port->in_buffer); + for (i = 0; i < NUM_IN_DESCR; i++) { + port->in_descr[i].buf = buf; + port->in_descr[i].after = buf + port->inbufchunk; + port->in_descr[i].intr = 1; + port->in_descr[i].next = (dma_descr_data*)virt_to_phys(&port->in_descr[i+1]); + port->in_descr[i].buf = buf; + buf += port->inbufchunk; + } + /* Link the last descriptor to the first */ + port->in_descr[i-1].next = (dma_descr_data*)virt_to_phys(&port->in_descr[0]); + port->in_descr[i-1].eol = regk_sser_yes; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1]; + port->in_context.saved_data = (dma_descr_data*)virt_to_phys(&port->in_descr[0]); + port->in_context.saved_data_buf = port->in_descr[0].buf; + DMA_START_CONTEXT(port->regi_dmain, virt_to_phys(&port->in_context)); +} + +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + reg_dma_r_masked_intr masked; + reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; + int i; + struct dma_descr_data *descr; + unsigned int sentl; + int found = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) + { + sync_port *port = &ports[i]; + if (!port->enabled || !port->use_dma ) + continue; + + masked = REG_RD(dma, port->regi_dmaout, r_masked_intr); + + if (masked.data) /* IRQ active for the port? */ + { + found = 1; + /* Clear IRQ */ + REG_WR(dma, port->regi_dmaout, rw_ack_intr, ack_intr); + descr = &port->out_descr; + sentl = descr->after - descr->buf; + port->out_count -= sentl; + port->outp += sentl; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + if (port->out_count) { + int c; + c = port->out_buffer + OUT_BUFFER_SIZE - port->outp; + if (c > port->out_count) + c = port->out_count; + DEBUGTXINT(printk("tx_int DMAWRITE %i %i\n", sentl, c)); + start_dma(port, port->outp, c); + } else { + DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl)); + port->tr_running = 0; + } + wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */ + } + } + return IRQ_RETVAL(found); +} /* tr_interrupt */ + +static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + reg_dma_r_masked_intr masked; + reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; + + int i; + int found = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) + { + sync_port *port = &ports[i]; + + if (!port->enabled || !port->use_dma ) + continue; + + masked = REG_RD(dma, port->regi_dmain, r_masked_intr); + + if (masked.data) /* Descriptor interrupt */ + { + found = 1; + while (REG_RD(dma, port->regi_dmain, rw_data) != + virt_to_phys(port->next_rx_desc)) { + + if (port->writep + port->inbufchunk > port->flip + port->in_buffer_size) { + int first_size = port->flip + port->in_buffer_size - port->writep; + memcpy((char*)port->writep, phys_to_virt((unsigned)port->next_rx_desc->buf), first_size); + memcpy(port->flip, phys_to_virt((unsigned)port->next_rx_desc->buf+first_size), port->inbufchunk - first_size); + port->writep = port->flip + port->inbufchunk - first_size; + } else { + memcpy((char*)port->writep, + phys_to_virt((unsigned)port->next_rx_desc->buf), + port->inbufchunk); + port->writep += port->inbufchunk; + if (port->writep >= port->flip + port->in_buffer_size) + port->writep = port->flip; + } + if (port->writep == port->readp) + { + port->full = 1; + } + + port->next_rx_desc->eol = 0; + port->prev_rx_desc->eol = 1; + port->prev_rx_desc = phys_to_virt((unsigned)port->next_rx_desc); + port->next_rx_desc = phys_to_virt((unsigned)port->next_rx_desc->next); + wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */ + DMA_CONTINUE(port->regi_dmain); + REG_WR(dma, port->regi_dmain, rw_ack_intr, ack_intr); + + } + } + } + return IRQ_RETVAL(found); +} /* rx_interrupt */ +#endif /* SYNC_SER_DMA */ + +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + int i; + int found = 0; + reg_sser_r_masked_intr masked; + + for (i = 0; i < NUMBER_OF_PORTS; i++) + { + sync_port* port = &ports[i]; + + if (!port->enabled || port->use_dma) + { + continue; + } + + masked = REG_RD(sser, port->regi_sser, r_masked_intr); + if (masked.rdav) /* Data received? */ + { + reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg); + reg_sser_r_rec_data data = REG_RD(sser, port->regi_sser, r_rec_data); + found = 1; + /* Read data */ + switch(rec_cfg.sample_size) + { + case 8: + *port->writep++ = data.data & 0xff; + break; + case 12: + *port->writep = (data.data & 0x0ff0) >> 4; + *(port->writep + 1) = data.data & 0x0f; + port->writep+=2; + break; + case 16: + *(unsigned short*)port->writep = data.data; + port->writep+=2; + break; + case 24: + *(unsigned int*)port->writep = data.data; + port->writep+=3; + break; + case 32: + *(unsigned int*)port->writep = data.data; + port->writep+=4; + break; + } + + if (port->writep >= port->flip + port->in_buffer_size) /* Wrap? */ + port->writep = port->flip; + if (port->writep == port->readp) { + /* receive buffer overrun, discard oldest data + */ + port->readp++; + if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */ + port->readp = port->flip; + } + if (sync_data_avail(port) >= port->inbufchunk) + wake_up_interruptible(&port->in_wait_q); /* Wake up application */ + } + + if (masked.trdy) /* Transmitter ready? */ + { + found = 1; + if (port->out_count > 0) /* More data to send */ + send_word(port); + else /* transmission finished */ + { + reg_sser_rw_intr_mask intr_mask; + intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); + intr_mask.trdy = 0; + REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); + wake_up_interruptible(&port->out_wait_q); /* Wake up application */ + } + } + } + return IRQ_RETVAL(found); +} +#endif + +module_init(etrax_sync_serial_init); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/Kconfig 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,296 @@ +config ETRAX_DRAM_VIRTUAL_BASE + hex + depends on ETRAX_ARCH_V32 + default "c0000000" + +config ETRAX_LED1G + string "First green LED bit" + depends on ETRAX_ARCH_V32 + default "PA3" + help + Bit to use for the first green LED (network LED). + Most Axis products use bit A3 here. + +config ETRAX_LED1R + string "First red LED bit" + depends on ETRAX_ARCH_V32 + default "PA4" + help + Bit to use for the first red LED (network LED). + Most Axis products use bit A4 here. + +config ETRAX_LED2G + string "Second green LED bit" + depends on ETRAX_ARCH_V32 + default "PA5" + help + Bit to use for the first green LED (status LED). + Most Axis products use bit A5 here. + +config ETRAX_LED2R + string "Second red LED bit" + depends on ETRAX_ARCH_V32 + default "PA6" + help + Bit to use for the first red LED (network LED). + Most Axis products use bit A6 here. + +config ETRAX_LED3G + string "Third green LED bit" + depends on ETRAX_ARCH_V32 + default "PA7" + help + Bit to use for the first green LED (drive/power LED). + Most Axis products use bit A7 here. + +config ETRAX_LED3R + string "Third red LED bit" + depends on ETRAX_ARCH_V32 + default "PA7" + help + Bit to use for the first red LED (drive/power LED). + Most Axis products use bit A7 here. + +choice + prompt "Product debug-port" + depends on ETRAX_ARCH_V32 + default ETRAX_DEBUG_PORT0 + +config ETRAX_DEBUG_PORT0 + bool "Serial-0" + help + Choose a serial port for the ETRAX debug console. Default to + port 0. + +config ETRAX_DEBUG_PORT1 + bool "Serial-1" + help + Use serial port 1 for the console. + +config ETRAX_DEBUG_PORT2 + bool "Serial-2" + help + Use serial port 2 for the console. + +config ETRAX_DEBUG_PORT3 + bool "Serial-3" + help + Use serial port 3 for the console. + +config ETRAX_DEBUG_PORT_NULL + bool "disabled" + help + Disable serial-port debugging. + +endchoice + +choice + prompt "Kernel GDB port" + depends on ETRAX_KGDB + default ETRAX_KGDB_PORT0 + help + Choose a serial port for kernel debugging. NOTE: This port should + not be enabled under Drivers for built-in interfaces (as it has its + own initialization code) and should not be the same as the debug port. + +config ETRAX_KGDB_PORT0 + bool "Serial-0" + help + Use serial port 0 for kernel debugging. + +config ETRAX_KGDB_PORT1 + bool "Serial-1" + help + Use serial port 1 for kernel debugging. + +config ETRAX_KGDB_PORT2 + bool "Serial-2" + help + Use serial port 2 for kernel debugging. + +config ETRAX_KGDB_PORT3 + bool "Serial-3" + help + Use serial port 3 for kernel debugging. + +endchoice + +config ETRAX_MEM_GRP1_CONFIG + hex "MEM_GRP1_CONFIG" + depends on ETRAX_ARCH_V32 + default "4044a" + help + Waitstates for flash. The default value is suitable for the + standard flashes used in axis products (120 ns). + +config ETRAX_MEM_GRP2_CONFIG + hex "MEM_GRP2_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + Waitstates for SRAM. 0 is a good choice for most Axis products. + +config ETRAX_MEM_GRP3_CONFIG + hex "MEM_GRP3_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + Waitstates for CSP0-3. 0 is a good choice for most Axis products. + It may need to be changed if external devices such as extra + register-mapped LEDs are used. + +config ETRAX_MEM_GRP4_CONFIG + hex "MEM_GRP4_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + Waitstates for CSP4-6. 0 is a good choice for most Axis products. + +config ETRAX_SDRAM_GRP0_CONFIG + hex "SDRAM_GRP0_CONFIG" + depends on ETRAX_ARCH_V32 + default "336" + help + SDRAM configuration for group 0. The value depends on the + hardware configuration. The default value is suitable + for 32 MB organized as two 16 bits chips (e.g. Axis + part number 18550) connected as one 32 bit device (i.e. in + the same group). + +config ETRAX_SDRAM_GRP1_CONFIG + hex "SDRAM_GRP1_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + SDRAM configuration for group 1. The defult value is 0 + because group 1 is not used in the default configuration, + described in the help for SDRAM_GRP0_CONFIG. + +config ETRAX_SDRAM_TIMING + hex "SDRAM_TIMING" + depends on ETRAX_ARCH_V32 + default "104a" + help + SDRAM timing parameters. The default value is ok for + most hardwares but large SDRAMs may require a faster + refresh (a.k.a 8K refresh). The default value implies + 100MHz clock and SDR mode. + +config ETRAX_SDRAM_COMMAND + hex "SDRAM_COMMAND" + depends on ETRAX_ARCH_V32 + default "0" + help + SDRAM command. Should be 0 unless you really know what + you are doing (may be != 0 for unusual address line + mappings such as in a MCM).. + +config ETRAX_DEF_GIO_PA_OE + hex "GIO_PA_OE" + depends on ETRAX_ARCH_V32 + default "1c" + help + Configures the direction of general port A bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PA_OUT + hex "GIO_PA_OUT" + depends on ETRAX_ARCH_V32 + default "00" + help + Configures the initial data for the general port A bits. Most + products should use 00 here. + +config ETRAX_DEF_GIO_PB_OE + hex "GIO_PB_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port B bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PB_OUT + hex "GIO_PB_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port B bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PC_OE + hex "GIO_PC_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port C bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PC_OUT + hex "GIO_PC_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port C bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PD_OE + hex "GIO_PD_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port D bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PD_OUT + hex "GIO_PD_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port D bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PE_OE + hex "GIO_PE_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port E bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PE_OUT + hex "GIO_PE_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port E bits. Most + products should use 00000 here. --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/arbiter.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,297 @@ +/* + * Memory arbiter functions. Allocates bandwith through the + * arbiter and sets up arbiter breakpoints. + * + * The algorithm first assigns slots to the clients that has specified + * bandwith (e.g. ethernet) and then the remaining slots are divided + * on all the active clients. + * + * Copyright (c) 2004, 2005 Axis Communications AB. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct crisv32_watch_entry +{ + unsigned long instance; + watch_callback* cb; + unsigned long start; + unsigned long end; + int used; +}; + +#define NUMBER_OF_BP 4 +#define NBR_OF_CLIENTS 14 +#define NBR_OF_SLOTS 64 +#define SDRAM_BANDWIDTH 100000000 /* Some kind of expected value */ +#define INTMEM_BANDWIDTH 400000000 +#define NBR_OF_REGIONS 2 + +static struct crisv32_watch_entry watches[NUMBER_OF_BP] = +{ + {regi_marb_bp0}, + {regi_marb_bp1}, + {regi_marb_bp2}, + {regi_marb_bp3} +}; + +static int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH}; + +DEFINE_SPINLOCK(arbiter_lock); + +static irqreturn_t +crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs); + +static void crisv32_arbiter_config(int region) +{ + int slot; + int client; + int interval = 0; + int val[NBR_OF_SLOTS]; + + for (slot = 0; slot < NBR_OF_SLOTS; slot++) + val[slot] = NBR_OF_CLIENTS + 1; + + for (client = 0; client < NBR_OF_CLIENTS; client++) + { + int pos; + if (!requested_slots[region][client]) + continue; + interval = NBR_OF_SLOTS / requested_slots[region][client]; + pos = 0; + while (pos < NBR_OF_SLOTS) + { + if (val[pos] != NBR_OF_CLIENTS + 1) + pos++; + else + { + val[pos] = client; + pos += interval; + } + } + } + + client = 0; + for (slot = 0; slot < NBR_OF_SLOTS; slot++) + { + if (val[slot] == NBR_OF_CLIENTS + 1) + { + int first = client; + while(!active_clients[region][client]) { + client = (client + 1) % NBR_OF_CLIENTS; + if (client == first) + break; + } + val[slot] = client; + client = (client + 1) % NBR_OF_CLIENTS; + } + if (region == EXT_REGION) + REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, val[slot]); + else if (region == INT_REGION) + REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, val[slot]); + } +} + +extern char _stext, _etext; + +static void crisv32_arbiter_init(void) +{ + static int initialized = 0; + + if (initialized) + return; + + initialized = 1; + + /* CPU caches are active. */ + active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; + crisv32_arbiter_config(EXT_REGION); + crisv32_arbiter_config(INT_REGION); + + if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, SA_INTERRUPT, + "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + +#ifndef CONFIG_ETRAX_KGDB + /* Global watch for writes to kernel text segment. */ + crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, + arbiter_all_clients, arbiter_all_write, NULL); +#endif +} + + + +int crisv32_arbiter_allocate_bandwith(int client, int region, + unsigned long bandwidth) +{ + int i; + int total_assigned = 0; + int total_clients = 0; + int req; + + crisv32_arbiter_init(); + + for (i = 0; i < NBR_OF_CLIENTS; i++) + { + total_assigned += requested_slots[region][i]; + total_clients += active_clients[region][i]; + } + req = NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); + + if (total_assigned + total_clients + req + 1 > NBR_OF_SLOTS) + return -ENOMEM; + + active_clients[region][client] = 1; + requested_slots[region][client] = req; + crisv32_arbiter_config(region); + + return 0; +} + +int crisv32_arbiter_watch(unsigned long start, unsigned long size, + unsigned long clients, unsigned long accesses, + watch_callback* cb) +{ + int i; + + crisv32_arbiter_init(); + + if (start > 0x80000000) { + printk("Arbiter: %lX doesn't look like a physical address", start); + return -EFAULT; + } + + spin_lock(&arbiter_lock); + + for (i = 0; i < NUMBER_OF_BP; i++) { + if (!watches[i].used) { + reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); + + watches[i].used = 1; + watches[i].start = start; + watches[i].end = start + size; + watches[i].cb = cb; + + REG_WR_INT(marb_bp, watches[i].instance, rw_first_addr, watches[i].start); + REG_WR_INT(marb_bp, watches[i].instance, rw_last_addr, watches[i].end); + REG_WR_INT(marb_bp, watches[i].instance, rw_op, accesses); + REG_WR_INT(marb_bp, watches[i].instance, rw_clients, clients); + + if (i == 0) + intr_mask.bp0 = regk_marb_yes; + else if (i == 1) + intr_mask.bp1 = regk_marb_yes; + else if (i == 2) + intr_mask.bp2 = regk_marb_yes; + else if (i == 3) + intr_mask.bp3 = regk_marb_yes; + + REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); + spin_unlock(&arbiter_lock); + + return i; + } + } + spin_unlock(&arbiter_lock); + return -ENOMEM; +} + +int crisv32_arbiter_unwatch(int id) +{ + reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); + + crisv32_arbiter_init(); + + spin_lock(&arbiter_lock); + + if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) { + spin_unlock(&arbiter_lock); + return -EINVAL; + } + + memset(&watches[id], 0, sizeof(struct crisv32_watch_entry)); + + if (id == 0) + intr_mask.bp0 = regk_marb_no; + else if (id == 1) + intr_mask.bp2 = regk_marb_no; + else if (id == 2) + intr_mask.bp2 = regk_marb_no; + else if (id == 3) + intr_mask.bp3 = regk_marb_no; + + REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); + + spin_unlock(&arbiter_lock); + return 0; +} + +extern void show_registers(struct pt_regs *regs); + +static irqreturn_t +crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs) +{ + reg_marb_r_masked_intr masked_intr = REG_RD(marb, regi_marb, r_masked_intr); + reg_marb_bp_r_brk_clients r_clients; + reg_marb_bp_r_brk_addr r_addr; + reg_marb_bp_r_brk_op r_op; + reg_marb_bp_r_brk_first_client r_first; + reg_marb_bp_r_brk_size r_size; + reg_marb_bp_rw_ack ack = {0}; + reg_marb_rw_ack_intr ack_intr = {.bp0=1,.bp1=1,.bp2=1,.bp3=1}; + struct crisv32_watch_entry* watch; + + if (masked_intr.bp0) { + watch = &watches[0]; + ack_intr.bp0 = regk_marb_yes; + } else if (masked_intr.bp1) { + watch = &watches[1]; + ack_intr.bp1 = regk_marb_yes; + } else if (masked_intr.bp2) { + watch = &watches[2]; + ack_intr.bp2 = regk_marb_yes; + } else if (masked_intr.bp3) { + watch = &watches[3]; + ack_intr.bp3 = regk_marb_yes; + } else { + return IRQ_NONE; + } + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_bp, watch->instance, r_brk_size); + + printk("Arbiter IRQ\n"); + printk("Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_size, r_size)); + + REG_WR(marb_bp, watch->instance, rw_ack, ack); + REG_WR(marb, regi_marb, rw_ack_intr, ack_intr); + + printk("IRQ occured at %lX\n", regs->erp); + + if (watch->cb) + watch->cb(); + + + return IRQ_HANDLED; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/asm-offsets.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,49 @@ +#include +#include + +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed to extract + * and format the required data. + */ + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() asm volatile("\n->" : : ) + +int main(void) +{ +#define ENTRY(entry) DEFINE(PT_ ## entry, offsetof(struct pt_regs, entry)) + ENTRY(orig_r10); + ENTRY(r13); + ENTRY(r12); + ENTRY(r11); + ENTRY(r10); + ENTRY(r9); + ENTRY(acr); + ENTRY(srs); + ENTRY(mof); + ENTRY(ccs); + ENTRY(srp); + BLANK(); +#undef ENTRY +#define ENTRY(entry) DEFINE(TI_ ## entry, offsetof(struct thread_info, entry)) + ENTRY(task); + ENTRY(flags); + ENTRY(preempt_count); + BLANK(); +#undef ENTRY +#define ENTRY(entry) DEFINE(THREAD_ ## entry, offsetof(struct thread_struct, entry)) + ENTRY(ksp); + ENTRY(usp); + ENTRY(ccs); + BLANK(); +#undef ENTRY +#define ENTRY(entry) DEFINE(TASK_ ## entry, offsetof(struct task_struct, entry)) + ENTRY(pid); + BLANK(); + DEFINE(LCLONE_VM, CLONE_VM); + DEFINE(LCLONE_UNTRACED, CLONE_UNTRACED); + return 0; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/crisksyms.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include + +/* Functions for allocating DMA channels */ +EXPORT_SYMBOL(crisv32_request_dma); +EXPORT_SYMBOL(crisv32_free_dma); + +/* Functions for handling internal RAM */ +EXPORT_SYMBOL(crisv32_intmem_alloc); +EXPORT_SYMBOL(crisv32_intmem_free); +EXPORT_SYMBOL(crisv32_intmem_phys_to_virt); +EXPORT_SYMBOL(crisv32_intmem_virt_to_phys); + +/* Functions for handling pinmux */ +EXPORT_SYMBOL(crisv32_pinmux_alloc); +EXPORT_SYMBOL(crisv32_pinmux_dealloc); + +/* Functions masking/unmasking interrupts */ +EXPORT_SYMBOL(mask_irq); +EXPORT_SYMBOL(unmask_irq); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/debugport.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2003, Axis Communications AB. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct dbg_port +{ + unsigned char nbr; + unsigned long instance; + unsigned int started; + unsigned long baudrate; + unsigned char parity; + unsigned int bits; +}; + +struct dbg_port ports[] = +{ + { + 0, + regi_ser0, + 0, + 115200, + 'N', + 8 + }, + { + 1, + regi_ser1, + 0, + 115200, + 'N', + 8 + }, + { + 2, + regi_ser2, + 0, + 115200, + 'N', + 8 + }, + { + 3, + regi_ser3, + 0, + 115200, + 'N', + 8 + } +}; +static struct dbg_port *port = +#if defined(CONFIG_ETRAX_DEBUG_PORT0) +&ports[0]; +#elif defined(CONFIG_ETRAX_DEBUG_PORT1) +&ports[1]; +#elif defined(CONFIG_ETRAX_DEBUG_PORT2) +&ports[2]; +#elif defined(CONFIG_ETRAX_DEBUG_PORT3) +&ports[3]; +#else +NULL; +#endif + +#ifdef CONFIG_ETRAX_KGDB +static struct dbg_port *kgdb_port = +#if defined(CONFIG_ETRAX_KGDB_PORT0) +&ports[0]; +#elif defined(CONFIG_ETRAX_KGDB_PORT1) +&ports[1]; +#elif defined(CONFIG_ETRAX_KGDB_PORT2) +&ports[2]; +#elif defined(CONFIG_ETRAX_KGDB_PORT3) +&ports[3]; +#else +NULL; +#endif +#endif + +#ifdef CONFIG_ETRAXFS_SIM +extern void print_str( const char *str ); +static char buffer[1024]; +static char msg[] = "Debug: "; +static int buffer_pos = sizeof(msg) - 1; +#endif + +extern struct tty_driver *serial_driver; + +static void +start_port(struct dbg_port* p) +{ + if (!p) + return; + + if (p->started) + return; + p->started = 1; + + if (p->nbr == 1) + crisv32_pinmux_alloc_fixed(pinmux_ser1); + else if (p->nbr == 2) + crisv32_pinmux_alloc_fixed(pinmux_ser2); + else if (p->nbr == 3) + crisv32_pinmux_alloc_fixed(pinmux_ser3); + + /* Set up serial port registers */ + reg_ser_rw_tr_ctrl tr_ctrl = {0}; + reg_ser_rw_tr_dma_en tr_dma_en = {0}; + + reg_ser_rw_rec_ctrl rec_ctrl = {0}; + reg_ser_rw_tr_baud_div tr_baud_div = {0}; + reg_ser_rw_rec_baud_div rec_baud_div = {0}; + + tr_ctrl.base_freq = rec_ctrl.base_freq = regk_ser_f29_493; + tr_dma_en.en = rec_ctrl.dma_mode = regk_ser_no; + tr_baud_div.div = rec_baud_div.div = 29493000 / p->baudrate / 8; + tr_ctrl.en = rec_ctrl.en = 1; + + if (p->parity == 'O') + { + tr_ctrl.par_en = regk_ser_yes; + tr_ctrl.par = regk_ser_odd; + rec_ctrl.par_en = regk_ser_yes; + rec_ctrl.par = regk_ser_odd; + } + else if (p->parity == 'E') + { + tr_ctrl.par_en = regk_ser_yes; + tr_ctrl.par = regk_ser_even; + rec_ctrl.par_en = regk_ser_yes; + rec_ctrl.par = regk_ser_odd; + } + + if (p->bits == 7) + { + tr_ctrl.data_bits = regk_ser_bits7; + rec_ctrl.data_bits = regk_ser_bits7; + } + + REG_WR (ser, p->instance, rw_tr_baud_div, tr_baud_div); + REG_WR (ser, p->instance, rw_rec_baud_div, rec_baud_div); + REG_WR (ser, p->instance, rw_tr_dma_en, tr_dma_en); + REG_WR (ser, p->instance, rw_tr_ctrl, tr_ctrl); + REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl); +} + +/* No debug */ +#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL + +static void +console_write(struct console *co, const char *buf, unsigned int len) +{ + return; +} + +/* Target debug */ +#elif !defined(CONFIG_ETRAXFS_SIM) + +static void +console_write_direct(struct console *co, const char *buf, unsigned int len) +{ + int i; + reg_ser_r_stat_din stat; + reg_ser_rw_tr_dma_en tr_dma_en, old; + + /* Switch to manual mode */ + tr_dma_en = old = REG_RD (ser, port->instance, rw_tr_dma_en); + if (tr_dma_en.en == regk_ser_yes) { + tr_dma_en.en = regk_ser_no; + REG_WR(ser, port->instance, rw_tr_dma_en, tr_dma_en); + } + + /* Send data */ + for (i = 0; i < len; i++) { + /* LF -> CRLF */ + if (buf[i] == '\n') { + do { + stat = REG_RD (ser, port->instance, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT (ser, port->instance, rw_dout, '\r'); + } + /* Wait until transmitter is ready and send.*/ + do { + stat = REG_RD (ser, port->instance, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT (ser, port->instance, rw_dout, buf[i]); + } + + /* Restore mode */ + if (tr_dma_en.en != old.en) + REG_WR(ser, port->instance, rw_tr_dma_en, old); +} + +static void +console_write(struct console *co, const char *buf, unsigned int len) +{ + if (!port) + return; + console_write_direct(co, buf, len); +} + + + +#else + +/* VCS debug */ + +static void +console_write(struct console *co, const char *buf, unsigned int len) +{ + char* pos; + pos = memchr(buf, '\n', len); + if (pos) { + int l = ++pos - buf; + memcpy(buffer + buffer_pos, buf, l); + memcpy(buffer, msg, sizeof(msg) - 1); + buffer[buffer_pos + l] = '\0'; + print_str(buffer); + buffer_pos = sizeof(msg) - 1; + if (pos - buf != len) { + memcpy(buffer + buffer_pos, pos, len - l); + buffer_pos += len - l; + } + } else { + memcpy(buffer + buffer_pos, buf, len); + buffer_pos += len; + } +} + +#endif + +int raw_printk(const char *fmt, ...) +{ + static char buf[1024]; + int printed_len; + va_list args; + va_start(args, fmt); + printed_len = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + console_write(NULL, buf, strlen(buf)); + return printed_len; +} + +void +stupid_debug(char* buf) +{ + console_write(NULL, buf, strlen(buf)); +} + +#ifdef CONFIG_ETRAX_KGDB +/* Use polling to get a single character from the kernel debug port */ +int +getDebugChar(void) +{ + reg_ser_rs_status_data stat; + reg_ser_rw_ack_intr ack_intr = { 0 }; + + do { + stat = REG_RD(ser, kgdb_instance, rs_status_data); + } while (!stat.data_avail); + + /* Ack the data_avail interrupt. */ + ack_intr.data_avail = 1; + REG_WR(ser, kgdb_instance, rw_ack_intr, ack_intr); + + return stat.data; +} + +/* Use polling to put a single character to the kernel debug port */ +void +putDebugChar(int val) +{ + reg_ser_r_status_data stat; + do { + stat = REG_RD (ser, kgdb_instance, r_status_data); + } while (!stat.tr_ready); + REG_WR (ser, kgdb_instance, rw_data_out, REG_TYPE_CONV(reg_ser_rw_data_out, int, val)); +} +#endif /* CONFIG_ETRAX_KGDB */ + +static int __init +console_setup(struct console *co, char *options) +{ + char* s; + + if (options) { + port = &ports[co->index]; + port->baudrate = 115200; + port->parity = 'N'; + port->bits = 8; + port->baudrate = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) port->parity = *s++; + if (*s) port->bits = *s++ - '0'; + port->started = 0; + start_port(port); + } + return 0; +} + +/* This is a dummy serial device that throws away anything written to it. + * This is used when no debug output is wanted. + */ +static struct tty_driver dummy_driver; + +static int dummy_open(struct tty_struct *tty, struct file * filp) +{ + return 0; +} + +static void dummy_close(struct tty_struct *tty, struct file * filp) +{ +} + +static int dummy_write(struct tty_struct * tty, + const unsigned char *buf, int count) +{ + return count; +} + +static int +dummy_write_room(struct tty_struct *tty) +{ + return 8192; +} + +void __init +init_dummy_console(void) +{ + memset(&dummy_driver, 0, sizeof(struct tty_driver)); + dummy_driver.driver_name = "serial"; + dummy_driver.name = "ttyS"; + dummy_driver.major = TTY_MAJOR; + dummy_driver.minor_start = 68; + dummy_driver.num = 1; /* etrax100 has 4 serial ports */ + dummy_driver.type = TTY_DRIVER_TYPE_SERIAL; + dummy_driver.subtype = SERIAL_TYPE_NORMAL; + dummy_driver.init_termios = tty_std_termios; + dummy_driver.init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ + dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + + dummy_driver.open = dummy_open; + dummy_driver.close = dummy_close; + dummy_driver.write = dummy_write; + dummy_driver.write_room = dummy_write_room; + if (tty_register_driver(&dummy_driver)) + panic("Couldn't register dummy serial driver\n"); +} + +static struct tty_driver* +crisv32_console_device(struct console* co, int *index) +{ + if (port) + *index = port->nbr; + return port ? serial_driver : &dummy_driver; +} + +static struct console sercons = { + name : "ttyS", + write: console_write, + read : NULL, + device : crisv32_console_device, + unblank : NULL, + setup : console_setup, + flags : CON_PRINTBUFFER, + index : -1, + cflag : 0, + next : NULL +}; +static struct console sercons0 = { + name : "ttyS", + write: console_write, + read : NULL, + device : crisv32_console_device, + unblank : NULL, + setup : console_setup, + flags : CON_PRINTBUFFER, + index : 0, + cflag : 0, + next : NULL +}; + +static struct console sercons1 = { + name : "ttyS", + write: console_write, + read : NULL, + device : crisv32_console_device, + unblank : NULL, + setup : console_setup, + flags : CON_PRINTBUFFER, + index : 1, + cflag : 0, + next : NULL +}; +static struct console sercons2 = { + name : "ttyS", + write: console_write, + read : NULL, + device : crisv32_console_device, + unblank : NULL, + setup : console_setup, + flags : CON_PRINTBUFFER, + index : 2, + cflag : 0, + next : NULL +}; +static struct console sercons3 = { + name : "ttyS", + write: console_write, + read : NULL, + device : crisv32_console_device, + unblank : NULL, + setup : console_setup, + flags : CON_PRINTBUFFER, + index : 3, + cflag : 0, + next : NULL +}; + +/* Register console for printk's, etc. */ +int __init +init_etrax_debug(void) +{ + static int first = 1; + + if (!first) { + unregister_console(&sercons); + register_console(&sercons0); + register_console(&sercons1); + register_console(&sercons2); + register_console(&sercons3); + init_dummy_console(); + return 0; + } + first = 0; + register_console(&sercons); + start_port(port); + +#ifdef CONFIG_ETRAX_KGDB + start_port(kgdb_port); +#endif /* CONFIG_ETRAX_KGDB */ + return 0; +} + +__initcall(init_etrax_debug); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/dma.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,224 @@ +/* Wrapper for DMA channel allocator that starts clocks etc */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char used_dma_channels[MAX_DMA_CHANNELS]; +static const char * used_dma_channels_users[MAX_DMA_CHANNELS]; + +static DEFINE_SPINLOCK(dma_lock); + +int crisv32_request_dma(unsigned int dmanr, const char * device_id, + unsigned options, unsigned int bandwidth, + enum dma_owner owner) +{ + unsigned long flags; + reg_config_rw_clk_ctrl clk_ctrl; + reg_strmux_rw_cfg strmux_cfg; + + if (crisv32_arbiter_allocate_bandwith(dmanr, + options & DMA_INT_MEM ? INT_REGION : EXT_REGION, + bandwidth)) + return -ENOMEM; + + spin_lock_irqsave(&dma_lock, flags); + + if (used_dma_channels[dmanr]) { + spin_unlock_irqrestore(&dma_lock, flags); + if (options & DMA_VERBOSE_ON_ERROR) { + printk("Failed to request DMA %i for %s, already allocated by %s\n", dmanr, device_id, used_dma_channels_users[dmanr]); + } + if (options & DMA_PANIC_ON_ERROR) + panic("request_dma error!"); + return -EBUSY; + } + clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); + strmux_cfg = REG_RD(strmux, regi_strmux, rw_cfg); + + switch(dmanr) + { + case 0: + case 1: + clk_ctrl.dma01_eth0 = 1; + break; + case 2: + case 3: + clk_ctrl.dma23 = 1; + break; + case 4: + case 5: + clk_ctrl.dma45 = 1; + break; + case 6: + case 7: + clk_ctrl.dma67 = 1; + break; + case 8: + case 9: + clk_ctrl.dma89_strcop = 1; + break; +#if MAX_DMA_CHANNELS-1 != 9 +#error Check dma.c +#endif + default: + spin_unlock_irqrestore(&dma_lock, flags); + if (options & DMA_VERBOSE_ON_ERROR) { + printk("Failed to request DMA %i for %s, only 0-%i valid)\n", dmanr, device_id, MAX_DMA_CHANNELS-1); + } + + if (options & DMA_PANIC_ON_ERROR) + panic("request_dma error!"); + return -EINVAL; + } + + switch(owner) + { + case dma_eth0: + if (dmanr == 0) + strmux_cfg.dma0 = regk_strmux_eth0; + else if (dmanr == 1) + strmux_cfg.dma1 = regk_strmux_eth0; + else + panic("Invalid DMA channel for eth0\n"); + break; + case dma_eth1: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_eth1; + else if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_eth1; + else + panic("Invalid DMA channel for eth1\n"); + break; + case dma_iop0: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_iop0; + else if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_iop0; + else + panic("Invalid DMA channel for iop0\n"); + break; + case dma_iop1: + if (dmanr == 4) + strmux_cfg.dma4 = regk_strmux_iop1; + else if (dmanr == 5) + strmux_cfg.dma5 = regk_strmux_iop1; + else + panic("Invalid DMA channel for iop1\n"); + break; + case dma_ser0: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_ser0; + else if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_ser0; + else + panic("Invalid DMA channel for ser0\n"); + break; + case dma_ser1: + if (dmanr == 4) + strmux_cfg.dma4 = regk_strmux_ser1; + else if (dmanr == 5) + strmux_cfg.dma5 = regk_strmux_ser1; + else + panic("Invalid DMA channel for ser1\n"); + break; + case dma_ser2: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_ser2; + else if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_ser2; + else + panic("Invalid DMA channel for ser2\n"); + break; + case dma_ser3: + if (dmanr == 8) + strmux_cfg.dma8 = regk_strmux_ser3; + else if (dmanr == 9) + strmux_cfg.dma9 = regk_strmux_ser3; + else + panic("Invalid DMA channel for ser3\n"); + break; + case dma_sser0: + if (dmanr == 4) + strmux_cfg.dma4 = regk_strmux_sser0; + else if (dmanr == 5) + strmux_cfg.dma5 = regk_strmux_sser0; + else + panic("Invalid DMA channel for sser0\n"); + break; + case dma_sser1: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_sser1; + else if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_sser1; + else + panic("Invalid DMA channel for sser1\n"); + break; + case dma_ata: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_ata; + else if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_ata; + else + panic("Invalid DMA channel for ata\n"); + break; + case dma_strp: + if (dmanr == 8) + strmux_cfg.dma8 = regk_strmux_strcop; + else if (dmanr == 9) + strmux_cfg.dma9 = regk_strmux_strcop; + else + panic("Invalid DMA channel for strp\n"); + break; + case dma_ext0: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_ext0; + else + panic("Invalid DMA channel for ext0\n"); + break; + case dma_ext1: + if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_ext1; + else + panic("Invalid DMA channel for ext1\n"); + break; + case dma_ext2: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_ext2; + else if (dmanr == 8) + strmux_cfg.dma8 = regk_strmux_ext2; + else + panic("Invalid DMA channel for ext2\n"); + break; + case dma_ext3: + if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_ext3; + else if (dmanr == 9) + strmux_cfg.dma9 = regk_strmux_ext2; + else + panic("Invalid DMA channel for ext2\n"); + break; + } + + used_dma_channels[dmanr] = 1; + used_dma_channels_users[dmanr] = device_id; + REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl); + REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg); + spin_unlock_irqrestore(&dma_lock,flags); + return 0; +} + +void crisv32_free_dma(unsigned int dmanr) +{ + spin_lock(&dma_lock); + used_dma_channels[dmanr] = 0; + spin_unlock(&dma_lock); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/entry.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2000-2003 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * Tobias Anderberg (tobiasa@axis.com), CRISv32 port. + * + * Code for the system-call and fault low-level handling routines. + * + * NOTE: This code handles signal-recognition, which happens every time + * after a timer-interrupt and after each system call. + * + * Stack layout in 'ret_from_system_call': + * ptrace needs to have all regs on the stack. + * if the order here is changed, it needs to be + * updated in fork.c:copy_process, signal.c:do_signal, + * ptrace.c and ptrace.h + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + ;; Exported functions. + .globl system_call + .globl ret_from_intr + .globl ret_from_fork + .globl resume + .globl multiple_interrupt + .globl nmi_interrupt + .globl spurious_interrupt + .globl do_sigtrap + .globl gdb_handle_exception + .globl sys_call_table + + ; Check if preemptive kernel scheduling should be done. +#ifdef CONFIG_PREEMPT +_resume_kernel: + di + ; Load current task struct. + movs.w -8192, $r0 ; THREAD_SIZE = 8192 + and.d $sp, $r0 + + addoq +TI_preempt_count, $r0, $acr + move.d [$acr], $r10 ; Preemption disabled? + bne _Rexit + nop + +_need_resched: + addoq +TI_flags, $r0, $acr + move.d [$acr], $r10 + btstq TIF_NEED_RESCHED, $r10 ; Check if need_resched is set. + bpl _Rexit + nop + + ; Do preemptive kernel scheduling. + jsr preempt_schedule_irq + nop + + ; Load new task struct. + movs.w -8192, $r0 ; THREAD_SIZE = 8192. + and.d $sp, $r0 + + ; One more time with new task. + ba _need_resched + nop +#else +#define _resume_kernel _Rexit +#endif + + ; Called at exit from fork. schedule_tail must be called to drop + ; spinlock if CONFIG_PREEMPT. +ret_from_fork: + jsr schedule_tail + nop + ba ret_from_sys_call + nop + +ret_from_intr: + ;; Check for resched if preemptive kernel, or if we're going back to + ;; user-mode. This test matches the user_regs(regs) macro. Don't simply + ;; test CCS since that doesn't necessarily reflect what mode we'll + ;; return into. + addoq +PT_ccs, $sp, $acr + move.d [$acr], $r0 + btstq 16, $r0 ; User-mode flag. + bpl _resume_kernel + + ; Note that di below is in delay slot. + +_resume_userspace: + di ; So need_resched and sigpending don't change. + + movs.w -8192, $r0 ; THREAD_SIZE == 8192 + and.d $sp, $r0 + + addoq +TI_flags, $r0, $acr ; current->work + move.d [$acr], $r10 + and.d _TIF_WORK_MASK, $r10 ; Work to be done on return? + bne _work_pending + nop + ba _Rexit + nop + + ;; The system_call is called by a BREAK instruction, which looks pretty + ;; much like any other exception. + ;; + ;; System calls can't be made from interrupts but we still stack ERP + ;; to have a complete stack frame. + ;; + ;; In r9 we have the wanted syscall number. Arguments come in r10,r11,r12, + ;; r13,mof,srp + ;; + ;; This function looks on the _surface_ like spaghetti programming, but it's + ;; really designed so that the fast-path does not force cache-loading of + ;; non-used instructions. Only the non-common cases cause the outlined code + ;; to run.. + +system_call: + ;; Stack-frame similar to the irq heads, which is reversed in + ;; ret_from_sys_call. + subq 12, $sp ; Skip EXS, EDA. + move $erp, [$sp] + subq 4, $sp + move $srp, [$sp] + subq 4, $sp + move $ccs, [$sp] + subq 4, $sp + ei ; Allow IRQs while handling system call + move $spc, [$sp] + subq 4, $sp + move $mof, [$sp] + subq 4, $sp + move $srs, [$sp] + subq 4, $sp + move.d $acr, [$sp] + subq 14*4, $sp ; Make room for R0-R13. + movem $r13, [$sp] ; Push R0-R13 + subq 4, $sp + move.d $r10, [$sp] ; Push orig_r10. + +; Set S-bit when kernel debugging to keep hardware breakpoints active. +#ifdef CONFIG_ETRAX_KGDB + move $ccs, $r0 + or.d (1<<9), $r0 + move $r0, $ccs +#endif + + movs.w -ENOSYS, $r0 + addoq +PT_r10, $sp, $acr + move.d $r0, [$acr] + + ;; Check if this process is syscall-traced. + movs.w -8192, $r0 ; THREAD_SIZE == 8192 + and.d $sp, $r0 + + addoq +TI_flags, $r0, $acr + move.d [$acr], $r0 + btstq TIF_SYSCALL_TRACE, $r0 + bmi _syscall_trace_entry + nop + +_syscall_traced: + ;; Check for sanity in the requested syscall number. + cmpu.w NR_syscalls, $r9 + bhs ret_from_sys_call + lslq 2, $r9 ; Multiply by 4, in the delay slot. + + ;; The location on the stack for the register structure is passed as a + ;; seventh argument. Some system calls need this. + move.d $sp, $r0 + subq 4, $sp + move.d $r0, [$sp] + + ;; The registers carrying parameters (R10-R13) are intact. The optional + ;; fifth and sixth parameters is in MOF and SRP respectivly. Put them + ;; back on the stack. + subq 4, $sp + move $srp, [$sp] + subq 4, $sp + move $mof, [$sp] + + ;; Actually to the system call. + addo.d +sys_call_table, $r9, $acr + move.d [$acr], $acr + jsr $acr + nop + + addq 3*4, $sp ; Pop the mof, srp and regs parameters. + addoq +PT_r10, $sp, $acr + move.d $r10, [$acr] ; Save the return value. + + moveq 1, $r9 ; "Parameter" to ret_from_sys_call to + ; show it was a sys call. + + ;; Fall through into ret_from_sys_call to return. + +ret_from_sys_call: + ;; R9 is a parameter: + ;; >= 1 from syscall + ;; 0 from irq + + ;; Get the current task-struct pointer. + movs.w -8192, $r0 ; THREAD_SIZE == 8192 + and.d $sp, $r0 + + di ; Make sure need_resched and sigpending don't change. + + addoq +TI_flags, $r0, $acr + move.d [$acr], $r1 + and.d _TIF_ALLWORK_MASK, $r1 + bne _syscall_exit_work + nop + +_Rexit: + ;; This epilogue MUST match the prologues in multiple_interrupt, irq.h + ;; and ptregs.h. + addq 4, $sp ; Skip orig_r10. + movem [$sp+], $r13 ; Registers R0-R13. + move.d [$sp+], $acr + move [$sp], $srs + addq 4, $sp + move [$sp+], $mof + move [$sp+], $spc + move [$sp+], $ccs + move [$sp+], $srp + move [$sp+], $erp + addq 8, $sp ; Skip EXS, EDA. + jump $erp + rfe ; Restore condition code stack in delay-slot. + + ;; We get here after doing a syscall if extra work might need to be done + ;; perform syscall exit tracing if needed. + +_syscall_exit_work: + ;; R0 contains current at this point and irq's are disabled. + + addoq +TI_flags, $r0, $acr + move.d [$acr], $r1 + btstq TIF_SYSCALL_TRACE, $r1 + bpl _work_pending + nop + ei + move.d $r9, $r1 ; Preserve R9. + jsr do_syscall_trace + nop + move.d $r1, $r9 + ba _resume_userspace + nop + +_work_pending: + addoq +TI_flags, $r0, $acr + move.d [$acr], $r10 + btstq TIF_NEED_RESCHED, $r10 ; Need resched? + bpl _work_notifysig ; No, must be signal/notify. + nop + +_work_resched: + move.d $r9, $r1 ; Preserve R9. + jsr schedule + nop + move.d $r1, $r9 + di + + addoq +TI_flags, $r0, $acr + move.d [$acr], $r1 + and.d _TIF_WORK_MASK, $r1 ; Ignore sycall trace counter. + beq _Rexit + nop + btstq TIF_NEED_RESCHED, $r1 + bmi _work_resched ; current->work.need_resched. + nop + +_work_notifysig: + ;; Deal with pending signals and notify-resume requests. + + addoq +TI_flags, $r0, $acr + move.d [$acr], $r13 ; The thread_info_flags parameter. + move.d $r9, $r10 ; do_notify_resume syscall/irq param. + moveq 0, $r11 ; oldset param - 0 in this case. + move.d $sp, $r12 ; The regs param. + jsr do_notify_resume + nop + + ba _Rexit + nop + + ;; We get here as a sidetrack when we've entered a syscall with the + ;; trace-bit set. We need to call do_syscall_trace and then continue + ;; with the call. + +_syscall_trace_entry: + ;; PT_r10 in the frame contains -ENOSYS as required, at this point. + + jsr do_syscall_trace + nop + + ;; Now re-enter the syscall code to do the syscall itself. We need to + ;; restore R9 here to contain the wanted syscall, and the other + ;; parameter-bearing registers. + addoq +PT_r9, $sp, $acr + move.d [$acr], $r9 + addoq +PT_orig_r10, $sp, $acr + move.d [$acr], $r10 ; PT_r10 is already -ENOSYS. + addoq +PT_r11, $sp, $acr + move.d [$acr], $r11 + addoq +PT_r12, $sp, $acr + move.d [$acr], $r12 + addoq +PT_r13, $sp, $acr + move.d [$acr], $r13 + addoq +PT_mof, $sp, $acr + move [$acr], $mof + addoq +PT_srp, $sp, $acr + move [$acr], $srp + + ba _syscall_traced + nop + + ;; Resume performs the actual task-switching, by switching stack + ;; pointers. Input arguments are: + ;; + ;; R10 = prev + ;; R11 = next + ;; R12 = thread offset in task struct. + ;; + ;; Returns old current in R10. + +resume: + subq 4, $sp + move $srp, [$sp] ; Keep old/new PC on the stack. + add.d $r12, $r10 ; R10 = current tasks tss. + addoq +THREAD_ccs, $r10, $acr + move $ccs, [$acr] ; Save IRQ enable state. + di + + addoq +THREAD_usp, $r10, $acr + move $usp, [$acr] ; Save user-mode stackpointer. + + ;; See copy_thread for the reason why register R9 is saved. + subq 10*4, $sp + movem $r9, [$sp] ; Save non-scratch registers and R9. + + addoq +THREAD_ksp, $r10, $acr + move.d $sp, [$acr] ; Save kernel SP for old task. + + move.d $sp, $r10 ; Return last running task in R10. + and.d -8192, $r10 ; Get thread_info from stackpointer. + addoq +TI_task, $r10, $acr + move.d [$acr], $r10 ; Get task. + add.d $r12, $r11 ; Find the new tasks tss. + addoq +THREAD_ksp, $r11, $acr + move.d [$acr], $sp ; Switch to new stackframe. + movem [$sp+], $r9 ; Restore non-scratch registers and R9. + + addoq +THREAD_usp, $r11, $acr + move [$acr], $usp ; Restore user-mode stackpointer. + + addoq +THREAD_ccs, $r11, $acr + move [$acr], $ccs ; Restore IRQ enable status. + move.d [$sp+], $acr + jump $acr ; Restore PC. + nop + +nmi_interrupt: + +;; If we receive a watchdog interrupt while it is not expected, then set +;; up a canonical frame and dump register contents before dying. + + ;; This prologue MUST match the one in irq.h and the struct in ptregs.h! + subq 12, $sp ; Skip EXS, EDA. + move $nrp, [$sp] + subq 4, $sp + move $srp, [$sp] + subq 4, $sp + move $ccs, [$sp] + subq 4, $sp + move $spc, [$sp] + subq 4, $sp + move $mof, [$sp] + subq 4, $sp + move $srs, [$sp] + subq 4, $sp + move.d $acr, [$sp] + subq 14*4, $sp ; Make room for R0-R13. + movem $r13, [$sp] ; Push R0-R13. + subq 4, $sp + move.d $r10, [$sp] ; Push orig_r10. + move.d REG_ADDR(intr_vect, regi_irq, r_nmi), $r0 + move.d [$r0], $r0 + btstq REG_BIT(intr_vect, r_nmi, watchdog), $r0 + bpl 1f + nop + jsr handle_watchdog_bite ; In time.c. + move.d $sp, $r10 ; Pointer to registers +1: btstq REG_BIT(intr_vect, r_nmi, ext), $r0 + bpl 1f + nop + jsr handle_nmi + move.d $sp, $r10 ; Pointer to registers +1: addq 4, $sp ; Skip orig_r10 + movem [$sp+], $r13 + move.d [$sp+], $acr + move [$sp], $srs + addq 4, $sp + move [$sp+], $mof + move [$sp+], $spc + move [$sp+], $ccs + move [$sp+], $srp + move [$sp+], $nrp + addq 8, $sp ; Skip EXS, EDA. + jump $nrp + rfn + + .comm cause_of_death, 4 ;; Don't declare this anywhere. + +spurious_interrupt: + di + jump hard_reset_now + nop + + ;; This handles the case when multiple interrupts arrive at the same + ;; time. Jump to the first set interrupt bit in a priotiry fashion. The + ;; hardware will call the unserved interrupts after the handler + ;; finishes. +multiple_interrupt: + ;; This prologue MUST match the one in irq.h and the struct in ptregs.h! + subq 12, $sp ; Skip EXS, EDA. + move $erp, [$sp] + subq 4, $sp + move $srp, [$sp] + subq 4, $sp + move $ccs, [$sp] + subq 4, $sp + move $spc, [$sp] + subq 4, $sp + move $mof, [$sp] + subq 4, $sp + move $srs, [$sp] + subq 4, $sp + move.d $acr, [$sp] + subq 14*4, $sp ; Make room for R0-R13. + movem $r13, [$sp] ; Push R0-R13. + subq 4, $sp + move.d $r10, [$sp] ; Push orig_r10. + +; Set S-bit when kernel debugging to keep hardware breakpoints active. +#ifdef CONFIG_ETRAX_KGDB + move $ccs, $r0 + or.d (1<<9), $r0 + move $r0, $ccs +#endif + + jsr crisv32_do_multiple + move.d $sp, $r10 + jump ret_from_intr + nop + +do_sigtrap: + ;; Sigtraps the process that executed the BREAK instruction. Creates a + ;; frame that Rexit expects. + subq 4, $sp + move $eda, [$sp] + subq 4, $sp + move $exs, [$sp] + subq 4, $sp + move $erp, [$sp] + subq 4, $sp + move $srp, [$sp] + subq 4, $sp + move $ccs, [$sp] + subq 4, $sp + move $spc, [$sp] + subq 4, $sp + move $mof, [$sp] + subq 4, $sp + move $srs, [$sp] + subq 4, $sp + move.d $acr, [$sp] + di ; Need to disable irq's at this point. + subq 14*4, $sp ; Make room for r0-r13. + movem $r13, [$sp] ; Push the r0-r13 registers. + subq 4, $sp + move.d $r10, [$sp] ; Push orig_r10. + + movs.w -8192, $r9 ; THREAD_SIZE == 8192 + and.d $sp, $r9 + + ;; thread_info as first parameter + move.d $r9, $r10 + moveq 5, $r11 ; SIGTRAP as second argument. + jsr ugdb_trap_user + nop + jump ret_from_intr ; Use the return routine for interrupts. + nop + +gdb_handle_exception: + subq 4, $sp + move.d $r0, [$sp] +#ifdef CONFIG_ETRAX_KGDB + move $ccs, $r0 ; U-flag not affected by previous insns. + btstq 16, $r0 ; Test the U-flag. + bmi _ugdb_handle_exception ; Go to user mode debugging. + nop ; Empty delay-slot (cannot pop R0 here). + ba kgdb_handle_exception ; Go to kernel debugging. + move.d [$sp+], $r0 ; Restore R0 in delay slot. +#endif + +_ugdb_handle_exception: + ba do_sigtrap ; SIGTRAP the offending process. + move.d [$sp+], $r0 ; Restore R0 in delay slot. + + .data + + .section .rodata,"a" +sys_call_table: + .long sys_restart_syscall ; 0 - old "setup()" system call, used + ; for restarting. + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown16 + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid16 + .long sys_getuid16 + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid16 + .long sys_getgid16 + .long sys_signal + .long sys_geteuid16 + .long sys_getegid16 /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys( */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall /* old sys_olduname holder */ + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid16 /* 70 */ + .long sys_setregid16 + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_old_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups16 /* 80 */ + .long sys_setgroups16 + .long sys_select /* was old_select in Linux/E100 */ + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long old_readdir + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown16 /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* sys_ioperm in i386 */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_ni_syscall /* old sys_uname holder */ + .long sys_ni_syscall /* sys_iopl in i386 */ + .long sys_vhangup + .long sys_ni_syscall /* old "idle" system call */ + .long sys_ni_syscall /* vm86old in i386 */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* sys_modify_ldt */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* old "create_module" */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130: old "get_kernel_syms" */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid16 + .long sys_setfsgid16 + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid16 + .long sys_getresuid16 /* 165 */ + .long sys_ni_syscall /* sys_vm86 */ + .long sys_ni_syscall /* Old sys_query_module */ + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid16 /* 170 */ + .long sys_getresgid16 + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_chown16 + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_lchown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_chown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_mincore + .long sys_madvise + .long sys_getdents64 /* 220 */ + .long sys_fcntl64 + .long sys_ni_syscall /* reserved for TUX */ + .long sys_ni_syscall + .long sys_gettid + .long sys_readahead /* 225 */ + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr /* 230 */ + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr /* 235 */ + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_tkill + .long sys_sendfile64 + .long sys_futex /* 240 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall /* sys_set_thread_area */ + .long sys_ni_syscall /* sys_get_thread_area */ + .long sys_io_setup /* 245 */ + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel + .long sys_fadvise64 /* 250 */ + .long sys_ni_syscall + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 255 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 270 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_ni_syscall /* sys_vserver */ + .long sys_ni_syscall /* sys_mbind */ + .long sys_ni_syscall /* 275 sys_get_mempolicy */ + .long sys_ni_syscall /* sys_set_mempolicy */ + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive /* 280 */ + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_ni_syscall /* reserved for kexec */ + .long sys_waitid + + /* + * NOTE!! This doesn't have to be exact - we just have + * to make sure we have _enough_ of the "sys_ni_syscall" + * entries. Don't panic if you notice that this hasn't + * been shrunk every time we add a new system call. + */ + + .rept NR_syscalls - (.-sys_call_table) / 4 + .long sys_ni_syscall + .endr + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/fasttimer.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,996 @@ +/* $Id: fasttimer.c,v 1.11 2005/01/04 11:15:46 starvik Exp $ + * linux/arch/cris/kernel/fasttimer.c + * + * Fast timers for ETRAX FS + * This may be useful in other OS than Linux so use 2 space indentation... + * + * $Log: fasttimer.c,v $ + * Revision 1.11 2005/01/04 11:15:46 starvik + * Don't share timer IRQ. + * + * Revision 1.10 2004/12/07 09:19:38 starvik + * Corrected includes. + * Use correct interrupt macros. + * + * Revision 1.9 2004/05/14 10:18:58 starvik + * Export fast_timer_list + * + * Revision 1.8 2004/05/14 07:58:03 starvik + * Merge of changes from 2.4 + * + * Revision 1.7 2003/07/10 12:06:14 starvik + * Return IRQ_NONE if irq wasn't handled + * + * Revision 1.6 2003/07/04 08:27:49 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.5 2003/06/05 10:16:22 johana + * New INTR_VECT macros. + * + * Revision 1.4 2003/06/03 08:49:45 johana + * Fixed typo. + * + * Revision 1.3 2003/06/02 12:51:27 johana + * Now compiles. + * Commented some include files that probably can be removed. + * + * Revision 1.2 2003/06/02 12:09:41 johana + * Ported to ETRAX FS using the trig interrupt instead of timer1. + * + * Revision 1.3 2002/12/12 08:26:32 starvik + * Don't use C-comments inside CVS comments + * + * Revision 1.2 2002/12/11 15:42:02 starvik + * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ + * + * Revision 1.1 2002/11/18 07:58:06 starvik + * Fast timers (from Linux 2.4) + * + * Revision 1.5 2002/10/15 06:21:39 starvik + * Added call to init_waitqueue_head + * + * Revision 1.4 2002/05/28 17:47:59 johana + * Added del_fast_timer() + * + * Revision 1.3 2002/05/28 16:16:07 johana + * Handle empty fast_timer_list + * + * Revision 1.2 2002/05/27 15:38:42 johana + * Made it compile without warnings on Linux 2.4. + * (includes, wait_queue, PROC_FS and snprintf) + * + * Revision 1.1 2002/05/27 15:32:25 johana + * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree. + * + * Revision 1.8 2001/11/27 13:50:40 pkj + * Disable interrupts while stopping the timer and while modifying the + * list of active timers in timer1_handler() as it may be interrupted + * by other interrupts (e.g., the serial interrupt) which may add fast + * timers. + * + * Revision 1.7 2001/11/22 11:50:32 pkj + * * Only store information about the last 16 timers. + * * proc_fasttimer_read() now uses an allocated buffer, since it + * requires more space than just a page even for only writing the + * last 16 timers. The buffer is only allocated on request, so + * unless /proc/fasttimer is read, it is never allocated. + * * Renamed fast_timer_started to fast_timers_started to match + * fast_timers_added and fast_timers_expired. + * * Some clean-up. + * + * Revision 1.6 2000/12/13 14:02:08 johana + * Removed volatile for fast_timer_list + * + * Revision 1.5 2000/12/13 13:55:35 johana + * Added DEBUG_LOG, added som cli() and cleanup + * + * Revision 1.4 2000/12/05 13:48:50 johana + * Added range check when writing proc file, modified timer int handling + * + * Revision 1.3 2000/11/23 10:10:20 johana + * More debug/logging possibilities. + * Moved GET_JIFFIES_USEC() to timex.h and time.c + * + * Revision 1.2 2000/11/01 13:41:04 johana + * Clean up and bugfixes. + * Created new do_gettimeofday_fast() that gets a timeval struct + * with time based on jiffies and *R_TIMER0_DATA, uses a table + * for fast conversion of timer value to microseconds. + * (Much faster the standard do_gettimeofday() and we don't really + * wan't to use the true time - we wan't the "uptime" so timers don't screw up + * when we change the time. + * TODO: Add efficient support for continuous timers as well. + * + * Revision 1.1 2000/10/26 15:49:16 johana + * Added fasttimer, highresolution timers. + * + * Copyright (C) 2000,2001 2002, 2003 Axis Communications AB, Lund, Sweden + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* + * timer0 is running at 100MHz and generating jiffies timer ticks + * at 100 or 1000 HZ. + * fasttimer gives an API that gives timers that expire "between" the jiffies + * giving microsecond resolution (10 ns). + * fasttimer uses reg_timer_rw_trig register to get interrupt when + * r_time reaches a certain value. + */ + + +#define DEBUG_LOG_INCLUDED +#define FAST_TIMER_LOG +//#define FAST_TIMER_TEST + +#define FAST_TIMER_SANITY_CHECKS + +#ifdef FAST_TIMER_SANITY_CHECKS +#define SANITYCHECK(x) x +static int sanity_failed = 0; +#else +#define SANITYCHECK(x) +#endif + +#define D1(x) +#define D2(x) +#define DP(x) + +#define __INLINE__ inline + +static int fast_timer_running = 0; +static int fast_timers_added = 0; +static int fast_timers_started = 0; +static int fast_timers_expired = 0; +static int fast_timers_deleted = 0; +static int fast_timer_is_init = 0; +static int fast_timer_ints = 0; + +struct fast_timer *fast_timer_list = NULL; + +#ifdef DEBUG_LOG_INCLUDED +#define DEBUG_LOG_MAX 128 +static const char * debug_log_string[DEBUG_LOG_MAX]; +static unsigned long debug_log_value[DEBUG_LOG_MAX]; +static int debug_log_cnt = 0; +static int debug_log_cnt_wrapped = 0; + +#define DEBUG_LOG(string, value) \ +{ \ + unsigned long log_flags; \ + local_irq_save(log_flags); \ + debug_log_string[debug_log_cnt] = (string); \ + debug_log_value[debug_log_cnt] = (unsigned long)(value); \ + if (++debug_log_cnt >= DEBUG_LOG_MAX) \ + { \ + debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \ + debug_log_cnt_wrapped = 1; \ + } \ + local_irq_restore(log_flags); \ +} +#else +#define DEBUG_LOG(string, value) +#endif + + +#define NUM_TIMER_STATS 16 +#ifdef FAST_TIMER_LOG +struct fast_timer timer_added_log[NUM_TIMER_STATS]; +struct fast_timer timer_started_log[NUM_TIMER_STATS]; +struct fast_timer timer_expired_log[NUM_TIMER_STATS]; +#endif + +int timer_div_settings[NUM_TIMER_STATS]; +int timer_delay_settings[NUM_TIMER_STATS]; + + +static void +timer_trig_handler(void); + + + +/* Not true gettimeofday, only checks the jiffies (uptime) + useconds */ +void __INLINE__ do_gettimeofday_fast(struct timeval *tv) +{ + unsigned long sec = jiffies; + unsigned long usec = GET_JIFFIES_USEC(); + + usec += (sec % HZ) * (1000000 / HZ); + sec = sec / HZ; + + if (usec > 1000000) + { + usec -= 1000000; + sec++; + } + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1) +{ + if (t0->tv_sec < t1->tv_sec) + { + return -1; + } + else if (t0->tv_sec > t1->tv_sec) + { + return 1; + } + if (t0->tv_usec < t1->tv_usec) + { + return -1; + } + else if (t0->tv_usec > t1->tv_usec) + { + return 1; + } + return 0; +} + +/* Called with ints off */ +void __INLINE__ start_timer_trig(unsigned long delay_us) +{ + reg_timer_rw_ack_intr ack_intr = { 0 }; + reg_timer_rw_intr_mask intr_mask; + reg_timer_rw_trig trig; + reg_timer_rw_trig_cfg trig_cfg = { 0 }; + reg_timer_r_time r_time; + + r_time = REG_RD(timer, regi_timer, r_time); + + D1(printk("start_timer_trig : %d us freq: %i div: %i\n", + delay_us, freq_index, div)); + /* Clear trig irq */ + intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask.trig = 0; + REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + + /* Set timer values */ + /* r_time is 100MHz (10 ns resolution) */ + trig = r_time + delay_us*(1000/10); + + timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = trig; + timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us; + + /* Ack interrupt */ + ack_intr.trig = 1; + REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); + + /* Start timer */ + REG_WR(timer, regi_timer, rw_trig, trig); + trig_cfg.tmr = regk_timer_time; + REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); + + /* Check if we have already passed the trig time */ + r_time = REG_RD(timer, regi_timer, r_time); + if (r_time < trig) { + /* No, Enable trig irq */ + intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask.trig = 1; + REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + fast_timers_started++; + fast_timer_running = 1; + } + else + { + /* We have passed the time, disable trig point, ack intr */ + trig_cfg.tmr = regk_timer_off; + REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); + /* call the int routine directly */ + timer_trig_handler(); + } + +} + +/* In version 1.4 this function takes 27 - 50 us */ +void start_one_shot_timer(struct fast_timer *t, + fast_timer_function_type *function, + unsigned long data, + unsigned long delay_us, + const char *name) +{ + unsigned long flags; + struct fast_timer *tmp; + + D1(printk("sft %s %d us\n", name, delay_us)); + + local_irq_save(flags); + + do_gettimeofday_fast(&t->tv_set); + tmp = fast_timer_list; + + SANITYCHECK({ /* Check so this is not in the list already... */ + while (tmp != NULL) + { + if (tmp == t) + { + printk("timer name: %s data: 0x%08lX already in list!\n", name, data); + sanity_failed++; + return; + } + else + { + tmp = tmp->next; + } + } + tmp = fast_timer_list; + }); + + t->delay_us = delay_us; + t->function = function; + t->data = data; + t->name = name; + + t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000; + t->tv_expires.tv_sec = t->tv_set.tv_sec + delay_us / 1000000; + if (t->tv_expires.tv_usec > 1000000) + { + t->tv_expires.tv_usec -= 1000000; + t->tv_expires.tv_sec++; + } +#ifdef FAST_TIMER_LOG + timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t; +#endif + fast_timers_added++; + + /* Check if this should timeout before anything else */ + if (tmp == NULL || timeval_cmp(&t->tv_expires, &tmp->tv_expires) < 0) + { + /* Put first in list and modify the timer value */ + t->prev = NULL; + t->next = fast_timer_list; + if (fast_timer_list) + { + fast_timer_list->prev = t; + } + fast_timer_list = t; +#ifdef FAST_TIMER_LOG + timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; +#endif + start_timer_trig(delay_us); + } else { + /* Put in correct place in list */ + while (tmp->next && + timeval_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0) + { + tmp = tmp->next; + } + /* Insert t after tmp */ + t->prev = tmp; + t->next = tmp->next; + if (tmp->next) + { + tmp->next->prev = t; + } + tmp->next = t; + } + + D2(printk("start_one_shot_timer: %d us done\n", delay_us)); + + local_irq_restore(flags); +} /* start_one_shot_timer */ + +static inline int fast_timer_pending (const struct fast_timer * t) +{ + return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list); +} + +static inline int detach_fast_timer (struct fast_timer *t) +{ + struct fast_timer *next, *prev; + if (!fast_timer_pending(t)) + return 0; + next = t->next; + prev = t->prev; + if (next) + next->prev = prev; + if (prev) + prev->next = next; + else + fast_timer_list = next; + fast_timers_deleted++; + return 1; +} + +int del_fast_timer(struct fast_timer * t) +{ + unsigned long flags; + int ret; + + local_irq_save(flags); + ret = detach_fast_timer(t); + t->next = t->prev = NULL; + local_irq_restore(flags); + return ret; +} /* del_fast_timer */ + + +/* Interrupt routines or functions called in interrupt context */ + +/* Timer interrupt handler for trig interrupts */ + +static irqreturn_t +timer_trig_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + reg_timer_r_masked_intr masked_intr; + + /* Check if the timer interrupt is for us (a trig int) */ + masked_intr = REG_RD(timer, regi_timer, r_masked_intr); + if (!masked_intr.trig) + return IRQ_NONE; + timer_trig_handler(); + return IRQ_HANDLED; +} + +static void timer_trig_handler(void) +{ + reg_timer_rw_ack_intr ack_intr = { 0 }; + reg_timer_rw_intr_mask intr_mask; + reg_timer_rw_trig_cfg trig_cfg = { 0 }; + struct fast_timer *t; + unsigned long flags; + + local_irq_save(flags); + + /* Clear timer trig interrupt */ + intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask.trig = 0; + REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + + /* First stop timer, then ack interrupt */ + /* Stop timer */ + trig_cfg.tmr = regk_timer_off; + REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); + + /* Ack interrupt */ + ack_intr.trig = 1; + REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); + + fast_timer_running = 0; + fast_timer_ints++; + + local_irq_restore(flags); + + t = fast_timer_list; + while (t) + { + struct timeval tv; + + /* Has it really expired? */ + do_gettimeofday_fast(&tv); + D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec)); + + if (timeval_cmp(&t->tv_expires, &tv) <= 0) + { + /* Yes it has expired */ +#ifdef FAST_TIMER_LOG + timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t; +#endif + fast_timers_expired++; + + /* Remove this timer before call, since it may reuse the timer */ + local_irq_save(flags); + if (t->prev) + { + t->prev->next = t->next; + } + else + { + fast_timer_list = t->next; + } + if (t->next) + { + t->next->prev = t->prev; + } + t->prev = NULL; + t->next = NULL; + local_irq_restore(flags); + + if (t->function != NULL) + { + t->function(t->data); + } + else + { + DEBUG_LOG("!trimertrig %i function==NULL!\n", fast_timer_ints); + } + } + else + { + /* Timer is to early, let's set it again using the normal routines */ + D1(printk(".\n")); + } + + local_irq_save(flags); + if ((t = fast_timer_list) != NULL) + { + /* Start next timer.. */ + long us; + struct timeval tv; + + do_gettimeofday_fast(&tv); + us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 + + t->tv_expires.tv_usec - tv.tv_usec); + if (us > 0) + { + if (!fast_timer_running) + { +#ifdef FAST_TIMER_LOG + timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; +#endif + start_timer_trig(us); + } + local_irq_restore(flags); + break; + } + else + { + /* Timer already expired, let's handle it better late than never. + * The normal loop handles it + */ + D1(printk("e! %d\n", us)); + } + } + local_irq_restore(flags); + } + + if (!t) + { + D1(printk("ttrig stop!\n")); + } +} + +static void wake_up_func(unsigned long data) +{ +#ifdef DECLARE_WAITQUEUE + wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data; +#else + struct wait_queue **sleep_wait_p = (struct wait_queue **)data; +#endif + wake_up(sleep_wait_p); +} + + +/* Useful API */ + +void schedule_usleep(unsigned long us) +{ + struct fast_timer t; +#ifdef DECLARE_WAITQUEUE + wait_queue_head_t sleep_wait; + init_waitqueue_head(&sleep_wait); + { + DECLARE_WAITQUEUE(wait, current); +#else + struct wait_queue *sleep_wait = NULL; + struct wait_queue wait = { current, NULL }; +#endif + + D1(printk("schedule_usleep(%d)\n", us)); + add_wait_queue(&sleep_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us, + "usleep"); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&sleep_wait, &wait); + D1(printk("done schedule_usleep(%d)\n", us)); +#ifdef DECLARE_WAITQUEUE + } +#endif +} + +#ifdef CONFIG_PROC_FS +static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + ,int *eof, void *data_unused +#else + ,int unused +#endif + ); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +static struct proc_dir_entry *fasttimer_proc_entry; +#else +static struct proc_dir_entry fasttimer_proc_entry = +{ + 0, 9, "fasttimer", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, NULL /* ops -- default to array */, + &proc_fasttimer_read /* get_info */, +}; +#endif +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_PROC_FS + +/* This value is very much based on testing */ +#define BIG_BUF_SIZE (500 + NUM_TIMER_STATS * 300) + +static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + ,int *eof, void *data_unused +#else + ,int unused +#endif + ) +{ + unsigned long flags; + int i = 0; + int num_to_show; + struct timeval tv; + struct fast_timer *t, *nextt; + static char *bigbuf = NULL; + static unsigned long used; + + if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE))) + { + used = 0; + bigbuf[0] = '\0'; + return 0; + } + + if (!offset || !used) + { + do_gettimeofday_fast(&tv); + + used = 0; + used += sprintf(bigbuf + used, "Fast timers added: %i\n", + fast_timers_added); + used += sprintf(bigbuf + used, "Fast timers started: %i\n", + fast_timers_started); + used += sprintf(bigbuf + used, "Fast timer interrupts: %i\n", + fast_timer_ints); + used += sprintf(bigbuf + used, "Fast timers expired: %i\n", + fast_timers_expired); + used += sprintf(bigbuf + used, "Fast timers deleted: %i\n", + fast_timers_deleted); + used += sprintf(bigbuf + used, "Fast timer running: %s\n", + fast_timer_running ? "yes" : "no"); + used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n", + (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_usec); +#ifdef FAST_TIMER_SANITY_CHECKS + used += sprintf(bigbuf + used, "Sanity failed: %i\n", + sanity_failed); +#endif + used += sprintf(bigbuf + used, "\n"); + +#ifdef DEBUG_LOG_INCLUDED + { + int end_i = debug_log_cnt; + i = 0; + + if (debug_log_cnt_wrapped) + { + i = debug_log_cnt; + } + + while ((i != end_i || (debug_log_cnt_wrapped && !used)) && + used+100 < BIG_BUF_SIZE) + { + used += sprintf(bigbuf + used, debug_log_string[i], + debug_log_value[i]); + i = (i+1) % DEBUG_LOG_MAX; + } + } + used += sprintf(bigbuf + used, "\n"); +#endif + + num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started: + NUM_TIMER_STATS); + used += sprintf(bigbuf + used, "Timers started: %i\n", fast_timers_started); + for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE) ; i++) + { + int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS; + +#if 1 //ndef FAST_TIMER_LOG + used += sprintf(bigbuf + used, "div: %i delay: %i" + "\n", + timer_div_settings[cur], + timer_delay_settings[cur] + ); +#endif +#ifdef FAST_TIMER_LOG + t = &timer_started_log[cur]; + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data + ); +#endif + } + used += sprintf(bigbuf + used, "\n"); + +#ifdef FAST_TIMER_LOG + num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added: + NUM_TIMER_STATS); + used += sprintf(bigbuf + used, "Timers added: %i\n", fast_timers_added); + for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++) + { + t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS]; + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data + ); + } + used += sprintf(bigbuf + used, "\n"); + + num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired: + NUM_TIMER_STATS); + used += sprintf(bigbuf + used, "Timers expired: %i\n", fast_timers_expired); + for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++) + { + t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS]; + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data + ); + } + used += sprintf(bigbuf + used, "\n"); +#endif + + used += sprintf(bigbuf + used, "Active timers:\n"); + local_irq_save(flags); + local_irq_save(flags); + t = fast_timer_list; + while (t != NULL && (used+100 < BIG_BUF_SIZE)) + { + nextt = t->next; + local_irq_restore(flags); + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" +/* " func: 0x%08lX" */ + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data +/* , t->function */ + ); + local_irq_disable(); + if (t->next != nextt) + { + printk("timer removed!\n"); + } + t = nextt; + } + local_irq_restore(flags); + } + + if (used - offset < len) + { + len = used - offset; + } + + memcpy(buf, bigbuf + offset, len); + *start = buf; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + *eof = 1; +#endif + + return len; +} +#endif /* PROC_FS */ + +#ifdef FAST_TIMER_TEST +static volatile unsigned long i = 0; +static volatile int num_test_timeout = 0; +static struct fast_timer tr[10]; +static int exp_num[10]; + +static struct timeval tv_exp[100]; + +static void test_timeout(unsigned long data) +{ + do_gettimeofday_fast(&tv_exp[data]); + exp_num[data] = num_test_timeout; + + num_test_timeout++; +} + +static void test_timeout1(unsigned long data) +{ + do_gettimeofday_fast(&tv_exp[data]); + exp_num[data] = num_test_timeout; + if (data < 7) + { + start_one_shot_timer(&tr[i], test_timeout1, i, 1000, "timeout1"); + i++; + } + num_test_timeout++; +} + +DP( +static char buf0[2000]; +static char buf1[2000]; +static char buf2[2000]; +static char buf3[2000]; +static char buf4[2000]; +); + +static char buf5[6000]; +static int j_u[1000]; + +static void fast_timer_test(void) +{ + int prev_num; + int j; + + struct timeval tv, tv0, tv1, tv2; + + printk("fast_timer_test() start\n"); + do_gettimeofday_fast(&tv); + + for (j = 0; j < 1000; j++) + { + j_u[j] = GET_JIFFIES_USEC(); + } + for (j = 0; j < 100; j++) + { + do_gettimeofday_fast(&tv_exp[j]); + } + printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec); + + for (j = 0; j < 1000; j++) + { + printk("%i %i %i %i %i\n",j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]); + j += 4; + } + for (j = 0; j < 100; j++) + { + printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n", + tv_exp[j].tv_sec,tv_exp[j].tv_usec, + tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec, + tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec, + tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec, + tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec); + j += 4; + } + do_gettimeofday_fast(&tv0); + start_one_shot_timer(&tr[i], test_timeout, i, 50000, "test0"); + DP(proc_fasttimer_read(buf0, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout, i, 70000, "test1"); + DP(proc_fasttimer_read(buf1, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout, i, 40000, "test2"); + DP(proc_fasttimer_read(buf2, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout, i, 60000, "test3"); + DP(proc_fasttimer_read(buf3, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout1, i, 55000, "test4xx"); + DP(proc_fasttimer_read(buf4, NULL, 0, 0, 0)); + i++; + do_gettimeofday_fast(&tv1); + + proc_fasttimer_read(buf5, NULL, 0, 0, 0); + + prev_num = num_test_timeout; + while (num_test_timeout < i) + { + if (num_test_timeout != prev_num) + { + prev_num = num_test_timeout; + } + } + do_gettimeofday_fast(&tv2); + printk("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec); + printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec); + printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec); + DP(printk("buf0:\n"); + printk(buf0); + printk("buf1:\n"); + printk(buf1); + printk("buf2:\n"); + printk(buf2); + printk("buf3:\n"); + printk(buf3); + printk("buf4:\n"); + printk(buf4); + ); + printk("buf5:\n"); + printk(buf5); + + printk("timers set:\n"); + for(j = 0; jname, + t->tv_set.tv_sec, + t->tv_set.tv_usec, + t->tv_expires.tv_sec, + t->tv_expires.tv_usec, + t->data, + t->function + ); + + printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n", + t->delay_us, + tv_exp[j].tv_sec, + tv_exp[j].tv_usec, + exp_num[j], + (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); + } + proc_fasttimer_read(buf5, NULL, 0, 0, 0); + printk("buf5 after all done:\n"); + printk(buf5); + printk("fast_timer_test() done\n"); +} +#endif + + +void fast_timer_init(void) +{ + /* For some reason, request_irq() hangs when called froom time_init() */ + if (!fast_timer_is_init) + { + printk("fast_timer_init()\n"); + +#ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 ))) + fasttimer_proc_entry->read_proc = proc_fasttimer_read; +#else + proc_register_dynamic(&proc_root, &fasttimer_proc_entry); +#endif +#endif /* PROC_FS */ + if(request_irq(TIMER_INTR_VECT, timer_trig_interrupt, SA_INTERRUPT, + "fast timer int", NULL)) + { + printk("err: timer1 irq\n"); + } + fast_timer_is_init = 1; +#ifdef FAST_TIMER_TEST + printk("do test\n"); + fast_timer_test(); +#endif + } +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/head.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,448 @@ +/* + * CRISv32 kernel startup code. + * + * Copyright (C) 2003, Axis Communications AB + */ + +#include + +#define ASSEMBLER_MACROS_ONLY + +/* + * The macros found in mmu_defs_asm.h uses the ## concatenation operator, so + * -traditional must not be used when assembling this file. + */ +#include +#include +#include +#include +#include + +#define CRAMFS_MAGIC 0x28cd3d45 +#define RAM_INIT_MAGIC 0x56902387 +#define COMMAND_LINE_MAGIC 0x87109563 + + ;; NOTE: R8 and R9 carry information from the decompressor (if the + ;; kernel was compressed). They must not be used in the code below + ;; until they are read! + + ;; Exported symbols. + .global etrax_irv + .global romfs_start + .global romfs_length + .global romfs_in_flash + .global swapper_pg_dir + .global crisv32_nand_boot + .global crisv32_nand_cramfs_offset + + ;; Dummy section to make it bootable with current VCS simulator +#ifdef CONFIG_ETRAXFS_SIM + .section ".boot", "ax" + ba tstart + nop +#endif + + .text +tstart: + ;; This is the entry point of the kernel. The CPU is currently in + ;; supervisor mode. + ;; + ;; 0x00000000 if flash. + ;; 0x40004000 if DRAM. + ;; + di + + ;; Start clocks for used blocks. + move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 + move.d [$r1], $r0 + or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \ + REG_STATE(config, rw_clk_ctrl, bif, yes) | \ + REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 + move.d $r0, [$r1] + + ;; Set up waitstates etc + move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r0 + move.d CONFIG_ETRAX_MEM_GRP1_CONFIG, $r1 + move.d $r1, [$r0] + move.d REG_ADDR(bif_core, regi_bif_core, rw_grp2_cfg), $r0 + move.d CONFIG_ETRAX_MEM_GRP2_CONFIG, $r1 + move.d $r1, [$r0] + move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r0 + move.d CONFIG_ETRAX_MEM_GRP3_CONFIG, $r1 + move.d $r1, [$r0] + move.d REG_ADDR(bif_core, regi_bif_core, rw_grp4_cfg), $r0 + move.d CONFIG_ETRAX_MEM_GRP4_CONFIG, $r1 + move.d $r1, [$r0] + +#ifdef CONFIG_ETRAXFS_SIM + ;; Set up minimal flash waitstates + move.d 0, $r10 + move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r11 + move.d $r10, [$r11] +#endif + + ;; Setup and enable the MMU. Use same configuration for both the data + ;; and the instruction MMU. + ;; + ;; Note; 3 cycles is needed for a bank-select to take effect. Further; + ;; bank 1 is the instruction MMU, bank 2 is the data MMU. +#ifndef CONFIG_ETRAXFS_SIM + move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \ + | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \ + | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0 +#else + ;; Map the virtual DRAM to the RW eprom area at address 0. + ;; Also map 0xa for the hook calls, + move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \ + | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0) \ + | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) \ + | REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa), $r0 +#endif + + ;; Temporary map of 0x40 -> 0x40 and 0x00 -> 0x00. + move.d REG_FIELD(mmu, rw_mm_kbase_lo, base_4, 4) \ + | REG_FIELD(mmu, rw_mm_kbase_lo, base_0, 0), $r1 + + ;; Enable certain page protections and setup linear mapping + ;; for f,e,c,b,4,0. +#ifndef CONFIG_ETRAXFS_SIM + move.d REG_STATE(mmu, rw_mm_cfg, we, on) \ + | REG_STATE(mmu, rw_mm_cfg, acc, on) \ + | REG_STATE(mmu, rw_mm_cfg, ex, on) \ + | REG_STATE(mmu, rw_mm_cfg, inv, on) \ + | REG_STATE(mmu, rw_mm_cfg, seg_f, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_e, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_d, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_c, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_b, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_a, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_9, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_8, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_7, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_6, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_5, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_3, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_2, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_1, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2 +#else + move.d REG_STATE(mmu, rw_mm_cfg, we, on) \ + | REG_STATE(mmu, rw_mm_cfg, acc, on) \ + | REG_STATE(mmu, rw_mm_cfg, ex, on) \ + | REG_STATE(mmu, rw_mm_cfg, inv, on) \ + | REG_STATE(mmu, rw_mm_cfg, seg_f, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_e, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_d, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_c, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_b, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_a, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_9, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_8, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_7, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_6, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_5, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \ + | REG_STATE(mmu, rw_mm_cfg, seg_3, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_2, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_1, page) \ + | REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2 +#endif + + ;; Update instruction MMU. + move 1, $srs + nop + nop + nop + move $r0, $s2 ; kbase_hi. + move $r1, $s1 ; kbase_lo. + move $r2, $s0 ; mm_cfg, virtual memory configuration. + + ;; Update data MMU. + move 2, $srs + nop + nop + nop + move $r0, $s2 ; kbase_hi. + move $r1, $s1 ; kbase_lo + move $r2, $s0 ; mm_cfg, virtual memory configuration. + + ;; Enable data and instruction MMU. + move 0, $srs + moveq 0xf, $r0 ; IMMU, DMMU, DCache, Icache on + nop + nop + nop + move $r0, $s0 + nop + nop + nop + +#ifdef CONFIG_SMP + ;; Read CPU ID + move 0, $srs + nop + nop + nop + move $s10, $r0 + cmpq 0, $r0 + beq master_cpu + nop +slave_cpu: + ; A slave waits for cpu_now_booting to be equal to CPU ID. + move.d cpu_now_booting, $r1 +slave_wait: + cmp.d [$r1], $r0 + bne slave_wait + nop + ; Time to boot-up. Get stack location provided by master CPU. + move.d smp_init_current_idle_thread, $r1 + move.d [$r1], $sp + add.d 8192, $sp + move.d ebp_start, $r0 ; Defined in linker-script. + move $r0, $ebp + jsr smp_callin + nop +master_cpu: +#endif +#ifndef CONFIG_ETRAXFS_SIM + ;; Check if starting from DRAM or flash. + lapcq ., $r0 + and.d 0x7fffffff, $r0 ; Mask off the non-cache bit. + cmp.d 0x10000, $r0 ; Arbitrary, something above this code. + blo _inflash0 + nop +#endif + + jump _inram ; Jump to cached RAM. + nop + + ;; Jumpgate. +_inflash0: + jump _inflash + nop + + ;; Put the following in a section so that storage for it can be + ;; reclaimed after init is finished. + .section ".init.text", "ax" + +_inflash: + + ;; Initialize DRAM. + cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized? + beq _dram_initialized + nop + +#include "../lib/dram_init.S" + +_dram_initialized: + ;; Copy the text and data section to DRAM. This depends on that the + ;; variables used below are correctly set up by the linker script. + ;; The calculated value stored in R4 is used below. + moveq 0, $r0 ; Source. + move.d text_start, $r1 ; Destination. + move.d __vmlinux_end, $r2 + move.d $r2, $r4 + sub.d $r1, $r4 +1: move.w [$r0+], $r3 + move.w $r3, [$r1+] + cmp.d $r2, $r1 + blo 1b + nop + + ;; Keep CRAMFS in flash. + moveq 0, $r0 + move.d romfs_length, $r1 + move.d $r0, [$r1] + move.d [$r4], $r0 ; cramfs_super.magic + cmp.d CRAMFS_MAGIC, $r0 + bne 1f + nop + + addoq +4, $r4, $acr + move.d [$acr], $r0 + move.d romfs_length, $r1 + move.d $r0, [$r1] + add.d 0xf0000000, $r4 ; Add cached flash start in virtual memory. + move.d romfs_start, $r1 + move.d $r4, [$r1] +1: moveq 1, $r0 + move.d romfs_in_flash, $r1 + move.d $r0, [$r1] + + jump _start_it ; Jump to cached code. + nop + +_inram: + ;; Check if booting from NAND flash (in that case we just remember the offset + ;; into the flash where cramfs should be). + move.d REG_ADDR(config, regi_config, r_bootsel), $r0 + move.d [$r0], $r0 + and.d REG_MASK(config, r_bootsel, boot_mode), $r0 + cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 + bne move_cramfs + moveq 1,$r0 + move.d crisv32_nand_boot, $r1 + move.d $r0, [$r1] + move.d crisv32_nand_cramfs_offset, $r1 + move.d $r9, [$r1] + moveq 1, $r0 + move.d romfs_in_flash, $r1 + move.d $r0, [$r1] + jump _start_it + nop + +move_cramfs: + ;; Move the cramfs after BSS. + moveq 0, $r0 + move.d romfs_length, $r1 + move.d $r0, [$r1] + +#ifndef CONFIG_ETRAXFS_SIM + ;; The kernel could have been unpacked to DRAM by the loader, but + ;; the cramfs image could still be inte the flash immediately + ;; following the compressed kernel image. The loaded passes the address + ;; of the bute succeeding the last compressed byte in the flash in + ;; register R9 when starting the kernel. + cmp.d 0x0ffffff8, $r9 + bhs _no_romfs_in_flash ; R9 points outside the flash area. + nop +#else + ba _no_romfs_in_flash + nop +#endif + move.d [$r9], $r0 ; cramfs_super.magic + cmp.d CRAMFS_MAGIC, $r0 + bne _no_romfs_in_flash + nop + + addoq +4, $r9, $acr + move.d [$acr], $r0 + move.d romfs_length, $r1 + move.d $r0, [$r1] + add.d 0xf0000000, $r9 ; Add cached flash start in virtual memory. + move.d romfs_start, $r1 + move.d $r9, [$r1] + moveq 1, $r0 + move.d romfs_in_flash, $r1 + move.d $r0, [$r1] + + jump _start_it ; Jump to cached code. + nop + +_no_romfs_in_flash: + ;; Look for cramfs. +#ifndef CONFIG_ETRAXFS_SIM + move.d __vmlinux_end, $r0 +#else + move.d __end, $r0 +#endif + move.d [$r0], $r1 + cmp.d CRAMFS_MAGIC, $r1 + bne 2f + nop + + addoq +4, $r0, $acr + move.d [$acr], $r2 + move.d _end, $r1 + move.d romfs_start, $r3 + move.d $r1, [$r3] + move.d romfs_length, $r3 + move.d $r2, [$r3] + +#ifndef CONFIG_ETRAXFS_SIM + add.d $r2, $r0 + add.d $r2, $r1 + + lsrq 1, $r2 ; Size is in bytes, we copy words. + addq 1, $r2 +1: + move.w [$r0], $r3 + move.w $r3, [$r1] + subq 2, $r0 + subq 2, $r1 + subq 1, $r2 + bne 1b + nop +#endif + +2: + moveq 0, $r0 + move.d romfs_in_flash, $r1 + move.d $r0, [$r1] + + jump _start_it ; Jump to cached code. + nop + +_start_it: + + ;; Check if kernel command line is supplied + cmp.d COMMAND_LINE_MAGIC, $r10 + bne no_command_line + nop + + move.d 256, $r13 + move.d cris_command_line, $r10 + or.d 0x80000000, $r11 ; Make it virtual +1: + move.b [$r11+], $r12 + move.b $r12, [$r10+] + subq 1, $r13 + bne 1b + nop + +no_command_line: + + ;; The kernel stack contains a task structure for each task. This + ;; the initial kernel stack is in the same page as the init_task, + ;; but starts at the top of the page, i.e. + 8192 bytes. + move.d init_thread_union + 8192, $sp + move.d ebp_start, $r0 ; Defined in linker-script. + move $r0, $ebp + move.d etrax_irv, $r1 ; Set the exception base register and pointer. + move.d $r0, [$r1] + +#ifndef CONFIG_ETRAXFS_SIM + ;; Clear the BSS region from _bss_start to _end. + move.d __bss_start, $r0 + move.d _end, $r1 +1: clear.d [$r0+] + cmp.d $r1, $r0 + blo 1b + nop +#endif + +#ifdef CONFIG_ETRAXFS_SIM + /* Set the watchdog timeout to something big. Will be removed when */ + /* watchdog can be disabled with command line option */ + move.d 0x7fffffff, $r10 + jsr CPU_WATCHDOG_TIMEOUT + nop +#endif + + ; Initialize registers to increase determinism + move.d __bss_start, $r0 + movem [$r0], $r13 + + jump start_kernel ; Jump to start_kernel() in init/main.c. + nop + + .data +etrax_irv: + .dword 0 +romfs_start: + .dword 0 +romfs_length: + .dword 0 +romfs_in_flash: + .dword 0 +crisv32_nand_boot: + .dword 0 +crisv32_nand_cramfs_offset: + .dword 0 + +swapper_pg_dir = 0xc0002000 + + .section ".init.data", "aw" + +#include "../lib/hw_settings.S" --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/io.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,154 @@ +/* + * Helper functions for I/O pins. + * + * Copyright (c) 2004 Axis Communications AB. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct crisv32_ioport crisv32_ioports[] = +{ + { + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_oe), + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_dout), + (unsigned long*)REG_ADDR(gio, regi_gio, r_pa_din), + 8 + }, + { + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_oe), + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_dout), + (unsigned long*)REG_ADDR(gio, regi_gio, r_pb_din), + 18 + }, + { + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_oe), + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_dout), + (unsigned long*)REG_ADDR(gio, regi_gio, r_pc_din), + 18 + }, + { + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_oe), + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_dout), + (unsigned long*)REG_ADDR(gio, regi_gio, r_pd_din), + 18 + }, + { + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_oe), + (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_dout), + (unsigned long*)REG_ADDR(gio, regi_gio, r_pe_din), + 18 + } +}; + +#define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport) + +struct crisv32_iopin crisv32_led1_green; +struct crisv32_iopin crisv32_led1_red; +struct crisv32_iopin crisv32_led2_green; +struct crisv32_iopin crisv32_led2_red; +struct crisv32_iopin crisv32_led3_green; +struct crisv32_iopin crisv32_led3_red; + +/* Dummy port used when green LED and red LED is on the same bit */ +static unsigned long io_dummy; +static struct crisv32_ioport dummy_port = +{ + &io_dummy, + &io_dummy, + &io_dummy, + 18 +}; +static struct crisv32_iopin dummy_led = +{ + &dummy_port, + 0 +}; + +static int __init crisv32_io_init(void) +{ + int ret = 0; + /* Initialize LEDs */ + ret += crisv32_io_get_name(&crisv32_led1_green, CONFIG_ETRAX_LED1G); + ret += crisv32_io_get_name(&crisv32_led1_red, CONFIG_ETRAX_LED1R); + ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_LED2G); + ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_LED2R); + ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_LED3G); + ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_LED3R); + crisv32_io_set_dir(&crisv32_led1_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led1_red, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); + + if (!strcmp(CONFIG_ETRAX_LED1G, CONFIG_ETRAX_LED1R)) + crisv32_led1_red = dummy_led; + if (!strcmp(CONFIG_ETRAX_LED2G, CONFIG_ETRAX_LED2R)) + crisv32_led2_red = dummy_led; + + return ret; +} + +__initcall(crisv32_io_init); + +int crisv32_io_get(struct crisv32_iopin* iopin, + unsigned int port, unsigned int pin) +{ + if (port > NBR_OF_PORTS) + return -EINVAL; + if (port > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + return -EIO; + + return 0; +} + +int crisv32_io_get_name(struct crisv32_iopin* iopin, + char* name) +{ + int port; + int pin; + + if (toupper(*name) == 'P') + name++; + + if (toupper(*name) < 'A' || toupper(*name) > 'E') + return -EINVAL; + + port = toupper(*name) - 'A'; + name++; + pin = simple_strtoul(name, NULL, 10); + + if (pin < 0 || pin > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + return -EIO; + + return 0; +} + +#ifdef CONFIG_PCI +/* PCI I/O access stuff */ +struct cris_io_operations* cris_iops = NULL; +EXPORT_SYMBOL(cris_iops); +#endif + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/irq.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2003, Axis Communications AB. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPU_FIXED -1 + +/* IRQ masks (refer to comment for crisv32_do_multiple) */ +#define TIMER_MASK (1 << (TIMER_INTR_VECT - FIRST_IRQ)) +#ifdef CONFIG_ETRAX_KGDB +#if defined(CONFIG_ETRAX_KGDB_PORT0) +#define IGNOREMASK (1 << (SER0_INTR_VECT - FIRST_IRQ)) +#elif defined(CONFIG_ETRAX_KGDB_PORT1) +#define IGNOREMASK (1 << (SER1_INTR_VECT - FIRST_IRQ)) +#elif defined(CONFIG_ETRAX_KGB_PORT2) +#define IGNOREMASK (1 << (SER2_INTR_VECT - FIRST_IRQ)) +#elif defined(CONFIG_ETRAX_KGDB_PORT3) +#define IGNOREMASK (1 << (SER3_INTR_VECT - FIRST_IRQ)) +#endif +#endif + +DEFINE_SPINLOCK(irq_lock); + +struct cris_irq_allocation +{ + int cpu; /* The CPU to which the IRQ is currently allocated. */ + cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */ +}; + +struct cris_irq_allocation irq_allocations[NR_IRQS] = + {[0 ... NR_IRQS - 1] = {0, CPU_MASK_ALL}}; + +static unsigned long irq_regs[NR_CPUS] = +{ + regi_irq, +#ifdef CONFIG_SMP + regi_irq2, +#endif +}; + +unsigned long cpu_irq_counters[NR_CPUS]; +unsigned long irq_counters[NR_REAL_IRQS]; + +/* From irq.c. */ +extern void weird_irq(void); + +/* From entry.S. */ +extern void system_call(void); +extern void nmi_interrupt(void); +extern void multiple_interrupt(void); +extern void gdb_handle_exception(void); +extern void i_mmu_refill(void); +extern void i_mmu_invalid(void); +extern void i_mmu_access(void); +extern void i_mmu_execute(void); +extern void d_mmu_refill(void); +extern void d_mmu_invalid(void); +extern void d_mmu_access(void); +extern void d_mmu_write(void); + +/* From kgdb.c. */ +extern void kgdb_init(void); +extern void breakpoint(void); + +/* + * Build the IRQ handler stubs using macros from irq.h. First argument is the + * IRQ number, the second argument is the corresponding bit in + * intr_rw_vect_mask found in asm/arch/hwregs/intr_vect_defs.h. + */ +BUILD_IRQ(0x31, (1 << 0)) /* memarb */ +BUILD_IRQ(0x32, (1 << 1)) /* gen_io */ +BUILD_IRQ(0x33, (1 << 2)) /* iop0 */ +BUILD_IRQ(0x34, (1 << 3)) /* iop1 */ +BUILD_IRQ(0x35, (1 << 4)) /* iop2 */ +BUILD_IRQ(0x36, (1 << 5)) /* iop3 */ +BUILD_IRQ(0x37, (1 << 6)) /* dma0 */ +BUILD_IRQ(0x38, (1 << 7)) /* dma1 */ +BUILD_IRQ(0x39, (1 << 8)) /* dma2 */ +BUILD_IRQ(0x3a, (1 << 9)) /* dma3 */ +BUILD_IRQ(0x3b, (1 << 10)) /* dma4 */ +BUILD_IRQ(0x3c, (1 << 11)) /* dma5 */ +BUILD_IRQ(0x3d, (1 << 12)) /* dma6 */ +BUILD_IRQ(0x3e, (1 << 13)) /* dma7 */ +BUILD_IRQ(0x3f, (1 << 14)) /* dma8 */ +BUILD_IRQ(0x40, (1 << 15)) /* dma9 */ +BUILD_IRQ(0x41, (1 << 16)) /* ata */ +BUILD_IRQ(0x42, (1 << 17)) /* sser0 */ +BUILD_IRQ(0x43, (1 << 18)) /* sser1 */ +BUILD_IRQ(0x44, (1 << 19)) /* ser0 */ +BUILD_IRQ(0x45, (1 << 20)) /* ser1 */ +BUILD_IRQ(0x46, (1 << 21)) /* ser2 */ +BUILD_IRQ(0x47, (1 << 22)) /* ser3 */ +BUILD_IRQ(0x48, (1 << 23)) +BUILD_IRQ(0x49, (1 << 24)) /* eth0 */ +BUILD_IRQ(0x4a, (1 << 25)) /* eth1 */ +BUILD_TIMER_IRQ(0x4b, (1 << 26))/* timer */ +BUILD_IRQ(0x4c, (1 << 27)) /* bif_arb */ +BUILD_IRQ(0x4d, (1 << 28)) /* bif_dma */ +BUILD_IRQ(0x4e, (1 << 29)) /* ext */ +BUILD_IRQ(0x4f, (1 << 29)) /* ipi */ + +/* Pointers to the low-level handlers. */ +static void (*interrupt[NR_IRQS])(void) = { + IRQ0x31_interrupt, IRQ0x32_interrupt, IRQ0x33_interrupt, + IRQ0x34_interrupt, IRQ0x35_interrupt, IRQ0x36_interrupt, + IRQ0x37_interrupt, IRQ0x38_interrupt, IRQ0x39_interrupt, + IRQ0x3a_interrupt, IRQ0x3b_interrupt, IRQ0x3c_interrupt, + IRQ0x3d_interrupt, IRQ0x3e_interrupt, IRQ0x3f_interrupt, + IRQ0x40_interrupt, IRQ0x41_interrupt, IRQ0x42_interrupt, + IRQ0x43_interrupt, IRQ0x44_interrupt, IRQ0x45_interrupt, + IRQ0x46_interrupt, IRQ0x47_interrupt, IRQ0x48_interrupt, + IRQ0x49_interrupt, IRQ0x4a_interrupt, IRQ0x4b_interrupt, + IRQ0x4c_interrupt, IRQ0x4d_interrupt, IRQ0x4e_interrupt, + IRQ0x4f_interrupt +}; + +void +block_irq(int irq, int cpu) +{ + int intr_mask; + unsigned long flags; + + spin_lock_irqsave(&irq_lock, flags); + intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); + + /* Remember; 1 let thru, 0 block. */ + intr_mask &= ~(1 << (irq - FIRST_IRQ)); + + REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); + spin_unlock_irqrestore(&irq_lock, flags); +} + +void +unblock_irq(int irq, int cpu) +{ + int intr_mask; + unsigned long flags; + + spin_lock_irqsave(&irq_lock, flags); + intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); + + /* Remember; 1 let thru, 0 block. */ + intr_mask |= (1 << (irq - FIRST_IRQ)); + + REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); + spin_unlock_irqrestore(&irq_lock, flags); +} + +/* Find out which CPU the irq should be allocated to. */ +static int irq_cpu(int irq) +{ + int cpu; + unsigned long flags; + + spin_lock_irqsave(&irq_lock, flags); + cpu = irq_allocations[irq - FIRST_IRQ].cpu; + + /* Fixed interrupts stay on the local CPU. */ + if (cpu == CPU_FIXED) + { + spin_unlock_irqrestore(&irq_lock, flags); + return smp_processor_id(); + } + + + /* Let the interrupt stay if possible */ + if (cpu_isset(cpu, irq_allocations[irq - FIRST_IRQ].mask)) + goto out; + + /* IRQ must be moved to another CPU. */ + cpu = first_cpu(irq_allocations[irq - FIRST_IRQ].mask); + irq_allocations[irq - FIRST_IRQ].cpu = cpu; +out: + spin_unlock_irqrestore(&irq_lock, flags); + return cpu; +} + +void +mask_irq(int irq) +{ + int cpu; + + for (cpu = 0; cpu < NR_CPUS; cpu++) + block_irq(irq, cpu); +} + +void +unmask_irq(int irq) +{ + unblock_irq(irq, irq_cpu(irq)); +} + + +static unsigned int startup_crisv32_irq(unsigned int irq) +{ + unmask_irq(irq); + return 0; +} + +static void shutdown_crisv32_irq(unsigned int irq) +{ + mask_irq(irq); +} + +static void enable_crisv32_irq(unsigned int irq) +{ + unmask_irq(irq); +} + +static void disable_crisv32_irq(unsigned int irq) +{ + mask_irq(irq); +} + +static void ack_crisv32_irq(unsigned int irq) +{ +} + +static void end_crisv32_irq(unsigned int irq) +{ +} + +void set_affinity_crisv32_irq(unsigned int irq, cpumask_t dest) +{ + unsigned long flags; + spin_lock_irqsave(&irq_lock, flags); + irq_allocations[irq - FIRST_IRQ].mask = dest; + spin_unlock_irqrestore(&irq_lock, flags); +} + +static struct hw_interrupt_type crisv32_irq_type = { + .typename = "CRISv32", + .startup = startup_crisv32_irq, + .shutdown = shutdown_crisv32_irq, + .enable = enable_crisv32_irq, + .disable = disable_crisv32_irq, + .ack = ack_crisv32_irq, + .end = end_crisv32_irq, + .set_affinity = set_affinity_crisv32_irq +}; + +void +set_exception_vector(int n, irqvectptr addr) +{ + etrax_irv->v[n] = (irqvectptr) addr; +} + +extern void do_IRQ(int irq, struct pt_regs * regs); + +void +crisv32_do_IRQ(int irq, int block, struct pt_regs* regs) +{ + /* Interrupts that may not be moved to another CPU and + * are SA_INTERRUPT may skip blocking. This is currently + * only valid for the timer IRQ and the IPI and is used + * for the timer interrupt to avoid watchdog starvation. + */ + if (!block) { + do_IRQ(irq, regs); + return; + } + + block_irq(irq, smp_processor_id()); + do_IRQ(irq, regs); + + unblock_irq(irq, irq_cpu(irq)); +} + +/* If multiple interrupts occur simultaneously we get a multiple + * interrupt from the CPU and software has to sort out which + * interrupts that happened. There are two special cases here: + * + * 1. Timer interrupts may never be blocked because of the + * watchdog (refer to comment in include/asr/arch/irq.h) + * 2. GDB serial port IRQs are unhandled here and will be handled + * as a single IRQ when it strikes again because the GDB + * stubb wants to save the registers in its own fashion. + */ +void +crisv32_do_multiple(struct pt_regs* regs) +{ + int cpu; + int mask; + int masked; + int bit; + + cpu = smp_processor_id(); + + /* An extra irq_enter here to prevent softIRQs to run after + * each do_IRQ. This will decrease the interrupt latency. + */ + irq_enter(); + + /* Get which IRQs that happend. */ + masked = REG_RD_INT(intr_vect, irq_regs[cpu], r_masked_vect); + + /* Calculate new IRQ mask with these IRQs disabled. */ + mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); + mask &= ~masked; + + /* Timer IRQ is never masked */ + if (masked & TIMER_MASK) + mask |= TIMER_MASK; + + /* Block all the IRQs */ + REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); + + /* Check for timer IRQ and handle it special. */ + if (masked & TIMER_MASK) { + masked &= ~TIMER_MASK; + do_IRQ(TIMER_INTR_VECT, regs); + } + +#ifdef IGNORE_MASK + /* Remove IRQs that can't be handled as multiple. */ + masked &= ~IGNORE_MASK; +#endif + + /* Handle the rest of the IRQs. */ + for (bit = 0; bit < 32; bit++) + { + if (masked & (1 << bit)) + do_IRQ(bit + FIRST_IRQ, regs); + } + + /* Unblock all the IRQs. */ + mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); + mask |= masked; + REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); + + /* This irq_exit() will trigger the soft IRQs. */ + irq_exit(); +} + +/* + * This is called by start_kernel. It fixes the IRQ masks and setup the + * interrupt vector table to point to bad_interrupt pointers. + */ +void __init +init_IRQ(void) +{ + int i; + int j; + reg_intr_vect_rw_mask vect_mask = {0}; + + /* Clear all interrupts masks. */ + REG_WR(intr_vect, regi_irq, rw_mask, vect_mask); + + for (i = 0; i < 256; i++) + etrax_irv->v[i] = weird_irq; + + /* Point all IRQ's to bad handlers. */ + for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) { + irq_desc[j].handler = &crisv32_irq_type; + set_exception_vector(i, interrupt[j]); + } + + /* Mark Timer and IPI IRQs as CPU local */ + irq_allocations[TIMER_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; + irq_desc[TIMER_INTR_VECT].status |= IRQ_PER_CPU; + irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; + irq_desc[IPI_INTR_VECT].status |= IRQ_PER_CPU; + + set_exception_vector(0x00, nmi_interrupt); + set_exception_vector(0x30, multiple_interrupt); + + /* Set up handler for various MMU bus faults. */ + set_exception_vector(0x04, i_mmu_refill); + set_exception_vector(0x05, i_mmu_invalid); + set_exception_vector(0x06, i_mmu_access); + set_exception_vector(0x07, i_mmu_execute); + set_exception_vector(0x08, d_mmu_refill); + set_exception_vector(0x09, d_mmu_invalid); + set_exception_vector(0x0a, d_mmu_access); + set_exception_vector(0x0b, d_mmu_write); + + /* The system-call trap is reached by "break 13". */ + set_exception_vector(0x1d, system_call); + + /* Exception handlers for debugging, both user-mode and kernel-mode. */ + + /* Break 8. */ + set_exception_vector(0x18, gdb_handle_exception); + /* Hardware single step. */ + set_exception_vector(0x3, gdb_handle_exception); + /* Hardware breakpoint. */ + set_exception_vector(0xc, gdb_handle_exception); + +#ifdef CONFIG_ETRAX_KGDB + kgdb_init(); + /* Everything is set up; now trap the kernel. */ + breakpoint(); +#endif +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/kgdb_asm.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2004 Axis Communications AB + * + * Code for handling break 8, hardware breakpoint, single step, and serial + * port exceptions for kernel debugging purposes. + */ + +#include +#include + + ;; Exported functions. + .globl kgdb_handle_exception + +kgdb_handle_exception: + +;; Create a register image of the caller. +;; +;; First of all, save the ACR on the stack since we need it for address calculations. +;; We put it into the register struct later. + + subq 4, $sp + move.d $acr, [$sp] + +;; Now we are free to use ACR all we want. +;; If we were running this handler with interrupts on, we would have to be careful +;; to save and restore CCS manually, but since we aren't we treat it like every other +;; register. + + move.d reg, $acr + move.d $r0, [$acr] ; Save R0 (start of register struct) + addq 4, $acr + move.d $r1, [$acr] ; Save R1 + addq 4, $acr + move.d $r2, [$acr] ; Save R2 + addq 4, $acr + move.d $r3, [$acr] ; Save R3 + addq 4, $acr + move.d $r4, [$acr] ; Save R4 + addq 4, $acr + move.d $r5, [$acr] ; Save R5 + addq 4, $acr + move.d $r6, [$acr] ; Save R6 + addq 4, $acr + move.d $r7, [$acr] ; Save R7 + addq 4, $acr + move.d $r8, [$acr] ; Save R8 + addq 4, $acr + move.d $r9, [$acr] ; Save R9 + addq 4, $acr + move.d $r10, [$acr] ; Save R10 + addq 4, $acr + move.d $r11, [$acr] ; Save R11 + addq 4, $acr + move.d $r12, [$acr] ; Save R12 + addq 4, $acr + move.d $r13, [$acr] ; Save R13 + addq 4, $acr + move.d $sp, [$acr] ; Save SP (R14) + addq 4, $acr + + ;; The ACR register is already saved on the stack, so pop it from there. + move.d [$sp],$r0 + move.d $r0, [$acr] + addq 4, $acr + + move $bz, [$acr] + addq 1, $acr + move $vr, [$acr] + addq 1, $acr + move $pid, [$acr] + addq 4, $acr + move $srs, [$acr] + addq 1, $acr + move $wz, [$acr] + addq 2, $acr + move $exs, [$acr] + addq 4, $acr + move $eda, [$acr] + addq 4, $acr + move $mof, [$acr] + addq 4, $acr + move $dz, [$acr] + addq 4, $acr + move $ebp, [$acr] + addq 4, $acr + move $erp, [$acr] + addq 4, $acr + move $srp, [$acr] + addq 4, $acr + move $nrp, [$acr] + addq 4, $acr + move $ccs, [$acr] + addq 4, $acr + move $usp, [$acr] + addq 4, $acr + move $spc, [$acr] + addq 4, $acr + +;; Skip the pseudo-PC. + addq 4, $acr + +;; Save the support registers in bank 0 - 3. + clear.d $r1 ; Bank counter + move.d sreg, $acr + +;; Bank 0 + move $r1, $srs + nop + nop + nop + move $s0, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s1, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s2, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s3, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s4, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s5, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s6, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s7, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s8, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s9, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s10, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s11, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s12, $r0 + move.d $r0, [$acr] + addq 4, $acr + + ;; Nothing in S13 - S15, bank 0 + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + +;; Bank 1 and bank 2 have the same layout, hence the loop. + addq 1, $r1 +1: + move $r1, $srs + nop + nop + nop + move $s0, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s1, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s2, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s3, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s4, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s5, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s6, $r0 + move.d $r0, [$acr] + addq 4, $acr + + ;; Nothing in S7 - S15, bank 1 and 2 + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + clear.d [$acr] + addq 4, $acr + + addq 1, $r1 + cmpq 3, $r1 + bne 1b + nop + +;; Bank 3 + move $r1, $srs + nop + nop + nop + move $s0, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s1, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s2, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s3, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s4, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s5, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s6, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s7, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s8, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s9, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s10, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s11, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s12, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s13, $r0 + move.d $r0, [$acr] + addq 4, $acr + move $s14, $r0 + move.d $r0, [$acr] + addq 4, $acr +;; Nothing in S15, bank 3 + clear.d [$acr] + addq 4, $acr + +;; Check what got us here: get IDX field of EXS. + move $exs, $r10 + and.d 0xff00, $r10 + lsrq 8, $r10 +#if defined(CONFIG_ETRAX_KGDB_PORT0) + cmp.d SER0_INTR_VECT, $r10 ; IRQ for serial port 0 + beq sigint + nop +#elif defined(CONFIG_ETRAX_KGDB_PORT1) + cmp.d SER1_INTR_VECT, $r10 ; IRQ for serial port 1 + beq sigint + nop +#elif defined(CONFIG_ETRAX_KGDB_PORT2) + cmp.d SER2_INTR_VECT, $r10 ; IRQ for serial port 2 + beq sigint + nop +#elif defined(CONFIG_ETRAX_KGDB_PORT3) + cmp.d SER3_INTR_VECT, $r10 ; IRQ for serial port 3 + beq sigint + nop +#endif +;; Multiple interrupt must be due to serial break. + cmp.d 0x30, $r10 ; Multiple interrupt + beq sigint + nop +;; Neither of those? Then it's a sigtrap. + ba handle_comm + moveq 5, $r10 ; Set SIGTRAP (delay slot) + +sigint: + ;; Serial interrupt; get character + jsr getDebugChar + nop ; Delay slot + cmp.b 3, $r10 ; \003 (Ctrl-C)? + bne return ; No, get out of here + nop + moveq 2, $r10 ; Set SIGINT + +;; +;; Handle the communication +;; +handle_comm: + move.d internal_stack+1020, $sp ; Use the internal stack which grows upwards + jsr handle_exception ; Interactive routine + nop + +;; +;; Return to the caller +;; +return: + +;; First of all, write the support registers. + clear.d $r1 ; Bank counter + move.d sreg, $acr + +;; Bank 0 + move $r1, $srs + nop + nop + nop + move.d [$acr], $r0 + move $r0, $s0 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s1 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s2 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s3 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s4 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s5 + addq 4, $acr + +;; Nothing in S6 - S7, bank 0. + addq 4, $acr + addq 4, $acr + + move.d [$acr], $r0 + move $r0, $s8 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s9 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s10 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s11 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s12 + addq 4, $acr + +;; Nothing in S13 - S15, bank 0 + addq 4, $acr + addq 4, $acr + addq 4, $acr + +;; Bank 1 and bank 2 have the same layout, hence the loop. + addq 1, $r1 +2: + move $r1, $srs + nop + nop + nop + move.d [$acr], $r0 + move $r0, $s0 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s1 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s2 + addq 4, $acr + +;; S3 (MM_CAUSE) is read-only. + addq 4, $acr + + move.d [$acr], $r0 + move $r0, $s4 + addq 4, $acr + +;; FIXME: Actually write S5/S6? (Affects MM_CAUSE.) + addq 4, $acr + addq 4, $acr + +;; Nothing in S7 - S15, bank 1 and 2 + addq 4, $acr + addq 4, $acr + addq 4, $acr + addq 4, $acr + addq 4, $acr + addq 4, $acr + addq 4, $acr + addq 4, $acr + addq 4, $acr + + addq 1, $r1 + cmpq 3, $r1 + bne 2b + nop + +;; Bank 3 + move $r1, $srs + nop + nop + nop + move.d [$acr], $r0 + move $r0, $s0 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s1 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s2 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s3 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s4 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s5 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s6 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s7 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s8 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s9 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s10 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s11 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s12 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s13 + addq 4, $acr + move.d [$acr], $r0 + move $r0, $s14 + addq 4, $acr + +;; Nothing in S15, bank 3 + addq 4, $acr + +;; Now, move on to the regular register restoration process. + + move.d reg, $acr ; Reset ACR to point at the beginning of the register image + move.d [$acr], $r0 ; Restore R0 + addq 4, $acr + move.d [$acr], $r1 ; Restore R1 + addq 4, $acr + move.d [$acr], $r2 ; Restore R2 + addq 4, $acr + move.d [$acr], $r3 ; Restore R3 + addq 4, $acr + move.d [$acr], $r4 ; Restore R4 + addq 4, $acr + move.d [$acr], $r5 ; Restore R5 + addq 4, $acr + move.d [$acr], $r6 ; Restore R6 + addq 4, $acr + move.d [$acr], $r7 ; Restore R7 + addq 4, $acr + move.d [$acr], $r8 ; Restore R8 + addq 4, $acr + move.d [$acr], $r9 ; Restore R9 + addq 4, $acr + move.d [$acr], $r10 ; Restore R10 + addq 4, $acr + move.d [$acr], $r11 ; Restore R11 + addq 4, $acr + move.d [$acr], $r12 ; Restore R12 + addq 4, $acr + move.d [$acr], $r13 ; Restore R13 + +;; +;; We restore all registers, even though some of them probably haven't changed. +;; + + addq 4, $acr + move.d [$acr], $sp ; Restore SP (R14) + + ;; ACR cannot be restored just yet. + addq 8, $acr + + ;; Skip BZ, VR. + addq 2, $acr + + move [$acr], $pid ; Restore PID + addq 4, $acr + move [$acr], $srs ; Restore SRS + nop + nop + nop + addq 1, $acr + + ;; Skip WZ. + addq 2, $acr + + move [$acr], $exs ; Restore EXS. + addq 4, $acr + move [$acr], $eda ; Restore EDA. + addq 4, $acr + move [$acr], $mof ; Restore MOF. + + ;; Skip DZ. + addq 8, $acr + + move [$acr], $ebp ; Restore EBP. + addq 4, $acr + move [$acr], $erp ; Restore ERP. + addq 4, $acr + move [$acr], $srp ; Restore SRP. + addq 4, $acr + move [$acr], $nrp ; Restore NRP. + addq 4, $acr + move [$acr], $ccs ; Restore CCS like an ordinary register. + addq 4, $acr + move [$acr], $usp ; Restore USP + addq 4, $acr + move [$acr], $spc ; Restore SPC + ; No restoration of pseudo-PC of course. + + move.d reg, $acr ; Reset ACR to point at the beginning of the register image + add.d 15*4, $acr + move.d [$acr], $acr ; Finally, restore ACR. + rete ; Same as jump ERP + rfe ; Shifts CCS --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/kgdb.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,1660 @@ +/* + * arch/cris/arch-v32/kernel/kgdb.c + * + * CRIS v32 version by Orjan Friberg, Axis Communications AB. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to + * + * Copyright (C) 1995 Andreas Busse + */ + +/* FIXME: Check the documentation. */ + +/* + * kgdb usage notes: + * ----------------- + * + * If you select CONFIG_ETRAX_KGDB in the configuration, the kernel will be + * built with different gcc flags: "-g" is added to get debug infos, and + * "-fomit-frame-pointer" is omitted to make debugging easier. Since the + * resulting kernel will be quite big (approx. > 7 MB), it will be stripped + * before compresion. Such a kernel will behave just as usually, except if + * given a "debug=" command line option. (Only serial devices are + * allowed for , i.e. no printers or the like; possible values are + * machine depedend and are the same as for the usual debug device, the one + * for logging kernel messages.) If that option is given and the device can be + * initialized, the kernel will connect to the remote gdb in trap_init(). The + * serial parameters are fixed to 8N1 and 115200 bps, for easyness of + * implementation. + * + * To start a debugging session, start that gdb with the debugging kernel + * image (the one with the symbols, vmlinux.debug) named on the command line. + * This file will be used by gdb to get symbol and debugging infos about the + * kernel. Next, select remote debug mode by + * target remote + * where is the name of the serial device over which the debugged + * machine is connected. Maybe you have to adjust the baud rate by + * set remotebaud + * or also other parameters with stty: + * shell stty ... #. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* From entry.S. */ +extern void gdb_handle_exception(void); +/* From kgdb_asm.S. */ +extern void kgdb_handle_exception(void); + +static int kgdb_started = 0; + +/********************************* Register image ****************************/ + +typedef +struct register_image +{ + /* Offset */ + unsigned int r0; /* 0x00 */ + unsigned int r1; /* 0x04 */ + unsigned int r2; /* 0x08 */ + unsigned int r3; /* 0x0C */ + unsigned int r4; /* 0x10 */ + unsigned int r5; /* 0x14 */ + unsigned int r6; /* 0x18 */ + unsigned int r7; /* 0x1C */ + unsigned int r8; /* 0x20; Frame pointer (if any) */ + unsigned int r9; /* 0x24 */ + unsigned int r10; /* 0x28 */ + unsigned int r11; /* 0x2C */ + unsigned int r12; /* 0x30 */ + unsigned int r13; /* 0x34 */ + unsigned int sp; /* 0x38; R14, Stack pointer */ + unsigned int acr; /* 0x3C; R15, Address calculation register. */ + + unsigned char bz; /* 0x40; P0, 8-bit zero register */ + unsigned char vr; /* 0x41; P1, Version register (8-bit) */ + unsigned int pid; /* 0x42; P2, Process ID */ + unsigned char srs; /* 0x46; P3, Support register select (8-bit) */ + unsigned short wz; /* 0x47; P4, 16-bit zero register */ + unsigned int exs; /* 0x49; P5, Exception status */ + unsigned int eda; /* 0x4D; P6, Exception data address */ + unsigned int mof; /* 0x51; P7, Multiply overflow register */ + unsigned int dz; /* 0x55; P8, 32-bit zero register */ + unsigned int ebp; /* 0x59; P9, Exception base pointer */ + unsigned int erp; /* 0x5D; P10, Exception return pointer. Contains the PC we are interested in. */ + unsigned int srp; /* 0x61; P11, Subroutine return pointer */ + unsigned int nrp; /* 0x65; P12, NMI return pointer */ + unsigned int ccs; /* 0x69; P13, Condition code stack */ + unsigned int usp; /* 0x6D; P14, User mode stack pointer */ + unsigned int spc; /* 0x71; P15, Single step PC */ + unsigned int pc; /* 0x75; Pseudo register (for the most part set to ERP). */ + +} registers; + +typedef +struct bp_register_image +{ + /* Support register bank 0. */ + unsigned int s0_0; + unsigned int s1_0; + unsigned int s2_0; + unsigned int s3_0; + unsigned int s4_0; + unsigned int s5_0; + unsigned int s6_0; + unsigned int s7_0; + unsigned int s8_0; + unsigned int s9_0; + unsigned int s10_0; + unsigned int s11_0; + unsigned int s12_0; + unsigned int s13_0; + unsigned int s14_0; + unsigned int s15_0; + + /* Support register bank 1. */ + unsigned int s0_1; + unsigned int s1_1; + unsigned int s2_1; + unsigned int s3_1; + unsigned int s4_1; + unsigned int s5_1; + unsigned int s6_1; + unsigned int s7_1; + unsigned int s8_1; + unsigned int s9_1; + unsigned int s10_1; + unsigned int s11_1; + unsigned int s12_1; + unsigned int s13_1; + unsigned int s14_1; + unsigned int s15_1; + + /* Support register bank 2. */ + unsigned int s0_2; + unsigned int s1_2; + unsigned int s2_2; + unsigned int s3_2; + unsigned int s4_2; + unsigned int s5_2; + unsigned int s6_2; + unsigned int s7_2; + unsigned int s8_2; + unsigned int s9_2; + unsigned int s10_2; + unsigned int s11_2; + unsigned int s12_2; + unsigned int s13_2; + unsigned int s14_2; + unsigned int s15_2; + + /* Support register bank 3. */ + unsigned int s0_3; /* BP_CTRL */ + unsigned int s1_3; /* BP_I0_START */ + unsigned int s2_3; /* BP_I0_END */ + unsigned int s3_3; /* BP_D0_START */ + unsigned int s4_3; /* BP_D0_END */ + unsigned int s5_3; /* BP_D1_START */ + unsigned int s6_3; /* BP_D1_END */ + unsigned int s7_3; /* BP_D2_START */ + unsigned int s8_3; /* BP_D2_END */ + unsigned int s9_3; /* BP_D3_START */ + unsigned int s10_3; /* BP_D3_END */ + unsigned int s11_3; /* BP_D4_START */ + unsigned int s12_3; /* BP_D4_END */ + unsigned int s13_3; /* BP_D5_START */ + unsigned int s14_3; /* BP_D5_END */ + unsigned int s15_3; /* BP_RESERVED */ + +} support_registers; + +enum register_name +{ + R0, R1, R2, R3, + R4, R5, R6, R7, + R8, R9, R10, R11, + R12, R13, SP, ACR, + + BZ, VR, PID, SRS, + WZ, EXS, EDA, MOF, + DZ, EBP, ERP, SRP, + NRP, CCS, USP, SPC, + PC, + + S0, S1, S2, S3, + S4, S5, S6, S7, + S8, S9, S10, S11, + S12, S13, S14, S15 + +}; + +/* The register sizes of the registers in register_name. An unimplemented register + is designated by size 0 in this array. */ +static int register_size[] = +{ + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + + 1, 1, 4, 1, + 2, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + + 4, + + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4 + +}; + +/* Contains the register image of the kernel. + (Global so that they can be reached from assembler code.) */ +registers reg; +support_registers sreg; + +/************** Prototypes for local library functions ***********************/ + +/* Copy of strcpy from libc. */ +static char *gdb_cris_strcpy(char *s1, const char *s2); + +/* Copy of strlen from libc. */ +static int gdb_cris_strlen(const char *s); + +/* Copy of memchr from libc. */ +static void *gdb_cris_memchr(const void *s, int c, int n); + +/* Copy of strtol from libc. Does only support base 16. */ +static int gdb_cris_strtol(const char *s, char **endptr, int base); + +/********************** Prototypes for local functions. **********************/ + +/* Write a value to a specified register regno in the register image + of the current thread. */ +static int write_register(int regno, char *val); + +/* Read a value from a specified register in the register image. Returns the + status of the read operation. The register value is returned in valptr. */ +static int read_register(char regno, unsigned int *valptr); + +/* Serial port, reads one character. ETRAX 100 specific. from debugport.c */ +int getDebugChar(void); + +#ifdef CONFIG_ETRAXFS_SIM +int getDebugChar(void) +{ + return socketread(); +} +#endif + +/* Serial port, writes one character. ETRAX 100 specific. from debugport.c */ +void putDebugChar(int val); + +#ifdef CONFIG_ETRAXFS_SIM +void putDebugChar(int val) +{ + socketwrite((char *)&val, 1); +} +#endif + +/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte, + represented by int x. */ +static char highhex(int x); + +/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte, + represented by int x. */ +static char lowhex(int x); + +/* Returns the integer equivalent of a hexadecimal character. */ +static int hex(char ch); + +/* Convert the memory, pointed to by mem into hexadecimal representation. + Put the result in buf, and return a pointer to the last character + in buf (null). */ +static char *mem2hex(char *buf, unsigned char *mem, int count); + +/* Convert the array, in hexadecimal representation, pointed to by buf into + binary representation. Put the result in mem, and return a pointer to + the character after the last byte written. */ +static unsigned char *hex2mem(unsigned char *mem, char *buf, int count); + +/* Put the content of the array, in binary representation, pointed to by buf + into memory pointed to by mem, and return a pointer to + the character after the last byte written. */ +static unsigned char *bin2mem(unsigned char *mem, unsigned char *buf, int count); + +/* Await the sequence $# and store in the array buffer + returned. */ +static void getpacket(char *buffer); + +/* Send $# from the in the array buffer. */ +static void putpacket(char *buffer); + +/* Build and send a response packet in order to inform the host the + stub is stopped. */ +static void stub_is_stopped(int sigval); + +/* All expected commands are sent from remote.c. Send a response according + to the description in remote.c. Not static since it needs to be reached + from assembler code. */ +void handle_exception(int sigval); + +/* Performs a complete re-start from scratch. ETRAX specific. */ +static void kill_restart(void); + +/******************** Prototypes for global functions. ***********************/ + +/* The string str is prepended with the GDB printout token and sent. */ +void putDebugString(const unsigned char *str, int len); + +/* A static breakpoint to be used at startup. */ +void breakpoint(void); + +/* Avoid warning as the internal_stack is not used in the C-code. */ +#define USEDVAR(name) { if (name) { ; } } +#define USEDFUN(name) { void (*pf)(void) = (void *)name; USEDVAR(pf) } + +/********************************** Packet I/O ******************************/ +/* BUFMAX defines the maximum number of characters in + inbound/outbound buffers */ +/* FIXME: How do we know it's enough? */ +#define BUFMAX 512 + +/* Run-length encoding maximum length. Send 64 at most. */ +#define RUNLENMAX 64 + +/* Definition of all valid hexadecimal characters */ +static const char hexchars[] = "0123456789abcdef"; + +/* The inbound/outbound buffers used in packet I/O */ +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; + +/* Error and warning messages. */ +enum error_type +{ + SUCCESS, E01, E02, E03, E04, E05, E06, +}; + +static char *error_message[] = +{ + "", + "E01 Set current or general thread - H[c,g] - internal error.", + "E02 Change register content - P - cannot change read-only register.", + "E03 Thread is not alive.", /* T, not used. */ + "E04 The command is not supported - [s,C,S,!,R,d,r] - internal error.", + "E05 Change register content - P - the register is not implemented..", + "E06 Change memory content - M - internal error.", +}; + +/********************************** Breakpoint *******************************/ +/* Use an internal stack in the breakpoint and interrupt response routines. + FIXME: How do we know the size of this stack is enough? + Global so it can be reached from assembler code. */ +#define INTERNAL_STACK_SIZE 1024 +char internal_stack[INTERNAL_STACK_SIZE]; + +/* Due to the breakpoint return pointer, a state variable is needed to keep + track of whether it is a static (compiled) or dynamic (gdb-invoked) + breakpoint to be handled. A static breakpoint uses the content of register + ERP as it is whereas a dynamic breakpoint requires subtraction with 2 + in order to execute the instruction. The first breakpoint is static; all + following are assumed to be dynamic. */ +static int dynamic_bp = 0; + +/********************************* String library ****************************/ +/* Single-step over library functions creates trap loops. */ + +/* Copy char s2[] to s1[]. */ +static char* +gdb_cris_strcpy(char *s1, const char *s2) +{ + char *s = s1; + + for (s = s1; (*s++ = *s2++) != '\0'; ) + ; + return s1; +} + +/* Find length of s[]. */ +static int +gdb_cris_strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; sc++) + ; + return (sc - s); +} + +/* Find first occurrence of c in s[n]. */ +static void* +gdb_cris_memchr(const void *s, int c, int n) +{ + const unsigned char uc = c; + const unsigned char *su; + + for (su = s; 0 < n; ++su, --n) + if (*su == uc) + return (void *)su; + return NULL; +} +/******************************* Standard library ****************************/ +/* Single-step over library functions creates trap loops. */ +/* Convert string to long. */ +static int +gdb_cris_strtol(const char *s, char **endptr, int base) +{ + char *s1; + char *sd; + int x = 0; + + for (s1 = (char*)s; (sd = gdb_cris_memchr(hexchars, *s1, base)) != NULL; ++s1) + x = x * base + (sd - hexchars); + + if (endptr) { + /* Unconverted suffix is stored in endptr unless endptr is NULL. */ + *endptr = s1; + } + + return x; +} + +/********************************* Register image ****************************/ + +/* Write a value to a specified register in the register image of the current + thread. Returns status code SUCCESS, E02 or E05. */ +static int +write_register(int regno, char *val) +{ + int status = SUCCESS; + + if (regno >= R0 && regno <= ACR) { + /* Consecutive 32-bit registers. */ + hex2mem((unsigned char *)®.r0 + (regno - R0) * sizeof(unsigned int), + val, sizeof(unsigned int)); + + } else if (regno == BZ || regno == VR || regno == WZ || regno == DZ) { + /* Read-only registers. */ + status = E02; + + } else if (regno == PID) { + /* 32-bit register. (Even though we already checked SRS and WZ, we cannot + combine this with the EXS - SPC write since SRS and WZ have different size.) */ + hex2mem((unsigned char *)®.pid, val, sizeof(unsigned int)); + + } else if (regno == SRS) { + /* 8-bit register. */ + hex2mem((unsigned char *)®.srs, val, sizeof(unsigned char)); + + } else if (regno >= EXS && regno <= SPC) { + /* Consecutive 32-bit registers. */ + hex2mem((unsigned char *)®.exs + (regno - EXS) * sizeof(unsigned int), + val, sizeof(unsigned int)); + + } else if (regno == PC) { + /* Pseudo-register. Treat as read-only. */ + status = E02; + + } else if (regno >= S0 && regno <= S15) { + /* 32-bit registers. */ + hex2mem((unsigned char *)&sreg.s0_0 + (reg.srs * 16 * sizeof(unsigned int)) + (regno - S0) * sizeof(unsigned int), val, sizeof(unsigned int)); + } else { + /* Non-existing register. */ + status = E05; + } + return status; +} + +/* Read a value from a specified register in the register image. Returns the + value in the register or -1 for non-implemented registers. */ +static int +read_register(char regno, unsigned int *valptr) +{ + int status = SUCCESS; + + /* We read the zero registers from the register struct (instead of just returning 0) + to catch errors. */ + + if (regno >= R0 && regno <= ACR) { + /* Consecutive 32-bit registers. */ + *valptr = *(unsigned int *)((char *)®.r0 + (regno - R0) * sizeof(unsigned int)); + + } else if (regno == BZ || regno == VR) { + /* Consecutive 8-bit registers. */ + *valptr = (unsigned int)(*(unsigned char *) + ((char *)®.bz + (regno - BZ) * sizeof(char))); + + } else if (regno == PID) { + /* 32-bit register. */ + *valptr = *(unsigned int *)((char *)®.pid); + + } else if (regno == SRS) { + /* 8-bit register. */ + *valptr = (unsigned int)(*(unsigned char *)((char *)®.srs)); + + } else if (regno == WZ) { + /* 16-bit register. */ + *valptr = (unsigned int)(*(unsigned short *)(char *)®.wz); + + } else if (regno >= EXS && regno <= PC) { + /* Consecutive 32-bit registers. */ + *valptr = *(unsigned int *)((char *)®.exs + (regno - EXS) * sizeof(unsigned int)); + + } else if (regno >= S0 && regno <= S15) { + /* Consecutive 32-bit registers, located elsewhere. */ + *valptr = *(unsigned int *)((char *)&sreg.s0_0 + (reg.srs * 16 * sizeof(unsigned int)) + (regno - S0) * sizeof(unsigned int)); + + } else { + /* Non-existing register. */ + status = E05; + } + return status; + +} + +/********************************** Packet I/O ******************************/ +/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte, + represented by int x. */ +static inline char +highhex(int x) +{ + return hexchars[(x >> 4) & 0xf]; +} + +/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte, + represented by int x. */ +static inline char +lowhex(int x) +{ + return hexchars[x & 0xf]; +} + +/* Returns the integer equivalent of a hexadecimal character. */ +static int +hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return -1; +} + +/* Convert the memory, pointed to by mem into hexadecimal representation. + Put the result in buf, and return a pointer to the last character + in buf (null). */ + +static char * +mem2hex(char *buf, unsigned char *mem, int count) +{ + int i; + int ch; + + if (mem == NULL) { + /* Invalid address, caught by 'm' packet handler. */ + for (i = 0; i < count; i++) { + *buf++ = '0'; + *buf++ = '0'; + } + } else { + /* Valid mem address. */ + for (i = 0; i < count; i++) { + ch = *mem++; + *buf++ = highhex (ch); + *buf++ = lowhex (ch); + } + } + /* Terminate properly. */ + *buf = '\0'; + return buf; +} + +/* Same as mem2hex, but puts it in network byte order. */ +static char * +mem2hex_nbo(char *buf, unsigned char *mem, int count) +{ + int i; + int ch; + + mem += count - 1; + for (i = 0; i < count; i++) { + ch = *mem--; + *buf++ = highhex (ch); + *buf++ = lowhex (ch); + } + + /* Terminate properly. */ + *buf = '\0'; + return buf; +} + +/* Convert the array, in hexadecimal representation, pointed to by buf into + binary representation. Put the result in mem, and return a pointer to + the character after the last byte written. */ +static unsigned char* +hex2mem(unsigned char *mem, char *buf, int count) +{ + int i; + unsigned char ch; + for (i = 0; i < count; i++) { + ch = hex (*buf++) << 4; + ch = ch + hex (*buf++); + *mem++ = ch; + } + return mem; +} + +/* Put the content of the array, in binary representation, pointed to by buf + into memory pointed to by mem, and return a pointer to the character after + the last byte written. + Gdb will escape $, #, and the escape char (0x7d). */ +static unsigned char* +bin2mem(unsigned char *mem, unsigned char *buf, int count) +{ + int i; + unsigned char *next; + for (i = 0; i < count; i++) { + /* Check for any escaped characters. Be paranoid and + only unescape chars that should be escaped. */ + if (*buf == 0x7d) { + next = buf + 1; + if (*next == 0x3 || *next == 0x4 || *next == 0x5D) { + /* #, $, ESC */ + buf++; + *buf += 0x20; + } + } + *mem++ = *buf++; + } + return mem; +} + +/* Await the sequence $# and store in the array buffer + returned. */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + + do { + while((ch = getDebugChar ()) != '$') + /* Wait for the start character $ and ignore all other characters */; + checksum = 0; + xmitcsum = -1; + count = 0; + /* Read until a # or the end of the buffer is reached */ + while (count < BUFMAX) { + ch = getDebugChar(); + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar()) << 4; + xmitcsum += hex(getDebugChar()); + if (checksum != xmitcsum) { + /* Wrong checksum */ + putDebugChar('-'); + } else { + /* Correct checksum */ + putDebugChar('+'); + /* If sequence characters are received, reply with them */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* Remove the sequence characters from the buffer */ + count = gdb_cris_strlen(buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* Send $# from the in the array buffer. */ + +static void +putpacket(char *buffer) +{ + int checksum; + int runlen; + int encode; + + do { + char *src = buffer; + putDebugChar('$'); + checksum = 0; + while (*src) { + /* Do run length encoding */ + putDebugChar(*src); + checksum += *src; + runlen = 0; + while (runlen < RUNLENMAX && *src == src[runlen]) { + runlen++; + } + if (runlen > 3) { + /* Got a useful amount */ + putDebugChar ('*'); + checksum += '*'; + encode = runlen + ' ' - 4; + putDebugChar(encode); + checksum += encode; + src += runlen; + } else { + src++; + } + } + putDebugChar('#'); + putDebugChar(highhex (checksum)); + putDebugChar(lowhex (checksum)); + } while(kgdb_started && (getDebugChar() != '+')); +} + +/* The string str is prepended with the GDB printout token and sent. Required + in traditional implementations. */ +void +putDebugString(const unsigned char *str, int len) +{ + /* Move SPC forward if we are single-stepping. */ + asm("spchere:"); + asm("move $spc, $r10"); + asm("cmp.d spchere, $r10"); + asm("bne nosstep"); + asm("nop"); + asm("move.d spccont, $r10"); + asm("move $r10, $spc"); + asm("nosstep:"); + + output_buffer[0] = 'O'; + mem2hex(&output_buffer[1], (unsigned char *)str, len); + putpacket(output_buffer); + + asm("spccont:"); +} + +/********************************** Handle exceptions ************************/ +/* Build and send a response packet in order to inform the host the + stub is stopped. TAAn...:r...;n...:r...;n...:r...; + AA = signal number + n... = register number (hex) + r... = register contents + n... = `thread' + r... = thread process ID. This is a hex integer. + n... = other string not starting with valid hex digit. + gdb should ignore this n,r pair and go on to the next. + This way we can extend the protocol. */ +static void +stub_is_stopped(int sigval) +{ + char *ptr = output_buffer; + unsigned int reg_cont; + + /* Send trap type (converted to signal) */ + + *ptr++ = 'T'; + *ptr++ = highhex(sigval); + *ptr++ = lowhex(sigval); + + if (((reg.exs & 0xff00) >> 8) == 0xc) { + + /* Some kind of hardware watchpoint triggered. Find which one + and determine its type (read/write/access). */ + int S, bp, trig_bits = 0, rw_bits = 0; + int trig_mask = 0; + unsigned int *bp_d_regs = &sreg.s3_3; + /* In a lot of cases, the stopped data address will simply be EDA. + In some cases, we adjust it to match the watched data range. + (We don't want to change the actual EDA though). */ + unsigned int stopped_data_address; + /* The S field of EXS. */ + S = (reg.exs & 0xffff0000) >> 16; + + if (S & 1) { + /* Instruction watchpoint. */ + /* FIXME: Check against, and possibly adjust reported EDA. */ + } else { + /* Data watchpoint. Find the one that triggered. */ + for (bp = 0; bp < 6; bp++) { + + /* Dx_RD, Dx_WR in the S field of EXS for this BP. */ + int bitpos_trig = 1 + bp * 2; + /* Dx_BPRD, Dx_BPWR in BP_CTRL for this BP. */ + int bitpos_config = 2 + bp * 4; + + /* Get read/write trig bits for this BP. */ + trig_bits = (S & (3 << bitpos_trig)) >> bitpos_trig; + + /* Read/write config bits for this BP. */ + rw_bits = (sreg.s0_3 & (3 << bitpos_config)) >> bitpos_config; + if (trig_bits) { + /* Sanity check: the BP shouldn't trigger for accesses + that it isn't configured for. */ + if ((rw_bits == 0x1 && trig_bits != 0x1) || + (rw_bits == 0x2 && trig_bits != 0x2)) + panic("Invalid r/w trigging for this BP"); + + /* Mark this BP as trigged for future reference. */ + trig_mask |= (1 << bp); + + if (reg.eda >= bp_d_regs[bp * 2] && + reg.eda <= bp_d_regs[bp * 2 + 1]) { + /* EDA withing range for this BP; it must be the one + we're looking for. */ + stopped_data_address = reg.eda; + break; + } + } + } + if (bp < 6) { + /* Found a trigged BP with EDA within its configured data range. */ + } else if (trig_mask) { + /* Something triggered, but EDA doesn't match any BP's range. */ + for (bp = 0; bp < 6; bp++) { + /* Dx_BPRD, Dx_BPWR in BP_CTRL for this BP. */ + int bitpos_config = 2 + bp * 4; + + /* Read/write config bits for this BP (needed later). */ + rw_bits = (sreg.s0_3 & (3 << bitpos_config)) >> bitpos_config; + + if (trig_mask & (1 << bp)) { + /* EDA within 31 bytes of the configured start address? */ + if (reg.eda + 31 >= bp_d_regs[bp * 2]) { + /* Changing the reported address to match + the start address of the first applicable BP. */ + stopped_data_address = bp_d_regs[bp * 2]; + break; + } else { + /* We continue since we might find another useful BP. */ + printk("EDA doesn't match trigged BP's range"); + } + } + } + } + + /* No match yet? */ + BUG_ON(bp >= 6); + /* Note that we report the type according to what the BP is configured + for (otherwise we'd never report an 'awatch'), not according to how + it trigged. We did check that the trigged bits match what the BP is + configured for though. */ + if (rw_bits == 0x1) { + /* read */ + strncpy(ptr, "rwatch", 6); + ptr += 6; + } else if (rw_bits == 0x2) { + /* write */ + strncpy(ptr, "watch", 5); + ptr += 5; + } else if (rw_bits == 0x3) { + /* access */ + strncpy(ptr, "awatch", 6); + ptr += 6; + } else { + panic("Invalid r/w bits for this BP."); + } + + *ptr++ = ':'; + /* Note that we don't read_register(EDA, ...) */ + ptr = mem2hex_nbo(ptr, (unsigned char *)&stopped_data_address, register_size[EDA]); + *ptr++ = ';'; + } + } + /* Only send PC, frame and stack pointer. */ + read_register(PC, ®_cont); + *ptr++ = highhex(PC); + *ptr++ = lowhex(PC); + *ptr++ = ':'; + ptr = mem2hex(ptr, (unsigned char *)®_cont, register_size[PC]); + *ptr++ = ';'; + + read_register(R8, ®_cont); + *ptr++ = highhex(R8); + *ptr++ = lowhex(R8); + *ptr++ = ':'; + ptr = mem2hex(ptr, (unsigned char *)®_cont, register_size[R8]); + *ptr++ = ';'; + + read_register(SP, ®_cont); + *ptr++ = highhex(SP); + *ptr++ = lowhex(SP); + *ptr++ = ':'; + ptr = mem2hex(ptr, (unsigned char *)®_cont, register_size[SP]); + *ptr++ = ';'; + + /* Send ERP as well; this will save us an entire register fetch in some cases. */ + read_register(ERP, ®_cont); + *ptr++ = highhex(ERP); + *ptr++ = lowhex(ERP); + *ptr++ = ':'; + ptr = mem2hex(ptr, (unsigned char *)®_cont, register_size[ERP]); + *ptr++ = ';'; + + /* null-terminate and send it off */ + *ptr = 0; + putpacket(output_buffer); +} + +/* Returns the size of an instruction that has a delay slot. */ + +int insn_size(unsigned long pc) +{ + unsigned short opcode = *(unsigned short *)pc; + int size = 0; + + switch ((opcode & 0x0f00) >> 8) { + case 0x0: + case 0x9: + case 0xb: + size = 2; + break; + case 0xe: + case 0xf: + size = 6; + break; + case 0xd: + /* Could be 4 or 6; check more bits. */ + if ((opcode & 0xff) == 0xff) + size = 4; + else + size = 6; + break; + default: + panic("Couldn't find size of opcode 0x%x at 0x%lx\n", opcode, pc); + } + + return size; +} + +void register_fixup(int sigval) +{ + /* Compensate for ACR push at the beginning of exception handler. */ + reg.sp += 4; + + /* Standard case. */ + reg.pc = reg.erp; + if (reg.erp & 0x1) { + /* Delay slot bit set. Report as stopped on proper instruction. */ + if (reg.spc) { + /* Rely on SPC if set. */ + reg.pc = reg.spc; + } else { + /* Calculate the PC from the size of the instruction + that the delay slot we're in belongs to. */ + reg.pc += insn_size(reg.erp & ~1) - 1 ; + } + } + + if ((reg.exs & 0x3) == 0x0) { + /* Bits 1 - 0 indicate the type of memory operation performed + by the interrupted instruction. 0 means no memory operation, + and EDA is undefined in that case. We zero it to avoid confusion. */ + reg.eda = 0; + } + + if (sigval == SIGTRAP) { + /* Break 8, single step or hardware breakpoint exception. */ + + /* Check IDX field of EXS. */ + if (((reg.exs & 0xff00) >> 8) == 0x18) { + + /* Break 8. */ + + /* Static (compiled) breakpoints must return to the next instruction + in order to avoid infinite loops (default value of ERP). Dynamic + (gdb-invoked) must subtract the size of the break instruction from + the ERP so that the instruction that was originally in the break + instruction's place will be run when we return from the exception. */ + if (!dynamic_bp) { + /* Assuming that all breakpoints are dynamic from now on. */ + dynamic_bp = 1; + } else { + + /* Only if not in a delay slot. */ + if (!(reg.erp & 0x1)) { + reg.erp -= 2; + reg.pc -= 2; + } + } + + } else if (((reg.exs & 0xff00) >> 8) == 0x3) { + /* Single step. */ + /* Don't fiddle with S1. */ + + } else if (((reg.exs & 0xff00) >> 8) == 0xc) { + + /* Hardware watchpoint exception. */ + + /* SPC has been updated so that we will get a single step exception + when we return, but we don't want that. */ + reg.spc = 0; + + /* Don't fiddle with S1. */ + } + + } else if (sigval == SIGINT) { + /* Nothing special. */ + } +} + +static void insert_watchpoint(char type, int addr, int len) +{ + /* Breakpoint/watchpoint types (GDB terminology): + 0 = memory breakpoint for instructions + (not supported; done via memory write instead) + 1 = hardware breakpoint for instructions (supported) + 2 = write watchpoint (supported) + 3 = read watchpoint (supported) + 4 = access watchpoint (supported) */ + + if (type < '1' || type > '4') { + output_buffer[0] = 0; + return; + } + + /* Read watchpoints are set as access watchpoints, because of GDB's + inability to deal with pure read watchpoints. */ + if (type == '3') + type = '4'; + + if (type == '1') { + /* Hardware (instruction) breakpoint. */ + /* Bit 0 in BP_CTRL holds the configuration for I0. */ + if (sreg.s0_3 & 0x1) { + /* Already in use. */ + gdb_cris_strcpy(output_buffer, error_message[E04]); + return; + } + /* Configure. */ + sreg.s1_3 = addr; + sreg.s2_3 = (addr + len - 1); + sreg.s0_3 |= 1; + } else { + int bp; + unsigned int *bp_d_regs = &sreg.s3_3; + + /* The watchpoint allocation scheme is the simplest possible. + For example, if a region is watched for read and + a write watch is requested, a new watchpoint will + be used. Also, if a watch for a region that is already + covered by one or more existing watchpoints, a new + watchpoint will be used. */ + + /* First, find a free data watchpoint. */ + for (bp = 0; bp < 6; bp++) { + /* Each data watchpoint's control registers occupy 2 bits + (hence the 3), starting at bit 2 for D0 (hence the 2) + with 4 bits between for each watchpoint (yes, the 4). */ + if (!(sreg.s0_3 & (0x3 << (2 + (bp * 4))))) { + break; + } + } + + if (bp > 5) { + /* We're out of watchpoints. */ + gdb_cris_strcpy(output_buffer, error_message[E04]); + return; + } + + /* Configure the control register first. */ + if (type == '3' || type == '4') { + /* Trigger on read. */ + sreg.s0_3 |= (1 << (2 + bp * 4)); + } + if (type == '2' || type == '4') { + /* Trigger on write. */ + sreg.s0_3 |= (2 << (2 + bp * 4)); + } + + /* Ugly pointer arithmetics to configure the watched range. */ + bp_d_regs[bp * 2] = addr; + bp_d_regs[bp * 2 + 1] = (addr + len - 1); + } + + /* Set the S1 flag to enable watchpoints. */ + reg.ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT)); + gdb_cris_strcpy(output_buffer, "OK"); +} + +static void remove_watchpoint(char type, int addr, int len) +{ + /* Breakpoint/watchpoint types: + 0 = memory breakpoint for instructions + (not supported; done via memory write instead) + 1 = hardware breakpoint for instructions (supported) + 2 = write watchpoint (supported) + 3 = read watchpoint (supported) + 4 = access watchpoint (supported) */ + if (type < '1' || type > '4') { + output_buffer[0] = 0; + return; + } + + /* Read watchpoints are set as access watchpoints, because of GDB's + inability to deal with pure read watchpoints. */ + if (type == '3') + type = '4'; + + if (type == '1') { + /* Hardware breakpoint. */ + /* Bit 0 in BP_CTRL holds the configuration for I0. */ + if (!(sreg.s0_3 & 0x1)) { + /* Not in use. */ + gdb_cris_strcpy(output_buffer, error_message[E04]); + return; + } + /* Deconfigure. */ + sreg.s1_3 = 0; + sreg.s2_3 = 0; + sreg.s0_3 &= ~1; + } else { + int bp; + unsigned int *bp_d_regs = &sreg.s3_3; + /* Try to find a watchpoint that is configured for the + specified range, then check that read/write also matches. */ + + /* Ugly pointer arithmetic, since I cannot rely on a + single switch (addr) as there may be several watchpoints with + the same start address for example. */ + + for (bp = 0; bp < 6; bp++) { + if (bp_d_regs[bp * 2] == addr && + bp_d_regs[bp * 2 + 1] == (addr + len - 1)) { + /* Matching range. */ + int bitpos = 2 + bp * 4; + int rw_bits; + + /* Read/write bits for this BP. */ + rw_bits = (sreg.s0_3 & (0x3 << bitpos)) >> bitpos; + + if ((type == '3' && rw_bits == 0x1) || + (type == '2' && rw_bits == 0x2) || + (type == '4' && rw_bits == 0x3)) { + /* Read/write matched. */ + break; + } + } + } + + if (bp > 5) { + /* No watchpoint matched. */ + gdb_cris_strcpy(output_buffer, error_message[E04]); + return; + } + + /* Found a matching watchpoint. Now, deconfigure it by + both disabling read/write in bp_ctrl and zeroing its + start/end addresses. */ + sreg.s0_3 &= ~(3 << (2 + (bp * 4))); + bp_d_regs[bp * 2] = 0; + bp_d_regs[bp * 2 + 1] = 0; + } + + /* Note that we don't clear the S1 flag here. It's done when continuing. */ + gdb_cris_strcpy(output_buffer, "OK"); +} + + + +/* All expected commands are sent from remote.c. Send a response according + to the description in remote.c. */ +void +handle_exception(int sigval) +{ + /* Avoid warning of not used. */ + + USEDFUN(handle_exception); + USEDVAR(internal_stack[0]); + + register_fixup(sigval); + + /* Send response. */ + stub_is_stopped(sigval); + + for (;;) { + output_buffer[0] = '\0'; + getpacket(input_buffer); + switch (input_buffer[0]) { + case 'g': + /* Read registers: g + Success: Each byte of register data is described by two hex digits. + Registers are in the internal order for GDB, and the bytes + in a register are in the same order the machine uses. + Failure: void. */ + { + char *buf; + /* General and special registers. */ + buf = mem2hex(output_buffer, (char *)®, sizeof(registers)); + /* Support registers. */ + /* -1 because of the null termination that mem2hex adds. */ + mem2hex(buf, + (char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)), + 16 * sizeof(unsigned int)); + break; + } + case 'G': + /* Write registers. GXX..XX + Each byte of register data is described by two hex digits. + Success: OK + Failure: void. */ + /* General and special registers. */ + hex2mem((char *)®, &input_buffer[1], sizeof(registers)); + /* Support registers. */ + hex2mem((char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)), + &input_buffer[1] + sizeof(registers), + 16 * sizeof(unsigned int)); + gdb_cris_strcpy(output_buffer, "OK"); + break; + + case 'P': + /* Write register. Pn...=r... + Write register n..., hex value without 0x, with value r..., + which contains a hex value without 0x and two hex digits + for each byte in the register (target byte order). P1f=11223344 means + set register 31 to 44332211. + Success: OK + Failure: E02, E05 */ + { + char *suffix; + int regno = gdb_cris_strtol(&input_buffer[1], &suffix, 16); + int status; + + status = write_register(regno, suffix+1); + + switch (status) { + case E02: + /* Do not support read-only registers. */ + gdb_cris_strcpy(output_buffer, error_message[E02]); + break; + case E05: + /* Do not support non-existing registers. */ + gdb_cris_strcpy(output_buffer, error_message[E05]); + break; + default: + /* Valid register number. */ + gdb_cris_strcpy(output_buffer, "OK"); + break; + } + } + break; + + case 'm': + /* Read from memory. mAA..AA,LLLL + AA..AA is the address and LLLL is the length. + Success: XX..XX is the memory content. Can be fewer bytes than + requested if only part of the data may be read. m6000120a,6c means + retrieve 108 byte from base address 6000120a. + Failure: void. */ + { + char *suffix; + unsigned char *addr = (unsigned char *)gdb_cris_strtol(&input_buffer[1], + &suffix, 16); + int len = gdb_cris_strtol(suffix+1, 0, 16); + + /* Bogus read (i.e. outside the kernel's + segment)? . */ + if (!((unsigned int)addr >= 0xc0000000 && + (unsigned int)addr < 0xd0000000)) + addr = NULL; + + mem2hex(output_buffer, addr, len); + } + break; + + case 'X': + /* Write to memory. XAA..AA,LLLL:XX..XX + AA..AA is the start address, LLLL is the number of bytes, and + XX..XX is the binary data. + Success: OK + Failure: void. */ + case 'M': + /* Write to memory. MAA..AA,LLLL:XX..XX + AA..AA is the start address, LLLL is the number of bytes, and + XX..XX is the hexadecimal data. + Success: OK + Failure: void. */ + { + char *lenptr; + char *dataptr; + unsigned char *addr = (unsigned char *)gdb_cris_strtol(&input_buffer[1], + &lenptr, 16); + int len = gdb_cris_strtol(lenptr+1, &dataptr, 16); + if (*lenptr == ',' && *dataptr == ':') { + if (input_buffer[0] == 'M') { + hex2mem(addr, dataptr + 1, len); + } else /* X */ { + bin2mem(addr, dataptr + 1, len); + } + gdb_cris_strcpy(output_buffer, "OK"); + } + else { + gdb_cris_strcpy(output_buffer, error_message[E06]); + } + } + break; + + case 'c': + /* Continue execution. cAA..AA + AA..AA is the address where execution is resumed. If AA..AA is + omitted, resume at the present address. + Success: return to the executing thread. + Failure: will never know. */ + + if (input_buffer[1] != '\0') { + /* FIXME: Doesn't handle address argument. */ + gdb_cris_strcpy(output_buffer, error_message[E04]); + break; + } + + /* Before continuing, make sure everything is set up correctly. */ + + /* Set the SPC to some unlikely value. */ + reg.spc = 0; + /* Set the S1 flag to 0 unless some watchpoint is enabled (since setting + S1 to 0 would also disable watchpoints). (Note that bits 26-31 in BP_CTRL + are reserved, so don't check against those). */ + if ((sreg.s0_3 & 0x3fff) == 0) { + reg.ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT)); + } + + return; + + case 's': + /* Step. sAA..AA + AA..AA is the address where execution is resumed. If AA..AA is + omitted, resume at the present address. Success: return to the + executing thread. Failure: will never know. */ + + if (input_buffer[1] != '\0') { + /* FIXME: Doesn't handle address argument. */ + gdb_cris_strcpy(output_buffer, error_message[E04]); + break; + } + + /* Set the SPC to PC, which is where we'll return + (deduced previously). */ + reg.spc = reg.pc; + + /* Set the S1 (first stacked, not current) flag, which will + kick into action when we rfe. */ + reg.ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT)); + return; + + case 'Z': + + /* Insert breakpoint or watchpoint, Ztype,addr,length. + Remote protocol says: A remote target shall return an empty string + for an unrecognized breakpoint or watchpoint packet type. */ + { + char *lenptr; + char *dataptr; + int addr = gdb_cris_strtol(&input_buffer[3], &lenptr, 16); + int len = gdb_cris_strtol(lenptr + 1, &dataptr, 16); + char type = input_buffer[1]; + + insert_watchpoint(type, addr, len); + break; + } + + case 'z': + /* Remove breakpoint or watchpoint, Ztype,addr,length. + Remote protocol says: A remote target shall return an empty string + for an unrecognized breakpoint or watchpoint packet type. */ + { + char *lenptr; + char *dataptr; + int addr = gdb_cris_strtol(&input_buffer[3], &lenptr, 16); + int len = gdb_cris_strtol(lenptr + 1, &dataptr, 16); + char type = input_buffer[1]; + + remove_watchpoint(type, addr, len); + break; + } + + + case '?': + /* The last signal which caused a stop. ? + Success: SAA, where AA is the signal number. + Failure: void. */ + output_buffer[0] = 'S'; + output_buffer[1] = highhex(sigval); + output_buffer[2] = lowhex(sigval); + output_buffer[3] = 0; + break; + + case 'D': + /* Detach from host. D + Success: OK, and return to the executing thread. + Failure: will never know */ + putpacket("OK"); + return; + + case 'k': + case 'r': + /* kill request or reset request. + Success: restart of target. + Failure: will never know. */ + kill_restart(); + break; + + case 'C': + case 'S': + case '!': + case 'R': + case 'd': + /* Continue with signal sig. Csig;AA..AA + Step with signal sig. Ssig;AA..AA + Use the extended remote protocol. ! + Restart the target system. R0 + Toggle debug flag. d + Search backwards. tAA:PP,MM + Not supported: E04 */ + + /* FIXME: What's the difference between not supported + and ignored (below)? */ + gdb_cris_strcpy(output_buffer, error_message[E04]); + break; + + default: + /* The stub should ignore other request and send an empty + response ($#). This way we can extend the protocol and GDB + can tell whether the stub it is talking to uses the old or the new. */ + output_buffer[0] = 0; + break; + } + putpacket(output_buffer); + } +} + +void +kgdb_init(void) +{ + reg_intr_vect_rw_mask intr_mask; + reg_ser_rw_intr_mask ser_intr_mask; + + /* Configure the kgdb serial port. */ +#if defined(CONFIG_ETRAX_KGDB_PORT0) + /* Note: no shortcut registered (not handled by multiple_interrupt). + See entry.S. */ + set_exception_vector(SER0_INTR_VECT, kgdb_handle_exception); + /* Enable the ser irq in the global config. */ + intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); + intr_mask.ser0 = 1; + REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + + ser_intr_mask = REG_RD(ser, regi_ser0, rw_intr_mask); + ser_intr_mask.data_avail = regk_ser_yes; + REG_WR(ser, regi_ser0, rw_intr_mask, ser_intr_mask); +#elif defined(CONFIG_ETRAX_KGDB_PORT1) + /* Note: no shortcut registered (not handled by multiple_interrupt). + See entry.S. */ + set_exception_vector(SER1_INTR_VECT, kgdb_handle_exception); + /* Enable the ser irq in the global config. */ + intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); + intr_mask.ser1 = 1; + REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + + ser_intr_mask = REG_RD(ser, regi_ser1, rw_intr_mask); + ser_intr_mask.data_avail = regk_ser_yes; + REG_WR(ser, regi_ser1, rw_intr_mask, ser_intr_mask); +#elif defined(CONFIG_ETRAX_KGDB_PORT2) + /* Note: no shortcut registered (not handled by multiple_interrupt). + See entry.S. */ + set_exception_vector(SER2_INTR_VECT, kgdb_handle_exception); + /* Enable the ser irq in the global config. */ + intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); + intr_mask.ser2 = 1; + REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + + ser_intr_mask = REG_RD(ser, regi_ser2, rw_intr_mask); + ser_intr_mask.data_avail = regk_ser_yes; + REG_WR(ser, regi_ser2, rw_intr_mask, ser_intr_mask); +#elif defined(CONFIG_ETRAX_KGDB_PORT3) + /* Note: no shortcut registered (not handled by multiple_interrupt). + See entry.S. */ + set_exception_vector(SER3_INTR_VECT, kgdb_handle_exception); + /* Enable the ser irq in the global config. */ + intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); + intr_mask.ser3 = 1; + REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + + ser_intr_mask = REG_RD(ser, regi_ser3, rw_intr_mask); + ser_intr_mask.data_avail = regk_ser_yes; + REG_WR(ser, regi_ser3, rw_intr_mask, ser_intr_mask); +#endif + +} +/* Performs a complete re-start from scratch. */ +static void +kill_restart(void) +{ + machine_restart(""); +} + +/* Use this static breakpoint in the start-up only. */ + +void +breakpoint(void) +{ + kgdb_started = 1; + dynamic_bp = 0; /* This is a static, not a dynamic breakpoint. */ + __asm__ volatile ("break 8"); /* Jump to kgdb_handle_breakpoint. */ +} + +/****************************** End of file **********************************/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/Makefile 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,21 @@ +# $Id: Makefile,v 1.11 2004/12/17 10:16:13 starvik Exp $ +# +# Makefile for the linux kernel. +# + +extra-y := head.o + + +obj-y := entry.o traps.o irq.o debugport.o dma.o pinmux.o \ + process.o ptrace.o setup.o signal.o traps.o time.o \ + arbiter.o io.o + +obj-$(CONFIG_ETRAXFS_SIM) += vcs_hook.o + +obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_ETRAX_KGDB) += kgdb.o kgdb_asm.o +obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o +obj-$(CONFIG_MODULES) += crisksyms.o + +clean: + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/pinmux.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,229 @@ +/* + * Allocator for I/O pins. All pins are allocated to GPIO at bootup. + * Unassigned pins and GPIO pins can be allocated to a fixed interface + * or the I/O processor instead. + * + * Copyright (c) 2004 Axis Communications AB. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#define PORT_PINS 18 +#define PORTS 4 + +static char pins[PORTS][PORT_PINS]; +static DEFINE_SPINLOCK(pinmux_lock); + +static void crisv32_pinmux_set(int port); + +int +crisv32_pinmux_init(void) +{ + static int initialized = 0; + + if (!initialized) { + reg_pinmux_rw_pa pa = REG_RD(pinmux, regi_pinmux, rw_pa); + initialized = 1; + pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 = + pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes; + REG_WR(pinmux, regi_pinmux, rw_pa, pa); + crisv32_pinmux_alloc(PORT_B, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_C, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_D, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_E, 0, PORT_PINS - 1, pinmux_gpio); + } + + return 0; +} + +int +crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) + { + if ((pins[port][i] != pinmux_none) && (pins[port][i] != pinmux_gpio) && + (pins[port][i] != mode)) + { + spin_unlock_irqrestore(&pinmux_lock, flags); +#ifdef DEBUG + panic("Pinmux alloc failed!\n"); +#endif + return -EPERM; + } + } + + for (i = first_pin; i <= last_pin; i++) + pins[port][i] = mode; + + crisv32_pinmux_set(port); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int +crisv32_pinmux_alloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch(function) + { + case pinmux_ser1: + ret = crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed); + hwprot.ser1 = regk_pinmux_yes; + break; + case pinmux_ser2: + ret = crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed); + hwprot.ser2 = regk_pinmux_yes; + break; + case pinmux_ser3: + ret = crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed); + hwprot.ser3 = regk_pinmux_yes; + break; + case pinmux_sser0: + ret = crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + hwprot.sser0 = regk_pinmux_yes; + break; + case pinmux_sser1: + ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + hwprot.sser1 = regk_pinmux_yes; + break; + case pinmux_ata0: + ret = crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed); + hwprot.ata0 = regk_pinmux_yes; + break; + case pinmux_ata1: + ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed); + hwprot.ata1 = regk_pinmux_yes; + break; + case pinmux_ata2: + ret = crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed); + hwprot.ata2 = regk_pinmux_yes; + break; + case pinmux_ata3: + ret = crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed); + hwprot.ata2 = regk_pinmux_yes; + break; + case pinmux_ata: + ret = crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed); + hwprot.ata = regk_pinmux_yes; + break; + case pinmux_eth1: + ret = crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed); + hwprot.eth1 = regk_pinmux_yes; + hwprot.eth1_mgm = regk_pinmux_yes; + break; + case pinmux_timer: + ret = crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + hwprot.timer = regk_pinmux_yes; + spin_unlock_irqrestore(&pinmux_lock, flags); + return ret; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void +crisv32_pinmux_set(int port) +{ + int i; + int gpio_val = 0; + int iop_val = 0; + + for (i = 0; i < PORT_PINS; i++) + { + if (pins[port][i] == pinmux_gpio) + gpio_val |= (1 << i); + else if (pins[port][i] == pinmux_iop) + iop_val |= (1 << i); + } + + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_gio + 8*port, gpio_val); + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_iop + 8*port, iop_val); + +#ifdef DEBUG + crisv32_pinmux_dump(); +#endif +} + +int +crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) + pins[port][i] = pinmux_none; + + crisv32_pinmux_set(port); + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +void +crisv32_pinmux_dump(void) +{ + int i, j; + + crisv32_pinmux_init(); + + for (i = 0; i < PORTS; i++) + { + printk("Port %c\n", 'B'+i); + for (j = 0; j < PORT_PINS; j++) + printk(" Pin %d = %d\n", j, pins[i][j]); + } +} + +__initcall(crisv32_pinmux_init); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/process.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2000-2003 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * Mikael Starvik (starvik@axis.com) + * Tobias Anderberg (tobiasa@axis.com), CRISv32 port. + * + * This file handles the architecture-dependent parts of process handling.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void stop_watchdog(void); + +#ifdef CONFIG_ETRAX_GPIO +extern void etrax_gpio_wake_up_check(void); /* Defined in drivers/gpio.c. */ +#endif + +extern int cris_hlt_counter; + +/* We use this if we don't have any better idle routine. */ +void default_idle(void) +{ + local_irq_disable(); + if (!need_resched() && !cris_hlt_counter) { + /* Halt until exception. */ + __asm__ volatile("ei \n\t" + "halt "); + } + local_irq_enable(); +} + +/* + * Free current thread data structures etc.. + */ + +extern void deconfigure_bp(long pid); +void exit_thread(void) +{ + deconfigure_bp(current->pid); +} + +/* + * If the watchdog is enabled, disable interrupts and enter an infinite loop. + * The watchdog will reset the CPU after 0.1s. If the watchdog isn't enabled + * then enable it and wait. + */ +extern void arch_enable_nmi(void); + +void +hard_reset_now(void) +{ + /* + * Don't declare this variable elsewhere. We don't want any other + * code to know about it than the watchdog handler in entry.S and + * this code, implementing hard reset through the watchdog. + */ +#if defined(CONFIG_ETRAX_WATCHDOG) + extern int cause_of_death; +#endif + + printk("*** HARD RESET ***\n"); + local_irq_disable(); + +#if defined(CONFIG_ETRAX_WATCHDOG) + cause_of_death = 0xbedead; +#else +{ + reg_timer_rw_wd_ctrl wd_ctrl = {0}; + + stop_watchdog(); + + wd_ctrl.key = 16; /* Arbitrary key. */ + wd_ctrl.cnt = 1; /* Minimum time. */ + wd_ctrl.cmd = regk_timer_start; + + arch_enable_nmi(); + REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); +} +#endif + + while (1) + ; /* Wait for reset. */ +} + +/* + * Return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *t) +{ + return (unsigned long)user_regs(t->thread_info)->erp; +} + +static void +kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg) +{ + fn(arg); + do_exit(-1); /* Should never be called, return bad exit value. */ +} + +/* Create a kernel thread. */ +int +kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + + /* Don't use r10 since that is set to 0 in copy_thread. */ + regs.r11 = (unsigned long) fn; + regs.r12 = (unsigned long) arg; + regs.erp = (unsigned long) kernel_thread_helper; + regs.ccs = 1 << (I_CCS_BITNR + CCS_SHIFT); + + /* Create the new process. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); +} + +/* + * Setup the child's kernel stack with a pt_regs and call switch_stack() on it. + * It will be unnested during _resume and _ret_from_sys_call when the new thread + * is scheduled. + * + * Also setup the thread switching structure which is used to keep + * thread-specific data during _resumes. + */ + +extern asmlinkage void ret_from_fork(void); + +int +copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct *p, struct pt_regs *regs) +{ + struct pt_regs *childregs; + struct switch_stack *swstack; + + /* + * Put the pt_regs structure at the end of the new kernel stack page and + * fix it up. Note: the task_struct doubles as the kernel stack for the + * task. + */ + childregs = user_regs(p->thread_info); + *childregs = *regs; /* Struct copy of pt_regs. */ + p->set_child_tid = p->clear_child_tid = NULL; + childregs->r10 = 0; /* Child returns 0 after a fork/clone. */ + + /* Set a new TLS ? + * The TLS is in $mof beacuse it is the 5th argument to sys_clone. + */ + if (p->mm && (clone_flags & CLONE_SETTLS)) { + p->thread_info->tls = regs->mof; + } + + /* Put the switch stack right below the pt_regs. */ + swstack = ((struct switch_stack *) childregs) - 1; + + /* Paramater to ret_from_sys_call. 0 is don't restart the syscall. */ + swstack->r9 = 0; + + /* + * We want to return into ret_from_sys_call after the _resume. + * ret_from_fork will call ret_from_sys_call. + */ + swstack->return_ip = (unsigned long) ret_from_fork; + + /* Fix the user-mode and kernel-mode stackpointer. */ + p->thread.usp = usp; + p->thread.ksp = (unsigned long) swstack; + + return 0; +} + +/* + * Be aware of the "magic" 7th argument in the four system-calls below. + * They need the latest stackframe, which is put as the 7th argument by + * entry.S. The previous arguments are dummies or actually used, but need + * to be defined to reach the 7th argument. + * + * N.B.: Another method to get the stackframe is to use current_regs(). But + * it returns the latest stack-frame stacked when going from _user mode_ and + * some of these (at least sys_clone) are called from kernel-mode sometimes + * (for example during kernel_thread, above) and thus cannot use it. Thus, + * to be sure not to get any surprises, we use the method for the other calls + * as well. + */ +asmlinkage int +sys_fork(long r10, long r11, long r12, long r13, long mof, long srp, + struct pt_regs *regs) +{ + return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL); +} + +/* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */ +asmlinkage int +sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid, + unsigned long tls, long srp, struct pt_regs *regs) +{ + if (!newusp) + newusp = rdusp(); + + return do_fork(flags, newusp, regs, 0, parent_tid, child_tid); +} + +/* + * vfork is a system call in i386 because of register-pressure - maybe + * we can remove it and handle it in libc but we put it here until then. + */ +asmlinkage int +sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp, + struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL); +} + +/* sys_execve() executes a new program. */ +asmlinkage int +sys_execve(const char *fname, char **argv, char **envp, long r13, long mof, long srp, + struct pt_regs *regs) +{ + int error; + char *filename; + + filename = getname(fname); + error = PTR_ERR(filename); + + if (IS_ERR(filename)) + goto out; + + error = do_execve(filename, argv, envp, regs); + putname(filename); + out: + return error; +} + +unsigned long +get_wchan(struct task_struct *p) +{ + /* TODO */ + return 0; +} +#undef last_sched +#undef first_sched + +void show_regs(struct pt_regs * regs) +{ + unsigned long usp = rdusp(); + printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", + regs->erp, regs->srp, regs->ccs, usp, regs->mof); + + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); + + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); + + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + regs->r8, regs->r9, regs->r10, regs->r11); + + printk("r12: %08lx r13: %08lx oR10: %08lx\n", + regs->r12, regs->r13, regs->orig_r10); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/ptrace.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2000-2003, Axis Communications AB. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Determines which bits in CCS the user has access to. + * 1 = access, 0 = no access. + */ +#define CCS_MASK 0x00087c00 /* SXNZVC */ + +#define SBIT_USER (1 << (S_CCS_BITNR + CCS_SHIFT)) + +static int put_debugreg(long pid, unsigned int regno, long data); +static long get_debugreg(long pid, unsigned int regno); +static unsigned long get_pseudo_pc(struct task_struct *child); +void deconfigure_bp(long pid); + +extern unsigned long cris_signal_return_page; + +/* + * Get contents of register REGNO in task TASK. + */ +long get_reg(struct task_struct *task, unsigned int regno) +{ + /* USP is a special case, it's not in the pt_regs struct but + * in the tasks thread struct + */ + unsigned long ret; + + if (regno <= PT_EDA) + ret = ((unsigned long *)user_regs(task->thread_info))[regno]; + else if (regno == PT_USP) + ret = task->thread.usp; + else if (regno == PT_PPC) + ret = get_pseudo_pc(task); + else if (regno <= PT_MAX) + ret = get_debugreg(task->pid, regno); + else + ret = 0; + + return ret; +} + +/* + * Write contents of register REGNO in task TASK. + */ +int put_reg(struct task_struct *task, unsigned int regno, unsigned long data) +{ + if (regno <= PT_EDA) + ((unsigned long *)user_regs(task->thread_info))[regno] = data; + else if (regno == PT_USP) + task->thread.usp = data; + else if (regno == PT_PPC) { + /* Write pseudo-PC to ERP only if changed. */ + if (data != get_pseudo_pc(task)) + ((unsigned long *)user_regs(task->thread_info))[PT_ERP] = data; + } else if (regno <= PT_MAX) + return put_debugreg(task->pid, regno, data); + else + return -1; + return 0; +} + +/* + * Called by kernel/ptrace.c when detaching. + * + * Make sure the single step bit is not set. + */ +void +ptrace_disable(struct task_struct *child) +{ + unsigned long tmp; + + /* Deconfigure SPC and S-bit. */ + tmp = get_reg(child, PT_CCS) & ~SBIT_USER; + put_reg(child, PT_CCS, tmp); + put_reg(child, PT_SPC, 0); + + /* Deconfigure any watchpoints associated with the child. */ + deconfigure_bp(child->pid); +} + + +asmlinkage int +sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + unsigned long __user *datap = (unsigned long __user *)data; + + lock_kernel(); + ret = -EPERM; + + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + ret = security_ptrace(current->parent, current); + if (ret) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + + if (child) + get_task_struct(child); + + read_unlock(&tasklist_lock); + + if (!child) + goto out; + + ret = -EPERM; + + if (pid == 1) /* Leave the init process alone! */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + goto out_tsk; + + switch (request) { + /* Read word at location address. */ + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + ret = -EIO; + + /* The signal trampoline page is outside the normal user-addressable + * space but still accessible. This is hack to make it possible to + * access the signal handler code in GDB. + */ + if ((addr & PAGE_MASK) == cris_signal_return_page) { + /* The trampoline page is globally mapped, no page table to traverse.*/ + tmp = *(unsigned long*)addr; + } else { + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + + if (copied != sizeof(tmp)) + break; + } + + ret = put_user(tmp,datap); + break; + } + + /* Read the word at location address in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) + break; + + tmp = get_reg(child, addr >> 2); + ret = put_user(tmp, datap); + break; + } + + /* Write the word at location address. */ + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + ret = 0; + + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + + ret = -EIO; + break; + + /* Write the word at location address in the USER area. */ + case PTRACE_POKEUSR: + ret = -EIO; + if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) + break; + + addr >>= 2; + + if (addr == PT_CCS) { + /* don't allow the tracing process to change stuff like + * interrupt enable, kernel/user bit, dma enables etc. + */ + data &= CCS_MASK; + data |= get_reg(child, PT_CCS) & ~CCS_MASK; + } + if (put_reg(child, addr, data)) + break; + ret = 0; + break; + + case PTRACE_SYSCALL: + case PTRACE_CONT: + ret = -EIO; + + if (!valid_signal(data)) + break; + + /* Continue means no single-step. */ + put_reg(child, PT_SPC, 0); + + if (!get_debugreg(child->pid, PT_BP_CTRL)) { + unsigned long tmp; + /* If no h/w bp configured, disable S bit. */ + tmp = get_reg(child, PT_CCS) & ~SBIT_USER; + put_reg(child, PT_CCS, tmp); + } + + if (request == PTRACE_SYSCALL) { + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + else { + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + + child->exit_code = data; + + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + ret = 0; + + break; + + /* Make the child exit by sending it a sigkill. */ + case PTRACE_KILL: + ret = 0; + + if (child->exit_state == EXIT_ZOMBIE) + break; + + child->exit_code = SIGKILL; + + /* Deconfigure single-step and h/w bp. */ + ptrace_disable(child); + + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + break; + + /* Set the trap flag. */ + case PTRACE_SINGLESTEP: { + unsigned long tmp; + ret = -EIO; + + /* Set up SPC if not set already (in which case we have + no other choice but to trust it). */ + if (!get_reg(child, PT_SPC)) { + /* In case we're stopped in a delay slot. */ + tmp = get_reg(child, PT_ERP) & ~1; + put_reg(child, PT_SPC, tmp); + } + tmp = get_reg(child, PT_CCS) | SBIT_USER; + put_reg(child, PT_CCS, tmp); + + if (!valid_signal(data)) + break; + + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + + /* TODO: set some clever breakpoint mechanism... */ + + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + + } + case PTRACE_DETACH: + ret = ptrace_detach(child, data); + break; + + /* Get all GP registers from the child. */ + case PTRACE_GETREGS: { + int i; + unsigned long tmp; + + for (i = 0; i <= PT_MAX; i++) { + tmp = get_reg(child, i); + + if (put_user(tmp, datap)) { + ret = -EFAULT; + goto out_tsk; + } + + datap++; + } + + ret = 0; + break; + } + + /* Set all GP registers in the child. */ + case PTRACE_SETREGS: { + int i; + unsigned long tmp; + + for (i = 0; i <= PT_MAX; i++) { + if (get_user(tmp, datap)) { + ret = -EFAULT; + goto out_tsk; + } + + if (i == PT_CCS) { + tmp &= CCS_MASK; + tmp |= get_reg(child, PT_CCS) & ~CCS_MASK; + } + + put_reg(child, i, tmp); + datap++; + } + + ret = 0; + break; + } + + default: + ret = ptrace_request(child, request, addr, data); + break; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void do_syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + + if (!(current->ptrace & PT_PTRACED)) + return; + + /* the 0x80 provides a way for the tracing parent to distinguish + between a syscall stop and SIGTRAP delivery */ + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); + + /* + * This isn't the same as continuing with a signal, but it will do for + * normal use. + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + +/* Returns the size of an instruction that has a delay slot. */ + +static int insn_size(struct task_struct *child, unsigned long pc) +{ + unsigned long opcode; + int copied; + int opsize = 0; + + /* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */ + copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0); + if (copied != sizeof(opcode)) + return 0; + + switch ((opcode & 0x0f00) >> 8) { + case 0x0: + case 0x9: + case 0xb: + opsize = 2; + break; + case 0xe: + case 0xf: + opsize = 6; + break; + case 0xd: + /* Could be 4 or 6; check more bits. */ + if ((opcode & 0xff) == 0xff) + opsize = 4; + else + opsize = 6; + break; + default: + panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n", + opcode, pc); + } + + return opsize; +} + +static unsigned long get_pseudo_pc(struct task_struct *child) +{ + /* Default value for PC is ERP. */ + unsigned long pc = get_reg(child, PT_ERP); + + if (pc & 0x1) { + unsigned long spc = get_reg(child, PT_SPC); + /* Delay slot bit set. Report as stopped on proper + instruction. */ + if (spc) { + /* Rely on SPC if set. FIXME: We might want to check + that EXS indicates we stopped due to a single-step + exception. */ + pc = spc; + } else { + /* Calculate the PC from the size of the instruction + that the delay slot we're in belongs to. */ + pc += insn_size(child, pc & ~1) - 1; + } + } + return pc; +} + +static long bp_owner = 0; + +/* Reachable from exit_thread in signal.c, so not static. */ +void deconfigure_bp(long pid) +{ + int bp; + + /* Only deconfigure if the pid is the owner. */ + if (bp_owner != pid) + return; + + for (bp = 0; bp < 6; bp++) { + unsigned long tmp; + /* Deconfigure start and end address (also gets rid of ownership). */ + put_debugreg(pid, PT_BP + 3 + (bp * 2), 0); + put_debugreg(pid, PT_BP + 4 + (bp * 2), 0); + + /* Deconfigure relevant bits in control register. */ + tmp = get_debugreg(pid, PT_BP_CTRL) & ~(3 << (2 + (bp * 4))); + put_debugreg(pid, PT_BP_CTRL, tmp); + } + /* No owner now. */ + bp_owner = 0; +} + +static int put_debugreg(long pid, unsigned int regno, long data) +{ + int ret = 0; + register int old_srs; + +#ifdef CONFIG_ETRAX_KGDB + /* Ignore write, but pretend it was ok if value is 0 + (we don't want POKEUSR/SETREGS failing unnessecarily). */ + return (data == 0) ? ret : -1; +#endif + + /* Simple owner management. */ + if (!bp_owner) + bp_owner = pid; + else if (bp_owner != pid) { + /* Ignore write, but pretend it was ok if value is 0 + (we don't want POKEUSR/SETREGS failing unnessecarily). */ + return (data == 0) ? ret : -1; + } + + /* Remember old SRS. */ + SPEC_REG_RD(SPEC_REG_SRS, old_srs); + /* Switch to BP bank. */ + SUPP_BANK_SEL(BANK_BP); + + switch (regno - PT_BP) { + case 0: + SUPP_REG_WR(0, data); break; + case 1: + case 2: + if (data) + ret = -1; + break; + case 3: + SUPP_REG_WR(3, data); break; + case 4: + SUPP_REG_WR(4, data); break; + case 5: + SUPP_REG_WR(5, data); break; + case 6: + SUPP_REG_WR(6, data); break; + case 7: + SUPP_REG_WR(7, data); break; + case 8: + SUPP_REG_WR(8, data); break; + case 9: + SUPP_REG_WR(9, data); break; + case 10: + SUPP_REG_WR(10, data); break; + case 11: + SUPP_REG_WR(11, data); break; + case 12: + SUPP_REG_WR(12, data); break; + case 13: + SUPP_REG_WR(13, data); break; + case 14: + SUPP_REG_WR(14, data); break; + default: + ret = -1; + break; + } + + /* Restore SRS. */ + SPEC_REG_WR(SPEC_REG_SRS, old_srs); + /* Just for show. */ + NOP(); + NOP(); + NOP(); + + return ret; +} + +static long get_debugreg(long pid, unsigned int regno) +{ + register int old_srs; + register long data; + + if (pid != bp_owner) { + return 0; + } + + /* Remember old SRS. */ + SPEC_REG_RD(SPEC_REG_SRS, old_srs); + /* Switch to BP bank. */ + SUPP_BANK_SEL(BANK_BP); + + switch (regno - PT_BP) { + case 0: + SUPP_REG_RD(0, data); break; + case 1: + case 2: + /* error return value? */ + data = 0; + break; + case 3: + SUPP_REG_RD(3, data); break; + case 4: + SUPP_REG_RD(4, data); break; + case 5: + SUPP_REG_RD(5, data); break; + case 6: + SUPP_REG_RD(6, data); break; + case 7: + SUPP_REG_RD(7, data); break; + case 8: + SUPP_REG_RD(8, data); break; + case 9: + SUPP_REG_RD(9, data); break; + case 10: + SUPP_REG_RD(10, data); break; + case 11: + SUPP_REG_RD(11, data); break; + case 12: + SUPP_REG_RD(12, data); break; + case 13: + SUPP_REG_RD(13, data); break; + case 14: + SUPP_REG_RD(14, data); break; + default: + /* error return value? */ + data = 0; + } + + /* Restore SRS. */ + SPEC_REG_WR(SPEC_REG_SRS, old_srs); + /* Just for show. */ + NOP(); + NOP(); + NOP(); + + return data; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/setup.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,118 @@ +/* + * Display CPU info in /proc/cpuinfo. + * + * Copyright (C) 2003, Axis Communications AB. + */ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_PROC_FS + +#define HAS_FPU 0x0001 +#define HAS_MMU 0x0002 +#define HAS_ETHERNET100 0x0004 +#define HAS_TOKENRING 0x0008 +#define HAS_SCSI 0x0010 +#define HAS_ATA 0x0020 +#define HAS_USB 0x0040 +#define HAS_IRQ_BUG 0x0080 +#define HAS_MMU_BUG 0x0100 + +struct cpu_info { + char *cpu_model; + unsigned short rev; + unsigned short cache_size; + unsigned short flags; +}; + +/* Some of these model are here for historical reasons only. */ +static struct cpu_info cpinfo[] = { + {"ETRAX 1", 0, 0, 0}, + {"ETRAX 2", 1, 0, 0}, + {"ETRAX 3", 2, 0, 0}, + {"ETRAX 4", 3, 0, 0}, + {"Simulator", 7, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA}, + {"ETRAX 100", 8, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG}, + {"ETRAX 100", 9, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA}, + + {"ETRAX 100LX", 10, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB + | HAS_MMU | HAS_MMU_BUG}, + + {"ETRAX 100LX v2", 11, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB + | HAS_MMU}, + + {"ETRAX FS", 32, 32, HAS_ETHERNET100 | HAS_ATA | HAS_MMU}, + + {"Unknown", 0, 0, 0} +}; + +int +show_cpuinfo(struct seq_file *m, void *v) +{ + int i; + int cpu = (int)v - 1; + int entries; + unsigned long revision; + struct cpu_info *info; + + entries = sizeof cpinfo / sizeof(struct cpu_info); + info = &cpinfo[entries - 1]; + +#ifdef CONFIG_SMP + if (!cpu_online(cpu)) + return 0; +#endif + + revision = rdvr(); + + for (i = 0; i < entries; i++) { + if (cpinfo[i].rev == revision) { + info = &cpinfo[i]; + break; + } + } + + return seq_printf(m, + "processor\t: %d\n" + "cpu\t\t: CRIS\n" + "cpu revision\t: %lu\n" + "cpu model\t: %s\n" + "cache size\t: %d KB\n" + "fpu\t\t: %s\n" + "mmu\t\t: %s\n" + "mmu DMA bug\t: %s\n" + "ethernet\t: %s Mbps\n" + "token ring\t: %s\n" + "scsi\t\t: %s\n" + "ata\t\t: %s\n" + "usb\t\t: %s\n" + "bogomips\t: %lu.%02lu\n\n", + + cpu, + revision, + info->cpu_model, + info->cache_size, + info->flags & HAS_FPU ? "yes" : "no", + info->flags & HAS_MMU ? "yes" : "no", + info->flags & HAS_MMU_BUG ? "yes" : "no", + info->flags & HAS_ETHERNET100 ? "10/100" : "10", + info->flags & HAS_TOKENRING ? "4/16 Mbps" : "no", + info->flags & HAS_SCSI ? "yes" : "no", + info->flags & HAS_ATA ? "yes" : "no", + info->flags & HAS_USB ? "yes" : "no", + (loops_per_jiffy * HZ + 500) / 500000, + ((loops_per_jiffy * HZ + 500) / 5000) % 100); +} + +#endif /* CONFIG_PROC_FS */ + +void +show_etrax_copyright(void) +{ + printk(KERN_INFO + "Linux/CRISv32 port on ETRAX FS (C) 2003, 2004 Axis Communications AB\n"); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/signal.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,708 @@ +/* + * Copyright (C) 2003, Axis Communications AB. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +extern unsigned long cris_signal_return_page; + +/* Flag to check if a signal is blockable. */ +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* + * A syscall in CRIS is really a "break 13" instruction, which is 2 + * bytes. The registers is manipulated so upon return the instruction + * will be executed again. + * + * This relies on that PC points to the instruction after the break call. + */ +#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->erp -= 2; + +/* Signal frames. */ +struct signal_frame { + struct sigcontext sc; + unsigned long extramask[_NSIG_WORDS - 1]; + unsigned char retcode[8]; /* Trampoline code. */ +}; + +struct rt_signal_frame { + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; + unsigned char retcode[8]; /* Trampoline code. */ +}; + +int do_signal(int restart, sigset_t *oldset, struct pt_regs *regs); +void keep_debug_flags(unsigned long oldccs, unsigned long oldspc, + struct pt_regs *regs); +/* + * Swap in the new signal mask, and wait for a signal. Define some + * dummy arguments to be able to reach the regs argument. + */ +int +sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, + long srp, struct pt_regs *regs) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + + spin_lock_irq(¤t->sighand->siglock); + + saveset = current->blocked; + + siginitset(¤t->blocked, mask); + + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->r10 = -EINTR; + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + + if (do_signal(0, &saveset, regs)) { + /* + * This point is reached twice: once to call + * the signal handler, then again to return + * from the sigsuspend system call. When + * calling the signal handler, R10 hold the + * signal number as set by do_signal(). The + * sigsuspend call will always return with + * the restored value above; -EINTR. + */ + return regs->r10; + } + } +} + +/* Define some dummy arguments to be able to reach the regs argument. */ +int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, + long mof, long srp, struct pt_regs *regs) +{ + sigset_t saveset; + sigset_t newset; + + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + + sigdelsetmask(&newset, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + + saveset = current->blocked; + current->blocked = newset; + + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->r10 = -EINTR; + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + + if (do_signal(0, &saveset, regs)) { + /* See comment in function above. */ + return regs->r10; + } + } +} + +int +sys_sigaction(int signal, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + int retval; + struct k_sigaction newk; + struct k_sigaction oldk; + + if (act) { + old_sigset_t mask; + + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || + __get_user(newk.sa.sa_handler, &act->sa_handler) || + __get_user(newk.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + + __get_user(newk.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&newk.sa.sa_mask, mask); + } + + retval = do_sigaction(signal, act ? &newk : NULL, oact ? &oldk : NULL); + + if (!retval && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(oldk.sa.sa_handler, &oact->sa_handler) || + __put_user(oldk.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + + __put_user(oldk.sa.sa_flags, &oact->sa_flags); + __put_user(oldk.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return retval; +} + +int +sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) +{ + return do_sigaltstack(uss, uoss, rdusp()); +} + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) +{ + unsigned int err = 0; + unsigned long old_usp; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + /* + * Restore the registers from &sc->regs. sc is already checked + * for VERIFY_READ since the signal_frame was previously + * checked in sys_sigreturn(). + */ + if (__copy_from_user(regs, sc, sizeof(struct pt_regs))) + goto badframe; + + /* Make that the user-mode flag is set. */ + regs->ccs |= (1 << (U_CCS_BITNR + CCS_SHIFT)); + + /* Restore the old USP. */ + err |= __get_user(old_usp, &sc->usp); + wrusp(old_usp); + + return err; + +badframe: + return 1; +} + +/* Define some dummy arguments to be able to reach the regs argument. */ +asmlinkage int +sys_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp, + struct pt_regs *regs) +{ + sigset_t set; + struct signal_frame __user *frame; + unsigned long oldspc = regs->spc; + unsigned long oldccs = regs->ccs; + + frame = (struct signal_frame *) rdusp(); + + /* + * Since the signal is stacked on a dword boundary, the frame + * should be dword aligned here as well. It it's not, then the + * user is trying some funny business. + */ + if (((long)frame) & 3) + goto badframe; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + + if (__get_user(set.sig[0], &frame->sc.oldmask) || + (_NSIG_WORDS > 1 && __copy_from_user(&set.sig[1], + frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + + current->blocked = set; + + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(regs, &frame->sc)) + goto badframe; + + keep_debug_flags(oldccs, oldspc, regs); + + return regs->r10; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* Define some dummy variables to be able to reach the regs argument. */ +asmlinkage int +sys_rt_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp, + struct pt_regs *regs) +{ + sigset_t set; + struct rt_signal_frame __user *frame; + unsigned long oldspc = regs->spc; + unsigned long oldccs = regs->ccs; + + frame = (struct rt_signal_frame *) rdusp(); + + /* + * Since the signal is stacked on a dword boundary, the frame + * should be dword aligned here as well. It it's not, then the + * user is trying some funny business. + */ + if (((long)frame) & 3) + goto badframe; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + + current->blocked = set; + + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) + goto badframe; + + if (do_sigaltstack(&frame->uc.uc_stack, NULL, rdusp()) == -EFAULT) + goto badframe; + + keep_debug_flags(oldccs, oldspc, regs); + + return regs->r10; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* Setup a signal frame. */ +static int +setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, + unsigned long mask) +{ + int err; + unsigned long usp; + + err = 0; + usp = rdusp(); + + /* + * Copy the registers. They are located first in sc, so it's + * possible to use sc directly. + */ + err |= __copy_to_user(sc, regs, sizeof(struct pt_regs)); + + err |= __put_user(mask, &sc->oldmask); + err |= __put_user(usp, &sc->usp); + + return err; +} + +/* Figure out where to put the new signal frame - usually on the stack. */ +static inline void __user * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long sp; + + sp = rdusp(); + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (!on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + /* Make sure the frame is dword-aligned. */ + sp &= ~3; + + return (void __user *)(sp - frame_size); +} + +/* Grab and setup a signal frame. + * + * Basically a lot of state-info is stacked, and arranged for the + * user-mode program to return to the kernel using either a trampiline + * which performs the syscall sigreturn(), or a provided user-mode + * trampoline. + */ +static void +setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, + struct pt_regs * regs) +{ + int err; + unsigned long return_ip; + struct signal_frame __user *frame; + + err = 0; + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); + + if (err) + goto give_sigsegv; + + if (_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + + if (err) + goto give_sigsegv; + + /* + * Set up to return from user-space. If provided, use a stub + * already located in user-space. + */ + if (ka->sa.sa_flags & SA_RESTORER) { + return_ip = (unsigned long)ka->sa.sa_restorer; + } else { + /* Trampoline - the desired return ip is in the signal return page. */ + return_ip = cris_signal_return_page; + + /* + * This is movu.w __NR_sigreturn, r9; break 13; + * + * WE DO NOT USE IT ANY MORE! It's only left here for historical + * reasons and because gdb uses it as a signature to notice + * signal handler stack frames. + */ + err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0)); + err |= __put_user(__NR_sigreturn, (short __user*)(frame->retcode+2)); + err |= __put_user(0xe93d, (short __user*)(frame->retcode+4)); + } + + if (err) + goto give_sigsegv; + + /* + * Set up registers for signal handler. + * + * Where the code enters now. + * Where the code enter later. + * First argument, signo. + */ + regs->erp = (unsigned long) ka->sa.sa_handler; + regs->srp = return_ip; + regs->r10 = sig; + + /* Actually move the USP to reflect the stacked frame. */ + wrusp((unsigned long)frame); + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + + force_sig(SIGSEGV, current); +} + +static void +setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + int err; + unsigned long return_ip; + struct rt_signal_frame __user *frame; + + err = 0; + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + /* TODO: what is the current->exec_domain stuff and invmap ? */ + + err |= __put_user(&frame->info, &frame->pinfo); + err |= __put_user(&frame->uc, &frame->puc); + err |= copy_siginfo_to_user(&frame->info, info); + + if (err) + goto give_sigsegv; + + /* Clear all the bits of the ucontext we don't use. */ + err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext)); + err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + + if (err) + goto give_sigsegv; + + /* + * Set up to return from user-space. If provided, use a stub + * already located in user-space. + */ + if (ka->sa.sa_flags & SA_RESTORER) { + return_ip = (unsigned long) ka->sa.sa_restorer; + } else { + /* Trampoline - the desired return ip is in the signal return page. */ + return_ip = cris_signal_return_page + 6; + + /* + * This is movu.w __NR_rt_sigreturn, r9; break 13; + * + * WE DO NOT USE IT ANY MORE! It's only left here for historical + * reasons and because gdb uses it as a signature to notice + * signal handler stack frames. + */ + err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0)); + + err |= __put_user(__NR_rt_sigreturn, + (short __user*)(frame->retcode+2)); + + err |= __put_user(0xe93d, (short __user*)(frame->retcode+4)); + } + + if (err) + goto give_sigsegv; + + /* + * Set up registers for signal handler. + * + * Where the code enters now. + * Where the code enters later. + * First argument is signo. + * Second argument is (siginfo_t *). + * Third argument is unused. + */ + regs->erp = (unsigned long) ka->sa.sa_handler; + regs->srp = return_ip; + regs->r10 = sig; + regs->r11 = (unsigned long) &frame->info; + regs->r12 = 0; + + /* Actually move the usp to reflect the stacked frame. */ + wrusp((unsigned long)frame); + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + + force_sig(SIGSEGV, current); +} + +/* Invoke a singal handler to, well, handle the signal. */ +extern inline void +handle_signal(int canrestart, unsigned long sig, + siginfo_t *info, struct k_sigaction *ka, + sigset_t *oldset, struct pt_regs * regs) +{ + /* Check if this got called from a system call. */ + if (canrestart) { + /* If so, check system call restarting. */ + switch (regs->r10) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + /* + * This means that the syscall should + * only be restarted if there was no + * handler for the signal, and since + * this point isn't reached unless + * there is a handler, there's no need + * to restart. + */ + regs->r10 = -EINTR; + break; + + case -ERESTARTSYS: + /* + * This means restart the syscall if + * there is no handler, or the handler + * was registered with SA_RESTART. + */ + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->r10 = -EINTR; + break; + } + + /* Fall through. */ + + case -ERESTARTNOINTR: + /* + * This means that the syscall should + * be called again after the signal + * handler returns. + */ + RESTART_CRIS_SYS(regs); + break; + } + } + + /* Set up the stack frame. */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Also note that the regs structure given here as an argument, is the latest + * pushed pt_regs. It may or may not be the same as the first pushed registers + * when the initial usermode->kernelmode transition took place. Therefore + * we can use user_mode(regs) to see if we came directly from kernel or user + * mode below. + */ +int +do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) +{ + int signr; + siginfo_t info; + struct k_sigaction ka; + + /* + * The common case should go fast, which is why this point is + * reached from kernel-mode. If that's the case, just return + * without doing anything. + */ + if (!user_mode(regs)) + return 1; + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + + if (signr > 0) { + /* Deliver the signal. */ + handle_signal(canrestart, signr, &info, &ka, oldset, regs); + return 1; + } + + /* Got here from a system call? */ + if (canrestart) { + /* Restart the system call - no handlers present. */ + if (regs->r10 == -ERESTARTNOHAND || + regs->r10 == -ERESTARTSYS || + regs->r10 == -ERESTARTNOINTR) { + RESTART_CRIS_SYS(regs); + } + + if (regs->r10 == -ERESTART_RESTARTBLOCK){ + regs->r10 = __NR_restart_syscall; + regs->erp -= 2; + } + } + + return 0; +} + +asmlinkage void +ugdb_trap_user(struct thread_info *ti, int sig) +{ + if (((user_regs(ti)->exs & 0xff00) >> 8) != SINGLE_STEP_INTR_VECT) { + /* Zero single-step PC if the reason we stopped wasn't a single + step exception. This is to avoid relying on it when it isn't + reliable. */ + user_regs(ti)->spc = 0; + } + /* FIXME: Filter out false h/w breakpoint hits (i.e. EDA + not withing any configured h/w breakpoint range). Synchronize with + what already exists for kernel debugging. */ + if (((user_regs(ti)->exs & 0xff00) >> 8) == BREAK_8_INTR_VECT) { + /* Break 8: subtract 2 from ERP unless in a delay slot. */ + if (!(user_regs(ti)->erp & 0x1)) + user_regs(ti)->erp -= 2; + } + sys_kill(ti->task->pid, sig); +} + +void +keep_debug_flags(unsigned long oldccs, unsigned long oldspc, + struct pt_regs *regs) +{ + if (oldccs & (1 << Q_CCS_BITNR)) { + /* Pending single step due to single-stepping the break 13 + in the signal trampoline: keep the Q flag. */ + regs->ccs |= (1 << Q_CCS_BITNR); + /* S flag should be set - complain if it's not. */ + if (!(oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT)))) { + printk("Q flag but no S flag?"); + } + regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT)); + /* Assume the SPC is valid and interesting. */ + regs->spc = oldspc; + + } else if (oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT))) { + /* If a h/w bp was set in the signal handler we need + to keep the S flag. */ + regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT)); + /* Don't keep the old SPC though; if we got here due to + a single-step, the Q flag should have been set. */ + } else if (regs->spc) { + /* If we were single-stepping *before* the signal was taken, + we don't want to restore that state now, because GDB will + have forgotten all about it. */ + regs->spc = 0; + regs->ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT)); + } +} + +/* Set up the trampolines on the signal return page. */ +int __init +cris_init_signal(void) +{ + u16* data = (u16*)kmalloc(PAGE_SIZE, GFP_KERNEL); + + /* This is movu.w __NR_sigreturn, r9; break 13; */ + data[0] = 0x9c5f; + data[1] = __NR_sigreturn; + data[2] = 0xe93d; + /* This is movu.w __NR_rt_sigreturn, r9; break 13; */ + data[3] = 0x9c5f; + data[4] = __NR_rt_sigreturn; + data[5] = 0xe93d; + + /* Map to userspace with appropriate permissions (no write access...) */ + cris_signal_return_page = (unsigned long) + __ioremap_prot(virt_to_phys(data), PAGE_SIZE, PAGE_SIGNAL_TRAMPOLINE); + + return 0; +} + +__initcall(cris_init_signal); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/smp.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,348 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define IPI_SCHEDULE 1 +#define IPI_CALL 2 +#define IPI_FLUSH_TLB 4 + +#define FLUSH_ALL (void*)0xffffffff + +/* Vector of locks used for various atomic operations */ +spinlock_t cris_atomic_locks[] = { [0 ... LOCK_COUNT - 1] = SPIN_LOCK_UNLOCKED}; + +/* CPU masks */ +cpumask_t cpu_online_map = CPU_MASK_NONE; +cpumask_t phys_cpu_present_map = CPU_MASK_NONE; + +/* Variables used during SMP boot */ +volatile int cpu_now_booting = 0; +volatile struct thread_info *smp_init_current_idle_thread; + +/* Variables used during IPI */ +static DEFINE_SPINLOCK(call_lock); +static DEFINE_SPINLOCK(tlbstate_lock); + +struct call_data_struct { + void (*func) (void *info); + void *info; + int wait; +}; + +static struct call_data_struct * call_data; + +static struct mm_struct* flush_mm; +static struct vm_area_struct* flush_vma; +static unsigned long flush_addr; + +extern int setup_irq(int, struct irqaction *); + +/* Mode registers */ +static unsigned long irq_regs[NR_CPUS] = +{ + regi_irq, + regi_irq2 +}; + +static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int send_ipi(int vector, int wait, cpumask_t cpu_mask); +static struct irqaction irq_ipi = { crisv32_ipi_interrupt, SA_INTERRUPT, + CPU_MASK_NONE, "ipi", NULL, NULL}; + +extern void cris_mmu_init(void); +extern void cris_timer_init(void); + +/* SMP initialization */ +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + int i; + + /* From now on we can expect IPIs so set them up */ + setup_irq(IPI_INTR_VECT, &irq_ipi); + + /* Mark all possible CPUs as present */ + for (i = 0; i < max_cpus; i++) + cpu_set(i, phys_cpu_present_map); +} + +void __devinit smp_prepare_boot_cpu(void) +{ + /* PGD pointer has moved after per_cpu initialization so + * update the MMU. + */ + pgd_t **pgd; + pgd = (pgd_t**)&per_cpu(current_pgd, smp_processor_id()); + + SUPP_BANK_SEL(1); + SUPP_REG_WR(RW_MM_TLB_PGD, pgd); + SUPP_BANK_SEL(2); + SUPP_REG_WR(RW_MM_TLB_PGD, pgd); + + cpu_set(0, cpu_online_map); + cpu_set(0, phys_cpu_present_map); +} + +void __init smp_cpus_done(unsigned int max_cpus) +{ +} + +/* Bring one cpu online.*/ +static int __init +smp_boot_one_cpu(int cpuid) +{ + unsigned timeout; + struct task_struct *idle; + + idle = fork_idle(cpuid); + if (IS_ERR(idle)) + panic("SMP: fork failed for CPU:%d", cpuid); + + idle->thread_info->cpu = cpuid; + + /* Information to the CPU that is about to boot */ + smp_init_current_idle_thread = idle->thread_info; + cpu_now_booting = cpuid; + + /* Wait for CPU to come online */ + for (timeout = 0; timeout < 10000; timeout++) { + if(cpu_online(cpuid)) { + cpu_now_booting = 0; + smp_init_current_idle_thread = NULL; + return 0; /* CPU online */ + } + udelay(100); + barrier(); + } + + put_task_struct(idle); + idle = NULL; + + printk(KERN_CRIT "SMP: CPU:%d is stuck.\n", cpuid); + return -1; +} + +/* Secondary CPUs starts uing C here. Here we need to setup CPU + * specific stuff such as the local timer and the MMU. */ +void __init smp_callin(void) +{ + extern void cpu_idle(void); + + int cpu = cpu_now_booting; + reg_intr_vect_rw_mask vect_mask = {0}; + + /* Initialise the idle task for this CPU */ + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + + /* Set up MMU */ + cris_mmu_init(); + __flush_tlb_all(); + + /* Setup local timer. */ + cris_timer_init(); + + /* Enable IRQ and idle */ + REG_WR(intr_vect, irq_regs[cpu], rw_mask, vect_mask); + unmask_irq(IPI_INTR_VECT); + unmask_irq(TIMER_INTR_VECT); + local_irq_enable(); + + cpu_set(cpu, cpu_online_map); + cpu_idle(); +} + +/* Stop execution on this CPU.*/ +void stop_this_cpu(void* dummy) +{ + local_irq_disable(); + asm volatile("halt"); +} + +/* Other calls */ +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); +} + +int setup_profiling_timer(unsigned int multiplier) +{ + return -EINVAL; +} + + +/* cache_decay_ticks is used by the scheduler to decide if a process + * is "hot" on one CPU. A higher value means a higher penalty to move + * a process to another CPU. Our cache is rather small so we report + * 1 tick. + */ +unsigned long cache_decay_ticks = 1; + +int __devinit __cpu_up(unsigned int cpu) +{ + smp_boot_one_cpu(cpu); + return cpu_online(cpu) ? 0 : -ENOSYS; +} + +void smp_send_reschedule(int cpu) +{ + cpumask_t cpu_mask = CPU_MASK_NONE; + cpu_set(cpu, cpu_mask); + send_ipi(IPI_SCHEDULE, 0, cpu_mask); +} + +/* TLB flushing + * + * Flush needs to be done on the local CPU and on any other CPU that + * may have the same mapping. The mm->cpu_vm_mask is used to keep track + * of which CPUs that a specific process has been executed on. + */ +void flush_tlb_common(struct mm_struct* mm, struct vm_area_struct* vma, unsigned long addr) +{ + unsigned long flags; + cpumask_t cpu_mask; + + spin_lock_irqsave(&tlbstate_lock, flags); + cpu_mask = (mm == FLUSH_ALL ? CPU_MASK_ALL : mm->cpu_vm_mask); + cpu_clear(smp_processor_id(), cpu_mask); + flush_mm = mm; + flush_vma = vma; + flush_addr = addr; + send_ipi(IPI_FLUSH_TLB, 1, cpu_mask); + spin_unlock_irqrestore(&tlbstate_lock, flags); +} + +void flush_tlb_all(void) +{ + __flush_tlb_all(); + flush_tlb_common(FLUSH_ALL, FLUSH_ALL, 0); +} + +void flush_tlb_mm(struct mm_struct *mm) +{ + __flush_tlb_mm(mm); + flush_tlb_common(mm, FLUSH_ALL, 0); + /* No more mappings in other CPUs */ + cpus_clear(mm->cpu_vm_mask); + cpu_set(smp_processor_id(), mm->cpu_vm_mask); +} + +void flush_tlb_page(struct vm_area_struct *vma, + unsigned long addr) +{ + __flush_tlb_page(vma, addr); + flush_tlb_common(vma->vm_mm, vma, addr); +} + +/* Inter processor interrupts + * + * The IPIs are used for: + * * Force a schedule on a CPU + * * FLush TLB on other CPUs + * * Call a function on other CPUs + */ + +int send_ipi(int vector, int wait, cpumask_t cpu_mask) +{ + int i = 0; + reg_intr_vect_rw_ipi ipi = REG_RD(intr_vect, irq_regs[i], rw_ipi); + int ret = 0; + + /* Calculate CPUs to send to. */ + cpus_and(cpu_mask, cpu_mask, cpu_online_map); + + /* Send the IPI. */ + for_each_cpu_mask(i, cpu_mask) + { + ipi.vector |= vector; + REG_WR(intr_vect, irq_regs[i], rw_ipi, ipi); + } + + /* Wait for IPI to finish on other CPUS */ + if (wait) { + for_each_cpu_mask(i, cpu_mask) { + int j; + for (j = 0 ; j < 1000; j++) { + ipi = REG_RD(intr_vect, irq_regs[i], rw_ipi); + if (!ipi.vector) + break; + udelay(100); + } + + /* Timeout? */ + if (ipi.vector) { + printk("SMP call timeout from %d to %d\n", smp_processor_id(), i); + ret = -ETIMEDOUT; + dump_stack(); + } + } + } + return ret; +} + +/* + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler or from a bottom half handler. + */ +int smp_call_function(void (*func)(void *info), void *info, + int nonatomic, int wait) +{ + cpumask_t cpu_mask = CPU_MASK_ALL; + struct call_data_struct data; + int ret; + + cpu_clear(smp_processor_id(), cpu_mask); + + WARN_ON(irqs_disabled()); + + data.func = func; + data.info = info; + data.wait = wait; + + spin_lock(&call_lock); + call_data = &data; + ret = send_ipi(IPI_CALL, wait, cpu_mask); + spin_unlock(&call_lock); + + return ret; +} + +irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + reg_intr_vect_rw_ipi ipi; + + ipi = REG_RD(intr_vect, irq_regs[smp_processor_id()], rw_ipi); + + if (ipi.vector & IPI_CALL) { + func(info); + } + if (ipi.vector & IPI_FLUSH_TLB) { + if (flush_mm == FLUSH_ALL) + __flush_tlb_all(); + else if (flush_vma == FLUSH_ALL) + __flush_tlb_mm(flush_mm); + else + __flush_tlb_page(flush_vma, flush_addr); + } + + ipi.vector = 0; + REG_WR(intr_vect, irq_regs[smp_processor_id()], rw_ipi, ipi); + + return IRQ_HANDLED; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/time.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,341 @@ +/* $Id: time.c,v 1.19 2005/04/29 05:40:09 starvik Exp $ + * + * linux/arch/cris/arch-v32/kernel/time.c + * + * Copyright (C) 2003 Axis Communications AB + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Watchdog defines */ +#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */ +#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */ +#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) /* Number of 763 counts before watchdog bites */ + +unsigned long timer_regs[NR_CPUS] = +{ + regi_timer, +#ifdef CONFIG_SMP + regi_timer2 +#endif +}; + +extern void update_xtime_from_cmos(void); +extern int set_rtc_mmss(unsigned long nowtime); +extern int setup_irq(int, struct irqaction *); +extern int have_rtc; + +unsigned long get_ns_in_jiffie(void) +{ + reg_timer_r_tmr0_data data; + unsigned long ns; + + data = REG_RD(timer, regi_timer, r_tmr0_data); + ns = (TIMER0_DIV - data) * 10; + return ns; +} + +unsigned long do_slow_gettimeoffset(void) +{ + unsigned long count; + unsigned long usec_count = 0; + + static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */ + static unsigned long jiffies_p = 0; + + /* + * cache volatile jiffies temporarily; we have IRQs turned off. + */ + unsigned long jiffies_t; + + /* The timer interrupt comes from Etrax timer 0. In order to get + * better precision, we check the current value. It might have + * underflowed already though. + */ + + count = REG_RD(timer, regi_timer, r_tmr0_data); + jiffies_t = jiffies; + + /* + * avoiding timer inconsistencies (they are rare, but they happen)... + * there are one problem that must be avoided here: + * 1. the timer counter underflows + */ + if( jiffies_t == jiffies_p ) { + if( count > count_p ) { + /* Timer wrapped, use new count and prescale + * increase the time corresponding to one jiffie + */ + usec_count = 1000000/HZ; + } + } else + jiffies_p = jiffies_t; + count_p = count; + /* Convert timer value to usec */ + /* 100 MHz timer, divide by 100 to get usec */ + usec_count += (TIMER0_DIV - count) / 100; + return usec_count; +} + +/* From timer MDS describing the hardware watchdog: + * 4.3.1 Watchdog Operation + * The watchdog timer is an 8-bit timer with a configurable start value. + * Once started the whatchdog counts downwards with a frequency of 763 Hz + * (100/131072 MHz). When the watchdog counts down to 1, it generates an + * NMI (Non Maskable Interrupt), and when it counts down to 0, it resets the + * chip. + */ +/* This gives us 1.3 ms to do something useful when the NMI comes */ + +/* right now, starting the watchdog is the same as resetting it */ +#define start_watchdog reset_watchdog + +#if defined(CONFIG_ETRAX_WATCHDOG) +static short int watchdog_key = 42; /* arbitrary 7 bit number */ +#endif + +/* number of pages to consider "out of memory". it is normal that the memory + * is used though, so put this really low. + */ + +#define WATCHDOG_MIN_FREE_PAGES 8 + +void +reset_watchdog(void) +{ +#if defined(CONFIG_ETRAX_WATCHDOG) + reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; + + /* only keep watchdog happy as long as we have memory left! */ + if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) { + /* reset the watchdog with the inverse of the old key */ + watchdog_key ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */ + wd_ctrl.cnt = ETRAX_WD_CNT; + wd_ctrl.cmd = regk_timer_start; + wd_ctrl.key = watchdog_key; + REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + } +#endif +} + +/* stop the watchdog - we still need the correct key */ + +void +stop_watchdog(void) +{ +#if defined(CONFIG_ETRAX_WATCHDOG) + reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; + watchdog_key ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */ + wd_ctrl.cnt = ETRAX_WD_CNT; + wd_ctrl.cmd = regk_timer_stop; + wd_ctrl.key = watchdog_key; + REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); +#endif +} + +extern void show_registers(struct pt_regs *regs); + +void +handle_watchdog_bite(struct pt_regs* regs) +{ +#if defined(CONFIG_ETRAX_WATCHDOG) + extern int cause_of_death; + + raw_printk("Watchdog bite\n"); + + /* Check if forced restart or unexpected watchdog */ + if (cause_of_death == 0xbedead) { + while(1); + } + + /* Unexpected watchdog, stop the watchdog and dump registers*/ + stop_watchdog(); + raw_printk("Oops: bitten by watchdog\n"); + show_registers(regs); +#ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + reset_watchdog(); +#endif + while(1) /* nothing */; +#endif +} + +/* last time the cmos clock got updated */ +static long last_rtc_update = 0; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ + +//static unsigned short myjiff; /* used by our debug routine print_timestamp */ + +extern void cris_do_profile(struct pt_regs *regs); + +static inline irqreturn_t +timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + reg_timer_r_masked_intr masked_intr; + reg_timer_rw_ack_intr ack_intr = { 0 }; + + /* Check if the timer interrupt is for us (a tmr0 int) */ + masked_intr = REG_RD(timer, timer_regs[cpu], r_masked_intr); + if (!masked_intr.tmr0) + return IRQ_NONE; + + /* acknowledge the timer irq */ + ack_intr.tmr0 = 1; + REG_WR(timer, timer_regs[cpu], rw_ack_intr, ack_intr); + + /* reset watchdog otherwise it resets us! */ + reset_watchdog(); + + /* Update statistics. */ + update_process_times(user_mode(regs)); + + cris_do_profile(regs); /* Save profiling information */ + + /* The master CPU is responsible for the time keeping. */ + if (cpu != 0) + return IRQ_HANDLED; + + /* call the real timer interrupt handler */ + do_timer(regs); + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + * + * The division here is not time critical since it will run once in + * 11 minutes + */ + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + (xtime.tv_nsec / 1000) >= 500000 - (tick_nsec / 1000) / 2 && + (xtime.tv_nsec / 1000) <= 500000 + (tick_nsec / 1000) / 2) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } + return IRQ_HANDLED; +} + +/* timer is SA_SHIRQ so drivers can add stuff to the timer irq chain + * it needs to be SA_INTERRUPT to make the jiffies update work properly + */ + +static struct irqaction irq_timer = { timer_interrupt, SA_SHIRQ | SA_INTERRUPT, + CPU_MASK_NONE, "timer", NULL, NULL}; + +void __init +cris_timer_init(void) +{ + int cpu = smp_processor_id(); + reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; + reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV; + reg_timer_rw_intr_mask timer_intr_mask; + + /* Setup the etrax timers + * Base frequency is 100MHz, divider 1000000 -> 100 HZ + * We use timer0, so timer1 is free. + * The trig timer is used by the fasttimer API if enabled. + */ + + tmr0_ctrl.op = regk_timer_ld; + tmr0_ctrl.freq = regk_timer_f100; + REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div); + REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */ + tmr0_ctrl.op = regk_timer_run; + REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */ + + /* enable the timer irq */ + timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask); + timer_intr_mask.tmr0 = 1; + REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); +} + +void __init +time_init(void) +{ + reg_intr_vect_rw_mask intr_mask; + + /* probe for the RTC and read it if it exists + * Before the RTC can be probed the loops_per_usec variable needs + * to be initialized to make usleep work. A better value for + * loops_per_usec is calculated by the kernel later once the + * clock has started. + */ + loops_per_usec = 50; + + if(RTC_INIT() < 0) { + /* no RTC, start at 1980 */ + xtime.tv_sec = 0; + xtime.tv_nsec = 0; + have_rtc = 0; + } else { + /* get the current time */ + have_rtc = 1; + update_xtime_from_cmos(); + } + + /* + * Initialize wall_to_monotonic such that adding it to xtime will yield zero, the + * tv_nsec field must be normalized (i.e., 0 <= nsec < NSEC_PER_SEC). + */ + set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec); + + /* Start CPU local timer */ + cris_timer_init(); + + /* enable the timer irq in global config */ + intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); + intr_mask.timer = 1; + REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + + /* now actually register the timer irq handler that calls timer_interrupt() */ + + setup_irq(TIMER_INTR_VECT, &irq_timer); + + /* enable watchdog if we should use one */ + +#if defined(CONFIG_ETRAX_WATCHDOG) + printk("Enabling watchdog...\n"); + start_watchdog(); + + /* If we use the hardware watchdog, we want to trap it as an NMI + and dump registers before it resets us. For this to happen, we + must set the "m" NMI enable flag (which once set, is unset only + when an NMI is taken). + + The same goes for the external NMI, but that doesn't have any + driver or infrastructure support yet. */ + { + unsigned long flags; + local_save_flags(flags); + flags |= (1<<30); /* NMI M flag is at bit 30 */ + local_irq_restore(flags); + } +#endif +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/traps.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2003, Axis Communications AB. + */ + +#include +#include +#include + +#include + +extern void reset_watchdog(void); +extern void stop_watchdog(void); + +extern int raw_printk(const char *fmt, ...); + +void +show_registers(struct pt_regs *regs) +{ + /* + * It's possible to use either the USP register or current->thread.usp. + * USP might not correspond to the current proccess for all cases this + * function is called, and current->thread.usp isn't up to date for the + * current proccess. Experience shows that using USP is the way to go. + */ + unsigned long usp; + unsigned long d_mmu_cause; + unsigned long i_mmu_cause; + + usp = rdusp(); + + raw_printk("CPU: %d\n", smp_processor_id()); + + raw_printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", + regs->erp, regs->srp, regs->ccs, usp, regs->mof); + + raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); + + raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); + + raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + regs->r8, regs->r9, regs->r10, regs->r11); + + raw_printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n", + regs->r12, regs->r13, regs->orig_r10, regs->acr); + + raw_printk("sp: %08lx\n", regs); + + SUPP_BANK_SEL(BANK_IM); + SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause); + + SUPP_BANK_SEL(BANK_DM); + SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause); + + raw_printk(" Data MMU Cause: %08lx\n", d_mmu_cause); + raw_printk("Instruction MMU Cause: %08lx\n", i_mmu_cause); + + raw_printk("Process %s (pid: %d, stackpage: %08lx)\n", + current->comm, current->pid, (unsigned long) current); + + /* Show additional info if in kernel-mode. */ + if (!user_mode(regs)) { + int i; + unsigned char c; + + show_stack(NULL, (unsigned long *) usp); + + /* + * If the previous stack-dump wasn't a kernel one, dump the + * kernel stack now. + */ + if (usp != 0) + show_stack(NULL, NULL); + + raw_printk("\nCode: "); + + if (regs->erp < PAGE_OFFSET) + goto bad_value; + + /* + * Quite often the value at regs->erp doesn't point to the + * interesting instruction, which often is the previous + * instruction. So dump at an offset large enough that the + * instruction decoding should be in sync at the interesting + * point, but small enough to fit on a row. The regs->erp + * location is pointed out in a ksymoops-friendly way by + * wrapping the byte for that address in parenthesis. + */ + for (i = -12; i < 12; i++) { + if (__get_user(c, &((unsigned char *) regs->erp)[i])) { +bad_value: + raw_printk(" Bad IP value."); + break; + } + + if (i == 0) + raw_printk("(%02x) ", c); + else + raw_printk("%02x ", c); + } + + raw_printk("\n"); + } +} + +/* + * This gets called from entry.S when the watchdog has bitten. Show something + * similiar to an Oops dump, and if the kernel if configured to be a nice doggy; + * halt instead of reboot. + */ +void +watchdog_bite_hook(struct pt_regs *regs) +{ +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + local_irq_disable(); + stop_watchdog(); + show_registers(regs); + + while (1) + ; /* Do nothing. */ +#else + show_registers(regs); +#endif +} + +/* This is normally the Oops function. */ +void +die_if_kernel(const char *str, struct pt_regs *regs, long err) +{ + if (user_mode(regs)) + return; + +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + /* + * This printout might take too long and could trigger + * the watchdog normally. If NICE_DOGGY is set, simply + * stop the watchdog during the printout. + */ + stop_watchdog(); +#endif + + raw_printk("%s: %04lx\n", str, err & 0xffff); + + show_registers(regs); + +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + reset_watchdog(); +#endif + + do_exit(SIGSEGV); +} + +void arch_enable_nmi(void) +{ + unsigned long flags; + local_save_flags(flags); + flags |= (1<<30); /* NMI M flag is at bit 30 */ + local_irq_restore(flags); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/vcs_hook.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,96 @@ +// $Id: vcs_hook.c,v 1.2 2003/08/12 12:01:06 starvik Exp $ +// +// Call simulator hook. This is the part running in the +// simulated program. +// + +#include "vcs_hook.h" +#include +#include +#include + +#define HOOK_TRIG_ADDR 0xb7000000 /* hook cvlog model reg address */ +#define HOOK_MEM_BASE_ADDR 0xa0000000 /* csp4 (shared mem) base addr */ + +#define HOOK_DATA(offset) ((unsigned*) HOOK_MEM_BASE_ADDR)[offset] +#define VHOOK_DATA(offset) ((volatile unsigned*) HOOK_MEM_BASE_ADDR)[offset] +#define HOOK_TRIG(funcid) do { *((unsigned *) HOOK_TRIG_ADDR) = funcid; } while(0) +#define HOOK_DATA_BYTE(offset) ((unsigned char*) HOOK_MEM_BASE_ADDR)[offset] + + +// ------------------------------------------------------------------ hook_call +int hook_call( unsigned id, unsigned pcnt, ...) { + va_list ap; + unsigned i; + unsigned ret; +#ifdef USING_SOS + PREEMPT_OFF_SAVE(); +#endif + + // pass parameters + HOOK_DATA(0) = id; + + /* Have to make hook_print_str a special case since we call with a + parameter of byte type. Should perhaps be a separate + hook_call. */ + + if (id == hook_print_str) { + int i; + char *str; + + HOOK_DATA(1) = pcnt; + + va_start(ap, pcnt); + str = (char*)va_arg(ap,unsigned); + + for (i=0; i!=pcnt; i++) { + HOOK_DATA_BYTE(8+i) = str[i]; + } + HOOK_DATA_BYTE(8+i) = 0; /* null byte */ + } + else { + va_start(ap, pcnt); + for( i = 1; i <= pcnt; i++ ) HOOK_DATA(i) = va_arg(ap,unsigned); + va_end(ap); + } + + // read from mem to make sure data has propagated to memory before trigging + *((volatile unsigned*) HOOK_MEM_BASE_ADDR); + + // trigger hook + HOOK_TRIG(id); + + // wait for call to finish + while( VHOOK_DATA(0) > 0 ) {} + + // extract return value + + ret = VHOOK_DATA(1); + +#ifdef USING_SOS + PREEMPT_RESTORE(); +#endif + return ret; +} + +unsigned +hook_buf(unsigned i) +{ + return (HOOK_DATA(i)); +} + +void print_str( const char *str ) { + int i; + for (i=1; str[i]; i++); /* find null at end of string */ + hook_call(hook_print_str, i, str); +} + +// --------------------------------------------------------------- CPU_KICK_DOG +void CPU_KICK_DOG(void) { + (void) hook_call( hook_kick_dog, 0 ); +} + +// ------------------------------------------------------- CPU_WATCHDOG_TIMEOUT +void CPU_WATCHDOG_TIMEOUT( unsigned t ) { + (void) hook_call( hook_dog_timeout, 1, t ); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/kernel/vcs_hook.h 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,42 @@ +// $Id: vcs_hook.h,v 1.1 2003/08/12 12:01:06 starvik Exp $ +// +// Call simulator hook functions + +#ifndef HOOK_H +#define HOOK_H + +int hook_call( unsigned id, unsigned pcnt, ...); + +enum hook_ids { + hook_debug_on = 1, + hook_debug_off, + hook_stop_sim_ok, + hook_stop_sim_fail, + hook_alloc_shared, + hook_ptr_shared, + hook_free_shared, + hook_file2shared, + hook_cmp_shared, + hook_print_params, + hook_sim_time, + hook_stop_sim, + hook_kick_dog, + hook_dog_timeout, + hook_rand, + hook_srand, + hook_rand_range, + hook_print_str, + hook_print_hex, + hook_cmp_offset_shared, + hook_fill_random_shared, + hook_alloc_random_data, + hook_calloc_random_data, + hook_print_int, + hook_print_uint, + hook_fputc, + hook_init_fd, + hook_sbrk + +}; + +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/lib/checksumcopy.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,120 @@ +/* + * A fast checksum+copy routine using movem + * Copyright (c) 1998, 2001, 2003 Axis Communications AB + * + * Authors: Bjorn Wesen + * + * csum_partial_copy_nocheck(const char *src, char *dst, + * int len, unsigned int sum) + */ + + .globl csum_partial_copy_nocheck +csum_partial_copy_nocheck: + + ;; r10 - src + ;; r11 - dst + ;; r12 - length + ;; r13 - checksum + + ;; check for breakeven length between movem and normal word looping versions + ;; we also do _NOT_ want to compute a checksum over more than the + ;; actual length when length < 40 + + cmpu.w 80,$r12 + blo _word_loop + nop + + ;; need to save the registers we use below in the movem loop + ;; this overhead is why we have a check above for breakeven length + ;; only r0 - r8 have to be saved, the other ones are clobber-able + ;; according to the ABI + + subq 9*4,$sp + subq 10*4,$r12 ; update length for the first loop + movem $r8,[$sp] + + ;; do a movem copy and checksum + +1: ;; A failing userspace access (the read) will have this as PC. +_mloop: movem [$r10+],$r9 ; read 10 longwords + movem $r9,[$r11+] ; write 10 longwords + + ;; perform dword checksumming on the 10 longwords + + add.d $r0,$r13 + addc $r1,$r13 + addc $r2,$r13 + addc $r3,$r13 + addc $r4,$r13 + addc $r5,$r13 + addc $r6,$r13 + addc $r7,$r13 + addc $r8,$r13 + addc $r9,$r13 + + ;; fold the carry into the checksum, to avoid having to loop the carry + ;; back into the top + + addc 0,$r13 + addc 0,$r13 ; do it again, since we might have generated a carry + + subq 10*4,$r12 + bge _mloop + nop + + addq 10*4,$r12 ; compensate for last loop underflowing length + + movem [$sp+],$r8 ; restore regs + +_word_loop: + ;; only fold if there is anything to fold. + + cmpq 0,$r13 + beq _no_fold + + ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below + ;; r9 can be used as temporary. + + move.d $r13,$r9 + lsrq 16,$r9 ; r0 = checksum >> 16 + and.d 0xffff,$r13 ; checksum = checksum & 0xffff + add.d $r9,$r13 ; checksum += r0 + move.d $r13,$r9 ; do the same again, maybe we got a carry last add + lsrq 16,$r9 + and.d 0xffff,$r13 + add.d $r9,$r13 + +_no_fold: + cmpq 2,$r12 + blt _no_words + nop + + ;; copy and checksum the rest of the words + + subq 2,$r12 + +2: ;; A failing userspace access for the read below will have this as PC. +_wloop: move.w [$r10+],$r9 + addu.w $r9,$r13 + subq 2,$r12 + bge _wloop + move.w $r9,[$r11+] + + addq 2,$r12 + +_no_words: + ;; see if we have one odd byte more + cmpq 1,$r12 + beq _do_byte + nop + ret + move.d $r13,$r10 + +_do_byte: + ;; copy and checksum the last byte +3: ;; A failing userspace access for the read below will have this as PC. + move.b [$r10],$r9 + addu.b $r9,$r13 + move.b $r9,[$r11] + ret + move.d $r13,$r10 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/lib/checksum.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,111 @@ +/* + * A fast checksum routine using movem + * Copyright (c) 1998-2001, 2003 Axis Communications AB + * + * csum_partial(const unsigned char * buff, int len, unsigned int sum) + */ + + .globl csum_partial +csum_partial: + + ;; r10 - src + ;; r11 - length + ;; r12 - checksum + + ;; check for breakeven length between movem and normal word looping versions + ;; we also do _NOT_ want to compute a checksum over more than the + ;; actual length when length < 40 + + cmpu.w 80,$r11 + blo _word_loop + nop + + ;; need to save the registers we use below in the movem loop + ;; this overhead is why we have a check above for breakeven length + ;; only r0 - r8 have to be saved, the other ones are clobber-able + ;; according to the ABI + + subq 9*4,$sp + subq 10*4,$r11 ; update length for the first loop + movem $r8,[$sp] + + ;; do a movem checksum + +_mloop: movem [$r10+],$r9 ; read 10 longwords + + ;; perform dword checksumming on the 10 longwords + + add.d $r0,$r12 + addc $r1,$r12 + addc $r2,$r12 + addc $r3,$r12 + addc $r4,$r12 + addc $r5,$r12 + addc $r6,$r12 + addc $r7,$r12 + addc $r8,$r12 + addc $r9,$r12 + + ;; fold the carry into the checksum, to avoid having to loop the carry + ;; back into the top + + addc 0,$r12 + addc 0,$r12 ; do it again, since we might have generated a carry + + subq 10*4,$r11 + bge _mloop + nop + + addq 10*4,$r11 ; compensate for last loop underflowing length + + movem [$sp+],$r8 ; restore regs + +_word_loop: + ;; only fold if there is anything to fold. + + cmpq 0,$r12 + beq _no_fold + + ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below. + ;; r9 and r13 can be used as temporaries. + + moveq -1,$r9 ; put 0xffff in r9, faster than move.d 0xffff,r9 + lsrq 16,$r9 + + move.d $r12,$r13 + lsrq 16,$r13 ; r13 = checksum >> 16 + and.d $r9,$r12 ; checksum = checksum & 0xffff + add.d $r13,$r12 ; checksum += r13 + move.d $r12,$r13 ; do the same again, maybe we got a carry last add + lsrq 16,$r13 + and.d $r9,$r12 + add.d $r13,$r12 + +_no_fold: + cmpq 2,$r11 + blt _no_words + nop + + ;; checksum the rest of the words + + subq 2,$r11 + +_wloop: subq 2,$r11 + bge _wloop + addu.w [$r10+],$r12 + + addq 2,$r11 + +_no_words: + ;; see if we have one odd byte more + cmpq 1,$r11 + beq _do_byte + nop + ret + move.d $r12,$r10 + +_do_byte: + ;; copy and checksum the last byte + addu.b [$r10],$r12 + ret + move.d $r12,$r10 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/lib/csumcpfruser.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,69 @@ +/* + * Add-on to transform csum_partial_copy_nocheck in checksumcopy.S into + * csum_partial_copy_from_user by adding exception records. + * + * Copyright (C) 2001, 2003 Axis Communications AB. + * + * Author: Hans-Peter Nilsson. + */ + +#include + +/* Same function body, but a different name. If we just added exception + records to _csum_partial_copy_nocheck and made it generic, we wouldn't + know a user fault from a kernel fault and we would have overhead in + each kernel caller for the error-pointer argument. + + unsigned int csum_partial_copy_from_user + (const char *src, char *dst, int len, unsigned int sum, int *errptr); + + Note that the errptr argument is only set if we encounter an error. + It is conveniently located on the stack, so the normal function body + does not have to handle it. */ + +#define csum_partial_copy_nocheck csum_partial_copy_from_user + +/* There are local labels numbered 1, 2 and 3 present to mark the + different from-user accesses. */ +#include "checksumcopy.S" + + .section .fixup,"ax" + +;; Here from the movem loop; restore stack. +4: + movem [$sp+],$r8 +;; r12 is already decremented. Add back chunk_size-2. + addq 40-2,$r12 + +;; Here from the word loop; r12 is off by 2; add it back. +5: + addq 2,$r12 + +;; Here from a failing single byte. +6: + +;; Signal in *errptr that we had a failing access. + move.d [$sp],$acr + moveq -EFAULT,$r9 + subq 4,$sp + move.d $r9,[$acr] + +;; Clear the rest of the destination area using memset. Preserve the +;; checksum for the readable bytes. + move.d $r13,[$sp] + subq 4,$sp + move.d $r11,$r10 + move $srp,[$sp] + jsr memset + clear.d $r11 + + move [$sp+],$srp + ret + move.d [$sp+],$r10 + + .previous + .section __ex_table,"a" + .dword 1b,4b + .dword 2b,5b + .dword 3b,6b + .previous --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/lib/dram_init.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,120 @@ +/* $Id: dram_init.S,v 1.4 2005/04/24 18:48:32 starvik Exp $ + * + * DRAM/SDRAM initialization - alter with care + * This file is intended to be included from other assembler files + * + * Note: This file may not modify r8 or r9 because they are used to + * carry information from the decompresser to the kernel + * + * Copyright (C) 2000-2003 Axis Communications AB + * + * Authors: Mikael Starvik (starvik@axis.com) + */ + +/* Just to be certain the config file is included, we include it here + * explicitely instead of depending on it being included in the file that + * uses this code. + */ + +#include +#include +#include + + ;; WARNING! The registers r8 and r9 are used as parameters carrying + ;; information from the decompressor (if the kernel was compressed). + ;; They should not be used in the code below. + + ; Refer to BIF MDS for a description of SDRAM initialization + + ; Bank configuration + move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp0), $r0 + move.d CONFIG_ETRAX_SDRAM_GRP0_CONFIG, $r1 + move.d $r1, [$r0] + move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp1), $r0 + move.d CONFIG_ETRAX_SDRAM_GRP1_CONFIG, $r1 + move.d $r1, [$r0] + + ; Calculate value of mrs_data + ; CAS latency = 2 && bus_width = 32 => 0x40 + ; CAS latency = 3 && bus_width = 32 => 0x60 + ; CAS latency = 2 && bus_width = 16 => 0x20 + ; CAS latency = 3 && bus_width = 16 => 0x30 + + ; Check if value is already supplied in kernel config + move.d CONFIG_ETRAX_SDRAM_COMMAND, $r2 + bne _set_timing + nop + + move.d 0x40, $r4 ; Assume 32 bits and CAS latency = 2 + move.d CONFIG_ETRAX_SDRAM_TIMING, $r1 + and.d 0x07, $r1 ; Get CAS latency + cmpq 2, $r1 ; CL = 2 ? + beq _bw_check + nop + move.d 0x60, $r4 + +_bw_check: + ; Assume that group 0 width is equal to group 1. This assumption + ; is wrong for a group 1 only hardware (such as the grand old + ; StorPoint+). + move.d CONFIG_ETRAX_SDRAM_GRP0_CONFIG, $r1 + and.d 0x200, $r1 ; DRAM width is bit 9 + beq _set_timing + lslq 2, $r4 ; mrs_data starts at bit 2 + lsrq 1, $r4 ; 16 bits. Shift down value. + + ; Set timing parameters (refresh off to avoid Guinness TR 83) +_set_timing: + move.d CONFIG_ETRAX_SDRAM_TIMING, $r1 + and.d ~(3 << reg_bif_core_rw_sdram_timing___ref___lsb), $r1 + move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing), $r0 + move.d $r1, [$r0] + + ; Issue NOP command + move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_cmd), $r5 + moveq regk_bif_core_nop, $r1 + move.d $r1, [$r5] + + ; Wait 200us + move.d 10000, $r2 +1: bne 1b + subq 1, $r2 + + ; Issue initialization command sequence + move.d _sdram_commands_start, $r2 + and.d 0x000fffff, $r2 ; Make sure commands are read from flash + move.d _sdram_commands_end, $r3 + and.d 0x000fffff, $r3 +1: clear.d $r6 + move.b [$r2+], $r6 ; Load command + or.d $r4, $r6 ; Add calculated mrs + move.d $r6, [$r5] ; Write rw_sdram_cmd + ; Wait 80 ns between each command + move.d 4000, $r7 +2: bne 2b + subq 1, $r7 + cmp.d $r2, $r3 ; Last command? + bne 1b + nop + + ; Start refresh + move.d CONFIG_ETRAX_SDRAM_TIMING, $r1 + move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing), $r0 + move.d $r1, [$r0] + + ; Initialization finished + ba _sdram_commands_end + nop + +_sdram_commands_start: + .byte regk_bif_core_pre ; Precharge + .byte regk_bif_core_ref ; refresh + .byte regk_bif_core_ref ; refresh + .byte regk_bif_core_ref ; refresh + .byte regk_bif_core_ref ; refresh + .byte regk_bif_core_ref ; refresh + .byte regk_bif_core_ref ; refresh + .byte regk_bif_core_ref ; refresh + .byte regk_bif_core_ref ; refresh + .byte regk_bif_core_mrs ; mrs +_sdram_commands_end: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/lib/hw_settings.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,73 @@ +/* + * $Id: hw_settings.S,v 1.3 2005/04/24 18:36:57 starvik Exp $ + * + * This table is used by some tools to extract hardware parameters. + * The table should be included in the kernel and the decompressor. + * Don't forget to update the tools if you change this table. + * + * Copyright (C) 2001 Axis Communications AB + * + * Authors: Mikael Starvik (starvik@axis.com) + */ + +#include +#include +#include +#include + + .ascii "HW_PARAM_MAGIC" ; Magic number + .dword 0xc0004000 ; Kernel start address + + ; Debug port +#ifdef CONFIG_ETRAX_DEBUG_PORT0 + .dword 0 +#elif defined(CONFIG_ETRAX_DEBUG_PORT1) + .dword 1 +#elif defined(CONFIG_ETRAX_DEBUG_PORT2) + .dword 2 +#elif defined(CONFIG_ETRAX_DEBUG_PORT3) + .dword 3 +#else + .dword 4 ; No debug +#endif + + ; Register values + .dword REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg) + .dword CONFIG_ETRAX_MEM_GRP1_CONFIG + .dword REG_ADDR(bif_core, regi_bif_core, rw_grp2_cfg) + .dword CONFIG_ETRAX_MEM_GRP2_CONFIG + .dword REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg) + .dword CONFIG_ETRAX_MEM_GRP3_CONFIG + .dword REG_ADDR(bif_core, regi_bif_core, rw_grp4_cfg) + .dword CONFIG_ETRAX_MEM_GRP4_CONFIG + .dword REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp0) + .dword CONFIG_ETRAX_SDRAM_GRP0_CONFIG + .dword REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp1) + .dword CONFIG_ETRAX_SDRAM_GRP1_CONFIG + .dword REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing) + .dword CONFIG_ETRAX_SDRAM_TIMING + .dword REG_ADDR(bif_core, regi_bif_core, rw_sdram_cmd) + .dword CONFIG_ETRAX_SDRAM_COMMAND + + .dword REG_ADDR(gio, regi_gio, rw_pa_dout) + .dword CONFIG_ETRAX_DEF_GIO_PA_OUT + .dword REG_ADDR(gio, regi_gio, rw_pa_oe) + .dword CONFIG_ETRAX_DEF_GIO_PA_OE + .dword REG_ADDR(gio, regi_gio, rw_pb_dout) + .dword CONFIG_ETRAX_DEF_GIO_PB_OUT + .dword REG_ADDR(gio, regi_gio, rw_pb_oe) + .dword CONFIG_ETRAX_DEF_GIO_PB_OE + .dword REG_ADDR(gio, regi_gio, rw_pc_dout) + .dword CONFIG_ETRAX_DEF_GIO_PC_OUT + .dword REG_ADDR(gio, regi_gio, rw_pc_oe) + .dword CONFIG_ETRAX_DEF_GIO_PC_OE + .dword REG_ADDR(gio, regi_gio, rw_pd_dout) + .dword CONFIG_ETRAX_DEF_GIO_PD_OUT + .dword REG_ADDR(gio, regi_gio, rw_pd_oe) + .dword CONFIG_ETRAX_DEF_GIO_PD_OE + .dword REG_ADDR(gio, regi_gio, rw_pe_dout) + .dword CONFIG_ETRAX_DEF_GIO_PE_OUT + .dword REG_ADDR(gio, regi_gio, rw_pe_oe) + .dword CONFIG_ETRAX_DEF_GIO_PE_OE + + .dword 0 ; No more register values --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/lib/Makefile 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,6 @@ +# +# Makefile for Etrax-specific library files.. +# + +lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o spinlock.o + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/lib/memset.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,253 @@ +/*#************************************************************************#*/ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# FUNCTION NAME: memset() */ +/*# */ +/*# PARAMETERS: void* dst; Destination address. */ +/*# int c; Value of byte to write. */ +/*# int len; Number of bytes to write. */ +/*# */ +/*# RETURNS: dst. */ +/*# */ +/*# DESCRIPTION: Sets the memory dst of length len bytes to c, as standard. */ +/*# Framework taken from memcpy. This routine is */ +/*# very sensitive to compiler changes in register allocation. */ +/*# Should really be rewritten to avoid this problem. */ +/*# */ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# HISTORY */ +/*# */ +/*# DATE NAME CHANGES */ +/*# ---- ---- ------- */ +/*# 990713 HP Tired of watching this function (or */ +/*# really, the nonoptimized generic */ +/*# implementation) take up 90% of simulator */ +/*# output. Measurements needed. */ +/*# */ +/*#-------------------------------------------------------------------------*/ + +#include + +/* No, there's no macro saying 12*4, since it is "hard" to get it into + the asm in a good way. Thus better to expose the problem everywhere. + */ + +/* Assuming 1 cycle per dword written or read (ok, not really true), and + one per instruction, then 43+3*(n/48-1) <= 24+24*(n/48-1) + so n >= 45.7; n >= 0.9; we win on the first full 48-byte block to set. */ + +#define ZERO_BLOCK_SIZE (1*12*4) + +void *memset(void *pdst, + int c, + size_t plen) +{ + /* Ok. Now we want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. */ + + register char *return_dst __asm__ ("r10") = pdst; + register int n __asm__ ("r12") = plen; + register int lc __asm__ ("r11") = c; + + /* Most apps use memset sanely. Only those memsetting about 3..4 + bytes or less get penalized compared to the generic implementation + - and that's not really sane use. */ + + /* Ugh. This is fragile at best. Check with newer GCC releases, if + they compile cascaded "x |= x << 8" sanely! */ + __asm__("movu.b %0,$r13 \n\ + lslq 8,$r13 \n\ + move.b %0,$r13 \n\ + move.d $r13,%0 \n\ + lslq 16,$r13 \n\ + or.d $r13,%0" + : "=r" (lc) : "0" (lc) : "r13"); + + { + register char *dst __asm__ ("r13") = pdst; + + /* This is NONPORTABLE, but since this whole routine is */ + /* grossly nonportable that doesn't matter. */ + + if (((unsigned long) pdst & 3) != 0 + /* Oops! n=0 must be a legal call, regardless of alignment. */ + && n >= 3) + { + if ((unsigned long)dst & 1) + { + *dst = (char) lc; + n--; + dst++; + } + + if ((unsigned long)dst & 2) + { + *(short *)dst = lc; + n -= 2; + dst += 2; + } + } + + /* Now the fun part. For the threshold value of this, check the equation + above. */ + /* Decide which copying method to use. */ + if (n >= ZERO_BLOCK_SIZE) + { + /* For large copies we use 'movem' */ + + /* It is not optimal to tell the compiler about clobbering any + registers; that will move the saving/restoring of those registers + to the function prologue/epilogue, and make non-movem sizes + suboptimal. + + This method is not foolproof; it assumes that the "asm reg" + declarations at the beginning of the function really are used + here (beware: they may be moved to temporary registers). + This way, we do not have to save/move the registers around into + temporaries; we can safely use them straight away. + + If you want to check that the allocation was right; then + check the equalities in the first comment. It should say + "r13=r13, r12=r12, r11=r11" */ + __asm__ volatile (" \n\ + ;; Check that the register asm declaration got right. \n\ + ;; The GCC manual says it will work, but there *has* been bugs. \n\ + .ifnc %0-%1-%4,$r13-$r12-$r11 \n\ + .err \n\ + .endif \n\ + \n\ + ;; Save the registers we'll clobber in the movem process \n\ + ;; on the stack. Don't mention them to gcc, it will only be \n\ + ;; upset. \n\ + subq 11*4,$sp \n\ + movem $r10,[$sp] \n\ + \n\ + move.d $r11,$r0 \n\ + move.d $r11,$r1 \n\ + move.d $r11,$r2 \n\ + move.d $r11,$r3 \n\ + move.d $r11,$r4 \n\ + move.d $r11,$r5 \n\ + move.d $r11,$r6 \n\ + move.d $r11,$r7 \n\ + move.d $r11,$r8 \n\ + move.d $r11,$r9 \n\ + move.d $r11,$r10 \n\ + \n\ + ;; Now we've got this: \n\ + ;; r13 - dst \n\ + ;; r12 - n \n\ + \n\ + ;; Update n for the first loop \n\ + subq 12*4,$r12 \n\ +0: \n\ + subq 12*4,$r12 \n\ + bge 0b \n\ + movem $r11,[$r13+] \n\ + \n\ + addq 12*4,$r12 ;; compensate for last loop underflowing n \n\ + \n\ + ;; Restore registers from stack \n\ + movem [$sp+],$r10" + + /* Outputs */ : "=r" (dst), "=r" (n) + /* Inputs */ : "0" (dst), "1" (n), "r" (lc)); + } + + /* Either we directly starts copying, using dword copying + in a loop, or we copy as much as possible with 'movem' + and then the last block (<44 bytes) is copied here. + This will work since 'movem' will have updated src,dst,n. */ + + while ( n >= 16 ) + { + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + n -= 16; + } + + /* A switch() is definitely the fastest although it takes a LOT of code. + * Particularly if you inline code this. + */ + switch (n) + { + case 0: + break; + case 1: + *(char*)dst = (char) lc; + break; + case 2: + *(short*)dst = (short) lc; + break; + case 3: + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + case 4: + *((long*)dst)++ = lc; + break; + case 5: + *((long*)dst)++ = lc; + *(char*)dst = (char) lc; + break; + case 6: + *((long*)dst)++ = lc; + *(short*)dst = (short) lc; + break; + case 7: + *((long*)dst)++ = lc; + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + case 8: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + break; + case 9: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(char*)dst = (char) lc; + break; + case 10: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(short*)dst = (short) lc; + break; + case 11: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + case 12: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + break; + case 13: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(char*)dst = (char) lc; + break; + case 14: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(short*)dst = (short) lc; + break; + case 15: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + } + } + + return return_dst; /* destination pointer. */ +} /* memset() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/lib/nand_init.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,179 @@ +##============================================================================= +## +## nand_init.S +## +## The bootrom copies data from the NAND flash to the internal RAM but +## due to a bug/feature we can only trust the 256 first bytes. So this +## code copies more data from NAND flash to internal RAM. Obvioulsy this +## code must fit in the first 256 bytes so alter with care. +## +## Some notes about the bug/feature for future reference: +## The bootrom copies the first 127 KB from NAND flash to internal +## memory. The problem is that it does a bytewise copy. NAND flashes +## does autoincrement on the address so for a 16-bite device each +## read/write increases the address by two. So the copy loop in the +## bootrom will discard every second byte. This is solved by inserting +## zeroes in every second byte in the first erase block. +## +## The bootrom also incorrectly assumes that it can read the flash +## linear with only one read command but the flash will actually +## switch between normal area and spare area if you do that so we +## can't trust more than the first 256 bytes. +## +##============================================================================= + +#include +#include +#include +#include +#include +#include + +;; There are 8-bit NAND flashes and 16-bit NAND flashes. +;; We need to treat them slightly different. +#if CONFIG_ETRAX_FLASH_BUSWIDTH==2 +#define PAGE_SIZE 256 +#else +#error 2 +#define PAGE_SIZE 512 +#endif +#define ERASE_BLOCK 16384 + +;; GPIO pins connected to NAND flash +#define CE 4 +#define CLE 5 +#define ALE 6 +#define BY 7 + +;; Address space for NAND flash +#define NAND_RD_ADDR 0x90000000 +#define NAND_WR_ADDR 0x94000000 + +#define READ_CMD 0x00 + +;; Readability macros +#define CSP_MASK \ + REG_MASK(bif_core, rw_grp3_cfg, gated_csp0) | \ + REG_MASK(bif_core, rw_grp3_cfg, gated_csp1) +#define CSP_VAL \ + REG_STATE(bif_core, rw_grp3_cfg, gated_csp0, rd) | \ + REG_STATE(bif_core, rw_grp3_cfg, gated_csp1, wr) + +;;---------------------------------------------------------------------------- +;; Macros to set/clear GPIO bits + +.macro SET x + or.b (1<<\x),$r9 + move.d $r9, [$r2] +.endm + +.macro CLR x + and.b ~(1<<\x),$r9 + move.d $r9, [$r2] +.endm + +;;---------------------------------------------------------------------------- + +nand_boot: + ;; Check if nand boot was selected + move.d REG_ADDR(config, regi_config, r_bootsel), $r0 + move.d [$r0], $r0 + and.d REG_MASK(config, r_bootsel, boot_mode), $r0 + cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 + bne normal_boot ; No NAND boot + nop + +copy_nand_to_ram: + ;; copy_nand_to_ram + ;; Arguments + ;; r10 - destination + ;; r11 - source offset + ;; r12 - size + ;; r13 - Address to jump to after completion + ;; Note : r10-r12 are clobbered on return + ;; Registers used: + ;; r0 - NAND_RD_ADDR + ;; r1 - NAND_WR_ADDR + ;; r2 - reg_gio_rw_pa_dout + ;; r3 - reg_gio_r_pa_din + ;; r4 - tmp + ;; r5 - byte counter within a page + ;; r6 - reg_pinmux_rw_pa + ;; r7 - reg_gio_rw_pa_oe + ;; r8 - reg_bif_core_rw_grp3_cfg + ;; r9 - reg_gio_rw_pa_dout shadow + move.d 0x90000000, $r0 + move.d 0x94000000, $r1 + move.d REG_ADDR(gio, regi_gio, rw_pa_dout), $r2 + move.d REG_ADDR(gio, regi_gio, r_pa_din), $r3 + move.d REG_ADDR(pinmux, regi_pinmux, rw_pa), $r6 + move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r7 + move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r8 + +#if CONFIG_ETRAX_FLASH_BUSWIDTH==2 + lsrq 1, $r11 +#endif + ;; Set up GPIO + move.d [$r2], $r9 + move.d [$r7], $r4 + or.b (1< + +void *memcpy(void *pdst, + const void *psrc, + size_t pn) +{ + /* Ok. Now we want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register void *return_dst __asm__ ("r10") = pdst; + register char *dst __asm__ ("r13") = pdst; + register const char *src __asm__ ("r11") = psrc; + register int n __asm__ ("r12") = pn; + + + /* When src is aligned but not dst, this makes a few extra needless + cycles. I believe it would take as many to check that the + re-alignment was unnecessary. */ + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes; so we + don't have to check further for overflows. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + n--; + *(char*)dst = *(char*)src; + src++; + dst++; + } + + if ((unsigned long) dst & 2) + { + n -= 2; + *(short*)dst = *(short*)src; + src += 2; + dst += 2; + } + } + + /* Decide which copying method to use. Movem is dirt cheap, so the + overheap is low enough to always use the minimum block size as the + threshold. */ + if (n >= 44) + { + /* For large copies we use 'movem' */ + + /* It is not optimal to tell the compiler about clobbering any + registers; that will move the saving/restoring of those registers + to the function prologue/epilogue, and make non-movem sizes + suboptimal. */ + __asm__ volatile (" \n\ + ;; Check that the register asm declaration got right. \n\ + ;; The GCC manual explicitly says TRT will happen. \n\ + .ifnc %0-%1-%2,$r13-$r11-$r12 \n\ + .err \n\ + .endif \n\ + \n\ + ;; Save the registers we'll use in the movem process \n\ + \n\ + ;; on the stack. \n\ + subq 11*4,$sp \n\ + movem $r10,[$sp] \n\ + \n\ + ;; Now we've got this: \n\ + ;; r11 - src \n\ + ;; r13 - dst \n\ + ;; r12 - n \n\ + \n\ + ;; Update n for the first loop \n\ + subq 44,$r12 \n\ +0: \n\ + movem [$r11+],$r10 \n\ + subq 44,$r12 \n\ + bge 0b \n\ + movem $r10,[$r13+] \n\ + \n\ + addq 44,$r12 ;; compensate for last loop underflowing n \n\ + \n\ + ;; Restore registers from stack \n\ + movem [$sp+],$r10" + + /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n) + /* Inputs */ : "0" (dst), "1" (src), "2" (n)); + + } + + /* Either we directly starts copying, using dword copying + in a loop, or we copy as much as possible with 'movem' + and then the last block (<44 bytes) is copied here. + This will work since 'movem' will have updated src,dst,n. */ + + while ( n >= 16 ) + { + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + n -= 16; + } + + /* A switch() is definitely the fastest although it takes a LOT of code. + * Particularly if you inline code this. + */ + switch (n) + { + case 0: + break; + case 1: + *(char*)dst = *(char*)src; + break; + case 2: + *(short*)dst = *(short*)src; + break; + case 3: + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + case 4: + *((long*)dst)++ = *((long*)src)++; + break; + case 5: + *((long*)dst)++ = *((long*)src)++; + *(char*)dst = *(char*)src; + break; + case 6: + *((long*)dst)++ = *((long*)src)++; + *(short*)dst = *(short*)src; + break; + case 7: + *((long*)dst)++ = *((long*)src)++; + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + case 8: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + break; + case 9: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(char*)dst = *(char*)src; + break; + case 10: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(short*)dst = *(short*)src; + break; + case 11: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + case 12: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + break; + case 13: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(char*)dst = *(char*)src; + break; + case 14: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(short*)dst = *(short*)src; + break; + case 15: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + } + + return return_dst; /* destination pointer. */ +} /* memcpy() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/lib/usercopy.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,470 @@ +/* + * User address space access functions. + * The non-inlined parts of asm-cris/uaccess.h are here. + * + * Copyright (C) 2000, 2003 Axis Communications AB. + * + * Written by Hans-Peter Nilsson. + * Pieces used from memcpy, originally by Kenny Ranerup long time ago. + */ + +#include + +/* Asm:s have been tweaked (within the domain of correctness) to give + satisfactory results for "gcc version 3.2.1 Axis release R53/1.53-v32". + + Check regularly... + + Note that for CRISv32, the PC saved at a bus-fault is the address + *at* the faulting instruction, with a special case for instructions + in delay slots: then it's the address of the branch. Note also that + in contrast to v10, a postincrement in the instruction is *not* + performed at a bus-fault; the register is seen having the original + value in fault handlers. */ + + +/* Copy to userspace. This is based on the memcpy used for + kernel-to-kernel copying; see "string.c". */ + +unsigned long +__copy_user (void __user *pdst, const void *psrc, unsigned long pn) +{ + /* We want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + FIXME: Comment for old gcc version. Check. + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register char *dst __asm__ ("r13") = pdst; + register const char *src __asm__ ("r11") = psrc; + register int n __asm__ ("r12") = pn; + register int retn __asm__ ("r10") = 0; + + + /* When src is aligned but not dst, this makes a few extra needless + cycles. I believe it would take as many to check that the + re-alignment was unnecessary. */ + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes; so we + don't have to check further for overflows. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + __asm_copy_to_user_1 (dst, src, retn); + n--; + } + + if ((unsigned long) dst & 2) + { + __asm_copy_to_user_2 (dst, src, retn); + n -= 2; + } + } + + /* Movem is dirt cheap. The overheap is low enough to always use the + minimum possible block size as the threshold. */ + if (n >= 44) + { + /* For large copies we use 'movem'. */ + + /* It is not optimal to tell the compiler about clobbering any + registers; that will move the saving/restoring of those registers + to the function prologue/epilogue, and make non-movem sizes + suboptimal. */ + __asm__ volatile ("\ + ;; Check that the register asm declaration got right. \n\ + ;; The GCC manual explicitly says TRT will happen. \n\ + .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\ + .err \n\ + .endif \n\ + \n\ + ;; Save the registers we'll use in the movem process \n\ + ;; on the stack. \n\ + subq 11*4,$sp \n\ + movem $r10,[$sp] \n\ + \n\ + ;; Now we've got this: \n\ + ;; r11 - src \n\ + ;; r13 - dst \n\ + ;; r12 - n \n\ + \n\ + ;; Update n for the first loop \n\ + subq 44,$r12 \n\ +0: \n\ + movem [$r11+],$r10 \n\ + subq 44,$r12 \n\ +1: bge 0b \n\ + movem $r10,[$r13+] \n\ +3: \n\ + addq 44,$r12 ;; compensate for last loop underflowing n \n\ + \n\ + ;; Restore registers from stack \n\ + movem [$sp+],$r10 \n\ +2: \n\ + .section .fixup,\"ax\" \n\ +4: \n\ +; When failing on any of the 1..44 bytes in a chunk, we adjust back the \n\ +; source pointer and just drop through to the by-16 and by-4 loops to \n\ +; get the correct number of failing bytes. This necessarily means a \n\ +; few extra exceptions, but invalid user pointers shouldn't happen in \n\ +; time-critical code anyway. \n\ + jump 3b \n\ + subq 44,$r11 \n\ + \n\ + .previous \n\ + .section __ex_table,\"a\" \n\ + .dword 1b,4b \n\ + .previous" + + /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn) + /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn)); + + } + + while (n >= 16) + { + __asm_copy_to_user_16 (dst, src, retn); + n -= 16; + } + + /* Having a separate by-four loops cuts down on cache footprint. + FIXME: Test with and without; increasing switch to be 0..15. */ + while (n >= 4) + { + __asm_copy_to_user_4 (dst, src, retn); + n -= 4; + } + + switch (n) + { + case 0: + break; + case 1: + __asm_copy_to_user_1 (dst, src, retn); + break; + case 2: + __asm_copy_to_user_2 (dst, src, retn); + break; + case 3: + __asm_copy_to_user_3 (dst, src, retn); + break; + } + + return retn; +} + +/* Copy from user to kernel, zeroing the bytes that were inaccessible in + userland. The return-value is the number of bytes that were + inaccessible. */ + +unsigned long +__copy_user_zeroing (void __user *pdst, const void *psrc, unsigned long pn) +{ + /* We want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + FIXME: Comment for old gcc version. Check. + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register char *dst __asm__ ("r13") = pdst; + register const char *src __asm__ ("r11") = psrc; + register int n __asm__ ("r12") = pn; + register int retn __asm__ ("r10") = 0; + + /* The best reason to align src is that we then know that a read-fault + was for aligned bytes; there's no 1..3 remaining good bytes to + pickle. */ + if (((unsigned long) src & 3) != 0) + { + if (((unsigned long) src & 1) && n != 0) + { + __asm_copy_from_user_1 (dst, src, retn); + n--; + } + + if (((unsigned long) src & 2) && n >= 2) + { + __asm_copy_from_user_2 (dst, src, retn); + n -= 2; + } + + /* We only need one check after the unalignment-adjustments, because + if both adjustments were done, either both or neither reference + had an exception. */ + if (retn != 0) + goto copy_exception_bytes; + } + + /* Movem is dirt cheap. The overheap is low enough to always use the + minimum possible block size as the threshold. */ + if (n >= 44) + { + /* It is not optimal to tell the compiler about clobbering any + registers; that will move the saving/restoring of those registers + to the function prologue/epilogue, and make non-movem sizes + suboptimal. */ + __asm__ volatile ("\ + .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\ + .err \n\ + .endif \n\ + \n\ + ;; Save the registers we'll use in the movem process \n\ + ;; on the stack. \n\ + subq 11*4,$sp \n\ + movem $r10,[$sp] \n\ + \n\ + ;; Now we've got this: \n\ + ;; r11 - src \n\ + ;; r13 - dst \n\ + ;; r12 - n \n\ + \n\ + ;; Update n for the first loop \n\ + subq 44,$r12 \n\ +0: \n\ + movem [$r11+],$r10 \n\ + \n\ + subq 44,$r12 \n\ + bge 0b \n\ + movem $r10,[$r13+] \n\ + \n\ +4: \n\ + addq 44,$r12 ;; compensate for last loop underflowing n \n\ + \n\ + ;; Restore registers from stack \n\ + movem [$sp+],$r10 \n\ + .section .fixup,\"ax\" \n\ + \n\ +;; Do not jump back into the loop if we fail. For some uses, we get a \n\ +;; page fault somewhere on the line. Without checking for page limits, \n\ +;; we don't know where, but we need to copy accurately and keep an \n\ +;; accurate count; not just clear the whole line. To do that, we fall \n\ +;; down in the code below, proceeding with smaller amounts. It should \n\ +;; be kept in mind that we have to cater to code like what at one time \n\ +;; was in fs/super.c: \n\ +;; i = size - copy_from_user((void *)page, data, size); \n\ +;; which would cause repeated faults while clearing the remainder of \n\ +;; the SIZE bytes at PAGE after the first fault. \n\ +;; A caveat here is that we must not fall through from a failing page \n\ +;; to a valid page. \n\ + \n\ +3: \n\ + jump 4b ;; Fall through, pretending the fault didn't happen. \n\ + nop \n\ + \n\ + .previous \n\ + .section __ex_table,\"a\" \n\ + .dword 0b,3b \n\ + .previous" + + /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn) + /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn)); + } + + /* Either we directly start copying here, using dword copying in a loop, + or we copy as much as possible with 'movem' and then the last block + (<44 bytes) is copied here. This will work since 'movem' will have + updated src, dst and n. (Except with failing src.) + + Since we want to keep src accurate, we can't use + __asm_copy_from_user_N with N != (1, 2, 4); it updates dst and + retn, but not src (by design; it's value is ignored elsewhere). */ + + while (n >= 4) + { + __asm_copy_from_user_4 (dst, src, retn); + n -= 4; + + if (retn) + goto copy_exception_bytes; + } + + /* If we get here, there were no memory read faults. */ + switch (n) + { + /* These copies are at least "naturally aligned" (so we don't have + to check each byte), due to the src alignment code before the + movem loop. The *_3 case *will* get the correct count for retn. */ + case 0: + /* This case deliberately left in (if you have doubts check the + generated assembly code). */ + break; + case 1: + __asm_copy_from_user_1 (dst, src, retn); + break; + case 2: + __asm_copy_from_user_2 (dst, src, retn); + break; + case 3: + __asm_copy_from_user_3 (dst, src, retn); + break; + } + + /* If we get here, retn correctly reflects the number of failing + bytes. */ + return retn; + +copy_exception_bytes: + /* We already have "retn" bytes cleared, and need to clear the + remaining "n" bytes. A non-optimized simple byte-for-byte in-line + memset is preferred here, since this isn't speed-critical code and + we'd rather have this a leaf-function than calling memset. */ + { + char *endp; + for (endp = dst + n; dst < endp; dst++) + *dst = 0; + } + + return retn + n; +} + +/* Zero userspace. */ + +unsigned long +__do_clear_user (void __user *pto, unsigned long pn) +{ + /* We want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + FIXME: Comment for old gcc version. Check. + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register char *dst __asm__ ("r13") = pto; + register int n __asm__ ("r12") = pn; + register int retn __asm__ ("r10") = 0; + + + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + __asm_clear_1 (dst, retn); + n--; + } + + if ((unsigned long) dst & 2) + { + __asm_clear_2 (dst, retn); + n -= 2; + } + } + + /* Decide which copying method to use. + FIXME: This number is from the "ordinary" kernel memset. */ + if (n >= 48) + { + /* For large clears we use 'movem' */ + + /* It is not optimal to tell the compiler about clobbering any + call-saved registers; that will move the saving/restoring of + those registers to the function prologue/epilogue, and make + non-movem sizes suboptimal. + + This method is not foolproof; it assumes that the "asm reg" + declarations at the beginning of the function really are used + here (beware: they may be moved to temporary registers). + This way, we do not have to save/move the registers around into + temporaries; we can safely use them straight away. + + If you want to check that the allocation was right; then + check the equalities in the first comment. It should say + something like "r13=r13, r11=r11, r12=r12". */ + __asm__ volatile ("\ + .ifnc %0%1%2,$r13$r12$r10 \n\ + .err \n\ + .endif \n\ + \n\ + ;; Save the registers we'll clobber in the movem process \n\ + ;; on the stack. Don't mention them to gcc, it will only be \n\ + ;; upset. \n\ + subq 11*4,$sp \n\ + movem $r10,[$sp] \n\ + \n\ + clear.d $r0 \n\ + clear.d $r1 \n\ + clear.d $r2 \n\ + clear.d $r3 \n\ + clear.d $r4 \n\ + clear.d $r5 \n\ + clear.d $r6 \n\ + clear.d $r7 \n\ + clear.d $r8 \n\ + clear.d $r9 \n\ + clear.d $r10 \n\ + clear.d $r11 \n\ + \n\ + ;; Now we've got this: \n\ + ;; r13 - dst \n\ + ;; r12 - n \n\ + \n\ + ;; Update n for the first loop \n\ + subq 12*4,$r12 \n\ +0: \n\ + subq 12*4,$r12 \n\ +1: \n\ + bge 0b \n\ + movem $r11,[$r13+] \n\ + \n\ + addq 12*4,$r12 ;; compensate for last loop underflowing n \n\ + \n\ + ;; Restore registers from stack \n\ + movem [$sp+],$r10 \n\ +2: \n\ + .section .fixup,\"ax\" \n\ +3: \n\ + movem [$sp],$r10 \n\ + addq 12*4,$r10 \n\ + addq 12*4,$r13 \n\ + movem $r10,[$sp] \n\ + jump 0b \n\ + clear.d $r10 \n\ + \n\ + .previous \n\ + .section __ex_table,\"a\" \n\ + .dword 1b,3b \n\ + .previous" + + /* Outputs */ : "=r" (dst), "=r" (n), "=r" (retn) + /* Inputs */ : "0" (dst), "1" (n), "2" (retn) + /* Clobber */ : "r11"); + } + + while (n >= 16) + { + __asm_clear_16 (dst, retn); + n -= 16; + } + + /* Having a separate by-four loops cuts down on cache footprint. + FIXME: Test with and without; increasing switch to be 0..15. */ + while (n >= 4) + { + __asm_clear_4 (dst, retn); + n -= 4; + } + + switch (n) + { + case 0: + break; + case 1: + __asm_clear_1 (dst, retn); + break; + case 2: + __asm_clear_2 (dst, retn); + break; + case 3: + __asm_clear_3 (dst, retn); + break; + } + + return retn; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/mm/init.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,174 @@ +/* + * Set up paging and the MMU. + * + * Copyright (C) 2000-2003, Axis Communications AB. + * + * Authors: Bjorn Wesen + * Tobias Anderberg , CRISv32 port. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void tlb_init(void); + +/* + * The kernel is already mapped with linear mapping at kseg_c so there's no + * need to map it with a page table. However, head.S also temporarily mapped it + * at kseg_4 thus the ksegs are set up again. Also clear the TLB and do various + * other paging stuff. + */ +void __init +cris_mmu_init(void) +{ + unsigned long mmu_config; + unsigned long mmu_kbase_hi; + unsigned long mmu_kbase_lo; + unsigned short mmu_page_id; + + /* + * Make sure the current pgd table points to something sane, even if it + * is most probably not used until the next switch_mm. + */ + per_cpu(current_pgd, smp_processor_id()) = init_mm.pgd; + +#ifdef CONFIG_SMP + { + pgd_t **pgd; + pgd = (pgd_t**)&per_cpu(current_pgd, smp_processor_id()); + SUPP_BANK_SEL(1); + SUPP_REG_WR(RW_MM_TLB_PGD, pgd); + SUPP_BANK_SEL(2); + SUPP_REG_WR(RW_MM_TLB_PGD, pgd); + } +#endif + + /* Initialise the TLB. Function found in tlb.c. */ + tlb_init(); + + /* Enable exceptions and initialize the kernel segments. */ + mmu_config = ( REG_STATE(mmu, rw_mm_cfg, we, on) | + REG_STATE(mmu, rw_mm_cfg, acc, on) | + REG_STATE(mmu, rw_mm_cfg, ex, on) | + REG_STATE(mmu, rw_mm_cfg, inv, on) | + REG_STATE(mmu, rw_mm_cfg, seg_f, linear) | + REG_STATE(mmu, rw_mm_cfg, seg_e, linear) | + REG_STATE(mmu, rw_mm_cfg, seg_d, page) | + REG_STATE(mmu, rw_mm_cfg, seg_c, linear) | + REG_STATE(mmu, rw_mm_cfg, seg_b, linear) | +#ifndef CONFIG_ETRAXFS_SIM + REG_STATE(mmu, rw_mm_cfg, seg_a, page) | +#else + REG_STATE(mmu, rw_mm_cfg, seg_a, linear) | +#endif + REG_STATE(mmu, rw_mm_cfg, seg_9, page) | + REG_STATE(mmu, rw_mm_cfg, seg_8, page) | + REG_STATE(mmu, rw_mm_cfg, seg_7, page) | + REG_STATE(mmu, rw_mm_cfg, seg_6, page) | + REG_STATE(mmu, rw_mm_cfg, seg_5, page) | + REG_STATE(mmu, rw_mm_cfg, seg_4, page) | + REG_STATE(mmu, rw_mm_cfg, seg_3, page) | + REG_STATE(mmu, rw_mm_cfg, seg_2, page) | + REG_STATE(mmu, rw_mm_cfg, seg_1, page) | + REG_STATE(mmu, rw_mm_cfg, seg_0, page)); + + mmu_kbase_hi = ( REG_FIELD(mmu, rw_mm_kbase_hi, base_f, 0x0) | + REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 0x8) | + REG_FIELD(mmu, rw_mm_kbase_hi, base_d, 0x0) | +#ifndef CONFIG_ETRAXFS_SIM + REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x4) | +#else + REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x0) | +#endif + REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) | +#ifndef CONFIG_ETRAXFS_SIM + REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0x0) | +#else + REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa) | +#endif + REG_FIELD(mmu, rw_mm_kbase_hi, base_9, 0x0) | + REG_FIELD(mmu, rw_mm_kbase_hi, base_8, 0x0)); + + mmu_kbase_lo = ( REG_FIELD(mmu, rw_mm_kbase_lo, base_7, 0x0) | + REG_FIELD(mmu, rw_mm_kbase_lo, base_6, 0x0) | + REG_FIELD(mmu, rw_mm_kbase_lo, base_5, 0x0) | + REG_FIELD(mmu, rw_mm_kbase_lo, base_4, 0x0) | + REG_FIELD(mmu, rw_mm_kbase_lo, base_3, 0x0) | + REG_FIELD(mmu, rw_mm_kbase_lo, base_2, 0x0) | + REG_FIELD(mmu, rw_mm_kbase_lo, base_1, 0x0) | + REG_FIELD(mmu, rw_mm_kbase_lo, base_0, 0x0)); + + mmu_page_id = REG_FIELD(mmu, rw_mm_tlb_hi, pid, 0); + + /* Update the instruction MMU. */ + SUPP_BANK_SEL(BANK_IM); + SUPP_REG_WR(RW_MM_CFG, mmu_config); + SUPP_REG_WR(RW_MM_KBASE_HI, mmu_kbase_hi); + SUPP_REG_WR(RW_MM_KBASE_LO, mmu_kbase_lo); + SUPP_REG_WR(RW_MM_TLB_HI, mmu_page_id); + + /* Update the data MMU. */ + SUPP_BANK_SEL(BANK_DM); + SUPP_REG_WR(RW_MM_CFG, mmu_config); + SUPP_REG_WR(RW_MM_KBASE_HI, mmu_kbase_hi); + SUPP_REG_WR(RW_MM_KBASE_LO, mmu_kbase_lo); + SUPP_REG_WR(RW_MM_TLB_HI, mmu_page_id); + + SPEC_REG_WR(SPEC_REG_PID, 0); + + /* + * The MMU has been enabled ever since head.S but just to make it + * totally obvious enable it here as well. + */ + SUPP_BANK_SEL(BANK_GC); + SUPP_REG_WR(RW_GC_CFG, 0xf); /* IMMU, DMMU, ICache, DCache on */ +} + +void __init +paging_init(void) +{ + int i; + unsigned long zones_size[MAX_NR_ZONES]; + + printk("Setting up paging and the MMU.\n"); + + /* Clear out the init_mm.pgd that will contain the kernel's mappings. */ + for(i = 0; i < PTRS_PER_PGD; i++) + swapper_pg_dir[i] = __pgd(0); + + cris_mmu_init(); + + /* + * Initialize the bad page table and bad page to point to a couple of + * allocated pages. + */ + empty_zero_page = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); + memset((void *) empty_zero_page, 0, PAGE_SIZE); + + /* All pages are DMA'able in Etrax, so put all in the DMA'able zone. */ + zones_size[0] = ((unsigned long) high_memory - PAGE_OFFSET) >> PAGE_SHIFT; + + for (i = 1; i < MAX_NR_ZONES; i++) + zones_size[i] = 0; + + /* + * Use free_area_init_node instead of free_area_init, because it is + * designed for systems where the DRAM starts at an address + * substantially higher than 0, like us (we start at PAGE_OFFSET). This + * saves space in the mem_map page array. + */ + free_area_init_node(0, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); + + mem_map = contig_page_data.node_mem_map; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/mm/intmem.c 2005-07-12 00:27:02.000000000 -0700 @@ -0,0 +1,139 @@ +/* + * Simple allocator for internal RAM in ETRAX FS + * + * Copyright (c) 2004 Axis Communications AB. + */ + +#include +#include +#include +#include + +#define STATUS_FREE 0 +#define STATUS_ALLOCATED 1 + +struct intmem_allocation { + struct list_head entry; + unsigned int size; + unsigned offset; + char status; +}; + + +static struct list_head intmem_allocations; +static void* intmem_virtual; + +static void crisv32_intmem_init(void) +{ + static int initiated = 0; + if (!initiated) { + struct intmem_allocation* alloc = + (struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL); + INIT_LIST_HEAD(&intmem_allocations); + intmem_virtual = ioremap(MEM_INTMEM_START, MEM_INTMEM_SIZE); + initiated = 1; + alloc->size = MEM_INTMEM_SIZE; + alloc->offset = 0; + alloc->status = STATUS_FREE; + list_add_tail(&alloc->entry, &intmem_allocations); + } +} + +void* crisv32_intmem_alloc(unsigned size, unsigned align) +{ + struct intmem_allocation* allocation; + struct intmem_allocation* tmp; + void* ret = NULL; + + preempt_disable(); + crisv32_intmem_init(); + + list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) { + int alignment = allocation->offset % align; + alignment = alignment ? align - alignment : alignment; + + if (allocation->status == STATUS_FREE && + allocation->size >= size + alignment) { + if (allocation->size > size + alignment) { + struct intmem_allocation* alloc = + (struct intmem_allocation*) + kmalloc(sizeof *alloc, GFP_ATOMIC); + alloc->status = STATUS_FREE; + alloc->size = allocation->size - size - alignment; + alloc->offset = allocation->offset + size; + list_add(&alloc->entry, &allocation->entry); + + if (alignment) { + struct intmem_allocation* tmp; + tmp = (struct intmem_allocation*) + kmalloc(sizeof *tmp, GFP_ATOMIC); + tmp->offset = allocation->offset; + tmp->size = alignment; + tmp->status = STATUS_FREE; + allocation->offset += alignment; + list_add_tail(&tmp->entry, &allocation->entry); + } + } + allocation->status = STATUS_ALLOCATED; + allocation->size = size; + ret = (void*)((int)intmem_virtual + allocation->offset); + } + } + preempt_enable(); + return ret; +} + +void crisv32_intmem_free(void* addr) +{ + struct intmem_allocation* allocation; + struct intmem_allocation* tmp; + + if (addr == NULL) + return; + + preempt_disable(); + crisv32_intmem_init(); + + list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) { + if (allocation->offset == (int)(addr - intmem_virtual)) { + struct intmem_allocation* prev = + list_entry(allocation->entry.prev, + struct intmem_allocation, entry); + struct intmem_allocation* next = + list_entry(allocation->entry.next, + struct intmem_allocation, entry); + + allocation->status = STATUS_FREE; + /* Join with prev and/or next if also free */ + if (prev->status == STATUS_FREE) { + prev->size += allocation->size; + list_del(&allocation->entry); + kfree(allocation); + allocation = prev; + } + if (next->status == STATUS_FREE) { + allocation->size += next->size; + list_del(&next->entry); + kfree(next); + } + preempt_enable(); + return; + } + } + preempt_enable(); +} + +void* crisv32_intmem_phys_to_virt(unsigned long addr) +{ + return (void*)(addr - MEM_INTMEM_START+ + (unsigned long)intmem_virtual); +} + +unsigned long crisv32_intmem_virt_to_phys(void* addr) +{ + return (unsigned long)((unsigned long )addr - + (unsigned long)intmem_virtual + MEM_INTMEM_START); +} + + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/mm/Makefile 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,3 @@ +# Makefile for the Linux/cris parts of the memory manager. + +obj-y := mmu.o init.o tlb.o intmem.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/mm/mmu.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2003 Axis Communications AB + * + * Authors: Mikael Starvik (starvik@axis.com) + * + * Code for the fault low-level handling routines. + * + */ + +#include +#include + +; Save all register. Must save in same order as struct pt_regs. +.macro SAVE_ALL + subq 12, $sp + move $erp, [$sp] + subq 4, $sp + move $srp, [$sp] + subq 4, $sp + move $ccs, [$sp] + subq 4, $sp + move $spc, [$sp] + subq 4, $sp + move $mof, [$sp] + subq 4, $sp + move $srs, [$sp] + subq 4, $sp + move.d $acr, [$sp] + subq 14*4, $sp + movem $r13, [$sp] + subq 4, $sp + move.d $r10, [$sp] +.endm + +; Bus fault handler. Extracts relevant information and calls mm subsystem +; to handle the fault. +.macro MMU_BUS_FAULT_HANDLER handler, mmu, we, ex + .globl \handler +\handler: + SAVE_ALL + move \mmu, $srs ; Select MMU support register bank + move.d $sp, $r11 ; regs + moveq 1, $r12 ; protection fault + moveq \we, $r13 ; write exception? + orq \ex << 1, $r13 ; execute? + move $s3, $r10 ; rw_mm_cause + and.d ~8191, $r10 ; Get faulting page start address + + jsr do_page_fault + nop + ba ret_from_intr + nop +.endm + +; Refill handler. Three cases may occur: +; 1. PMD and PTE exists in mm subsystem but not in TLB +; 2. PMD exists but not PTE +; 3. PMD doesn't exist +; The code below handles case 1 and calls the mm subsystem for case 2 and 3. +; Do not touch this code without very good reasons and extensive testing. +; Note that the code is optimized to minimize stalls (makes the code harder +; to read). +; +; Each page is 8 KB. Each PMD holds 8192/4 PTEs (each PTE is 4 bytes) so each +; PMD holds 16 MB of virtual memory. +; Bits 0-12 : Offset within a page +; Bits 13-23 : PTE offset within a PMD +; Bits 24-31 : PMD offset within the PGD + +.macro MMU_REFILL_HANDLER handler, mmu + .globl \handler +\handler: + subq 4, $sp +; (The pipeline stalls for one cycle; $sp used as address in the next cycle.) + move $srs, [$sp] + subq 4, $sp + move \mmu, $srs ; Select MMU support register bank + move.d $acr, [$sp] + subq 4, $sp + move.d $r0, [$sp] +#ifdef CONFIG_SMP + move $s7, $acr ; PGD +#else + move.d per_cpu__current_pgd, $acr ; PGD +#endif + ; Look up PMD in PGD + move $s3, $r0 ; rw_mm_cause + lsrq 24, $r0 ; Get PMD index into PGD (bit 24-31) + move.d [$acr], $acr ; PGD for the current process + addi $r0.d, $acr, $acr + move $s3, $r0 ; rw_mm_cause + move.d [$acr], $acr ; Get PMD + beq 1f + ; Look up PTE in PMD + lsrq PAGE_SHIFT, $r0 + and.w PAGE_MASK, $acr ; Remove PMD flags + and.d 0x7ff, $r0 ; Get PTE index into PMD (bit 13-23) + addi $r0.d, $acr, $acr + move.d [$acr], $acr ; Get PTE + beq 2f + move.d [$sp+], $r0 ; Pop r0 in delayslot + ; Store in TLB + move $acr, $s5 + ; Return + move.d [$sp+], $acr + move [$sp], $srs + addq 4, $sp + rete + rfe +1: ; PMD missing, let the mm subsystem fix it up. + move.d [$sp+], $r0 ; Pop r0 +2: ; PTE missing, let the mm subsystem fix it up. + move.d [$sp+], $acr + move [$sp], $srs + addq 4, $sp + SAVE_ALL + move \mmu, $srs + move.d $sp, $r11 ; regs + clear.d $r12 ; Not a protection fault + move.w PAGE_MASK, $acr + move $s3, $r10 ; rw_mm_cause + btstq 9, $r10 ; Check if write access + smi $r13 + and.w PAGE_MASK, $r10 ; Get VPN (virtual address) + jsr do_page_fault + and.w $acr, $r10 + ; Return + ba ret_from_intr + nop +.endm + + ; This is the MMU bus fault handlers. + +MMU_REFILL_HANDLER i_mmu_refill, 1 +MMU_BUS_FAULT_HANDLER i_mmu_invalid, 1, 0, 0 +MMU_BUS_FAULT_HANDLER i_mmu_access, 1, 0, 0 +MMU_BUS_FAULT_HANDLER i_mmu_execute, 1, 0, 1 +MMU_REFILL_HANDLER d_mmu_refill, 2 +MMU_BUS_FAULT_HANDLER d_mmu_invalid, 2, 0, 0 +MMU_BUS_FAULT_HANDLER d_mmu_access, 2, 0, 0 +MMU_BUS_FAULT_HANDLER d_mmu_write, 2, 1, 0 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/mm/tlb.c 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,208 @@ +/* + * Low level TLB handling. + * + * Copyright (C) 2000-2003, Axis Communications AB. + * + * Authors: Bjorn Wesen + * Tobias Anderberg , CRISv32 port. + */ + +#include +#include +#include +#include + +#define UPDATE_TLB_SEL_IDX(val) \ +do { \ + unsigned long tlb_sel; \ + \ + tlb_sel = REG_FIELD(mmu, rw_mm_tlb_sel, idx, val); \ + SUPP_REG_WR(RW_MM_TLB_SEL, tlb_sel); \ +} while(0) + +#define UPDATE_TLB_HILO(tlb_hi, tlb_lo) \ +do { \ + SUPP_REG_WR(RW_MM_TLB_HI, tlb_hi); \ + SUPP_REG_WR(RW_MM_TLB_LO, tlb_lo); \ +} while(0) + +/* + * The TLB can host up to 256 different mm contexts at the same time. The running + * context is found in the PID register. Each TLB entry contains a page_id that + * has to match the PID register to give a hit. page_id_map keeps track of which + * mm's is assigned to which page_id's, making sure it's known when to + * invalidate TLB entries. + * + * The last page_id is never running, it is used as an invalid page_id so that + * it's possible to make TLB entries that will nerver match. + * + * Note; the flushes needs to be atomic otherwise an interrupt hander that uses + * vmalloc'ed memory might cause a TLB load in the middle of a flush. + */ + +/* Flush all TLB entries. */ +void +__flush_tlb_all(void) +{ + int i; + int mmu; + unsigned long flags; + unsigned long mmu_tlb_hi; + unsigned long mmu_tlb_sel; + + /* + * Mask with 0xf so similar TLB entries aren't written in the same 4-way + * entry group. + */ + local_save_flags(flags); + local_irq_disable(); + + for (mmu = 1; mmu <= 2; mmu++) { + SUPP_BANK_SEL(mmu); /* Select the MMU */ + for (i = 0; i < NUM_TLB_ENTRIES; i++) { + /* Store invalid entry */ + mmu_tlb_sel = REG_FIELD(mmu, rw_mm_tlb_sel, idx, i); + + mmu_tlb_hi = (REG_FIELD(mmu, rw_mm_tlb_hi, pid, INVALID_PAGEID) + | REG_FIELD(mmu, rw_mm_tlb_hi, vpn, i & 0xf)); + + SUPP_REG_WR(RW_MM_TLB_SEL, mmu_tlb_sel); + SUPP_REG_WR(RW_MM_TLB_HI, mmu_tlb_hi); + SUPP_REG_WR(RW_MM_TLB_LO, 0); + } + } + + local_irq_restore(flags); +} + +/* Flush an entire user address space. */ +void +__flush_tlb_mm(struct mm_struct *mm) +{ + int i; + int mmu; + unsigned long flags; + unsigned long page_id; + unsigned long tlb_hi; + unsigned long mmu_tlb_hi; + + page_id = mm->context.page_id; + + if (page_id == NO_CONTEXT) + return; + + /* Mark the TLB entries that match the page_id as invalid. */ + local_save_flags(flags); + local_irq_disable(); + + for (mmu = 1; mmu <= 2; mmu++) { + SUPP_BANK_SEL(mmu); + for (i = 0; i < NUM_TLB_ENTRIES; i++) { + UPDATE_TLB_SEL_IDX(i); + + /* Get the page_id */ + SUPP_REG_RD(RW_MM_TLB_HI, tlb_hi); + + /* Check if the page_id match. */ + if ((tlb_hi & 0xff) == page_id) { + mmu_tlb_hi = (REG_FIELD(mmu, rw_mm_tlb_hi, pid, + INVALID_PAGEID) + | REG_FIELD(mmu, rw_mm_tlb_hi, vpn, + i & 0xf)); + + UPDATE_TLB_HILO(mmu_tlb_hi, 0); + } + } + } + + local_irq_restore(flags); +} + +/* Invalidate a single page. */ +void +__flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) +{ + int i; + int mmu; + unsigned long page_id; + unsigned long flags; + unsigned long tlb_hi; + unsigned long mmu_tlb_hi; + + page_id = vma->vm_mm->context.page_id; + + if (page_id == NO_CONTEXT) + return; + + addr &= PAGE_MASK; + + /* + * Invalidate those TLB entries that match both the mm context and the + * requested virtual address. + */ + local_save_flags(flags); + local_irq_disable(); + + for (mmu = 1; mmu <= 2; mmu++) { + SUPP_BANK_SEL(mmu); + for (i = 0; i < NUM_TLB_ENTRIES; i++) { + UPDATE_TLB_SEL_IDX(i); + SUPP_REG_RD(RW_MM_TLB_HI, tlb_hi); + + /* Check if page_id and address matches */ + if (((tlb_hi & 0xff) == page_id) && + ((tlb_hi & PAGE_MASK) == addr)) { + mmu_tlb_hi = REG_FIELD(mmu, rw_mm_tlb_hi, pid, + INVALID_PAGEID) | addr; + + UPDATE_TLB_HILO(mmu_tlb_hi, 0); + } + } + } + + local_irq_restore(flags); +} + +/* + * Initialize the context related info for a new mm_struct + * instance. + */ + +int +init_new_context(struct task_struct *tsk, struct mm_struct *mm) +{ + mm->context.page_id = NO_CONTEXT; + return 0; +} + +/* Called in schedule() just before actually doing the switch_to. */ +void +switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk) +{ + int cpu = smp_processor_id(); + + /* Make sure there is a MMU context. */ + spin_lock(&next->page_table_lock); + get_mmu_context(next); + cpu_set(cpu, next->cpu_vm_mask); + spin_unlock(&next->page_table_lock); + + /* + * Remember the pgd for the fault handlers. Keep a seperate copy of it + * because current and active_mm might be invalid at points where + * there's still a need to derefer the pgd. + */ + per_cpu(current_pgd, cpu) = next->pgd; + + /* Switch context in the MMU. */ + if (tsk && tsk->thread_info) + { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | tsk->thread_info->tls); + } + else + { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id); + } +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/output_arch.ld 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,2 @@ +/* At the time of this writing, there's no equivalent ld option. */ +OUTPUT_ARCH (crisv32) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/arch-v32/vmlinux.lds.S 2005-07-12 00:27:00.000000000 -0700 @@ -0,0 +1,134 @@ +/* ld script to make the Linux/CRIS kernel + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * It is VERY DANGEROUS to fiddle around with the symbols in this + * script. It is for example quite vital that all generated sections + * that are used are actually named here, otherwise the linker will + * put them at the end, where the init stuff is which is FREED after + * the kernel has booted. + */ + +#include +#include + +jiffies = jiffies_64; +SECTIONS +{ + . = DRAM_VIRTUAL_BASE; + dram_start = .; + ebp_start = .; + + /* The boot section is only necessary until the VCS top level testbench */ + /* includes both flash and DRAM. */ + .boot : { *(.boot) } + + . = DRAM_VIRTUAL_BASE + 0x4000; /* See head.S and pages reserved at the start. */ + + _text = .; /* Text and read-only data. */ + text_start = .; /* Lots of aliases. */ + _stext = .; + __stext = .; + .text : { + *(.text) + SCHED_TEXT + LOCK_TEXT + *(.fixup) + *(.text.__*) + } + + _etext = . ; /* End of text section. */ + __etext = .; + + . = ALIGN(4); /* Exception table. */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + RODATA + + . = ALIGN (4); + ___data_start = . ; + __Sdata = . ; + .data : { /* Data */ + *(.data) + } + __edata = . ; /* End of data section. */ + _edata = . ; + + . = ALIGN(8192); /* init_task and stack, must be aligned. */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(8192); /* Init code and data. */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; + __start___param = .; + __param : { *(__param) } + __stop___param = .; + .initcall.init : { + __initcall_start = .; + *(.initcall1.init); + *(.initcall2.init); + *(.initcall3.init); + *(.initcall4.init); + *(.initcall5.init); + *(.initcall6.init); + *(.initcall7.init); + __initcall_end = .; + } + + .con_initcall.init : { + __con_initcall_start = .; + *(.con_initcall.init) + __con_initcall_end = .; + } + SECURITY_INIT + + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; + + .init.ramfs : { + __initramfs_start = .; + *(.init.ramfs) + __initramfs_end = .; + /* + * We fill to the next page, so we can discard all init + * pages without needing to consider what payload might be + * appended to the kernel image. + */ + FILL (0); + . = ALIGN (8192); + } + + __vmlinux_end = .; /* Last address of the physical file. */ + __init_end = .; + + __data_end = . ; /* Move to _edata? */ + __bss_start = .; /* BSS. */ + .bss : { + *(COMMON) + *(.bss) + } + + . = ALIGN (0x20); + _end = .; + __end = .; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.text.exit) + *(.data.exit) + *(.exitcall.exit) + } + + dram_end = dram_start + CONFIG_ETRAX_DRAM_SIZE*1024*1024; +} --- linux-2.6.13-rc2/arch/cris/defconfig 2005-03-01 23:37:52.000000000 -0800 +++ devel/arch/cris/defconfig 2005-07-12 00:26:58.000000000 -0700 @@ -1,22 +1,27 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.11 +# Mon Jun 20 13:42:02 2005 # CONFIG_MMU=y CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_CRIS=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y CONFIG_BROKEN_ON_SMP=y # # General setup # -CONFIG_SWAP=y +CONFIG_LOCALVERSION="" +# CONFIG_SWAP is not set # CONFIG_SYSVIPC is not set # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set @@ -24,16 +29,19 @@ CONFIG_SYSCTL=y # CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set CONFIG_EMBEDDED=y # CONFIG_KALLSYMS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -45,23 +53,28 @@ CONFIG_IOSCHED_CFQ=y # CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_SMP is not set CONFIG_ETRAX_CMDLINE="root=/dev/mtdblock3 init=/linuxrc" -CONFIG_ETRAX_WATCHDOG=y -CONFIG_ETRAX_WATCHDOG_NICE_DOGGY=y +# CONFIG_ETRAX_WATCHDOG is not set CONFIG_ETRAX_FAST_TIMER=y # CONFIG_PREEMPT is not set +# CONFIG_OOM_REBOOT is not set # # Hardware setup # -CONFIG_ETRAX100LX=y -# CONFIG_ETRAX100LX_V2 is not set +# CONFIG_ETRAX100LX is not set +CONFIG_ETRAX100LX_V2=y # CONFIG_SVINTO_SIM is not set +# CONFIG_ETRAXFS is not set +# CONFIG_ETRAXFS_SIM is not set CONFIG_ETRAX_ARCH_V10=y -CONFIG_ETRAX_DRAM_SIZE=16 +# CONFIG_ETRAX_ARCH_V32 is not set +CONFIG_ETRAX_DRAM_SIZE=32 CONFIG_ETRAX_FLASH_BUSWIDTH=2 -CONFIG_CRIS_LOW_MAP=y -CONFIG_ETRAX_DRAM_VIRTUAL_BASE=60000000 +CONFIG_ETRAX_FLASH1_SIZE=4 +CONFIG_ETRAX_DRAM_VIRTUAL_BASE=c0000000 CONFIG_ETRAX_PA_LEDS=y # CONFIG_ETRAX_PB_LEDS is not set # CONFIG_ETRAX_CSP0_LEDS is not set @@ -81,13 +94,13 @@ CONFIG_ETRAX_RESCUE_SER0=y # CONFIG_ETRAX_RESCUE_SER1 is not set # CONFIG_ETRAX_RESCUE_SER2 is not set # CONFIG_ETRAX_RESCUE_SER3 is not set -CONFIG_ETRAX_DEF_R_WAITSTATES=0x95f8 -CONFIG_ETRAX_DEF_R_BUS_CONFIG=0x104 +CONFIG_ETRAX_DEF_R_WAITSTATES=0x95a6 +CONFIG_ETRAX_DEF_R_BUS_CONFIG=0x4 CONFIG_ETRAX_SDRAM=y -CONFIG_ETRAX_DEF_R_SDRAM_CONFIG=0x00e03636 +CONFIG_ETRAX_DEF_R_SDRAM_CONFIG=0x09e05757 CONFIG_ETRAX_DEF_R_SDRAM_TIMING=0x80008002 CONFIG_ETRAX_DEF_R_PORT_PA_DIR=0x1d -CONFIG_ETRAX_DEF_R_PORT_PA_DATA=0xf0 +CONFIG_ETRAX_DEF_R_PORT_PA_DATA=0x00 CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=0x00 CONFIG_ETRAX_DEF_R_PORT_PB_DIR=0x1e CONFIG_ETRAX_DEF_R_PORT_PB_DATA=0xf3 @@ -97,16 +110,17 @@ CONFIG_ETRAX_DEF_R_PORT_PB_DATA=0xf3 # Drivers for built-in interfaces # CONFIG_ETRAX_ETHERNET=y -CONFIG_NET_ETHERNET=y # CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y CONFIG_ETRAX_SERIAL=y -CONFIG_ETRAX_SERIAL_FAST_TIMER=y +# CONFIG_ETRAX_SERIAL_FAST_TIMER is not set +# CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST is not set +CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS=5 CONFIG_ETRAX_SERIAL_PORT0=y -# CONFIG_CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT is not set -CONFIG_CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT=y -# CONFIG_CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN is not set -CONFIG_CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN=y +# CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT is not set +CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT=y +# CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN is not set +CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN=y CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE=y # CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PA is not set # CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set @@ -121,10 +135,10 @@ CONFIG_ETRAX_SER0_DSR_ON_PB_BIT=-1 CONFIG_ETRAX_SER0_CD_ON_PB_BIT=-1 # CONFIG_ETRAX_SERIAL_PORT1 is not set CONFIG_ETRAX_SERIAL_PORT2=y -# CONFIG_CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT is not set -CONFIG_CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT=y -# CONFIG_CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN is not set -CONFIG_CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN=y +# CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT is not set +CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT=y +# CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN is not set +CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN=y CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE=y # CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA is not set # CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PB is not set @@ -138,44 +152,51 @@ CONFIG_ETRAX_SER2_RI_ON_PB_BIT=-1 CONFIG_ETRAX_SER2_DSR_ON_PB_BIT=-1 CONFIG_ETRAX_SER2_CD_ON_PB_BIT=-1 # CONFIG_ETRAX_SERIAL_PORT3 is not set -# CONFIG_ETRAX_RS485 is not set -# CONFIG_ETRAX_IDE is not set -# CONFIG_IDE is not set -# CONFIG_ETRAX_USB_HOST is not set +CONFIG_ETRAX_RS485=y +# CONFIG_ETRAX_RS485_ON_PA is not set +# CONFIG_ETRAX_RS485_DISABLE_RECEIVER is not set +CONFIG_ETRAX_IDE=y +CONFIG_ETRAX_IDE_DELAY=15 +CONFIG_ETRAX_IDE_PB7_RESET=y +# CONFIG_ETRAX_IDE_G27_RESET is not set +CONFIG_ETRAX_USB_HOST=y +CONFIG_ETRAX_USB_HOST_PORT1=y +CONFIG_ETRAX_USB_HOST_PORT2=y CONFIG_ETRAX_AXISFLASHMAP=y CONFIG_ETRAX_PTABLE_SECTOR=65536 -CONFIG_MTD=y -CONFIG_MTD_CFI=y -CONFIG_MTD_CFI_AMDSTD=y -CONFIG_MTD_OBSOLETE_CHIPS=y -CONFIG_MTD_AMDSTD=y -CONFIG_MTD_CHAR=y -CONFIG_MTD_BLOCK=y -CONFIG_MTD_PARTITIONS=y -CONFIG_MTD_CONCAT=y # CONFIG_ETRAX_I2C is not set -CONFIG_ETRAX_GPIO=y -CONFIG_ETRAX_PA_BUTTON_BITMASK=0x02 -CONFIG_ETRAX_PA_CHANGEABLE_DIR=0x00 -CONFIG_ETRAX_PA_CHANGEABLE_BITS=0xFF -CONFIG_ETRAX_PB_CHANGEABLE_DIR=0x00 -CONFIG_ETRAX_PB_CHANGEABLE_BITS=0xFF -# CONFIG_ETRAX_RTC is not set +# CONFIG_ETRAX_GPIO is not set +CONFIG_ETRAX_RTC=y +CONFIG_ETRAX_DS1302=y +# CONFIG_ETRAX_PCF8563 is not set +CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT=y +CONFIG_ETRAX_DS1302_RSTBIT=0 +CONFIG_ETRAX_DS1302_SCLBIT=1 +CONFIG_ETRAX_DS1302_SDABIT=0 +CONFIG_ETRAX_DS1302_TRICKLE_CHARGE=0 # # Generic Driver Options # +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set # # Memory Technology Devices (MTD) # +CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CONCAT=y # CONFIG_MTD_REDBOOT_PARTS is not set # CONFIG_MTD_CMDLINE_PARTS is not set # # User Modules And Translation Layers # +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y # CONFIG_FTL is not set # CONFIG_NFTL is not set # CONFIG_INFTL is not set @@ -183,14 +204,30 @@ CONFIG_ETRAX_PB_CHANGEABLE_BITS=0xFF # # RAM/ROM/Flash chip drivers # +CONFIG_MTD_CFI=y # CONFIG_MTD_JEDECPROBE is not set CONFIG_MTD_GEN_PROBE=y # CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 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_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_AMDSTD_RETRY=0 # CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y CONFIG_MTD_RAM=y # CONFIG_MTD_ROM is not set # CONFIG_MTD_ABSENT is not set +CONFIG_MTD_OBSOLETE_CHIPS=y +CONFIG_MTD_AMDSTD=y # CONFIG_MTD_SHARP is not set # CONFIG_MTD_JEDEC is not set @@ -204,11 +241,13 @@ CONFIG_MTD_COMPLEX_MAPPINGS=y # Self-contained MTD device drivers # # CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set CONFIG_MTD_MTDRAM=y CONFIG_MTDRAM_TOTAL_SIZE=0 CONFIG_MTDRAM_ERASE_SIZE=64 CONFIG_MTDRAM_ABS_POS=0x0 # CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_BLOCK2MTD is not set # # Disk-On-Chip Device Drivers @@ -235,11 +274,25 @@ CONFIG_MTDRAM_ABS_POS=0x0 # Block devices # # CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_COW_COMMON is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 # CONFIG_BLK_DEV_INITRD is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_ATA_OVER_ETH is not set # # Multi-device support (RAID and LVM) @@ -249,6 +302,28 @@ CONFIG_BLK_DEV_RAM_SIZE=4096 # # ATA/ATAPI/MFM/RLL support # +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_IDE_TASK_IOCTL is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_IDE_GENERIC is not set +# CONFIG_IDE_ARM is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set # # SCSI device support @@ -258,7 +333,6 @@ CONFIG_BLK_DEV_RAM_SIZE=4096 # # IEEE 1394 (FireWire) support # -# CONFIG_IEEE1394 is not set # # I2O device support @@ -288,6 +362,9 @@ CONFIG_INET=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_IP_TCPDIAG=y +# CONFIG_IP_TCPDIAG_IPV6 is not set # # IP: Virtual Server Configuration @@ -301,11 +378,10 @@ CONFIG_NETFILTER=y # IP: Netfilter Configuration # # CONFIG_IP_NF_CONNTRACK is not set +# CONFIG_IP_NF_CONNTRACK_MARK is not set # CONFIG_IP_NF_QUEUE is not set # CONFIG_IP_NF_IPTABLES is not set # CONFIG_IP_NF_ARPTABLES is not set -# CONFIG_IP_NF_COMPAT_IPCHAINS is not set -# CONFIG_IP_NF_COMPAT_IPFWADM is not set # # SCTP Configuration (EXPERIMENTAL) @@ -323,12 +399,12 @@ CONFIG_NETFILTER=y # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set -# CONFIG_NET_HW_FLOWCONTROL is not set # # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set # # Network testing @@ -338,7 +414,26 @@ CONFIG_NETFILTER=y # CONFIG_NET_POLL_CONTROLLER is not set # CONFIG_HAMRADIO is not set # CONFIG_IRDA is not set -# CONFIG_BT is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +# CONFIG_BT_SCO is not set +CONFIG_BT_RFCOMM=y +# CONFIG_BT_RFCOMM_TTY is not set +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +# CONFIG_BT_HIDP is not set + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIUSB=y +# CONFIG_BT_HCIUSB_SCO is not set +# CONFIG_BT_HCIUART is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIVHCI is not set CONFIG_NETDEVICES=y # CONFIG_DUMMY is not set # CONFIG_BONDING is not set @@ -348,6 +443,7 @@ CONFIG_NETDEVICES=y # # Ethernet (10 or 100Mbit) # +CONFIG_NET_ETHERNET=y # CONFIG_MII is not set # @@ -389,11 +485,19 @@ CONFIG_NETDEVICES=y # # Input device support # -# CONFIG_INPUT is not set +CONFIG_INPUT=y # # Userland interfaces # +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set # # Input I/O drivers @@ -404,10 +508,25 @@ CONFIG_SERIO=y # CONFIG_SERIO_I8042 is not set # CONFIG_SERIO_SERPORT is not set # CONFIG_SERIO_CT82C710 is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set # # Input Device Drivers # +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set # # Character devices @@ -426,7 +545,6 @@ CONFIG_SERIO=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 -# CONFIG_QIC02_TAPE is not set # # IPMI @@ -441,13 +559,10 @@ CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_GEN_RTC is not set # CONFIG_DTLK is not set # CONFIG_R3964 is not set -# CONFIG_APPLICOM is not set # # Ftape, the floppy tape device driver # -# CONFIG_FTAPE is not set -# CONFIG_AGP is not set # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set @@ -469,10 +584,15 @@ CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_JBD is not set # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set + +# +# XFS support +# # CONFIG_XFS_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set @@ -485,7 +605,8 @@ CONFIG_LEGACY_PTY_COUNT=256 # # DOS/FAT/NT Filesystems # -# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set # CONFIG_NTFS_FS is not set # @@ -497,6 +618,7 @@ CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set # CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y @@ -512,7 +634,15 @@ CONFIG_RAMFS=y # CONFIG_EFS_FS is not set CONFIG_JFFS_FS=y CONFIG_JFFS_FS_VERBOSE=0 -# CONFIG_JFFS2_FS is not set +# CONFIG_JFFS_PROC_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_NAND is not set +# CONFIG_JFFS2_FS_NOR_ECC is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set CONFIG_CRAMFS=y # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set @@ -530,14 +660,13 @@ CONFIG_NFS_V3=y # CONFIG_NFSD is not set CONFIG_LOCKD=y CONFIG_LOCKD_V4=y -# CONFIG_EXPORTFS is not set CONFIG_SUNRPC=y # CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set -# CONFIG_INTERMEZZO_FS is not set # CONFIG_AFS_FS is not set # @@ -557,8 +686,120 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_SOUND is not set # +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# PC-card bridges +# + +# # USB support # +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_ARCH_HAS_HCD is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_SL811_HCD is not set + +# +# USB Device Class drivers +# + +# +# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# +# CONFIG_USB_STORAGE is not set + +# +# USB Input Devices +# +# CONFIG_USB_HID is not set + +# +# USB HID Boot Protocol drivers +# +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +CONFIG_USB_RTL8150=y +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETKIT is not set +# CONFIG_USB_PHIDGETSERVO is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_TEST is not set + +# +# USB ATM/DSL drivers +# # # USB Gadget Support @@ -568,14 +809,17 @@ CONFIG_MSDOS_PARTITION=y # # Kernel hacking # -# CONFIG_PROFILE is not set +# CONFIG_PROFILING is not set +# CONFIG_SYSTEM_PROFILER is not set # CONFIG_ETRAX_KGDB is not set # CONFIG_DEBUG_INFO is not set # CONFIG_FRAME_POINTER is not set +# CONFIG_DEBUG_NMI_OOPS is not set # # Security options # +# CONFIG_KEYS is not set # CONFIG_SECURITY is not set # @@ -584,8 +828,14 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_CRYPTO is not set # +# Hardware crypto devices +# + +# # Library routines # -# CONFIG_CRC32 is not set +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y --- linux-2.6.13-rc2/arch/cris/Kconfig.debug 2005-03-01 23:38:38.000000000 -0800 +++ devel/arch/cris/Kconfig.debug 2005-07-12 00:26:58.000000000 -0700 @@ -38,4 +38,9 @@ config FRAME_POINTER If you don't debug the kernel, you can say N, but we may not be able to solve problems without frame pointers. +config DEBUG_NMI_OOPS + bool "NMI causes oops printout" + help + If the system locks up without any debug information you can say Y + here to make it possible to dump an OOPS with an external NMI. endmenu --- linux-2.6.13-rc2/arch/cris/kernel/crisksyms.c 2005-03-01 23:38:38.000000000 -0800 +++ devel/arch/cris/kernel/crisksyms.c 2005-07-12 00:26:59.000000000 -0700 @@ -27,13 +27,13 @@ extern void __Udiv(void); extern void __Umod(void); extern void __Div(void); extern void __Mod(void); +extern void __ashldi3(void); extern void __ashrdi3(void); -extern void iounmap(void *addr); +extern void __lshrdi3(void); +extern void iounmap(volatile void * __iomem); /* Platform dependent support */ EXPORT_SYMBOL(dump_thread); -EXPORT_SYMBOL(enable_irq); -EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(get_cmos_time); EXPORT_SYMBOL(loops_per_usec); @@ -57,7 +57,9 @@ EXPORT_SYMBOL(__Udiv); EXPORT_SYMBOL(__Umod); EXPORT_SYMBOL(__Div); EXPORT_SYMBOL(__Mod); +EXPORT_SYMBOL(__ashldi3); EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__lshrdi3); /* Memory functions */ EXPORT_SYMBOL(__ioremap); @@ -69,23 +71,10 @@ EXPORT_SYMBOL(__down); EXPORT_SYMBOL(__down_interruptible); EXPORT_SYMBOL(__down_trylock); -/* Export shadow registers for the CPU I/O pins */ -EXPORT_SYMBOL(genconfig_shadow); -EXPORT_SYMBOL(port_pa_data_shadow); -EXPORT_SYMBOL(port_pa_dir_shadow); -EXPORT_SYMBOL(port_pb_data_shadow); -EXPORT_SYMBOL(port_pb_dir_shadow); -EXPORT_SYMBOL(port_pb_config_shadow); -EXPORT_SYMBOL(port_g_data_shadow); - /* Userspace access functions */ EXPORT_SYMBOL(__copy_user_zeroing); EXPORT_SYMBOL(__copy_user); -/* Cache flush functions */ -EXPORT_SYMBOL(flush_etrax_cache); -EXPORT_SYMBOL(prepare_rx_descriptor); - #undef memcpy #undef memset extern void * memset(void *, int, __kernel_size_t); --- linux-2.6.13-rc2/arch/cris/kernel/irq.c 2005-03-01 23:38:33.000000000 -0800 +++ devel/arch/cris/kernel/irq.c 2005-07-12 00:26:59.000000000 -0700 @@ -12,8 +12,6 @@ * shouldn't result in any weird surprises, and installing new handlers * should be easier. * - * Notice Linux/CRIS: these routines do not care about SMP - * */ /* @@ -24,6 +22,7 @@ #include #include #include +#include #include #include @@ -36,84 +35,56 @@ #include #include #include -#include +#include #include -/* Defined in arch specific irq.c */ -extern void arch_setup_irq(int irq); -extern void arch_free_irq(int irq); - -void -disable_irq(unsigned int irq_nr) -{ - unsigned long flags; - - local_save_flags(flags); - local_irq_disable(); - mask_irq(irq_nr); - local_irq_restore(flags); -} - -void -enable_irq(unsigned int irq_nr) -{ - unsigned long flags; - local_save_flags(flags); - local_irq_disable(); - unmask_irq(irq_nr); - local_irq_restore(flags); -} - -unsigned long -probe_irq_on() +void ack_bad_irq(unsigned int irq) { - return 0; + printk("unexpected IRQ trap at vector %02x\n", irq); } -EXPORT_SYMBOL(probe_irq_on); - -int -probe_irq_off(unsigned long x) -{ - return 0; -} - -EXPORT_SYMBOL(probe_irq_off); - -/* - * Initial irq handlers. - */ - -static struct irqaction *irq_action[NR_IRQS]; - int show_interrupts(struct seq_file *p, void *v) { - int i = *(loff_t *) v; + int i = *(loff_t *) v, j; struct irqaction * action; unsigned long flags; + if (i == 0) { + seq_printf(p, " "); + for (j=0; jflags & SA_INTERRUPT) ? '+' : ' ', - action->name); - for (action = action->next; action; action = action->next) { - seq_printf(p, ",%s %s", - (action->flags & SA_INTERRUPT) ? " +" : "", - action->name); - } + seq_printf(p, "%3d: ",i); +#ifndef CONFIG_SMP + seq_printf(p, "%10u ", kstat_irqs(i)); +#else + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); +#endif + seq_printf(p, " %14s", irq_desc[i].handler->typename); + seq_printf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + seq_putc(p, '\n'); skip: - local_irq_restore(flags); + spin_unlock_irqrestore(&irq_desc[i].lock, flags); } return 0; } + /* called by the assembler IRQ entry functions defined in irq.h * to dispatch the interrupts to registred handlers * interrupts are disabled upon entry - depending on if the @@ -123,164 +94,17 @@ skip: asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { - struct irqaction *action; - int do_random, cpu; - int ret, retval = 0; - - cpu = smp_processor_id(); - irq_enter(); - kstat_cpu(cpu).irqs[irq - FIRST_IRQ]++; - action = irq_action[irq - FIRST_IRQ]; - - if (action) { - if (!(action->flags & SA_INTERRUPT)) - local_irq_enable(); - do_random = 0; - do { - ret = action->handler(irq, action->dev_id, regs); - if (ret == IRQ_HANDLED) - do_random |= action->flags; - retval |= ret; - action = action->next; - } while (action); - - if (retval != 1) { - if (retval) { - printk("irq event %d: bogus retval mask %x\n", - irq, retval); - } else { - printk("irq %d: nobody cared\n", irq); - } - } - - if (do_random & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq); - local_irq_disable(); - } - irq_exit(); -} - -/* this function links in a handler into the chain of handlers for the - given irq, and if the irq has never been registred, the appropriate - handler is entered into the interrupt vector -*/ - -int setup_irq(int irq, struct irqaction * new) -{ - int shared = 0; - struct irqaction *old, **p; - unsigned long flags; - - p = irq_action + irq - FIRST_IRQ; - if ((old = *p) != NULL) { - /* Can't share interrupts unless both agree to */ - if (!(old->flags & new->flags & SA_SHIRQ)) - return -EBUSY; - - /* Can't share interrupts unless both are same type */ - if ((old->flags ^ new->flags) & SA_INTERRUPT) - return -EBUSY; - - /* add new interrupt at end of irq queue */ - do { - p = &old->next; - old = *p; - } while (old); - shared = 1; - } - - if (new->flags & SA_SAMPLE_RANDOM) - rand_initialize_irq(irq); - - local_save_flags(flags); - local_irq_disable(); - *p = new; - - if (!shared) { - /* if the irq wasn't registred before, enter it into the vector table - and unmask it physically - */ - arch_setup_irq(irq); - unmask_irq(irq); + unsigned long sp; + irq_enter(); + sp = rdsp(); + if (unlikely((sp & (PAGE_SIZE - 1)) < (PAGE_SIZE/8))) { + printk("do_IRQ: stack overflow: %lX\n", sp); + show_stack(NULL, (unsigned long *)sp); } - - local_irq_restore(flags); - return 0; -} - -/* this function is called by a driver to register an irq handler - Valid flags: - SA_INTERRUPT -> it's a fast interrupt, handler called with irq disabled and - no signal checking etc is performed upon exit - SA_SHIRQ -> the interrupt can be shared between different handlers, the handler - is required to check if the irq was "aimed" at it explicitely - SA_RANDOM -> the interrupt will add to the random generators entropy -*/ - -int request_irq(unsigned int irq, - irqreturn_t (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, - const char * devname, - void *dev_id) -{ - int retval; - struct irqaction * action; - - if(!handler) - return -EINVAL; - - /* allocate and fill in a handler structure and setup the irq */ - - action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); - if (!action) - return -ENOMEM; - - action->handler = handler; - action->flags = irqflags; - cpus_clear(action->mask); - action->name = devname; - action->next = NULL; - action->dev_id = dev_id; - - retval = setup_irq(irq, action); - - if (retval) - kfree(action); - return retval; -} - -EXPORT_SYMBOL(request_irq); - -void free_irq(unsigned int irq, void *dev_id) -{ - struct irqaction * action, **p; - unsigned long flags; - - if (irq >= NR_IRQS) { - printk("Trying to free IRQ%d\n",irq); - return; - } - for (p = irq - FIRST_IRQ + irq_action; (action = *p) != NULL; p = &action->next) { - if (action->dev_id != dev_id) - continue; - - /* Found it - now free it */ - local_save_flags(flags); - local_irq_disable(); - *p = action->next; - if (!irq_action[irq - FIRST_IRQ]) { - mask_irq(irq); - arch_free_irq(irq); - } - local_irq_restore(flags); - kfree(action); - return; - } - printk("Trying to free free IRQ%d\n",irq); + __do_IRQ(irq, regs); + irq_exit(); } -EXPORT_SYMBOL(free_irq); - void weird_irq(void) { local_irq_disable(); @@ -288,10 +112,3 @@ void weird_irq(void) while(1); } -#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL) -/* Used by other archs to show/control IRQ steering during SMP */ -void __init -init_irq_proc(void) -{ -} -#endif --- linux-2.6.13-rc2/arch/cris/kernel/Makefile 2005-03-01 23:38:38.000000000 -0800 +++ devel/arch/cris/kernel/Makefile 2005-07-12 00:26:58.000000000 -0700 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.10 2004/05/14 10:18:12 starvik Exp $ +# $Id: Makefile,v 1.12 2004/10/19 13:07:43 starvik Exp $ # # Makefile for the linux kernel. # @@ -10,6 +10,7 @@ obj-y := process.o traps.o irq.o ptrac obj-$(CONFIG_MODULES) += crisksyms.o obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_SYSTEM_PROFILER) += profile.o clean: --- linux-2.6.13-rc2/arch/cris/kernel/module.c 2005-03-01 23:37:55.000000000 -0800 +++ devel/arch/cris/kernel/module.c 2005-07-12 00:27:00.000000000 -0700 @@ -32,7 +32,7 @@ void *module_alloc(unsigned long size) { if (size == 0) return NULL; - return vmalloc(size); + return vmalloc_exec(size); } @@ -59,26 +59,8 @@ int apply_relocate(Elf32_Shdr *sechdrs, unsigned int relsec, struct module *me) { - unsigned int i; - Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; - Elf32_Sym *sym; - uint32_t *location; - - DEBUGP("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); - for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { - /* This is where to make the change */ - location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset - + rel[i].r_offset; - /* This is the symbol it is referring to. Note that all - undefined symbols have been resolved. */ - sym = (Elf32_Sym *)sechdrs[symindex].sh_addr - + ELF32_R_SYM(rel[i].r_info); - - /* We add the value into the location given */ - *location += sym->st_value; - } - return 0; + printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); + return -ENOEXEC; } int apply_relocate_add(Elf32_Shdr *sechdrs, @@ -90,7 +72,7 @@ int apply_relocate_add(Elf32_Shdr *sechd unsigned int i; Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; - DEBUGP ("Applying relocate section %u to %u\n", relsec, + DEBUGP ("Applying add relocate section %u to %u\n", relsec, sechdrs[relsec].sh_info); for (i = 0; i < sechdrs[relsec].sh_size / sizeof (*rela); i++) { @@ -103,7 +85,18 @@ int apply_relocate_add(Elf32_Shdr *sechd Elf32_Sym *sym = ((Elf32_Sym *)sechdrs[symindex].sh_addr + ELF32_R_SYM (rela[i].r_info)); - *loc = sym->st_value + rela[i].r_addend; + switch (ELF32_R_TYPE(rela[i].r_info)) { + case R_CRIS_32: + *loc = sym->st_value + rela[i].r_addend; + break; + case R_CRIS_32_PCREL: + *loc = sym->st_value - (unsigned)loc + rela[i].r_addend - 4; + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rela[i].r_info)); + return -ENOEXEC; + } } return 0; --- linux-2.6.13-rc2/arch/cris/kernel/process.c 2005-03-01 23:38:12.000000000 -0800 +++ devel/arch/cris/kernel/process.c 2005-07-12 00:27:00.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.17 2004/04/05 13:53:48 starvik Exp $ +/* $Id: process.c,v 1.21 2005/03/04 08:16:17 starvik Exp $ * * linux/arch/cris/kernel/process.c * @@ -8,6 +8,18 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: process.c,v $ + * Revision 1.21 2005/03/04 08:16:17 starvik + * Merge of Linux 2.6.11. + * + * Revision 1.20 2005/01/18 05:57:22 starvik + * Renamed hlt_counter to cris_hlt_counter and made it global. + * + * Revision 1.19 2004/10/19 13:07:43 starvik + * Merge of Linux 2.6.9 + * + * Revision 1.18 2004/08/16 12:37:23 starvik + * Merge of Linux 2.6.8 + * * Revision 1.17 2004/04/05 13:53:48 starvik * Merge of Linux 2.6.5 * @@ -160,18 +172,18 @@ EXPORT_SYMBOL(init_task); * region by enable_hlt/disable_hlt. */ -static int hlt_counter=0; +int cris_hlt_counter=0; void disable_hlt(void) { - hlt_counter++; + cris_hlt_counter++; } EXPORT_SYMBOL(disable_hlt); void enable_hlt(void) { - hlt_counter--; + cris_hlt_counter--; } EXPORT_SYMBOL(enable_hlt); @@ -194,16 +206,19 @@ void cpu_idle (void) /* endless idle loop with no priority at all */ while (1) { while (!need_resched()) { - void (*idle)(void) = pm_idle; - + void (*idle)(void); + /* + * Mark this as an RCU critical section so that + * synchronize_kernel() in the unload path waits + * for our completion. + */ + idle = pm_idle; if (!idle) idle = default_idle; - idle(); } schedule(); } - } void hard_reset_now (void); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/cris/kernel/profile.c 2005-07-12 00:26:59.000000000 -0700 @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include + +#define SAMPLE_BUFFER_SIZE 8192 + +static char* sample_buffer; +static char* sample_buffer_pos; +static int prof_running = 0; + +void +cris_profile_sample(struct pt_regs* regs) +{ + if (!prof_running) + return; + if (user_mode(regs)) + *(unsigned int*)sample_buffer_pos = current->pid; + else + *(unsigned int*)sample_buffer_pos = 0; + *(unsigned int*)(sample_buffer_pos + 4) = instruction_pointer(regs); + sample_buffer_pos += 8; + if (sample_buffer_pos == sample_buffer + SAMPLE_BUFFER_SIZE) + sample_buffer_pos = sample_buffer; +} + +static ssize_t +read_cris_profile(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + unsigned long p = *ppos; + if (p > SAMPLE_BUFFER_SIZE) + return 0; + if (p + count > SAMPLE_BUFFER_SIZE) + count = SAMPLE_BUFFER_SIZE - p; + if (copy_to_user(buf, sample_buffer + p,count)) + return -EFAULT; + memset(sample_buffer + p, 0, count); + *ppos += count; + return count; +} + +static ssize_t +write_cris_profile(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + sample_buffer_pos = sample_buffer; + memset(sample_buffer, 0, SAMPLE_BUFFER_SIZE); +} + +static struct file_operations cris_proc_profile_operations = { + .read = read_cris_profile, + .write = write_cris_profile, +}; + +static int +__init init_cris_profile(void) +{ + struct proc_dir_entry *entry; + sample_buffer = (char*)kmalloc(SAMPLE_BUFFER_SIZE, GFP_KERNEL); + sample_buffer_pos = sample_buffer; + entry = create_proc_entry("system_profile", S_IWUSR | S_IRUGO, NULL); + if (entry) { + entry->proc_fops = &cris_proc_profile_operations; + entry->size = SAMPLE_BUFFER_SIZE; + } + prof_running = 1; + return 0; +} + +__initcall(init_cris_profile); --- linux-2.6.13-rc2/arch/cris/kernel/ptrace.c 2005-03-01 23:37:53.000000000 -0800 +++ devel/arch/cris/kernel/ptrace.c 2005-07-12 00:26:58.000000000 -0700 @@ -8,6 +8,12 @@ * Authors: Bjorn Wesen * * $Log: ptrace.c,v $ + * Revision 1.10 2004/09/22 11:50:01 orjanf + * * Moved get_reg/put_reg to arch-specific files. + * * Added functions to access debug registers (CRISv32). + * * Added support for PTRACE_SINGLESTEP (CRISv32). + * * Added S flag to CCS_MASK (CRISv32). + * * Revision 1.9 2003/07/04 12:56:11 tobiasa * Moved arch-specific code to arch-specific files. * @@ -72,37 +78,6 @@ #include #include -/* - * Get contents of register REGNO in task TASK. - */ -inline long get_reg(struct task_struct *task, unsigned int regno) -{ - /* USP is a special case, it's not in the pt_regs struct but - * in the tasks thread struct - */ - - if (regno == PT_USP) - return task->thread.usp; - else if (regno < PT_MAX) - return ((unsigned long *)user_regs(task->thread_info))[regno]; - else - return 0; -} - -/* - * Write contents of register REGNO in task TASK. - */ -inline int put_reg(struct task_struct *task, unsigned int regno, - unsigned long data) -{ - if (regno == PT_USP) - task->thread.usp = data; - else if (regno < PT_MAX) - ((unsigned long *)user_regs(task->thread_info))[regno] = data; - else - return -1; - return 0; -} /* notification of userspace execution resumption * - triggered by current->work.notify_resume --- linux-2.6.13-rc2/arch/cris/kernel/setup.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/cris/kernel/setup.c 2005-07-12 00:26:59.000000000 -0700 @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -29,7 +30,7 @@ struct screen_info screen_info; extern int root_mountflags; extern char _etext, _edata, _end; -static char command_line[COMMAND_LINE_SIZE] = { 0, }; +char cris_command_line[COMMAND_LINE_SIZE] = { 0, }; extern const unsigned long text_start, edata; /* set by the linker script */ extern unsigned long dram_start, dram_end; @@ -147,34 +148,35 @@ setup_arch(char **cmdline_p) paging_init(); - /* We don't use a command line yet, so just re-initialize it without - saving anything that might be there. */ - - *cmdline_p = command_line; + *cmdline_p = cris_command_line; #ifdef CONFIG_ETRAX_CMDLINE - strlcpy(command_line, CONFIG_ETRAX_CMDLINE, COMMAND_LINE_SIZE); - command_line[COMMAND_LINE_SIZE - 1] = '\0'; + if (!strcmp(cris_command_line, "")) { + strlcpy(cris_command_line, CONFIG_ETRAX_CMDLINE, COMMAND_LINE_SIZE); + cris_command_line[COMMAND_LINE_SIZE - 1] = '\0'; + } +#endif /* Save command line for future references. */ - memcpy(saved_command_line, command_line, COMMAND_LINE_SIZE); + memcpy(saved_command_line, cris_command_line, COMMAND_LINE_SIZE); saved_command_line[COMMAND_LINE_SIZE - 1] = '\0'; -#endif /* give credit for the CRIS port */ show_etrax_copyright(); + + /* Setup utsname */ + strcpy(system_utsname.machine, cris_machine_name); } static void *c_start(struct seq_file *m, loff_t *pos) { - /* We only got one CPU... */ - return *pos < 1 ? (void *)1 : NULL; + return *pos < NR_CPUS ? (void *)(int)(*pos + 1): NULL; } static void *c_next(struct seq_file *m, void *v, loff_t *pos) { ++*pos; - return NULL; + return c_start(m, pos); } static void c_stop(struct seq_file *m, void *v) --- linux-2.6.13-rc2/arch/cris/kernel/time.c 2005-03-01 23:38:10.000000000 -0800 +++ devel/arch/cris/kernel/time.c 2005-07-12 00:26:59.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.14 2004/06/01 05:38:11 starvik Exp $ +/* $Id: time.c,v 1.18 2005/03/04 08:16:17 starvik Exp $ * * linux/arch/cris/kernel/time.c * @@ -30,6 +30,7 @@ #include #include #include +#include u64 jiffies_64 = INITIAL_JIFFIES; @@ -214,6 +215,21 @@ update_xtime_from_cmos(void) } } +extern void cris_profile_sample(struct pt_regs* regs); + +void +cris_do_profile(struct pt_regs* regs) +{ + +#if CONFIG_SYSTEM_PROFILER + cris_profile_sample(regs); +#endif + +#if CONFIG_PROFILING + profile_tick(CPU_PROFILING, regs); +#endif +} + /* * Scheduler clock - returns current time in nanosec units. */ --- linux-2.6.13-rc2/arch/cris/kernel/traps.c 2005-03-01 23:38:25.000000000 -0800 +++ devel/arch/cris/kernel/traps.c 2005-07-12 00:26:58.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.9 2004/05/11 12:28:26 starvik Exp $ +/* $Id: traps.c,v 1.11 2005/01/24 16:03:19 orjanf Exp $ * * linux/arch/cris/traps.c * @@ -20,13 +20,15 @@ static int kstack_depth_to_print = 24; +extern int raw_printk(const char *fmt, ...); + void show_trace(unsigned long * stack) { unsigned long addr, module_start, module_end; extern char _stext, _etext; int i; - printk("\nCall Trace: "); + raw_printk("\nCall Trace: "); i = 1; module_start = VMALLOC_START; @@ -37,7 +39,7 @@ void show_trace(unsigned long * stack) /* This message matches "failing address" marked s390 in ksymoops, so lines containing it will not be filtered out by ksymoops. */ - printk ("Failing address 0x%lx\n", (unsigned long)stack); + raw_printk ("Failing address 0x%lx\n", (unsigned long)stack); break; } stack++; @@ -54,8 +56,8 @@ void show_trace(unsigned long * stack) (addr <= (unsigned long) &_etext)) || ((addr >= module_start) && (addr <= module_end))) { if (i && ((i % 8) == 0)) - printk("\n "); - printk("[<%08lx>] ", addr); + raw_printk("\n "); + raw_printk("[<%08lx>] ", addr); i++; } } @@ -96,25 +98,59 @@ show_stack(struct task_struct *task, uns stack = sp; - printk("\nStack from %08lx:\n ", (unsigned long)stack); + raw_printk("\nStack from %08lx:\n ", (unsigned long)stack); for(i = 0; i < kstack_depth_to_print; i++) { if (((long) stack & (THREAD_SIZE-1)) == 0) break; if (i && ((i % 8) == 0)) - printk("\n "); + raw_printk("\n "); if (__get_user (addr, stack)) { /* This message matches "failing address" marked s390 in ksymoops, so lines containing it will not be filtered out by ksymoops. */ - printk ("Failing address 0x%lx\n", (unsigned long)stack); + raw_printk ("Failing address 0x%lx\n", (unsigned long)stack); break; } stack++; - printk("%08lx ", addr); + raw_printk("%08lx ", addr); } show_trace(sp); } +static void (*nmi_handler)(struct pt_regs*); +extern void arch_enable_nmi(void); + +void set_nmi_handler(void (*handler)(struct pt_regs*)) +{ + nmi_handler = handler; + arch_enable_nmi(); +} + +void handle_nmi(struct pt_regs* regs) +{ + if (nmi_handler) + nmi_handler(regs); +} + +#ifdef CONFIG_DEBUG_NMI_OOPS +void oops_nmi_handler(struct pt_regs* regs) +{ + stop_watchdog(); + raw_printk("NMI!\n"); + show_registers(regs); +} + +static int +__init oops_nmi_register(void) +{ + set_nmi_handler(oops_nmi_handler); + return 0; +} + +__initcall(oops_nmi_register); + +#endif + #if 0 /* displays a short stack trace */ @@ -123,9 +159,9 @@ show_stack() { unsigned long *sp = (unsigned long *)rdusp(); int i; - printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); + raw_printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); for(i = 0; i < 16; i++) - printk("sp + %d: 0x%08lx\n", i*4, sp[i]); + raw_printk("sp + %d: 0x%08lx\n", i*4, sp[i]); return 0; } #endif @@ -142,3 +178,9 @@ trap_init(void) { /* Nothing needs to be done */ } + +void spinning_cpu(void* addr) +{ + raw_printk("CPU %d spinning on %X\n", smp_processor_id(), addr); + dump_stack(); +} --- linux-2.6.13-rc2/arch/cris/Makefile 2005-03-01 23:38:08.000000000 -0800 +++ devel/arch/cris/Makefile 2005-07-12 00:26:58.000000000 -0700 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.23 2004/10/19 13:07:34 starvik Exp $ +# $Id: Makefile,v 1.28 2005/03/17 10:44:37 larsv Exp $ # cris/Makefile # # This file is included by the global makefile so that you can add your own @@ -15,6 +15,7 @@ arch-y := v10 arch-$(CONFIG_ETRAX_ARCH_V10) := v10 +arch-$(CONFIG_ETRAX_ARCH_V32) := v32 # No config avaiable for make clean etc ifneq ($(arch-y),) @@ -46,6 +47,21 @@ core-y += arch/$(ARCH)/$(SARCH)/kernel/ drivers-y += arch/$(ARCH)/$(SARCH)/drivers/ libs-y += arch/$(ARCH)/$(SARCH)/lib/ $(LIBGCC) +# cris source path +SRC_ARCH = $(srctree)/arch/$(ARCH) +# cris object files path +OBJ_ARCH = $(objtree)/arch/$(ARCH) + +target_boot_arch_dir = $(OBJ_ARCH)/$(SARCH)/boot +target_boot_dir = $(OBJ_ARCH)/boot +src_boot_dir = $(SRC_ARCH)/boot +target_compressed_dir = $(OBJ_ARCH)/boot/compressed +src_compressed_dir = $(SRC_ARCH)/boot/compressed +target_rescue_dir = $(OBJ_ARCH)/boot/rescue +src_rescue_dir = $(SRC_ARCH)/boot/rescue + +export target_boot_arch_dir target_boot_dir src_boot_dir target_compressed_dir src_compressed_dir target_rescue_dir src_rescue_dir + vmlinux.bin: vmlinux $(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux.bin @@ -65,44 +81,52 @@ cramfs: clinux: vmlinux.bin decompress.bin rescue.bin -decompress.bin: FORCE - @make -C arch/$(ARCH)/boot/compressed decompress.bin +decompress.bin: $(target_boot_dir) + @$(MAKE) -f $(src_compressed_dir)/Makefile $(target_compressed_dir)/decompress.bin -rescue.bin: FORCE - @make -C arch/$(ARCH)/boot/rescue rescue.bin +$(target_rescue_dir)/rescue.bin: $(target_boot_dir) + @$(MAKE) -f $(src_rescue_dir)/Makefile $(target_rescue_dir)/rescue.bin -zImage: vmlinux.bin rescue.bin +zImage: $(target_boot_dir) vmlinux.bin $(target_rescue_dir)/rescue.bin ## zImage - Compressed kernel (gzip) - @make -C arch/$(ARCH)/boot/ zImage + @$(MAKE) -f $(src_boot_dir)/Makefile zImage + +$(target_boot_dir): $(target_boot_arch_dir) + ln -sfn $< $@ + +$(target_boot_arch_dir): + mkdir -p $@ compressed: zImage archmrproper: archclean: - $(Q)$(MAKE) $(clean)=arch/$(ARCH)/boot + @if [ -d arch/$(ARCH)/boot ]; then \ + $(MAKE) $(clean)=arch/$(ARCH)/boot ; \ + fi rm -f timage vmlinux.bin decompress.bin rescue.bin cramfs.img rm -rf $(LD_SCRIPT).tmp -prepare: arch/$(ARCH)/.links include/asm-$(ARCH)/.arch \ +prepare: $(SRC_ARCH)/.links $(srctree)/include/asm-$(ARCH)/.arch \ include/asm-$(ARCH)/$(SARCH)/offset.h # Create some links to make all tools happy -arch/$(ARCH)/.links: - @rm -rf arch/$(ARCH)/drivers - @ln -sfn $(SARCH)/drivers arch/$(ARCH)/drivers - @rm -rf arch/$(ARCH)/boot - @ln -sfn $(SARCH)/boot arch/$(ARCH)/boot - @rm -rf arch/$(ARCH)/lib - @ln -sfn $(SARCH)/lib arch/$(ARCH)/lib - @ln -sfn $(SARCH) arch/$(ARCH)/arch - @ln -sfn ../$(SARCH)/vmlinux.lds.S arch/$(ARCH)/kernel/vmlinux.lds.S +$(SRC_ARCH)/.links: + @rm -rf $(SRC_ARCH)/drivers + @ln -sfn $(SRC_ARCH)/$(SARCH)/drivers $(SRC_ARCH)/drivers + @rm -rf $(SRC_ARCH)/boot + @ln -sfn $(SRC_ARCH)/$(SARCH)/boot $(SRC_ARCH)/boot + @rm -rf $(SRC_ARCH)/lib + @ln -sfn $(SRC_ARCH)/$(SARCH)/lib $(SRC_ARCH)/lib + @ln -sfn $(SRC_ARCH)/$(SARCH) $(SRC_ARCH)/arch + @ln -sfn $(SRC_ARCH)/$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S @touch $@ # Create link to sub arch includes -include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h) - @echo ' Making asm-$(ARCH)/arch -> asm-$(ARCH)/$(SARCH) symlink' +$(srctree)/include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h) + @echo ' Making $(srctree)/include/asm-$(ARCH)/arch -> $(srctree)/include/asm-$(ARCH)/$(SARCH) symlink' @rm -f include/asm-$(ARCH)/arch - @ln -sf $(SARCH) include/asm-$(ARCH)/arch + @ln -sf $(srctree)/include/asm-$(ARCH)/$(SARCH) $(srctree)/include/asm-$(ARCH)/arch @touch $@ arch/$(ARCH)/$(SARCH)/kernel/asm-offsets.s: include/asm include/linux/version.h \ --- linux-2.6.13-rc2/arch/cris/mm/fault.c 2005-03-01 23:38:25.000000000 -0800 +++ devel/arch/cris/mm/fault.c 2005-07-12 00:26:59.000000000 -0700 @@ -6,6 +6,38 @@ * Authors: Bjorn Wesen * * $Log: fault.c,v $ + * Revision 1.20 2005/03/04 08:16:18 starvik + * Merge of Linux 2.6.11. + * + * Revision 1.19 2005/01/14 10:07:59 starvik + * Fixed warning. + * + * Revision 1.18 2005/01/12 08:10:14 starvik + * Readded the change of frametype when handling kernel page fault fixup + * for v10. This is necessary to avoid that the CPU remakes the faulting + * access. + * + * Revision 1.17 2005/01/11 13:53:05 starvik + * Use raw_printk. + * + * Revision 1.16 2004/12/17 11:39:41 starvik + * SMP support. + * + * Revision 1.15 2004/11/23 18:36:18 starvik + * Stack is now non-executable. + * Signal handler trampolines are placed in a reserved page mapped into all + * processes. + * + * Revision 1.14 2004/11/23 07:10:21 starvik + * Moved find_fixup_code to generic code. + * + * Revision 1.13 2004/11/23 07:00:54 starvik + * Actually use the execute permission bit in the MMU. This makes it possible + * to prevent e.g. attacks where executable code is put on the stack. + * + * Revision 1.12 2004/09/29 06:16:04 starvik + * Use instruction_pointer + * * Revision 1.11 2004/05/14 07:58:05 starvik * Merge of changes from 2.4 * @@ -103,6 +135,7 @@ extern int find_fixup_code(struct pt_regs *); extern void die_if_kernel(const char *, struct pt_regs *, long); +extern int raw_printk(const char *fmt, ...); /* debug of low-level TLB reload */ #undef DEBUG @@ -118,7 +151,8 @@ extern void die_if_kernel(const char *, /* current active page directory */ -volatile pgd_t *current_pgd; +volatile DEFINE_PER_CPU(pgd_t *,current_pgd); +unsigned long cris_signal_return_page; /* * This routine handles page faults. It determines the address, @@ -146,8 +180,9 @@ do_page_fault(unsigned long address, str struct vm_area_struct * vma; siginfo_t info; - D(printk("Page fault for %X at %X, prot %d write %d\n", - address, regs->erp, protection, writeaccess)); + D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", + address, smp_processor_id(), instruction_pointer(regs), + protection, writeaccess)); tsk = current; @@ -175,8 +210,19 @@ do_page_fault(unsigned long address, str !user_mode(regs)) goto vmalloc_fault; + /* When stack execution is not allowed we store the signal + * trampolines in the reserved cris_signal_return_page. + * Handle this in the exact same way as vmalloc (we know + * that the mapping is there and is valid so no need to + * call handle_mm_fault). + */ + if (cris_signal_return_page && + address == cris_signal_return_page && + !protection && user_mode(regs)) + goto vmalloc_fault; + /* we can and should enable interrupts at this point */ - sti(); + local_irq_enable(); mm = tsk->mm; info.si_code = SEGV_MAPERR; @@ -220,7 +266,10 @@ do_page_fault(unsigned long address, str /* first do some preliminary protection checks */ - if (writeaccess) { + if (writeaccess == 2){ + if (!(vma->vm_flags & VM_EXEC)) + goto bad_area; + } else if (writeaccess == 1) { if (!(vma->vm_flags & VM_WRITE)) goto bad_area; } else { @@ -234,7 +283,7 @@ do_page_fault(unsigned long address, str * the fault. */ - switch (handle_mm_fault(mm, vma, address, writeaccess)) { + switch (handle_mm_fault(mm, vma, address, writeaccess & 1)) { case 1: tsk->min_flt++; break; @@ -292,10 +341,10 @@ do_page_fault(unsigned long address, str */ if ((unsigned long) (address) < PAGE_SIZE) - printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + raw_printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); else - printk(KERN_ALERT "Unable to handle kernel access"); - printk(" at virtual address %08lx\n",address); + raw_printk(KERN_ALERT "Unable to handle kernel access"); + raw_printk(" at virtual address %08lx\n",address); die_if_kernel("Oops", regs, (writeaccess << 1) | protection); @@ -346,10 +395,11 @@ vmalloc_fault: int offset = pgd_index(address); pgd_t *pgd, *pgd_k; + pud_t *pud, *pud_k; pmd_t *pmd, *pmd_k; pte_t *pte_k; - pgd = (pgd_t *)current_pgd + offset; + pgd = (pgd_t *)per_cpu(current_pgd, smp_processor_id()) + offset; pgd_k = init_mm.pgd + offset; /* Since we're two-level, we don't need to do both @@ -364,8 +414,13 @@ vmalloc_fault: * it exists. */ - pmd = pmd_offset(pgd, address); - pmd_k = pmd_offset(pgd_k, address); + pud = pud_offset(pgd, address); + pud_k = pud_offset(pgd_k, address); + if (!pud_present(*pud_k)) + goto no_context; + + pmd = pmd_offset(pud, address); + pmd_k = pmd_offset(pud_k, address); if (!pmd_present(*pmd_k)) goto bad_area_nosemaphore; @@ -385,3 +440,19 @@ vmalloc_fault: return; } } + +/* Find fixup code. */ +int +find_fixup_code(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + if ((fixup = search_exception_tables(instruction_pointer(regs))) != 0) { + /* Adjust the instruction pointer in the stackframe. */ + instruction_pointer(regs) = fixup->fixup; + arch_fixup(regs); + return 1; + } + + return 0; +} --- linux-2.6.13-rc2/arch/cris/mm/ioremap.c 2005-03-01 23:37:48.000000000 -0800 +++ devel/arch/cris/mm/ioremap.c 2005-07-12 00:26:59.000000000 -0700 @@ -14,9 +14,10 @@ #include #include #include +#include extern inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, - unsigned long phys_addr, unsigned long flags) + unsigned long phys_addr, pgprot_t prot) { unsigned long end; @@ -31,9 +32,7 @@ extern inline void remap_area_pte(pte_t printk("remap_area_pte: page already exists\n"); BUG(); } - set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | __READABLE | - __WRITEABLE | _PAGE_GLOBAL | - _PAGE_KERNEL | flags))); + set_pte(pte, mk_pte_phys(phys_addr, prot)); address += PAGE_SIZE; phys_addr += PAGE_SIZE; pte++; @@ -41,7 +40,7 @@ extern inline void remap_area_pte(pte_t } static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, - unsigned long phys_addr, unsigned long flags) + unsigned long phys_addr, pgprot_t prot) { unsigned long end; @@ -56,7 +55,7 @@ static inline int remap_area_pmd(pmd_t * pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; - remap_area_pte(pte, address, end - address, address + phys_addr, flags); + remap_area_pte(pte, address, end - address, address + phys_addr, prot); address = (address + PMD_SIZE) & PMD_MASK; pmd++; } while (address && (address < end)); @@ -64,7 +63,7 @@ static inline int remap_area_pmd(pmd_t * } static int remap_area_pages(unsigned long address, unsigned long phys_addr, - unsigned long size, unsigned long flags) + unsigned long size, pgprot_t prot) { int error; pgd_t * dir; @@ -77,13 +76,19 @@ static int remap_area_pages(unsigned lon BUG(); spin_lock(&init_mm.page_table_lock); do { + pud_t *pud; pmd_t *pmd; - pmd = pmd_alloc(&init_mm, dir, address); + error = -ENOMEM; + pud = pud_alloc(&init_mm, dir, address); + if (!pud) + break; + pmd = pmd_alloc(&init_mm, pud, address); + if (!pmd) break; if (remap_area_pmd(pmd, address, end - address, - phys_addr + address, flags)) + phys_addr + address, prot)) break; error = 0; address = (address + PGDIR_SIZE) & PGDIR_MASK; @@ -107,9 +112,9 @@ static int remap_area_pages(unsigned lon * have to convert them into an offset in a page-aligned mapping, but the * caller shouldn't need to know that small detail. */ -void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +void __iomem * __ioremap_prot(unsigned long phys_addr, unsigned long size, pgprot_t prot) { - void * addr; + void __iomem * addr; struct vm_struct * area; unsigned long offset, last_addr; @@ -131,15 +136,36 @@ void * __ioremap(unsigned long phys_addr area = get_vm_area(size, VM_IOREMAP); if (!area) return NULL; - addr = area->addr; - if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { - vfree(addr); + addr = (void __iomem *)area->addr; + if (remap_area_pages((unsigned long) addr, phys_addr, size, prot)) { + vfree((void __force *)addr); return NULL; } - return (void *) (offset + (char *)addr); + return (void __iomem *) (offset + (char __iomem *)addr); +} + +void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + return __ioremap_prot(phys_addr, size, + __pgprot(_PAGE_PRESENT | __READABLE | + __WRITEABLE | _PAGE_GLOBAL | + _PAGE_KERNEL | flags)); +} + +/** + * ioremap_nocache - map bus memory into CPU space + * @offset: bus address of the memory + * @size: size of the resource to map + * + * Must be freed with iounmap. + */ + +void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size) +{ + return __ioremap(phys_addr | MEM_NON_CACHEABLE, size, 0); } -void iounmap(void *addr) +void iounmap(volatile void __iomem *addr) { if (addr > high_memory) return vfree((void *) (PAGE_MASK & (unsigned long) addr)); --- linux-2.6.13-rc2/arch/cris/mm/tlb.c 2005-03-01 23:37:49.000000000 -0800 +++ devel/arch/cris/mm/tlb.c 2005-07-12 00:26:59.000000000 -0700 @@ -29,18 +29,6 @@ struct mm_struct *page_id_map[NUM_PAGEID]; static int map_replace_ptr = 1; /* which page_id_map entry to replace next */ -/* - * Initialize the context related info for a new mm_struct - * instance. - */ - -int -init_new_context(struct task_struct *tsk, struct mm_struct *mm) -{ - mm->context = NO_CONTEXT; - return 0; -} - /* the following functions are similar to those used in the PPC port */ static inline void @@ -60,12 +48,12 @@ alloc_context(struct mm_struct *mm) */ flush_tlb_mm(old_mm); - old_mm->context = NO_CONTEXT; + old_mm->context.page_id = NO_CONTEXT; } /* insert it into the page_id_map */ - mm->context = map_replace_ptr; + mm->context.page_id = map_replace_ptr; page_id_map[map_replace_ptr] = mm; map_replace_ptr++; @@ -81,7 +69,7 @@ alloc_context(struct mm_struct *mm) void get_mmu_context(struct mm_struct *mm) { - if(mm->context == NO_CONTEXT) + if(mm->context.page_id == NO_CONTEXT) alloc_context(mm); } @@ -96,11 +84,10 @@ get_mmu_context(struct mm_struct *mm) void destroy_context(struct mm_struct *mm) { - if(mm->context != NO_CONTEXT) { - D(printk("destroy_context %d (%p)\n", mm->context, mm)); + if(mm->context.page_id != NO_CONTEXT) { + D(printk("destroy_context %d (%p)\n", mm->context.page_id, mm)); flush_tlb_mm(mm); /* TODO this might be redundant ? */ - page_id_map[mm->context] = NULL; - /* mm->context = NO_CONTEXT; redundant.. mm will be freed */ + page_id_map[mm->context.page_id] = NULL; } } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/frv/defconfig 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,627 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.11.8 +# Fri May 13 17:16:03 2005 +# +CONFIG_FRV=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +# CONFIG_GENERIC_CALIBRATE_DELAY is not set +# CONFIG_GENERIC_HARDIRQS is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +# CONFIG_HOTPLUG is not set +# CONFIG_KOBJECT_UEVENT is not set +# CONFIG_IKCONFIG is not set +CONFIG_EMBEDDED=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# Fujitsu FR-V system setup +# +CONFIG_MMU=y +CONFIG_FRV_OUTOFLINE_ATOMIC_OPS=y +CONFIG_HIGHMEM=y +CONFIG_HIGHPTE=y +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_FRV_DEFL_CACHE_WBACK is not set +# CONFIG_FRV_DEFL_CACHE_WBEHIND is not set +CONFIG_FRV_DEFL_CACHE_WTHRU=y +# CONFIG_FRV_DEFL_CACHE_DISABLED is not set + +# +# CPU core support +# +CONFIG_CPU_FR451=y +CONFIG_CPU_FR451_COMPILE=y +CONFIG_FRV_L1_CACHE_SHIFT=5 +CONFIG_MB93091_VDK=y +# CONFIG_MB93093_PDK is not set +CONFIG_MB93090_MB00=y +# CONFIG_MB93091_NO_MB is not set +# CONFIG_GPREL_DATA_8 is not set +CONFIG_GPREL_DATA_4=y +# CONFIG_GPREL_DATA_NONE is not set +CONFIG_PCI=y +# CONFIG_PCI_LEGACY_PROC is not set +# CONFIG_PCI_NAMES is not set +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCMCIA is not set + +# +# Power management options +# +# CONFIG_PM is not set + +# +# Executable formats +# +# CONFIG_BINFMT_ELF is not set +CONFIG_BINFMT_ELF_FDPIC=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Connector - unified userspace <-> kernelspace linker +# +# CONFIG_CONNECTOR is not set +# CONFIG_FORK_CONNECTOR is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_RAM is not set +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_ATA_OVER_ETH is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_IP_TCPDIAG is not set +# CONFIG_IP_TCPDIAG_IPV6 is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_KGDBOE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NETPOLL_RX is not set +# CONFIG_NETPOLL_TRAP is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set + +# +# Tulip family network device support +# +# CONFIG_NET_TULIP is not set +# CONFIG_HP100 is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_DGRS is not set +# CONFIG_EEPRO100 is not set +# CONFIG_E100 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +CONFIG_NE2K_PCI=y +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SKGE is not set +# CONFIG_SK98LIN is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_CHELSIO_T1 is not set +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=1 +CONFIG_SERIAL_8250_EXTENDED=y +# CONFIG_SERIAL_8250_MANY_PORTS is not set +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_MULTIPORT is not set +# CONFIG_SERIAL_8250_RSA is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# +# CONFIG_TCG_TPM is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# InfiniBand support +# +# CONFIG_INFINIBAND is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISER4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set + +# +# XFS support +# +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +# CONFIG_PROC_KCORE is not set +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_RELAYFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_FRAME_POINTER is not set +# CONFIG_EARLY_PRINTK is not set +CONFIG_DEBUG_STACKOVERFLOW=y +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_GDBSTUB is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set --- linux-2.6.13-rc2/arch/frv/mb93090-mb00/pci-irq.c 2005-03-01 23:38:33.000000000 -0800 +++ devel/arch/frv/mb93090-mb00/pci-irq.c 2005-07-12 00:26:38.000000000 -0700 @@ -60,7 +60,7 @@ void __init pcibios_fixup_irqs(void) } } -void __init pcibios_penalize_isa_irq(int irq) +void __init pcibios_penalize_isa_irq(int irq, int active) { } --- linux-2.6.13-rc2/arch/i386/Kconfig 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/Kconfig 2005-07-12 00:27:18.000000000 -0700 @@ -500,6 +500,18 @@ config NR_CPUS This is purely to save memory - each supported CPU adds approximately eight kilobytes to the kernel image. +config MAX_IO_APICS + int "Maximum number of IO-APICs (1-256)" + range 1 256 + default "32" if X86_NUMAQ || X86_SUMMIT || X86_BIGSMP + default "65" if X86_ES7000 || X86_GENERICARCH + default "1" if !X86_IO_APIC + default "8" + help + This option allows to specify the maximum number of I/O interrupt + controllers (IO-APICs). The maximum supported value is 256 and + the minimum is 1. + config SCHED_SMT bool "SMT (Hyperthreading) scheduler support" depends on SMP @@ -1133,6 +1145,8 @@ config APM_REAL_MODE_POWER_OFF a work-around for a number of buggy BIOSes. Switch this option on if your computer crashes instead of powering off properly. +source "drivers/perfctr/Kconfig" + endmenu source "arch/i386/kernel/cpu/cpufreq/Kconfig" @@ -1310,6 +1324,11 @@ config GENERIC_IRQ_PROBE bool default y +config GENERIC_PENDING_IRQ + bool + depends on GENERIC_HARDIRQS && SMP + default y + config X86_SMP bool depends on SMP && !X86_VOYAGER --- linux-2.6.13-rc2/arch/i386/Kconfig.debug 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/i386/Kconfig.debug 2005-07-12 00:27:16.000000000 -0700 @@ -69,4 +69,6 @@ config X86_MPPARSE depends on X86_LOCAL_APIC && !X86_VISWS default y +source "arch/i386/Kconfig.kgdb" + endmenu --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/i386/Kconfig.kgdb 2005-07-12 00:27:16.000000000 -0700 @@ -0,0 +1,178 @@ +config KGDB + bool "Include kgdb kernel debugger" + depends on DEBUG_KERNEL && !KPROBES + help + If you say Y here, the system will be compiled with the debug + option (-g) and a debugging stub will be included in the + kernel. This stub communicates with gdb on another (host) + computer via a serial port. The host computer should have + access to the kernel binary file (vmlinux) and a serial port + that is connected to the target machine. Gdb can be made to + configure the serial port or you can use stty and setserial to + do this. See the 'target' command in gdb. This option also + configures in the ability to request a breakpoint early in the + boot process. To request the breakpoint just include 'kgdb' + as a boot option when booting the target machine. The system + will then break as soon as it looks at the boot options. This + option also installs a breakpoint in panic and sends any + kernel faults to the debugger. For more information see the + Documentation/i386/kgdb/kgdb.txt file. + +choice + depends on KGDB + prompt "Debug serial port BAUD" + default KGDB_115200BAUD + help + Gdb and the kernel stub need to agree on the baud rate to be + used. Some systems (x86 family at this writing) allow this to + be configured. + +config KGDB_9600BAUD + bool "9600" + +config KGDB_19200BAUD + bool "19200" + +config KGDB_38400BAUD + bool "38400" + +config KGDB_57600BAUD + bool "57600" + +config KGDB_115200BAUD + bool "115200" +endchoice + +config KGDB_PORT + hex "hex I/O port address of the debug serial port" + depends on KGDB + default 3f8 + help + Some systems (x86 family at this writing) allow the port + address to be configured. The number entered is assumed to be + hex, don't put 0x in front of it. The standard address are: + COM1 3f8 , irq 4 and COM2 2f8 irq 3. Setserial /dev/ttySx + will tell you what you have. It is good to test the serial + connection with a live system before trying to debug. + +config KGDB_IRQ + int "IRQ of the debug serial port" + depends on KGDB + default 4 + help + This is the irq for the debug port. If everything is working + correctly and the kernel has interrupts on a control C to the + port should cause a break into the kernel debug stub. + +config KGDB_MORE + bool "Add any additional compile options" + depends on KGDB + default n + help + Saying yes here turns on the ability to enter additional + compile options. + + +config KGDB_OPTIONS + depends on KGDB_MORE + string "Additional compile arguments" + default "-O1" + help + This option allows you enter additional compile options for + the whole kernel compile. Each platform will have a default + that seems right for it. For example on PPC "-ggdb -O1", and + for i386 "-O1". Note that by configuring KGDB "-g" is already + turned on. In addition, on i386 platforms + "-fomit-frame-pointer" is deleted from the standard compile + options. + +config NO_KGDB_CPUS + int "Number of CPUs" + depends on KGDB && SMP + default NR_CPUS + help + + This option sets the number of cpus for kgdb ONLY. It is used + to prune some internal structures so they look "nice" when + displayed with gdb. This is to overcome possibly larger + numbers that may have been entered above. Enter the real + number to get nice clean kgdb_info displays. + +config KGDB_TS + bool "Enable kgdb time stamp macros?" + depends on KGDB + default n + help + Kgdb event macros allow you to instrument your code with calls + to the kgdb event recording function. The event log may be + examined with gdb at a break point. Turning on this + capability also allows you to choose how many events to + keep. Kgdb always keeps the lastest events. + +choice + depends on KGDB_TS + prompt "Max number of time stamps to save?" + default KGDB_TS_128 + +config KGDB_TS_64 + bool "64" + +config KGDB_TS_128 + bool "128" + +config KGDB_TS_256 + bool "256" + +config KGDB_TS_512 + bool "512" + +config KGDB_TS_1024 + bool "1024" + +endchoice + +config STACK_OVERFLOW_TEST + bool "Turn on kernel stack overflow testing?" + depends on KGDB + default n + help + This option enables code in the front line interrupt handlers + to check for kernel stack overflow on interrupts and system + calls. This is part of the kgdb code on x86 systems. + +config TRAP_BAD_SYSCALL_EXITS + bool "Debug bad system call exits" + depends on KGDB + help + If you say Y here the kernel will check for system calls which + return without clearing preempt. + default n + +config KGDB_CONSOLE + bool "Enable serial console thru kgdb port" + depends on KGDB + default n + help + This option enables the command line "console=kgdb" option. + When the system is booted with this option in the command line + all kernel printk output is sent to gdb (as well as to other + consoles). For this to work gdb must be connected. For this + reason, this command line option will generate a breakpoint if + gdb has not yet connected. After the gdb continue command is + given all pent up console output will be printed by gdb on the + host machine. Neither this option, nor KGDB require the + serial driver to be configured. + +config KGDB_SYSRQ + bool "Turn on SysRq 'G' command to do a break?" + depends on KGDB + default y + help + This option includes an option in the SysRq code that allows + you to enter SysRq G which generates a breakpoint to the KGDB + stub. This will work if the keyboard is alive and can + interrupt the system. Because of constraints on when the + serial port interrupt can be enabled, this code may allow you + to interrupt the system before the serial port control C is + available. Just say yes here. + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/i386/kernel/acpi/cstate.c 2005-07-12 00:26:38.000000000 -0700 @@ -0,0 +1,102 @@ +/* + * arch/i386/kernel/acpi/cstate.c + * + * Copyright (C) 2005 Intel Corporation + * Venkatesh Pallipadi + * - Added _PDC for SMP C-states on Intel CPUs + */ + +#include +#include +#include +#include + +#include +#include + +static void acpi_processor_power_init_intel_pdc( + struct acpi_processor_power *pow) +{ + struct acpi_object_list *obj_list; + union acpi_object *obj; + u32 *buf; + + /* allocate and initialize pdc. It will be used later. */ + obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL); + if (!obj_list) { + printk(KERN_ERR "Memory allocation error\n"); + return; + } + + obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL); + if (!obj) { + printk(KERN_ERR "Memory allocation error\n"); + kfree(obj_list); + return; + } + + buf = kmalloc(12, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "Memory allocation error\n"); + kfree(obj); + kfree(obj_list); + return; + } + + buf[0] = ACPI_PDC_REVISION_ID; + buf[1] = 1; + buf[2] = ACPI_PDC_C_CAPABILITY_SMP; + + obj->type = ACPI_TYPE_BUFFER; + obj->buffer.length = 12; + obj->buffer.pointer = (u8 *)buf; + obj_list->count = 1; + obj_list->pointer = obj; + pow->pdc = obj_list; + + return; +} + +/* Initialize _PDC data based on the CPU vendor */ +void acpi_processor_power_init_pdc(struct acpi_processor_power *pow, + unsigned int cpu) +{ + struct cpuinfo_x86 *c = cpu_data + cpu; + + pow->pdc = NULL; + if (c->x86_vendor == X86_VENDOR_INTEL) + acpi_processor_power_init_intel_pdc(pow); + + return; +} +EXPORT_SYMBOL(acpi_processor_power_init_pdc); + +/* + * Initialize bm_flags based on the CPU cache properties + * On SMP it depends on cache configuration + * - When cache is not shared among all CPUs, we flush cache + * before entering C3. + * - When cache is shared among all CPUs, we use bm_check + * mechanism as in UP case + * + * This routine is called only after all the CPUs are online + */ +void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, + unsigned int cpu) +{ + struct cpuinfo_x86 *c = cpu_data + cpu; + + flags->bm_check = 0; + if (num_online_cpus() == 1) + flags->bm_check = 1; + else if (c->x86_vendor == X86_VENDOR_INTEL) { + /* + * Today all CPUs that support C3 share cache. + * TBD: This needs to look at cache shared map, once + * multi-core detection patch makes to the base. + */ + flags->bm_check = 1; + } +} + +EXPORT_SYMBOL(acpi_processor_power_init_bm_check); --- linux-2.6.13-rc2/arch/i386/kernel/acpi/Makefile 2005-03-01 23:38:09.000000000 -0800 +++ devel/arch/i386/kernel/acpi/Makefile 2005-07-12 00:26:38.000000000 -0700 @@ -2,3 +2,7 @@ obj-$(CONFIG_ACPI_BOOT) := boot.o obj-$(CONFIG_X86_IO_APIC) += earlyquirk.o obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup.o +ifneq ($(CONFIG_ACPI_PROCESSOR),) +obj-y += cstate.o +endif + --- linux-2.6.13-rc2/arch/i386/kernel/acpi/wakeup.S 2005-03-01 23:37:49.000000000 -0800 +++ devel/arch/i386/kernel/acpi/wakeup.S 2005-07-12 00:26:38.000000000 -0700 @@ -74,8 +74,9 @@ wakeup_code: movw %ax,%fs movw $0x0e00 + 'i', %fs:(0x12) - # need a gdt - lgdt real_save_gdt - wakeup_code + # need a gdt -- use lgdtl to force 32-bit operands, in case + # the GDT is located past 16 megabytes. + lgdtl real_save_gdt - wakeup_code movl real_save_cr0 - wakeup_code, %eax movl %eax, %cr0 --- linux-2.6.13-rc2/arch/i386/kernel/cpu/common.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/cpu/common.c 2005-07-12 00:26:30.000000000 -0700 @@ -435,6 +435,11 @@ void __devinit identify_cpu(struct cpuin if (c == &boot_cpu_data) sysenter_setup(); enable_sep_cpu(); + + if (c == &boot_cpu_data) + mtrr_bp_init(); + else + mtrr_ap_init(); } #ifdef CONFIG_X86_HT --- linux-2.6.13-rc2/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2005-07-12 00:26:40.000000000 -0700 @@ -110,14 +110,13 @@ static int query_current_values_with_pen u32 lo, hi; u32 i = 0; - lo = MSR_S_LO_CHANGE_PENDING; - while (lo & MSR_S_LO_CHANGE_PENDING) { + do { if (i++ > 0x1000000) { printk(KERN_ERR PFX "detected change pending stuck\n"); return 1; } rdmsr(MSR_FIDVID_STATUS, lo, hi); - } + } while (lo & MSR_S_LO_CHANGE_PENDING); data->currvid = hi & MSR_S_HI_CURRENT_VID; data->currfid = lo & MSR_S_LO_CURRENT_FID; @@ -454,7 +453,6 @@ static int check_supported_cpu(unsigned oldmask = current->cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(cpu)); - schedule(); if (smp_processor_id() != cpu) { printk(KERN_ERR "limiting to cpu %u failed\n", cpu); @@ -489,7 +487,6 @@ static int check_supported_cpu(unsigned out: set_cpus_allowed(current, oldmask); - schedule(); return rc; } @@ -896,7 +893,6 @@ static int powernowk8_target(struct cpuf /* only run on specific CPU from here on */ oldmask = current->cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(pol->cpu)); - schedule(); if (smp_processor_id() != pol->cpu) { printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu); @@ -960,8 +956,6 @@ static int powernowk8_target(struct cpuf err_out: set_cpus_allowed(current, oldmask); - schedule(); - return ret; } @@ -1018,7 +1012,6 @@ static int __init powernowk8_cpu_init(st /* only run on specific CPU from here on */ oldmask = current->cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(pol->cpu)); - schedule(); if (smp_processor_id() != pol->cpu) { printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu); @@ -1037,7 +1030,6 @@ static int __init powernowk8_cpu_init(st /* run on any CPU again */ set_cpus_allowed(current, oldmask); - schedule(); pol->governor = CPUFREQ_DEFAULT_GOVERNOR; pol->cpus = cpu_core_map[pol->cpu]; @@ -1070,7 +1062,6 @@ static int __init powernowk8_cpu_init(st err_out: set_cpus_allowed(current, oldmask); - schedule(); powernow_k8_cpu_exit_acpi(data); kfree(data); @@ -1106,7 +1097,6 @@ static unsigned int powernowk8_get (unsi set_cpus_allowed(current, oldmask); return 0; } - preempt_disable(); if (query_current_values_with_pending_wait(data)) goto out; @@ -1114,7 +1104,6 @@ static unsigned int powernowk8_get (unsi khz = find_khz_freq_from_fid(data->currfid); out: - preempt_enable_no_resched(); set_cpus_allowed(current, oldmask); return khz; --- linux-2.6.13-rc2/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c 2005-07-12 00:26:38.000000000 -0700 @@ -375,7 +375,7 @@ static int centrino_cpu_init_acpi(struct arg0.buffer.pointer = (u8 *) arg0_buf; arg0_buf[0] = ACPI_PDC_REVISION_ID; arg0_buf[1] = 1; - arg0_buf[2] = ACPI_PDC_EST_CAPABILITY_SMP | ACPI_PDC_EST_CAPABILITY_MSR; + arg0_buf[2] = ACPI_PDC_EST_CAPABILITY_SMP_MSR; p.pdc = &arg_list; --- linux-2.6.13-rc2/arch/i386/kernel/cpu/intel.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/cpu/intel.c 2005-07-12 00:26:30.000000000 -0700 @@ -25,7 +25,7 @@ extern int trap_init_f00f_bug(void); /* * Alignment at which movsl is preferred for bulk memory copies. */ -struct movsl_mask movsl_mask; +struct movsl_mask movsl_mask __read_mostly; #endif void __devinit early_intel_workaround(struct cpuinfo_x86 *c) --- linux-2.6.13-rc2/arch/i386/kernel/cpu/mtrr/generic.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/cpu/mtrr/generic.c 2005-07-12 00:26:30.000000000 -0700 @@ -67,13 +67,6 @@ void __init get_mtrr_state(void) mtrr_state.enabled = (lo & 0xc00) >> 10; } -/* Free resources associated with a struct mtrr_state */ -void __init finalize_mtrr_state(void) -{ - kfree(mtrr_state.var_ranges); - mtrr_state.var_ranges = NULL; -} - /* Some BIOS's are fucked and don't set all MTRRs the same! */ void __init mtrr_state_warn(void) { @@ -334,6 +327,9 @@ static void generic_set_mtrr(unsigned in */ { unsigned long flags; + struct mtrr_var_range *vr; + + vr = &mtrr_state.var_ranges[reg]; local_irq_save(flags); prepare_set(); @@ -342,11 +338,15 @@ static void generic_set_mtrr(unsigned in /* The invalid bit is kept in the mask, so we simply clear the relevant mask register to disable a range. */ mtrr_wrmsr(MTRRphysMask_MSR(reg), 0, 0); + memset(vr, 0, sizeof(struct mtrr_var_range)); } else { - mtrr_wrmsr(MTRRphysBase_MSR(reg), base << PAGE_SHIFT | type, - (base & size_and_mask) >> (32 - PAGE_SHIFT)); - mtrr_wrmsr(MTRRphysMask_MSR(reg), -size << PAGE_SHIFT | 0x800, - (-size & size_and_mask) >> (32 - PAGE_SHIFT)); + vr->base_lo = base << PAGE_SHIFT | type; + vr->base_hi = (base & size_and_mask) >> (32 - PAGE_SHIFT); + vr->mask_lo = -size << PAGE_SHIFT | 0x800; + vr->mask_hi = (-size & size_and_mask) >> (32 - PAGE_SHIFT); + + mtrr_wrmsr(MTRRphysBase_MSR(reg), vr->base_lo, vr->base_hi); + mtrr_wrmsr(MTRRphysMask_MSR(reg), vr->mask_lo, vr->mask_hi); } post_set(); --- linux-2.6.13-rc2/arch/i386/kernel/cpu/mtrr/main.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/cpu/mtrr/main.c 2005-07-12 00:26:30.000000000 -0700 @@ -332,6 +332,8 @@ int mtrr_add_page(unsigned long base, un error = -EINVAL; + /* No CPU hotplug when we change MTRR entries */ + lock_cpu_hotplug(); /* Search for existing MTRR */ down(&main_lock); for (i = 0; i < num_var_ranges; ++i) { @@ -372,6 +374,7 @@ int mtrr_add_page(unsigned long base, un error = i; out: up(&main_lock); + unlock_cpu_hotplug(); return error; } @@ -461,6 +464,8 @@ int mtrr_del_page(int reg, unsigned long return -ENXIO; max = num_var_ranges; + /* No CPU hotplug when we change MTRR entries */ + lock_cpu_hotplug(); down(&main_lock); if (reg < 0) { /* Search for existing MTRR */ @@ -501,6 +506,7 @@ int mtrr_del_page(int reg, unsigned long error = reg; out: up(&main_lock); + unlock_cpu_hotplug(); return error; } /** @@ -544,21 +550,9 @@ static void __init init_ifs(void) centaur_init_mtrr(); } -static void __init init_other_cpus(void) -{ - if (use_intel()) - get_mtrr_state(); - - /* bring up the other processors */ - set_mtrr(~0U,0,0,0); - - if (use_intel()) { - finalize_mtrr_state(); - mtrr_state_warn(); - } -} - - +/* The suspend/resume methods are only for CPU without MTRR. CPU using generic + * MTRR driver doesn't require this + */ struct mtrr_value { mtrr_type ltype; unsigned long lbase; @@ -611,13 +605,13 @@ static struct sysdev_driver mtrr_sysdev_ /** - * mtrr_init - initialize mtrrs on the boot CPU + * mtrr_bp_init - initialize mtrrs on the boot CPU * * This needs to be called early; before any of the other CPUs are * initialized (i.e. before smp_init()). * */ -static int __init mtrr_init(void) +void __init mtrr_bp_init(void) { init_ifs(); @@ -674,12 +668,48 @@ static int __init mtrr_init(void) if (mtrr_if) { set_num_var_ranges(); init_table(); - init_other_cpus(); - - return sysdev_driver_register(&cpu_sysdev_class, - &mtrr_sysdev_driver); + if (use_intel()) + get_mtrr_state(); } - return -ENXIO; } -subsys_initcall(mtrr_init); +void mtrr_ap_init(void) +{ + unsigned long flags; + + if (!mtrr_if || !use_intel()) + return; + /* + * Ideally we should hold main_lock here to avoid mtrr entries changed, + * but this routine will be called in cpu boot time, holding the lock + * breaks it. This routine is called in two cases: 1.very earily time + * of software resume, when there absolutely isn't mtrr entry changes; + * 2.cpu hotadd time. We let mtrr_add/del_page hold cpuhotplug lock to + * prevent mtrr entry changes + */ + local_irq_save(flags); + + mtrr_if->set_all(); + + local_irq_restore(flags); +} + +static int __init mtrr_init_finialize(void) +{ + if (!mtrr_if) + return 0; + if (use_intel()) + mtrr_state_warn(); + else { + /* The CPUs haven't MTRR and seemes not support SMP. They have + * specific drivers, we use a tricky method to support + * suspend/resume for them. + * TBD: is there any system with such CPU which supports + * suspend/resume? if no, we should remove the code. + */ + sysdev_driver_register(&cpu_sysdev_class, + &mtrr_sysdev_driver); + } + return 0; +} +subsys_initcall(mtrr_init_finialize); --- linux-2.6.13-rc2/arch/i386/kernel/cpu/mtrr/mtrr.h 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/i386/kernel/cpu/mtrr/mtrr.h 2005-07-12 00:26:30.000000000 -0700 @@ -91,7 +91,6 @@ extern struct mtrr_ops * mtrr_if; extern unsigned int num_var_ranges; -void finalize_mtrr_state(void); void mtrr_state_warn(void); char *mtrr_attrib_to_str(int x); void mtrr_wrmsr(unsigned, unsigned, unsigned); --- linux-2.6.13-rc2/arch/i386/kernel/entry.S 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/i386/kernel/entry.S 2005-07-12 00:27:18.000000000 -0700 @@ -49,6 +49,18 @@ #include #include #include "irq_vectors.h" + /* We do not recover from a stack overflow, but at least + * we know it happened and should be able to track it down. + */ +#ifdef CONFIG_STACK_OVERFLOW_TEST +#define STACK_OVERFLOW_TEST \ + testl $(THREAD_SIZE - 512),%esp; \ + jnz 10f; \ + call stack_overflow; \ +10: +#else +#define STACK_OVERFLOW_TEST +#endif #define nr_syscalls ((syscall_table_size)/4) @@ -95,7 +107,8 @@ VM_MASK = 0x00020000 pushl %ebx; \ movl $(__USER_DS), %edx; \ movl %edx, %ds; \ - movl %edx, %es; + movl %edx, %es; \ + STACK_OVERFLOW_TEST #define RESTORE_INT_REGS \ popl %ebx; \ @@ -178,6 +191,7 @@ need_resched: # sysenter call handler stub ENTRY(sysenter_entry) movl TSS_sysenter_esp0(%esp),%esp + .globl sysenter_past_esp sysenter_past_esp: sti pushl $(__USER_DS) @@ -239,6 +253,10 @@ syscall_exit: cli # make sure we don't miss an interrupt # setting need_resched or sigpending # between sampling and the iret +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS + movl %esp, %eax # pt_regs pointer + call sys_call_exit +#endif movl TI_flags(%ebp), %ecx testw $_TIF_ALLWORK_MASK, %cx # current->work jne syscall_exit_work @@ -427,6 +445,16 @@ ENTRY(name) \ /* The include is where all of the SMP etc. interrupts come from */ #include "entry_arch.h" +#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_PERFCTR) +ENTRY(perfctr_interrupt) + pushl $LOCAL_PERFCTR_VECTOR-256 + SAVE_ALL + pushl %esp + call smp_perfctr_interrupt + addl $4, %esp + jmp ret_from_intr +#endif + ENTRY(divide_error) pushl $0 # no error code pushl $do_divide_error --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/i386/kernel/i8237.c 2005-07-12 00:26:58.000000000 -0700 @@ -0,0 +1,67 @@ +/* + * i8237.c: 8237A DMA controller suspend functions. + * + * Written by Pierre Ossman, 2005. + */ + +#include +#include + +#include + +/* + * This module just handles suspend/resume issues with the + * 8237A DMA controller (used for ISA and LPC). + * Allocation is handled in kernel/dma.c and normal usage is + * in asm/dma.h. + */ + +static int i8237A_resume(struct sys_device *dev) +{ + unsigned long flags; + int i; + + flags = claim_dma_lock(); + + dma_outb(DMA1_RESET_REG, 0); + dma_outb(DMA2_RESET_REG, 0); + + for (i = 0;i < 8;i++) { + set_dma_addr(i, 0x000000); + /* DMA count is a bit weird so this is not 0 */ + set_dma_count(i, 1); + } + + /* Enable cascade DMA or channel 0-3 won't work */ + enable_dma(4); + + release_dma_lock(flags); + + return 0; +} + +static int i8237A_suspend(struct sys_device *dev, pm_message_t state) +{ + return 0; +} + +static struct sysdev_class i8237_sysdev_class = { + set_kset_name("i8237"), + .suspend = i8237A_suspend, + .resume = i8237A_resume, +}; + +static struct sys_device device_i8237A = { + .id = 0, + .cls = &i8237_sysdev_class, +}; + +static int __init i8237A_init_sysfs(void) +{ + int error = sysdev_class_register(&i8237_sysdev_class); + if (!error) + error = sysdev_register(&device_i8237A); + return error; +} + +device_initcall(i8237A_init_sysfs); --- linux-2.6.13-rc2/arch/i386/kernel/i8259.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/i8259.c 2005-07-12 00:27:18.000000000 -0700 @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -424,6 +425,8 @@ void __init init_IRQ(void) */ intr_init_hook(); + perfctr_vector_init(); + /* * Set the clock to HZ Hz, we already have a valid * vector now: --- linux-2.6.13-rc2/arch/i386/kernel/io_apic.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/io_apic.c 2005-07-12 00:26:57.000000000 -0700 @@ -33,6 +33,7 @@ #include #include #include + #include #include #include @@ -222,13 +223,21 @@ static void clear_IO_APIC (void) clear_IO_APIC_pin(apic, pin); } +#ifdef CONFIG_SMP static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t cpumask) { unsigned long flags; int pin; struct irq_pin_list *entry = irq_2_pin + irq; unsigned int apicid_value; + cpumask_t tmp; + cpus_and(tmp, cpumask, cpu_online_map); + if (cpus_empty(tmp)) + tmp = TARGET_CPUS; + + cpus_and(cpumask, tmp, CPU_MASK_ALL); + apicid_value = cpu_mask_to_apicid(cpumask); /* Prepare to do the io_apic_write */ apicid_value = apicid_value << 24; @@ -242,6 +251,7 @@ static void set_ioapic_affinity_irq(unsi break; entry = irq_2_pin + entry->next; } + set_irq_info(irq, cpumask); spin_unlock_irqrestore(&ioapic_lock, flags); } @@ -259,7 +269,6 @@ static void set_ioapic_affinity_irq(unsi # define Dprintk(x...) # endif -cpumask_t __cacheline_aligned pending_irq_balance_cpumask[NR_IRQS]; #define IRQBALANCE_CHECK_ARCH -999 static int irqbalance_disabled = IRQBALANCE_CHECK_ARCH; @@ -328,12 +337,7 @@ static inline void balance_irq(int cpu, cpus_and(allowed_mask, cpu_online_map, irq_affinity[irq]); new_cpu = move(cpu, allowed_mask, now, 1); if (cpu != new_cpu) { - irq_desc_t *desc = irq_desc + irq; - unsigned long flags; - - spin_lock_irqsave(&desc->lock, flags); - pending_irq_balance_cpumask[irq] = cpumask_of_cpu(new_cpu); - spin_unlock_irqrestore(&desc->lock, flags); + set_pending_irq(irq, cpumask_of_cpu(new_cpu)); } } @@ -528,16 +532,12 @@ tryanotherirq: cpus_and(tmp, target_cpu_mask, allowed_mask); if (!cpus_empty(tmp)) { - irq_desc_t *desc = irq_desc + selected_irq; - unsigned long flags; Dprintk("irq = %d moved to cpu = %d\n", selected_irq, min_loaded); /* mark for change destination */ - spin_lock_irqsave(&desc->lock, flags); - pending_irq_balance_cpumask[selected_irq] = - cpumask_of_cpu(min_loaded); - spin_unlock_irqrestore(&desc->lock, flags); + set_pending_irq(selected_irq, cpumask_of_cpu(min_loaded)); + /* Since we made a change, come back sooner to * check for more variation. */ @@ -568,7 +568,8 @@ static int balanced_irq(void *unused) /* push everything to CPU 0 to give us a starting point. */ for (i = 0 ; i < NR_IRQS ; i++) { - pending_irq_balance_cpumask[i] = cpumask_of_cpu(0); + pending_irq_cpumask[i] = cpumask_of_cpu(0); + set_pending_irq(i, cpumask_of_cpu(0)); } for ( ; ; ) { @@ -647,20 +648,9 @@ int __init irqbalance_disable(char *str) __setup("noirqbalance", irqbalance_disable); -static inline void move_irq(int irq) -{ - /* note - we hold the desc->lock */ - if (unlikely(!cpus_empty(pending_irq_balance_cpumask[irq]))) { - set_ioapic_affinity_irq(irq, pending_irq_balance_cpumask[irq]); - cpus_clear(pending_irq_balance_cpumask[irq]); - } -} - late_initcall(balanced_irq_init); - -#else /* !CONFIG_IRQBALANCE */ -static inline void move_irq(int irq) { } #endif /* CONFIG_IRQBALANCE */ +#endif /* CONFIG_SMP */ #ifndef CONFIG_SMP void fastcall send_IPI_self(int vector) @@ -820,6 +810,7 @@ EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector * we need to reprogram the ioredtbls to cater for the cpus which have come online * so mask in all cases should simply be TARGET_CPUS */ +#ifdef CONFIG_SMP void __init setup_ioapic_dest(void) { int pin, ioapic, irq, irq_entry; @@ -838,6 +829,7 @@ void __init setup_ioapic_dest(void) } } +#endif /* * EISA Edge/Level control register, ELCR @@ -1249,6 +1241,7 @@ static void __init setup_IO_APIC_irqs(vo spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); + set_native_irq_info(irq, TARGET_CPUS); spin_unlock_irqrestore(&ioapic_lock, flags); } } @@ -1944,6 +1937,7 @@ static void ack_edge_ioapic_vector(unsig { int irq = vector_to_irq(vector); + move_irq(vector); ack_edge_ioapic_irq(irq); } @@ -1958,6 +1952,7 @@ static void end_level_ioapic_vector (uns { int irq = vector_to_irq(vector); + move_irq(vector); end_level_ioapic_irq(irq); } @@ -1975,14 +1970,17 @@ static void unmask_IO_APIC_vector (unsig unmask_IO_APIC_irq(irq); } +#ifdef CONFIG_SMP static void set_ioapic_affinity_vector (unsigned int vector, cpumask_t cpu_mask) { int irq = vector_to_irq(vector); + set_native_irq_info(vector, cpu_mask); set_ioapic_affinity_irq(irq, cpu_mask); } #endif +#endif /* * Level and edge triggered IO-APIC interrupts need different handling, @@ -2000,7 +1998,9 @@ static struct hw_interrupt_type ioapic_e .disable = disable_edge_ioapic, .ack = ack_edge_ioapic, .end = end_edge_ioapic, +#ifdef CONFIG_SMP .set_affinity = set_ioapic_affinity, +#endif }; static struct hw_interrupt_type ioapic_level_type = { @@ -2011,7 +2011,9 @@ static struct hw_interrupt_type ioapic_l .disable = disable_level_ioapic, .ack = mask_and_ack_level_ioapic, .end = end_level_ioapic, +#ifdef CONFIG_SMP .set_affinity = set_ioapic_affinity, +#endif }; static inline void init_IO_APIC_traps(void) @@ -2569,6 +2571,7 @@ int io_apic_set_pci_routing (int ioapic, spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(ioapic, 0x11+2*pin, *(((int *)&entry)+1)); io_apic_write(ioapic, 0x10+2*pin, *(((int *)&entry)+0)); + set_native_irq_info(use_pci_vector() ? entry.vector : irq, TARGET_CPUS); spin_unlock_irqrestore(&ioapic_lock, flags); return 0; --- linux-2.6.13-rc2/arch/i386/kernel/irq.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/irq.c 2005-07-12 00:27:16.000000000 -0700 @@ -109,6 +109,8 @@ fastcall unsigned int do_IRQ(struct pt_r irq_exit(); + kgdb_process_breakpoint(); + return 1; } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/i386/kernel/kgdb_stub.c 2005-07-12 00:27:16.000000000 -0700 @@ -0,0 +1,2452 @@ +/* + * + * 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, 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. + * + */ + +/* + * Copyright (c) 2000 VERITAS Software Corporation. + * + */ +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * Updated by: David Grothe + * Updated by: Robert Walsh + * Updated by: wangdi + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Compatibility with 2.1.xx kernel by David Grothe + * + * Changes to allow auto initilization. All that is needed is that it + * be linked with the kernel and a break point (int 3) be executed. + * The header file defines BREAKPOINT to allow one to do + * this. It should also be possible, once the interrupt system is up, to + * call putDebugChar("+"). Once this is done, the remote debugger should + * get our attention by sending a ^C in a packet. George Anzinger + * + * Integrated into 2.2.5 kernel by Tigran Aivazian + * Added thread support, support for multiple processors, + * support for ia-32(x86) hardware debugging. + * Amit S. Kale ( akale@veritas.com ) + * + * Modified to support debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing an int 3. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ +#define KGDB_VERSION "<20030915.1651.33>" +#include +#include +#include /* for strcpy */ +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include +#include +#include + +/************************************************************************ + * + * external low-level support routines + */ +typedef void (*Function) (void); /* pointer to a function */ + +/* Thread reference */ +typedef unsigned char threadref[8]; + +extern int tty_putDebugChar(int); /* write a single character */ +extern int tty_getDebugChar(void); /* read and return a single char */ +extern void tty_flushDebugChar(void); /* flush pending characters */ +extern int eth_putDebugChar(int); /* write a single character */ +extern int eth_getDebugChar(void); /* read and return a single char */ +extern void eth_flushDebugChar(void); /* flush pending characters */ + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +#define BUFMAX 400 + +char *kgdb_version = KGDB_VERSION; + +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ +int debug_regs = 0; /* set to non-zero to print registers */ + +/* filled in by an external module */ +char *gdb_module_offsets; + +static const char hexchars[] = "0123456789abcdef"; + +/* Number of bytes of registers. */ +#define NUMREGBYTES 64 +/* + * Note that this register image is in a different order than + * the register image that Linux produces at interrupt time. + * + * Linux's register image is defined by struct pt_regs in ptrace.h. + * Just why GDB uses a different order is a historical mystery. + */ +enum regnames { _EAX, /* 0 */ + _ECX, /* 1 */ + _EDX, /* 2 */ + _EBX, /* 3 */ + _ESP, /* 4 */ + _EBP, /* 5 */ + _ESI, /* 6 */ + _EDI, /* 7 */ + _PC /* 8 also known as eip */ , + _PS /* 9 also known as eflags */ , + _CS, /* 10 */ + _SS, /* 11 */ + _DS, /* 12 */ + _ES, /* 13 */ + _FS, /* 14 */ + _GS /* 15 */ +}; + +/*************************** ASSEMBLY CODE MACROS *************************/ +/* + * Put the error code here just in case the user cares. + * Likewise, the vector number here (since GDB only gets the signal + * number through the usual means, and that's not very specific). + * The called_from is the return address so he can tell how we entered kgdb. + * This will allow him to seperate out the various possible entries. + */ +#define REMOTE_DEBUG 0 /* set != to turn on printing (also available in info) */ + +#define PID_MAX PID_MAX_DEFAULT + +#ifdef CONFIG_SMP +void smp_send_nmi_allbutself(void); +#define IF_SMP(x) x +#undef MAX_NO_CPUS +#ifndef CONFIG_NO_KGDB_CPUS +#define CONFIG_NO_KGDB_CPUS 2 +#endif +#if CONFIG_NO_KGDB_CPUS > NR_CPUS +#define MAX_NO_CPUS NR_CPUS +#else +#define MAX_NO_CPUS CONFIG_NO_KGDB_CPUS +#endif +#define hold_init hold_on_sstep: 1, +#define MAX_CPU_MASK (unsigned long)((1LL << MAX_NO_CPUS) - 1LL) +#define NUM_CPUS num_online_cpus() +#else +#define IF_SMP(x) +#define hold_init +#undef MAX_NO_CPUS +#define MAX_NO_CPUS 1 +#define NUM_CPUS 1 +#endif +#define NOCPU (struct task_struct *)0xbad1fbad +/* *INDENT-OFF* */ +struct kgdb_info { + int used_malloc; + void *called_from; + long long entry_tsc; + int errcode; + int vector; + int print_debug_info; +#ifdef CONFIG_SMP + int hold_on_sstep; + struct { + volatile struct task_struct *task; + int pid; + int hold; + struct pt_regs *regs; + } cpus_waiting[MAX_NO_CPUS]; +#endif +} kgdb_info = {hold_init print_debug_info:REMOTE_DEBUG, vector:-1}; + +/* *INDENT-ON* */ + +#define used_m kgdb_info.used_malloc +/* + * This is little area we set aside to contain the stack we + * need to build to allow gdb to call functions. We use one + * per cpu to avoid locking issues. We will do all this work + * with interrupts off so that should take care of the protection + * issues. + */ +#define LOOKASIDE_SIZE 200 /* should be more than enough */ +#define MALLOC_MAX 200 /* Max malloc size */ +struct { + unsigned int esp; + int array[LOOKASIDE_SIZE]; +} fn_call_lookaside[MAX_NO_CPUS]; + +static int trap_cpu; +static unsigned int OLD_esp; + +#define END_OF_LOOKASIDE &fn_call_lookaside[trap_cpu].array[LOOKASIDE_SIZE] +#define IF_BIT 0x200 +#define TF_BIT 0x100 + +#define MALLOC_ROUND 8-1 + +static char malloc_array[MALLOC_MAX]; +IF_SMP(static void to_gdb(const char *mess)); +void * +malloc(int size) +{ + + if (size <= (MALLOC_MAX - used_m)) { + int old_used = used_m; + used_m += ((size + MALLOC_ROUND) & (~MALLOC_ROUND)); + return &malloc_array[old_used]; + } else { + return NULL; + } +} + +/* + * I/O dispatch functions... + * Based upon kgdboe, either call the ethernet + * handler or the serial one.. + */ +void +putDebugChar(int c) +{ + if (!kgdboe) { + tty_putDebugChar(c); + } else { + eth_putDebugChar(c); + } +} + +int +getDebugChar(void) +{ + if (!kgdboe) { + return tty_getDebugChar(); + } else { + return eth_getDebugChar(); + } +} + +void +flushDebugChar(void) +{ + if (!kgdboe) { + tty_flushDebugChar(); + } else { + eth_flushDebugChar(); + } +} + +/* + * Gdb calls functions by pushing agruments, including a return address + * on the stack and the adjusting EIP to point to the function. The + * whole assumption in GDB is that we are on a different stack than the + * one the "user" i.e. code that hit the break point, is on. This, of + * course is not true in the kernel. Thus various dodges are needed to + * do the call without directly messing with EIP (which we can not change + * as it is just a location and not a register. To adjust it would then + * require that we move every thing below EIP up or down as needed. This + * will not work as we may well have stack relative pointer on the stack + * (such as the pointer to regs, for example). + + * So here is what we do: + * We detect gdb attempting to store into the stack area and instead, store + * into the fn_call_lookaside.array at the same relative location as if it + * were the area ESP pointed at. We also trap ESP modifications + * and uses these to adjust fn_call_lookaside.esp. On entry + * fn_call_lookaside.esp will be set to point at the last entry in + * fn_call_lookaside.array. This allows us to check if it has changed, and + * if so, on exit, we add the registers we will use to do the move and a + * trap/ interrupt return exit sequence. We then adjust the eflags in the + * regs array (remember we now have a copy in the fn_call_lookaside.array) to + * kill the interrupt bit, AND we change EIP to point at our set up stub. + * As part of the register set up we preset the registers to point at the + * begining and end of the fn_call_lookaside.array, so all the stub needs to + * do is move words from the array to the stack until ESP= the desired value + * then do the rti. This will then transfer to the desired function with + * all the correct registers. Nifty huh? + */ +extern asmlinkage void fn_call_stub(void); +extern asmlinkage void fn_rtn_stub(void); +/* *INDENT-OFF* */ +__asm__("fn_rtn_stub:\n\t" + "movl %eax,%esp\n\t" + "fn_call_stub:\n\t" + "1:\n\t" + "addl $-4,%ebx\n\t" + "movl (%ebx), %eax\n\t" + "pushl %eax\n\t" + "cmpl %esp,%ecx\n\t" + "jne 1b\n\t" + "popl %eax\n\t" + "popl %ebx\n\t" + "popl %ecx\n\t" + "iret \n\t"); +/* *INDENT-ON* */ +#define gdb_i386vector kgdb_info.vector +#define gdb_i386errcode kgdb_info.errcode +#define waiting_cpus kgdb_info.cpus_waiting +#define remote_debug kgdb_info.print_debug_info +#define hold_cpu(cpu) kgdb_info.cpus_waiting[cpu].hold +/* gdb locks */ + +#ifdef CONFIG_SMP +static int in_kgdb_called; +static spinlock_t waitlocks[MAX_NO_CPUS] = + {[0 ... MAX_NO_CPUS - 1] = SPIN_LOCK_UNLOCKED }; +/* + * The following array has the thread pointer of each of the "other" + * cpus. We make it global so it can be seen by gdb. + */ +volatile int in_kgdb_entry_log[MAX_NO_CPUS]; +volatile struct pt_regs *in_kgdb_here_log[MAX_NO_CPUS]; +/* +static spinlock_t continuelocks[MAX_NO_CPUS]; +*/ +spinlock_t kgdb_spinlock = SPIN_LOCK_UNLOCKED; +/* waiters on our spinlock plus us */ +static atomic_t spinlock_waiters = ATOMIC_INIT(1); +static int spinlock_count = 0; +static int spinlock_cpu = 0; +/* + * Note we use nested spin locks to account for the case where a break + * point is encountered when calling a function by user direction from + * kgdb. Also there is the memory exception recursion to account for. + * Well, yes, but this lets other cpus thru too. Lets add a + * cpu id to the lock. + */ +#define KGDB_SPIN_LOCK(x) if( spinlock_count == 0 || \ + spinlock_cpu != smp_processor_id()){\ + atomic_inc(&spinlock_waiters); \ + while (! spin_trylock(x)) {\ + in_kgdb(®s);\ + }\ + atomic_dec(&spinlock_waiters); \ + spinlock_count = 1; \ + spinlock_cpu = smp_processor_id(); \ + }else{ \ + spinlock_count++; \ + } +#define KGDB_SPIN_UNLOCK(x) if( --spinlock_count == 0) spin_unlock(x) +#else +unsigned kgdb_spinlock = 0; +#define KGDB_SPIN_LOCK(x) --*x +#define KGDB_SPIN_UNLOCK(x) ++*x +#endif + +int +hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* scan for the sequence $# */ +void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + + do { + /* wait around for the start character, ignore all other characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum += hex(getDebugChar() & 0x7f); + if ((remote_debug) && (checksum != xmitcsum)) { + printk + ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", + checksum, xmitcsum, buffer); + } + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); + + if (remote_debug) + printk("R:%s\n", buffer); + flushDebugChar(); +} + +/* send the packet in buffer. */ + +void +putpacket(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $#. */ + + if (!kgdboe) { + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + + } while ((getDebugChar() & 0x7f) != '+'); + } else { + /* + * For udp, we can not transfer too much bytes once. + * We only transfer MAX_SEND_COUNT size bytes each time + */ + +#define MAX_SEND_COUNT 30 + + int send_count = 0, i = 0; + char send_buf[MAX_SEND_COUNT]; + + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + send_count = 0; + while ((ch = buffer[count])) { + if (send_count >= MAX_SEND_COUNT) { + for(i = 0; i < MAX_SEND_COUNT; i++) { + putDebugChar(send_buf[i]); + } + flushDebugChar(); + send_count = 0; + } else { + send_buf[send_count] = ch; + checksum += ch; + count ++; + send_count++; + } + } + for(i = 0; i < send_count; i++) + putDebugChar(send_buf[i]); + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + } while ((getDebugChar() & 0x7f) != '+'); + } +} + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static short error; + +void +debug_error(char *format, char *parm) +{ + if (remote_debug) + printk(format, parm); +} + +static void +print_regs(struct pt_regs *regs) +{ + printk("EAX=%08lx ", regs->eax); + printk("EBX=%08lx ", regs->ebx); + printk("ECX=%08lx ", regs->ecx); + printk("EDX=%08lx ", regs->edx); + printk("\n"); + printk("ESI=%08lx ", regs->esi); + printk("EDI=%08lx ", regs->edi); + printk("EBP=%08lx ", regs->ebp); + printk("ESP=%08lx ", (long) ®s->esp); + printk("\n"); + printk(" DS=%08x ", regs->xds); + printk(" ES=%08x ", regs->xes); + printk(" SS=%08x ", __KERNEL_DS); + printk(" FL=%08lx ", regs->eflags); + printk("\n"); + printk(" CS=%08x ", regs->xcs); + printk(" IP=%08lx ", regs->eip); +#if 0 + printk(" FS=%08x ", regs->fs); + printk(" GS=%08x ", regs->gs); +#endif + printk("\n"); + +} /* print_regs */ + +#define NEW_esp fn_call_lookaside[trap_cpu].esp + +static void +regs_to_gdb_regs(int *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[_EAX] = regs->eax; + gdb_regs[_EBX] = regs->ebx; + gdb_regs[_ECX] = regs->ecx; + gdb_regs[_EDX] = regs->edx; + gdb_regs[_ESI] = regs->esi; + gdb_regs[_EDI] = regs->edi; + gdb_regs[_EBP] = regs->ebp; + gdb_regs[_DS] = regs->xds; + gdb_regs[_ES] = regs->xes; + gdb_regs[_PS] = regs->eflags; + gdb_regs[_CS] = regs->xcs; + gdb_regs[_PC] = regs->eip; + /* Note, as we are a debugging the kernel, we will always + * trap in kernel code, this means no priviledge change, + * and so the pt_regs structure is not completely valid. In a non + * privilege change trap, only EFLAGS, CS and EIP are put on the stack, + * SS and ESP are not stacked, this means that the last 2 elements of + * pt_regs is not valid (they would normally refer to the user stack) + * also, using regs+1 is no good because you end up will a value that is + * 2 longs (8) too high. This used to cause stepping over functions + * to fail, so my fix is to use the address of regs->esp, which + * should point at the end of the stack frame. Note I have ignored + * completely exceptions that cause an error code to be stacked, such + * as double fault. Stuart Hughes, Zentropix. + * original code: gdb_regs[_ESP] = (int) (regs + 1) ; + + * this is now done on entry and moved to OLD_esp (as well as NEW_esp). + */ + gdb_regs[_ESP] = NEW_esp; + gdb_regs[_SS] = __KERNEL_DS; + gdb_regs[_FS] = 0xFFFF; + gdb_regs[_GS] = 0xFFFF; +} /* regs_to_gdb_regs */ + +static void +gdb_regs_to_regs(int *gdb_regs, struct pt_regs *regs) +{ + regs->eax = gdb_regs[_EAX]; + regs->ebx = gdb_regs[_EBX]; + regs->ecx = gdb_regs[_ECX]; + regs->edx = gdb_regs[_EDX]; + regs->esi = gdb_regs[_ESI]; + regs->edi = gdb_regs[_EDI]; + regs->ebp = gdb_regs[_EBP]; + regs->xds = gdb_regs[_DS]; + regs->xes = gdb_regs[_ES]; + regs->eflags = gdb_regs[_PS]; + regs->xcs = gdb_regs[_CS]; + regs->eip = gdb_regs[_PC]; + NEW_esp = gdb_regs[_ESP]; /* keep the value */ +#if 0 /* can't change these */ + regs->esp = gdb_regs[_ESP]; + regs->xss = gdb_regs[_SS]; + regs->fs = gdb_regs[_FS]; + regs->gs = gdb_regs[_GS]; +#endif + +} /* gdb_regs_to_regs */ + +int thread_list = 0; + +void +get_gdb_regs(struct task_struct *p, struct pt_regs *regs, int *gdb_regs) +{ + unsigned long stack_page; + int count = 0; + IF_SMP(int i); + if (!p || p == current) { + regs_to_gdb_regs(gdb_regs, regs); + return; + } +#ifdef CONFIG_SMP + for (i = 0; i < MAX_NO_CPUS; i++) { + if (p == kgdb_info.cpus_waiting[i].task) { + regs_to_gdb_regs(gdb_regs, + kgdb_info.cpus_waiting[i].regs); + gdb_regs[_ESP] = + (int) &kgdb_info.cpus_waiting[i].regs->esp; + + return; + } + } +#endif + memset(gdb_regs, 0, NUMREGBYTES); + gdb_regs[_ESP] = p->thread.esp; + gdb_regs[_PC] = p->thread.eip; + gdb_regs[_EBP] = *(int *) gdb_regs[_ESP]; + gdb_regs[_EDI] = *(int *) (gdb_regs[_ESP] + 4); + gdb_regs[_ESI] = *(int *) (gdb_regs[_ESP] + 8); + +/* + * This code is to give a more informative notion of where a process + * is waiting. It is used only when the user asks for a thread info + * list. If he then switches to the thread, s/he will find the task + * is in schedule, but a back trace should show the same info we come + * up with. This code was shamelessly purloined from process.c. It was + * then enhanced to provide more registers than simply the program + * counter. + */ + + if (!thread_list) { + return; + } + + if (p->state == TASK_RUNNING) + return; + stack_page = (unsigned long) p->thread_info; + if (gdb_regs[_ESP] < stack_page || gdb_regs[_ESP] > + THREAD_SIZE - sizeof(long) + stack_page) + return; + /* include/asm-i386/system.h:switch_to() pushes ebp last. */ + do { + if (gdb_regs[_EBP] < stack_page || + gdb_regs[_EBP] > THREAD_SIZE - 2*sizeof(long) + stack_page) + return; + gdb_regs[_PC] = *(unsigned long *) (gdb_regs[_EBP] + 4); + gdb_regs[_ESP] = gdb_regs[_EBP] + 8; + gdb_regs[_EBP] = *(unsigned long *) gdb_regs[_EBP]; + if (!in_sched_functions(gdb_regs[_PC])) + return; + } while (count++ < 16); + return; +} + +/* Indicate to caller of mem2hex or hex2mem that there has been an + error. */ +static volatile int mem_err = 0; +static volatile int mem_err_expected = 0; +static volatile int mem_err_cnt = 0; +static int garbage_loc = -1; + +int +get_char(char *addr) +{ + return *addr; +} + +void +set_char(char *addr, int val, int may_fault) +{ + /* + * This code traps references to the area mapped to the kernel + * stack as given by the regs and, instead, stores to the + * fn_call_lookaside[cpu].array + */ + if (may_fault && + (unsigned int) addr < OLD_esp && + ((unsigned int) addr > (OLD_esp - (unsigned int) LOOKASIDE_SIZE))) { + addr = (char *) END_OF_LOOKASIDE - ((char *) OLD_esp - addr); + } + *addr = val; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +/* If MAY_FAULT is non-zero, then we should set mem_err in response to + a fault; if zero treat a fault like any other fault in the stub. */ +char * +mem2hex(char *mem, char *buf, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + /* printk("%lx = ", mem) ; */ + + ch = get_char(mem++); + + /* printk("%02x\n", ch & 0xFF) ; */ + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault fetching from addr %lx\n", + (long) (mem - 1)); + *buf = 0; /* truncate buffer */ + return (buf); + } + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (may_fault) + mem_err_expected = 0; + return (buf); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ +/* NOTE: We use the may fault flag to also indicate if the write is to + * the registers (0) or "other" memory (!=0) + */ +char * +hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + set_char(mem++, ch, may_fault); + + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault storing to addr %lx\n", + (long) (mem - 1)); + return (mem); + } + } + if (may_fault) + mem_err_expected = 0; + return (mem); +} + +/**********************************************/ +/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ +/* RETURN NUMBER OF CHARS PROCESSED */ +/**********************************************/ +int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +#define stubhex(h) hex(h) +#ifdef old_thread_list + +static int +stub_unpack_int(char *buff, int fieldlength) +{ + int nibble; + int retval = 0; + + while (fieldlength) { + nibble = stubhex(*buff++); + retval |= nibble; + fieldlength--; + if (fieldlength) + retval = retval << 4; + } + return retval; +} +#endif +static char * +pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +#define BUF_THREAD_ID_SIZE 16 + +static char * +pack_threadid(char *pkt, threadref * id) +{ + char *limit; + unsigned char *altid; + + altid = (unsigned char *) id; + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *altid++); + return pkt; +} + +#ifdef old_thread_list +static char * +unpack_byte(char *buf, int *value) +{ + *value = stub_unpack_int(buf, 2); + return buf + 2; +} + +static char * +unpack_threadid(char *inbuf, threadref * id) +{ + char *altref; + char *limit = inbuf + BUF_THREAD_ID_SIZE; + int x, y; + + altref = (char *) id; + + while (inbuf < limit) { + x = stubhex(*inbuf++); + y = stubhex(*inbuf++); + *altref++ = (x << 4) | y; + } + return inbuf; +} +#endif +void +int_to_threadref(threadref * id, int value) +{ + unsigned char *scan; + + scan = (unsigned char *) id; + { + int i = 4; + while (i--) + *scan++ = 0; + } + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} +int +int_to_hex_v(unsigned char * id, int value) +{ + unsigned char *start = id; + int shift; + int ch; + + for (shift = 28; shift >= 0; shift -= 4) { + if ((ch = (value >> shift) & 0xf) || (id != start)) { + *id = hexchars[ch]; + id++; + } + } + if (id == start) + *id++ = '0'; + return id - start; +} +#ifdef old_thread_list + +static int +threadref_to_int(threadref * ref) +{ + int i, value = 0; + unsigned char *scan; + + scan = (char *) ref; + scan += 4; + i = 4; + while (i-- > 0) + value = (value << 8) | ((*scan++) & 0xff); + return value; +} +#endif +static int +cmp_str(char *s1, char *s2, int count) +{ + while (count--) { + if (*s1++ != *s2++) + return 0; + } + return 1; +} + +#if 1 /* this is a hold over from 2.4 where O(1) was "sometimes" */ +extern struct task_struct *kgdb_get_idle(int cpu); +#define idle_task(cpu) kgdb_get_idle(cpu) +#else +#define idle_task(cpu) init_tasks[cpu] +#endif + +extern int kgdb_pid_init_done; + +struct task_struct * +getthread(int pid) +{ + struct task_struct *thread; + if (pid >= PID_MAX && pid <= (PID_MAX + MAX_NO_CPUS)) { + + return idle_task(pid - PID_MAX); + } else { + /* + * find_task_by_pid is relatively safe all the time + * Other pid functions require lock downs which imply + * that we may be interrupting them (as we get here + * in the middle of most any lock down). + * Still we don't want to call until the table exists! + */ + if (kgdb_pid_init_done){ + thread = find_task_by_pid(pid); + if (thread) { + return thread; + } + } + } + return NULL; +} +/* *INDENT-OFF* */ +struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned addr; +} breakinfo[4] = { {enabled:0}, + {enabled:0}, + {enabled:0}, + {enabled:0}}; +/* *INDENT-ON* */ +unsigned hw_breakpoint_status; +void +correct_hw_break(void) +{ + int breakno; + int correctit; + int breakbit; + unsigned dr7; + + asm volatile ("movl %%db7, %0\n":"=r" (dr7) + :); + /* *INDENT-OFF* */ + do { + unsigned addr0, addr1, addr2, addr3; + asm volatile ("movl %%db0, %0\n" + "movl %%db1, %1\n" + "movl %%db2, %2\n" + "movl %%db3, %3\n" + :"=r" (addr0), "=r"(addr1), + "=r"(addr2), "=r"(addr3) + :); + } while (0); + /* *INDENT-ON* */ + correctit = 0; + for (breakno = 0; breakno < 3; breakno++) { + breakbit = 2 << (breakno << 1); + if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { + correctit = 1; + dr7 |= breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + dr7 |= (((breakinfo[breakno].len << 2) | + breakinfo[breakno].type) << 16) << + (breakno << 2); + switch (breakno) { + case 0: + asm volatile ("movl %0, %%dr0\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 1: + asm volatile ("movl %0, %%dr1\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 2: + asm volatile ("movl %0, %%dr2\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 3: + asm volatile ("movl %0, %%dr3\n"::"r" + (breakinfo[breakno].addr)); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) { + asm volatile ("movl %0, %%db7\n"::"r" (dr7)); + } +} + +int +remove_hw_break(unsigned breakno) +{ + if (!breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 0; + return 0; +} + +int +set_hw_break(unsigned breakno, unsigned type, unsigned len, unsigned addr) +{ + if (breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].len = len; + breakinfo[breakno].addr = addr; + return 0; +} + +#ifdef CONFIG_SMP +static int in_kgdb_console = 0; + +int +in_kgdb(struct pt_regs *regs) +{ + unsigned flags; + int cpu = smp_processor_id(); + in_kgdb_called = 1; + if (!spin_is_locked(&kgdb_spinlock)) { + if (in_kgdb_here_log[cpu] || /* we are holding this cpu */ + in_kgdb_console) { /* or we are doing slow i/o */ + return 1; + } + return 0; + } + + /* As I see it the only reason not to let all cpus spin on + * the same spin_lock is to allow selected ones to proceed. + * This would be a good thing, so we leave it this way. + * Maybe someday.... Done ! + + * in_kgdb() is called from an NMI so we don't pretend + * to have any resources, like printk() for example. + */ + + kgdb_local_irq_save(flags); /* only local here, to avoid hanging */ + /* + * log arival of this cpu + * The NMI keeps on ticking. Protect against recurring more + * than once, and ignor the cpu that has the kgdb lock + */ + in_kgdb_entry_log[cpu]++; + in_kgdb_here_log[cpu] = regs; + if (cpu == spinlock_cpu || waiting_cpus[cpu].task) + goto exit_in_kgdb; + + /* + * For protection of the initilization of the spin locks by kgdb + * it locks the kgdb spinlock before it gets the wait locks set + * up. We wait here for the wait lock to be taken. If the + * kgdb lock goes away first?? Well, it could be a slow exit + * sequence where the wait lock is removed prior to the kgdb lock + * so if kgdb gets unlocked, we just exit. + */ + + while (spin_is_locked(&kgdb_spinlock) && + !spin_is_locked(waitlocks + cpu)) ; + if (!spin_is_locked(&kgdb_spinlock)) + goto exit_in_kgdb; + + waiting_cpus[cpu].task = current; + waiting_cpus[cpu].pid = (current->pid) ? : (PID_MAX + cpu); + waiting_cpus[cpu].regs = regs; + + spin_unlock_wait(waitlocks + cpu); + + /* + * log departure of this cpu + */ + waiting_cpus[cpu].task = 0; + waiting_cpus[cpu].pid = 0; + waiting_cpus[cpu].regs = 0; + correct_hw_break(); + exit_in_kgdb: + in_kgdb_here_log[cpu] = 0; + kgdb_local_irq_restore(flags); + return 1; + /* + spin_unlock(continuelocks + smp_processor_id()); + */ +} + +void +smp__in_kgdb(struct pt_regs regs) +{ + ack_APIC_irq(); + in_kgdb(®s); +} +#else +int +in_kgdb(struct pt_regs *regs) +{ + return (kgdb_spinlock); +} +#endif + +void +printexceptioninfo(int exceptionNo, int errorcode, char *buffer) +{ + unsigned dr6; + int i; + switch (exceptionNo) { + case 1: /* debug exception */ + break; + case 3: /* breakpoint */ + sprintf(buffer, "Software breakpoint"); + return; + default: + sprintf(buffer, "Details not available"); + return; + } + asm volatile ("movl %%db6, %0\n":"=r" (dr6) + :); + if (dr6 & 0x4000) { + sprintf(buffer, "Single step"); + return; + } + for (i = 0; i < 4; ++i) { + if (dr6 & (1 << i)) { + sprintf(buffer, "Hardware breakpoint %d", i); + return; + } + } + sprintf(buffer, "Unknown trap"); + return; +} + +/* + * This function does all command procesing for interfacing to gdb. + * + * NOTE: The INT nn instruction leaves the state of the interrupt + * enable flag UNCHANGED. That means that when this routine + * is entered via a breakpoint (INT 3) instruction from code + * that has interrupts enabled, then interrupts will STILL BE + * enabled when this routine is entered. The first thing that + * we do here is disable interrupts so as to prevent recursive + * entries and bothersome serial interrupts while we are + * trying to run the serial port in polled mode. + * + * For kernel version 2.1.xx the kgdb_cli() actually gets a spin lock so + * it is always necessary to do a restore_flags before returning + * so as to let go of that lock. + */ +int +kgdb_handle_exception(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs) +{ + struct task_struct *usethread = NULL; + struct task_struct *thread_list_start = 0, *thread = NULL; + int addr, length; + int breakno, breaktype; + char *ptr; + int newPC; + threadref thref; + int threadid; + int thread_min = PID_MAX + MAX_NO_CPUS; +#ifdef old_thread_list + int maxthreads; +#endif + int nothreads; + unsigned long flags; + int gdb_regs[NUMREGBYTES / 4]; + int dr6; + IF_SMP(int entry_state = 0); /* 0, ok, 1, no nmi, 2 sync failed */ +#define NO_NMI 1 +#define NO_SYNC 2 +#define regs (*linux_regs) +#define NUMREGS NUMREGBYTES/4 + /* + * If the entry is not from the kernel then return to the Linux + * trap handler and let it process the interrupt normally. + */ + if ((linux_regs->eflags & VM_MASK) || (3 & linux_regs->xcs)) { + printk("ignoring non-kernel exception\n"); + print_regs(®s); + return (0); + } + /* + * If we're using eth mode, set the 'mode' in the netdevice. + */ + + if (kgdboe) + netpoll_set_trap(1); + + kgdb_local_irq_save(flags); + + /* Get kgdb spinlock */ + + KGDB_SPIN_LOCK(&kgdb_spinlock); + rdtscll(kgdb_info.entry_tsc); + /* + * We depend on this spinlock and the NMI watch dog to control the + * other cpus. They will arrive at "in_kgdb()" as a result of the + * NMI and will wait there for the following spin locks to be + * released. + */ +#ifdef CONFIG_SMP + +#if 0 + if (cpu_callout_map & ~MAX_CPU_MASK) { + printk("kgdb : too many cpus, possibly not mapped" + " in contiguous space, change MAX_NO_CPUS" + " in kgdb_stub and make new kernel.\n" + " cpu_callout_map is %lx\n", cpu_callout_map); + goto exit_just_unlock; + } +#endif + if (spinlock_count == 1) { + int time = 0, end_time, dum = 0; + int i; + int cpu_logged_in[MAX_NO_CPUS] = {[0 ... MAX_NO_CPUS - 1] = (0) + }; + if (remote_debug) { + printk("kgdb : cpu %d entry, syncing others\n", + smp_processor_id()); + } + for (i = 0; i < MAX_NO_CPUS; i++) { + /* + * Use trylock as we may already hold the lock if + * we are holding the cpu. Net result is all + * locked. + */ + spin_trylock(&waitlocks[i]); + } + for (i = 0; i < MAX_NO_CPUS; i++) + cpu_logged_in[i] = 0; + /* + * Wait for their arrival. We know the watch dog is active if + * in_kgdb() has ever been called, as it is always called on a + * watchdog tick. + */ + rdtsc(dum, time); + end_time = time + 2; /* Note: we use the High order bits! */ + i = 1; + if (num_online_cpus() > 1) { + int me_in_kgdb = in_kgdb_entry_log[smp_processor_id()]; + smp_send_nmi_allbutself(); + + while (i < num_online_cpus() && time != end_time) { + int j; + for (j = 0; j < MAX_NO_CPUS; j++) { + if (waiting_cpus[j].task && + waiting_cpus[j].task != NOCPU && + !cpu_logged_in[j]) { + i++; + cpu_logged_in[j] = 1; + if (remote_debug) { + printk + ("kgdb : cpu %d arrived at kgdb\n", + j); + } + break; + } else if (!waiting_cpus[j].task && + !cpu_online(j)) { + waiting_cpus[j].task = NOCPU; + cpu_logged_in[j] = 1; + waiting_cpus[j].hold = 1; + break; + } + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + + int wait = 100000; + while (wait--) ; + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + printk + ("kgdb : cpu %d stall" + " in in_kgdb\n", + j); + i++; + cpu_logged_in[j] = 1; + waiting_cpus[j].task = + (struct task_struct + *) 1; + } + } + } + + if (in_kgdb_entry_log[smp_processor_id()] > + (me_in_kgdb + 10)) { + break; + } + + rdtsc(dum, time); + } + if (i < num_online_cpus()) { + printk + ("kgdb : time out, proceeding without sync\n"); +#if 0 + printk("kgdb : Waiting_cpus: 0 = %d, 1 = %d\n", + waiting_cpus[0].task != 0, + waiting_cpus[1].task != 0); + printk("kgdb : Cpu_logged in: 0 = %d, 1 = %d\n", + cpu_logged_in[0], cpu_logged_in[1]); + printk + ("kgdb : in_kgdb_here_log in: 0 = %d, 1 = %d\n", + in_kgdb_here_log[0] != 0, + in_kgdb_here_log[1] != 0); +#endif + entry_state = NO_SYNC; + } else { +#if 0 + int ent = + in_kgdb_entry_log[smp_processor_id()] - + me_in_kgdb; + printk("kgdb : sync after %d entries\n", ent); +#endif + } + } else { + if (remote_debug) { + printk + ("kgdb : %d cpus, but watchdog not active\n" + "proceeding without locking down other cpus\n", + num_online_cpus()); + entry_state = NO_NMI; + } + } + } +#endif + + if (remote_debug) { + unsigned long *lp = (unsigned long *) &linux_regs; + + printk("handle_exception(exceptionVector=%d, " + "signo=%d, err_code=%d, linux_regs=%p)\n", + exceptionVector, signo, err_code, linux_regs); + if (debug_regs) { + print_regs(®s); + printk("Stk: %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[0], lp[1], lp[2], lp[3], + lp[4], lp[5], lp[6], lp[7]); + printk(" %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[8], lp[9], lp[10], lp[11], + lp[12], lp[13], lp[14], lp[15]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[16], lp[17], lp[18], lp[19], + lp[20], lp[21], lp[22], lp[23]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[24], lp[25], lp[26], lp[27], + lp[28], lp[29], lp[30], lp[31]); + } + } + + /* Disable hardware debugging while we are in kgdb */ + /* Get the debug register status register */ +/* *INDENT-OFF* */ + __asm__("movl %0,%%db7" + : /* no output */ + :"r"(0)); + + asm volatile ("movl %%db6, %0\n" + :"=r" (hw_breakpoint_status) + :); + +/* *INDENT-ON* */ + switch (exceptionVector) { + case 0: /* divide error */ + case 1: /* debug exception */ + case 2: /* NMI */ + case 3: /* breakpoint */ + case 4: /* overflow */ + case 5: /* bounds check */ + case 6: /* invalid opcode */ + case 7: /* device not available */ + case 8: /* double fault (errcode) */ + case 10: /* invalid TSS (errcode) */ + case 12: /* stack fault (errcode) */ + case 16: /* floating point error */ + case 17: /* alignment check (errcode) */ + default: /* any undocumented */ + break; + case 11: /* segment not present (errcode) */ + case 13: /* general protection (errcode) */ + case 14: /* page fault (special errcode) */ + case 19: /* cache flush denied */ + if (mem_err_expected) { + /* + * This fault occured because of the + * get_char or set_char routines. These + * two routines use either eax of edx to + * indirectly reference the location in + * memory that they are working with. + * For a page fault, when we return the + * instruction will be retried, so we + * have to make sure that these + * registers point to valid memory. + */ + mem_err = 1; /* set mem error flag */ + mem_err_expected = 0; + mem_err_cnt++; /* helps in debugging */ + /* make valid address */ + regs.eax = (long) &garbage_loc; + /* make valid address */ + regs.edx = (long) &garbage_loc; + if (remote_debug) + printk("Return after memory error: " + "mem_err_cnt=%d\n", mem_err_cnt); + if (debug_regs) + print_regs(®s); + goto exit_kgdb; + } + break; + } + if (remote_debug) + printk("kgdb : entered kgdb on cpu %d\n", smp_processor_id()); + + gdb_i386vector = exceptionVector; + gdb_i386errcode = err_code; + kgdb_info.called_from = __builtin_return_address(0); +#ifdef CONFIG_SMP + /* + * OK, we can now communicate, lets tell gdb about the sync. + * but only if we had a problem. + */ + switch (entry_state) { + case NO_NMI: + to_gdb("NMI not active, other cpus not stopped\n"); + break; + case NO_SYNC: + to_gdb("Some cpus not stopped, see 'kgdb_info' for details\n"); + default:; + } + +#endif +/* + * Set up the gdb function call area. + */ + trap_cpu = smp_processor_id(); + OLD_esp = NEW_esp = (int) (&linux_regs->esp); + + IF_SMP(once_again:) + /* reply to host that an exception has occurred */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + + putpacket(remcomOutBuffer); + + while (1 == 1) { + error = 0; + remcomOutBuffer[0] = 0; + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + printk("Remote debug %s\n", + remote_debug ? "on" : "off"); + break; + case 'g': /* return the value of the CPU registers */ + get_gdb_regs(usethread, ®s, gdb_regs); + mem2hex((char *) gdb_regs, + remcomOutBuffer, NUMREGBYTES, 0); + break; + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem(&remcomInBuffer[1], + (char *) gdb_regs, NUMREGBYTES, 0); + if (!usethread || usethread == current) { + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E00"); + } + break; + + case 'P':{ /* set the value of a single CPU register - + return OK */ + /* + * For some reason, gdb wants to talk about psudo + * registers (greater than 15). These may have + * meaning for ptrace, but for us it is safe to + * ignor them. We do this by dumping them into + * _GS which we also ignor, but do have memory for. + */ + int regno; + + ptr = &remcomInBuffer[1]; + regs_to_gdb_regs(gdb_regs, ®s); + if ((!usethread || usethread == current) && + hexToInt(&ptr, ®no) && + *ptr++ == '=' && (regno >= 0)) { + regno = + (regno >= NUMREGS ? _GS : regno); + hex2mem(ptr, (char *) &gdb_regs[regno], + 4, 0); + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + break; + } + strcpy(remcomOutBuffer, "E01"); + break; + } + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr) && + (*(ptr++) == ',') && (hexToInt(&ptr, &length))) { + ptr = 0; + /* + * hex doubles the byte count + */ + if (length > (BUFMAX / 2)) + length = BUFMAX / 2; + mem2hex((char *) addr, + remcomOutBuffer, length, 1); + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } + } + + if (ptr) { + strcpy(remcomOutBuffer, "E01"); + debug_error + ("malformed read memory command: %s\n", + remcomInBuffer); + } + break; + + /* MAA..AA,LLLL: + Write LLLL bytes at address AA.AA return OK */ + case 'M': + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr) && + (*(ptr++) == ',') && + (hexToInt(&ptr, &length)) && (*(ptr++) == ':')) { + hex2mem(ptr, (char *) addr, length, 1); + + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } else { + strcpy(remcomOutBuffer, "OK"); + } + + ptr = 0; + } + if (ptr) { + strcpy(remcomOutBuffer, "E02"); + debug_error + ("malformed write memory command: %s\n", + remcomInBuffer); + } + break; + case 'S': + remcomInBuffer[0] = 's'; + case 'C': + /* Csig;AA..AA where ;AA..AA is optional + * continue with signal + * Since signals are meaning less to us, delete that + * part and then fall into the 'c' code. + */ + ptr = &remcomInBuffer[1]; + length = 2; + while (*ptr && *ptr != ';') { + length++; + ptr++; + } + if (*ptr) { + do { + ptr++; + *(ptr - length++) = *ptr; + } while (*ptr); + } else { + remcomInBuffer[1] = 0; + } + + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + /* D detach, reply OK and then continue */ + case 'c': + case 's': + case 'D': + + /* try to read optional parameter, + pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) { + if (remote_debug) + printk("Changing EIP to 0x%x\n", addr); + + regs.eip = addr; + } + + newPC = regs.eip; + + /* clear the trace bit */ + regs.eflags &= 0xfffffeff; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') + regs.eflags |= 0x100; + + /* detach is a friendly version of continue. Note that + debugging is still enabled (e.g hit control C) + */ + if (remcomInBuffer[0] == 'D') { + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + } + + if (remote_debug) { + printk("Resuming execution\n"); + print_regs(®s); + } + asm volatile ("movl %%db6, %0\n":"=r" (dr6) + :); + if (!(dr6 & 0x4000)) { + for (breakno = 0; breakno < 4; ++breakno) { + if (dr6 & (1 << breakno) && + (breakinfo[breakno].type == 0)) { + /* Set restore flag */ + regs.eflags |= 0x10000; + break; + } + } + } + + if (kgdboe) + netpoll_set_trap(0); + + correct_hw_break(); + asm volatile ("movl %0, %%db6\n"::"r" (0)); + goto exit_kgdb; + + /* kill the program */ + case 'k': /* do nothing */ + break; + + /* query */ + case 'q': + nothreads = 0; + switch (remcomInBuffer[1]) { + case 'f': + threadid = 1; + thread_list = 2; + thread_list_start = (usethread ? : current); + case 's': + if (!cmp_str(&remcomInBuffer[2], + "ThreadInfo", 10)) + break; + + remcomOutBuffer[nothreads++] = 'm'; + for (; threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + nothreads += int_to_hex_v( + &remcomOutBuffer[ + nothreads], + threadid); + if (thread_min > threadid) + thread_min = threadid; + remcomOutBuffer[ + nothreads] = ','; + nothreads++; + if (nothreads > BUFMAX - 10) + break; + } + } + if (remcomOutBuffer[nothreads - 1] == 'm') { + remcomOutBuffer[nothreads - 1] = 'l'; + } else { + nothreads--; + } + remcomOutBuffer[nothreads] = 0; + break; + +#ifdef old_thread_list /* Old thread info request */ + case 'L': + /* List threads */ + thread_list = 2; + thread_list_start = (usethread ? : current); + unpack_byte(remcomInBuffer + 3, &maxthreads); + unpack_threadid(remcomInBuffer + 5, &thref); + do { + int buf_thread_limit = + (BUFMAX - 22) / BUF_THREAD_ID_SIZE; + if (maxthreads > buf_thread_limit) { + maxthreads = buf_thread_limit; + } + } while (0); + remcomOutBuffer[0] = 'q'; + remcomOutBuffer[1] = 'M'; + remcomOutBuffer[4] = '0'; + pack_threadid(remcomOutBuffer + 5, &thref); + + threadid = threadref_to_int(&thref); + for (nothreads = 0; + nothreads < maxthreads && + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + int_to_threadref(&thref, + threadid); + pack_threadid(remcomOutBuffer + + 21 + + nothreads * 16, + &thref); + nothreads++; + if (thread_min > threadid) + thread_min = threadid; + } + } + + if (threadid == PID_MAX + MAX_NO_CPUS) { + remcomOutBuffer[4] = '1'; + } + pack_hex_byte(remcomOutBuffer + 2, nothreads); + remcomOutBuffer[21 + nothreads * 16] = '\0'; + break; +#endif + case 'C': + /* Current thread id */ + remcomOutBuffer[0] = 'Q'; + remcomOutBuffer[1] = 'C'; + threadid = current->pid; + if (!threadid) { + /* + * idle thread + */ + for (threadid = PID_MAX; + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + if (current == + idle_task(threadid - + PID_MAX)) + break; + } + } + int_to_threadref(&thref, threadid); + pack_threadid(remcomOutBuffer + 2, &thref); + remcomOutBuffer[18] = '\0'; + break; + + case 'E': + /* Print exception info */ + printexceptioninfo(exceptionVector, + err_code, remcomOutBuffer); + break; + case 'T':{ + char * nptr; + /* Thread extra info */ + if (!cmp_str(&remcomInBuffer[2], + "hreadExtraInfo,", 15)) { + break; + } + ptr = &remcomInBuffer[17]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + nptr = &thread->comm[0]; + length = 0; + ptr = &remcomOutBuffer[0]; + do { + length++; + ptr = pack_hex_byte(ptr, *nptr++); + } while (*nptr && length < 16); + /* + * would like that 16 to be the size of + * task_struct.comm but don't know the + * syntax.. + */ + *ptr = 0; + } + } + break; + + /* task related */ + case 'H': + switch (remcomInBuffer[1]) { + case 'g': + ptr = &remcomInBuffer[2]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (!thread) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + break; + } + /* + * Just in case I forget what this is all about, + * the "thread info" command to gdb causes it + * to ask for a thread list. It then switches + * to each thread and asks for the registers. + * For this (and only this) usage, we want to + * fudge the registers of tasks not on the run + * list (i.e. waiting) to show the routine that + * called schedule. Also, gdb, is a minimalist + * in that if the current thread is the last + * it will not re-read the info when done. + * This means that in this case we must show + * the real registers. So here is how we do it: + * Each entry we keep track of the min + * thread in the list (the last that gdb will) + * get info for. We also keep track of the + * starting thread. + * "thread_list" is cleared when switching back + * to the min thread if it is was current, or + * if it was not current, thread_list is set + * to 1. When the switch to current comes, + * if thread_list is 1, clear it, else do + * nothing. + */ + usethread = thread; + if ((thread_list == 1) && + (thread == thread_list_start)) { + thread_list = 0; + } + if (thread_list && (threadid == thread_min)) { + if (thread == thread_list_start) { + thread_list = 0; + } else { + thread_list = 1; + } + } + /* follow through */ + case 'c': + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + break; + } + break; + + /* Query thread status */ + case 'T': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (thread) { + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + if (thread_min > threadid) + thread_min = threadid; + } else { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + } + break; + + case 'Y': /* set up a hardware breakpoint */ + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + ptr++; + hexToInt(&ptr, &breaktype); + ptr++; + hexToInt(&ptr, &length); + ptr++; + hexToInt(&ptr, &addr); + if (set_hw_break(breakno & 0x3, + breaktype & 0x3, + length & 0x3, addr) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + /* Remove hardware breakpoint */ + case 'y': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + if (remove_hw_break(breakno & 0x3) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + case 'r': /* reboot */ + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + /*to_gdb("Rebooting\n"); */ + /* triplefault no return from here */ + { + static long no_idt[2]; + __asm__ __volatile__("lidt %0"::"m"(no_idt[0])); + BREAKPOINT; + } + + } /* switch */ + + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1==1) */ + /* + * reached by goto only. + */ + exit_kgdb: + /* + * Here is where we set up to trap a gdb function call. NEW_esp + * will be changed if we are trying to do this. We handle both + * adding and subtracting, thus allowing gdb to put grung on + * the stack which it removes later. + */ + if (NEW_esp != OLD_esp) { + int *ptr = END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) + ptr -= (OLD_esp - NEW_esp) / sizeof (int); + *--ptr = linux_regs->eflags; + *--ptr = linux_regs->xcs; + *--ptr = linux_regs->eip; + *--ptr = linux_regs->ecx; + *--ptr = linux_regs->ebx; + *--ptr = linux_regs->eax; + linux_regs->ecx = NEW_esp - (sizeof (int) * 6); + linux_regs->ebx = (unsigned int) END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) { + linux_regs->eip = (unsigned int) fn_call_stub; + } else { + linux_regs->eip = (unsigned int) fn_rtn_stub; + linux_regs->eax = NEW_esp; + } + linux_regs->eflags &= ~(IF_BIT | TF_BIT); + } +#ifdef CONFIG_SMP + /* + * Release gdb wait locks + * Sanity check time. Must have at least one cpu to run. Also single + * step must not be done if the current cpu is on hold. + */ + if (spinlock_count == 1) { + int ss_hold = (regs.eflags & 0x100) && kgdb_info.hold_on_sstep; + int cpu_avail = 0; + int i; + + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!cpu_online(i)) + break; + if (!hold_cpu(i)) { + cpu_avail = 1; + } + } + /* + * Early in the bring up there will be NO cpus on line... + */ + if (!cpu_avail && !cpus_empty(cpu_online_map)) { + to_gdb("No cpus unblocked, see 'kgdb_info.hold_cpu'\n"); + goto once_again; + } + if (hold_cpu(smp_processor_id()) && (regs.eflags & 0x100)) { + to_gdb + ("Current cpu must be unblocked to single step\n"); + goto once_again; + } + if (!(ss_hold)) { + int i; + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!hold_cpu(i)) { + spin_unlock(&waitlocks[i]); + } + } + } else { + spin_unlock(&waitlocks[smp_processor_id()]); + } + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + /* + * If this cpu is on hold, this is where we + * do it. Note, the NMI will pull us out of here, + * but will return as the above lock is not held. + * We will stay here till another cpu releases the lock for us. + */ + spin_unlock_wait(waitlocks + smp_processor_id()); + kgdb_local_irq_restore(flags); + return (0); + } +#if 0 +exit_just_unlock: +#endif +#endif + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + kgdb_local_irq_restore(flags); + return (0); +} + +/* this function is used to set up exception handlers for tracing and + * breakpoints. + * This function is not needed as the above line does all that is needed. + * We leave it for backward compatitability... + */ +void +set_debug_traps(void) +{ + /* + * linux_debug_hook is defined in traps.c. We store a pointer + * to our own exception handler into it. + + * But really folks, every hear of labeled common, an old Fortran + * concept. Lots of folks can reference it and it is define if + * anyone does. Only one can initialize it at link time. We do + * this with the hook. See the statement above. No need for any + * executable code and it is ready as soon as the kernel is + * loaded. Very desirable in kernel debugging. + + linux_debug_hook = handle_exception ; + */ + + /* In case GDB is started before us, ack any packets (presumably + "$?#xx") sitting there. + putDebugChar ('+'); + + initialized = 1; + */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ +/* But really, just use the BREAKPOINT macro. We will handle the int stuff + */ + +#ifdef later +/* + * possibly we should not go thru the traps.c code at all? Someday. + */ +void +do_kgdb_int3(struct pt_regs *regs, long error_code) +{ + kgdb_handle_exception(3, 5, error_code, regs); + return; +} +#endif +#undef regs +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS +fastcall void sys_call_exit(struct pt_regs *regs) +{ + if (preempt_count()) + printk("Sys call %d return with %x preempt_count\n", + (int) regs->orig_eax, preempt_count()); +} +#endif +#ifdef CONFIG_STACK_OVERFLOW_TEST +#include +fastcall void stack_overflow(void) +{ +#ifdef BREAKPOINT + BREAKPOINT; +#else + printk("Kernel stack overflow, looping forever\n"); +#endif + while (1) { + } +} +#endif + +#if defined(CONFIG_SMP) || defined(CONFIG_KGDB_CONSOLE) +char gdbconbuf[BUFMAX]; + +static void +kgdb_gdb_message(const char *s, unsigned count) +{ + int i; + int wcount; + char *bufptr; + /* + * This takes care of NMI while spining out chars to gdb + */ + IF_SMP(in_kgdb_console = 1); + gdbconbuf[0] = 'O'; + bufptr = gdbconbuf + 1; + while (count > 0) { + if ((count << 1) > (BUFMAX - 2)) { + wcount = (BUFMAX - 2) >> 1; + } else { + wcount = count; + } + count -= wcount; + for (i = 0; i < wcount; i++) { + bufptr = pack_hex_byte(bufptr, s[i]); + } + *bufptr = '\0'; + s += wcount; + + putpacket(gdbconbuf); + + } + IF_SMP(in_kgdb_console = 0); +} +#endif +#ifdef CONFIG_SMP +static void +to_gdb(const char *s) +{ + int count = 0; + while (s[count] && (count++ < BUFMAX)) ; + kgdb_gdb_message(s, count); +} +#endif +#ifdef CONFIG_KGDB_CONSOLE +#include +#include +#include +#include +#include + +void +kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + + if (gdb_i386vector == -1) { + /* + * We have not yet talked to gdb. What to do... + * lets break, on continue we can do the write. + * But first tell him whats up. Uh, well no can do, + * as this IS the console. Oh well... + * We do need to wait or the messages will be lost. + * Other option would be to tell the above code to + * ignore this breakpoint and do an auto return, + * but that might confuse gdb. Also this happens + * early enough in boot up that we don't have the traps + * set up yet, so... + */ + breakpoint(); + } + kgdb_gdb_message(s, count); +} + +/* + * ------------------------------------------------------------ + * Serial KGDB driver + * ------------------------------------------------------------ + */ + +static struct console kgdbcons = { + name:"kgdb", + write:kgdb_console_write, +#ifdef CONFIG_KGDB_USER_CONSOLE + device:kgdb_console_device, +#endif + flags:CON_PRINTBUFFER | CON_ENABLED, + index:-1, +}; + +/* + * The trick here is that this file gets linked before printk.o + * That means we get to peer at the console info in the command + * line before it does. If we are up, we register, otherwise, + * do nothing. By returning 0, we allow printk to look also. + */ +static int kgdb_console_enabled; + +int __init +kgdb_console_init(char *str) +{ + if ((strncmp(str, "kgdb", 4) == 0) || (strncmp(str, "gdb", 3) == 0)) { + register_console(&kgdbcons); + kgdb_console_enabled = 1; + } + return 0; /* let others look at the string */ +} + +__setup("console=", kgdb_console_init); + +#ifdef CONFIG_KGDB_USER_CONSOLE +static kdev_t kgdb_console_device(struct console *c); +/* This stuff sort of works, but it knocks out telnet devices + * we are leaving it here in case we (or you) find time to figure it out + * better.. + */ + +/* + * We need a real char device as well for when the console is opened for user + * space activities. + */ + +static int +kgdb_consdev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t +kgdb_consdev_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + int size, ret = 0; + static char kbuf[128]; + static DECLARE_MUTEX(sem); + + /* We are not reentrant... */ + if (down_interruptible(&sem)) + return -ERESTARTSYS; + + while (count > 0) { + /* need to copy the data from user space */ + size = count; + if (size > sizeof (kbuf)) + size = sizeof (kbuf); + if (copy_from_user(kbuf, buf, size)) { + ret = -EFAULT; + break;; + } + kgdb_console_write(&kgdbcons, kbuf, size); + count -= size; + ret += size; + buf += size; + } + + up(&sem); + + return ret; +} + +struct file_operations kgdb_consdev_fops = { + open:kgdb_consdev_open, + write:kgdb_consdev_write +}; +static kdev_t +kgdb_console_device(struct console *c) +{ + return MKDEV(TTYAUX_MAJOR, 1); +} + +/* + * This routine gets called from the serial stub in the i386/lib + * This is so it is done late in bring up (just before the console open). + */ +void +kgdb_console_finit(void) +{ + if (kgdb_console_enabled) { + char *cptr = cdevname(MKDEV(TTYAUX_MAJOR, 1)); + char *cp = cptr; + while (*cptr && *cptr != '(') + cptr++; + *cptr = 0; + unregister_chrdev(TTYAUX_MAJOR, cp); + register_chrdev(TTYAUX_MAJOR, "kgdb", &kgdb_consdev_fops); + } +} +#endif +#endif +#ifdef CONFIG_KGDB_TS +#include /* time stamp code */ +#include /* in_interrupt */ +#ifdef CONFIG_KGDB_TS_64 +#define DATA_POINTS 64 +#endif +#ifdef CONFIG_KGDB_TS_128 +#define DATA_POINTS 128 +#endif +#ifdef CONFIG_KGDB_TS_256 +#define DATA_POINTS 256 +#endif +#ifdef CONFIG_KGDB_TS_512 +#define DATA_POINTS 512 +#endif +#ifdef CONFIG_KGDB_TS_1024 +#define DATA_POINTS 1024 +#endif +#ifndef DATA_POINTS +#define DATA_POINTS 128 /* must be a power of two */ +#endif +#define INDEX_MASK (DATA_POINTS - 1) +#if (INDEX_MASK & DATA_POINTS) +#error "CONFIG_KGDB_TS_COUNT must be a power of 2" +#endif +struct kgdb_and_then_struct { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + int data0; + int data1; +}; +struct kgdb_and_then_struct2 { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + struct task_struct *t1; + struct task_struct *t2; +}; +struct kgdb_and_then_struct kgdb_data[DATA_POINTS]; + +struct kgdb_and_then_struct *kgdb_and_then = &kgdb_data[0]; +int kgdb_and_then_count; + +void +kgdb_tstamp(int line, char *source, int data0, int data1) +{ + static spinlock_t ts_spin = SPIN_LOCK_UNLOCKED; + int flags; + kgdb_local_irq_save(flags); + spin_lock(&ts_spin); + rdtscll(kgdb_and_then->at_time); +#ifdef CONFIG_SMP + kgdb_and_then->on_cpu = smp_processor_id(); +#endif + kgdb_and_then->task = current; + kgdb_and_then->from_ln = line; + kgdb_and_then->in_src = source; + kgdb_and_then->from = __builtin_return_address(0); + kgdb_and_then->with_shpf = (int *) (((flags & IF_BIT) >> 9) | + (preempt_count() << 8)); + kgdb_and_then->data0 = data0; + kgdb_and_then->data1 = data1; + kgdb_and_then = &kgdb_data[++kgdb_and_then_count & INDEX_MASK]; + spin_unlock(&ts_spin); + kgdb_local_irq_restore(flags); +#ifdef CONFIG_PREEMPT + +#endif + return; +} +#endif +typedef int gdb_debug_hook(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs); +gdb_debug_hook *linux_debug_hook = &kgdb_handle_exception; /* histerical reasons... */ + +static int kgdb_need_breakpoint[NR_CPUS]; + +void kgdb_schedule_breakpoint(void) +{ + kgdb_need_breakpoint[smp_processor_id()] = 1; +} + +void kgdb_process_breakpoint(void) +{ + /* + * Handle a breakpoint queued from inside network driver code + * to avoid reentrancy issues + */ + if (kgdb_need_breakpoint[smp_processor_id()]) { + kgdb_need_breakpoint[smp_processor_id()] = 0; + BREAKPOINT; + } +} + --- linux-2.6.13-rc2/arch/i386/kernel/Makefile 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/Makefile 2005-07-12 00:27:16.000000000 -0700 @@ -7,13 +7,14 @@ extra-y := head.o init_task.o vmlinux.ld obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_i386.o \ pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \ - doublefault.o quirks.o + doublefault.o quirks.o i8237.o obj-y += cpu/ obj-y += timers/ obj-$(CONFIG_ACPI_BOOT) += acpi/ obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o obj-$(CONFIG_MCA) += mca.o +obj-$(CONFIG_KGDB) += kgdb_stub.o obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_CPUID) += cpuid.o obj-$(CONFIG_MICROCODE) += microcode.o --- linux-2.6.13-rc2/arch/i386/kernel/mpparse.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/mpparse.c 2005-07-12 00:26:57.000000000 -0700 @@ -1116,7 +1116,15 @@ int mp_register_gsi (u32 gsi, int edge_l */ int irq = gsi; if (gsi < MAX_GSI_NUM) { - gsi = pci_irq++; + if (gsi > 15) + gsi = pci_irq++; +#ifdef CONFIG_ACPI_BUS + /* + * Don't assign IRQ used by ACPI SCI + */ + if (gsi == acpi_fadt.sci_int) + gsi = pci_irq++; +#endif gsi_to_irq[irq] = gsi; } else { printk(KERN_ERR "GSI %u is too high\n", gsi); --- linux-2.6.13-rc2/arch/i386/kernel/nmi.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/nmi.c 2005-07-12 00:27:18.000000000 -0700 @@ -33,7 +33,17 @@ #include "mach_traps.h" +#ifdef CONFIG_KGDB +#include +#ifdef CONFIG_SMP +unsigned int nmi_watchdog = NMI_IO_APIC; +#else +unsigned int nmi_watchdog = NMI_LOCAL_APIC; +#endif +#else unsigned int nmi_watchdog = NMI_NONE; +#endif + extern int unknown_nmi_panic; static unsigned int nmi_hz = HZ; static unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ @@ -478,10 +488,33 @@ void touch_nmi_watchdog (void) */ for (i = 0; i < NR_CPUS; i++) alert_counter[i] = 0; + + /* + * Tickle the softlockup detector too: + */ + touch_softlockup_watchdog(); } +#ifdef CONFIG_KGDB +int tune_watchdog = 5*HZ; +#endif extern void die_nmi(struct pt_regs *, const char *msg); +static spinlock_t show_regs_lock = SPIN_LOCK_UNLOCKED; + +void smp_show_regs(struct pt_regs *regs, void *info) +{ + if (regs == NULL) + return; + + bust_spinlocks(1); + spin_lock(&show_regs_lock); + printk("----------- IPI show regs -----------"); + show_regs(regs); + spin_unlock(&show_regs_lock); + bust_spinlocks(0); +} + void nmi_watchdog_tick (struct pt_regs * regs) { @@ -494,12 +527,24 @@ void nmi_watchdog_tick (struct pt_regs * sum = per_cpu(irq_stat, cpu).apic_timer_irqs; +#ifdef CONFIG_KGDB + if (!in_kgdb(regs) && last_irq_sums[cpu] == sum) { + +#else if (last_irq_sums[cpu] == sum) { +#endif /* * Ayiee, looks like this CPU is stuck ... * wait a few IRQs (5 seconds) before doing the oops ... */ alert_counter[cpu]++; +#ifdef CONFIG_KGDB + if (alert_counter[cpu] == tune_watchdog) { + kgdb_handle_exception(2, SIGPWR, 0, regs); + last_irq_sums[cpu] = sum; + alert_counter[cpu] = 0; + } +#endif if (alert_counter[cpu] == 5*nmi_hz) die_nmi(regs, "NMI Watchdog detected LOCKUP"); } else { --- linux-2.6.13-rc2/arch/i386/kernel/process.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/process.c 2005-07-12 00:27:18.000000000 -0700 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -399,6 +400,7 @@ void exit_thread(void) tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET; put_cpu(); } + perfctr_exit_thread(&tsk->thread); } void flush_thread(void) @@ -478,6 +480,8 @@ int copy_thread(int nr, unsigned long cl savesegment(fs,p->thread.fs); savesegment(gs,p->thread.gs); + perfctr_copy_task(p, regs); + tsk = current; if (unlikely(NULL != tsk->thread.io_bitmap_ptr)) { p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); @@ -724,6 +728,7 @@ struct task_struct fastcall * __switch_t disable_tsc(prev_p, next_p); + perfctr_resume_thread(next); return prev_p; } --- linux-2.6.13-rc2/arch/i386/kernel/setup.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/setup.c 2005-07-12 00:27:06.000000000 -0700 @@ -82,7 +82,7 @@ EXPORT_SYMBOL(efi_enabled); /* cpu data as detected by the assembly code in head.S */ struct cpuinfo_x86 new_cpu_data __initdata = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; /* common cpu data for all cpus */ -struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; +struct cpuinfo_x86 boot_cpu_data __read_mostly = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; EXPORT_SYMBOL(boot_cpu_data); unsigned long mmu_cr4_features; --- linux-2.6.13-rc2/arch/i386/kernel/smpboot.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/smpboot.c 2005-07-12 00:27:20.000000000 -0700 @@ -68,21 +68,21 @@ EXPORT_SYMBOL(smp_num_siblings); #endif /* Package ID of each logical CPU */ -int phys_proc_id[NR_CPUS] = {[0 ... NR_CPUS-1] = BAD_APICID}; +int phys_proc_id[NR_CPUS] __read_mostly = {[0 ... NR_CPUS-1] = BAD_APICID}; EXPORT_SYMBOL(phys_proc_id); /* Core ID of each logical CPU */ -int cpu_core_id[NR_CPUS] = {[0 ... NR_CPUS-1] = BAD_APICID}; +int cpu_core_id[NR_CPUS] __read_mostly = {[0 ... NR_CPUS-1] = BAD_APICID}; EXPORT_SYMBOL(cpu_core_id); -cpumask_t cpu_sibling_map[NR_CPUS]; +cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly; EXPORT_SYMBOL(cpu_sibling_map); -cpumask_t cpu_core_map[NR_CPUS]; +cpumask_t cpu_core_map[NR_CPUS] __read_mostly; EXPORT_SYMBOL(cpu_core_map); /* bitmap of online cpus */ -cpumask_t cpu_online_map; +cpumask_t cpu_online_map __read_mostly; EXPORT_SYMBOL(cpu_online_map); cpumask_t cpu_callin_map; @@ -100,7 +100,7 @@ static int __devinitdata tsc_sync_disabl struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned; EXPORT_SYMBOL(cpu_data); -u8 x86_cpu_to_apicid[NR_CPUS] = +u8 x86_cpu_to_apicid[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = 0xff }; EXPORT_SYMBOL(x86_cpu_to_apicid); @@ -550,10 +550,10 @@ extern struct { #ifdef CONFIG_NUMA /* which logical CPUs are on which nodes */ -cpumask_t node_2_cpu_mask[MAX_NUMNODES] = +cpumask_t node_2_cpu_mask[MAX_NUMNODES] __read_mostly = { [0 ... MAX_NUMNODES-1] = CPU_MASK_NONE }; /* which node each logical CPU is on */ -int cpu_2_node[NR_CPUS] = { [0 ... NR_CPUS-1] = 0 }; +int cpu_2_node[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = 0 }; EXPORT_SYMBOL(cpu_2_node); /* set up a mapping between cpu and node. */ @@ -581,7 +581,7 @@ static inline void unmap_cpu_to_node(int #endif /* CONFIG_NUMA */ -u8 cpu_2_logical_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = BAD_APICID }; +u8 cpu_2_logical_apicid[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = BAD_APICID }; static void map_cpu_to_logical_apicid(void) { @@ -1059,6 +1059,7 @@ static void smp_tune_scheduling (void) cachesize = 16; /* Pentiums, 2x8kB cache */ bandwidth = 100; } + max_cache_size = cachesize * 1024; } } --- linux-2.6.13-rc2/arch/i386/kernel/smp.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/smp.c 2005-07-12 00:27:18.000000000 -0700 @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -469,7 +470,17 @@ void flush_tlb_all(void) { on_each_cpu(do_flush_tlb_all, NULL, 1, 1); } - +#ifdef CONFIG_KGDB +/* + * By using the NMI code instead of a vector we just sneak thru the + * word generator coming out with just what we want. AND it does + * not matter if clustered_apic_mode is set or not. + */ +void smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(APIC_DM_NMI); +} +#endif /* * this function sends a 'reschedule' IPI to another CPU. * it goes straight through and wastes no time serializing @@ -567,6 +578,89 @@ int smp_call_function (void (*func) (voi } EXPORT_SYMBOL(smp_call_function); +static spinlock_t nmi_call_lock = SPIN_LOCK_UNLOCKED; +static struct nmi_call_data_struct { + smp_nmi_function func; + void *info; + atomic_t started; + atomic_t finished; + cpumask_t cpus_called; + int wait; +} *nmi_call_data; + +static int smp_nmi_callback(struct pt_regs * regs, int cpu) +{ + smp_nmi_function func; + void *info; + int wait; + + func = nmi_call_data->func; + info = nmi_call_data->info; + wait = nmi_call_data->wait; + ack_APIC_irq(); + /* prevent from calling func() multiple times */ + if (cpu_test_and_set(cpu, nmi_call_data->cpus_called)) + return 0; + /* + * notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + mb(); + atomic_inc(&nmi_call_data->started); + /* at this point the nmi_call_data structure is out of scope */ + irq_enter(); + func(regs, info); + irq_exit(); + if (wait) + atomic_inc(&nmi_call_data->finished); + + return 0; +} + +/* + * This function tries to call func(regs, info) on each cpu. + * Func must be fast and non-blocking. + * May be called with disabled interrupts and from any context. + */ +int smp_nmi_call_function(smp_nmi_function func, void *info, int wait) +{ + struct nmi_call_data_struct data; + int cpus; + + cpus = num_online_cpus() - 1; + if (!cpus) + return 0; + + data.func = func; + data.info = info; + data.wait = wait; + atomic_set(&data.started, 0); + atomic_set(&data.finished, 0); + cpus_clear(data.cpus_called); + /* prevent this cpu from calling func if NMI happens */ + cpu_set(smp_processor_id(), data.cpus_called); + + if (!spin_trylock(&nmi_call_lock)) + return -1; + + nmi_call_data = &data; + set_nmi_ipi_callback(smp_nmi_callback); + mb(); + + /* Send a message to all other CPUs and wait for them to respond */ + send_IPI_allbutself(APIC_DM_NMI); + while (atomic_read(&data.started) != cpus) + barrier(); + + unset_nmi_ipi_callback(); + if (wait) + while (atomic_read(&data.finished) != cpus) + barrier(); + spin_unlock(&nmi_call_lock); + + return 0; +} + static void stop_this_cpu (void * dummy) { /* --- linux-2.6.13-rc2/arch/i386/kernel/syscall_table.S 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/syscall_table.S 2005-07-12 00:27:18.000000000 -0700 @@ -291,3 +291,9 @@ ENTRY(sys_call_table) .long sys_keyctl .long sys_ioprio_set .long sys_ioprio_get /* 290 */ + .long sys_pselect6 + .long sys_ppoll + .long sys_vperfctr_open + .long sys_vperfctr_control + .long sys_vperfctr_write /* 295 */ + .long sys_vperfctr_read --- linux-2.6.13-rc2/arch/i386/kernel/time.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/time.c 2005-07-12 00:27:33.000000000 -0700 @@ -91,7 +91,7 @@ EXPORT_SYMBOL(rtc_lock); DEFINE_SPINLOCK(i8253_lock); EXPORT_SYMBOL(i8253_lock); -struct timer_opts *cur_timer = &timer_none; +struct timer_opts *cur_timer __read_mostly = &timer_none; /* * This is a special lock that is owned by the CPU and holds the index @@ -333,8 +333,7 @@ EXPORT_SYMBOL(get_cmos_time); static void sync_cmos_clock(unsigned long dummy); -static struct timer_list sync_cmos_timer = - TIMER_INITIALIZER(sync_cmos_clock, 0, 0); +static DEFINE_TIMER(sync_cmos_timer, sync_cmos_clock, 0, 0); static void sync_cmos_clock(unsigned long dummy) { @@ -383,6 +382,7 @@ void notify_arch_cmos_timer(void) static long clock_cmos_diff, sleep_start; +static struct timer_opts *last_timer; static int timer_suspend(struct sys_device *dev, pm_message_t state) { /* @@ -391,6 +391,10 @@ static int timer_suspend(struct sys_devi clock_cmos_diff = -get_cmos_time(); clock_cmos_diff += get_seconds(); sleep_start = get_cmos_time(); + last_timer = cur_timer; + cur_timer = &timer_none; + if (last_timer->suspend) + last_timer->suspend(state); return 0; } @@ -404,6 +408,7 @@ static int timer_resume(struct sys_devic if (is_hpet_enabled()) hpet_reenable(); #endif + setup_pit_timer(); sec = get_cmos_time() + clock_cmos_diff; sleep_length = (get_cmos_time() - sleep_start) * HZ; write_seqlock_irqsave(&xtime_lock, flags); @@ -412,6 +417,11 @@ static int timer_resume(struct sys_devic write_sequnlock_irqrestore(&xtime_lock, flags); jiffies += sleep_length; wall_jiffies += sleep_length; + if (last_timer->resume) + last_timer->resume(); + cur_timer = last_timer; + last_timer = NULL; + touch_softlockup_watchdog(); return 0; } --- linux-2.6.13-rc2/arch/i386/kernel/timers/timer_hpet.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/timers/timer_hpet.c 2005-07-12 00:27:06.000000000 -0700 @@ -18,7 +18,7 @@ #include "mach_timer.h" #include -static unsigned long hpet_usec_quotient; /* convert hpet clks to usec */ +static unsigned long hpet_usec_quotient __read_mostly; /* convert hpet clks to usec */ static unsigned long tsc_hpet_quotient; /* convert tsc to hpet clks */ static unsigned long hpet_last; /* hpet counter value at last tick*/ static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */ @@ -177,16 +177,30 @@ static int __init init_hpet(char* overri return 0; } +static int hpet_resume(void) +{ + write_seqlock(&monotonic_lock); + /* Assume this is the last mark offset time */ + rdtsc(last_tsc_low, last_tsc_high); + + if (hpet_use_timer) + hpet_last = hpet_readl(HPET_T0_CMP) - hpet_tick; + else + hpet_last = hpet_readl(HPET_COUNTER); + write_sequnlock(&monotonic_lock); + return 0; +} /************************************************************/ /* tsc timer_opts struct */ -static struct timer_opts timer_hpet = { +static struct timer_opts timer_hpet __read_mostly = { .name = "hpet", .mark_offset = mark_offset_hpet, .get_offset = get_offset_hpet, .monotonic_clock = monotonic_clock_hpet, .delay = delay_hpet, .read_timer = read_timer_tsc, + .resume = hpet_resume, }; struct init_timer_opts __initdata timer_hpet_init = { --- linux-2.6.13-rc2/arch/i386/kernel/timers/timer_pit.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/timers/timer_pit.c 2005-07-12 00:26:58.000000000 -0700 @@ -175,30 +175,3 @@ void setup_pit_timer(void) outb(LATCH >> 8 , PIT_CH0); /* MSB */ spin_unlock_irqrestore(&i8253_lock, flags); } - -static int timer_resume(struct sys_device *dev) -{ - setup_pit_timer(); - return 0; -} - -static struct sysdev_class timer_sysclass = { - set_kset_name("timer_pit"), - .resume = timer_resume, -}; - -static struct sys_device device_timer = { - .id = 0, - .cls = &timer_sysclass, -}; - -static int __init init_timer_sysfs(void) -{ - int error = sysdev_class_register(&timer_sysclass); - if (!error) - error = sysdev_register(&device_timer); - return error; -} - -device_initcall(init_timer_sysfs); - --- linux-2.6.13-rc2/arch/i386/kernel/timers/timer_pm.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/timers/timer_pm.c 2005-07-12 00:26:58.000000000 -0700 @@ -186,6 +186,14 @@ static void mark_offset_pmtmr(void) } } +static int pmtmr_resume(void) +{ + write_seqlock(&monotonic_lock); + /* Assume this is the last mark offset time */ + offset_tick = read_pmtmr(); + write_sequnlock(&monotonic_lock); + return 0; +} static unsigned long long monotonic_clock_pmtmr(void) { @@ -247,6 +255,7 @@ static struct timer_opts timer_pmtmr = { .monotonic_clock = monotonic_clock_pmtmr, .delay = delay_pmtmr, .read_timer = read_timer_tsc, + .resume = pmtmr_resume, }; struct init_timer_opts __initdata timer_pmtmr_init = { --- linux-2.6.13-rc2/arch/i386/kernel/timers/timer_tsc.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/timers/timer_tsc.c 2005-07-12 00:26:58.000000000 -0700 @@ -543,6 +543,19 @@ static int __init init_tsc(char* overrid return -ENODEV; } +static int tsc_resume(void) +{ + write_seqlock(&monotonic_lock); + /* Assume this is the last mark offset time */ + rdtsc(last_tsc_low, last_tsc_high); +#ifdef CONFIG_HPET_TIMER + if (is_hpet_enabled() && hpet_use_timer) + hpet_last = hpet_readl(HPET_COUNTER); +#endif + write_sequnlock(&monotonic_lock); + return 0; +} + #ifndef CONFIG_X86_TSC /* disable flag for tsc. Takes effect by clearing the TSC cpu flag * in cpu/common.c */ @@ -573,6 +586,7 @@ static struct timer_opts timer_tsc = { .monotonic_clock = monotonic_clock_tsc, .delay = delay_tsc, .read_timer = read_timer_tsc, + .resume = tsc_resume, }; struct init_timer_opts __initdata timer_tsc_init = { --- linux-2.6.13-rc2/arch/i386/kernel/traps.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/traps.c 2005-07-12 00:27:18.000000000 -0700 @@ -96,6 +96,8 @@ static int kstack_depth_to_print = 24; struct notifier_block *i386die_chain; static DEFINE_SPINLOCK(die_notifier_lock); +extern char last_sysfs_file[]; + int register_die_notifier(struct notifier_block *nb) { int err = 0; @@ -107,6 +109,39 @@ int register_die_notifier(struct notifie } EXPORT_SYMBOL(register_die_notifier); +#ifdef CONFIG_KGDB +extern void sysenter_past_esp(void); +#include +#include +void set_intr_gate(unsigned int n, void *addr); +static void set_intr_usr_gate(unsigned int n, void *addr); +/* + * Should be able to call this breakpoint() very early in + * bring up. Just hard code the call where needed. + * The breakpoint() code is here because set_?_gate() functions + * are local (static) to trap.c. They need be done only once, + * but it does not hurt to do them over. + */ +void breakpoint(void) +{ + set_intr_usr_gate(3,&int3); /* disable ints on trap */ + set_intr_gate(1,&debug); + set_intr_gate(14,&page_fault); + + BREAKPOINT; +} +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) \ + { \ + if (!user_mode(regs) ) \ + { \ + kgdb_handle_exception(trapnr, signr, error_code, regs); \ + after; \ + } else if ((trapnr == 3) && (regs->eflags &0x200)) local_irq_enable(); \ + } +#else +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) +#endif + static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) { return p > (void *)tinfo && @@ -337,6 +372,16 @@ void die(const char * str, struct pt_reg #endif if (nl) printk("\n"); + printk(KERN_ALERT "last sysfs file: %s\n", last_sysfs_file); +#ifdef CONFIG_KGDB + /* This is about the only place we want to go to kgdb even if in + * user mode. But we must go in via a trap so within kgdb we will + * always be in kernel mode. + */ + if (user_mode(regs)) + BREAKPOINT; +#endif + CHK_REMOTE_DEBUG(0,SIGTRAP,err,regs,) notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV); show_registers(regs); } else @@ -406,6 +451,7 @@ static void do_trap(int trapnr, int sign #define DO_ERROR(trapnr, signr, str, name) \ fastcall void do_##name(struct pt_regs * regs, long error_code) \ { \ + CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,) \ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ == NOTIFY_STOP) \ return; \ @@ -429,6 +475,7 @@ fastcall void do_##name(struct pt_regs * #define DO_VM86_ERROR(trapnr, signr, str, name) \ fastcall void do_##name(struct pt_regs * regs, long error_code) \ { \ + CHK_REMOTE_DEBUG(trapnr, signr, error_code,regs, return) \ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ == NOTIFY_STOP) \ return; \ @@ -516,6 +563,7 @@ gp_in_vm86: gp_in_kernel: if (!fixup_exception(regs)) { + CHK_REMOTE_DEBUG(13,SIGSEGV,error_code,regs,) if (notify_die(DIE_GPF, "general protection fault", regs, error_code, 13, SIGSEGV) == NOTIFY_STOP) return; @@ -578,6 +626,8 @@ void die_nmi (struct pt_regs *regs, cons printk(" on CPU%d, eip %08lx, registers:\n", smp_processor_id(), regs->eip); show_registers(regs); + smp_nmi_call_function(smp_show_regs, NULL, 1); + bust_spinlocks(1); printk("console shuts up ...\n"); console_silent(); spin_unlock(&nmi_print_lock); @@ -594,6 +644,14 @@ void die_nmi (struct pt_regs *regs, cons do_exit(SIGSEGV); } +static int dummy_nmi_callback(struct pt_regs * regs, int cpu) +{ + return 0; +} + +static nmi_callback_t nmi_callback = dummy_nmi_callback; +static nmi_callback_t nmi_ipi_callback = dummy_nmi_callback; + static void default_do_nmi(struct pt_regs * regs) { unsigned char reason = 0; @@ -616,6 +674,9 @@ static void default_do_nmi(struct pt_reg return; } #endif + if (nmi_ipi_callback != dummy_nmi_callback) + return; + unknown_nmi_error(reason, regs); return; } @@ -632,13 +693,6 @@ static void default_do_nmi(struct pt_reg reassert_nmi(); } -static int dummy_nmi_callback(struct pt_regs * regs, int cpu) -{ - return 0; -} - -static nmi_callback_t nmi_callback = dummy_nmi_callback; - fastcall void do_nmi(struct pt_regs * regs, long error_code) { int cpu; @@ -656,15 +710,26 @@ fastcall void do_nmi(struct pt_regs * re ++nmi_count(cpu); - if (!nmi_callback(regs, cpu)) + if (!rcu_dereference(nmi_callback)(regs, cpu)) default_do_nmi(regs); + nmi_ipi_callback(regs, cpu); nmi_exit(); } +void set_nmi_ipi_callback(nmi_callback_t callback) +{ + nmi_ipi_callback = callback; +} + +void unset_nmi_ipi_callback(void) +{ + nmi_ipi_callback = dummy_nmi_callback; +} + void set_nmi_callback(nmi_callback_t callback) { - nmi_callback = callback; + rcu_assign_pointer(nmi_callback, callback); } EXPORT_SYMBOL_GPL(set_nmi_callback); @@ -745,10 +810,33 @@ fastcall void do_debug(struct pt_regs * * check for kernel mode by just checking the CPL * of CS. */ +#ifdef CONFIG_KGDB + /* + * I think this is the only "real" case of a TF in the kernel + * that really belongs to user space. Others are + * "Ours all ours!" + */ + if (!user_mode(regs) && (void *)regs->eip == sysenter_past_esp) + goto clear_TF_reenable; +#else if (!user_mode(regs)) goto clear_TF_reenable; +#endif } +#ifdef CONFIG_KGDB + /* + * If this is a kernel mode trap, we need to reset db7 to allow us + * to continue sanely ALSO skip the signal delivery + */ + if (!user_mode(regs)) + goto clear_dr7; + + /* if not kernel, allow ints but only if they were on */ + if (regs->eflags & 0x200) + local_irq_enable(); +#endif + /* Ok, finally something we can handle */ send_sigtrap(tsk, regs, error_code); @@ -757,6 +845,7 @@ fastcall void do_debug(struct pt_regs * */ clear_dr7: set_debugreg(0, 7); + CHK_REMOTE_DEBUG(1,SIGTRAP,error_code,regs,) return; debug_vm86: @@ -1057,6 +1146,12 @@ static void __init set_task_gate(unsigne { _set_gate(idt_table+n,5,0,0,(gdt_entry<<3)); } +#ifdef CONFIG_KGDB +void set_intr_usr_gate(unsigned int n, void *addr) +{ + _set_gate(idt_table+n,14,3,addr,__KERNEL_CS); +} +#endif void __init trap_init(void) @@ -1076,7 +1171,11 @@ void __init trap_init(void) set_trap_gate(0,÷_error); set_intr_gate(1,&debug); set_intr_gate(2,&nmi); +#ifndef CONFIG_KGDB set_system_intr_gate(3, &int3); /* int3-5 can be called from all */ +#else + set_intr_usr_gate(3,&int3); /* int3-5 can be called from all */ +#endif set_system_gate(4,&overflow); set_system_gate(5,&bounds); set_trap_gate(6,&invalid_op); --- linux-2.6.13-rc2/arch/i386/kernel/vmlinux.lds.S 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/kernel/vmlinux.lds.S 2005-07-12 00:26:30.000000000 -0700 @@ -57,6 +57,9 @@ SECTIONS *(.data.cacheline_aligned) } + /* rarely changed data like cpu maps */ + . = ALIGN(32); + .data.read_mostly : AT(ADDR(.data.read_mostly) - LOAD_OFFSET) { *(.data.read_mostly) } _edata = .; /* End of data section */ . = ALIGN(THREAD_SIZE); /* init_task */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/i386/lib/kgdb_serial.c 2005-07-12 00:27:16.000000000 -0700 @@ -0,0 +1,499 @@ +/* + * Serial interface GDB stub + * + * Written (hacked together) by David Grothe (dave@gcom.com) + * Modified to allow invokation early in boot see also + * kgdb.h for instructions by George Anzinger(george@mvista.com) + * Modified to handle debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KGDB_USER_CONSOLE +extern void kgdb_console_finit(void); +#endif +#define PRNT_off +#define TEST_EXISTANCE +#ifdef PRNT +#define dbprintk(s) printk s +#else +#define dbprintk(s) +#endif +#define TEST_INTERRUPT_off +#ifdef TEST_INTERRUPT +#define intprintk(s) printk s +#else +#define intprintk(s) +#endif + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char gdb_buf[GDB_BUF_SIZE]; +static int gdb_buf_in_inx; +static atomic_t gdb_buf_in_cnt; +static int gdb_buf_out_inx; + +struct async_struct *gdb_async_info; +static int gdb_async_irq; + +#define outb_px(a,b) outb_p(b,a) + +static void program_uart(struct async_struct *info); +static void write_char(struct async_struct *info, int chr); +/* + * Get a byte from the hardware data buffer and return it + */ +static int +read_data_bfr(struct async_struct *info) +{ + char it = inb_p(info->port + UART_LSR); + + if (it & UART_LSR_DR) + return (inb_p(info->port + UART_RX)); + /* + * If we have a framing error assume somebody messed with + * our uart. Reprogram it and send '-' both ways... + */ + if (it & 0xc) { + program_uart(info); + write_char(info, '-'); + return ('-'); + } + return (-1); + +} /* read_data_bfr */ + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + + * Locking here is a bit of a problem. We MUST not lock out communication + * if we are trying to talk to gdb about a kgdb entry. ON the other hand + * we can loose chars in the console pass thru if we don't lock. It is also + * possible that we could hold the lock or be waiting for it when kgdb + * NEEDS to talk. Since kgdb locks down the world, it does not need locks. + * We do, of course have possible issues with interrupting a uart operation, + * but we will just depend on the uart status to help keep that straight. + + */ +static spinlock_t uart_interrupt_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_SMP +extern spinlock_t kgdb_spinlock; +#endif + +static int +read_char(struct async_struct *info) +{ + int chr; + unsigned long flags; + local_irq_save(flags); +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_lock(&uart_interrupt_lock); + } +#endif + if (atomic_read(&gdb_buf_in_cnt) != 0) { /* intr routine has q'd chars */ + chr = gdb_buf[gdb_buf_out_inx++]; + gdb_buf_out_inx &= (GDB_BUF_SIZE - 1); + atomic_dec(&gdb_buf_in_cnt); + } else { + chr = read_data_bfr(info); + } +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_unlock(&uart_interrupt_lock); + } +#endif + local_irq_restore(flags); + return (chr); +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void +write_char(struct async_struct *info, int chr) +{ + while (!(inb_p(info->port + UART_LSR) & UART_LSR_THRE)) ; + + outb_p(chr, info->port + UART_TX); + +} /* write_char */ + +/* + * Mostly we don't need a spinlock, but since the console goes + * thru here with interrutps on, well, we need to catch those + * chars. + */ +/* + * This is the receiver interrupt routine for the GDB stub. + * It will receive a limited number of characters of input + * from the gdb host machine and save them up in a buffer. + * + * When the gdb stub routine tty_getDebugChar() is called it + * draws characters out of the buffer until it is empty and + * then reads directly from the serial port. + * + * We do not attempt to write chars from the interrupt routine + * since the stubs do all of that via tty_putDebugChar() which + * writes one byte after waiting for the interface to become + * ready. + * + * The debug stubs like to run with interrupts disabled since, + * after all, they run as a consequence of a breakpoint in + * the kernel. + * + * Perhaps someone who knows more about the tty driver than I + * care to learn can make this work for any low level serial + * driver. + */ +static irqreturn_t +gdb_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct async_struct *info; + unsigned long flags; + + info = gdb_async_info; + if (!info || !info->tty || irq != gdb_async_irq) + return IRQ_NONE; + + local_irq_save(flags); + spin_lock(&uart_interrupt_lock); + do { + int chr = read_data_bfr(info); + intprintk(("Debug char on int: %x hex\n", chr)); + if (chr < 0) + continue; + + if (chr == 3) { /* Ctrl-C means remote interrupt */ + BREAKPOINT; + continue; + } + + if (atomic_read(&gdb_buf_in_cnt) >= GDB_BUF_SIZE) { + /* buffer overflow tosses early char */ + read_char(info); + } + gdb_buf[gdb_buf_in_inx++] = chr; + gdb_buf_in_inx &= (GDB_BUF_SIZE - 1); + } while (inb_p(info->port + UART_IIR) & UART_IIR_RDI); + spin_unlock(&uart_interrupt_lock); + local_irq_restore(flags); + return IRQ_HANDLED; +} /* gdb_interrupt */ + +/* + * Just a NULL routine for testing. + */ +void +gdb_null(void) +{ +} /* gdb_null */ + +/* These structure are filled in with values defined in asm/kgdb_local.h + */ +static struct serial_state state = SB_STATE; +static struct async_struct local_info = SB_INFO; +static int ok_to_enable_ints = 0; +static void kgdb_enable_ints_now(void); + +extern char *kgdb_version; +/* + * Hook an IRQ for KGDB. + * + * This routine is called from tty_putDebugChar, below. + */ +static int ints_disabled = 1; +int +gdb_hook_interrupt(struct async_struct *info, int verb) +{ + struct serial_state *state = info->state; + unsigned long flags; + int port; +#ifdef TEST_EXISTANCE + int scratch, scratch2; +#endif + + /* The above fails if memory managment is not set up yet. + * Rather than fail the set up, just keep track of the fact + * and pick up the interrupt thing later. + */ + gdb_async_info = info; + port = gdb_async_info->port; + gdb_async_irq = state->irq; + if (verb) { + printk("kgdb %s : port =%x, IRQ=%d, divisor =%d\n", + kgdb_version, + port, + gdb_async_irq, gdb_async_info->state->custom_divisor); + } + local_irq_save(flags); +#ifdef TEST_EXISTANCE + /* Existance test */ + /* Should not need all this, but just in case.... */ + + scratch = inb_p(port + UART_IER); + outb_px(port + UART_IER, 0); + outb_px(0xff, 0x080); + scratch2 = inb_p(port + UART_IER); + outb_px(port + UART_IER, scratch); + if (scratch2) { + printk + ("gdb_hook_interrupt: Could not clear IER, not a UART!\n"); + local_irq_restore(flags); + return 1; /* We failed; there's nothing here */ + } + scratch2 = inb_p(port + UART_LCR); + outb_px(port + UART_LCR, 0xBF); /* set up for StarTech test */ + outb_px(port + UART_EFR, 0); /* EFR is the same as FCR */ + outb_px(port + UART_LCR, 0); + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = inb_p(port + UART_IIR) >> 6; + if (scratch == 1) { + printk("gdb_hook_interrupt: Undefined UART type!" + " Not a UART! \n"); + local_irq_restore(flags); + return 1; + } else { + dbprintk(("gdb_hook_interrupt: UART type " + "is %d where 0=16450, 2=16550 3=16550A\n", scratch)); + } + scratch = inb_p(port + UART_MCR); + outb_px(port + UART_MCR, UART_MCR_LOOP | scratch); + outb_px(port + UART_MCR, UART_MCR_LOOP | 0x0A); + scratch2 = inb_p(port + UART_MSR) & 0xF0; + outb_px(port + UART_MCR, scratch); + if (scratch2 != 0x90) { + printk("gdb_hook_interrupt: " + "Loop back test failed! Not a UART!\n"); + local_irq_restore(flags); + return scratch2 + 1000; /* force 0 to fail */ + } +#endif /* test existance */ + program_uart(info); + local_irq_restore(flags); + + return (0); + +} /* gdb_hook_interrupt */ + +static void +program_uart(struct async_struct *info) +{ + int port = info->port; + + (void) inb_p(port + UART_RX); + outb_px(port + UART_IER, 0); + + (void) inb_p(port + UART_RX); /* serial driver comments say */ + (void) inb_p(port + UART_IIR); /* this clears the interrupt regs */ + (void) inb_p(port + UART_MSR); + outb_px(port + UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); + outb_px(port + UART_DLL, info->state->custom_divisor & 0xff); /* LS */ + outb_px(port + UART_DLM, info->state->custom_divisor >> 8); /* MS */ + outb_px(port + UART_MCR, info->MCR); + + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1 | UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); /* set fcr */ + outb_px(port + UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1); /* set fcr */ + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + return; +} + +/* + * tty_getDebugChar + * + * This is a GDB stub routine. It waits for a character from the + * serial interface and then returns it. If there is no serial + * interface connection then it returns a bogus value which will + * almost certainly cause the system to hang. In the + */ +int kgdb_in_isr = 0; +int kgdb_in_lsr = 0; +extern spinlock_t kgdb_spinlock; + +/* Caller takes needed protections */ + +int +tty_getDebugChar(void) +{ + volatile int chr, dum, time, end_time; + + dbprintk(("tty_getDebugChar(port %x): ", gdb_async_info->port)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + /* + * This trick says if we wait a very long time and get + * no char, return the -1 and let the upper level deal + * with it. + */ + rdtsc(dum, time); + end_time = time + 2; + while (((chr = read_char(gdb_async_info)) == -1) && + (end_time - time) > 0) { + rdtsc(dum, time); + }; + /* + * This covers our butts if some other code messes with + * our uart, hay, it happens :o) + */ + if (chr == -1) + program_uart(gdb_async_info); + + dbprintk(("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ')); + return (chr); + +} /* tty_getDebugChar */ + +static int count = 3; +static spinlock_t one_at_atime = SPIN_LOCK_UNLOCKED; + +static int __init +kgdb_enable_ints(void) +{ + if (kgdboe) { + return 0; + } + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 1); + } + ok_to_enable_ints = 1; + kgdb_enable_ints_now(); +#ifdef CONFIG_KGDB_USER_CONSOLE + kgdb_console_finit(); +#endif + return 0; +} + +#ifdef CONFIG_SERIAL_8250 +void shutdown_for_kgdb(struct async_struct *gdb_async_info); +#endif + +#ifdef CONFIG_DISCONTIGMEM +static inline int kgdb_mem_init_done(void) +{ + return totalram_pages != 0; +} +#else +static inline int kgdb_mem_init_done(void) +{ + return max_mapnr != 0; +} +#endif + +static void +kgdb_enable_ints_now(void) +{ + if (!spin_trylock(&one_at_atime)) + return; + if (!ints_disabled) + goto exit; + if (kgdb_mem_init_done() && + ints_disabled) { /* don't try till mem init */ +#ifdef CONFIG_SERIAL_8250 + /* + * The ifdef here allows the system to be configured + * without the serial driver. + * Don't make it a module, however, it will steal the port + */ + shutdown_for_kgdb(gdb_async_info); +#endif + ints_disabled = request_irq(gdb_async_info->state->irq, + gdb_interrupt, + IRQ_T(gdb_async_info), + "KGDB-stub", NULL); + intprintk(("KGDB: request_irq returned %d\n", ints_disabled)); + } + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + exit: + spin_unlock(&one_at_atime); +} + +/* + * tty_putDebugChar + * + * This is a GDB stub routine. It waits until the interface is ready + * to transmit a char and then sends it. If there is no serial + * interface connection then it simply returns to its caller, having + * pretended to send the char. Caller takes needed protections. + */ +void +tty_putDebugChar(int chr) +{ + dbprintk(("tty_putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n", + gdb_async_info->port, + chr, + chr > ' ' && chr < 0x7F ? chr : ' ', ints_disabled ? 0 : 1)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + + write_char(gdb_async_info, chr); /* this routine will wait */ + count = (chr == '#') ? 0 : count + 1; + if ((count == 2)) { /* try to enable after */ + if (ints_disabled & ok_to_enable_ints) + kgdb_enable_ints_now(); /* try to enable after */ + + /* We do this a lot because, well we really want to get these + * interrupts. The serial driver will clear these bits when it + * initializes the chip. Every thing else it does is ok, + * but this. + */ + if (!ints_disabled) { + outb_px(gdb_async_info->port + UART_IER, + gdb_async_info->IER); + } + } + +} /* tty_putDebugChar */ + +/* + * This does nothing for the serial port, since it doesn't buffer. + */ + +void tty_flushDebugChar(void) +{ +} + +module_init(kgdb_enable_ints); --- linux-2.6.13-rc2/arch/i386/lib/Makefile 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/i386/lib/Makefile 2005-07-12 00:27:16.000000000 -0700 @@ -8,3 +8,4 @@ lib-y = checksum.o delay.o usercopy.o ge lib-$(CONFIG_X86_USE_3DNOW) += mmx.o lib-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o +lib-$(CONFIG_KGDB) += kgdb_serial.o --- linux-2.6.13-rc2/arch/i386/Makefile 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/Makefile 2005-07-12 00:27:16.000000000 -0700 @@ -111,6 +111,9 @@ core-$(CONFIG_X86_ES7000) := arch/i386/m # default subarch .h files mflags-y += -Iinclude/asm-i386/mach-default +mflags-$(CONFIG_KGDB) += -gdwarf-2 +mflags-$(CONFIG_KGDB_MORE) += $(shell echo $(CONFIG_KGDB_OPTIONS) | sed -e 's/"//g') + head-y := arch/i386/kernel/head.o arch/i386/kernel/init_task.o libs-y += arch/i386/lib/ --- linux-2.6.13-rc2/arch/i386/mm/fault.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/mm/fault.c 2005-07-12 00:27:16.000000000 -0700 @@ -199,6 +199,18 @@ static inline int is_prefetch(struct pt_ return 0; } +static noinline void force_sig_info_fault(int si_signo, int si_code, + unsigned long address, struct task_struct *tsk) +{ + siginfo_t info; + + info.si_signo = si_signo; + info.si_errno = 0; + info.si_code = si_code; + info.si_addr = (void __user *)address; + force_sig_info(si_signo, &info, tsk); +} + fastcall void do_invalid_op(struct pt_regs *, unsigned long); /* @@ -218,8 +230,7 @@ fastcall void do_page_fault(struct pt_re struct vm_area_struct * vma; unsigned long address; unsigned long page; - int write; - siginfo_t info; + int write, si_code; /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); @@ -233,7 +244,7 @@ fastcall void do_page_fault(struct pt_re tsk = current; - info.si_code = SEGV_MAPERR; + si_code = SEGV_MAPERR; /* * We fault-in kernel-space virtual memory on-demand. The @@ -313,7 +324,7 @@ fastcall void do_page_fault(struct pt_re * we can handle it.. */ good_area: - info.si_code = SEGV_ACCERR; + si_code = SEGV_ACCERR; write = 0; switch (error_code & 3) { default: /* 3: write, present */ @@ -387,11 +398,7 @@ bad_area_nosemaphore: /* Kernel addresses are always protection faults */ tsk->thread.error_code = error_code | (address >= TASK_SIZE); tsk->thread.trap_no = 14; - info.si_signo = SIGSEGV; - info.si_errno = 0; - /* info.si_code has been set above */ - info.si_addr = (void __user *)address; - force_sig_info(SIGSEGV, &info, tsk); + force_sig_info_fault(SIGSEGV, si_code, address, tsk); return; } @@ -428,6 +435,12 @@ no_context: * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ +#ifdef CONFIG_KGDB + if (!user_mode(regs)){ + kgdb_handle_exception(14,SIGBUS, error_code, regs); + return; + } +#endif bust_spinlocks(1); @@ -500,11 +513,7 @@ do_sigbus: tsk->thread.cr2 = address; tsk->thread.error_code = error_code; tsk->thread.trap_no = 14; - info.si_signo = SIGBUS; - info.si_errno = 0; - info.si_code = BUS_ADRERR; - info.si_addr = (void __user *)address; - force_sig_info(SIGBUS, &info, tsk); + force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk); return; vmalloc_fault: --- linux-2.6.13-rc2/arch/i386/mm/ioremap.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/mm/ioremap.c 2005-07-12 00:26:30.000000000 -0700 @@ -228,7 +228,8 @@ EXPORT_SYMBOL(ioremap_nocache); void iounmap(volatile void __iomem *addr) { struct vm_struct *p; - if ((void __force *) addr <= high_memory) + + if ((void __force *)addr <= high_memory) return; /* @@ -241,9 +242,10 @@ void iounmap(volatile void __iomem *addr return; write_lock(&vmlist_lock); - p = __remove_vm_area((void *) (PAGE_MASK & (unsigned long __force) addr)); + p = __remove_vm_area((void *)(PAGE_MASK & (unsigned long __force)addr)); if (!p) { printk(KERN_WARNING "iounmap: bad address %p\n", addr); + dump_stack(); goto out_unlock; } --- linux-2.6.13-rc2/arch/i386/pci/common.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/pci/common.c 2005-07-12 00:26:48.000000000 -0700 @@ -165,7 +165,6 @@ static int __init pcibios_init(void) if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT)) pcibios_sort(); #endif - pci_assign_unassigned_resources(); return 0; } --- linux-2.6.13-rc2/arch/i386/pci/i386.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/i386/pci/i386.c 2005-07-12 00:26:48.000000000 -0700 @@ -106,16 +106,11 @@ static void __init pcibios_allocate_bus_ if ((dev = bus->self)) { for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { r = &dev->resource[idx]; - if (!r->flags) + if (!r->start) continue; pr = pci_find_parent_resource(dev, r); - if (!r->start || !pr || request_resource(pr, r) < 0) { + if (!pr || request_resource(pr, r) < 0) printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, pci_name(dev)); - /* Something is wrong with the region. - Invalidate the resource to prevent child - resource allocations in this range. */ - r->flags = 0; - } } } pcibios_allocate_bus_resources(&bus->children); @@ -232,7 +227,7 @@ int pcibios_enable_resources(struct pci_ pci_read_config_word(dev, PCI_COMMAND, &cmd); old_cmd = cmd; - for(idx = 0; idx < PCI_NUM_RESOURCES; idx++) { + for(idx=0; idx<6; idx++) { /* Only set up the requested stuff */ if (!(mask & (1<irq = assign_irq_vector(AUTO_ASSIGN); + if ((rc = assign_irq_vector(AUTO_ASSIGN)) < 0) + panic("%s: out of interrupt vectors!\n", __FUNCTION__); + dev->irq = rc; /* * attach the interrupt in the simulator, this does enable interrupts --- linux-2.6.13-rc2/arch/ia64/hp/sim/simserial.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/hp/sim/simserial.c 2005-07-12 00:26:30.000000000 -0700 @@ -982,7 +982,7 @@ static struct tty_operations hp_ops = { static int __init simrs_init (void) { - int i; + int i, rc; struct serial_state *state; if (!ia64_platform_is("hpsim")) @@ -1017,7 +1017,10 @@ simrs_init (void) if (state->type == PORT_UNKNOWN) continue; if (!state->irq) { - state->irq = assign_irq_vector(AUTO_ASSIGN); + if ((rc = assign_irq_vector(AUTO_ASSIGN)) < 0) + panic("%s: out of interrupt vectors!\n", + __FUNCTION__); + state->irq = rc; ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq); } --- linux-2.6.13-rc2/arch/ia64/Kconfig 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/Kconfig 2005-07-12 00:26:57.000000000 -0700 @@ -440,6 +440,11 @@ config GENERIC_IRQ_PROBE bool default y +config GENERIC_PENDING_IRQ + bool + depends on GENERIC_HARDIRQS && SMP + default y + source "arch/ia64/hp/sim/Kconfig" source "arch/ia64/oprofile/Kconfig" --- linux-2.6.13-rc2/arch/ia64/kernel/acpi.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/acpi.c 2005-07-12 00:26:38.000000000 -0700 @@ -11,6 +11,7 @@ * Copyright (C) 2001 Jenna Hall * Copyright (C) 2001 Takayoshi Kochi * Copyright (C) 2002 Erich Focht + * Copyright (C) 2004 Ashok Raj * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -67,6 +68,11 @@ EXPORT_SYMBOL(pm_power_off); unsigned char acpi_kbd_controller_present = 1; unsigned char acpi_legacy_devices; +static unsigned int __initdata acpi_madt_rev; + +unsigned int acpi_cpei_override; +unsigned int acpi_cpei_phys_cpuid; + #define MAX_SAPICS 256 u16 ia64_acpiid_to_sapicid[MAX_SAPICS] = { [0 ... MAX_SAPICS - 1] = -1 }; @@ -265,10 +271,56 @@ acpi_parse_plat_int_src ( (plintsrc->flags.trigger == 1) ? IOSAPIC_EDGE : IOSAPIC_LEVEL); platform_intr_list[plintsrc->type] = vector; + if (acpi_madt_rev > 1) { + acpi_cpei_override = plintsrc->plint_flags.cpei_override_flag; + } + + /* + * Save the physical id, so we can check when its being removed + */ + acpi_cpei_phys_cpuid = ((plintsrc->id << 8) | (plintsrc->eid)) & 0xffff; + return 0; } +unsigned int can_cpei_retarget(void) +{ + extern int cpe_vector; + + /* + * Only if CPEI is supported and the override flag + * is present, otherwise return that its re-targettable + * if we are in polling mode. + */ + if (cpe_vector > 0 && !acpi_cpei_override) + return 0; + else + return 1; +} + +unsigned int is_cpu_cpei_target(unsigned int cpu) +{ + unsigned int logical_id; + + logical_id = cpu_logical_id(acpi_cpei_phys_cpuid); + + if (logical_id == cpu) + return 1; + else + return 0; +} + +void set_cpei_target_cpu(unsigned int cpu) +{ + acpi_cpei_phys_cpuid = cpu_physical_id(cpu); +} + +unsigned int get_cpei_target_cpu(void) +{ + return acpi_cpei_phys_cpuid; +} + static int __init acpi_parse_int_src_ovr ( acpi_table_entry_header *header, const unsigned long end) @@ -326,6 +378,8 @@ acpi_parse_madt (unsigned long phys_addr acpi_madt = (struct acpi_table_madt *) __va(phys_addr); + acpi_madt_rev = acpi_madt->header.revision; + /* remember the value for reference after free_initmem() */ #ifdef CONFIG_ITANIUM has_8259 = 1; /* Firmware on old Itanium systems is broken */ @@ -640,9 +694,11 @@ acpi_boot_init (void) if (smp_boot_data.cpu_phys_id[cpu] != hard_smp_processor_id()) node_cpuid[i++].phys_id = smp_boot_data.cpu_phys_id[cpu]; } - build_cpu_to_node_map(); # endif #endif +#ifdef CONFIG_ACPI_NUMA + build_cpu_to_node_map(); +#endif /* Make boot-up look pretty */ printk(KERN_INFO "%d CPUs available, %d CPUs total\n", available_cpus, total_cpus); return 0; --- linux-2.6.13-rc2/arch/ia64/kernel/domain.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/domain.c 2005-07-12 00:27:20.000000000 -0700 @@ -350,6 +350,10 @@ next_sg: #endif cpu_attach_domain(sd, i); } + /* + * Tune cache-hot values: + */ + calibrate_migration_costs(); } /* * Set up scheduler domains and groups. Callers must hold the hotplug lock. --- linux-2.6.13-rc2/arch/ia64/kernel/entry.S 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/entry.S 2005-07-12 00:26:30.000000000 -0700 @@ -1249,7 +1249,7 @@ ENTRY(sys_rt_sigreturn) stf.spill [r17]=f11 adds out0=16,sp // out0 = &sigscratch br.call.sptk.many rp=ia64_rt_sigreturn -.ret19: .restore sp 0 +.ret19: .restore sp,0 adds sp=16,sp ;; ld8 r9=[sp] // load new ar.unat --- linux-2.6.13-rc2/arch/ia64/kernel/iosapic.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/iosapic.c 2005-07-12 00:26:30.000000000 -0700 @@ -489,8 +489,6 @@ static int iosapic_find_sharable_vector } } } - if (vector < 0) - panic("%s: out of interrupt vectors!\n", __FUNCTION__); return vector; } @@ -506,6 +504,8 @@ iosapic_reassign_vector (int vector) if (!list_empty(&iosapic_intr_info[vector].rtes)) { new_vector = assign_irq_vector(AUTO_ASSIGN); + if (new_vector < 0) + panic("%s: out of interrupt vectors!\n", __FUNCTION__); printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector); memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector], sizeof(struct iosapic_intr_info)); @@ -734,9 +734,12 @@ again: spin_unlock_irqrestore(&iosapic_lock, flags); /* If vector is running out, we try to find a sharable vector */ - vector = assign_irq_vector_nopanic(AUTO_ASSIGN); - if (vector < 0) + vector = assign_irq_vector(AUTO_ASSIGN); + if (vector < 0) { vector = iosapic_find_sharable_vector(trigger, polarity); + if (vector < 0) + panic("%s: out of interrupt vectors!\n", __FUNCTION__); + } spin_lock_irqsave(&irq_descp(vector)->lock, flags); spin_lock(&iosapic_lock); @@ -884,6 +887,8 @@ iosapic_register_platform_intr (u32 int_ break; case ACPI_INTERRUPT_INIT: vector = assign_irq_vector(AUTO_ASSIGN); + if (vector < 0) + panic("%s: out of interrupt vectors!\n", __FUNCTION__); delivery = IOSAPIC_INIT; break; case ACPI_INTERRUPT_CPEI: --- linux-2.6.13-rc2/arch/ia64/kernel/irq.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/kernel/irq.c 2005-07-12 00:26:57.000000000 -0700 @@ -91,23 +91,8 @@ skip: } #ifdef CONFIG_SMP -/* - * This is updated when the user sets irq affinity via /proc - */ -static cpumask_t __cacheline_aligned pending_irq_cpumask[NR_IRQS]; -static unsigned long pending_irq_redir[BITS_TO_LONGS(NR_IRQS)]; - static char irq_redir [NR_IRQS]; // = { [0 ... NR_IRQS-1] = 1 }; -/* - * Arch specific routine for deferred write to iosapic rte to reprogram - * intr destination. - */ -void proc_set_irq_affinity(unsigned int irq, cpumask_t mask_val) -{ - pending_irq_cpumask[irq] = mask_val; -} - void set_irq_affinity_info (unsigned int irq, int hwid, int redir) { cpumask_t mask = CPU_MASK_NONE; @@ -116,32 +101,10 @@ void set_irq_affinity_info (unsigned int if (irq < NR_IRQS) { irq_affinity[irq] = mask; + set_irq_info(irq, mask); irq_redir[irq] = (char) (redir & 0xff); } } - - -void move_irq(int irq) -{ - /* note - we hold desc->lock */ - cpumask_t tmp; - irq_desc_t *desc = irq_descp(irq); - int redir = test_bit(irq, pending_irq_redir); - - if (unlikely(!desc->handler->set_affinity)) - return; - - if (!cpus_empty(pending_irq_cpumask[irq])) { - cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map); - if (unlikely(!cpus_empty(tmp))) { - desc->handler->set_affinity(irq | (redir ? IA64_IRQ_REDIRECTED : 0), - pending_irq_cpumask[irq]); - } - cpus_clear(pending_irq_cpumask[irq]); - } -} - - #endif /* CONFIG_SMP */ #ifdef CONFIG_HOTPLUG_CPU --- linux-2.6.13-rc2/arch/ia64/kernel/irq_ia64.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/kernel/irq_ia64.c 2005-07-12 00:26:30.000000000 -0700 @@ -63,30 +63,19 @@ EXPORT_SYMBOL(isa_irq_to_vector_map); static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)]; int -assign_irq_vector_nopanic (int irq) +assign_irq_vector (int irq) { int pos, vector; again: pos = find_first_zero_bit(ia64_vector_mask, IA64_NUM_DEVICE_VECTORS); vector = IA64_FIRST_DEVICE_VECTOR + pos; if (vector > IA64_LAST_DEVICE_VECTOR) - return -1; + return -ENOSPC; if (test_and_set_bit(pos, ia64_vector_mask)) goto again; return vector; } -int -assign_irq_vector (int irq) -{ - int vector = assign_irq_vector_nopanic(irq); - - if (vector < 0) - panic("assign_irq_vector: out of interrupt vectors!"); - - return vector; -} - void free_irq_vector (int vector) { --- linux-2.6.13-rc2/arch/ia64/kernel/Makefile 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/Makefile 2005-07-12 00:26:30.000000000 -0700 @@ -17,6 +17,7 @@ obj-$(CONFIG_IA64_PALINFO) += palinfo.o obj-$(CONFIG_IOSAPIC) += iosapic.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_SMP) += smp.o smpboot.o domain.o +obj-$(CONFIG_NUMA) += numa.o obj-$(CONFIG_PERFMON) += perfmon_default_smpl.o obj-$(CONFIG_IA64_CYCLONE) += cyclone.o obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o --- linux-2.6.13-rc2/arch/ia64/kernel/mca.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/kernel/mca.c 2005-07-12 00:27:15.000000000 -0700 @@ -271,7 +271,7 @@ ia64_mca_log_sal_error_record(int sal_in #ifdef CONFIG_ACPI -static int cpe_vector = -1; +int cpe_vector = -1; static irqreturn_t ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs) @@ -473,6 +473,9 @@ init_handler_platform (pal_min_state_are struct pt_regs *pt, struct switch_stack *sw) { struct unw_frame_info info; +#ifdef CONFIG_SMP + int locked; +#endif /* if a kernel debugger is available call it here else just dump the registers */ @@ -492,9 +495,7 @@ init_handler_platform (pal_min_state_are ia64_do_show_stack(&info, NULL); #ifdef CONFIG_SMP - /* read_trylock() would be handy... */ - if (!tasklist_lock.write_lock) - read_lock(&tasklist_lock); + locked = read_trylock(&tasklist_lock); #endif { struct task_struct *g, *t; @@ -507,7 +508,7 @@ init_handler_platform (pal_min_state_are } while_each_thread (g, t); } #ifdef CONFIG_SMP - if (!tasklist_lock.write_lock) + if (locked) read_unlock(&tasklist_lock); #endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel/arch/ia64/kernel/numa.c 2005-07-12 00:26:30.000000000 -0700 @@ -0,0 +1,57 @@ +/* + * 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 + * + * ia64 kernel NUMA specific stuff + * + * Copyright (C) 2002 Erich Focht + * Copyright (C) 2004 Silicon Graphics, Inc. + * Jesse Barnes + */ +#include +#include +#include +#include +#include + +u8 cpu_to_node_map[NR_CPUS] __cacheline_aligned; +EXPORT_SYMBOL(cpu_to_node_map); + +cpumask_t node_to_cpu_mask[MAX_NUMNODES] __cacheline_aligned; + +/** + * build_cpu_to_node_map - setup cpu to node and node to cpumask arrays + * + * Build cpu to node mapping and initialize the per node cpu masks using + * info from the node_cpuid array handed to us by ACPI. + */ +void __init build_cpu_to_node_map(void) +{ + int cpu, i, node; + + for(node=0; node < MAX_NUMNODES; node++) + cpus_clear(node_to_cpu_mask[node]); + + for(cpu = 0; cpu < NR_CPUS; ++cpu) { + node = -1; + for (i = 0; i < NR_CPUS; ++i) + if (cpu_physical_id(cpu) == node_cpuid[i].phys_id) { + node = node_cpuid[i].nid; + break; + } + cpu_to_node_map[cpu] = (node >= 0) ? node : 0; + if (node >= 0) + cpu_set(cpu, node_to_cpu_mask[node]); + } +} --- linux-2.6.13-rc2/arch/ia64/kernel/perfmon.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/kernel/perfmon.c 2005-07-12 00:27:34.000000000 -0700 @@ -37,8 +37,8 @@ #include #include #include -#include #include +#include #include #include @@ -497,7 +497,7 @@ typedef struct { static pfm_stats_t pfm_stats[NR_CPUS]; static pfm_session_t pfm_sessions; /* global sessions information */ -static spinlock_t pfm_alt_install_check = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(pfm_alt_install_check); static pfm_intr_handler_desc_t *pfm_alt_intr_handler; static struct proc_dir_entry *perfmon_dir; @@ -2218,15 +2218,17 @@ static void pfm_free_fd(int fd, struct file *file) { struct files_struct *files = current->files; + struct fdtable *fdt = files_fdtable(files); /* * there ie no fd_uninstall(), so we do it here */ spin_lock(&files->file_lock); - files->fd[fd] = NULL; + rcu_assign_pointer(fdt->fd[fd], NULL); spin_unlock(&files->file_lock); - if (file) put_filp(file); + if (file) + put_filp(file); put_unused_fd(fd); } --- linux-2.6.13-rc2/arch/ia64/kernel/process.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/process.c 2005-07-12 00:26:38.000000000 -0700 @@ -196,6 +196,7 @@ update_pal_halt_status(int status) void default_idle (void) { + local_irq_enable(); while (!need_resched()) if (can_do_pal_halt) safe_halt(); --- linux-2.6.13-rc2/arch/ia64/kernel/setup.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/setup.c 2005-07-12 00:27:20.000000000 -0700 @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include #include @@ -56,6 +58,7 @@ #include #include #include +#include #if defined(CONFIG_SMP) && (IA64_CPU_SIZE > PAGE_SIZE) # error "struct cpuinfo_ia64 too big!" @@ -630,6 +633,7 @@ static void get_max_cacheline_size (void) { unsigned long line_size, max = 1; + unsigned int cache_size = 0; u64 l, levels, unique_caches; pal_cache_config_info_t cci; s64 status; @@ -654,8 +658,13 @@ get_max_cacheline_size (void) line_size = 1 << cci.pcci_line_size; if (line_size > max) max = line_size; + if (cache_size < cci.pcci_cache_size) + cache_size = cci.pcci_cache_size; } out: +#ifdef CONFIG_SMP + max_cache_size = max(max_cache_size, cache_size); +#endif if (max > ia64_max_cacheline_size) ia64_max_cacheline_size = max; } @@ -783,6 +792,7 @@ cpu_init (void) /* size of physical stacked register partition plus 8 bytes: */ __get_cpu_var(ia64_phys_stacked_size_p8) = num_phys_stacked*8 + 8; platform_cpu_init(); + pm_idle = default_idle; } void @@ -791,3 +801,12 @@ check_bugs (void) ia64_patch_mckinley_e9((unsigned long) __start___mckinley_e9_bundles, (unsigned long) __end___mckinley_e9_bundles); } + +/* + * On SMP systems, when the scheduler does migration-cost autodetection, + * it needs a way to flush as much of the CPU's caches as possible. + */ +void sched_cacheflush(void) +{ + ia64_sal_cache_flush(3); +} --- linux-2.6.13-rc2/arch/ia64/kernel/signal.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/signal.c 2005-07-12 00:26:31.000000000 -0700 @@ -143,6 +143,7 @@ restore_sigcontext (struct sigcontext __ __copy_from_user(current->thread.fph, &sc->sc_fr[32], 96*16); psr->mfh = 0; /* drop signal handler's fph contents... */ + preempt_disable(); if (psr->dfh) ia64_drop_fpu(current); else { @@ -150,6 +151,7 @@ restore_sigcontext (struct sigcontext __ __ia64_load_fpu(current->thread.fph); ia64_set_local_fpu_owner(current); } + preempt_enable(); } return err; } --- linux-2.6.13-rc2/arch/ia64/kernel/smpboot.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/smpboot.c 2005-07-12 00:26:31.000000000 -0700 @@ -525,47 +525,6 @@ smp_build_cpu_map (void) } } -#ifdef CONFIG_NUMA - -/* on which node is each logical CPU (one cacheline even for 64 CPUs) */ -u8 cpu_to_node_map[NR_CPUS] __cacheline_aligned; -EXPORT_SYMBOL(cpu_to_node_map); -/* which logical CPUs are on which nodes */ -cpumask_t node_to_cpu_mask[MAX_NUMNODES] __cacheline_aligned; - -/* - * Build cpu to node mapping and initialize the per node cpu masks. - */ -void __init -build_cpu_to_node_map (void) -{ - int cpu, i, node; - - for(node=0; node= 0) ? node : 0; - if (node >= 0) - cpu_set(cpu, node_to_cpu_mask[node]); - } -} - -#endif /* CONFIG_NUMA */ - /* * Cycle through the APs sending Wakeup IPIs to boot each. */ --- linux-2.6.13-rc2/arch/ia64/kernel/topology.c 2005-03-01 23:38:38.000000000 -0800 +++ devel/arch/ia64/kernel/topology.c 2005-07-12 00:26:38.000000000 -0700 @@ -36,6 +36,13 @@ int arch_register_cpu(int num) parent = &sysfs_nodes[cpu_to_node(num)]; #endif /* CONFIG_NUMA */ + /* + * If CPEI cannot be re-targetted, and this is + * CPEI target, then dont create the control file + */ + if (!can_cpei_retarget() && is_cpu_cpei_target(num)) + sysfs_cpus[num].cpu.no_control = 1; + return register_cpu(&sysfs_cpus[num].cpu, num, parent); } --- linux-2.6.13-rc2/arch/ia64/kernel/traps.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/kernel/traps.c 2005-07-12 00:26:31.000000000 -0700 @@ -90,14 +90,16 @@ die (const char *str, struct pt_regs *re .lock_owner_depth = 0 }; static int die_counter; + int cpu = get_cpu(); - if (die.lock_owner != smp_processor_id()) { + if (die.lock_owner != cpu) { console_verbose(); spin_lock_irq(&die.lock); - die.lock_owner = smp_processor_id(); + die.lock_owner = cpu; die.lock_owner_depth = 0; bust_spinlocks(1); } + put_cpu(); if (++die.lock_owner_depth < 3) { printk("%s[%d]: %s %ld [%d]\n", --- linux-2.6.13-rc2/arch/ia64/mm/discontig.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/mm/discontig.c 2005-07-12 00:26:31.000000000 -0700 @@ -44,150 +44,7 @@ struct early_node_data { }; static struct early_node_data mem_data[MAX_NUMNODES] __initdata; - -/** - * reassign_cpu_only_nodes - called from find_memory to move CPU-only nodes to a memory node - * - * This function will move nodes with only CPUs (no memory) - * to a node with memory which is at the minimum numa_slit distance. - * Any reassigments will result in the compression of the nodes - * and renumbering the nid values where appropriate. - * The static declarations below are to avoid large stack size which - * makes the code not re-entrant. - */ -static void __init reassign_cpu_only_nodes(void) -{ - struct node_memblk_s *p; - int i, j, k, nnode, nid, cpu, cpunid, pxm; - u8 cslit, slit; - static DECLARE_BITMAP(nodes_with_mem, MAX_NUMNODES) __initdata; - static u8 numa_slit_fix[MAX_NUMNODES * MAX_NUMNODES] __initdata; - static int node_flip[MAX_NUMNODES] __initdata; - static int old_nid_map[NR_CPUS] __initdata; - - for (nnode = 0, p = &node_memblk[0]; p < &node_memblk[num_node_memblks]; p++) - if (!test_bit(p->nid, (void *) nodes_with_mem)) { - set_bit(p->nid, (void *) nodes_with_mem); - nnode++; - } - - /* - * All nids with memory. - */ - if (nnode == num_online_nodes()) - return; - - /* - * Change nids and attempt to migrate CPU-only nodes - * to the best numa_slit (closest neighbor) possible. - * For reassigned CPU nodes a nid can't be arrived at - * until after this loop because the target nid's new - * identity might not have been established yet. So - * new nid values are fabricated above num_online_nodes() and - * mapped back later to their true value. - */ - /* MCD - This code is a bit complicated, but may be unnecessary now. - * We can now handle much more interesting node-numbering. - * The old requirement that 0 <= nid <= numnodes <= MAX_NUMNODES - * and that there be no holes in the numbering 0..numnodes - * has become simply 0 <= nid <= MAX_NUMNODES. - */ - nid = 0; - for_each_online_node(i) { - if (test_bit(i, (void *) nodes_with_mem)) { - /* - * Save original nid value for numa_slit - * fixup and node_cpuid reassignments. - */ - node_flip[nid] = i; - - if (i == nid) { - nid++; - continue; - } - - for (p = &node_memblk[0]; p < &node_memblk[num_node_memblks]; p++) - if (p->nid == i) - p->nid = nid; - - cpunid = nid; - nid++; - } else - cpunid = MAX_NUMNODES; - - for (cpu = 0; cpu < NR_CPUS; cpu++) - if (node_cpuid[cpu].nid == i) { - /* - * For nodes not being reassigned just - * fix the cpu's nid and reverse pxm map - */ - if (cpunid < MAX_NUMNODES) { - pxm = nid_to_pxm_map[i]; - pxm_to_nid_map[pxm] = - node_cpuid[cpu].nid = cpunid; - continue; - } - - /* - * For nodes being reassigned, find best node by - * numa_slit information and then make a temporary - * nid value based on current nid and num_online_nodes(). - */ - slit = 0xff; - k = 2*num_online_nodes(); - for_each_online_node(j) { - if (i == j) - continue; - else if (test_bit(j, (void *) nodes_with_mem)) { - cslit = numa_slit[i * num_online_nodes() + j]; - if (cslit < slit) { - k = num_online_nodes() + j; - slit = cslit; - } - } - } - - /* save old nid map so we can update the pxm */ - old_nid_map[cpu] = node_cpuid[cpu].nid; - node_cpuid[cpu].nid = k; - } - } - - /* - * Fixup temporary nid values for CPU-only nodes. - */ - for (cpu = 0; cpu < NR_CPUS; cpu++) - if (node_cpuid[cpu].nid == (2*num_online_nodes())) { - pxm = nid_to_pxm_map[old_nid_map[cpu]]; - pxm_to_nid_map[pxm] = node_cpuid[cpu].nid = nnode - 1; - } else { - for (i = 0; i < nnode; i++) { - if (node_flip[i] != (node_cpuid[cpu].nid - num_online_nodes())) - continue; - - pxm = nid_to_pxm_map[old_nid_map[cpu]]; - pxm_to_nid_map[pxm] = node_cpuid[cpu].nid = i; - break; - } - } - - /* - * Fix numa_slit by compressing from larger - * nid array to reduced nid array. - */ - for (i = 0; i < nnode; i++) - for (j = 0; j < nnode; j++) - numa_slit_fix[i * nnode + j] = - numa_slit[node_flip[i] * num_online_nodes() + node_flip[j]]; - - memcpy(numa_slit, numa_slit_fix, sizeof (numa_slit)); - - nodes_clear(node_online_map); - for (i = 0; i < nnode; i++) - node_set_online(i); - - return; -} +static nodemask_t memory_less_mask __initdata; /* * To prevent cache aliasing effects, align per-node structures so that they @@ -233,44 +90,101 @@ static int __init build_node_maps(unsign } /** - * early_nr_phys_cpus_node - return number of physical cpus on a given node + * early_nr_cpus_node - return number of cpus on a given node * @node: node to check * - * Count the number of physical cpus on @node. These are cpus that actually - * exist. We can't use nr_cpus_node() yet because + * Count the number of cpus on @node. We can't use nr_cpus_node() yet because * acpi_boot_init() (which builds the node_to_cpu_mask array) hasn't been - * called yet. + * called yet. Note that node 0 will also count all non-existent cpus. */ -static int early_nr_phys_cpus_node(int node) +static int __init early_nr_cpus_node(int node) { int cpu, n = 0; for (cpu = 0; cpu < NR_CPUS; cpu++) if (node == node_cpuid[cpu].nid) - if ((cpu == 0) || node_cpuid[cpu].phys_id) - n++; + n++; return n; } +/** + * compute_pernodesize - compute size of pernode data + * @node: the node id. + */ +static unsigned long __init compute_pernodesize(int node) +{ + unsigned long pernodesize = 0, cpus; + + cpus = early_nr_cpus_node(node); + pernodesize += PERCPU_PAGE_SIZE * cpus; + pernodesize += node * L1_CACHE_BYTES; + pernodesize += L1_CACHE_ALIGN(sizeof(pg_data_t)); + pernodesize += L1_CACHE_ALIGN(sizeof(struct ia64_node_data)); + pernodesize = PAGE_ALIGN(pernodesize); + return pernodesize; +} /** - * early_nr_cpus_node - return number of cpus on a given node - * @node: node to check + * per_cpu_node_setup - setup per-cpu areas on each node + * @cpu_data: per-cpu area on this node + * @node: node to setup * - * Count the number of cpus on @node. We can't use nr_cpus_node() yet because - * acpi_boot_init() (which builds the node_to_cpu_mask array) hasn't been - * called yet. Note that node 0 will also count all non-existent cpus. + * Copy the static per-cpu data into the region we just set aside and then + * setup __per_cpu_offset for each CPU on this node. Return a pointer to + * the end of the area. */ -static int early_nr_cpus_node(int node) +static void *per_cpu_node_setup(void *cpu_data, int node) { - int cpu, n = 0; +#ifdef CONFIG_SMP + int cpu; - for (cpu = 0; cpu < NR_CPUS; cpu++) - if (node == node_cpuid[cpu].nid) - n++; + for (cpu = 0; cpu < NR_CPUS; cpu++) { + if (node == node_cpuid[cpu].nid) { + memcpy(__va(cpu_data), __phys_per_cpu_start, + __per_cpu_end - __per_cpu_start); + __per_cpu_offset[cpu] = (char*)__va(cpu_data) - + __per_cpu_start; + cpu_data += PERCPU_PAGE_SIZE; + } + } +#endif + return cpu_data; +} - return n; +/** + * fill_pernode - initialize pernode data. + * @node: the node id. + * @pernode: physical address of pernode data + * @pernodesize: size of the pernode data + */ +static void __init fill_pernode(int node, unsigned long pernode, + unsigned long pernodesize) +{ + void *cpu_data; + int cpus = early_nr_cpus_node(node); + struct bootmem_data *bdp = &mem_data[node].bootmem_data; + + mem_data[node].pernode_addr = pernode; + mem_data[node].pernode_size = pernodesize; + memset(__va(pernode), 0, pernodesize); + + cpu_data = (void *)pernode; + pernode += PERCPU_PAGE_SIZE * cpus; + pernode += node * L1_CACHE_BYTES; + + mem_data[node].pgdat = __va(pernode); + pernode += L1_CACHE_ALIGN(sizeof(pg_data_t)); + + mem_data[node].node_data = __va(pernode); + pernode += L1_CACHE_ALIGN(sizeof(struct ia64_node_data)); + + mem_data[node].pgdat->bdata = bdp; + pernode += L1_CACHE_ALIGN(sizeof(pg_data_t)); + + cpu_data = per_cpu_node_setup(cpu_data, node); + + return; } /** @@ -304,9 +218,8 @@ static int early_nr_cpus_node(int node) static int __init find_pernode_space(unsigned long start, unsigned long len, int node) { - unsigned long epfn, cpu, cpus, phys_cpus; + unsigned long epfn; unsigned long pernodesize = 0, pernode, pages, mapsize; - void *cpu_data; struct bootmem_data *bdp = &mem_data[node].bootmem_data; epfn = (start + len) >> PAGE_SHIFT; @@ -329,49 +242,12 @@ static int __init find_pernode_space(uns * Calculate total size needed, incl. what's necessary * for good alignment and alias prevention. */ - cpus = early_nr_cpus_node(node); - phys_cpus = early_nr_phys_cpus_node(node); - pernodesize += PERCPU_PAGE_SIZE * cpus; - pernodesize += node * L1_CACHE_BYTES; - pernodesize += L1_CACHE_ALIGN(sizeof(pg_data_t)); - pernodesize += L1_CACHE_ALIGN(sizeof(struct ia64_node_data)); - pernodesize = PAGE_ALIGN(pernodesize); + pernodesize = compute_pernodesize(node); pernode = NODEDATA_ALIGN(start, node); /* Is this range big enough for what we want to store here? */ - if (start + len > (pernode + pernodesize + mapsize)) { - mem_data[node].pernode_addr = pernode; - mem_data[node].pernode_size = pernodesize; - memset(__va(pernode), 0, pernodesize); - - cpu_data = (void *)pernode; - pernode += PERCPU_PAGE_SIZE * cpus; - pernode += node * L1_CACHE_BYTES; - - mem_data[node].pgdat = __va(pernode); - pernode += L1_CACHE_ALIGN(sizeof(pg_data_t)); - - mem_data[node].node_data = __va(pernode); - pernode += L1_CACHE_ALIGN(sizeof(struct ia64_node_data)); - - mem_data[node].pgdat->bdata = bdp; - pernode += L1_CACHE_ALIGN(sizeof(pg_data_t)); - - /* - * Copy the static per-cpu data into the region we - * just set aside and then setup __per_cpu_offset - * for each CPU on this node. - */ - for (cpu = 0; cpu < NR_CPUS; cpu++) { - if (node == node_cpuid[cpu].nid) { - memcpy(__va(cpu_data), __phys_per_cpu_start, - __per_cpu_end - __per_cpu_start); - __per_cpu_offset[cpu] = (char*)__va(cpu_data) - - __per_cpu_start; - cpu_data += PERCPU_PAGE_SIZE; - } - } - } + if (start + len > (pernode + pernodesize + mapsize)) + fill_pernode(node, pernode, pernodesize); return 0; } @@ -411,6 +287,9 @@ static void __init reserve_pernode_space for_each_online_node(node) { pg_data_t *pdp = mem_data[node].pgdat; + if (node_isset(node, memory_less_mask)) + continue; + bdp = pdp->bdata; /* First the bootmem_map itself */ @@ -436,8 +315,8 @@ static void __init reserve_pernode_space */ static void __init initialize_pernode_data(void) { - int cpu, node; pg_data_t *pgdat_list[MAX_NUMNODES]; + int cpu, node; for_each_online_node(node) pgdat_list[node] = mem_data[node].pgdat; @@ -447,12 +326,99 @@ static void __init initialize_pernode_da memcpy(mem_data[node].node_data->pg_data_ptrs, pgdat_list, sizeof(pgdat_list)); } - +#ifdef CONFIG_SMP /* Set the node_data pointer for each per-cpu struct */ for (cpu = 0; cpu < NR_CPUS; cpu++) { node = node_cpuid[cpu].nid; per_cpu(cpu_info, cpu).node_data = mem_data[node].node_data; } +#else + { + struct cpuinfo_ia64 *cpu0_cpu_info; + cpu = 0; + node = node_cpuid[cpu].nid; + cpu0_cpu_info = (struct cpuinfo_ia64 *)(__phys_per_cpu_start + + ((char *)&per_cpu__cpu_info - __per_cpu_start)); + cpu0_cpu_info->node_data = mem_data[node].node_data; + } +#endif /* CONFIG_SMP */ +} + +/** + * memory_less_node_alloc - * attempt to allocate memory on the best NUMA slit + * node but fall back to any other node when __alloc_bootmem_node fails + * for best. + * @nid: node id + * @pernodesize: size of this node's pernode data + * @align: alignment to use for this node's pernode data + */ +static void __init *memory_less_node_alloc(int nid, unsigned long pernodesize, + unsigned long align) +{ + void *ptr = NULL; + u8 best = 0xff; + int bestnode = -1, node; + + for_each_online_node(node) { + if (node_isset(node, memory_less_mask)) + continue; + else if (node_distance(nid, node) < best) { + best = node_distance(nid, node); + bestnode = node; + } + } + + ptr = __alloc_bootmem_node(mem_data[bestnode].pgdat, + pernodesize, align, __pa(MAX_DMA_ADDRESS)); + + if (!ptr) + panic("NO memory for memory less node\n"); + return ptr; +} + +/** + * pgdat_insert - insert the pgdat into global pgdat_list + * @pgdat: the pgdat for a node. + */ +static void __init pgdat_insert(pg_data_t *pgdat) +{ + pg_data_t *prev = NULL, *next; + + for_each_pgdat(next) + if (pgdat->node_id < next->node_id) + break; + else + prev = next; + + if (prev) { + prev->pgdat_next = pgdat; + pgdat->pgdat_next = next; + } else { + pgdat->pgdat_next = pgdat_list; + pgdat_list = pgdat; + } + + return; +} + +/** + * memory_less_nodes - allocate and initialize CPU only nodes pernode + * information. + */ +static void __init memory_less_nodes(void) +{ + unsigned long pernodesize; + void *pernode; + int node; + + for_each_node_mask(node, memory_less_mask) { + pernodesize = compute_pernodesize(node); + pernode = memory_less_node_alloc(node, pernodesize, + (node) ? (node * PERCPU_PAGE_SIZE) : (1024*1024)); + fill_pernode(node, __pa(pernode), pernodesize); + } + + return; } /** @@ -472,16 +438,19 @@ void __init find_memory(void) node_set_online(0); } + nodes_or(memory_less_mask, memory_less_mask, node_online_map); min_low_pfn = -1; max_low_pfn = 0; - if (num_online_nodes() > 1) - reassign_cpu_only_nodes(); - /* These actually end up getting called by call_pernode_memory() */ efi_memmap_walk(filter_rsvd_memory, build_node_maps); efi_memmap_walk(filter_rsvd_memory, find_pernode_space); + for_each_online_node(node) + if (mem_data[node].bootmem_data.node_low_pfn) { + node_clear(node, memory_less_mask); + mem_data[node].min_pfn = ~0UL; + } /* * Initialize the boot memory maps in reverse order since that's * what the bootmem allocator expects @@ -492,17 +461,14 @@ void __init find_memory(void) if (!node_online(node)) continue; + else if (node_isset(node, memory_less_mask)) + continue; bdp = &mem_data[node].bootmem_data; pernode = mem_data[node].pernode_addr; pernodesize = mem_data[node].pernode_size; map = pernode + pernodesize; - /* Sanity check... */ - if (!pernode) - panic("pernode space for node %d " - "could not be allocated!", node); - init_bootmem_node(mem_data[node].pgdat, map>>PAGE_SHIFT, bdp->node_boot_start>>PAGE_SHIFT, @@ -512,6 +478,7 @@ void __init find_memory(void) efi_memmap_walk(filter_rsvd_memory, free_node_bootmem); reserve_pernode_space(); + memory_less_nodes(); initialize_pernode_data(); max_pfn = max_low_pfn; @@ -519,6 +486,7 @@ void __init find_memory(void) find_initrd(); } +#ifdef CONFIG_SMP /** * per_cpu_init - setup per-cpu variables * @@ -529,15 +497,15 @@ void *per_cpu_init(void) { int cpu; - if (smp_processor_id() == 0) { - for (cpu = 0; cpu < NR_CPUS; cpu++) { - per_cpu(local_per_cpu_offset, cpu) = - __per_cpu_offset[cpu]; - } - } + if (smp_processor_id() != 0) + return __per_cpu_start + __per_cpu_offset[smp_processor_id()]; + + for (cpu = 0; cpu < NR_CPUS; cpu++) + per_cpu(local_per_cpu_offset, cpu) = __per_cpu_offset[cpu]; return __per_cpu_start + __per_cpu_offset[smp_processor_id()]; } +#endif /* CONFIG_SMP */ /** * show_mem - give short summary of memory stats @@ -680,12 +648,13 @@ void __init paging_init(void) max_dma = virt_to_phys((void *) MAX_DMA_ADDRESS) >> PAGE_SHIFT; - /* so min() will work in count_node_pages */ - for_each_online_node(node) - mem_data[node].min_pfn = ~0UL; - efi_memmap_walk(filter_rsvd_memory, count_node_pages); + vmalloc_end -= PAGE_ALIGN(max_low_pfn * sizeof(struct page)); + vmem_map = (struct page *) vmalloc_end; + efi_memmap_walk(create_mem_map_page_table, NULL); + printk("Virtual mem_map starts at 0x%p\n", vmem_map); + for_each_online_node(node) { memset(zones_size, 0, sizeof(zones_size)); memset(zholes_size, 0, sizeof(zholes_size)); @@ -719,15 +688,6 @@ void __init paging_init(void) mem_data[node].num_dma_physpages); } - if (node == 0) { - vmalloc_end -= - PAGE_ALIGN(max_low_pfn * sizeof(struct page)); - vmem_map = (struct page *) vmalloc_end; - - efi_memmap_walk(create_mem_map_page_table, NULL); - printk("Virtual mem_map starts at 0x%p\n", vmem_map); - } - pfn_offset = mem_data[node].min_pfn; NODE_DATA(node)->node_mem_map = vmem_map + pfn_offset; @@ -735,5 +695,11 @@ void __init paging_init(void) pfn_offset, zholes_size); } + /* + * Make memory less nodes become a member of the known nodes. + */ + for_each_node_mask(node, memory_less_mask) + pgdat_insert(mem_data[node].pgdat); + zero_page_memmap_ptr = virt_to_page(ia64_imva(empty_zero_page)); } --- linux-2.6.13-rc2/arch/ia64/mm/init.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/mm/init.c 2005-07-12 00:26:31.000000000 -0700 @@ -597,7 +597,8 @@ mem_init (void) kclist_add(&kcore_kernel, _stext, _end - _stext); for_each_pgdat(pgdat) - totalram_pages += free_all_bootmem_node(pgdat); + if (pgdat->bdata->node_bootmem_map) + totalram_pages += free_all_bootmem_node(pgdat); reserved_pages = 0; efi_memmap_walk(count_reserved_pages, &reserved_pages); --- linux-2.6.13-rc2/arch/ia64/sn/include/pci/pcibr_provider.h 2005-06-17 16:03:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,151 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 1992-1997,2000-2004 Silicon Graphics, Inc. All rights reserved. - */ -#ifndef _ASM_IA64_SN_PCI_PCIBR_PROVIDER_H -#define _ASM_IA64_SN_PCI_PCIBR_PROVIDER_H - -/* Workarounds */ -#define PV907516 (1 << 1) /* TIOCP: Don't write the write buffer flush reg */ - -#define BUSTYPE_MASK 0x1 - -/* Macros given a pcibus structure */ -#define IS_PCIX(ps) ((ps)->pbi_bridge_mode & BUSTYPE_MASK) -#define IS_PCI_BRIDGE_ASIC(asic) (asic == PCIIO_ASIC_TYPE_PIC || \ - asic == PCIIO_ASIC_TYPE_TIOCP) -#define IS_PIC_SOFT(ps) (ps->pbi_bridge_type == PCIBR_BRIDGETYPE_PIC) - - -/* - * The different PCI Bridge types supported on the SGI Altix platforms - */ -#define PCIBR_BRIDGETYPE_UNKNOWN -1 -#define PCIBR_BRIDGETYPE_PIC 2 -#define PCIBR_BRIDGETYPE_TIOCP 3 - -/* - * Bridge 64bit Direct Map Attributes - */ -#define PCI64_ATTR_PREF (1ull << 59) -#define PCI64_ATTR_PREC (1ull << 58) -#define PCI64_ATTR_VIRTUAL (1ull << 57) -#define PCI64_ATTR_BAR (1ull << 56) -#define PCI64_ATTR_SWAP (1ull << 55) -#define PCI64_ATTR_VIRTUAL1 (1ull << 54) - -#define PCI32_LOCAL_BASE 0 -#define PCI32_MAPPED_BASE 0x40000000 -#define PCI32_DIRECT_BASE 0x80000000 - -#define IS_PCI32_MAPPED(x) ((uint64_t)(x) < PCI32_DIRECT_BASE && \ - (uint64_t)(x) >= PCI32_MAPPED_BASE) -#define IS_PCI32_DIRECT(x) ((uint64_t)(x) >= PCI32_MAPPED_BASE) - - -/* - * Bridge PMU Address Transaltion Entry Attibutes - */ -#define PCI32_ATE_V (0x1 << 0) -#define PCI32_ATE_CO (0x1 << 1) -#define PCI32_ATE_PREC (0x1 << 2) -#define PCI32_ATE_PREF (0x1 << 3) -#define PCI32_ATE_BAR (0x1 << 4) -#define PCI32_ATE_ADDR_SHFT 12 - -#define MINIMAL_ATES_REQUIRED(addr, size) \ - (IOPG(IOPGOFF(addr) + (size) - 1) == IOPG((size) - 1)) - -#define MINIMAL_ATE_FLAG(addr, size) \ - (MINIMAL_ATES_REQUIRED((uint64_t)addr, size) ? 1 : 0) - -/* bit 29 of the pci address is the SWAP bit */ -#define ATE_SWAPSHIFT 29 -#define ATE_SWAP_ON(x) ((x) |= (1 << ATE_SWAPSHIFT)) -#define ATE_SWAP_OFF(x) ((x) &= ~(1 << ATE_SWAPSHIFT)) - -/* - * I/O page size - */ -#if PAGE_SIZE < 16384 -#define IOPFNSHIFT 12 /* 4K per mapped page */ -#else -#define IOPFNSHIFT 14 /* 16K per mapped page */ -#endif - -#define IOPGSIZE (1 << IOPFNSHIFT) -#define IOPG(x) ((x) >> IOPFNSHIFT) -#define IOPGOFF(x) ((x) & (IOPGSIZE-1)) - -#define PCIBR_DEV_SWAP_DIR (1ull << 19) -#define PCIBR_CTRL_PAGE_SIZE (0x1 << 21) - -/* - * PMU resources. - */ -struct ate_resource{ - uint64_t *ate; - uint64_t num_ate; - uint64_t lowest_free_index; -}; - -struct pcibus_info { - struct pcibus_bussoft pbi_buscommon; /* common header */ - uint32_t pbi_moduleid; - short pbi_bridge_type; - short pbi_bridge_mode; - - struct ate_resource pbi_int_ate_resource; - uint64_t pbi_int_ate_size; - - uint64_t pbi_dir_xbase; - char pbi_hub_xid; - - uint64_t pbi_devreg[8]; - spinlock_t pbi_lock; - - uint32_t pbi_valid_devices; - uint32_t pbi_enabled_devices; -}; - -/* - * pcibus_info structure locking macros - */ -inline static unsigned long -pcibr_lock(struct pcibus_info *pcibus_info) -{ - unsigned long flag; - spin_lock_irqsave(&pcibus_info->pbi_lock, flag); - return(flag); -} -#define pcibr_unlock(pcibus_info, flag) spin_unlock_irqrestore(&pcibus_info->pbi_lock, flag) - -extern int pcibr_init_provider(void); -extern void *pcibr_bus_fixup(struct pcibus_bussoft *); -extern dma_addr_t pcibr_dma_map(struct pci_dev *, unsigned long, size_t); -extern dma_addr_t pcibr_dma_map_consistent(struct pci_dev *, unsigned long, size_t); -extern void pcibr_dma_unmap(struct pci_dev *, dma_addr_t, int); - -/* - * prototypes for the bridge asic register access routines in pcibr_reg.c - */ -extern void pcireg_control_bit_clr(struct pcibus_info *, uint64_t); -extern void pcireg_control_bit_set(struct pcibus_info *, uint64_t); -extern uint64_t pcireg_tflush_get(struct pcibus_info *); -extern uint64_t pcireg_intr_status_get(struct pcibus_info *); -extern void pcireg_intr_enable_bit_clr(struct pcibus_info *, uint64_t); -extern void pcireg_intr_enable_bit_set(struct pcibus_info *, uint64_t); -extern void pcireg_intr_addr_addr_set(struct pcibus_info *, int, uint64_t); -extern void pcireg_force_intr_set(struct pcibus_info *, int); -extern uint64_t pcireg_wrb_flush_get(struct pcibus_info *, int); -extern void pcireg_int_ate_set(struct pcibus_info *, int, uint64_t); -extern uint64_t * pcireg_int_ate_addr(struct pcibus_info *, int); -extern void pcibr_force_interrupt(struct sn_irq_info *sn_irq_info); -extern void pcibr_change_devices_irq(struct sn_irq_info *sn_irq_info); -extern int pcibr_ate_alloc(struct pcibus_info *, int); -extern void pcibr_ate_free(struct pcibus_info *, int); -extern void ate_write(struct pcibus_info *, int, int, uint64_t); -#endif --- linux-2.6.13-rc2/arch/ia64/sn/include/pci/pic.h 2005-03-01 23:38:09.000000000 -0800 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,261 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved. - */ -#ifndef _ASM_IA64_SN_PCI_PIC_H -#define _ASM_IA64_SN_PCI_PIC_H - -/* - * PIC AS DEVICE ZERO - * ------------------ - * - * PIC handles PCI/X busses. PCI/X requires that the 'bridge' (i.e. PIC) - * be designated as 'device 0'. That is a departure from earlier SGI - * PCI bridges. Because of that we use config space 1 to access the - * config space of the first actual PCI device on the bus. - * Here's what the PIC manual says: - * - * The current PCI-X bus specification now defines that the parent - * hosts bus bridge (PIC for example) must be device 0 on bus 0. PIC - * reduced the total number of devices from 8 to 4 and removed the - * device registers and windows, now only supporting devices 0,1,2, and - * 3. PIC did leave all 8 configuration space windows. The reason was - * there was nothing to gain by removing them. Here in lies the problem. - * The device numbering we do using 0 through 3 is unrelated to the device - * numbering which PCI-X requires in configuration space. In the past we - * correlated Configs pace and our device space 0 <-> 0, 1 <-> 1, etc. - * PCI-X requires we start a 1, not 0 and currently the PX brick - * does associate our: - * - * device 0 with configuration space window 1, - * device 1 with configuration space window 2, - * device 2 with configuration space window 3, - * device 3 with configuration space window 4. - * - * The net effect is that all config space access are off-by-one with - * relation to other per-slot accesses on the PIC. - * Here is a table that shows some of that: - * - * Internal Slot# - * | - * | 0 1 2 3 - * ----------|--------------------------------------- - * config | 0x21000 0x22000 0x23000 0x24000 - * | - * even rrb | 0[0] n/a 1[0] n/a [] == implied even/odd - * | - * odd rrb | n/a 0[1] n/a 1[1] - * | - * int dev | 00 01 10 11 - * | - * ext slot# | 1 2 3 4 - * ----------|--------------------------------------- - */ - -#define PIC_ATE_TARGETID_SHFT 8 -#define PIC_HOST_INTR_ADDR 0x0000FFFFFFFFFFFFUL -#define PIC_PCI64_ATTR_TARG_SHFT 60 - - -/***************************************************************************** - *********************** PIC MMR structure mapping *************************** - *****************************************************************************/ - -/* NOTE: PIC WAR. PV#854697. PIC does not allow writes just to [31:0] - * of a 64-bit register. When writing PIC registers, always write the - * entire 64 bits. - */ - -struct pic { - - /* 0x000000-0x00FFFF -- Local Registers */ - - /* 0x000000-0x000057 -- Standard Widget Configuration */ - uint64_t p_wid_id; /* 0x000000 */ - uint64_t p_wid_stat; /* 0x000008 */ - uint64_t p_wid_err_upper; /* 0x000010 */ - uint64_t p_wid_err_lower; /* 0x000018 */ - #define p_wid_err p_wid_err_lower - uint64_t p_wid_control; /* 0x000020 */ - uint64_t p_wid_req_timeout; /* 0x000028 */ - uint64_t p_wid_int_upper; /* 0x000030 */ - uint64_t p_wid_int_lower; /* 0x000038 */ - #define p_wid_int p_wid_int_lower - uint64_t p_wid_err_cmdword; /* 0x000040 */ - uint64_t p_wid_llp; /* 0x000048 */ - uint64_t p_wid_tflush; /* 0x000050 */ - - /* 0x000058-0x00007F -- Bridge-specific Widget Configuration */ - uint64_t p_wid_aux_err; /* 0x000058 */ - uint64_t p_wid_resp_upper; /* 0x000060 */ - uint64_t p_wid_resp_lower; /* 0x000068 */ - #define p_wid_resp p_wid_resp_lower - uint64_t p_wid_tst_pin_ctrl; /* 0x000070 */ - uint64_t p_wid_addr_lkerr; /* 0x000078 */ - - /* 0x000080-0x00008F -- PMU & MAP */ - uint64_t p_dir_map; /* 0x000080 */ - uint64_t _pad_000088; /* 0x000088 */ - - /* 0x000090-0x00009F -- SSRAM */ - uint64_t p_map_fault; /* 0x000090 */ - uint64_t _pad_000098; /* 0x000098 */ - - /* 0x0000A0-0x0000AF -- Arbitration */ - uint64_t p_arb; /* 0x0000A0 */ - uint64_t _pad_0000A8; /* 0x0000A8 */ - - /* 0x0000B0-0x0000BF -- Number In A Can or ATE Parity Error */ - uint64_t p_ate_parity_err; /* 0x0000B0 */ - uint64_t _pad_0000B8; /* 0x0000B8 */ - - /* 0x0000C0-0x0000FF -- PCI/GIO */ - uint64_t p_bus_timeout; /* 0x0000C0 */ - uint64_t p_pci_cfg; /* 0x0000C8 */ - uint64_t p_pci_err_upper; /* 0x0000D0 */ - uint64_t p_pci_err_lower; /* 0x0000D8 */ - #define p_pci_err p_pci_err_lower - uint64_t _pad_0000E0[4]; /* 0x0000{E0..F8} */ - - /* 0x000100-0x0001FF -- Interrupt */ - uint64_t p_int_status; /* 0x000100 */ - uint64_t p_int_enable; /* 0x000108 */ - uint64_t p_int_rst_stat; /* 0x000110 */ - uint64_t p_int_mode; /* 0x000118 */ - uint64_t p_int_device; /* 0x000120 */ - uint64_t p_int_host_err; /* 0x000128 */ - uint64_t p_int_addr[8]; /* 0x0001{30,,,68} */ - uint64_t p_err_int_view; /* 0x000170 */ - uint64_t p_mult_int; /* 0x000178 */ - uint64_t p_force_always[8]; /* 0x0001{80,,,B8} */ - uint64_t p_force_pin[8]; /* 0x0001{C0,,,F8} */ - - /* 0x000200-0x000298 -- Device */ - uint64_t p_device[4]; /* 0x0002{00,,,18} */ - uint64_t _pad_000220[4]; /* 0x0002{20,,,38} */ - uint64_t p_wr_req_buf[4]; /* 0x0002{40,,,58} */ - uint64_t _pad_000260[4]; /* 0x0002{60,,,78} */ - uint64_t p_rrb_map[2]; /* 0x0002{80,,,88} */ - #define p_even_resp p_rrb_map[0] /* 0x000280 */ - #define p_odd_resp p_rrb_map[1] /* 0x000288 */ - uint64_t p_resp_status; /* 0x000290 */ - uint64_t p_resp_clear; /* 0x000298 */ - - uint64_t _pad_0002A0[12]; /* 0x0002{A0..F8} */ - - /* 0x000300-0x0003F8 -- Buffer Address Match Registers */ - struct { - uint64_t upper; /* 0x0003{00,,,F0} */ - uint64_t lower; /* 0x0003{08,,,F8} */ - } p_buf_addr_match[16]; - - /* 0x000400-0x0005FF -- Performance Monitor Registers (even only) */ - struct { - uint64_t flush_w_touch; /* 0x000{400,,,5C0} */ - uint64_t flush_wo_touch; /* 0x000{408,,,5C8} */ - uint64_t inflight; /* 0x000{410,,,5D0} */ - uint64_t prefetch; /* 0x000{418,,,5D8} */ - uint64_t total_pci_retry; /* 0x000{420,,,5E0} */ - uint64_t max_pci_retry; /* 0x000{428,,,5E8} */ - uint64_t max_latency; /* 0x000{430,,,5F0} */ - uint64_t clear_all; /* 0x000{438,,,5F8} */ - } p_buf_count[8]; - - - /* 0x000600-0x0009FF -- PCI/X registers */ - uint64_t p_pcix_bus_err_addr; /* 0x000600 */ - uint64_t p_pcix_bus_err_attr; /* 0x000608 */ - uint64_t p_pcix_bus_err_data; /* 0x000610 */ - uint64_t p_pcix_pio_split_addr; /* 0x000618 */ - uint64_t p_pcix_pio_split_attr; /* 0x000620 */ - uint64_t p_pcix_dma_req_err_attr; /* 0x000628 */ - uint64_t p_pcix_dma_req_err_addr; /* 0x000630 */ - uint64_t p_pcix_timeout; /* 0x000638 */ - - uint64_t _pad_000640[120]; /* 0x000{640,,,9F8} */ - - /* 0x000A00-0x000BFF -- PCI/X Read&Write Buffer */ - struct { - uint64_t p_buf_addr; /* 0x000{A00,,,AF0} */ - uint64_t p_buf_attr; /* 0X000{A08,,,AF8} */ - } p_pcix_read_buf_64[16]; - - struct { - uint64_t p_buf_addr; /* 0x000{B00,,,BE0} */ - uint64_t p_buf_attr; /* 0x000{B08,,,BE8} */ - uint64_t p_buf_valid; /* 0x000{B10,,,BF0} */ - uint64_t __pad1; /* 0x000{B18,,,BF8} */ - } p_pcix_write_buf_64[8]; - - /* End of Local Registers -- Start of Address Map space */ - - char _pad_000c00[0x010000 - 0x000c00]; - - /* 0x010000-0x011fff -- Internal ATE RAM (Auto Parity Generation) */ - uint64_t p_int_ate_ram[1024]; /* 0x010000-0x011fff */ - - /* 0x012000-0x013fff -- Internal ATE RAM (Manual Parity Generation) */ - uint64_t p_int_ate_ram_mp[1024]; /* 0x012000-0x013fff */ - - char _pad_014000[0x18000 - 0x014000]; - - /* 0x18000-0x197F8 -- PIC Write Request Ram */ - uint64_t p_wr_req_lower[256]; /* 0x18000 - 0x187F8 */ - uint64_t p_wr_req_upper[256]; /* 0x18800 - 0x18FF8 */ - uint64_t p_wr_req_parity[256]; /* 0x19000 - 0x197F8 */ - - char _pad_019800[0x20000 - 0x019800]; - - /* 0x020000-0x027FFF -- PCI Device Configuration Spaces */ - union { - uint8_t c[0x1000 / 1]; /* 0x02{0000,,,7FFF} */ - uint16_t s[0x1000 / 2]; /* 0x02{0000,,,7FFF} */ - uint32_t l[0x1000 / 4]; /* 0x02{0000,,,7FFF} */ - uint64_t d[0x1000 / 8]; /* 0x02{0000,,,7FFF} */ - union { - uint8_t c[0x100 / 1]; - uint16_t s[0x100 / 2]; - uint32_t l[0x100 / 4]; - uint64_t d[0x100 / 8]; - } f[8]; - } p_type0_cfg_dev[8]; /* 0x02{0000,,,7FFF} */ - - /* 0x028000-0x028FFF -- PCI Type 1 Configuration Space */ - union { - uint8_t c[0x1000 / 1]; /* 0x028000-0x029000 */ - uint16_t s[0x1000 / 2]; /* 0x028000-0x029000 */ - uint32_t l[0x1000 / 4]; /* 0x028000-0x029000 */ - uint64_t d[0x1000 / 8]; /* 0x028000-0x029000 */ - union { - uint8_t c[0x100 / 1]; - uint16_t s[0x100 / 2]; - uint32_t l[0x100 / 4]; - uint64_t d[0x100 / 8]; - } f[8]; - } p_type1_cfg; /* 0x028000-0x029000 */ - - char _pad_029000[0x030000-0x029000]; - - /* 0x030000-0x030007 -- PCI Interrupt Acknowledge Cycle */ - union { - uint8_t c[8 / 1]; - uint16_t s[8 / 2]; - uint32_t l[8 / 4]; - uint64_t d[8 / 8]; - } p_pci_iack; /* 0x030000-0x030007 */ - - char _pad_030007[0x040000-0x030008]; - - /* 0x040000-0x030007 -- PCIX Special Cycle */ - union { - uint8_t c[8 / 1]; - uint16_t s[8 / 2]; - uint32_t l[8 / 4]; - uint64_t d[8 / 8]; - } p_pcix_cycle; /* 0x040000-0x040007 */ -}; - -#endif /* _ASM_IA64_SN_PCI_PIC_H */ --- linux-2.6.13-rc2/arch/ia64/sn/include/pci/tiocp.h 2005-03-01 23:38:13.000000000 -0800 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,256 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2003-2004 Silicon Graphics, Inc. All rights reserved. - */ -#ifndef _ASM_IA64_SN_PCI_TIOCP_H -#define _ASM_IA64_SN_PCI_TIOCP_H - -#define TIOCP_HOST_INTR_ADDR 0x003FFFFFFFFFFFFFUL -#define TIOCP_PCI64_CMDTYPE_MEM (0x1ull << 60) - - -/***************************************************************************** - *********************** TIOCP MMR structure mapping *************************** - *****************************************************************************/ - -struct tiocp{ - - /* 0x000000-0x00FFFF -- Local Registers */ - - /* 0x000000-0x000057 -- (Legacy Widget Space) Configuration */ - uint64_t cp_id; /* 0x000000 */ - uint64_t cp_stat; /* 0x000008 */ - uint64_t cp_err_upper; /* 0x000010 */ - uint64_t cp_err_lower; /* 0x000018 */ - #define cp_err cp_err_lower - uint64_t cp_control; /* 0x000020 */ - uint64_t cp_req_timeout; /* 0x000028 */ - uint64_t cp_intr_upper; /* 0x000030 */ - uint64_t cp_intr_lower; /* 0x000038 */ - #define cp_intr cp_intr_lower - uint64_t cp_err_cmdword; /* 0x000040 */ - uint64_t _pad_000048; /* 0x000048 */ - uint64_t cp_tflush; /* 0x000050 */ - - /* 0x000058-0x00007F -- Bridge-specific Configuration */ - uint64_t cp_aux_err; /* 0x000058 */ - uint64_t cp_resp_upper; /* 0x000060 */ - uint64_t cp_resp_lower; /* 0x000068 */ - #define cp_resp cp_resp_lower - uint64_t cp_tst_pin_ctrl; /* 0x000070 */ - uint64_t cp_addr_lkerr; /* 0x000078 */ - - /* 0x000080-0x00008F -- PMU & MAP */ - uint64_t cp_dir_map; /* 0x000080 */ - uint64_t _pad_000088; /* 0x000088 */ - - /* 0x000090-0x00009F -- SSRAM */ - uint64_t cp_map_fault; /* 0x000090 */ - uint64_t _pad_000098; /* 0x000098 */ - - /* 0x0000A0-0x0000AF -- Arbitration */ - uint64_t cp_arb; /* 0x0000A0 */ - uint64_t _pad_0000A8; /* 0x0000A8 */ - - /* 0x0000B0-0x0000BF -- Number In A Can or ATE Parity Error */ - uint64_t cp_ate_parity_err; /* 0x0000B0 */ - uint64_t _pad_0000B8; /* 0x0000B8 */ - - /* 0x0000C0-0x0000FF -- PCI/GIO */ - uint64_t cp_bus_timeout; /* 0x0000C0 */ - uint64_t cp_pci_cfg; /* 0x0000C8 */ - uint64_t cp_pci_err_upper; /* 0x0000D0 */ - uint64_t cp_pci_err_lower; /* 0x0000D8 */ - #define cp_pci_err cp_pci_err_lower - uint64_t _pad_0000E0[4]; /* 0x0000{E0..F8} */ - - /* 0x000100-0x0001FF -- Interrupt */ - uint64_t cp_int_status; /* 0x000100 */ - uint64_t cp_int_enable; /* 0x000108 */ - uint64_t cp_int_rst_stat; /* 0x000110 */ - uint64_t cp_int_mode; /* 0x000118 */ - uint64_t cp_int_device; /* 0x000120 */ - uint64_t cp_int_host_err; /* 0x000128 */ - uint64_t cp_int_addr[8]; /* 0x0001{30,,,68} */ - uint64_t cp_err_int_view; /* 0x000170 */ - uint64_t cp_mult_int; /* 0x000178 */ - uint64_t cp_force_always[8]; /* 0x0001{80,,,B8} */ - uint64_t cp_force_pin[8]; /* 0x0001{C0,,,F8} */ - - /* 0x000200-0x000298 -- Device */ - uint64_t cp_device[4]; /* 0x0002{00,,,18} */ - uint64_t _pad_000220[4]; /* 0x0002{20,,,38} */ - uint64_t cp_wr_req_buf[4]; /* 0x0002{40,,,58} */ - uint64_t _pad_000260[4]; /* 0x0002{60,,,78} */ - uint64_t cp_rrb_map[2]; /* 0x0002{80,,,88} */ - #define cp_even_resp cp_rrb_map[0] /* 0x000280 */ - #define cp_odd_resp cp_rrb_map[1] /* 0x000288 */ - uint64_t cp_resp_status; /* 0x000290 */ - uint64_t cp_resp_clear; /* 0x000298 */ - - uint64_t _pad_0002A0[12]; /* 0x0002{A0..F8} */ - - /* 0x000300-0x0003F8 -- Buffer Address Match Registers */ - struct { - uint64_t upper; /* 0x0003{00,,,F0} */ - uint64_t lower; /* 0x0003{08,,,F8} */ - } cp_buf_addr_match[16]; - - /* 0x000400-0x0005FF -- Performance Monitor Registers (even only) */ - struct { - uint64_t flush_w_touch; /* 0x000{400,,,5C0} */ - uint64_t flush_wo_touch; /* 0x000{408,,,5C8} */ - uint64_t inflight; /* 0x000{410,,,5D0} */ - uint64_t prefetch; /* 0x000{418,,,5D8} */ - uint64_t total_pci_retry; /* 0x000{420,,,5E0} */ - uint64_t max_pci_retry; /* 0x000{428,,,5E8} */ - uint64_t max_latency; /* 0x000{430,,,5F0} */ - uint64_t clear_all; /* 0x000{438,,,5F8} */ - } cp_buf_count[8]; - - - /* 0x000600-0x0009FF -- PCI/X registers */ - uint64_t cp_pcix_bus_err_addr; /* 0x000600 */ - uint64_t cp_pcix_bus_err_attr; /* 0x000608 */ - uint64_t cp_pcix_bus_err_data; /* 0x000610 */ - uint64_t cp_pcix_pio_split_addr; /* 0x000618 */ - uint64_t cp_pcix_pio_split_attr; /* 0x000620 */ - uint64_t cp_pcix_dma_req_err_attr; /* 0x000628 */ - uint64_t cp_pcix_dma_req_err_addr; /* 0x000630 */ - uint64_t cp_pcix_timeout; /* 0x000638 */ - - uint64_t _pad_000640[24]; /* 0x000{640,,,6F8} */ - - /* 0x000700-0x000737 -- Debug Registers */ - uint64_t cp_ct_debug_ctl; /* 0x000700 */ - uint64_t cp_br_debug_ctl; /* 0x000708 */ - uint64_t cp_mux3_debug_ctl; /* 0x000710 */ - uint64_t cp_mux4_debug_ctl; /* 0x000718 */ - uint64_t cp_mux5_debug_ctl; /* 0x000720 */ - uint64_t cp_mux6_debug_ctl; /* 0x000728 */ - uint64_t cp_mux7_debug_ctl; /* 0x000730 */ - - uint64_t _pad_000738[89]; /* 0x000{738,,,9F8} */ - - /* 0x000A00-0x000BFF -- PCI/X Read&Write Buffer */ - struct { - uint64_t cp_buf_addr; /* 0x000{A00,,,AF0} */ - uint64_t cp_buf_attr; /* 0X000{A08,,,AF8} */ - } cp_pcix_read_buf_64[16]; - - struct { - uint64_t cp_buf_addr; /* 0x000{B00,,,BE0} */ - uint64_t cp_buf_attr; /* 0x000{B08,,,BE8} */ - uint64_t cp_buf_valid; /* 0x000{B10,,,BF0} */ - uint64_t __pad1; /* 0x000{B18,,,BF8} */ - } cp_pcix_write_buf_64[8]; - - /* End of Local Registers -- Start of Address Map space */ - - char _pad_000c00[0x010000 - 0x000c00]; - - /* 0x010000-0x011FF8 -- Internal ATE RAM (Auto Parity Generation) */ - uint64_t cp_int_ate_ram[1024]; /* 0x010000-0x011FF8 */ - - char _pad_012000[0x14000 - 0x012000]; - - /* 0x014000-0x015FF8 -- Internal ATE RAM (Manual Parity Generation) */ - uint64_t cp_int_ate_ram_mp[1024]; /* 0x014000-0x015FF8 */ - - char _pad_016000[0x18000 - 0x016000]; - - /* 0x18000-0x197F8 -- TIOCP Write Request Ram */ - uint64_t cp_wr_req_lower[256]; /* 0x18000 - 0x187F8 */ - uint64_t cp_wr_req_upper[256]; /* 0x18800 - 0x18FF8 */ - uint64_t cp_wr_req_parity[256]; /* 0x19000 - 0x197F8 */ - - char _pad_019800[0x1C000 - 0x019800]; - - /* 0x1C000-0x1EFF8 -- TIOCP Read Response Ram */ - uint64_t cp_rd_resp_lower[512]; /* 0x1C000 - 0x1CFF8 */ - uint64_t cp_rd_resp_upper[512]; /* 0x1D000 - 0x1DFF8 */ - uint64_t cp_rd_resp_parity[512]; /* 0x1E000 - 0x1EFF8 */ - - char _pad_01F000[0x20000 - 0x01F000]; - - /* 0x020000-0x021FFF -- Host Device (CP) Configuration Space (not used) */ - char _pad_020000[0x021000 - 0x20000]; - - /* 0x021000-0x027FFF -- PCI Device Configuration Spaces */ - union { - uint8_t c[0x1000 / 1]; /* 0x02{0000,,,7FFF} */ - uint16_t s[0x1000 / 2]; /* 0x02{0000,,,7FFF} */ - uint32_t l[0x1000 / 4]; /* 0x02{0000,,,7FFF} */ - uint64_t d[0x1000 / 8]; /* 0x02{0000,,,7FFF} */ - union { - uint8_t c[0x100 / 1]; - uint16_t s[0x100 / 2]; - uint32_t l[0x100 / 4]; - uint64_t d[0x100 / 8]; - } f[8]; - } cp_type0_cfg_dev[7]; /* 0x02{1000,,,7FFF} */ - - /* 0x028000-0x028FFF -- PCI Type 1 Configuration Space */ - union { - uint8_t c[0x1000 / 1]; /* 0x028000-0x029000 */ - uint16_t s[0x1000 / 2]; /* 0x028000-0x029000 */ - uint32_t l[0x1000 / 4]; /* 0x028000-0x029000 */ - uint64_t d[0x1000 / 8]; /* 0x028000-0x029000 */ - union { - uint8_t c[0x100 / 1]; - uint16_t s[0x100 / 2]; - uint32_t l[0x100 / 4]; - uint64_t d[0x100 / 8]; - } f[8]; - } cp_type1_cfg; /* 0x028000-0x029000 */ - - char _pad_029000[0x030000-0x029000]; - - /* 0x030000-0x030007 -- PCI Interrupt Acknowledge Cycle */ - union { - uint8_t c[8 / 1]; - uint16_t s[8 / 2]; - uint32_t l[8 / 4]; - uint64_t d[8 / 8]; - } cp_pci_iack; /* 0x030000-0x030007 */ - - char _pad_030007[0x040000-0x030008]; - - /* 0x040000-0x040007 -- PCIX Special Cycle */ - union { - uint8_t c[8 / 1]; - uint16_t s[8 / 2]; - uint32_t l[8 / 4]; - uint64_t d[8 / 8]; - } cp_pcix_cycle; /* 0x040000-0x040007 */ - - char _pad_040007[0x200000-0x040008]; - - /* 0x200000-0x7FFFFF -- PCI/GIO Device Spaces */ - union { - uint8_t c[0x100000 / 1]; - uint16_t s[0x100000 / 2]; - uint32_t l[0x100000 / 4]; - uint64_t d[0x100000 / 8]; - } cp_devio_raw[6]; /* 0x200000-0x7FFFFF */ - - #define cp_devio(n) cp_devio_raw[((n)<2)?(n*2):(n+2)] - - char _pad_800000[0xA00000-0x800000]; - - /* 0xA00000-0xBFFFFF -- PCI/GIO Device Spaces w/flush */ - union { - uint8_t c[0x100000 / 1]; - uint16_t s[0x100000 / 2]; - uint32_t l[0x100000 / 4]; - uint64_t d[0x100000 / 8]; - } cp_devio_raw_flush[6]; /* 0xA00000-0xBFFFFF */ - - #define cp_devio_flush(n) cp_devio_raw_flush[((n)<2)?(n*2):(n+2)] - -}; - -#endif /* _ASM_IA64_SN_PCI_TIOCP_H */ --- linux-2.6.13-rc2/arch/ia64/sn/include/xtalk/hubdev.h 2005-03-01 23:38:25.000000000 -0800 +++ devel/arch/ia64/sn/include/xtalk/hubdev.h 2005-07-12 00:26:31.000000000 -0700 @@ -8,6 +8,8 @@ #ifndef _ASM_IA64_SN_XTALK_HUBDEV_H #define _ASM_IA64_SN_XTALK_HUBDEV_H +#include "xtalk/xwidgetdev.h" + #define HUB_WIDGET_ID_MAX 0xf #define DEV_PER_WIDGET (2*2*8) #define IIO_ITTE_WIDGET_BITS 4 /* size of widget field */ --- linux-2.6.13-rc2/arch/ia64/sn/kernel/io_init.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/sn/kernel/io_init.c 2005-07-12 00:26:31.000000000 -0700 @@ -9,21 +9,28 @@ #include #include #include -#include #include -#include -#include -#include "pci/pcibr_provider.h" -#include "xtalk/xwidgetdev.h" #include -#include "xtalk/hubdev.h" #include +#include +#include +#include #include +#include #include +#include "xtalk/hubdev.h" +#include "xtalk/xwidgetdev.h" -char master_baseio_wid; nasid_t master_nasid = INVALID_NASID; /* Partition Master */ +static struct list_head sn_sysdata_list; + +/* sysdata list struct */ +struct sysdata_el { + struct list_head entry; + void *sysdata; +}; + struct slab_info { struct hubdev_info hubdev; }; @@ -138,23 +145,6 @@ sal_get_pcidev_info(u64 segment, u64 bus } /* - * sn_alloc_pci_sysdata() - This routine allocates a pci controller - * which is expected as the pci_dev and pci_bus sysdata by the Linux - * PCI infrastructure. - */ -static inline struct pci_controller *sn_alloc_pci_sysdata(void) -{ - struct pci_controller *pci_sysdata; - - pci_sysdata = kmalloc(sizeof(*pci_sysdata), GFP_KERNEL); - if (!pci_sysdata) - BUG(); - - memset(pci_sysdata, 0, sizeof(*pci_sysdata)); - return pci_sysdata; -} - -/* * sn_fixup_ionodes() - This routine initializes the HUB data strcuture for * each node in the system. */ @@ -221,22 +211,34 @@ static void sn_fixup_ionodes(void) } +void sn_pci_unfixup_slot(struct pci_dev *dev) +{ + struct pci_dev *host_pci_dev = SN_PCIDEV_INFO(dev)->host_pci_dev; + + sn_irq_unfixup(dev); + pci_dev_put(host_pci_dev); + pci_dev_put(dev); +} + /* * sn_pci_fixup_slot() - This routine sets up a slot's resources * consistent with the Linux PCI abstraction layer. Resources acquired * from our PCI provider include PIO maps to BAR space and interrupt * objects. */ -static void sn_pci_fixup_slot(struct pci_dev *dev) +void sn_pci_fixup_slot(struct pci_dev *dev) { int idx; int segment = 0; - uint64_t size; - struct sn_irq_info *sn_irq_info; - struct pci_dev *host_pci_dev; int status = 0; struct pcibus_bussoft *bs; + struct pci_bus *host_pci_bus; + struct pci_dev *host_pci_dev; + struct sn_irq_info *sn_irq_info; + unsigned long size; + unsigned int bus_no, devfn; + pci_dev_get(dev); /* for the sysdata pointer */ dev->sysdata = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL); if (SN_PCIDEV_INFO(dev) <= 0) BUG(); /* Cannot afford to run out of memory */ @@ -253,7 +255,7 @@ static void sn_pci_fixup_slot(struct pci (u64) __pa(SN_PCIDEV_INFO(dev)), (u64) __pa(sn_irq_info)); if (status) - BUG(); /* Cannot get platform pci device information information */ + BUG(); /* Cannot get platform pci device information */ /* Copy over PIO Mapped Addresses */ for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) { @@ -275,15 +277,21 @@ static void sn_pci_fixup_slot(struct pci dev->resource[idx].parent = &iomem_resource; } - /* set up host bus linkages */ - bs = SN_PCIBUS_BUSSOFT(dev->bus); - host_pci_dev = - pci_find_slot(SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32, - SN_PCIDEV_INFO(dev)-> - pdi_slot_host_handle & 0xffffffff); + /* + * Using the PROMs values for the PCI host bus, get the Linux + * PCI host_pci_dev struct and set up host bus linkages + */ + + bus_no = SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32; + devfn = SN_PCIDEV_INFO(dev)->pdi_slot_host_handle & 0xffffffff; + host_pci_bus = pci_find_bus(pci_domain_nr(dev->bus), bus_no); + host_pci_dev = pci_get_slot(host_pci_bus, devfn); + + SN_PCIDEV_INFO(dev)->host_pci_dev = host_pci_dev; SN_PCIDEV_INFO(dev)->pdi_host_pcidev_info = - SN_PCIDEV_INFO(host_pci_dev); + SN_PCIDEV_INFO(host_pci_dev); SN_PCIDEV_INFO(dev)->pdi_linux_pcidev = dev; + bs = SN_PCIBUS_BUSSOFT(dev->bus); SN_PCIDEV_INFO(dev)->pdi_pcibus_info = bs; if (bs && bs->bs_asic_type < PCIIO_ASIC_MAX_TYPES) { @@ -297,6 +305,9 @@ static void sn_pci_fixup_slot(struct pci SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = sn_irq_info; dev->irq = SN_PCIDEV_INFO(dev)->pdi_sn_irq_info->irq_irq; sn_irq_fixup(dev, sn_irq_info); + } else { + SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = NULL; + kfree(sn_irq_info); } } @@ -304,55 +315,57 @@ static void sn_pci_fixup_slot(struct pci * sn_pci_controller_fixup() - This routine sets up a bus's resources * consistent with the Linux PCI abstraction layer. */ -static void sn_pci_controller_fixup(int segment, int busnum) +void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus) { int status = 0; int nasid, cnode; - struct pci_bus *bus; struct pci_controller *controller; struct pcibus_bussoft *prom_bussoft_ptr; struct hubdev_info *hubdev_info; void *provider_soft; struct sn_pcibus_provider *provider; - status = - sal_get_pcibus_info((u64) segment, (u64) busnum, - (u64) ia64_tpa(&prom_bussoft_ptr)); - if (status > 0) { - return; /* bus # does not exist */ - } - + status = sal_get_pcibus_info((u64) segment, (u64) busnum, + (u64) ia64_tpa(&prom_bussoft_ptr)); + if (status > 0) + return; /*bus # does not exist */ prom_bussoft_ptr = __va(prom_bussoft_ptr); - controller = sn_alloc_pci_sysdata(); - /* controller non-zero is BUG'd in sn_alloc_pci_sysdata */ - bus = pci_scan_bus(busnum, &pci_root_ops, controller); + controller = kcalloc(1,sizeof(struct pci_controller), GFP_KERNEL); + if (!controller) + BUG(); + if (bus == NULL) { - return; /* error, or bus already scanned */ + bus = pci_scan_bus(busnum, &pci_root_ops, controller); + if (bus == NULL) + return; /* error, or bus already scanned */ + bus->sysdata = NULL; } + if (bus->sysdata) + goto error_return; /* sysdata already alloc'd */ + /* * Per-provider fixup. Copies the contents from prom to local * area and links SN_PCIBUS_BUSSOFT(). */ - if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES) { + if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES) return; /* unsupported asic type */ - } + + if (prom_bussoft_ptr->bs_asic_type == PCIIO_ASIC_TYPE_PPB) + goto error_return; /* no further fixup necessary */ provider = sn_pci_provider[prom_bussoft_ptr->bs_asic_type]; - if (provider == NULL) { + if (provider == NULL) return; /* no provider registerd for this asic */ - } provider_soft = NULL; - if (provider->bus_fixup) { + if (provider->bus_fixup) provider_soft = (*provider->bus_fixup) (prom_bussoft_ptr); - } - if (provider_soft == NULL) { + if (provider_soft == NULL) return; /* fixup failed or not applicable */ - } /* * Generic bus fixup goes here. Don't reference prom_bussoft_ptr @@ -361,12 +374,47 @@ static void sn_pci_controller_fixup(int bus->sysdata = controller; PCI_CONTROLLER(bus)->platform_data = provider_soft; - nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); cnode = nasid_to_cnodeid(nasid); hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); + + return; + +error_return: + + kfree(controller); + return; +} + +void sn_bus_store_sysdata(struct pci_dev *dev) +{ + struct sysdata_el *element; + + element = kcalloc(1, sizeof(struct sysdata_el), GFP_KERNEL); + if (!element) { + dev_dbg(dev, "%s: out of memory!\n", __FUNCTION__); + return; + } + element->sysdata = dev->sysdata; + list_add(&element->entry, &sn_sysdata_list); +} + +void sn_bus_free_sysdata(void) +{ + struct sysdata_el *element; + struct list_head *list; + +sn_sysdata_free_start: + list_for_each(list, &sn_sysdata_list) { + element = list_entry(list, struct sysdata_el, entry); + list_del(&element->entry); + kfree(element->sysdata); + kfree(element); + goto sn_sysdata_free_start; + } + return; } /* @@ -403,20 +451,17 @@ static int __init sn_pci_init(void) */ ia64_max_iommu_merge_mask = ~PAGE_MASK; sn_fixup_ionodes(); - sn_irq = kmalloc(sizeof(struct sn_irq_info *) * NR_IRQS, GFP_KERNEL); - if (sn_irq <= 0) - BUG(); /* Canno afford to run out of memory. */ - memset(sn_irq, 0, sizeof(struct sn_irq_info *) * NR_IRQS); - + sn_irq_lh_init(); + INIT_LIST_HEAD(&sn_sysdata_list); sn_init_cpei_timer(); #ifdef CONFIG_PROC_FS register_sn_procfs(); #endif - for (i = 0; i < PCI_BUSES_TO_SCAN; i++) { - sn_pci_controller_fixup(0, i); - } + /* busses are not known yet ... */ + for (i = 0; i < PCI_BUSES_TO_SCAN; i++) + sn_pci_controller_fixup(0, i, NULL); /* * Generic Linux PCI Layer has created the pci_bus and pci_dev @@ -425,9 +470,8 @@ static int __init sn_pci_init(void) */ while ((pci_dev = - pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { + pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) sn_pci_fixup_slot(pci_dev); - } sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */ @@ -469,3 +513,8 @@ cnodeid_get_geoid(cnodeid_t cnode) } subsys_initcall(sn_pci_init); +EXPORT_SYMBOL(sn_pci_fixup_slot); +EXPORT_SYMBOL(sn_pci_unfixup_slot); +EXPORT_SYMBOL(sn_pci_controller_fixup); +EXPORT_SYMBOL(sn_bus_store_sysdata); +EXPORT_SYMBOL(sn_bus_free_sysdata); --- linux-2.6.13-rc2/arch/ia64/sn/kernel/irq.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/sn/kernel/irq.c 2005-07-12 00:26:31.000000000 -0700 @@ -9,13 +9,13 @@ */ #include -#include +#include #include #include -#include "xtalk/xwidgetdev.h" +#include +#include #include #include -#include "pci/pcibr_provider.h" #include #include @@ -25,7 +25,8 @@ static void unregister_intr_pda(struct s extern int sn_force_interrupt_flag; extern int sn_ioif_inited; -struct sn_irq_info **sn_irq; +static struct list_head **sn_irq_lh; +static spinlock_t sn_irq_info_lock = SPIN_LOCK_UNLOCKED; /* non-IRQ lock */ static inline uint64_t sn_intr_alloc(nasid_t local_nasid, int local_widget, u64 sn_irq_info, @@ -101,7 +102,7 @@ static void sn_end_irq(unsigned int irq) nasid = get_nasid(); event_occurred = HUB_L((uint64_t *) GLOBAL_MMR_ADDR (nasid, SH_EVENT_OCCURRED)); - /* If the UART bit is set here, we may have received an + /* If the UART bit is set here, we may have received an * interrupt from the UART that the driver missed. To * make sure, we IPI ourselves to force us to look again. */ @@ -115,82 +116,84 @@ static void sn_end_irq(unsigned int irq) force_interrupt(irq); } +static void sn_irq_info_free(struct rcu_head *head); + static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask) { - struct sn_irq_info *sn_irq_info = sn_irq[irq]; - struct sn_irq_info *tmp_sn_irq_info; + struct sn_irq_info *sn_irq_info, *sn_irq_info_safe; int cpuid, cpuphys; - nasid_t t_nasid; /* nasid to target */ - int t_slice; /* slice to target */ - - /* allocate a temp sn_irq_info struct to get new target info */ - tmp_sn_irq_info = kmalloc(sizeof(*tmp_sn_irq_info), GFP_KERNEL); - if (!tmp_sn_irq_info) - return; cpuid = first_cpu(mask); cpuphys = cpu_physical_id(cpuid); - t_nasid = cpuid_to_nasid(cpuid); - t_slice = cpuid_to_slice(cpuid); - while (sn_irq_info) { - int status; - int local_widget; - uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge; - nasid_t local_nasid = NASID_GET(bridge); + list_for_each_entry_safe(sn_irq_info, sn_irq_info_safe, + sn_irq_lh[irq], list) { + uint64_t bridge; + int local_widget, status; + nasid_t local_nasid; + struct sn_irq_info *new_irq_info; + + new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC); + if (new_irq_info == NULL) + break; + memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info)); + + bridge = (uint64_t) new_irq_info->irq_bridge; + if (!bridge) { + kfree(new_irq_info); + break; /* irq is not a device interrupt */ + } - if (!bridge) - break; /* irq is not a device interrupt */ + local_nasid = NASID_GET(bridge); if (local_nasid & 1) local_widget = TIO_SWIN_WIDGETNUM(bridge); else local_widget = SWIN_WIDGETNUM(bridge); - /* Free the old PROM sn_irq_info structure */ - sn_intr_free(local_nasid, local_widget, sn_irq_info); + /* Free the old PROM new_irq_info structure */ + sn_intr_free(local_nasid, local_widget, new_irq_info); + /* Update kernels new_irq_info with new target info */ + unregister_intr_pda(new_irq_info); - /* allocate a new PROM sn_irq_info struct */ + /* allocate a new PROM new_irq_info struct */ status = sn_intr_alloc(local_nasid, local_widget, - __pa(tmp_sn_irq_info), irq, t_nasid, - t_slice); + __pa(new_irq_info), irq, + cpuid_to_nasid(cpuid), + cpuid_to_slice(cpuid)); + + /* SAL call failed */ + if (status) { + kfree(new_irq_info); + break; + } - if (status == 0) { - /* Update kernels sn_irq_info with new target info */ - unregister_intr_pda(sn_irq_info); - sn_irq_info->irq_cpuid = cpuid; - sn_irq_info->irq_nasid = t_nasid; - sn_irq_info->irq_slice = t_slice; - sn_irq_info->irq_xtalkaddr = - tmp_sn_irq_info->irq_xtalkaddr; - sn_irq_info->irq_cookie = tmp_sn_irq_info->irq_cookie; - register_intr_pda(sn_irq_info); + new_irq_info->irq_cpuid = cpuid; + register_intr_pda(new_irq_info); - if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type)) { - pcibr_change_devices_irq(sn_irq_info); - } + if (IS_PCI_BRIDGE_ASIC(new_irq_info->irq_bridge_type)) + pcibr_change_devices_irq(new_irq_info); - sn_irq_info = sn_irq_info->irq_next; + spin_lock(&sn_irq_info_lock); + list_replace_rcu(&sn_irq_info->list, &new_irq_info->list); + spin_unlock(&sn_irq_info_lock); + call_rcu(&sn_irq_info->rcu, sn_irq_info_free); #ifdef CONFIG_SMP - set_irq_affinity_info((irq & 0xff), cpuphys, 0); + set_irq_affinity_info((irq & 0xff), cpuphys, 0); #endif - } else { - break; /* snp_affinity failed the intr_alloc */ - } } - kfree(tmp_sn_irq_info); } struct hw_interrupt_type irq_type_sn = { - "SN hub", - sn_startup_irq, - sn_shutdown_irq, - sn_enable_irq, - sn_disable_irq, - sn_ack_irq, - sn_end_irq, - sn_set_affinity_irq + .typename = "SN hub", + .startup = sn_startup_irq, + .shutdown = sn_shutdown_irq, + .enable = sn_enable_irq, + .disable = sn_disable_irq, + .ack = sn_ack_irq, + .end = sn_end_irq, + .set_affinity = sn_set_affinity_irq }; unsigned int sn_local_vector_to_irq(u8 vector) @@ -231,19 +234,18 @@ static void unregister_intr_pda(struct s struct sn_irq_info *tmp_irq_info; int i, foundmatch; + rcu_read_lock(); if (pdacpu(cpu)->sn_last_irq == irq) { foundmatch = 0; - for (i = pdacpu(cpu)->sn_last_irq - 1; i; i--) { - tmp_irq_info = sn_irq[i]; - while (tmp_irq_info) { + for (i = pdacpu(cpu)->sn_last_irq - 1; + i && !foundmatch; i--) { + list_for_each_entry_rcu(tmp_irq_info, + sn_irq_lh[i], + list) { if (tmp_irq_info->irq_cpuid == cpu) { - foundmatch++; + foundmatch = 1; break; } - tmp_irq_info = tmp_irq_info->irq_next; - } - if (foundmatch) { - break; } } pdacpu(cpu)->sn_last_irq = i; @@ -251,60 +253,27 @@ static void unregister_intr_pda(struct s if (pdacpu(cpu)->sn_first_irq == irq) { foundmatch = 0; - for (i = pdacpu(cpu)->sn_first_irq + 1; i < NR_IRQS; i++) { - tmp_irq_info = sn_irq[i]; - while (tmp_irq_info) { + for (i = pdacpu(cpu)->sn_first_irq + 1; + i < NR_IRQS && !foundmatch; i++) { + list_for_each_entry_rcu(tmp_irq_info, + sn_irq_lh[i], + list) { if (tmp_irq_info->irq_cpuid == cpu) { - foundmatch++; + foundmatch = 1; break; } - tmp_irq_info = tmp_irq_info->irq_next; - } - if (foundmatch) { - break; } } pdacpu(cpu)->sn_first_irq = ((i == NR_IRQS) ? 0 : i); } + rcu_read_unlock(); } -struct sn_irq_info *sn_irq_alloc(nasid_t local_nasid, int local_widget, int irq, - nasid_t nasid, int slice) +static void sn_irq_info_free(struct rcu_head *head) { struct sn_irq_info *sn_irq_info; - int status; - - sn_irq_info = kmalloc(sizeof(*sn_irq_info), GFP_KERNEL); - if (sn_irq_info == NULL) - return NULL; - - memset(sn_irq_info, 0x0, sizeof(*sn_irq_info)); - - status = - sn_intr_alloc(local_nasid, local_widget, __pa(sn_irq_info), irq, - nasid, slice); - - if (status) { - kfree(sn_irq_info); - return NULL; - } else { - return sn_irq_info; - } -} - -void sn_irq_free(struct sn_irq_info *sn_irq_info) -{ - uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge; - nasid_t local_nasid = NASID_GET(bridge); - int local_widget; - - if (local_nasid & 1) /* tio check */ - local_widget = TIO_SWIN_WIDGETNUM(bridge); - else - local_widget = SWIN_WIDGETNUM(bridge); - - sn_intr_free(local_nasid, local_widget, sn_irq_info); + sn_irq_info = container_of(head, struct sn_irq_info, rcu); kfree(sn_irq_info); } @@ -314,30 +283,54 @@ void sn_irq_fixup(struct pci_dev *pci_de int slice = sn_irq_info->irq_slice; int cpu = nasid_slice_to_cpuid(nasid, slice); + pci_dev_get(pci_dev); sn_irq_info->irq_cpuid = cpu; sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev); /* link it into the sn_irq[irq] list */ - sn_irq_info->irq_next = sn_irq[sn_irq_info->irq_irq]; - sn_irq[sn_irq_info->irq_irq] = sn_irq_info; + spin_lock(&sn_irq_info_lock); + list_add_rcu(&sn_irq_info->list, sn_irq_lh[sn_irq_info->irq_irq]); + spin_unlock(&sn_irq_info_lock); (void)register_intr_pda(sn_irq_info); } +void sn_irq_unfixup(struct pci_dev *pci_dev) +{ + struct sn_irq_info *sn_irq_info; + + /* Only cleanup IRQ stuff if this device has a host bus context */ + if (!SN_PCIDEV_BUSSOFT(pci_dev)) + return; + + sn_irq_info = SN_PCIDEV_INFO(pci_dev)->pdi_sn_irq_info; + if (!sn_irq_info || !sn_irq_info->irq_irq) { + kfree(sn_irq_info); + return; + } + + unregister_intr_pda(sn_irq_info); + spin_lock(&sn_irq_info_lock); + list_del_rcu(&sn_irq_info->list); + spin_unlock(&sn_irq_info_lock); + call_rcu(&sn_irq_info->rcu, sn_irq_info_free); + pci_dev_put(pci_dev); +} + static void force_interrupt(int irq) { struct sn_irq_info *sn_irq_info; if (!sn_ioif_inited) return; - sn_irq_info = sn_irq[irq]; - while (sn_irq_info) { + + rcu_read_lock(); + list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[irq], list) { if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) && - (sn_irq_info->irq_bridge != NULL)) { + (sn_irq_info->irq_bridge != NULL)) pcibr_force_interrupt(sn_irq_info); - } - sn_irq_info = sn_irq_info->irq_next; } + rcu_read_unlock(); } /* @@ -402,19 +395,41 @@ static void sn_check_intr(int irq, struc void sn_lb_int_war_check(void) { + struct sn_irq_info *sn_irq_info; int i; if (!sn_ioif_inited || pda->sn_first_irq == 0) return; + + rcu_read_lock(); for (i = pda->sn_first_irq; i <= pda->sn_last_irq; i++) { - struct sn_irq_info *sn_irq_info = sn_irq[i]; - while (sn_irq_info) { - /* Only call for PCI bridges that are fully initialized. */ + list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[i], list) { + /* + * Only call for PCI bridges that are fully + * initialized. + */ if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) && - (sn_irq_info->irq_bridge != NULL)) { + (sn_irq_info->irq_bridge != NULL)) sn_check_intr(i, sn_irq_info); - } - sn_irq_info = sn_irq_info->irq_next; } } + rcu_read_unlock(); +} + +void sn_irq_lh_init(void) +{ + int i; + + sn_irq_lh = kmalloc(sizeof(struct list_head *) * NR_IRQS, GFP_KERNEL); + if (!sn_irq_lh) + panic("SN PCI INIT: Failed to allocate memory for PCI init\n"); + + for (i = 0; i < NR_IRQS; i++) { + sn_irq_lh[i] = kmalloc(sizeof(struct list_head), GFP_KERNEL); + if (!sn_irq_lh[i]) + panic("SN PCI INIT: Failed IRQ memory allocation\n"); + + INIT_LIST_HEAD(sn_irq_lh[i]); + } + } --- linux-2.6.13-rc2/arch/ia64/sn/kernel/setup.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/sn/kernel/setup.c 2005-07-12 00:26:31.000000000 -0700 @@ -270,7 +270,7 @@ void __init sn_setup(char **cmdline_p) { long status, ticks_per_sec, drift; int pxm; - int major = sn_sal_rev_major(), minor = sn_sal_rev_minor(); + u32 version = sn_sal_rev(); extern void sn_cpu_init(void); ia64_sn_plat_set_error_handling_features(); @@ -308,22 +308,21 @@ void __init sn_setup(char **cmdline_p) * support here so we don't have to listen to failed keyboard probe * messages. */ - if ((major < 2 || (major == 2 && minor <= 9)) && - acpi_kbd_controller_present) { + if (version <= 0x0209 && acpi_kbd_controller_present) { printk(KERN_INFO "Disabling legacy keyboard support as prom " "is too old and doesn't provide FADT\n"); acpi_kbd_controller_present = 0; } - printk("SGI SAL version %x.%02x\n", major, minor); + printk("SGI SAL version %x.%02x\n", version >> 8, version & 0x00FF); /* * Confirm the SAL we're running on is recent enough... */ - if ((major < SN_SAL_MIN_MAJOR) || (major == SN_SAL_MIN_MAJOR && - minor < SN_SAL_MIN_MINOR)) { + if (version < SN_SAL_MIN_VERSION) { printk(KERN_ERR "This kernel needs SGI SAL version >= " - "%x.%02x\n", SN_SAL_MIN_MAJOR, SN_SAL_MIN_MINOR); + "%x.%02x\n", SN_SAL_MIN_VERSION >> 8, + SN_SAL_MIN_VERSION & 0x00FF); panic("PROM version too old\n"); } --- linux-2.6.13-rc2/arch/ia64/sn/kernel/tiocx.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/sn/kernel/tiocx.c 2005-07-12 00:26:31.000000000 -0700 @@ -8,12 +8,12 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -481,6 +481,9 @@ static int __init tiocx_init(void) cnodeid_t cnodeid; int found_tiocx_device = 0; + if (!ia64_platform_is("sn2")) + return -ENODEV; + bus_register(&tiocx_bus_type); for (cnodeid = 0; cnodeid < MAX_COMPACT_NODES; cnodeid++) { --- linux-2.6.13-rc2/arch/ia64/sn/kernel/xpc_main.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/sn/kernel/xpc_main.c 2005-07-12 00:27:19.000000000 -0700 @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -308,8 +309,7 @@ xpc_make_first_contact(struct xpc_partit "partition %d\n", XPC_PARTID(part)); /* wait a 1/4 of a second or so */ - set_current_state(TASK_INTERRUPTIBLE); - (void) schedule_timeout(0.25 * HZ); + msleep_interruptible(250); if (part->act_state == XPC_P_DEACTIVATING) { return part->reason; @@ -420,7 +420,7 @@ xpc_activating(void *__partid) partid_t partid = (u64) __partid; struct xpc_partition *part = &xpc_partitions[partid]; unsigned long irq_flags; - struct sched_param param = { sched_priority: MAX_USER_RT_PRIO - 1 }; + struct sched_param param = { sched_priority: MAX_RT_PRIO - 1 }; int ret; @@ -841,9 +841,7 @@ xpc_do_exit(void) down(&xpc_discovery_exited); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(0.3 * HZ); - set_current_state(TASK_RUNNING); + msleep_interruptible(300); /* wait for all partitions to become inactive */ @@ -860,12 +858,8 @@ xpc_do_exit(void) } } - if (active_part_count) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(0.3 * HZ); - set_current_state(TASK_RUNNING); - } - + if (active_part_count) + msleep_interruptible(300); } while (active_part_count > 0); --- linux-2.6.13-rc2/arch/ia64/sn/kernel/xpnet.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/sn/kernel/xpnet.c 2005-07-12 00:27:34.000000000 -0700 @@ -130,7 +130,7 @@ struct net_device *xpnet_device; */ static u64 xpnet_broadcast_partitions; /* protect above */ -static spinlock_t xpnet_broadcast_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(xpnet_broadcast_lock); /* * Since the Block Transfer Engine (BTE) is being used for the transfer --- linux-2.6.13-rc2/arch/ia64/sn/pci/pcibr/pcibr_ate.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/sn/pci/pcibr/pcibr_ate.c 2005-07-12 00:26:31.000000000 -0700 @@ -8,9 +8,9 @@ #include #include +#include #include #include -#include "pci/pcibr_provider.h" int pcibr_invalidate_ate = 0; /* by default don't invalidate ATE on free */ --- linux-2.6.13-rc2/arch/ia64/sn/pci/pcibr/pcibr_dma.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/sn/pci/pcibr/pcibr_dma.c 2005-07-12 00:26:31.000000000 -0700 @@ -8,18 +8,17 @@ #include #include -#include +#include #include -#include "xtalk/xwidgetdev.h" -#include "xtalk/hubdev.h" +#include #include #include -#include "pci/tiocp.h" -#include "pci/pic.h" -#include "pci/pcibr_provider.h" -#include "pci/tiocp.h" +#include +#include +#include #include "tio.h" -#include +#include "xtalk/xwidgetdev.h" +#include "xtalk/hubdev.h" extern int sn_ioif_inited; --- linux-2.6.13-rc2/arch/ia64/sn/pci/pcibr/pcibr_provider.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/sn/pci/pcibr/pcibr_provider.c 2005-07-12 00:26:31.000000000 -0700 @@ -6,18 +6,51 @@ * Copyright (C) 2001-2004 Silicon Graphics, Inc. All rights reserved. */ -#include #include +#include #include -#include -#include "xtalk/xwidgetdev.h" +#include #include -#include "xtalk/hubdev.h" +#include #include #include -#include "pci/pcibr_provider.h" -#include +#include +#include "xtalk/xwidgetdev.h" +#include "xtalk/hubdev.h" + +int +sal_pcibr_slot_enable(struct pcibus_info *soft, int device, void *resp) +{ + struct ia64_sal_retval ret_stuff; + uint64_t busnum; + + ret_stuff.status = 0; + ret_stuff.v0 = 0; + busnum = soft->pbi_buscommon.bs_persist_busnum; + SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_ENABLE, (u64) busnum, + (u64) device, (u64) resp, 0, 0, 0, 0); + + return (int)ret_stuff.v0; +} + +int +sal_pcibr_slot_disable(struct pcibus_info *soft, int device, int action, + void *resp) +{ + struct ia64_sal_retval ret_stuff; + uint64_t busnum; + + ret_stuff.status = 0; + ret_stuff.v0 = 0; + + busnum = soft->pbi_buscommon.bs_persist_busnum; + SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_SLOT_DISABLE, + (u64) busnum, (u64) device, (u64) action, + (u64) resp, 0, 0, 0); + + return (int)ret_stuff.v0; +} static int sal_pcibr_error_interrupt(struct pcibus_info *soft) { @@ -188,3 +221,6 @@ pcibr_init_provider(void) return 0; } + +EXPORT_SYMBOL_GPL(sal_pcibr_slot_enable); +EXPORT_SYMBOL_GPL(sal_pcibr_slot_disable); --- linux-2.6.13-rc2/arch/ia64/sn/pci/pcibr/pcibr_reg.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/sn/pci/pcibr/pcibr_reg.c 2005-07-12 00:26:31.000000000 -0700 @@ -6,13 +6,13 @@ * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. */ -#include #include +#include +#include #include #include -#include "pci/tiocp.h" -#include "pci/pic.h" -#include "pci/pcibr_provider.h" +#include +#include union br_ptr { struct tiocp tio; --- linux-2.6.13-rc2/arch/ia64/sn/pci/pci_dma.c 2005-06-17 16:03:58.000000000 -0700 +++ devel/arch/ia64/sn/pci/pci_dma.c 2005-07-12 00:26:31.000000000 -0700 @@ -11,9 +11,10 @@ #include #include -#include +#include #include #include +#include #define SG_ENT_VIRT_ADDRESS(sg) (page_address((sg)->page) + (sg)->offset) #define SG_ENT_PHYS_ADDRESS(SG) virt_to_phys(SG_ENT_VIRT_ADDRESS(SG)) --- linux-2.6.13-rc2/arch/ia64/sn/pci/tioca_provider.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/ia64/sn/pci/tioca_provider.c 2005-07-12 00:26:31.000000000 -0700 @@ -589,8 +589,7 @@ tioca_bus_fixup(struct pcibus_bussoft *p /* sanity check prom rev */ - if (sn_sal_rev_major() < 4 || - (sn_sal_rev_major() == 4 && sn_sal_rev_minor() < 6)) { + if (sn_sal_rev() < 0x0406) { printk (KERN_ERR "%s: SGI prom rev 4.06 or greater required " "for tioca support\n", __FUNCTION__); --- linux-2.6.13-rc2/arch/m32r/kernel/setup_m32700ut.c 2005-07-05 22:22:07.000000000 -0700 +++ devel/arch/m32r/kernel/setup_m32700ut.c 2005-07-12 00:26:31.000000000 -0700 @@ -3,8 +3,8 @@ * * Setup routines for Renesas M32700UT Board * - * Copyright (c) 2002 Hiroyuki Kondo, Hirokazu Takata, - * Hitoshi Yamamoto, Takeo Takahashi + * Copyright (c) 2002-2005 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi * * This file is subject to the terms and conditions of the GNU General * Public License. See the file "COPYING" in the main directory of this @@ -435,7 +435,7 @@ void __init init_IRQ(void) icu_data[M32R_IRQ_INT2].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; enable_m32700ut_irq(M32R_IRQ_INT2); -//#if defined(CONFIG_VIDEO_M32R_AR) +#if defined(CONFIG_VIDEO_M32R_AR) /* * INT3# is used for AR */ @@ -445,9 +445,11 @@ void __init init_IRQ(void) irq_desc[M32R_IRQ_INT3].depth = 1; icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_m32700ut_irq(M32R_IRQ_INT3); -//#endif /* CONFIG_VIDEO_M32R_AR */ +#endif /* CONFIG_VIDEO_M32R_AR */ } +#if defined(CONFIG_SMC91X) + #define LAN_IOSTART 0x300 #define LAN_IOEND 0x320 static struct resource smc91x_resources[] = { @@ -469,10 +471,55 @@ static struct platform_device smc91x_dev .num_resources = ARRAY_SIZE(smc91x_resources), .resource = smc91x_resources, }; +#endif + +#if defined(CONFIG_FB_S1D13XXX) + +#include