From 2b8bcc5a2fca54648ece966902b8230de971b609 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 01:27:56 +0000 Subject: MIPS: avoid .set ISA for cache operations As a step towards unifying the cache maintenance code for mips32 & mips64 CPUs, stop using ".set " directives in the more developed mips32 version of the code. Instead, when present make use of the GCC builtin for emitting a cache instruction. When not present, simply don't bother with the .set directives since U-boot always builds with -march=mips32 or higher anyway. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/cpu/mips32/cache.S | 18 +++++------------- arch/mips/cpu/mips32/cpu.c | 40 +++++++++++++++------------------------- arch/mips/include/asm/cacheops.h | 13 +++++++++++++ 3 files changed, 33 insertions(+), 38 deletions(-) (limited to 'arch') diff --git a/arch/mips/cpu/mips32/cache.S b/arch/mips/cpu/mips32/cache.S index 22bd844..fb1d84b 100644 --- a/arch/mips/cpu/mips32/cache.S +++ b/arch/mips/cpu/mips32/cache.S @@ -22,14 +22,6 @@ #define INDEX_BASE CKSEG0 - .macro cache_op op addr - .set push - .set noreorder - .set mips3 - cache \op, 0(\addr) - .set pop - .endm - .macro f_fill64 dst, offset, val LONG_S \val, (\offset + 0 * LONGSIZE)(\dst) LONG_S \val, (\offset + 1 * LONGSIZE)(\dst) @@ -60,17 +52,17 @@ LEAF(mips_init_icache) /* clear tag to invalidate */ PTR_LI t0, INDEX_BASE PTR_ADDU t1, t0, a1 -1: cache_op INDEX_STORE_TAG_I t0 +1: cache INDEX_STORE_TAG_I, 0(t0) PTR_ADDU t0, a2 bne t0, t1, 1b /* fill once, so data field parity is correct */ PTR_LI t0, INDEX_BASE -2: cache_op FILL t0 +2: cache FILL, 0(t0) PTR_ADDU t0, a2 bne t0, t1, 2b /* invalidate again - prudent but not strictly neccessary */ PTR_LI t0, INDEX_BASE -1: cache_op INDEX_STORE_TAG_I t0 +1: cache INDEX_STORE_TAG_I, 0(t0) PTR_ADDU t0, a2 bne t0, t1, 1b 9: jr ra @@ -85,7 +77,7 @@ LEAF(mips_init_dcache) /* clear all tags */ PTR_LI t0, INDEX_BASE PTR_ADDU t1, t0, a1 -1: cache_op INDEX_STORE_TAG_D t0 +1: cache INDEX_STORE_TAG_D, 0(t0) PTR_ADDU t0, a2 bne t0, t1, 1b /* load from each line (in cached space) */ @@ -95,7 +87,7 @@ LEAF(mips_init_dcache) bne t0, t1, 2b /* clear all tags */ PTR_LI t0, INDEX_BASE -1: cache_op INDEX_STORE_TAG_D t0 +1: cache INDEX_STORE_TAG_D, 0(t0) PTR_ADDU t0, a2 bne t0, t1, 1b 9: jr ra diff --git a/arch/mips/cpu/mips32/cpu.c b/arch/mips/cpu/mips32/cpu.c index 278865b..1af909a 100644 --- a/arch/mips/cpu/mips32/cpu.c +++ b/arch/mips/cpu/mips32/cpu.c @@ -12,16 +12,6 @@ #include #include -#define cache_op(op,addr) \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noreorder \n" \ - " .set mips3\n\t \n" \ - " cache %0, %1 \n" \ - " .set pop \n" \ - : \ - : "i" (op), "R" (*(unsigned char *)(addr))) - void __attribute__((weak)) _machine_restart(void) { } @@ -74,20 +64,20 @@ void flush_cache(ulong start_addr, ulong size) { unsigned long ilsize = icache_line_size(); unsigned long dlsize = dcache_line_size(); - unsigned long addr, aend; + const void *addr, *aend; /* aend will be miscalculated when size is zero, so we return here */ if (size == 0) return; - addr = start_addr & ~(dlsize - 1); - aend = (start_addr + size - 1) & ~(dlsize - 1); + addr = (const void *)(start_addr & ~(dlsize - 1)); + aend = (const void *)((start_addr + size - 1) & ~(dlsize - 1)); if (ilsize == dlsize) { /* flush I-cache & D-cache simultaneously */ while (1) { - cache_op(HIT_WRITEBACK_INV_D, addr); - cache_op(HIT_INVALIDATE_I, addr); + mips_cache(HIT_WRITEBACK_INV_D, addr); + mips_cache(HIT_INVALIDATE_I, addr); if (addr == aend) break; addr += dlsize; @@ -97,17 +87,17 @@ void flush_cache(ulong start_addr, ulong size) /* flush D-cache */ while (1) { - cache_op(HIT_WRITEBACK_INV_D, addr); + mips_cache(HIT_WRITEBACK_INV_D, addr); if (addr == aend) break; addr += dlsize; } /* flush I-cache */ - addr = start_addr & ~(ilsize - 1); - aend = (start_addr + size - 1) & ~(ilsize - 1); + addr = (const void *)(start_addr & ~(ilsize - 1)); + aend = (const void *)((start_addr + size - 1) & ~(ilsize - 1)); while (1) { - cache_op(HIT_INVALIDATE_I, addr); + mips_cache(HIT_INVALIDATE_I, addr); if (addr == aend) break; addr += ilsize; @@ -117,11 +107,11 @@ void flush_cache(ulong start_addr, ulong size) void flush_dcache_range(ulong start_addr, ulong stop) { unsigned long lsize = dcache_line_size(); - unsigned long addr = start_addr & ~(lsize - 1); - unsigned long aend = (stop - 1) & ~(lsize - 1); + const void *addr = (const void *)(start_addr & ~(lsize - 1)); + const void *aend = (const void *)((stop - 1) & ~(lsize - 1)); while (1) { - cache_op(HIT_WRITEBACK_INV_D, addr); + mips_cache(HIT_WRITEBACK_INV_D, addr); if (addr == aend) break; addr += lsize; @@ -131,11 +121,11 @@ void flush_dcache_range(ulong start_addr, ulong stop) void invalidate_dcache_range(ulong start_addr, ulong stop) { unsigned long lsize = dcache_line_size(); - unsigned long addr = start_addr & ~(lsize - 1); - unsigned long aend = (stop - 1) & ~(lsize - 1); + const void *addr = (const void *)(start_addr & ~(lsize - 1)); + const void *aend = (const void *)((stop - 1) & ~(lsize - 1)); while (1) { - cache_op(HIT_INVALIDATE_D, addr); + mips_cache(HIT_INVALIDATE_D, addr); if (addr == aend) break; addr += lsize; diff --git a/arch/mips/include/asm/cacheops.h b/arch/mips/include/asm/cacheops.h index 6464250..75ec380 100644 --- a/arch/mips/include/asm/cacheops.h +++ b/arch/mips/include/asm/cacheops.h @@ -11,6 +11,19 @@ #ifndef __ASM_CACHEOPS_H #define __ASM_CACHEOPS_H +#ifndef __ASSEMBLY__ + +static inline void mips_cache(int op, const volatile void *addr) +{ +#ifdef __GCC_HAVE_BUILTIN_MIPS_CACHE + __builtin_mips_cache(op, addr); +#else + __asm__ __volatile__("cache %0, %1" : : "i"(op), "R"(addr)) +#endif +} + +#endif /* !__ASSEMBLY__ */ + /* * Cache Operations available on all MIPS processors with R4000-style caches */ -- cgit v1.1 From 30374f98d14d5979f95a9d21d66346eaa9a795a1 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 01:27:57 +0000 Subject: MIPS: unify cache maintenance functions Move the more developed mips32 version of the cache maintenance functions to a common arch/mips/lib/cache.c, in order to reduce duplication between mips32 & mips64. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/cpu/mips32/cpu.c | 109 ----------------------------------------- arch/mips/cpu/mips64/cpu.c | 58 ---------------------- arch/mips/lib/Makefile | 1 + arch/mips/lib/cache.c | 118 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 167 deletions(-) create mode 100644 arch/mips/lib/cache.c (limited to 'arch') diff --git a/arch/mips/cpu/mips32/cpu.c b/arch/mips/cpu/mips32/cpu.c index 1af909a..8e1cc4e 100644 --- a/arch/mips/cpu/mips32/cpu.c +++ b/arch/mips/cpu/mips32/cpu.c @@ -9,7 +9,6 @@ #include #include #include -#include #include void __attribute__((weak)) _machine_restart(void) @@ -24,114 +23,6 @@ int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 0; } -#ifdef CONFIG_SYS_CACHELINE_SIZE - -static inline unsigned long icache_line_size(void) -{ - return CONFIG_SYS_CACHELINE_SIZE; -} - -static inline unsigned long dcache_line_size(void) -{ - return CONFIG_SYS_CACHELINE_SIZE; -} - -#else /* !CONFIG_SYS_CACHELINE_SIZE */ - -static inline unsigned long icache_line_size(void) -{ - unsigned long conf1, il; - conf1 = read_c0_config1(); - il = (conf1 & MIPS_CONF1_IL) >> MIPS_CONF1_IL_SHIFT; - if (!il) - return 0; - return 2 << il; -} - -static inline unsigned long dcache_line_size(void) -{ - unsigned long conf1, dl; - conf1 = read_c0_config1(); - dl = (conf1 & MIPS_CONF1_DL) >> MIPS_CONF1_DL_SHIFT; - if (!dl) - return 0; - return 2 << dl; -} - -#endif /* !CONFIG_SYS_CACHELINE_SIZE */ - -void flush_cache(ulong start_addr, ulong size) -{ - unsigned long ilsize = icache_line_size(); - unsigned long dlsize = dcache_line_size(); - const void *addr, *aend; - - /* aend will be miscalculated when size is zero, so we return here */ - if (size == 0) - return; - - addr = (const void *)(start_addr & ~(dlsize - 1)); - aend = (const void *)((start_addr + size - 1) & ~(dlsize - 1)); - - if (ilsize == dlsize) { - /* flush I-cache & D-cache simultaneously */ - while (1) { - mips_cache(HIT_WRITEBACK_INV_D, addr); - mips_cache(HIT_INVALIDATE_I, addr); - if (addr == aend) - break; - addr += dlsize; - } - return; - } - - /* flush D-cache */ - while (1) { - mips_cache(HIT_WRITEBACK_INV_D, addr); - if (addr == aend) - break; - addr += dlsize; - } - - /* flush I-cache */ - addr = (const void *)(start_addr & ~(ilsize - 1)); - aend = (const void *)((start_addr + size - 1) & ~(ilsize - 1)); - while (1) { - mips_cache(HIT_INVALIDATE_I, addr); - if (addr == aend) - break; - addr += ilsize; - } -} - -void flush_dcache_range(ulong start_addr, ulong stop) -{ - unsigned long lsize = dcache_line_size(); - const void *addr = (const void *)(start_addr & ~(lsize - 1)); - const void *aend = (const void *)((stop - 1) & ~(lsize - 1)); - - while (1) { - mips_cache(HIT_WRITEBACK_INV_D, addr); - if (addr == aend) - break; - addr += lsize; - } -} - -void invalidate_dcache_range(ulong start_addr, ulong stop) -{ - unsigned long lsize = dcache_line_size(); - const void *addr = (const void *)(start_addr & ~(lsize - 1)); - const void *aend = (const void *)((stop - 1) & ~(lsize - 1)); - - while (1) { - mips_cache(HIT_INVALIDATE_D, addr); - if (addr == aend) - break; - addr += lsize; - } -} - void write_one_tlb(int index, u32 pagemask, u32 hi, u32 low0, u32 low1) { write_c0_entrylo0(low0); diff --git a/arch/mips/cpu/mips64/cpu.c b/arch/mips/cpu/mips64/cpu.c index 9f45cfc..1d32705 100644 --- a/arch/mips/cpu/mips64/cpu.c +++ b/arch/mips/cpu/mips64/cpu.c @@ -9,19 +9,8 @@ #include #include #include -#include #include -#define cache_op(op, addr) \ - __asm__ __volatile__( \ - " .set push\n" \ - " .set noreorder\n" \ - " .set mips64\n" \ - " cache %0, %1\n" \ - " .set pop\n" \ - : \ - : "i" (op), "R" (*(unsigned char *)(addr))) - void __attribute__((weak)) _machine_restart(void) { fprintf(stderr, "*** reset failed ***\n"); @@ -37,53 +26,6 @@ int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 0; } -void flush_cache(ulong start_addr, ulong size) -{ - unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE; - unsigned long addr = start_addr & ~(lsize - 1); - unsigned long aend = (start_addr + size - 1) & ~(lsize - 1); - - /* aend will be miscalculated when size is zero, so we return here */ - if (size == 0) - return; - - while (1) { - cache_op(HIT_WRITEBACK_INV_D, addr); - cache_op(HIT_INVALIDATE_I, addr); - if (addr == aend) - break; - addr += lsize; - } -} - -void flush_dcache_range(ulong start_addr, ulong stop) -{ - unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE; - unsigned long addr = start_addr & ~(lsize - 1); - unsigned long aend = (stop - 1) & ~(lsize - 1); - - while (1) { - cache_op(HIT_WRITEBACK_INV_D, addr); - if (addr == aend) - break; - addr += lsize; - } -} - -void invalidate_dcache_range(ulong start_addr, ulong stop) -{ - unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE; - unsigned long addr = start_addr & ~(lsize - 1); - unsigned long aend = (stop - 1) & ~(lsize - 1); - - while (1) { - cache_op(HIT_INVALIDATE_D, addr); - if (addr == aend) - break; - addr += lsize; - } -} - void write_one_tlb(int index, u32 pagemask, u32 hi, u32 low0, u32 low1) { write_c0_entrylo0(low0); diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index 7f9b653..d939ee6 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-y += cache.o obj-y += io.o obj-$(CONFIG_CMD_BOOTM) += bootm.o diff --git a/arch/mips/lib/cache.c b/arch/mips/lib/cache.c new file mode 100644 index 0000000..e245614 --- /dev/null +++ b/arch/mips/lib/cache.c @@ -0,0 +1,118 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#ifdef CONFIG_SYS_CACHELINE_SIZE + +static inline unsigned long icache_line_size(void) +{ + return CONFIG_SYS_CACHELINE_SIZE; +} + +static inline unsigned long dcache_line_size(void) +{ + return CONFIG_SYS_CACHELINE_SIZE; +} + +#else /* !CONFIG_SYS_CACHELINE_SIZE */ + +static inline unsigned long icache_line_size(void) +{ + unsigned long conf1, il; + conf1 = read_c0_config1(); + il = (conf1 & MIPS_CONF1_IL) >> MIPS_CONF1_IL_SHIFT; + if (!il) + return 0; + return 2 << il; +} + +static inline unsigned long dcache_line_size(void) +{ + unsigned long conf1, dl; + conf1 = read_c0_config1(); + dl = (conf1 & MIPS_CONF1_DL) >> MIPS_CONF1_DL_SHIFT; + if (!dl) + return 0; + return 2 << dl; +} + +#endif /* !CONFIG_SYS_CACHELINE_SIZE */ + +void flush_cache(ulong start_addr, ulong size) +{ + unsigned long ilsize = icache_line_size(); + unsigned long dlsize = dcache_line_size(); + const void *addr, *aend; + + /* aend will be miscalculated when size is zero, so we return here */ + if (size == 0) + return; + + addr = (const void *)(start_addr & ~(dlsize - 1)); + aend = (const void *)((start_addr + size - 1) & ~(dlsize - 1)); + + if (ilsize == dlsize) { + /* flush I-cache & D-cache simultaneously */ + while (1) { + mips_cache(HIT_WRITEBACK_INV_D, addr); + mips_cache(HIT_INVALIDATE_I, addr); + if (addr == aend) + break; + addr += dlsize; + } + return; + } + + /* flush D-cache */ + while (1) { + mips_cache(HIT_WRITEBACK_INV_D, addr); + if (addr == aend) + break; + addr += dlsize; + } + + /* flush I-cache */ + addr = (const void *)(start_addr & ~(ilsize - 1)); + aend = (const void *)((start_addr + size - 1) & ~(ilsize - 1)); + while (1) { + mips_cache(HIT_INVALIDATE_I, addr); + if (addr == aend) + break; + addr += ilsize; + } +} + +void flush_dcache_range(ulong start_addr, ulong stop) +{ + unsigned long lsize = dcache_line_size(); + const void *addr = (const void *)(start_addr & ~(lsize - 1)); + const void *aend = (const void *)((stop - 1) & ~(lsize - 1)); + + while (1) { + mips_cache(HIT_WRITEBACK_INV_D, addr); + if (addr == aend) + break; + addr += lsize; + } +} + +void invalidate_dcache_range(ulong start_addr, ulong stop) +{ + unsigned long lsize = dcache_line_size(); + const void *addr = (const void *)(start_addr & ~(lsize - 1)); + const void *aend = (const void *)((stop - 1) & ~(lsize - 1)); + + while (1) { + mips_cache(HIT_INVALIDATE_D, addr); + if (addr == aend) + break; + addr += lsize; + } +} -- cgit v1.1 From 4a5d8898bca3e442b61e34b811aec8332752efd3 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 01:27:58 +0000 Subject: MIPS: unify cache initialization code The mips32 & mips64 cache initialization code differs only in that the mips32 code supports reading the cache size from coprocessor 0 registers at runtime. Move the more developed mips32 version to a common arch/mips/lib/cache_init.S & remove the now-redundant mips64 version in order to reduce duplication. The temporary registers used are shuffled slightly in order to work for both mips32 & mips64 builds. The RA register is defined differently to suit mips32 & mips64, but will be removed by a later commit in the series after further cleanup. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/cpu/mips32/Makefile | 3 +- arch/mips/cpu/mips32/cache.S | 273 ----------------------------------------- arch/mips/cpu/mips64/Makefile | 2 +- arch/mips/cpu/mips64/cache.S | 213 -------------------------------- arch/mips/lib/Makefile | 1 + arch/mips/lib/cache_init.S | 277 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 280 insertions(+), 489 deletions(-) delete mode 100644 arch/mips/cpu/mips32/cache.S delete mode 100644 arch/mips/cpu/mips64/cache.S create mode 100644 arch/mips/lib/cache_init.S (limited to 'arch') diff --git a/arch/mips/cpu/mips32/Makefile b/arch/mips/cpu/mips32/Makefile index fa82dd3..19d316a 100644 --- a/arch/mips/cpu/mips32/Makefile +++ b/arch/mips/cpu/mips32/Makefile @@ -6,7 +6,6 @@ # extra-y = start.o -obj-y = cache.o -obj-y += cpu.o interrupts.o time.o +obj-y = cpu.o interrupts.o time.o obj-$(CONFIG_SOC_AU1X00) += au1x00/ diff --git a/arch/mips/cpu/mips32/cache.S b/arch/mips/cpu/mips32/cache.S deleted file mode 100644 index fb1d84b..0000000 --- a/arch/mips/cpu/mips32/cache.S +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Cache-handling routined for MIPS CPUs - * - * Copyright (c) 2003 Wolfgang Denk - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include - -#ifndef CONFIG_SYS_MIPS_CACHE_MODE -#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT -#endif - -#define RA t9 - -#define INDEX_BASE CKSEG0 - - .macro f_fill64 dst, offset, val - LONG_S \val, (\offset + 0 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 1 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 2 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 3 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 4 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 5 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 6 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 7 * LONGSIZE)(\dst) -#if LONGSIZE == 4 - LONG_S \val, (\offset + 8 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 9 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 10 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 11 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 12 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 13 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 14 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 15 * LONGSIZE)(\dst) -#endif - .endm - -/* - * mips_init_icache(uint PRId, ulong icache_size, unchar icache_linesz) - */ -LEAF(mips_init_icache) - blez a1, 9f - mtc0 zero, CP0_TAGLO - /* clear tag to invalidate */ - PTR_LI t0, INDEX_BASE - PTR_ADDU t1, t0, a1 -1: cache INDEX_STORE_TAG_I, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 1b - /* fill once, so data field parity is correct */ - PTR_LI t0, INDEX_BASE -2: cache FILL, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 2b - /* invalidate again - prudent but not strictly neccessary */ - PTR_LI t0, INDEX_BASE -1: cache INDEX_STORE_TAG_I, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 1b -9: jr ra - END(mips_init_icache) - -/* - * mips_init_dcache(uint PRId, ulong dcache_size, unchar dcache_linesz) - */ -LEAF(mips_init_dcache) - blez a1, 9f - mtc0 zero, CP0_TAGLO - /* clear all tags */ - PTR_LI t0, INDEX_BASE - PTR_ADDU t1, t0, a1 -1: cache INDEX_STORE_TAG_D, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 1b - /* load from each line (in cached space) */ - PTR_LI t0, INDEX_BASE -2: LONG_L zero, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 2b - /* clear all tags */ - PTR_LI t0, INDEX_BASE -1: cache INDEX_STORE_TAG_D, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 1b -9: jr ra - END(mips_init_dcache) - -/* - * mips_cache_reset - low level initialisation of the primary caches - * - * This routine initialises the primary caches to ensure that they have good - * parity. It must be called by the ROM before any cached locations are used - * to prevent the possibility of data with bad parity being written to memory. - * - * To initialise the instruction cache it is essential that a source of data - * with good parity is available. This routine will initialise an area of - * memory starting at location zero to be used as a source of parity. - * - * RETURNS: N/A - * - */ -NESTED(mips_cache_reset, 0, ra) - move RA, ra - -#if !defined(CONFIG_SYS_ICACHE_SIZE) || !defined(CONFIG_SYS_DCACHE_SIZE) || \ - !defined(CONFIG_SYS_CACHELINE_SIZE) - /* read Config1 for use below */ - mfc0 t5, CP0_CONFIG, 1 -#endif - -#ifdef CONFIG_SYS_CACHELINE_SIZE - li t7, CONFIG_SYS_CACHELINE_SIZE - li t8, CONFIG_SYS_CACHELINE_SIZE -#else - /* Detect I-cache line size. */ - srl t8, t5, MIPS_CONF1_IL_SHIFT - andi t8, t8, (MIPS_CONF1_IL >> MIPS_CONF1_IL_SHIFT) - beqz t8, 1f - li t6, 2 - sllv t8, t6, t8 - -1: /* Detect D-cache line size. */ - srl t7, t5, MIPS_CONF1_DL_SHIFT - andi t7, t7, (MIPS_CONF1_DL >> MIPS_CONF1_DL_SHIFT) - beqz t7, 1f - li t6, 2 - sllv t7, t6, t7 -1: -#endif - -#ifdef CONFIG_SYS_ICACHE_SIZE - li t2, CONFIG_SYS_ICACHE_SIZE -#else - /* Detect I-cache size. */ - srl t6, t5, MIPS_CONF1_IS_SHIFT - andi t6, t6, (MIPS_CONF1_IS >> MIPS_CONF1_IS_SHIFT) - li t4, 32 - xori t2, t6, 0x7 - beqz t2, 1f - addi t6, t6, 1 - sllv t4, t4, t6 -1: /* At this point t4 == I-cache sets. */ - mul t2, t4, t8 - srl t6, t5, MIPS_CONF1_IA_SHIFT - andi t6, t6, (MIPS_CONF1_IA >> MIPS_CONF1_IA_SHIFT) - addi t6, t6, 1 - /* At this point t6 == I-cache ways. */ - mul t2, t2, t6 -#endif - -#ifdef CONFIG_SYS_DCACHE_SIZE - li t3, CONFIG_SYS_DCACHE_SIZE -#else - /* Detect D-cache size. */ - srl t6, t5, MIPS_CONF1_DS_SHIFT - andi t6, t6, (MIPS_CONF1_DS >> MIPS_CONF1_DS_SHIFT) - li t4, 32 - xori t3, t6, 0x7 - beqz t3, 1f - addi t6, t6, 1 - sllv t4, t4, t6 -1: /* At this point t4 == I-cache sets. */ - mul t3, t4, t7 - srl t6, t5, MIPS_CONF1_DA_SHIFT - andi t6, t6, (MIPS_CONF1_DA >> MIPS_CONF1_DA_SHIFT) - addi t6, t6, 1 - /* At this point t6 == I-cache ways. */ - mul t3, t3, t6 -#endif - - /* Determine the largest L1 cache size */ -#if defined(CONFIG_SYS_ICACHE_SIZE) && defined(CONFIG_SYS_DCACHE_SIZE) -#if CONFIG_SYS_ICACHE_SIZE > CONFIG_SYS_DCACHE_SIZE - li v0, CONFIG_SYS_ICACHE_SIZE -#else - li v0, CONFIG_SYS_DCACHE_SIZE -#endif -#else - move v0, t2 - sltu t1, t2, t3 - movn v0, t3, t1 -#endif - /* - * Now clear that much memory starting from zero. - */ - PTR_LI a0, CKSEG1 - PTR_ADDU a1, a0, v0 -2: PTR_ADDIU a0, 64 - f_fill64 a0, -64, zero - bne a0, a1, 2b - - /* - * The caches are probably in an indeterminate state, - * so we force good parity into them by doing an - * invalidate, load/fill, invalidate for each line. - */ - - /* - * Assume bottom of RAM will generate good parity for the cache. - */ - - /* - * Initialize the I-cache first, - */ - move a1, t2 - move a2, t8 - PTR_LA v1, mips_init_icache - jalr v1 - - /* - * then initialize D-cache. - */ - move a1, t3 - move a2, t7 - PTR_LA v1, mips_init_dcache - jalr v1 - - jr RA - END(mips_cache_reset) - -/* - * dcache_status - get cache status - * - * RETURNS: 0 - cache disabled; 1 - cache enabled - * - */ -LEAF(dcache_status) - mfc0 t0, CP0_CONFIG - li t1, CONF_CM_UNCACHED - andi t0, t0, CONF_CM_CMASK - move v0, zero - beq t0, t1, 2f - li v0, 1 -2: jr ra - END(dcache_status) - -/* - * dcache_disable - disable cache - * - * RETURNS: N/A - * - */ -LEAF(dcache_disable) - mfc0 t0, CP0_CONFIG - li t1, -8 - and t0, t0, t1 - ori t0, t0, CONF_CM_UNCACHED - mtc0 t0, CP0_CONFIG - jr ra - END(dcache_disable) - -/* - * dcache_enable - enable cache - * - * RETURNS: N/A - * - */ -LEAF(dcache_enable) - mfc0 t0, CP0_CONFIG - ori t0, CONF_CM_CMASK - xori t0, CONF_CM_CMASK - ori t0, CONFIG_SYS_MIPS_CACHE_MODE - mtc0 t0, CP0_CONFIG - jr ra - END(dcache_enable) diff --git a/arch/mips/cpu/mips64/Makefile b/arch/mips/cpu/mips64/Makefile index 899c319..cb4db9c 100644 --- a/arch/mips/cpu/mips64/Makefile +++ b/arch/mips/cpu/mips64/Makefile @@ -6,4 +6,4 @@ # extra-y = start.o -obj-y = cpu.o interrupts.o time.o cache.o +obj-y = cpu.o interrupts.o time.o diff --git a/arch/mips/cpu/mips64/cache.S b/arch/mips/cpu/mips64/cache.S deleted file mode 100644 index 36d8688..0000000 --- a/arch/mips/cpu/mips64/cache.S +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Cache-handling routined for MIPS CPUs - * - * Copyright (c) 2003 Wolfgang Denk - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include - -#define RA t9 - -/* - * 16kB is the maximum size of instruction and data caches on MIPS 4K, - * 64kB is on 4KE, 24K, 5K, etc. Set bigger size for convenience. - * - * Note that the above size is the maximum size of primary cache. U-Boot - * doesn't have L2 cache support for now. - */ -#define MIPS_MAX_CACHE_SIZE 0x10000 - -#define INDEX_BASE CKSEG0 - - .macro cache_op op addr - .set push - .set noreorder - .set mips3 - cache \op, 0(\addr) - .set pop - .endm - - .macro f_fill64 dst, offset, val - LONG_S \val, (\offset + 0 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 1 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 2 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 3 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 4 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 5 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 6 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 7 * LONGSIZE)(\dst) -#if LONGSIZE == 4 - LONG_S \val, (\offset + 8 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 9 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 10 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 11 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 12 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 13 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 14 * LONGSIZE)(\dst) - LONG_S \val, (\offset + 15 * LONGSIZE)(\dst) -#endif - .endm - -/* - * mips_init_icache(uint PRId, ulong icache_size, unchar icache_linesz) - */ -LEAF(mips_init_icache) - blez a1, 9f - mtc0 zero, CP0_TAGLO - /* clear tag to invalidate */ - PTR_LI t0, INDEX_BASE - PTR_ADDU t1, t0, a1 -1: cache_op INDEX_STORE_TAG_I t0 - PTR_ADDU t0, a2 - bne t0, t1, 1b - /* fill once, so data field parity is correct */ - PTR_LI t0, INDEX_BASE -2: cache_op FILL t0 - PTR_ADDU t0, a2 - bne t0, t1, 2b - /* invalidate again - prudent but not strictly neccessary */ - PTR_LI t0, INDEX_BASE -1: cache_op INDEX_STORE_TAG_I t0 - PTR_ADDU t0, a2 - bne t0, t1, 1b -9: jr ra - END(mips_init_icache) - -/* - * mips_init_dcache(uint PRId, ulong dcache_size, unchar dcache_linesz) - */ -LEAF(mips_init_dcache) - blez a1, 9f - mtc0 zero, CP0_TAGLO - /* clear all tags */ - PTR_LI t0, INDEX_BASE - PTR_ADDU t1, t0, a1 -1: cache_op INDEX_STORE_TAG_D t0 - PTR_ADDU t0, a2 - bne t0, t1, 1b - /* load from each line (in cached space) */ - PTR_LI t0, INDEX_BASE -2: LONG_L zero, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 2b - /* clear all tags */ - PTR_LI t0, INDEX_BASE -1: cache_op INDEX_STORE_TAG_D t0 - PTR_ADDU t0, a2 - bne t0, t1, 1b -9: jr ra - END(mips_init_dcache) - -/* - * mips_cache_reset - low level initialisation of the primary caches - * - * This routine initialises the primary caches to ensure that they have good - * parity. It must be called by the ROM before any cached locations are used - * to prevent the possibility of data with bad parity being written to memory. - * - * To initialise the instruction cache it is essential that a source of data - * with good parity is available. This routine will initialise an area of - * memory starting at location zero to be used as a source of parity. - * - * RETURNS: N/A - * - */ -NESTED(mips_cache_reset, 0, ra) - move RA, ra - li t2, CONFIG_SYS_ICACHE_SIZE - li t3, CONFIG_SYS_DCACHE_SIZE - li t8, CONFIG_SYS_CACHELINE_SIZE - - li v0, MIPS_MAX_CACHE_SIZE - - /* - * Now clear that much memory starting from zero. - */ - PTR_LI a0, CKSEG1 - PTR_ADDU a1, a0, v0 -2: PTR_ADDIU a0, 64 - f_fill64 a0, -64, zero - bne a0, a1, 2b - - /* - * The caches are probably in an indeterminate state, - * so we force good parity into them by doing an - * invalidate, load/fill, invalidate for each line. - */ - - /* - * Assume bottom of RAM will generate good parity for the cache. - */ - - /* - * Initialize the I-cache first, - */ - move a1, t2 - move a2, t8 - PTR_LA v1, mips_init_icache - jalr v1 - - /* - * then initialize D-cache. - */ - move a1, t3 - move a2, t8 - PTR_LA v1, mips_init_dcache - jalr v1 - - jr RA - END(mips_cache_reset) - -/* - * dcache_status - get cache status - * - * RETURNS: 0 - cache disabled; 1 - cache enabled - * - */ -LEAF(dcache_status) - mfc0 t0, CP0_CONFIG - li t1, CONF_CM_UNCACHED - andi t0, t0, CONF_CM_CMASK - move v0, zero - beq t0, t1, 2f - li v0, 1 -2: jr ra - END(dcache_status) - -/* - * dcache_disable - disable cache - * - * RETURNS: N/A - * - */ -LEAF(dcache_disable) - mfc0 t0, CP0_CONFIG - li t1, -8 - and t0, t0, t1 - ori t0, t0, CONF_CM_UNCACHED - mtc0 t0, CP0_CONFIG - jr ra - END(dcache_disable) - -/* - * dcache_enable - enable cache - * - * RETURNS: N/A - * - */ -LEAF(dcache_enable) - mfc0 t0, CP0_CONFIG - ori t0, CONF_CM_CMASK - xori t0, CONF_CM_CMASK - ori t0, CONF_CM_CACHABLE_NONCOHERENT - mtc0 t0, CP0_CONFIG - jr ra - END(dcache_enable) diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index d939ee6..ac536da 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -6,6 +6,7 @@ # obj-y += cache.o +obj-y += cache_init.o obj-y += io.o obj-$(CONFIG_CMD_BOOTM) += bootm.o diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S new file mode 100644 index 0000000..6c02bf9 --- /dev/null +++ b/arch/mips/lib/cache_init.S @@ -0,0 +1,277 @@ +/* + * Cache-handling routined for MIPS CPUs + * + * Copyright (c) 2003 Wolfgang Denk + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_SYS_MIPS_CACHE_MODE +#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT +#endif + +#ifdef CONFIG_64BIT +# define RA ta3 +#else +# define RA t7 +#endif + +#define INDEX_BASE CKSEG0 + + .macro f_fill64 dst, offset, val + LONG_S \val, (\offset + 0 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 1 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 2 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 3 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 4 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 5 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 6 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 7 * LONGSIZE)(\dst) +#if LONGSIZE == 4 + LONG_S \val, (\offset + 8 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 9 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 10 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 11 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 12 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 13 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 14 * LONGSIZE)(\dst) + LONG_S \val, (\offset + 15 * LONGSIZE)(\dst) +#endif + .endm + +/* + * mips_init_icache(uint PRId, ulong icache_size, unchar icache_linesz) + */ +LEAF(mips_init_icache) + blez a1, 9f + mtc0 zero, CP0_TAGLO + /* clear tag to invalidate */ + PTR_LI t0, INDEX_BASE + PTR_ADDU t1, t0, a1 +1: cache INDEX_STORE_TAG_I, 0(t0) + PTR_ADDU t0, a2 + bne t0, t1, 1b + /* fill once, so data field parity is correct */ + PTR_LI t0, INDEX_BASE +2: cache FILL, 0(t0) + PTR_ADDU t0, a2 + bne t0, t1, 2b + /* invalidate again - prudent but not strictly neccessary */ + PTR_LI t0, INDEX_BASE +1: cache INDEX_STORE_TAG_I, 0(t0) + PTR_ADDU t0, a2 + bne t0, t1, 1b +9: jr ra + END(mips_init_icache) + +/* + * mips_init_dcache(uint PRId, ulong dcache_size, unchar dcache_linesz) + */ +LEAF(mips_init_dcache) + blez a1, 9f + mtc0 zero, CP0_TAGLO + /* clear all tags */ + PTR_LI t0, INDEX_BASE + PTR_ADDU t1, t0, a1 +1: cache INDEX_STORE_TAG_D, 0(t0) + PTR_ADDU t0, a2 + bne t0, t1, 1b + /* load from each line (in cached space) */ + PTR_LI t0, INDEX_BASE +2: LONG_L zero, 0(t0) + PTR_ADDU t0, a2 + bne t0, t1, 2b + /* clear all tags */ + PTR_LI t0, INDEX_BASE +1: cache INDEX_STORE_TAG_D, 0(t0) + PTR_ADDU t0, a2 + bne t0, t1, 1b +9: jr ra + END(mips_init_dcache) + +/* + * mips_cache_reset - low level initialisation of the primary caches + * + * This routine initialises the primary caches to ensure that they have good + * parity. It must be called by the ROM before any cached locations are used + * to prevent the possibility of data with bad parity being written to memory. + * + * To initialise the instruction cache it is essential that a source of data + * with good parity is available. This routine will initialise an area of + * memory starting at location zero to be used as a source of parity. + * + * RETURNS: N/A + * + */ +NESTED(mips_cache_reset, 0, ra) + move RA, ra + +#if !defined(CONFIG_SYS_ICACHE_SIZE) || !defined(CONFIG_SYS_DCACHE_SIZE) || \ + !defined(CONFIG_SYS_CACHELINE_SIZE) + /* read Config1 for use below */ + mfc0 t5, CP0_CONFIG, 1 +#endif + +#ifdef CONFIG_SYS_CACHELINE_SIZE + li t9, CONFIG_SYS_CACHELINE_SIZE + li t8, CONFIG_SYS_CACHELINE_SIZE +#else + /* Detect I-cache line size. */ + srl t8, t5, MIPS_CONF1_IL_SHIFT + andi t8, t8, (MIPS_CONF1_IL >> MIPS_CONF1_IL_SHIFT) + beqz t8, 1f + li t6, 2 + sllv t8, t6, t8 + +1: /* Detect D-cache line size. */ + srl t9, t5, MIPS_CONF1_DL_SHIFT + andi t9, t9, (MIPS_CONF1_DL >> MIPS_CONF1_DL_SHIFT) + beqz t9, 1f + li t6, 2 + sllv t9, t6, t9 +1: +#endif + +#ifdef CONFIG_SYS_ICACHE_SIZE + li t2, CONFIG_SYS_ICACHE_SIZE +#else + /* Detect I-cache size. */ + srl t6, t5, MIPS_CONF1_IS_SHIFT + andi t6, t6, (MIPS_CONF1_IS >> MIPS_CONF1_IS_SHIFT) + li t4, 32 + xori t2, t6, 0x7 + beqz t2, 1f + addi t6, t6, 1 + sllv t4, t4, t6 +1: /* At this point t4 == I-cache sets. */ + mul t2, t4, t8 + srl t6, t5, MIPS_CONF1_IA_SHIFT + andi t6, t6, (MIPS_CONF1_IA >> MIPS_CONF1_IA_SHIFT) + addi t6, t6, 1 + /* At this point t6 == I-cache ways. */ + mul t2, t2, t6 +#endif + +#ifdef CONFIG_SYS_DCACHE_SIZE + li t3, CONFIG_SYS_DCACHE_SIZE +#else + /* Detect D-cache size. */ + srl t6, t5, MIPS_CONF1_DS_SHIFT + andi t6, t6, (MIPS_CONF1_DS >> MIPS_CONF1_DS_SHIFT) + li t4, 32 + xori t3, t6, 0x7 + beqz t3, 1f + addi t6, t6, 1 + sllv t4, t4, t6 +1: /* At this point t4 == I-cache sets. */ + mul t3, t4, t9 + srl t6, t5, MIPS_CONF1_DA_SHIFT + andi t6, t6, (MIPS_CONF1_DA >> MIPS_CONF1_DA_SHIFT) + addi t6, t6, 1 + /* At this point t6 == I-cache ways. */ + mul t3, t3, t6 +#endif + + /* Determine the largest L1 cache size */ +#if defined(CONFIG_SYS_ICACHE_SIZE) && defined(CONFIG_SYS_DCACHE_SIZE) +#if CONFIG_SYS_ICACHE_SIZE > CONFIG_SYS_DCACHE_SIZE + li v0, CONFIG_SYS_ICACHE_SIZE +#else + li v0, CONFIG_SYS_DCACHE_SIZE +#endif +#else + move v0, t2 + sltu t1, t2, t3 + movn v0, t3, t1 +#endif + /* + * Now clear that much memory starting from zero. + */ + PTR_LI a0, CKSEG1 + PTR_ADDU a1, a0, v0 +2: PTR_ADDIU a0, 64 + f_fill64 a0, -64, zero + bne a0, a1, 2b + + /* + * The caches are probably in an indeterminate state, + * so we force good parity into them by doing an + * invalidate, load/fill, invalidate for each line. + */ + + /* + * Assume bottom of RAM will generate good parity for the cache. + */ + + /* + * Initialize the I-cache first, + */ + move a1, t2 + move a2, t8 + PTR_LA v1, mips_init_icache + jalr v1 + + /* + * then initialize D-cache. + */ + move a1, t3 + move a2, t9 + PTR_LA v1, mips_init_dcache + jalr v1 + + jr RA + END(mips_cache_reset) + +/* + * dcache_status - get cache status + * + * RETURNS: 0 - cache disabled; 1 - cache enabled + * + */ +LEAF(dcache_status) + mfc0 t0, CP0_CONFIG + li t1, CONF_CM_UNCACHED + andi t0, t0, CONF_CM_CMASK + move v0, zero + beq t0, t1, 2f + li v0, 1 +2: jr ra + END(dcache_status) + +/* + * dcache_disable - disable cache + * + * RETURNS: N/A + * + */ +LEAF(dcache_disable) + mfc0 t0, CP0_CONFIG + li t1, -8 + and t0, t0, t1 + ori t0, t0, CONF_CM_UNCACHED + mtc0 t0, CP0_CONFIG + jr ra + END(dcache_disable) + +/* + * dcache_enable - enable cache + * + * RETURNS: N/A + * + */ +LEAF(dcache_enable) + mfc0 t0, CP0_CONFIG + ori t0, CONF_CM_CMASK + xori t0, CONF_CM_CMASK + ori t0, CONFIG_SYS_MIPS_CACHE_MODE + mtc0 t0, CP0_CONFIG + jr ra + END(dcache_enable) -- cgit v1.1 From 536cb7ce1aca10c326ac864b3e1d05ab57b3ec7e Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 01:27:59 +0000 Subject: MIPS: refactor L1 cache config reads to a macro Reduce duplication between reading the configuration of the L1 dcache & icache by performing both using a macro which calculates the appropriate line & cache sizes from the coprocessor 0 Config1 register. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/lib/cache_init.S | 97 ++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 56 deletions(-) (limited to 'arch') diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index 6c02bf9..2e036d9 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -97,6 +97,43 @@ LEAF(mips_init_dcache) 9: jr ra END(mips_init_dcache) + .macro l1_info sz, line_sz, off + .set push + .set noat + + mfc0 $1, CP0_CONFIG, 1 + + /* detect line size */ + srl \line_sz, $1, \off + MIPS_CONF1_DL_SHIFT - MIPS_CONF1_DA_SHIFT + andi \line_sz, \line_sz, (MIPS_CONF1_DL >> MIPS_CONF1_DL_SHIFT) + move \sz, zero + beqz \line_sz, 10f + li \sz, 2 + sllv \line_sz, \sz, \line_sz + + /* detect associativity */ + srl \sz, $1, \off + MIPS_CONF1_DA_SHIFT - MIPS_CONF1_DA_SHIFT + andi \sz, \sz, (MIPS_CONF1_DA >> MIPS_CONF1_DA_SHIFT) + addi \sz, \sz, 1 + + /* sz *= line_sz */ + mul \sz, \sz, \line_sz + + /* detect log32(sets) */ + srl $1, $1, \off + MIPS_CONF1_DS_SHIFT - MIPS_CONF1_DA_SHIFT + andi $1, $1, (MIPS_CONF1_DS >> MIPS_CONF1_DS_SHIFT) + addiu $1, $1, 1 + andi $1, $1, 0x7 + + /* sz <<= log32(sets) */ + sllv \sz, \sz, $1 + + /* sz *= 32 */ + li $1, 32 + mul \sz, \sz, $1 +10: + .set pop + .endm /* * mips_cache_reset - low level initialisation of the primary caches * @@ -114,70 +151,18 @@ LEAF(mips_init_dcache) NESTED(mips_cache_reset, 0, ra) move RA, ra -#if !defined(CONFIG_SYS_ICACHE_SIZE) || !defined(CONFIG_SYS_DCACHE_SIZE) || \ - !defined(CONFIG_SYS_CACHELINE_SIZE) - /* read Config1 for use below */ - mfc0 t5, CP0_CONFIG, 1 -#endif - -#ifdef CONFIG_SYS_CACHELINE_SIZE - li t9, CONFIG_SYS_CACHELINE_SIZE - li t8, CONFIG_SYS_CACHELINE_SIZE -#else - /* Detect I-cache line size. */ - srl t8, t5, MIPS_CONF1_IL_SHIFT - andi t8, t8, (MIPS_CONF1_IL >> MIPS_CONF1_IL_SHIFT) - beqz t8, 1f - li t6, 2 - sllv t8, t6, t8 - -1: /* Detect D-cache line size. */ - srl t9, t5, MIPS_CONF1_DL_SHIFT - andi t9, t9, (MIPS_CONF1_DL >> MIPS_CONF1_DL_SHIFT) - beqz t9, 1f - li t6, 2 - sllv t9, t6, t9 -1: -#endif - #ifdef CONFIG_SYS_ICACHE_SIZE li t2, CONFIG_SYS_ICACHE_SIZE + li t8, CONFIG_SYS_CACHELINE_SIZE #else - /* Detect I-cache size. */ - srl t6, t5, MIPS_CONF1_IS_SHIFT - andi t6, t6, (MIPS_CONF1_IS >> MIPS_CONF1_IS_SHIFT) - li t4, 32 - xori t2, t6, 0x7 - beqz t2, 1f - addi t6, t6, 1 - sllv t4, t4, t6 -1: /* At this point t4 == I-cache sets. */ - mul t2, t4, t8 - srl t6, t5, MIPS_CONF1_IA_SHIFT - andi t6, t6, (MIPS_CONF1_IA >> MIPS_CONF1_IA_SHIFT) - addi t6, t6, 1 - /* At this point t6 == I-cache ways. */ - mul t2, t2, t6 + l1_info t2, t8, MIPS_CONF1_IA_SHIFT #endif #ifdef CONFIG_SYS_DCACHE_SIZE li t3, CONFIG_SYS_DCACHE_SIZE + li t9, CONFIG_SYS_CACHELINE_SIZE #else - /* Detect D-cache size. */ - srl t6, t5, MIPS_CONF1_DS_SHIFT - andi t6, t6, (MIPS_CONF1_DS >> MIPS_CONF1_DS_SHIFT) - li t4, 32 - xori t3, t6, 0x7 - beqz t3, 1f - addi t6, t6, 1 - sllv t4, t4, t6 -1: /* At this point t4 == I-cache sets. */ - mul t3, t4, t9 - srl t6, t5, MIPS_CONF1_DA_SHIFT - andi t6, t6, (MIPS_CONF1_DA >> MIPS_CONF1_DA_SHIFT) - addi t6, t6, 1 - /* At this point t6 == I-cache ways. */ - mul t3, t3, t6 + l1_info t3, t9, MIPS_CONF1_DA_SHIFT #endif /* Determine the largest L1 cache size */ -- cgit v1.1 From ac22feca1135b81ecc4d38995e98b59943d1bbf5 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 01:28:00 +0000 Subject: MIPS: refactor cache loops to a macro Reduce duplication by performing loops through cache tags using an assembler macro. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/lib/cache_init.S | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'arch') diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index 2e036d9..dc207a6 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -47,28 +47,28 @@ #endif .endm + .macro cache_loop curr, end, line_sz, op +10: cache \op, 0(\curr) + PTR_ADDU \curr, \curr, \line_sz + bne \curr, \end, 10b + .endm + /* * mips_init_icache(uint PRId, ulong icache_size, unchar icache_linesz) */ LEAF(mips_init_icache) blez a1, 9f mtc0 zero, CP0_TAGLO - /* clear tag to invalidate */ PTR_LI t0, INDEX_BASE PTR_ADDU t1, t0, a1 -1: cache INDEX_STORE_TAG_I, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 1b + /* clear tag to invalidate */ + cache_loop t0, t1, a2, INDEX_STORE_TAG_I /* fill once, so data field parity is correct */ PTR_LI t0, INDEX_BASE -2: cache FILL, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 2b + cache_loop t0, t1, a2, FILL /* invalidate again - prudent but not strictly neccessary */ PTR_LI t0, INDEX_BASE -1: cache INDEX_STORE_TAG_I, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 1b + cache_loop t0, t1, a2, INDEX_STORE_TAG_I 9: jr ra END(mips_init_icache) @@ -78,12 +78,10 @@ LEAF(mips_init_icache) LEAF(mips_init_dcache) blez a1, 9f mtc0 zero, CP0_TAGLO - /* clear all tags */ PTR_LI t0, INDEX_BASE PTR_ADDU t1, t0, a1 -1: cache INDEX_STORE_TAG_D, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 1b + /* clear all tags */ + cache_loop t0, t1, a2, INDEX_STORE_TAG_D /* load from each line (in cached space) */ PTR_LI t0, INDEX_BASE 2: LONG_L zero, 0(t0) @@ -91,9 +89,7 @@ LEAF(mips_init_dcache) bne t0, t1, 2b /* clear all tags */ PTR_LI t0, INDEX_BASE -1: cache INDEX_STORE_TAG_D, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 1b + cache_loop t0, t1, a2, INDEX_STORE_TAG_D 9: jr ra END(mips_init_dcache) -- cgit v1.1 From ca4e833cd6409c72e5b13ee803a4f08381e6d160 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 01:28:01 +0000 Subject: MIPS: inline mips_init_[id]cache functions The mips_init_[id]cache functions are small & only called once from a single callsite. Inlining them allows mips_cache_reset to avoid having to bother moving arguments around & leaves it a leaf function which is thus able to simply keep the return address live in the ra register throughout, simplifying the code. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/lib/cache_init.S | 86 +++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 58 deletions(-) (limited to 'arch') diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index dc207a6..cbd04bd 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -18,12 +18,6 @@ #define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT #endif -#ifdef CONFIG_64BIT -# define RA ta3 -#else -# define RA t7 -#endif - #define INDEX_BASE CKSEG0 .macro f_fill64 dst, offset, val @@ -53,46 +47,6 @@ bne \curr, \end, 10b .endm -/* - * mips_init_icache(uint PRId, ulong icache_size, unchar icache_linesz) - */ -LEAF(mips_init_icache) - blez a1, 9f - mtc0 zero, CP0_TAGLO - PTR_LI t0, INDEX_BASE - PTR_ADDU t1, t0, a1 - /* clear tag to invalidate */ - cache_loop t0, t1, a2, INDEX_STORE_TAG_I - /* fill once, so data field parity is correct */ - PTR_LI t0, INDEX_BASE - cache_loop t0, t1, a2, FILL - /* invalidate again - prudent but not strictly neccessary */ - PTR_LI t0, INDEX_BASE - cache_loop t0, t1, a2, INDEX_STORE_TAG_I -9: jr ra - END(mips_init_icache) - -/* - * mips_init_dcache(uint PRId, ulong dcache_size, unchar dcache_linesz) - */ -LEAF(mips_init_dcache) - blez a1, 9f - mtc0 zero, CP0_TAGLO - PTR_LI t0, INDEX_BASE - PTR_ADDU t1, t0, a1 - /* clear all tags */ - cache_loop t0, t1, a2, INDEX_STORE_TAG_D - /* load from each line (in cached space) */ - PTR_LI t0, INDEX_BASE -2: LONG_L zero, 0(t0) - PTR_ADDU t0, a2 - bne t0, t1, 2b - /* clear all tags */ - PTR_LI t0, INDEX_BASE - cache_loop t0, t1, a2, INDEX_STORE_TAG_D -9: jr ra - END(mips_init_dcache) - .macro l1_info sz, line_sz, off .set push .set noat @@ -144,9 +98,7 @@ LEAF(mips_init_dcache) * RETURNS: N/A * */ -NESTED(mips_cache_reset, 0, ra) - move RA, ra - +LEAF(mips_cache_reset) #ifdef CONFIG_SYS_ICACHE_SIZE li t2, CONFIG_SYS_ICACHE_SIZE li t8, CONFIG_SYS_CACHELINE_SIZE @@ -195,20 +147,38 @@ NESTED(mips_cache_reset, 0, ra) /* * Initialize the I-cache first, */ - move a1, t2 - move a2, t8 - PTR_LA v1, mips_init_icache - jalr v1 + blez t2, 1f + mtc0 zero, CP0_TAGLO + PTR_LI t0, INDEX_BASE + PTR_ADDU t1, t0, t2 + /* clear tag to invalidate */ + cache_loop t0, t1, t8, INDEX_STORE_TAG_I + /* fill once, so data field parity is correct */ + PTR_LI t0, INDEX_BASE + cache_loop t0, t1, t8, FILL + /* invalidate again - prudent but not strictly neccessary */ + PTR_LI t0, INDEX_BASE + cache_loop t0, t1, t8, INDEX_STORE_TAG_I /* * then initialize D-cache. */ - move a1, t3 - move a2, t9 - PTR_LA v1, mips_init_dcache - jalr v1 +1: blez t3, 3f + mtc0 zero, CP0_TAGLO + PTR_LI t0, INDEX_BASE + PTR_ADDU t1, t0, t3 + /* clear all tags */ + cache_loop t0, t1, t9, INDEX_STORE_TAG_D + /* load from each line (in cached space) */ + PTR_LI t0, INDEX_BASE +2: LONG_L zero, 0(t0) + PTR_ADDU t0, t9 + bne t0, t1, 2b + /* clear all tags */ + PTR_LI t0, INDEX_BASE + cache_loop t0, t1, t9, INDEX_STORE_TAG_D - jr RA +3: jr ra END(mips_cache_reset) /* -- cgit v1.1 From dd7c72006e51f0d27e5cb1dcf60d5b9bf307565e Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 01:28:02 +0000 Subject: MIPS: allow systems to skip loads during cache init Current MIPS systems do not require that loads be performed to force the parity of cache lines, a simple invalidate by clearing the tag for each line will suffice. Thus this patch makes the loads & subsequent second invalidation conditional upon the CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD option, and defines that for existing mips32 targets. Exceptions are malta where this is known to be unnecessary, and qemu-mips where caches are not implemented. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/Kconfig | 6 ++++++ arch/mips/lib/cache_init.S | 19 +++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index ef78929..bc4283d 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -36,6 +36,7 @@ config TARGET_VCT select SUPPORTS_BIG_ENDIAN select SUPPORTS_CPU_MIPS32_R1 select SUPPORTS_CPU_MIPS32_R2 + select SYS_MIPS_CACHE_INIT_RAM_LOAD config TARGET_DBAU1X00 bool "Support dbau1x00" @@ -43,12 +44,14 @@ config TARGET_DBAU1X00 select SUPPORTS_LITTLE_ENDIAN select SUPPORTS_CPU_MIPS32_R1 select SUPPORTS_CPU_MIPS32_R2 + select SYS_MIPS_CACHE_INIT_RAM_LOAD config TARGET_PB1X00 bool "Support pb1x00" select SUPPORTS_LITTLE_ENDIAN select SUPPORTS_CPU_MIPS32_R1 select SUPPORTS_CPU_MIPS32_R2 + select SYS_MIPS_CACHE_INIT_RAM_LOAD endchoice @@ -185,6 +188,9 @@ config 64BIT config SWAP_IO_SPACE bool +config SYS_MIPS_CACHE_INIT_RAM_LOAD + bool + endif endmenu diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index cbd04bd..04a36b2 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -113,6 +113,8 @@ LEAF(mips_cache_reset) l1_info t3, t9, MIPS_CONF1_DA_SHIFT #endif +#ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD + /* Determine the largest L1 cache size */ #if defined(CONFIG_SYS_ICACHE_SIZE) && defined(CONFIG_SYS_DCACHE_SIZE) #if CONFIG_SYS_ICACHE_SIZE > CONFIG_SYS_DCACHE_SIZE @@ -134,14 +136,15 @@ LEAF(mips_cache_reset) f_fill64 a0, -64, zero bne a0, a1, 2b - /* - * The caches are probably in an indeterminate state, - * so we force good parity into them by doing an - * invalidate, load/fill, invalidate for each line. - */ +#endif /* CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD */ /* - * Assume bottom of RAM will generate good parity for the cache. + * The caches are probably in an indeterminate state, so we force good + * parity into them by doing an invalidate for each line. If + * CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD is set then we'll proceed to + * perform a load/fill & a further invalidate for each line, assuming + * that the bottom of RAM (having just been cleared) will generate good + * parity for the cache. */ /* @@ -153,12 +156,14 @@ LEAF(mips_cache_reset) PTR_ADDU t1, t0, t2 /* clear tag to invalidate */ cache_loop t0, t1, t8, INDEX_STORE_TAG_I +#ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD /* fill once, so data field parity is correct */ PTR_LI t0, INDEX_BASE cache_loop t0, t1, t8, FILL /* invalidate again - prudent but not strictly neccessary */ PTR_LI t0, INDEX_BASE cache_loop t0, t1, t8, INDEX_STORE_TAG_I +#endif /* * then initialize D-cache. @@ -169,6 +174,7 @@ LEAF(mips_cache_reset) PTR_ADDU t1, t0, t3 /* clear all tags */ cache_loop t0, t1, t9, INDEX_STORE_TAG_D +#ifdef CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD /* load from each line (in cached space) */ PTR_LI t0, INDEX_BASE 2: LONG_L zero, 0(t0) @@ -177,6 +183,7 @@ LEAF(mips_cache_reset) /* clear all tags */ PTR_LI t0, INDEX_BASE cache_loop t0, t1, t9, INDEX_STORE_TAG_D +#endif 3: jr ra END(mips_cache_reset) -- cgit v1.1 From 8755d50706742e4d302a335f4e69dd6430ec12a2 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 01:28:03 +0000 Subject: MIPS: clear TagLo select 2 during cache init Current MIPS cores from Imagination Technologies use TagLo select 2 for the data cache. The architecture requires that it is safe for software to write to this register even if it isn't present, so take the trivial option of clearing both selects 0 & 2. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/lib/cache_init.S | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/mips/lib/cache_init.S b/arch/mips/lib/cache_init.S index 04a36b2..137d728 100644 --- a/arch/mips/lib/cache_init.S +++ b/arch/mips/lib/cache_init.S @@ -139,6 +139,14 @@ LEAF(mips_cache_reset) #endif /* CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD */ /* + * The TagLo registers used depend upon the CPU implementation, but the + * architecture requires that it is safe for software to write to both + * TagLo selects 0 & 2 covering supported cases. + */ + mtc0 zero, CP0_TAGLO + mtc0 zero, CP0_TAGLO, 2 + + /* * The caches are probably in an indeterminate state, so we force good * parity into them by doing an invalidate for each line. If * CONFIG_SYS_MIPS_CACHE_INIT_RAM_LOAD is set then we'll proceed to @@ -151,7 +159,6 @@ LEAF(mips_cache_reset) * Initialize the I-cache first, */ blez t2, 1f - mtc0 zero, CP0_TAGLO PTR_LI t0, INDEX_BASE PTR_ADDU t1, t0, t2 /* clear tag to invalidate */ @@ -169,7 +176,6 @@ LEAF(mips_cache_reset) * then initialize D-cache. */ 1: blez t3, 3f - mtc0 zero, CP0_TAGLO PTR_LI t0, INDEX_BASE PTR_ADDU t1, t0, t3 /* clear all tags */ -- cgit v1.1 From ba21a453a58fc02b890f4064b293b1cb09fe7484 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 10:38:20 +0000 Subject: malta: IDE support This patch adds IDE support to the MIPS Malta board. The IDE controller is enabled after probing the PCI bus and otherwise just makes use of U-boot generic IDE support. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/include/asm/malta.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'arch') diff --git a/arch/mips/include/asm/malta.h b/arch/mips/include/asm/malta.h index 9e7c045..d9ffc15 100644 --- a/arch/mips/include/asm/malta.h +++ b/arch/mips/include/asm/malta.h @@ -64,4 +64,9 @@ #define PCI_CFG_PIIX4_GENCFG_SERIRQ (1 << 16) +#define PCI_CFG_PIIX4_IDETIM_PRI 0x40 +#define PCI_CFG_PIIX4_IDETIM_SEC 0x42 + +#define PCI_CFG_PIIX4_IDETIM_IDE (1 << 15) + #endif /* _MIPS_ASM_MALTA_H */ -- cgit v1.1 From a39b1cb7f0ff6be3f39c25b9ec625de2a352da9e Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 10:04:08 +0000 Subject: MIPS: use asm.h macros in mips32 start.S Where the mips32 & mips64 implementations of start.S differ in terms of access sizes & offsets, use the appropriate macros from asm.h to abstract those differences away. This is in preparation for sharing a single copy of start.S between mips32 & mips64. The exception to this is loads of immediates to be written to the cop0 Config register, which is a 32bit register on mips64 and therefore constants written to it can be loaded as such. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/cpu/mips32/start.S | 116 +++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 55 deletions(-) (limited to 'arch') diff --git a/arch/mips/cpu/mips32/start.S b/arch/mips/cpu/mips32/start.S index 36b92cc..227af6d 100644 --- a/arch/mips/cpu/mips32/start.S +++ b/arch/mips/cpu/mips32/start.S @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -98,8 +99,8 @@ _start: reset: /* Clear watch registers */ - mtc0 zero, CP0_WATCHLO - mtc0 zero, CP0_WATCHHI + MTC0 zero, CP0_WATCHLO + MTC0 zero, CP0_WATCHHI /* WP(Watch Pending), SW0/1 should be cleared */ mtc0 zero, CP0_CAUSE @@ -116,21 +117,26 @@ reset: mtc0 t0, CP0_CONFIG #endif - /* Initialize $gp */ + /* + * Initialize $gp, force pointer sized alignment of bal instruction to + * forbid the compiler to put nop's between bal and _gp. This is + * required to keep _gp and ra aligned to 8 byte. + */ + .align PTRLOG bal 1f nop - .word _gp + PTR _gp 1: - lw gp, 0(ra) + PTR_L gp, 0(ra) #ifndef CONFIG_SKIP_LOWLEVEL_INIT /* Initialize any external memory */ - la t9, lowlevel_init + PTR_LA t9, lowlevel_init jalr t9 nop /* Initialize caches... */ - la t9, mips_cache_reset + PTR_LA t9, mips_cache_reset jalr t9 nop @@ -140,15 +146,15 @@ reset: #endif /* Set up temporary stack */ - li t0, -16 - li t1, CONFIG_SYS_INIT_SP_ADDR + PTR_LI t0, -16 + PTR_LI t1, CONFIG_SYS_INIT_SP_ADDR and sp, t1, t0 # force 16 byte alignment - sub sp, sp, GD_SIZE # reserve space for gd + PTR_SUB sp, sp, GD_SIZE # reserve space for gd and sp, sp, t0 # force 16 byte alignment move k0, sp # save gd pointer #ifdef CONFIG_SYS_MALLOC_F_LEN - li t2, CONFIG_SYS_MALLOC_F_LEN - sub sp, sp, t2 # reserve space for early malloc + PTR_LI t2, CONFIG_SYS_MALLOC_F_LEN + PTR_SUB sp, sp, t2 # reserve space for early malloc and sp, sp, t0 # force 16 byte alignment #endif move fp, sp @@ -158,14 +164,14 @@ reset: 1: sw zero, 0(t0) blt t0, t1, 1b - addi t0, 4 + PTR_ADDI t0, 4 #ifdef CONFIG_SYS_MALLOC_F_LEN - addu t0, k0, GD_MALLOC_BASE # gd->malloc_base offset + PTR_ADDU t0, k0, GD_MALLOC_BASE # gd->malloc_base offset sw sp, 0(t0) #endif - la t9, board_init_f + PTR_LA t9, board_init_f jr t9 move ra, zero @@ -188,14 +194,14 @@ relocate_code: move s0, a1 # save gd in s0 move s2, a2 # save destination address in s2 - li t0, CONFIG_SYS_MONITOR_BASE - sub s1, s2, t0 # s1 <-- relocation offset + PTR_LI t0, CONFIG_SYS_MONITOR_BASE + PTR_SUB s1, s2, t0 # s1 <-- relocation offset - la t3, in_ram - lw t2, -12(t3) # t2 <-- __image_copy_end + PTR_LA t3, in_ram + PTR_L t2, -(3 * PTRSIZE)(t3) # t2 <-- __image_copy_end move t1, a2 - add gp, s1 # adjust gp + PTR_ADD gp, s1 # adjust gp /* * t0 = source address @@ -205,26 +211,26 @@ relocate_code: 1: lw t3, 0(t0) sw t3, 0(t1) - addu t0, 4 + PTR_ADDU t0, 4 blt t0, t2, 1b - addu t1, 4 + PTR_ADDU t1, 4 /* If caches were enabled, we would have to flush them here. */ - sub a1, t1, s2 # a1 <-- size - la t9, flush_cache + PTR_SUB a1, t1, s2 # a1 <-- size + PTR_LA t9, flush_cache jalr t9 move a0, s2 # a0 <-- destination address /* Jump to where we've relocated ourselves */ - addi t0, s2, in_ram - _start + PTR_ADDI t0, s2, in_ram - _start jr t0 nop - .word __rel_dyn_end - .word __rel_dyn_start - .word __image_copy_end - .word _GLOBAL_OFFSET_TABLE_ - .word num_got_entries + PTR __rel_dyn_end + PTR __rel_dyn_start + PTR __image_copy_end + PTR _GLOBAL_OFFSET_TABLE_ + PTR num_got_entries in_ram: /* @@ -233,46 +239,46 @@ in_ram: * GOT[0] is reserved. GOT[1] is also reserved for the dynamic object * generated by GNU ld. Skip these reserved entries from relocation. */ - lw t3, -4(t0) # t3 <-- num_got_entries - lw t8, -8(t0) # t8 <-- _GLOBAL_OFFSET_TABLE_ - add t8, s1 # t8 now holds relocated _G_O_T_ - addi t8, t8, 8 # skipping first two entries - li t2, 2 + PTR_L t3, -(1 * PTRSIZE)(t0) # t3 <-- num_got_entries + PTR_L t8, -(2 * PTRSIZE)(t0) # t8 <-- _GLOBAL_OFFSET_TABLE_ + PTR_ADD t8, s1 # t8 now holds relocated _G_O_T_ + PTR_ADDI t8, t8, 2 * PTRSIZE # skipping first two entries + PTR_LI t2, 2 1: - lw t1, 0(t8) + PTR_L t1, 0(t8) beqz t1, 2f - add t1, s1 - sw t1, 0(t8) + PTR_ADD t1, s1 + PTR_S t1, 0(t8) 2: - addi t2, 1 + PTR_ADDI t2, 1 blt t2, t3, 1b - addi t8, 4 + PTR_ADDI t8, PTRSIZE /* Update dynamic relocations */ - lw t1, -16(t0) # t1 <-- __rel_dyn_start - lw t2, -20(t0) # t2 <-- __rel_dyn_end + PTR_L t1, -(4 * PTRSIZE)(t0) # t1 <-- __rel_dyn_start + PTR_L t2, -(5 * PTRSIZE)(t0) # t2 <-- __rel_dyn_end b 2f # skip first reserved entry - addi t1, 8 + PTR_ADDI t1, 2 * PTRSIZE 1: lw t8, -4(t1) # t8 <-- relocation info - li t3, 3 + PTR_LI t3, 3 bne t8, t3, 2f # skip non R_MIPS_REL32 entries nop - lw t3, -8(t1) # t3 <-- location to fix up in FLASH + PTR_L t3, -(2 * PTRSIZE)(t1) # t3 <-- location to fix up in FLASH - lw t8, 0(t3) # t8 <-- original pointer - add t8, s1 # t8 <-- adjusted pointer + PTR_L t8, 0(t3) # t8 <-- original pointer + PTR_ADD t8, s1 # t8 <-- adjusted pointer - add t3, s1 # t3 <-- location to fix up in RAM - sw t8, 0(t3) + PTR_ADD t3, s1 # t3 <-- location to fix up in RAM + PTR_S t8, 0(t3) 2: blt t1, t2, 1b - addi t1, 8 # each rel.dyn entry is 8 bytes + PTR_ADDI t1, 2 * PTRSIZE # each rel.dyn entry is 2*PTRSIZE bytes /* * Clear BSS @@ -280,17 +286,17 @@ in_ram: * GOT is now relocated. Thus __bss_start and __bss_end can be * accessed directly via $gp. */ - la t1, __bss_start # t1 <-- __bss_start - la t2, __bss_end # t2 <-- __bss_end + PTR_LA t1, __bss_start # t1 <-- __bss_start + PTR_LA t2, __bss_end # t2 <-- __bss_end 1: - sw zero, 0(t1) + PTR_S zero, 0(t1) blt t1, t2, 1b - addi t1, 4 + PTR_ADDI t1, PTRSIZE move a0, s0 # a0 <-- gd move a1, s2 - la t9, board_init_r + PTR_LA t9, board_init_r jr t9 move ra, zero -- cgit v1.1 From ab0d002677185e6a1bcaf92f507d140b96bb1053 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 10:04:09 +0000 Subject: MIPS: handle mips64 relocs in mips32 start.S In preparation for sharing a single copy of start.S between mips32 & mips64, handle mips64 relocations in the mips32 start.S when built for mips64. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/cpu/mips32/start.S | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/mips/cpu/mips32/start.S b/arch/mips/cpu/mips32/start.S index 227af6d..699c59a 100644 --- a/arch/mips/cpu/mips32/start.S +++ b/arch/mips/cpu/mips32/start.S @@ -21,6 +21,21 @@ CONFIG_SYS_INIT_SP_OFFSET) #endif +#ifdef CONFIG_32BIT +# define MIPS_RELOC 3 +#endif + +#ifdef CONFIG_64BIT +# ifdef CONFIG_SYS_LITTLE_ENDIAN +# define MIPS64_R_INFO(ssym, r_type3, r_type2, r_type) \ + (((r_type) << 24) | ((r_type2) << 16) | ((r_type3) << 8) | (ssym)) +# else +# define MIPS64_R_INFO(ssym, r_type3, r_type2, r_type) \ + ((r_type) | ((r_type2) << 8) | ((r_type3) << 16) | (ssym) << 24) +# endif +# define MIPS_RELOC MIPS64_R_INFO(0x00, 0x00, 0x12, 0x03) +#endif + /* * For the moment disable interrupts, mark the kernel mode and * set ST0_KX so that the CPU does not spit fire when using @@ -264,8 +279,8 @@ in_ram: 1: lw t8, -4(t1) # t8 <-- relocation info - PTR_LI t3, 3 - bne t8, t3, 2f # skip non R_MIPS_REL32 entries + PTR_LI t3, MIPS_RELOC + bne t8, t3, 2f # skip non-MIPS_RELOC entries nop PTR_L t3, -(2 * PTRSIZE)(t1) # t3 <-- location to fix up in FLASH -- cgit v1.1 From f1c64a08106db5ce87b6afe76785e2d4fddcff63 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 29 Jan 2015 10:04:10 +0000 Subject: MIPS: handle mips64 ST0_KX bit in mips32 start.S In preparation for sharing a single copy of start.S between mips32 & mips64, handle setting the KX bit of the cop0 Status register when the mips32 start.S is built for mips64. Signed-off-by: Paul Burton Cc: Daniel Schwierzeck --- arch/mips/cpu/mips32/start.S | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/mips/cpu/mips32/start.S b/arch/mips/cpu/mips32/start.S index 699c59a..3b5b622 100644 --- a/arch/mips/cpu/mips32/start.S +++ b/arch/mips/cpu/mips32/start.S @@ -23,6 +23,7 @@ #ifdef CONFIG_32BIT # define MIPS_RELOC 3 +# define STATUS_SET 0 #endif #ifdef CONFIG_64BIT @@ -34,6 +35,7 @@ ((r_type) | ((r_type2) << 8) | ((r_type3) << 16) | (ssym) << 24) # endif # define MIPS_RELOC MIPS64_R_INFO(0x00, 0x00, 0x12, 0x03) +# define STATUS_SET ST0_KX #endif /* @@ -120,7 +122,7 @@ reset: /* WP(Watch Pending), SW0/1 should be cleared */ mtc0 zero, CP0_CAUSE - setup_c0_status 0 0 + setup_c0_status STATUS_SET 0 /* Init Timer */ mtc0 zero, CP0_COUNT -- cgit v1.1 From d9a4a6223cc13fb43cbe291a00beeed1c847ed87 Mon Sep 17 00:00:00 2001 From: Daniel Schwierzeck Date: Thu, 29 Jan 2015 14:47:01 +0100 Subject: MIPS: move au1x00 SoC code to arch/mips/mach-au1x00 Move all au1x00 code out of arch/mips/cpu/mips32 to allow unification of CPU code in a later patch. The reorganization of the SoC specific header files will be done in a later patch series. Signed-off-by: Daniel Schwierzeck Cc: Paul Burton --- arch/mips/Makefile | 2 + arch/mips/cpu/mips32/Makefile | 2 - arch/mips/cpu/mips32/au1x00/Makefile | 8 - arch/mips/cpu/mips32/au1x00/au1x00_eth.c | 296 ----- arch/mips/cpu/mips32/au1x00/au1x00_ide.c | 15 - arch/mips/cpu/mips32/au1x00/au1x00_serial.c | 131 -- arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.c | 1609 ------------------------- arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.h | 416 ------- arch/mips/cpu/mips32/au1x00/config.mk | 8 - arch/mips/cpu/mips32/cpu.c | 8 - arch/mips/mach-au1x00/Makefile | 8 + arch/mips/mach-au1x00/au1x00_eth.c | 302 +++++ arch/mips/mach-au1x00/au1x00_ide.c | 15 + arch/mips/mach-au1x00/au1x00_serial.c | 131 ++ arch/mips/mach-au1x00/au1x00_usb_ohci.c | 1609 +++++++++++++++++++++++++ arch/mips/mach-au1x00/au1x00_usb_ohci.h | 416 +++++++ arch/mips/mach-au1x00/config.mk | 8 + 17 files changed, 2491 insertions(+), 2493 deletions(-) delete mode 100644 arch/mips/cpu/mips32/au1x00/Makefile delete mode 100644 arch/mips/cpu/mips32/au1x00/au1x00_eth.c delete mode 100644 arch/mips/cpu/mips32/au1x00/au1x00_ide.c delete mode 100644 arch/mips/cpu/mips32/au1x00/au1x00_serial.c delete mode 100644 arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.c delete mode 100644 arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.h delete mode 100644 arch/mips/cpu/mips32/au1x00/config.mk create mode 100644 arch/mips/mach-au1x00/Makefile create mode 100644 arch/mips/mach-au1x00/au1x00_eth.c create mode 100644 arch/mips/mach-au1x00/au1x00_ide.c create mode 100644 arch/mips/mach-au1x00/au1x00_serial.c create mode 100644 arch/mips/mach-au1x00/au1x00_usb_ohci.c create mode 100644 arch/mips/mach-au1x00/au1x00_usb_ohci.h create mode 100644 arch/mips/mach-au1x00/config.mk (limited to 'arch') diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 0a9e7e6..ff01bfe 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -8,3 +8,5 @@ head-$(CONFIG_CPU_MIPS64) := arch/mips/cpu/mips64/start.o libs-$(CONFIG_CPU_MIPS32) += arch/mips/cpu/mips32/ libs-$(CONFIG_CPU_MIPS64) += arch/mips/cpu/mips64/ libs-y += arch/mips/lib/ + +libs-$(CONFIG_SOC_AU1X00) += arch/mips/mach-au1x00/ diff --git a/arch/mips/cpu/mips32/Makefile b/arch/mips/cpu/mips32/Makefile index 19d316a..cb4db9c 100644 --- a/arch/mips/cpu/mips32/Makefile +++ b/arch/mips/cpu/mips32/Makefile @@ -7,5 +7,3 @@ extra-y = start.o obj-y = cpu.o interrupts.o time.o - -obj-$(CONFIG_SOC_AU1X00) += au1x00/ diff --git a/arch/mips/cpu/mips32/au1x00/Makefile b/arch/mips/cpu/mips32/au1x00/Makefile deleted file mode 100644 index c5643e7..0000000 --- a/arch/mips/cpu/mips32/au1x00/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2011 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y = au1x00_eth.o au1x00_serial.o au1x00_usb_ohci.o au1x00_ide.o diff --git a/arch/mips/cpu/mips32/au1x00/au1x00_eth.c b/arch/mips/cpu/mips32/au1x00/au1x00_eth.c deleted file mode 100644 index 4770f56..0000000 --- a/arch/mips/cpu/mips32/au1x00/au1x00_eth.c +++ /dev/null @@ -1,296 +0,0 @@ -/* Only eth0 supported for now - * - * (C) Copyright 2003 - * Thomas.Lange@corelatus.se - * - * SPDX-License-Identifier: GPL-2.0+ - */ -#include - -#if defined(CONFIG_SYS_DISCOVER_PHY) -#error "PHY not supported yet" -/* We just assume that we are running 100FD for now */ -/* We all use switches, right? ;-) */ -#endif - -/* I assume ethernet behaves like au1000 */ - -#ifdef CONFIG_SOC_AU1000 -/* Base address differ between cpu:s */ -#define ETH0_BASE AU1000_ETH0_BASE -#define MAC0_ENABLE AU1000_MAC0_ENABLE -#else -#ifdef CONFIG_SOC_AU1100 -#define ETH0_BASE AU1100_ETH0_BASE -#define MAC0_ENABLE AU1100_MAC0_ENABLE -#else -#ifdef CONFIG_SOC_AU1500 -#define ETH0_BASE AU1500_ETH0_BASE -#define MAC0_ENABLE AU1500_MAC0_ENABLE -#else -#ifdef CONFIG_SOC_AU1550 -#define ETH0_BASE AU1550_ETH0_BASE -#define MAC0_ENABLE AU1550_MAC0_ENABLE -#else -#error "No valid cpu set" -#endif -#endif -#endif -#endif - -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_CMD_MII) -#include -#endif - -/* Ethernet Transmit and Receive Buffers */ -#define DBUF_LENGTH 1520 -#define PKT_MAXBUF_SIZE 1518 - -static char txbuf[DBUF_LENGTH]; - -static int next_tx; -static int next_rx; - -/* 4 rx and 4 tx fifos */ -#define NO_OF_FIFOS 4 - -typedef struct{ - u32 status; - u32 addr; - u32 len; /* Only used for tx */ - u32 not_used; -} mac_fifo_t; - -mac_fifo_t mac_fifo[NO_OF_FIFOS]; - -#define MAX_WAIT 1000 - -#if defined(CONFIG_CMD_MII) -int au1x00_miiphy_read(const char *devname, unsigned char addr, - unsigned char reg, unsigned short * value) -{ - volatile u32 *mii_control_reg = (volatile u32*)(ETH0_BASE+MAC_MII_CNTRL); - volatile u32 *mii_data_reg = (volatile u32*)(ETH0_BASE+MAC_MII_DATA); - u32 mii_control; - unsigned int timedout = 20; - - while (*mii_control_reg & MAC_MII_BUSY) { - udelay(1000); - if (--timedout == 0) { - printf("au1x00_eth: miiphy_read busy timeout!!\n"); - return -1; - } - } - - mii_control = MAC_SET_MII_SELECT_REG(reg) | - MAC_SET_MII_SELECT_PHY(addr) | MAC_MII_READ; - - *mii_control_reg = mii_control; - - timedout = 20; - while (*mii_control_reg & MAC_MII_BUSY) { - udelay(1000); - if (--timedout == 0) { - printf("au1x00_eth: miiphy_read busy timeout!!\n"); - return -1; - } - } - *value = *mii_data_reg; - return 0; -} - -int au1x00_miiphy_write(const char *devname, unsigned char addr, - unsigned char reg, unsigned short value) -{ - volatile u32 *mii_control_reg = (volatile u32*)(ETH0_BASE+MAC_MII_CNTRL); - volatile u32 *mii_data_reg = (volatile u32*)(ETH0_BASE+MAC_MII_DATA); - u32 mii_control; - unsigned int timedout = 20; - - while (*mii_control_reg & MAC_MII_BUSY) { - udelay(1000); - if (--timedout == 0) { - printf("au1x00_eth: miiphy_write busy timeout!!\n"); - return -1; - } - } - - mii_control = MAC_SET_MII_SELECT_REG(reg) | - MAC_SET_MII_SELECT_PHY(addr) | MAC_MII_WRITE; - - *mii_data_reg = value; - *mii_control_reg = mii_control; - return 0; -} -#endif - -static int au1x00_send(struct eth_device *dev, void *packet, int length) -{ - volatile mac_fifo_t *fifo_tx = - (volatile mac_fifo_t*)(MAC0_TX_DMA_ADDR+MAC_TX_BUFF0_STATUS); - int i; - int res; - - /* tx fifo should always be idle */ - fifo_tx[next_tx].len = length; - fifo_tx[next_tx].addr = (virt_to_phys(packet))|TX_DMA_ENABLE; - au_sync(); - - udelay(1); - i=0; - while(!(fifo_tx[next_tx].addr&TX_T_DONE)){ - if(i>MAX_WAIT){ - printf("TX timeout\n"); - break; - } - udelay(1); - i++; - } - - /* Clear done bit */ - fifo_tx[next_tx].addr = 0; - fifo_tx[next_tx].len = 0; - au_sync(); - - res = fifo_tx[next_tx].status; - - next_tx++; - if(next_tx>=NO_OF_FIFOS){ - next_tx=0; - } - return(res); -} - -static int au1x00_recv(struct eth_device* dev){ - volatile mac_fifo_t *fifo_rx = - (volatile mac_fifo_t*)(MAC0_RX_DMA_ADDR+MAC_RX_BUFF0_STATUS); - - int length; - u32 status; - - for(;;){ - if(!(fifo_rx[next_rx].addr&RX_T_DONE)){ - /* Nothing has been received */ - return(-1); - } - - status = fifo_rx[next_rx].status; - - length = status&0x3FFF; - - if(status&RX_ERROR){ - printf("Rx error 0x%x\n", status); - } - else{ - /* Pass the packet up to the protocol layers. */ - NetReceive(NetRxPackets[next_rx], length - 4); - } - - fifo_rx[next_rx].addr = (virt_to_phys(NetRxPackets[next_rx]))|RX_DMA_ENABLE; - - next_rx++; - if(next_rx>=NO_OF_FIFOS){ - next_rx=0; - } - } /* for */ - - return(0); /* Does anyone use this? */ -} - -static int au1x00_init(struct eth_device* dev, bd_t * bd){ - - volatile u32 *macen = (volatile u32*)MAC0_ENABLE; - volatile u32 *mac_ctrl = (volatile u32*)(ETH0_BASE+MAC_CONTROL); - volatile u32 *mac_addr_high = (volatile u32*)(ETH0_BASE+MAC_ADDRESS_HIGH); - volatile u32 *mac_addr_low = (volatile u32*)(ETH0_BASE+MAC_ADDRESS_LOW); - volatile u32 *mac_mcast_high = (volatile u32*)(ETH0_BASE+MAC_MCAST_HIGH); - volatile u32 *mac_mcast_low = (volatile u32*)(ETH0_BASE+MAC_MCAST_LOW); - volatile mac_fifo_t *fifo_tx = - (volatile mac_fifo_t*)(MAC0_TX_DMA_ADDR+MAC_TX_BUFF0_STATUS); - volatile mac_fifo_t *fifo_rx = - (volatile mac_fifo_t*)(MAC0_RX_DMA_ADDR+MAC_RX_BUFF0_STATUS); - int i; - - next_tx = TX_GET_DMA_BUFFER(fifo_tx[0].addr); - next_rx = RX_GET_DMA_BUFFER(fifo_rx[0].addr); - - /* We have to enable clocks before releasing reset */ - *macen = MAC_EN_CLOCK_ENABLE; - udelay(10); - - /* Enable MAC0 */ - /* We have to release reset before accessing registers */ - *macen = MAC_EN_CLOCK_ENABLE|MAC_EN_RESET0| - MAC_EN_RESET1|MAC_EN_RESET2; - udelay(10); - - for(i=0;ienetaddr - *mac_addr_high = (ea[5] << 8) | (ea[4] ) ; - *mac_addr_low = (ea[3] << 24) | (ea[2] << 16) | - (ea[1] << 8) | (ea[0] ) ; -#undef ea - *mac_mcast_low = 0; - *mac_mcast_high = 0; - - /* Make sure the MAC buffer is in the correct endian mode */ -#ifdef __LITTLE_ENDIAN - *mac_ctrl = MAC_FULL_DUPLEX; - udelay(1); - *mac_ctrl = MAC_FULL_DUPLEX|MAC_RX_ENABLE|MAC_TX_ENABLE; -#else - *mac_ctrl = MAC_BIG_ENDIAN|MAC_FULL_DUPLEX; - udelay(1); - *mac_ctrl = MAC_BIG_ENDIAN|MAC_FULL_DUPLEX|MAC_RX_ENABLE|MAC_TX_ENABLE; -#endif - - return(1); -} - -static void au1x00_halt(struct eth_device* dev){ - volatile u32 *macen = (volatile u32*)MAC0_ENABLE; - - /* Put MAC0 in reset */ - *macen = 0; -} - -int au1x00_enet_initialize(bd_t *bis){ - struct eth_device* dev; - - if ((dev = (struct eth_device*)malloc(sizeof *dev)) == NULL) { - puts ("malloc failed\n"); - return -1; - } - - memset(dev, 0, sizeof *dev); - - sprintf(dev->name, "Au1X00 ethernet"); - dev->iobase = 0; - dev->priv = 0; - dev->init = au1x00_init; - dev->halt = au1x00_halt; - dev->send = au1x00_send; - dev->recv = au1x00_recv; - - eth_register(dev); - -#if defined(CONFIG_CMD_MII) - miiphy_register(dev->name, - au1x00_miiphy_read, au1x00_miiphy_write); -#endif - - return 1; -} diff --git a/arch/mips/cpu/mips32/au1x00/au1x00_ide.c b/arch/mips/cpu/mips32/au1x00/au1x00_ide.c deleted file mode 100644 index ba0b35d..0000000 --- a/arch/mips/cpu/mips32/au1x00/au1x00_ide.c +++ /dev/null @@ -1,15 +0,0 @@ -/* - * (C) Copyright 2000-2011 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include - -/* AU1X00 swaps data in big-endian mode, enforce little-endian function */ -void ide_input_swap_data(int dev, ulong *sect_buf, int words) -{ - ide_input_data(dev, sect_buf, words); -} diff --git a/arch/mips/cpu/mips32/au1x00/au1x00_serial.c b/arch/mips/cpu/mips32/au1x00/au1x00_serial.c deleted file mode 100644 index 0463508..0000000 --- a/arch/mips/cpu/mips32/au1x00/au1x00_serial.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * AU1X00 UART support - * - * Hardcoded to UART 0 for now - * Speed and options also hardcoded to 115200 8N1 - * - * Copyright (c) 2003 Thomas.Lange@corelatus.se - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include - -/****************************************************************************** -* -* serial_init - initialize a channel -* -* This routine initializes the number of data bits, parity -* and set the selected baud rate. Interrupts are disabled. -* Set the modem control signals if the option is selected. -* -* RETURNS: N/A -*/ - -static int au1x00_serial_init(void) -{ - volatile u32 *uart_fifoctl = (volatile u32*)(UART0_ADDR+UART_FCR); - volatile u32 *uart_enable = (volatile u32*)(UART0_ADDR+UART_ENABLE); - - /* Enable clocks first */ - *uart_enable = UART_EN_CE; - - /* Then release reset */ - /* Must release reset before setting other regs */ - *uart_enable = UART_EN_CE|UART_EN_E; - - /* Activate fifos, reset tx and rx */ - /* Set tx trigger level to 12 */ - *uart_fifoctl = UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR| - UART_FCR_CLEAR_XMIT|UART_FCR_T_TRIGGER_12; - - serial_setbrg(); - - return 0; -} - - -static void au1x00_serial_setbrg(void) -{ - volatile u32 *uart_clk = (volatile u32*)(UART0_ADDR+UART_CLK); - volatile u32 *uart_lcr = (volatile u32*)(UART0_ADDR+UART_LCR); - volatile u32 *sys_powerctrl = (u32 *)SYS_POWERCTRL; - int sd; - int divisorx2; - - /* sd is system clock divisor */ - /* see section 10.4.5 in au1550 datasheet */ - sd = (*sys_powerctrl & 0x03) + 2; - - /* calulate 2x baudrate and round */ - divisorx2 = ((CONFIG_SYS_MIPS_TIMER_FREQ/(sd * 16 * CONFIG_BAUDRATE))); - - if (divisorx2 & 0x01) - divisorx2 = divisorx2 + 1; - - *uart_clk = divisorx2 / 2; - - /* Set parity, stop bits and word length to 8N1 */ - *uart_lcr = UART_LCR_WLEN8; -} - -static void au1x00_serial_putc(const char c) -{ - volatile u32 *uart_lsr = (volatile u32*)(UART0_ADDR+UART_LSR); - volatile u32 *uart_tx = (volatile u32*)(UART0_ADDR+UART_TX); - - if (c == '\n') - au1x00_serial_putc('\r'); - - /* Wait for fifo to shift out some bytes */ - while((*uart_lsr&UART_LSR_THRE)==0); - - *uart_tx = (u32)c; -} - -static int au1x00_serial_getc(void) -{ - volatile u32 *uart_rx = (volatile u32*)(UART0_ADDR+UART_RX); - char c; - - while (!serial_tstc()); - - c = (*uart_rx&0xFF); - return c; -} - -static int au1x00_serial_tstc(void) -{ - volatile u32 *uart_lsr = (volatile u32*)(UART0_ADDR+UART_LSR); - - if(*uart_lsr&UART_LSR_DR){ - /* Data in rfifo */ - return(1); - } - return 0; -} - -static struct serial_device au1x00_serial_drv = { - .name = "au1x00_serial", - .start = au1x00_serial_init, - .stop = NULL, - .setbrg = au1x00_serial_setbrg, - .putc = au1x00_serial_putc, - .puts = default_serial_puts, - .getc = au1x00_serial_getc, - .tstc = au1x00_serial_tstc, -}; - -void au1x00_serial_initialize(void) -{ - serial_register(&au1x00_serial_drv); -} - -__weak struct serial_device *default_serial_console(void) -{ - return &au1x00_serial_drv; -} diff --git a/arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.c b/arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.c deleted file mode 100644 index 74bdb77..0000000 --- a/arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.c +++ /dev/null @@ -1,1609 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) for USB on the AU1x00. - * - * (C) Copyright 2003 - * Gary Jennejohn, DENX Software Engineering - * - * SPDX-License-Identifier: GPL-2.0+ - * Note: Part of this code has been derived from linux - * - */ -/* - * IMPORTANT NOTES - * 1 - this driver is intended for use with USB Mass Storage Devices - * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! - */ - -#include - -#ifdef CONFIG_USB_OHCI - -/* #include no PCI on the AU1x00 */ - -#include -#include -#include -#include -#include -#include "au1x00_usb_ohci.h" - -#define OHCI_USE_NPS /* force NoPowerSwitching mode */ -#define OHCI_VERBOSE_DEBUG /* not always helpful */ -#define OHCI_FILL_TRACE - -#define USBH_ENABLE_BE (1<<0) -#define USBH_ENABLE_C (1<<1) -#define USBH_ENABLE_E (1<<2) -#define USBH_ENABLE_CE (1<<3) -#define USBH_ENABLE_RD (1<<4) - -#ifdef __LITTLE_ENDIAN -#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C) -#else -#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE) -#endif - - -/* For initializing controller (mask in an HCFS mode too) */ -#define OHCI_CONTROL_INIT \ - (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE - -#undef readl -#undef writel - -#define readl(a) au_readl((long)(a)) -#define writel(v,a) au_writel((v),(int)(a)) - -#define DEBUG -#ifdef DEBUG -#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) -#else -#define dbg(format, arg...) do {} while(0) -#endif /* DEBUG */ -#define err(format, arg...) printf("ERROR: " format "\n", ## arg) -#define SHOW_INFO -#ifdef SHOW_INFO -#define info(format, arg...) printf("INFO: " format "\n", ## arg) -#else -#define info(format, arg...) do {} while(0) -#endif - -#define m16_swap(x) swap_16(x) -#define m32_swap(x) swap_32(x) - -/* global ohci_t */ -static ohci_t gohci; -/* this must be aligned to a 256 byte boundary */ -struct ohci_hcca ghcca[1]; -/* a pointer to the aligned storage */ -struct ohci_hcca *phcca; -/* this allocates EDs for all possible endpoints */ -struct ohci_device ohci_dev; -/* urb_priv */ -urb_priv_t urb_priv; -/* RHSC flag */ -int got_rhsc; -/* device which was disconnected */ -struct usb_device *devgone; - -/*-------------------------------------------------------------------------*/ - -/* AMD-756 (D2 rev) reports corrupt register contents in some cases. - * The erratum (#4) description is incorrect. AMD's workaround waits - * till some bits (mostly reserved) are clear; ok for all revs. - */ -#define OHCI_QUIRK_AMD756 0xabcd -#define read_roothub(hc, register, mask) ({ \ - u32 temp = readl (&hc->regs->roothub.register); \ - if (hc->flags & OHCI_QUIRK_AMD756) \ - while (temp & mask) \ - temp = readl (&hc->regs->roothub.register); \ - temp; }) - -static u32 roothub_a (struct ohci *hc) - { return read_roothub (hc, a, 0xfc0fe000); } -static inline u32 roothub_b (struct ohci *hc) - { return readl (&hc->regs->roothub.b); } -static inline u32 roothub_status (struct ohci *hc) - { return readl (&hc->regs->roothub.status); } -static u32 roothub_portstatus (struct ohci *hc, int i) - { return read_roothub (hc, portstatus [i], 0xffe0fce0); } - - -/* forward declaration */ -static int hc_interrupt (void); -static void -td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer, - int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval); - -/*-------------------------------------------------------------------------* - * URB support functions - *-------------------------------------------------------------------------*/ - -/* free HCD-private data associated with this URB */ - -static void urb_free_priv (urb_priv_t * urb) -{ - int i; - int last; - struct td * td; - - last = urb->length - 1; - if (last >= 0) { - for (i = 0; i <= last; i++) { - td = urb->td[i]; - if (td) { - td->usb_dev = NULL; - urb->td[i] = NULL; - } - } - } -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEBUG -static int sohci_get_current_frame_number (struct usb_device * dev); - -/* debug| print the main components of an URB - * small: 0) header + data packets 1) just header */ - -static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, - int transfer_len, struct devrequest * setup, char * str, int small) -{ - urb_priv_t * purb = &urb_priv; - - dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", - str, - sohci_get_current_frame_number (dev), - usb_pipedevice (pipe), - usb_pipeendpoint (pipe), - usb_pipeout (pipe)? 'O': 'I', - usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): - (usb_pipecontrol (pipe)? "CTRL": "BULK"), - purb->actual_length, - transfer_len, dev->status); -#ifdef OHCI_VERBOSE_DEBUG - if (!small) { - int i, len; - - if (usb_pipecontrol (pipe)) { - printf (__FILE__ ": cmd(8):"); - for (i = 0; i < 8 ; i++) - printf (" %02x", ((__u8 *) setup) [i]); - printf ("\n"); - } - if (transfer_len > 0 && buffer) { - printf (__FILE__ ": data(%d/%d):", - purb->actual_length, - transfer_len); - len = usb_pipeout (pipe)? - transfer_len: purb->actual_length; - for (i = 0; i < 16 && i < len; i++) - printf (" %02x", ((__u8 *) buffer) [i]); - printf ("%s\n", i < len? "...": ""); - } - } -#endif -} - -/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ -void ep_print_int_eds (ohci_t *ohci, char * str) { - int i, j; - __u32 * ed_p; - for (i= 0; i < 32; i++) { - j = 5; - ed_p = &(ohci->hcca->int_table [i]); - if (*ed_p == 0) - continue; - printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); - while (*ed_p != 0 && j--) { - ed_t *ed = (ed_t *)m32_swap(ed_p); - printf (" ed: %4x;", ed->hwINFO); - ed_p = &ed->hwNextED; - } - printf ("\n"); - } -} - -static void ohci_dump_intr_mask (char *label, __u32 mask) -{ - dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", - label, - mask, - (mask & OHCI_INTR_MIE) ? " MIE" : "", - (mask & OHCI_INTR_OC) ? " OC" : "", - (mask & OHCI_INTR_RHSC) ? " RHSC" : "", - (mask & OHCI_INTR_FNO) ? " FNO" : "", - (mask & OHCI_INTR_UE) ? " UE" : "", - (mask & OHCI_INTR_RD) ? " RD" : "", - (mask & OHCI_INTR_SF) ? " SF" : "", - (mask & OHCI_INTR_WDH) ? " WDH" : "", - (mask & OHCI_INTR_SO) ? " SO" : "" - ); -} - -static void maybe_print_eds (char *label, __u32 value) -{ - ed_t *edp = (ed_t *)value; - - if (value) { - dbg ("%s %08x", label, value); - dbg ("%08x", edp->hwINFO); - dbg ("%08x", edp->hwTailP); - dbg ("%08x", edp->hwHeadP); - dbg ("%08x", edp->hwNextED); - } -} - -static char * hcfs2string (int state) -{ - switch (state) { - case OHCI_USB_RESET: return "reset"; - case OHCI_USB_RESUME: return "resume"; - case OHCI_USB_OPER: return "operational"; - case OHCI_USB_SUSPEND: return "suspend"; - } - return "?"; -} - -/* dump control and status registers */ -static void ohci_dump_status (ohci_t *controller) -{ - struct ohci_regs *regs = controller->regs; - __u32 temp; - - temp = readl (®s->revision) & 0xff; - if (temp != 0x10) - dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); - - temp = readl (®s->control); - dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, - (temp & OHCI_CTRL_RWE) ? " RWE" : "", - (temp & OHCI_CTRL_RWC) ? " RWC" : "", - (temp & OHCI_CTRL_IR) ? " IR" : "", - hcfs2string (temp & OHCI_CTRL_HCFS), - (temp & OHCI_CTRL_BLE) ? " BLE" : "", - (temp & OHCI_CTRL_CLE) ? " CLE" : "", - (temp & OHCI_CTRL_IE) ? " IE" : "", - (temp & OHCI_CTRL_PLE) ? " PLE" : "", - temp & OHCI_CTRL_CBSR - ); - - temp = readl (®s->cmdstatus); - dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, - (temp & OHCI_SOC) >> 16, - (temp & OHCI_OCR) ? " OCR" : "", - (temp & OHCI_BLF) ? " BLF" : "", - (temp & OHCI_CLF) ? " CLF" : "", - (temp & OHCI_HCR) ? " HCR" : "" - ); - - ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); - ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); - - maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); - - maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); - maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); - - maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); - maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); - - maybe_print_eds ("donehead", readl (®s->donehead)); -} - -static void ohci_dump_roothub (ohci_t *controller, int verbose) -{ - __u32 temp, ndp, i; - - temp = roothub_a (controller); - ndp = (temp & RH_A_NDP); - - if (verbose) { - dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, - ((temp & RH_A_POTPGT) >> 24) & 0xff, - (temp & RH_A_NOCP) ? " NOCP" : "", - (temp & RH_A_OCPM) ? " OCPM" : "", - (temp & RH_A_DT) ? " DT" : "", - (temp & RH_A_NPS) ? " NPS" : "", - (temp & RH_A_PSM) ? " PSM" : "", - ndp - ); - temp = roothub_b (controller); - dbg ("roothub.b: %08x PPCM=%04x DR=%04x", - temp, - (temp & RH_B_PPCM) >> 16, - (temp & RH_B_DR) - ); - temp = roothub_status (controller); - dbg ("roothub.status: %08x%s%s%s%s%s%s", - temp, - (temp & RH_HS_CRWE) ? " CRWE" : "", - (temp & RH_HS_OCIC) ? " OCIC" : "", - (temp & RH_HS_LPSC) ? " LPSC" : "", - (temp & RH_HS_DRWE) ? " DRWE" : "", - (temp & RH_HS_OCI) ? " OCI" : "", - (temp & RH_HS_LPS) ? " LPS" : "" - ); - } - - for (i = 0; i < ndp; i++) { - temp = roothub_portstatus (controller, i); - dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", - i, - temp, - (temp & RH_PS_PRSC) ? " PRSC" : "", - (temp & RH_PS_OCIC) ? " OCIC" : "", - (temp & RH_PS_PSSC) ? " PSSC" : "", - (temp & RH_PS_PESC) ? " PESC" : "", - (temp & RH_PS_CSC) ? " CSC" : "", - - (temp & RH_PS_LSDA) ? " LSDA" : "", - (temp & RH_PS_PPS) ? " PPS" : "", - (temp & RH_PS_PRS) ? " PRS" : "", - (temp & RH_PS_POCI) ? " POCI" : "", - (temp & RH_PS_PSS) ? " PSS" : "", - - (temp & RH_PS_PES) ? " PES" : "", - (temp & RH_PS_CCS) ? " CCS" : "" - ); - } -} - -static void ohci_dump (ohci_t *controller, int verbose) -{ - dbg ("OHCI controller usb-%s state", controller->slot_name); - - /* dumps some of the state we know about */ - ohci_dump_status (controller); - if (verbose) - ep_print_int_eds (controller, "hcca"); - dbg ("hcca frame #%04x", controller->hcca->frame_no); - ohci_dump_roothub (controller, 1); -} - - -#endif /* DEBUG */ - -/*-------------------------------------------------------------------------* - * Interface functions (URB) - *-------------------------------------------------------------------------*/ - -/* get a transfer request */ - -int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) -{ - ohci_t *ohci; - ed_t * ed; - urb_priv_t *purb_priv; - int i, size = 0; - - ohci = &gohci; - - /* when controller's hung, permit only roothub cleanup attempts - * such as powering down ports */ - if (ohci->disabled) { - err("sohci_submit_job: EPIPE"); - return -1; - } - - /* every endpoint has a ed, locate and fill it */ - if (!(ed = ep_add_ed (dev, pipe))) { - err("sohci_submit_job: ENOMEM"); - return -1; - } - - /* for the private part of the URB we need the number of TDs (size) */ - switch (usb_pipetype (pipe)) { - case PIPE_BULK: /* one TD for every 4096 Byte */ - size = (transfer_len - 1) / 4096 + 1; - break; - case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ - size = (transfer_len == 0)? 2: - (transfer_len - 1) / 4096 + 3; - break; - } - - if (size >= (N_URB_TD - 1)) { - err("need %d TDs, only have %d", size, N_URB_TD); - return -1; - } - purb_priv = &urb_priv; - purb_priv->pipe = pipe; - - /* fill the private part of the URB */ - purb_priv->length = size; - purb_priv->ed = ed; - purb_priv->actual_length = 0; - - /* allocate the TDs */ - /* note that td[0] was allocated in ep_add_ed */ - for (i = 0; i < size; i++) { - purb_priv->td[i] = td_alloc (dev); - if (!purb_priv->td[i]) { - purb_priv->length = i; - urb_free_priv (purb_priv); - err("sohci_submit_job: ENOMEM"); - return -1; - } - } - - if (ed->state == ED_NEW || (ed->state & ED_DEL)) { - urb_free_priv (purb_priv); - err("sohci_submit_job: EINVAL"); - return -1; - } - - /* link the ed into a chain if is not already */ - if (ed->state != ED_OPER) - ep_link (ohci, ed); - - /* fill the TDs and link it to the ed */ - td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEBUG -/* tell us the current USB frame number */ - -static int sohci_get_current_frame_number (struct usb_device *usb_dev) -{ - ohci_t *ohci = &gohci; - - return m16_swap (ohci->hcca->frame_no); -} -#endif - -/*-------------------------------------------------------------------------* - * ED handling functions - *-------------------------------------------------------------------------*/ - -/* link an ed into one of the HC chains */ - -static int ep_link (ohci_t *ohci, ed_t *edi) -{ - volatile ed_t *ed = edi; - - ed->state = ED_OPER; - - switch (ed->type) { - case PIPE_CONTROL: - ed->hwNextED = 0; - if (ohci->ed_controltail == NULL) { - writel ((long)ed, &ohci->regs->ed_controlhead); - } else { - ohci->ed_controltail->hwNextED = m32_swap (ed); - } - ed->ed_prev = ohci->ed_controltail; - if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - } - ohci->ed_controltail = edi; - break; - - case PIPE_BULK: - ed->hwNextED = 0; - if (ohci->ed_bulktail == NULL) { - writel ((long)ed, &ohci->regs->ed_bulkhead); - } else { - ohci->ed_bulktail->hwNextED = m32_swap (ed); - } - ed->ed_prev = ohci->ed_bulktail; - if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && - !ohci->ed_rm_list[1] && !ohci->sleeping) { - ohci->hc_control |= OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - } - ohci->ed_bulktail = edi; - break; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* unlink an ed from one of the HC chains. - * just the link to the ed is unlinked. - * the link from the ed still points to another operational ed or 0 - * so the HC can eventually finish the processing of the unlinked ed */ - -static int ep_unlink (ohci_t *ohci, ed_t *ed) -{ - ed->hwINFO |= m32_swap (OHCI_ED_SKIP); - - switch (ed->type) { - case PIPE_CONTROL: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - } - writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_controltail == ed) { - ohci->ed_controltail = ed->ed_prev; - } else { - ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - - case PIPE_BULK: - if (ed->ed_prev == NULL) { - if (!ed->hwNextED) { - ohci->hc_control &= ~OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - } - writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); - } else { - ed->ed_prev->hwNextED = ed->hwNextED; - } - if (ohci->ed_bulktail == ed) { - ohci->ed_bulktail = ed->ed_prev; - } else { - ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; - } - break; - } - ed->state = ED_UNLINK; - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, - * but the USB stack is a little bit stateless so we do it at every transaction - * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK - * in all other cases the state is left unchanged - * the ed info fields are setted anyway even though most of them should not change */ - -static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) -{ - td_t *td; - ed_t *ed_ret; - volatile ed_t *ed; - - ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | - (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; - - if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { - err("ep_add_ed: pending delete"); - /* pending delete request */ - return NULL; - } - - if (ed->state == ED_NEW) { - ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */ - /* dummy td; end of td list for ed */ - td = td_alloc (usb_dev); - ed->hwTailP = m32_swap (td); - ed->hwHeadP = ed->hwTailP; - ed->state = ED_UNLINK; - ed->type = usb_pipetype (pipe); - ohci_dev.ed_cnt++; - } - - ed->hwINFO = m32_swap (usb_pipedevice (pipe) - | usb_pipeendpoint (pipe) << 7 - | (usb_pipeisoc (pipe)? 0x8000: 0) - | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) - | (usb_dev->speed == USB_SPEED_LOW) << 13 - | usb_maxpacket (usb_dev, pipe) << 16); - - return ed_ret; -} - -/*-------------------------------------------------------------------------* - * TD handling functions - *-------------------------------------------------------------------------*/ - -/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ - -static void td_fill (ohci_t *ohci, unsigned int info, - void *data, int len, - struct usb_device *dev, int index, urb_priv_t *urb_priv) -{ - volatile td_t *td, *td_pt; -#ifdef OHCI_FILL_TRACE - int i; -#endif - - if (index > urb_priv->length) { - err("index > length"); - return; - } - /* use this td as the next dummy */ - td_pt = urb_priv->td [index]; - td_pt->hwNextTD = 0; - - /* fill the old dummy TD */ - td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf); - - td->ed = urb_priv->ed; - td->next_dl_td = NULL; - td->index = index; - td->data = (__u32)data; -#ifdef OHCI_FILL_TRACE - if (1 || (usb_pipebulk(urb_priv->pipe) && - usb_pipeout(urb_priv->pipe))) { - for (i = 0; i < len; i++) - printf("td->data[%d] %#2x\n",i, ((unsigned char *)(td->data+0x80000000))[i]); - } -#endif - if (!len) - data = 0; - - td->hwINFO = m32_swap (info); - td->hwCBP = m32_swap (data); - if (data) - td->hwBE = m32_swap (data + len - 1); - else - td->hwBE = 0; - td->hwNextTD = m32_swap (td_pt); - td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); - - /* append to queue */ - td->ed->hwTailP = td->hwNextTD; -} - -/*-------------------------------------------------------------------------*/ - -/* prepare all TDs of a transfer */ - -#define kseg_to_phys(x) ((void *)((__u32)(x) - 0x80000000)) - -static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) -{ - ohci_t *ohci = &gohci; - int data_len = transfer_len; - void *data; - int cnt = 0; - __u32 info = 0; - unsigned int toggle = 0; - - /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ - if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { - toggle = TD_T_TOGGLE; - } else { - toggle = TD_T_DATA0; - usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); - } - urb->td_cnt = 0; - if (data_len) - data = kseg_to_phys(buffer); - else - data = 0; - - switch (usb_pipetype (pipe)) { - case PIPE_BULK: - info = usb_pipeout (pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; - while(data_len > 4096) { - td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); - data += 4096; data_len -= 4096; cnt++; - } - info = usb_pipeout (pipe)? - TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; - td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); - cnt++; - - if (!ohci->sleeping) - writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - break; - - case PIPE_CONTROL: - info = TD_CC | TD_DP_SETUP | TD_T_DATA0; - td_fill (ohci, info, kseg_to_phys(setup), 8, dev, cnt++, urb); - if (data_len > 0) { - info = usb_pipeout (pipe)? - TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; - /* NOTE: mishandles transfers >8K, some >4K */ - td_fill (ohci, info, data, data_len, dev, cnt++, urb); - } - info = usb_pipeout (pipe)? - TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; - td_fill (ohci, info, data, 0, dev, cnt++, urb); - if (!ohci->sleeping) - writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - break; - } - if (urb->length != cnt) - dbg("TD LENGTH %d != CNT %d", urb->length, cnt); -} - -/*-------------------------------------------------------------------------* - * Done List handling functions - *-------------------------------------------------------------------------*/ - - -/* calculate the transfer length and update the urb */ - -static void dl_transfer_length(td_t * td) -{ - __u32 tdINFO, tdBE, tdCBP; - urb_priv_t *lurb_priv = &urb_priv; - - tdINFO = m32_swap (td->hwINFO); - tdBE = m32_swap (td->hwBE); - tdCBP = m32_swap (td->hwCBP); - - - if (!(usb_pipecontrol(lurb_priv->pipe) && - ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { - if (tdBE != 0) { - if (td->hwCBP == 0) - lurb_priv->actual_length += tdBE - td->data + 1; - else - lurb_priv->actual_length += tdCBP - td->data; - } - } -} - -/*-------------------------------------------------------------------------*/ - -/* replies to the request have to be on a FIFO basis so - * we reverse the reversed done-list */ - -static td_t * dl_reverse_done_list (ohci_t *ohci) -{ - __u32 td_list_hc; - td_t *td_rev = NULL; - td_t *td_list = NULL; - urb_priv_t *lurb_priv = NULL; - - td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0; - ohci->hcca->done_head = 0; - - while (td_list_hc) { - td_list = (td_t *)td_list_hc; - - if (TD_CC_GET (m32_swap (td_list->hwINFO))) { - lurb_priv = &urb_priv; - dbg(" USB-error/status: %x : %p", - TD_CC_GET (m32_swap (td_list->hwINFO)), td_list); - if (td_list->ed->hwHeadP & m32_swap (0x1)) { - if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { - td_list->ed->hwHeadP = - (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) | - (td_list->ed->hwHeadP & m32_swap (0x2)); - lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; - } else - td_list->ed->hwHeadP &= m32_swap (0xfffffff2); - } - } - - td_list->next_dl_td = td_rev; - td_rev = td_list; - td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0; - } - return td_list; -} - -/*-------------------------------------------------------------------------*/ - -/* td done list */ -static int dl_done_list (ohci_t *ohci, td_t *td_list) -{ - td_t *td_list_next = NULL; - ed_t *ed; - int cc = 0; - int stat = 0; - /* urb_t *urb; */ - urb_priv_t *lurb_priv; - __u32 tdINFO, edHeadP, edTailP; - - while (td_list) { - td_list_next = td_list->next_dl_td; - - lurb_priv = &urb_priv; - tdINFO = m32_swap (td_list->hwINFO); - - ed = td_list->ed; - - dl_transfer_length(td_list); - - /* error code of transfer */ - cc = TD_CC_GET (tdINFO); - if (cc != 0) { - dbg("ConditionCode %#x", cc); - stat = cc_to_error[cc]; - } - - if (ed->state != ED_NEW) { - edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; - edTailP = m32_swap (ed->hwTailP); - - /* unlink eds if they are not busy */ - if ((edHeadP == edTailP) && (ed->state == ED_OPER)) - ep_unlink (ohci, ed); - } - - td_list = td_list_next; - } - return stat; -} - -/*-------------------------------------------------------------------------* - * Virtual Root Hub - *-------------------------------------------------------------------------*/ - -#include - -/* Hub class-specific descriptor is constructed dynamically */ - - -/*-------------------------------------------------------------------------*/ - -#define OK(x) len = (x); break -#ifdef DEBUG -#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} -#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} -#else -#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) -#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) -#endif -#define RD_RH_STAT roothub_status(&gohci) -#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) - -/* request to virtual root hub */ - -int rh_check_port_status(ohci_t *controller) -{ - __u32 temp, ndp, i; - int res; - - res = -1; - temp = roothub_a (controller); - ndp = (temp & RH_A_NDP); - for (i = 0; i < ndp; i++) { - temp = roothub_portstatus (controller, i); - /* check for a device disconnect */ - if (((temp & (RH_PS_PESC | RH_PS_CSC)) == - (RH_PS_PESC | RH_PS_CSC)) && - ((temp & RH_PS_CCS) == 0)) { - res = i; - break; - } - } - return res; -} - -static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int transfer_len, struct devrequest *cmd) -{ - void * data = buffer; - int leni = transfer_len; - int len = 0; - int stat = 0; - __u32 datab[4]; - __u8 *data_buf = (__u8 *)datab; - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - -#ifdef DEBUG -urb_priv.actual_length = 0; -pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); -#else - mdelay(1); -#endif - if (usb_pipeint(pipe)) { - info("Root-Hub submit IRQ: NOT implemented"); - return 0; - } - - bmRType_bReq = cmd->requesttype | (cmd->request << 8); - wValue = m16_swap (cmd->value); - wIndex = m16_swap (cmd->index); - wLength = m16_swap (cmd->length); - - info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", - dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data_buf = m16_swap (1); OK (2); - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = m16_swap (0); OK (2); - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = m16_swap (0); OK (2); - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = m32_swap ( - RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); - OK (4); - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case RH_C_HUB_LOCAL_POWER: - OK(0); - case (RH_C_HUB_OVER_CURRENT): - WR_RH_STAT(RH_HS_OCIC); OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); - case (RH_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_POCI); OK (0); - case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); - case (RH_C_PORT_CONNECTION): - WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); - case (RH_C_PORT_ENABLE): - WR_RH_PORTSTAT (RH_PS_PESC); OK (0); - case (RH_C_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); - case (RH_C_PORT_OVER_CURRENT): - WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); - case (RH_C_PORT_RESET): - WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); - case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT (RH_PS_PRS); - OK (0); - case (RH_PORT_POWER): - WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); - case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ - if (RD_RH_PORTSTAT & RH_PS_CCS) - WR_RH_PORTSTAT (RH_PS_PES ); - OK (0); - } - break; - - case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_dev_des), - wLength)); - data_buf = root_hub_dev_des; OK(len); - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_config_des), - wLength)); - data_buf = root_hub_config_des; OK(len); - case (0x03): /* string descriptors */ - if(wValue==0x0300) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_str_index0), - wLength)); - data_buf = root_hub_str_index0; - OK(len); - } - if(wValue==0x0301) { - len = min_t(unsigned int, - leni, - min_t(unsigned int, - sizeof (root_hub_str_index1), - wLength)); - data_buf = root_hub_str_index1; - OK(len); - } - default: - stat = USB_ST_STALLED; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - { - __u32 temp = roothub_a (&gohci); - - data_buf [0] = 9; /* min length; */ - data_buf [1] = 0x29; - data_buf [2] = temp & RH_A_NDP; - data_buf [3] = 0; - if (temp & RH_A_PSM) /* per-port power switching? */ - data_buf [3] |= 0x1; - if (temp & RH_A_NOCP) /* no overcurrent reporting? */ - data_buf [3] |= 0x10; - else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ - data_buf [3] |= 0x8; - - /* corresponds to data_buf[4-7] */ - datab [1] = 0; - data_buf [5] = (temp & RH_A_POTPGT) >> 24; - temp = roothub_b (&gohci); - data_buf [7] = temp & RH_B_DR; - if (data_buf [2] < 7) { - data_buf [8] = 0xff; - } else { - data_buf [0] += 2; - data_buf [8] = (temp & RH_B_DR) >> 8; - data_buf [10] = data_buf [9] = 0xff; - } - - len = min_t(unsigned int, leni, - min_t(unsigned int, data_buf [0], wLength)); - OK (len); - } - - case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); - - case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); - - default: - dbg ("unsupported root hub command"); - stat = USB_ST_STALLED; - } - -#ifdef DEBUG - ohci_dump_roothub (&gohci, 1); -#else - mdelay(1); -#endif - - len = min_t(int, len, leni); - if (data != data_buf) - memcpy (data, data_buf, len); - dev->act_len = len; - dev->status = stat; - -#ifdef DEBUG - if (transfer_len) - urb_priv.actual_length = transfer_len; - pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); -#else - mdelay(1); -#endif - - return stat; -} - -/*-------------------------------------------------------------------------*/ - -/* common code for handling submit messages - used for all but root hub */ -/* accesses. */ -int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup, int interval) -{ - int stat = 0; - int maxsize = usb_maxpacket(dev, pipe); - int timeout; - - /* device pulled? Shortcut the action. */ - if (devgone == dev) { - dev->status = USB_ST_CRC_ERR; - return 0; - } - -#ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); -#else - mdelay(1); -#endif - if (!maxsize) { - err("submit_common_message: pipesize for pipe %lx is zero", - pipe); - return -1; - } - - if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { - err("sohci_submit_job failed"); - return -1; - } - - mdelay(10); - /* ohci_dump_status(&gohci); */ - - /* allow more time for a BULK device to react - some are slow */ -#define BULK_TO 5000 /* timeout in milliseconds */ - if (usb_pipebulk(pipe)) - timeout = BULK_TO; - else - timeout = 100; - - timeout *= 4; - /* wait for it to complete */ - for (;;) { - /* check whether the controller is done */ - stat = hc_interrupt(); - if (stat < 0) { - stat = USB_ST_CRC_ERR; - break; - } - if (stat >= 0 && stat != 0xff) { - /* 0xff is returned for an SF-interrupt */ - break; - } - if (--timeout) { - udelay(250); /* mdelay(1); */ - } else { - err("CTL:TIMEOUT "); - stat = USB_ST_CRC_ERR; - break; - } - } - /* we got an Root Hub Status Change interrupt */ - if (got_rhsc) { -#ifdef DEBUG - ohci_dump_roothub (&gohci, 1); -#endif - got_rhsc = 0; - /* abuse timeout */ - timeout = rh_check_port_status(&gohci); - if (timeout >= 0) { -#if 0 /* this does nothing useful, but leave it here in case that changes */ - /* the called routine adds 1 to the passed value */ - usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); -#endif - /* - * XXX - * This is potentially dangerous because it assumes - * that only one device is ever plugged in! - */ - devgone = dev; - } - } - - dev->status = stat; - dev->act_len = transfer_len; - -#ifdef DEBUG - pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); -#else - mdelay(1); -#endif - - /* free TDs in urb_priv */ - urb_free_priv (&urb_priv); - return 0; -} - -/* submit routines called from usb.c */ -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len) -{ - info("submit_bulk_msg"); - return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); -} - -int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, struct devrequest *setup) -{ - int maxsize = usb_maxpacket(dev, pipe); - - info("submit_control_msg"); -#ifdef DEBUG - urb_priv.actual_length = 0; - pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); -#else - mdelay(1); -#endif - if (!maxsize) { - err("submit_control_message: pipesize for pipe %lx is zero", - pipe); - return -1; - } - if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { - gohci.rh.dev = dev; - /* root hub - redirect */ - return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, - setup); - } - - return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); -} - -int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int transfer_len, int interval) -{ - info("submit_int_msg"); - return -1; -} - -/*-------------------------------------------------------------------------* - * HC functions - *-------------------------------------------------------------------------*/ - -/* reset the HC and BUS */ - -static int hc_reset (ohci_t *ohci) -{ - int timeout = 30; - int smm_timeout = 50; /* 0,5 sec */ - - if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ - writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ - info("USB HC TakeOver from SMM"); - while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { - mdelay (10); - if (--smm_timeout == 0) { - err("USB HC TakeOver failed!"); - return -1; - } - } - } - - /* Disable HC interrupts */ - writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); - - dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;", - ohci->slot_name, - readl (&ohci->regs->control)); - - /* Reset USB (needed by some controllers) */ - writel (0, &ohci->regs->control); - - /* HC Reset requires max 10 us delay */ - writel (OHCI_HCR, &ohci->regs->cmdstatus); - while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { - if (--timeout == 0) { - err("USB HC reset timed out!"); - return -1; - } - udelay (1); - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* Start an OHCI controller, set the BUS operational - * enable interrupts - * connect the virtual root hub */ - -static int hc_start (ohci_t * ohci) -{ - __u32 mask; - unsigned int fminterval; - - ohci->disabled = 1; - - /* Tell the controller where the control and bulk lists are - * The lists are empty now. */ - - writel (0, &ohci->regs->ed_controlhead); - writel (0, &ohci->regs->ed_bulkhead); - - writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ - - fminterval = 0x2edf; - writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); - fminterval |= ((((fminterval - 210) * 6) / 7) << 16); - writel (fminterval, &ohci->regs->fminterval); - writel (0x628, &ohci->regs->lsthresh); - - /* start controller operations */ - ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; - ohci->disabled = 0; - writel (ohci->hc_control, &ohci->regs->control); - - /* disable all interrupts */ - mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | - OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | - OHCI_INTR_OC | OHCI_INTR_MIE); - writel (mask, &ohci->regs->intrdisable); - /* clear all interrupts */ - mask &= ~OHCI_INTR_MIE; - writel (mask, &ohci->regs->intrstatus); - /* Choose the interrupts we care about now - but w/o MIE */ - mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; - writel (mask, &ohci->regs->intrenable); - -#ifdef OHCI_USE_NPS - /* required for AMD-756 and some Mac platforms */ - writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, - &ohci->regs->roothub.a); - writel (RH_HS_LPSC, &ohci->regs->roothub.status); -#endif /* OHCI_USE_NPS */ - - /* POTPGT delay is bits 24-31, in 2 ms units. */ - mdelay ((roothub_a (ohci) >> 23) & 0x1fe); - - /* connect the virtual root hub */ - ohci->rh.devnum = 0; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* an interrupt happens */ - -static int -hc_interrupt (void) -{ - ohci_t *ohci = &gohci; - struct ohci_regs *regs = ohci->regs; - int ints; - int stat = -1; - - if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { - ints = OHCI_INTR_WDH; - } else { - ints = readl (®s->intrstatus); - } - - /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ - - if (ints & OHCI_INTR_RHSC) { - got_rhsc = 1; - } - - if (ints & OHCI_INTR_UE) { - ohci->disabled++; - err ("OHCI Unrecoverable Error, controller usb-%s disabled", - ohci->slot_name); - /* e.g. due to PCI Master/Target Abort */ - -#ifdef DEBUG - ohci_dump (ohci, 1); -#else - mdelay(1); -#endif - /* FIXME: be optimistic, hope that bug won't repeat often. */ - /* Make some non-interrupt context restart the controller. */ - /* Count and limit the retries though; either hardware or */ - /* software errors can go forever... */ - hc_reset (ohci); - return -1; - } - - if (ints & OHCI_INTR_WDH) { - mdelay(1); - writel (OHCI_INTR_WDH, ®s->intrdisable); - stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); - writel (OHCI_INTR_WDH, ®s->intrenable); - } - - if (ints & OHCI_INTR_SO) { - dbg("USB Schedule overrun\n"); - writel (OHCI_INTR_SO, ®s->intrenable); - stat = -1; - } - - /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ - if (ints & OHCI_INTR_SF) { - unsigned int frame = m16_swap (ohci->hcca->frame_no) & 1; - mdelay(1); - writel (OHCI_INTR_SF, ®s->intrdisable); - if (ohci->ed_rm_list[frame] != NULL) - writel (OHCI_INTR_SF, ®s->intrenable); - stat = 0xff; - } - - writel (ints, ®s->intrstatus); - return stat; -} - -/*-------------------------------------------------------------------------*/ - -/*-------------------------------------------------------------------------*/ - -/* De-allocate all resources.. */ - -static void hc_release_ohci (ohci_t *ohci) -{ - dbg ("USB HC release ohci usb-%s", ohci->slot_name); - - if (!ohci->disabled) - hc_reset (ohci); -} - -/*-------------------------------------------------------------------------*/ - -#define __read_32bit_c0_register(source, sel) \ -({ int __res; \ - if (sel == 0) \ - __asm__ __volatile__( \ - "mfc0\t%0, " #source "\n\t" \ - : "=r" (__res)); \ - else \ - __asm__ __volatile__( \ - ".set\tmips32\n\t" \ - "mfc0\t%0, " #source ", " #sel "\n\t" \ - ".set\tmips0\n\t" \ - : "=r" (__res)); \ - __res; \ -}) - -#define read_c0_prid() __read_32bit_c0_register($15, 0) - -/* - * low level initalisation routine, called from usb.c - */ -static char ohci_inited = 0; - -int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) -{ - u32 pin_func; - u32 sys_freqctrl, sys_clksrc; - u32 prid = read_c0_prid(); - - dbg("in usb_lowlevel_init\n"); - - /* zero and disable FREQ2 */ - sys_freqctrl = au_readl(SYS_FREQCTRL0); - sys_freqctrl &= ~0xFFF00000; - au_writel(sys_freqctrl, SYS_FREQCTRL0); - - /* zero and disable USBH/USBD clocks */ - sys_clksrc = au_readl(SYS_CLKSRC); - sys_clksrc &= ~0x00007FE0; - au_writel(sys_clksrc, SYS_CLKSRC); - - sys_freqctrl = au_readl(SYS_FREQCTRL0); - sys_freqctrl &= ~0xFFF00000; - - sys_clksrc = au_readl(SYS_CLKSRC); - sys_clksrc &= ~0x00007FE0; - - switch (prid & 0x000000FF) { - case 0x00: /* DA */ - case 0x01: /* HA */ - case 0x02: /* HB */ - /* CPU core freq to 48MHz to slow it way down... */ - au_writel(4, SYS_CPUPLL); - - /* - * Setup 48MHz FREQ2 from CPUPLL for USB Host - */ - /* FRDIV2=3 -> div by 8 of 384MHz -> 48MHz */ - sys_freqctrl |= ((3<<22) | (1<<21) | (0<<20)); - au_writel(sys_freqctrl, SYS_FREQCTRL0); - - /* CPU core freq to 384MHz */ - au_writel(0x20, SYS_CPUPLL); - - printf("Au1000: 48MHz OHCI workaround enabled\n"); - break; - - default: /* HC and newer */ - /* FREQ2 = aux/2 = 48 MHz */ - sys_freqctrl |= ((0<<22) | (1<<21) | (1<<20)); - au_writel(sys_freqctrl, SYS_FREQCTRL0); - break; - } - - /* - * Route 48MHz FREQ2 into USB Host and/or Device - */ - sys_clksrc |= ((4<<12) | (0<<11) | (0<<10)); - au_writel(sys_clksrc, SYS_CLKSRC); - - /* configure pins GPIO[14:9] as GPIO */ - pin_func = au_readl(SYS_PINFUNC) & (u32)(~0x8080); - - au_writel(pin_func, SYS_PINFUNC); - au_writel(0x2800, SYS_TRIOUTCLR); - au_writel(0x0030, SYS_OUTPUTCLR); - - dbg("OHCI board setup complete\n"); - - /* enable host controller */ - au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG); - udelay(1000); - au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG); - udelay(1000); - - /* wait for reset complete (read register twice; see au1500 errata) */ - while (au_readl(USB_HOST_CONFIG), - !(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD)) - udelay(1000); - - dbg("OHCI clock running\n"); - - memset (&gohci, 0, sizeof (ohci_t)); - memset (&urb_priv, 0, sizeof (urb_priv_t)); - - /* align the storage */ - if ((__u32)&ghcca[0] & 0xff) { - err("HCCA not aligned!!"); - return -1; - } - phcca = &ghcca[0]; - info("aligned ghcca %p", phcca); - memset(&ohci_dev, 0, sizeof(struct ohci_device)); - if ((__u32)&ohci_dev.ed[0] & 0x7) { - err("EDs not aligned!!"); - return -1; - } - memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); - if ((__u32)gtd & 0x7) { - err("TDs not aligned!!"); - return -1; - } - ptd = gtd; - gohci.hcca = phcca; - memset (phcca, 0, sizeof (struct ohci_hcca)); - - gohci.disabled = 1; - gohci.sleeping = 0; - gohci.irq = -1; - gohci.regs = (struct ohci_regs *)(USB_OHCI_BASE | 0xA0000000); - - gohci.flags = 0; - gohci.slot_name = "au1x00"; - - dbg("OHCI revision: 0x%08x\n" - " RH: a: 0x%08x b: 0x%08x\n", - readl(&gohci.regs->revision), - readl(&gohci.regs->roothub.a), readl(&gohci.regs->roothub.b)); - - if (hc_reset (&gohci) < 0) - goto errout; - - /* FIXME this is a second HC reset; why?? */ - writel (gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control); - mdelay (10); - - if (hc_start (&gohci) < 0) - goto errout; - -#ifdef DEBUG - ohci_dump (&gohci, 1); -#else - mdelay(1); -#endif - ohci_inited = 1; - return 0; - - errout: - err("OHCI initialization error\n"); - hc_release_ohci (&gohci); - /* Initialization failed */ - au_writel(readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); - return -1; -} - -int usb_lowlevel_stop(int index) -{ - /* this gets called really early - before the controller has */ - /* even been initialized! */ - if (!ohci_inited) - return 0; - /* TODO release any interrupts, etc. */ - /* call hc_release_ohci() here ? */ - hc_reset (&gohci); - /* may not want to do this */ - /* Disable clock */ - au_writel(readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); - return 0; -} - -#endif /* CONFIG_USB_OHCI */ diff --git a/arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.h b/arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.h deleted file mode 100644 index bb9f351..0000000 --- a/arch/mips/cpu/mips32/au1x00/au1x00_usb_ohci.h +++ /dev/null @@ -1,416 +0,0 @@ -/* - * URB OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2001 David Brownell - * - * usb-ohci.h - */ - - -static int cc_to_error[16] = { - -/* mapping of the OHCI CC status to error codes */ - /* No Error */ 0, - /* CRC Error */ USB_ST_CRC_ERR, - /* Bit Stuff */ USB_ST_BIT_ERR, - /* Data Togg */ USB_ST_CRC_ERR, - /* Stall */ USB_ST_STALLED, - /* DevNotResp */ -1, - /* PIDCheck */ USB_ST_BIT_ERR, - /* UnExpPID */ USB_ST_BIT_ERR, - /* DataOver */ USB_ST_BUF_ERR, - /* DataUnder */ USB_ST_BUF_ERR, - /* reservd */ -1, - /* reservd */ -1, - /* BufferOver */ USB_ST_BUF_ERR, - /* BuffUnder */ USB_ST_BUF_ERR, - /* Not Access */ -1, - /* Not Access */ -1 -}; - -/* ED States */ - -#define ED_NEW 0x00 -#define ED_UNLINK 0x01 -#define ED_OPER 0x02 -#define ED_DEL 0x04 -#define ED_URB_DEL 0x08 - -/* usb_ohci_ed */ -struct ed { - __u32 hwINFO; - __u32 hwTailP; - __u32 hwHeadP; - __u32 hwNextED; - - struct ed *ed_prev; - __u8 int_period; - __u8 int_branch; - __u8 int_load; - __u8 int_interval; - __u8 state; - __u8 type; - __u16 last_iso; - struct ed *ed_rm_list; - - struct usb_device *usb_dev; - __u32 unused[3]; -} __attribute__((aligned(16))); -typedef struct ed ed_t; - - -/* TD info field */ -#define TD_CC 0xf0000000 -#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) -#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) -#define TD_EC 0x0C000000 -#define TD_T 0x03000000 -#define TD_T_DATA0 0x02000000 -#define TD_T_DATA1 0x03000000 -#define TD_T_TOGGLE 0x00000000 -#define TD_R 0x00040000 -#define TD_DI 0x00E00000 -#define TD_DI_SET(X) (((X) & 0x07)<< 21) -#define TD_DP 0x00180000 -#define TD_DP_SETUP 0x00000000 -#define TD_DP_IN 0x00100000 -#define TD_DP_OUT 0x00080000 - -#define TD_ISO 0x00010000 -#define TD_DEL 0x00020000 - -/* CC Codes */ -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -#define TD_UNEXPECTEDPID 0x07 -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_NOTACCESSED 0x0F - - -#define MAXPSW 1 - -struct td { - __u32 hwINFO; - __u32 hwCBP; /* Current Buffer Pointer */ - __u32 hwNextTD; /* Next TD Pointer */ - __u32 hwBE; /* Memory Buffer End Pointer */ - - __u16 hwPSW[MAXPSW]; - __u8 unused; - __u8 index; - struct ed *ed; - struct td *next_dl_td; - struct usb_device *usb_dev; - int transfer_len; - __u32 data; - - __u32 unused2[2]; -} __attribute__((aligned(32))); -typedef struct td td_t; - -#define OHCI_ED_SKIP (1 << 14) - -/* - * The HCCA (Host Controller Communications Area) is a 256 byte - * structure defined in the OHCI spec. that the host controller is - * told the base address of. It must be 256-byte aligned. - */ - -#define NUM_INTS 32 /* part of the OHCI standard */ -struct ohci_hcca { - __u32 int_table[NUM_INTS]; /* Interrupt ED table */ - __u16 frame_no; /* current frame number */ - __u16 pad1; /* set to 0 on each frame_no change */ - __u32 done_head; /* info returned for an interrupt */ - u8 reserved_for_hc[116]; -} __attribute__((aligned(256))); - - -/* - * Maximum number of root hub ports. - */ -#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ - -/* - * This is the structure of the OHCI controller's memory mapped I/O - * region. This is Memory Mapped I/O. You must use the readl() and - * writel() macros defined in asm/io.h to access these!! - */ -struct ohci_regs { - /* control and status registers */ - __u32 revision; - __u32 control; - __u32 cmdstatus; - __u32 intrstatus; - __u32 intrenable; - __u32 intrdisable; - /* memory pointers */ - __u32 hcca; - __u32 ed_periodcurrent; - __u32 ed_controlhead; - __u32 ed_controlcurrent; - __u32 ed_bulkhead; - __u32 ed_bulkcurrent; - __u32 donehead; - /* frame counters */ - __u32 fminterval; - __u32 fmremaining; - __u32 fmnumber; - __u32 periodicstart; - __u32 lsthresh; - /* Root hub ports */ - struct ohci_roothub_regs { - __u32 a; - __u32 b; - __u32 status; - __u32 portstatus[MAX_ROOT_PORTS]; - } roothub; -} __attribute__((aligned(32))); - - -/* OHCI CONTROL AND STATUS REGISTER MASKS */ - -/* - * HcControl (control) register masks - */ -#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ -#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ -#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ -#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ -#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ -#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ -#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ -#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ - -/* pre-shifted values for HCFS */ -# define OHCI_USB_RESET (0 << 6) -# define OHCI_USB_RESUME (1 << 6) -# define OHCI_USB_OPER (2 << 6) -# define OHCI_USB_SUSPEND (3 << 6) - -/* - * HcCommandStatus (cmdstatus) register masks - */ -#define OHCI_HCR (1 << 0) /* host controller reset */ -#define OHCI_CLF (1 << 1) /* control list filled */ -#define OHCI_BLF (1 << 2) /* bulk list filled */ -#define OHCI_OCR (1 << 3) /* ownership change request */ -#define OHCI_SOC (3 << 16) /* scheduling overrun count */ - -/* - * masks used with interrupt registers: - * HcInterruptStatus (intrstatus) - * HcInterruptEnable (intrenable) - * HcInterruptDisable (intrdisable) - */ -#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ -#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ -#define OHCI_INTR_SF (1 << 2) /* start frame */ -#define OHCI_INTR_RD (1 << 3) /* resume detect */ -#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ -#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ -#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ -#define OHCI_INTR_OC (1 << 30) /* ownership change */ -#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ - - -/* Virtual Root HUB */ -struct virt_root_hub { - int devnum; /* Address of Root Hub endpoint */ - void *dev; /* was urb */ - void *int_addr; - int send; - int interval; -}; - -/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 - -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - - -/* OHCI ROOT HUB REGISTER MASKS */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status*/ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -/* urb */ -#define N_URB_TD 48 -typedef struct -{ - ed_t *ed; - __u16 length; /* number of tds associated with this request */ - __u16 td_cnt; /* number of tds already serviced */ - int state; - unsigned long pipe; - int actual_length; - td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ -} urb_priv_t; -#define URB_DEL 1 - -/* - * This is the full ohci controller description - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. (Linus) - */ - - -typedef struct ohci { - struct ohci_hcca *hcca; /* hcca */ - /*dma_addr_t hcca_dma;*/ - - int irq; - int disabled; /* e.g. got a UE, we're hung */ - int sleeping; - unsigned long flags; /* for HC bugs */ - - struct ohci_regs *regs; /* OHCI controller's memory */ - - ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ - ed_t *ed_bulktail; /* last endpoint of bulk list */ - ed_t *ed_controltail; /* last endpoint of control list */ - int intrstatus; - __u32 hc_control; /* copy of the hc control reg */ - struct usb_device *dev[32]; - struct virt_root_hub rh; - - const char *slot_name; -} ohci_t; - -#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ - -struct ohci_device { - ed_t ed[NUM_EDS]; - int ed_cnt; -}; - -/* hcd */ -/* endpoint */ -static int ep_link(ohci_t * ohci, ed_t * ed); -static int ep_unlink(ohci_t * ohci, ed_t * ed); -static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe); - -/*-------------------------------------------------------------------------*/ - -/* we need more TDs than EDs */ -#define NUM_TD 64 - -/* +1 so we can align the storage */ -td_t gtd[NUM_TD+1]; -/* pointers to aligned storage */ -td_t *ptd; - -/* TDs ... */ -static inline struct td * -td_alloc (struct usb_device *usb_dev) -{ - int i; - struct td *td; - - td = NULL; - for (i = 0; i < NUM_TD; i++) { - if (ptd[i].usb_dev == NULL) { - td = &ptd[i]; - td->usb_dev = usb_dev; - break; - } - } - return td; -} - -static inline void -ed_free (struct ed *ed) -{ - ed->usb_dev = NULL; -} diff --git a/arch/mips/cpu/mips32/au1x00/config.mk b/arch/mips/cpu/mips32/au1x00/config.mk deleted file mode 100644 index 5c89129..0000000 --- a/arch/mips/cpu/mips32/au1x00/config.mk +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2011 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -PLATFORM_CPPFLAGS += -mtune=4kc diff --git a/arch/mips/cpu/mips32/cpu.c b/arch/mips/cpu/mips32/cpu.c index 8e1cc4e..07deca8 100644 --- a/arch/mips/cpu/mips32/cpu.c +++ b/arch/mips/cpu/mips32/cpu.c @@ -32,11 +32,3 @@ void write_one_tlb(int index, u32 pagemask, u32 hi, u32 low0, u32 low1) write_c0_index(index); tlb_write_indexed(); } - -int cpu_eth_init(bd_t *bis) -{ -#ifdef CONFIG_SOC_AU1X00 - au1x00_enet_initialize(bis); -#endif - return 0; -} diff --git a/arch/mips/mach-au1x00/Makefile b/arch/mips/mach-au1x00/Makefile new file mode 100644 index 0000000..c5643e7 --- /dev/null +++ b/arch/mips/mach-au1x00/Makefile @@ -0,0 +1,8 @@ +# +# (C) Copyright 2011 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y = au1x00_eth.o au1x00_serial.o au1x00_usb_ohci.o au1x00_ide.o diff --git a/arch/mips/mach-au1x00/au1x00_eth.c b/arch/mips/mach-au1x00/au1x00_eth.c new file mode 100644 index 0000000..39c5b6b --- /dev/null +++ b/arch/mips/mach-au1x00/au1x00_eth.c @@ -0,0 +1,302 @@ +/* Only eth0 supported for now + * + * (C) Copyright 2003 + * Thomas.Lange@corelatus.se + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include + +#if defined(CONFIG_SYS_DISCOVER_PHY) +#error "PHY not supported yet" +/* We just assume that we are running 100FD for now */ +/* We all use switches, right? ;-) */ +#endif + +/* I assume ethernet behaves like au1000 */ + +#ifdef CONFIG_SOC_AU1000 +/* Base address differ between cpu:s */ +#define ETH0_BASE AU1000_ETH0_BASE +#define MAC0_ENABLE AU1000_MAC0_ENABLE +#else +#ifdef CONFIG_SOC_AU1100 +#define ETH0_BASE AU1100_ETH0_BASE +#define MAC0_ENABLE AU1100_MAC0_ENABLE +#else +#ifdef CONFIG_SOC_AU1500 +#define ETH0_BASE AU1500_ETH0_BASE +#define MAC0_ENABLE AU1500_MAC0_ENABLE +#else +#ifdef CONFIG_SOC_AU1550 +#define ETH0_BASE AU1550_ETH0_BASE +#define MAC0_ENABLE AU1550_MAC0_ENABLE +#else +#error "No valid cpu set" +#endif +#endif +#endif +#endif + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_CMD_MII) +#include +#endif + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 +#define PKT_MAXBUF_SIZE 1518 + +static char txbuf[DBUF_LENGTH]; + +static int next_tx; +static int next_rx; + +/* 4 rx and 4 tx fifos */ +#define NO_OF_FIFOS 4 + +typedef struct{ + u32 status; + u32 addr; + u32 len; /* Only used for tx */ + u32 not_used; +} mac_fifo_t; + +mac_fifo_t mac_fifo[NO_OF_FIFOS]; + +#define MAX_WAIT 1000 + +#if defined(CONFIG_CMD_MII) +int au1x00_miiphy_read(const char *devname, unsigned char addr, + unsigned char reg, unsigned short * value) +{ + volatile u32 *mii_control_reg = (volatile u32*)(ETH0_BASE+MAC_MII_CNTRL); + volatile u32 *mii_data_reg = (volatile u32*)(ETH0_BASE+MAC_MII_DATA); + u32 mii_control; + unsigned int timedout = 20; + + while (*mii_control_reg & MAC_MII_BUSY) { + udelay(1000); + if (--timedout == 0) { + printf("au1x00_eth: miiphy_read busy timeout!!\n"); + return -1; + } + } + + mii_control = MAC_SET_MII_SELECT_REG(reg) | + MAC_SET_MII_SELECT_PHY(addr) | MAC_MII_READ; + + *mii_control_reg = mii_control; + + timedout = 20; + while (*mii_control_reg & MAC_MII_BUSY) { + udelay(1000); + if (--timedout == 0) { + printf("au1x00_eth: miiphy_read busy timeout!!\n"); + return -1; + } + } + *value = *mii_data_reg; + return 0; +} + +int au1x00_miiphy_write(const char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + volatile u32 *mii_control_reg = (volatile u32*)(ETH0_BASE+MAC_MII_CNTRL); + volatile u32 *mii_data_reg = (volatile u32*)(ETH0_BASE+MAC_MII_DATA); + u32 mii_control; + unsigned int timedout = 20; + + while (*mii_control_reg & MAC_MII_BUSY) { + udelay(1000); + if (--timedout == 0) { + printf("au1x00_eth: miiphy_write busy timeout!!\n"); + return -1; + } + } + + mii_control = MAC_SET_MII_SELECT_REG(reg) | + MAC_SET_MII_SELECT_PHY(addr) | MAC_MII_WRITE; + + *mii_data_reg = value; + *mii_control_reg = mii_control; + return 0; +} +#endif + +static int au1x00_send(struct eth_device *dev, void *packet, int length) +{ + volatile mac_fifo_t *fifo_tx = + (volatile mac_fifo_t*)(MAC0_TX_DMA_ADDR+MAC_TX_BUFF0_STATUS); + int i; + int res; + + /* tx fifo should always be idle */ + fifo_tx[next_tx].len = length; + fifo_tx[next_tx].addr = (virt_to_phys(packet))|TX_DMA_ENABLE; + au_sync(); + + udelay(1); + i=0; + while(!(fifo_tx[next_tx].addr&TX_T_DONE)){ + if(i>MAX_WAIT){ + printf("TX timeout\n"); + break; + } + udelay(1); + i++; + } + + /* Clear done bit */ + fifo_tx[next_tx].addr = 0; + fifo_tx[next_tx].len = 0; + au_sync(); + + res = fifo_tx[next_tx].status; + + next_tx++; + if(next_tx>=NO_OF_FIFOS){ + next_tx=0; + } + return(res); +} + +static int au1x00_recv(struct eth_device* dev){ + volatile mac_fifo_t *fifo_rx = + (volatile mac_fifo_t*)(MAC0_RX_DMA_ADDR+MAC_RX_BUFF0_STATUS); + + int length; + u32 status; + + for(;;){ + if(!(fifo_rx[next_rx].addr&RX_T_DONE)){ + /* Nothing has been received */ + return(-1); + } + + status = fifo_rx[next_rx].status; + + length = status&0x3FFF; + + if(status&RX_ERROR){ + printf("Rx error 0x%x\n", status); + } + else{ + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[next_rx], length - 4); + } + + fifo_rx[next_rx].addr = (virt_to_phys(NetRxPackets[next_rx]))|RX_DMA_ENABLE; + + next_rx++; + if(next_rx>=NO_OF_FIFOS){ + next_rx=0; + } + } /* for */ + + return(0); /* Does anyone use this? */ +} + +static int au1x00_init(struct eth_device* dev, bd_t * bd){ + + volatile u32 *macen = (volatile u32*)MAC0_ENABLE; + volatile u32 *mac_ctrl = (volatile u32*)(ETH0_BASE+MAC_CONTROL); + volatile u32 *mac_addr_high = (volatile u32*)(ETH0_BASE+MAC_ADDRESS_HIGH); + volatile u32 *mac_addr_low = (volatile u32*)(ETH0_BASE+MAC_ADDRESS_LOW); + volatile u32 *mac_mcast_high = (volatile u32*)(ETH0_BASE+MAC_MCAST_HIGH); + volatile u32 *mac_mcast_low = (volatile u32*)(ETH0_BASE+MAC_MCAST_LOW); + volatile mac_fifo_t *fifo_tx = + (volatile mac_fifo_t*)(MAC0_TX_DMA_ADDR+MAC_TX_BUFF0_STATUS); + volatile mac_fifo_t *fifo_rx = + (volatile mac_fifo_t*)(MAC0_RX_DMA_ADDR+MAC_RX_BUFF0_STATUS); + int i; + + next_tx = TX_GET_DMA_BUFFER(fifo_tx[0].addr); + next_rx = RX_GET_DMA_BUFFER(fifo_rx[0].addr); + + /* We have to enable clocks before releasing reset */ + *macen = MAC_EN_CLOCK_ENABLE; + udelay(10); + + /* Enable MAC0 */ + /* We have to release reset before accessing registers */ + *macen = MAC_EN_CLOCK_ENABLE|MAC_EN_RESET0| + MAC_EN_RESET1|MAC_EN_RESET2; + udelay(10); + + for(i=0;ienetaddr + *mac_addr_high = (ea[5] << 8) | (ea[4] ) ; + *mac_addr_low = (ea[3] << 24) | (ea[2] << 16) | + (ea[1] << 8) | (ea[0] ) ; +#undef ea + *mac_mcast_low = 0; + *mac_mcast_high = 0; + + /* Make sure the MAC buffer is in the correct endian mode */ +#ifdef __LITTLE_ENDIAN + *mac_ctrl = MAC_FULL_DUPLEX; + udelay(1); + *mac_ctrl = MAC_FULL_DUPLEX|MAC_RX_ENABLE|MAC_TX_ENABLE; +#else + *mac_ctrl = MAC_BIG_ENDIAN|MAC_FULL_DUPLEX; + udelay(1); + *mac_ctrl = MAC_BIG_ENDIAN|MAC_FULL_DUPLEX|MAC_RX_ENABLE|MAC_TX_ENABLE; +#endif + + return(1); +} + +static void au1x00_halt(struct eth_device* dev){ + volatile u32 *macen = (volatile u32*)MAC0_ENABLE; + + /* Put MAC0 in reset */ + *macen = 0; +} + +int au1x00_enet_initialize(bd_t *bis){ + struct eth_device* dev; + + if ((dev = (struct eth_device*)malloc(sizeof *dev)) == NULL) { + puts ("malloc failed\n"); + return -1; + } + + memset(dev, 0, sizeof *dev); + + sprintf(dev->name, "Au1X00 ethernet"); + dev->iobase = 0; + dev->priv = 0; + dev->init = au1x00_init; + dev->halt = au1x00_halt; + dev->send = au1x00_send; + dev->recv = au1x00_recv; + + eth_register(dev); + +#if defined(CONFIG_CMD_MII) + miiphy_register(dev->name, + au1x00_miiphy_read, au1x00_miiphy_write); +#endif + + return 1; +} + +int cpu_eth_init(bd_t *bis) +{ + au1x00_enet_initialize(bis); + return 0; +} diff --git a/arch/mips/mach-au1x00/au1x00_ide.c b/arch/mips/mach-au1x00/au1x00_ide.c new file mode 100644 index 0000000..ba0b35d --- /dev/null +++ b/arch/mips/mach-au1x00/au1x00_ide.c @@ -0,0 +1,15 @@ +/* + * (C) Copyright 2000-2011 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +/* AU1X00 swaps data in big-endian mode, enforce little-endian function */ +void ide_input_swap_data(int dev, ulong *sect_buf, int words) +{ + ide_input_data(dev, sect_buf, words); +} diff --git a/arch/mips/mach-au1x00/au1x00_serial.c b/arch/mips/mach-au1x00/au1x00_serial.c new file mode 100644 index 0000000..0463508 --- /dev/null +++ b/arch/mips/mach-au1x00/au1x00_serial.c @@ -0,0 +1,131 @@ +/* + * AU1X00 UART support + * + * Hardcoded to UART 0 for now + * Speed and options also hardcoded to 115200 8N1 + * + * Copyright (c) 2003 Thomas.Lange@corelatus.se + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/****************************************************************************** +* +* serial_init - initialize a channel +* +* This routine initializes the number of data bits, parity +* and set the selected baud rate. Interrupts are disabled. +* Set the modem control signals if the option is selected. +* +* RETURNS: N/A +*/ + +static int au1x00_serial_init(void) +{ + volatile u32 *uart_fifoctl = (volatile u32*)(UART0_ADDR+UART_FCR); + volatile u32 *uart_enable = (volatile u32*)(UART0_ADDR+UART_ENABLE); + + /* Enable clocks first */ + *uart_enable = UART_EN_CE; + + /* Then release reset */ + /* Must release reset before setting other regs */ + *uart_enable = UART_EN_CE|UART_EN_E; + + /* Activate fifos, reset tx and rx */ + /* Set tx trigger level to 12 */ + *uart_fifoctl = UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR| + UART_FCR_CLEAR_XMIT|UART_FCR_T_TRIGGER_12; + + serial_setbrg(); + + return 0; +} + + +static void au1x00_serial_setbrg(void) +{ + volatile u32 *uart_clk = (volatile u32*)(UART0_ADDR+UART_CLK); + volatile u32 *uart_lcr = (volatile u32*)(UART0_ADDR+UART_LCR); + volatile u32 *sys_powerctrl = (u32 *)SYS_POWERCTRL; + int sd; + int divisorx2; + + /* sd is system clock divisor */ + /* see section 10.4.5 in au1550 datasheet */ + sd = (*sys_powerctrl & 0x03) + 2; + + /* calulate 2x baudrate and round */ + divisorx2 = ((CONFIG_SYS_MIPS_TIMER_FREQ/(sd * 16 * CONFIG_BAUDRATE))); + + if (divisorx2 & 0x01) + divisorx2 = divisorx2 + 1; + + *uart_clk = divisorx2 / 2; + + /* Set parity, stop bits and word length to 8N1 */ + *uart_lcr = UART_LCR_WLEN8; +} + +static void au1x00_serial_putc(const char c) +{ + volatile u32 *uart_lsr = (volatile u32*)(UART0_ADDR+UART_LSR); + volatile u32 *uart_tx = (volatile u32*)(UART0_ADDR+UART_TX); + + if (c == '\n') + au1x00_serial_putc('\r'); + + /* Wait for fifo to shift out some bytes */ + while((*uart_lsr&UART_LSR_THRE)==0); + + *uart_tx = (u32)c; +} + +static int au1x00_serial_getc(void) +{ + volatile u32 *uart_rx = (volatile u32*)(UART0_ADDR+UART_RX); + char c; + + while (!serial_tstc()); + + c = (*uart_rx&0xFF); + return c; +} + +static int au1x00_serial_tstc(void) +{ + volatile u32 *uart_lsr = (volatile u32*)(UART0_ADDR+UART_LSR); + + if(*uart_lsr&UART_LSR_DR){ + /* Data in rfifo */ + return(1); + } + return 0; +} + +static struct serial_device au1x00_serial_drv = { + .name = "au1x00_serial", + .start = au1x00_serial_init, + .stop = NULL, + .setbrg = au1x00_serial_setbrg, + .putc = au1x00_serial_putc, + .puts = default_serial_puts, + .getc = au1x00_serial_getc, + .tstc = au1x00_serial_tstc, +}; + +void au1x00_serial_initialize(void) +{ + serial_register(&au1x00_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &au1x00_serial_drv; +} diff --git a/arch/mips/mach-au1x00/au1x00_usb_ohci.c b/arch/mips/mach-au1x00/au1x00_usb_ohci.c new file mode 100644 index 0000000..74bdb77 --- /dev/null +++ b/arch/mips/mach-au1x00/au1x00_usb_ohci.c @@ -0,0 +1,1609 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the AU1x00. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * SPDX-License-Identifier: GPL-2.0+ + * Note: Part of this code has been derived from linux + * + */ +/* + * IMPORTANT NOTES + * 1 - this driver is intended for use with USB Mass Storage Devices + * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include + +#ifdef CONFIG_USB_OHCI + +/* #include no PCI on the AU1x00 */ + +#include +#include +#include +#include +#include +#include "au1x00_usb_ohci.h" + +#define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#define OHCI_VERBOSE_DEBUG /* not always helpful */ +#define OHCI_FILL_TRACE + +#define USBH_ENABLE_BE (1<<0) +#define USBH_ENABLE_C (1<<1) +#define USBH_ENABLE_E (1<<2) +#define USBH_ENABLE_CE (1<<3) +#define USBH_ENABLE_RD (1<<4) + +#ifdef __LITTLE_ENDIAN +#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C) +#else +#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE) +#endif + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#undef readl +#undef writel + +#define readl(a) au_readl((long)(a)) +#define writel(v,a) au_writel((v),(int)(a)) + +#define DEBUG +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#define SHOW_INFO +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) swap_16(x) +#define m32_swap(x) swap_32(x) + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +urb_priv_t urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; + +/*-------------------------------------------------------------------------*/ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define OHCI_QUIRK_AMD756 0xabcd +#define read_roothub(hc, register, mask) ({ \ + u32 temp = readl (&hc->regs->roothub.register); \ + if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = readl (&hc->regs->roothub.register); \ + temp; }) + +static u32 roothub_a (struct ohci *hc) + { return read_roothub (hc, a, 0xfc0fe000); } +static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) + { return read_roothub (hc, portstatus [i], 0xffe0fce0); } + + +/* forward declaration */ +static int hc_interrupt (void); +static void +td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv (urb_priv_t * urb) +{ + int i; + int last; + struct td * td; + + last = urb->length - 1; + if (last >= 0) { + for (i = 0; i <= last; i++) { + td = urb->td[i]; + if (td) { + td->usb_dev = NULL; + urb->td[i] = NULL; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number (struct usb_device * dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, + int transfer_len, struct devrequest * setup, char * str, int small) +{ + urb_priv_t * purb = &urb_priv; + + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", + str, + sohci_get_current_frame_number (dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + purb->actual_length, + transfer_len, dev->status); +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf (__FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((__u8 *) setup) [i]); + printf ("\n"); + } + if (transfer_len > 0 && buffer) { + printf (__FILE__ ": data(%d/%d):", + purb->actual_length, + transfer_len); + len = usb_pipeout (pipe)? + transfer_len: purb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((__u8 *) buffer) [i]); + printf ("%s\n", i < len? "...": ""); + } + } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t *ohci, char * str) { + int i, j; + __u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + ed_t *ed = (ed_t *)m32_swap(ed_p); + printf (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printf ("\n"); + } +} + +static void ohci_dump_intr_mask (char *label, __u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} + +static void maybe_print_eds (char *label, __u32 value) +{ + ed_t *edp = (ed_t *)value; + + if (value) { + dbg ("%s %08x", label, value); + dbg ("%08x", edp->hwINFO); + dbg ("%08x", edp->hwTailP); + dbg ("%08x", edp->hwHeadP); + dbg ("%08x", edp->hwNextED); + } +} + +static char * hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status (ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller usb-%s state", controller->slot_name); + + /* dumps some of the state we know about */ + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} + + +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + ohci_t *ohci; + ed_t * ed; + urb_priv_t *purb_priv; + int i, size = 0; + + ohci = &gohci; + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + err("sohci_submit_job: EPIPE"); + return -1; + } + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (dev, pipe))) { + err("sohci_submit_job: ENOMEM"); + return -1; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0)? 2: + (transfer_len - 1) / 4096 + 3; + break; + } + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv = &urb_priv; + purb_priv->pipe = pipe; + + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for (i = 0; i < size; i++) { + purb_priv->td[i] = td_alloc (dev); + if (!purb_priv->td[i]) { + purb_priv->length = i; + urb_free_priv (purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link (ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ + ohci_t *ohci = &gohci; + + return m16_swap (ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link (ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel ((long)ed, &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = m32_swap (ed); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + writel ((long)ed, &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = m32_swap (ed); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink (ohci_t *ohci, ed_t *ed) +{ + ed->hwINFO |= m32_swap (OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + } + ed->state = ED_UNLINK; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + + ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | + (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + + if (ed->state == ED_NEW) { + ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (usb_dev); + ed->hwTailP = m32_swap (td); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci_dev.ed_cnt++; + } + + ed->hwINFO = m32_swap (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | (usb_dev->speed == USB_SPEED_LOW) << 13 + | usb_maxpacket (usb_dev, pipe) << 16); + + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill (ohci_t *ohci, unsigned int info, + void *data, int len, + struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ + volatile td_t *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + + if (index > urb_priv->length) { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if (1 || (usb_pipebulk(urb_priv->pipe) && + usb_pipeout(urb_priv->pipe))) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x\n",i, ((unsigned char *)(td->data+0x80000000))[i]); + } +#endif + if (!len) + data = 0; + + td->hwINFO = m32_swap (info); + td->hwCBP = m32_swap (data); + if (data) + td->hwBE = m32_swap (data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = m32_swap (td_pt); + td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +#define kseg_to_phys(x) ((void *)((__u32)(x) - 0x80000000)) + +static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) +{ + ohci_t *ohci = &gohci; + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); + } + urb->td_cnt = 0; + if (data_len) + data = kseg_to_phys(buffer); + else + data = 0; + + switch (usb_pipetype (pipe)) { + case PIPE_BULK: + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); + cnt++; + + if (!ohci->sleeping) + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (ohci, info, kseg_to_phys(setup), 8, dev, cnt++, urb); + if (data_len > 0) { + info = usb_pipeout (pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, dev, cnt++, urb); + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, dev, cnt++, urb); + if (!ohci->sleeping) + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ + __u32 tdINFO, tdBE, tdCBP; + urb_priv_t *lurb_priv = &urb_priv; + + tdINFO = m32_swap (td->hwINFO); + tdBE = m32_swap (td->hwBE); + tdCBP = m32_swap (td->hwCBP); + + + if (!(usb_pipecontrol(lurb_priv->pipe) && + ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + lurb_priv->actual_length += tdBE - td->data + 1; + else + lurb_priv->actual_length += tdCBP - td->data; + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static td_t * dl_reverse_done_list (ohci_t *ohci) +{ + __u32 td_list_hc; + td_t *td_rev = NULL; + td_t *td_list = NULL; + urb_priv_t *lurb_priv = NULL; + + td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = (td_t *)td_list_hc; + + if (TD_CC_GET (m32_swap (td_list->hwINFO))) { + lurb_priv = &urb_priv; + dbg(" USB-error/status: %x : %p", + TD_CC_GET (m32_swap (td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & m32_swap (0x1)) { + if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { + td_list->ed->hwHeadP = + (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) | + (td_list->ed->hwHeadP & m32_swap (0x2)); + lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= m32_swap (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* td done list */ +static int dl_done_list (ohci_t *ohci, td_t *td_list) +{ + td_t *td_list_next = NULL; + ed_t *ed; + int cc = 0; + int stat = 0; + /* urb_t *urb; */ + urb_priv_t *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + lurb_priv = &urb_priv; + tdINFO = m32_swap (td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); + + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (cc != 0) { + dbg("ConditionCode %#x", cc); + stat = cc_to_error[cc]; + } + + if (ed->state != ED_NEW) { + edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; + edTailP = m32_swap (ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } + + td_list = td_list_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +#include + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} +#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} +#else +#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + __u32 temp, ndp, i; + int res; + + res = -1; + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && + ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + void * data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u32 datab[4]; + __u8 *data_buf = (__u8 *)datab; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + +#ifdef DEBUG +urb_priv.actual_length = 0; +pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +#else + mdelay(1); +#endif + if (usb_pipeint(pipe)) { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = m16_swap (cmd->value); + wIndex = m16_swap (cmd->index); + wLength = m16_swap (cmd->length); + + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", + dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = m16_swap (1); OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = m32_swap ( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK (4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_POCI); OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); + case (RH_C_PORT_CONNECTION): + WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); + case (RH_C_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_PESC); OK (0); + case (RH_C_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); + case (RH_C_PORT_OVER_CURRENT): + WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); + case (RH_C_PORT_RESET): + WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PRS); + OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PES ); + OK (0); + } + break; + + case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + if(wValue==0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + OK(len); + } + if(wValue==0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a (&gohci); + + data_buf [0] = 9; /* min length; */ + data_buf [1] = 0x29; + data_buf [2] = temp & RH_A_NDP; + data_buf [3] = 0; + if (temp & RH_A_PSM) /* per-port power switching? */ + data_buf [3] |= 0x1; + if (temp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf [3] |= 0x10; + else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ + data_buf [3] |= 0x8; + + /* corresponds to data_buf[4-7] */ + datab [1] = 0; + data_buf [5] = (temp & RH_A_POTPGT) >> 24; + temp = roothub_b (&gohci); + data_buf [7] = temp & RH_B_DR; + if (data_buf [2] < 7) { + data_buf [8] = 0xff; + } else { + data_buf [0] += 2; + data_buf [8] = (temp & RH_B_DR) >> 8; + data_buf [10] = data_buf [9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf [0], wLength)); + OK (len); + } + + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); + + case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); + + default: + dbg ("unsupported root hub command"); + stat = USB_ST_STALLED; + } + +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#else + mdelay(1); +#endif + + len = min_t(int, len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + dev->act_len = len; + dev->status = stat; + +#ifdef DEBUG + if (transfer_len) + urb_priv.actual_length = transfer_len; + pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#else + mdelay(1); +#endif + + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#else + mdelay(1); +#endif + if (!maxsize) { + err("submit_common_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + + if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { + err("sohci_submit_job failed"); + return -1; + } + + mdelay(10); + /* ohci_dump_status(&gohci); */ + + /* allow more time for a BULK device to react - some are slow */ +#define BULK_TO 5000 /* timeout in milliseconds */ + if (usb_pipebulk(pipe)) + timeout = BULK_TO; + else + timeout = 100; + + timeout *= 4; + /* wait for it to complete */ + for (;;) { + /* check whether the controller is done */ + stat = hc_interrupt(); + if (stat < 0) { + stat = USB_ST_CRC_ERR; + break; + } + if (stat >= 0 && stat != 0xff) { + /* 0xff is returned for an SF-interrupt */ + break; + } + if (--timeout) { + udelay(250); /* mdelay(1); */ + } else { + err("CTL:TIMEOUT "); + stat = USB_ST_CRC_ERR; + break; + } + } + /* we got an Root Hub Status Change interrupt */ + if (got_rhsc) { +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#endif + got_rhsc = 0; + /* abuse timeout */ + timeout = rh_check_port_status(&gohci); + if (timeout >= 0) { +#if 0 /* this does nothing useful, but leave it here in case that changes */ + /* the called routine adds 1 to the passed value */ + usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); +#endif + /* + * XXX + * This is potentially dangerous because it assumes + * that only one device is ever plugged in! + */ + devgone = dev; + } + } + + dev->status = stat; + dev->act_len = transfer_len; + +#ifdef DEBUG + pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); +#else + mdelay(1); +#endif + + /* free TDs in urb_priv */ + urb_free_priv (&urb_priv); + return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len) +{ + info("submit_bulk_msg"); + return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup) +{ + int maxsize = usb_maxpacket(dev, pipe); + + info("submit_control_msg"); +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#else + mdelay(1); +#endif + if (!maxsize) { + err("submit_control_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { + gohci.rh.dev = dev; + /* root hub - redirect */ + return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, + setup); + } + + return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, int interval) +{ + info("submit_int_msg"); + return -1; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset (ohci_t *ohci) +{ + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ + info("USB HC TakeOver from SMM"); + while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { + mdelay (10); + if (--smm_timeout == 0) { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;", + ohci->slot_name, + readl (&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + writel (0, &ohci->regs->control); + + /* HC Reset requires max 10 us delay */ + writel (OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start (ohci_t * ohci) +{ + __u32 mask; + unsigned int fminterval; + + ohci->disabled = 1; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel (0, &ohci->regs->ed_controlhead); + writel (0, &ohci->regs->ed_bulkhead); + + writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ + + fminterval = 0x2edf; + writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci->regs->fminterval); + writel (0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + writel (ohci->hc_control, &ohci->regs->control); + + /* disable all interrupts */ + mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | + OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | + OHCI_INTR_OC | OHCI_INTR_MIE); + writel (mask, &ohci->regs->intrdisable); + /* clear all interrupts */ + mask &= ~OHCI_INTR_MIE; + writel (mask, &ohci->regs->intrstatus); + /* Choose the interrupts we care about now - but w/o MIE */ + mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + + /* POTPGT delay is bits 24-31, in 2 ms units. */ + mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int +hc_interrupt (void) +{ + ohci_t *ohci = &gohci; + struct ohci_regs *regs = ohci->regs; + int ints; + int stat = -1; + + if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) { + ints = OHCI_INTR_WDH; + } else { + ints = readl (®s->intrstatus); + } + + /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ + + if (ints & OHCI_INTR_RHSC) { + got_rhsc = 1; + } + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err ("OHCI Unrecoverable Error, controller usb-%s disabled", + ohci->slot_name); + /* e.g. due to PCI Master/Target Abort */ + +#ifdef DEBUG + ohci_dump (ohci, 1); +#else + mdelay(1); +#endif + /* FIXME: be optimistic, hope that bug won't repeat often. */ + /* Make some non-interrupt context restart the controller. */ + /* Count and limit the retries though; either hardware or */ + /* software errors can go forever... */ + hc_reset (ohci); + return -1; + } + + if (ints & OHCI_INTR_WDH) { + mdelay(1); + writel (OHCI_INTR_WDH, ®s->intrdisable); + stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun\n"); + writel (OHCI_INTR_SO, ®s->intrenable); + stat = -1; + } + + /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ + if (ints & OHCI_INTR_SF) { + unsigned int frame = m16_swap (ohci->hcca->frame_no) & 1; + mdelay(1); + writel (OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[frame] != NULL) + writel (OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + + writel (ints, ®s->intrstatus); + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci (ohci_t *ohci) +{ + dbg ("USB HC release ohci usb-%s", ohci->slot_name); + + if (!ohci->disabled) + hc_reset (ohci); +} + +/*-------------------------------------------------------------------------*/ + +#define __read_32bit_c0_register(source, sel) \ +({ int __res; \ + if (sel == 0) \ + __asm__ __volatile__( \ + "mfc0\t%0, " #source "\n\t" \ + : "=r" (__res)); \ + else \ + __asm__ __volatile__( \ + ".set\tmips32\n\t" \ + "mfc0\t%0, " #source ", " #sel "\n\t" \ + ".set\tmips0\n\t" \ + : "=r" (__res)); \ + __res; \ +}) + +#define read_c0_prid() __read_32bit_c0_register($15, 0) + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + u32 pin_func; + u32 sys_freqctrl, sys_clksrc; + u32 prid = read_c0_prid(); + + dbg("in usb_lowlevel_init\n"); + + /* zero and disable FREQ2 */ + sys_freqctrl = au_readl(SYS_FREQCTRL0); + sys_freqctrl &= ~0xFFF00000; + au_writel(sys_freqctrl, SYS_FREQCTRL0); + + /* zero and disable USBH/USBD clocks */ + sys_clksrc = au_readl(SYS_CLKSRC); + sys_clksrc &= ~0x00007FE0; + au_writel(sys_clksrc, SYS_CLKSRC); + + sys_freqctrl = au_readl(SYS_FREQCTRL0); + sys_freqctrl &= ~0xFFF00000; + + sys_clksrc = au_readl(SYS_CLKSRC); + sys_clksrc &= ~0x00007FE0; + + switch (prid & 0x000000FF) { + case 0x00: /* DA */ + case 0x01: /* HA */ + case 0x02: /* HB */ + /* CPU core freq to 48MHz to slow it way down... */ + au_writel(4, SYS_CPUPLL); + + /* + * Setup 48MHz FREQ2 from CPUPLL for USB Host + */ + /* FRDIV2=3 -> div by 8 of 384MHz -> 48MHz */ + sys_freqctrl |= ((3<<22) | (1<<21) | (0<<20)); + au_writel(sys_freqctrl, SYS_FREQCTRL0); + + /* CPU core freq to 384MHz */ + au_writel(0x20, SYS_CPUPLL); + + printf("Au1000: 48MHz OHCI workaround enabled\n"); + break; + + default: /* HC and newer */ + /* FREQ2 = aux/2 = 48 MHz */ + sys_freqctrl |= ((0<<22) | (1<<21) | (1<<20)); + au_writel(sys_freqctrl, SYS_FREQCTRL0); + break; + } + + /* + * Route 48MHz FREQ2 into USB Host and/or Device + */ + sys_clksrc |= ((4<<12) | (0<<11) | (0<<10)); + au_writel(sys_clksrc, SYS_CLKSRC); + + /* configure pins GPIO[14:9] as GPIO */ + pin_func = au_readl(SYS_PINFUNC) & (u32)(~0x8080); + + au_writel(pin_func, SYS_PINFUNC); + au_writel(0x2800, SYS_TRIOUTCLR); + au_writel(0x0030, SYS_OUTPUTCLR); + + dbg("OHCI board setup complete\n"); + + /* enable host controller */ + au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG); + udelay(1000); + au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG); + udelay(1000); + + /* wait for reset complete (read register twice; see au1500 errata) */ + while (au_readl(USB_HOST_CONFIG), + !(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD)) + udelay(1000); + + dbg("OHCI clock running\n"); + + memset (&gohci, 0, sizeof (ohci_t)); + memset (&urb_priv, 0, sizeof (urb_priv_t)); + + /* align the storage */ + if ((__u32)&ghcca[0] & 0xff) { + err("HCCA not aligned!!"); + return -1; + } + phcca = &ghcca[0]; + info("aligned ghcca %p", phcca); + memset(&ohci_dev, 0, sizeof(struct ohci_device)); + if ((__u32)&ohci_dev.ed[0] & 0x7) { + err("EDs not aligned!!"); + return -1; + } + memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); + if ((__u32)gtd & 0x7) { + err("TDs not aligned!!"); + return -1; + } + ptd = gtd; + gohci.hcca = phcca; + memset (phcca, 0, sizeof (struct ohci_hcca)); + + gohci.disabled = 1; + gohci.sleeping = 0; + gohci.irq = -1; + gohci.regs = (struct ohci_regs *)(USB_OHCI_BASE | 0xA0000000); + + gohci.flags = 0; + gohci.slot_name = "au1x00"; + + dbg("OHCI revision: 0x%08x\n" + " RH: a: 0x%08x b: 0x%08x\n", + readl(&gohci.regs->revision), + readl(&gohci.regs->roothub.a), readl(&gohci.regs->roothub.b)); + + if (hc_reset (&gohci) < 0) + goto errout; + + /* FIXME this is a second HC reset; why?? */ + writel (gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control); + mdelay (10); + + if (hc_start (&gohci) < 0) + goto errout; + +#ifdef DEBUG + ohci_dump (&gohci, 1); +#else + mdelay(1); +#endif + ohci_inited = 1; + return 0; + + errout: + err("OHCI initialization error\n"); + hc_release_ohci (&gohci); + /* Initialization failed */ + au_writel(readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); + return -1; +} + +int usb_lowlevel_stop(int index) +{ + /* this gets called really early - before the controller has */ + /* even been initialized! */ + if (!ohci_inited) + return 0; + /* TODO release any interrupts, etc. */ + /* call hc_release_ohci() here ? */ + hc_reset (&gohci); + /* may not want to do this */ + /* Disable clock */ + au_writel(readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); + return 0; +} + +#endif /* CONFIG_USB_OHCI */ diff --git a/arch/mips/mach-au1x00/au1x00_usb_ohci.h b/arch/mips/mach-au1x00/au1x00_usb_ohci.h new file mode 100644 index 0000000..bb9f351 --- /dev/null +++ b/arch/mips/mach-au1x00/au1x00_usb_ohci.h @@ -0,0 +1,416 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * usb-ohci.h + */ + + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ + /* No Error */ 0, + /* CRC Error */ USB_ST_CRC_ERR, + /* Bit Stuff */ USB_ST_BIT_ERR, + /* Data Togg */ USB_ST_CRC_ERR, + /* Stall */ USB_ST_STALLED, + /* DevNotResp */ -1, + /* PIDCheck */ USB_ST_BIT_ERR, + /* UnExpPID */ USB_ST_BIT_ERR, + /* DataOver */ USB_ST_BUF_ERR, + /* DataUnder */ USB_ST_BUF_ERR, + /* reservd */ -1, + /* reservd */ -1, + /* BufferOver */ USB_ST_BUF_ERR, + /* BuffUnder */ USB_ST_BUF_ERR, + /* Not Access */ -1, + /* Not Access */ -1 +}; + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed *ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed *ed_rm_list; + + struct usb_device *usb_dev; + __u32 unused[3]; +} __attribute__((aligned(16))); +typedef struct ed ed_t; + + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + + __u16 hwPSW[MAXPSW]; + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute__((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute__((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute__((aligned(32))); + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct +{ + ed_t *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + int state; + unsigned long pipe; + int actual_length; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma;*/ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t *ed_bulktail; /* last endpoint of bulk list */ + ed_t *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +} ohci_t; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD+1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td * +td_alloc (struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) { + if (ptd[i].usb_dev == NULL) { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + return td; +} + +static inline void +ed_free (struct ed *ed) +{ + ed->usb_dev = NULL; +} diff --git a/arch/mips/mach-au1x00/config.mk b/arch/mips/mach-au1x00/config.mk new file mode 100644 index 0000000..5c89129 --- /dev/null +++ b/arch/mips/mach-au1x00/config.mk @@ -0,0 +1,8 @@ +# +# (C) Copyright 2011 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +PLATFORM_CPPFLAGS += -mtune=4kc -- cgit v1.1 From eef88dfb3e16e4631e078f42714dbcef3216e3e1 Mon Sep 17 00:00:00 2001 From: Daniel Schwierzeck Date: Thu, 29 Jan 2015 14:56:20 +0100 Subject: MIPS: unify CPU code in arch/mips/cpu/ Unify and move code in arch/mips/cpu/mips[32|64]/ to arch/mips/cpu/. The CPU specific config.mk files need to remain until CONFIG_STANDALONE_LOAD_ADDR is converted to a global Kconfig symbol. Signed-off-by: Paul Burton Signed-off-by: Daniel Schwierzeck --- arch/mips/Makefile | 6 +- arch/mips/cpu/Makefile | 9 ++ arch/mips/cpu/cpu.c | 38 +++++ arch/mips/cpu/interrupts.c | 22 +++ arch/mips/cpu/mips32/Makefile | 9 -- arch/mips/cpu/mips32/cpu.c | 34 ---- arch/mips/cpu/mips32/interrupts.c | 22 --- arch/mips/cpu/mips32/start.S | 320 -------------------------------------- arch/mips/cpu/mips32/time.c | 19 --- arch/mips/cpu/mips64/Makefile | 9 -- arch/mips/cpu/mips64/cpu.c | 37 ----- arch/mips/cpu/mips64/interrupts.c | 22 --- arch/mips/cpu/mips64/start.S | 291 ---------------------------------- arch/mips/cpu/mips64/time.c | 19 --- arch/mips/cpu/start.S | 320 ++++++++++++++++++++++++++++++++++++++ arch/mips/cpu/time.c | 19 +++ 16 files changed, 410 insertions(+), 786 deletions(-) create mode 100644 arch/mips/cpu/Makefile create mode 100644 arch/mips/cpu/cpu.c create mode 100644 arch/mips/cpu/interrupts.c delete mode 100644 arch/mips/cpu/mips32/Makefile delete mode 100644 arch/mips/cpu/mips32/cpu.c delete mode 100644 arch/mips/cpu/mips32/interrupts.c delete mode 100644 arch/mips/cpu/mips32/start.S delete mode 100644 arch/mips/cpu/mips32/time.c delete mode 100644 arch/mips/cpu/mips64/Makefile delete mode 100644 arch/mips/cpu/mips64/cpu.c delete mode 100644 arch/mips/cpu/mips64/interrupts.c delete mode 100644 arch/mips/cpu/mips64/start.S delete mode 100644 arch/mips/cpu/mips64/time.c create mode 100644 arch/mips/cpu/start.S create mode 100644 arch/mips/cpu/time.c (limited to 'arch') diff --git a/arch/mips/Makefile b/arch/mips/Makefile index ff01bfe..43f0f5c 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -2,11 +2,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -head-$(CONFIG_CPU_MIPS32) := arch/mips/cpu/mips32/start.o -head-$(CONFIG_CPU_MIPS64) := arch/mips/cpu/mips64/start.o +head-y := arch/mips/cpu/start.o -libs-$(CONFIG_CPU_MIPS32) += arch/mips/cpu/mips32/ -libs-$(CONFIG_CPU_MIPS64) += arch/mips/cpu/mips64/ +libs-y += arch/mips/cpu/ libs-y += arch/mips/lib/ libs-$(CONFIG_SOC_AU1X00) += arch/mips/mach-au1x00/ diff --git a/arch/mips/cpu/Makefile b/arch/mips/cpu/Makefile new file mode 100644 index 0000000..fc6b455 --- /dev/null +++ b/arch/mips/cpu/Makefile @@ -0,0 +1,9 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# + +extra-y = start.o + +obj-y += time.o +obj-y += interrupts.o +obj-y += cpu.o diff --git a/arch/mips/cpu/cpu.c b/arch/mips/cpu/cpu.c new file mode 100644 index 0000000..8d3b2f5 --- /dev/null +++ b/arch/mips/cpu/cpu.c @@ -0,0 +1,38 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +void __weak _machine_restart(void) +{ + fprintf(stderr, "*** reset failed ***\n"); + + while (1) + /* NOP */; +} + +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + _machine_restart(); + + return 0; +} + +void write_one_tlb(int index, u32 pagemask, u32 hi, u32 low0, u32 low1) +{ + write_c0_entrylo0(low0); + write_c0_pagemask(pagemask); + write_c0_entrylo1(low1); + write_c0_entryhi(hi); + write_c0_index(index); + tlb_write_indexed(); +} diff --git a/arch/mips/cpu/interrupts.c b/arch/mips/cpu/interrupts.c new file mode 100644 index 0000000..275fcf5 --- /dev/null +++ b/arch/mips/cpu/interrupts.c @@ -0,0 +1,22 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include + +int interrupt_init(void) +{ + return 0; +} + +void enable_interrupts(void) +{ +} + +int disable_interrupts(void) +{ + return 0; +} diff --git a/arch/mips/cpu/mips32/Makefile b/arch/mips/cpu/mips32/Makefile deleted file mode 100644 index cb4db9c..0000000 --- a/arch/mips/cpu/mips32/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# (C) Copyright 2003-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -extra-y = start.o -obj-y = cpu.o interrupts.o time.o diff --git a/arch/mips/cpu/mips32/cpu.c b/arch/mips/cpu/mips32/cpu.c deleted file mode 100644 index 07deca8..0000000 --- a/arch/mips/cpu/mips32/cpu.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * (C) Copyright 2003 - * Wolfgang Denk, DENX Software Engineering, - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include - -void __attribute__((weak)) _machine_restart(void) -{ -} - -int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - _machine_restart(); - - fprintf(stderr, "*** reset failed ***\n"); - return 0; -} - -void write_one_tlb(int index, u32 pagemask, u32 hi, u32 low0, u32 low1) -{ - write_c0_entrylo0(low0); - write_c0_pagemask(pagemask); - write_c0_entrylo1(low1); - write_c0_entryhi(hi); - write_c0_index(index); - tlb_write_indexed(); -} diff --git a/arch/mips/cpu/mips32/interrupts.c b/arch/mips/cpu/mips32/interrupts.c deleted file mode 100644 index 275fcf5..0000000 --- a/arch/mips/cpu/mips32/interrupts.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * (C) Copyright 2003 - * Wolfgang Denk, DENX Software Engineering, - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include - -int interrupt_init(void) -{ - return 0; -} - -void enable_interrupts(void) -{ -} - -int disable_interrupts(void) -{ - return 0; -} diff --git a/arch/mips/cpu/mips32/start.S b/arch/mips/cpu/mips32/start.S deleted file mode 100644 index 3b5b622..0000000 --- a/arch/mips/cpu/mips32/start.S +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Startup Code for MIPS32 CPU-core - * - * Copyright (c) 2003 Wolfgang Denk - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include - -#ifndef CONFIG_SYS_MIPS_CACHE_MODE -#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT -#endif - -#ifndef CONFIG_SYS_INIT_SP_ADDR -#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + \ - CONFIG_SYS_INIT_SP_OFFSET) -#endif - -#ifdef CONFIG_32BIT -# define MIPS_RELOC 3 -# define STATUS_SET 0 -#endif - -#ifdef CONFIG_64BIT -# ifdef CONFIG_SYS_LITTLE_ENDIAN -# define MIPS64_R_INFO(ssym, r_type3, r_type2, r_type) \ - (((r_type) << 24) | ((r_type2) << 16) | ((r_type3) << 8) | (ssym)) -# else -# define MIPS64_R_INFO(ssym, r_type3, r_type2, r_type) \ - ((r_type) | ((r_type2) << 8) | ((r_type3) << 16) | (ssym) << 24) -# endif -# define MIPS_RELOC MIPS64_R_INFO(0x00, 0x00, 0x12, 0x03) -# define STATUS_SET ST0_KX -#endif - - /* - * For the moment disable interrupts, mark the kernel mode and - * set ST0_KX so that the CPU does not spit fire when using - * 64-bit addresses. - */ - .macro setup_c0_status set clr - .set push - mfc0 t0, CP0_STATUS - or t0, ST0_CU0 | \set | 0x1f | \clr - xor t0, 0x1f | \clr - mtc0 t0, CP0_STATUS - .set noreorder - sll zero, 3 # ehb - .set pop - .endm - - .set noreorder - - .globl _start - .text -_start: - /* U-boot entry point */ - b reset - nop - - .org 0x10 -#if defined(CONFIG_SYS_XWAY_EBU_BOOTCFG) - /* - * Almost all Lantiq XWAY SoC devices have an external bus unit (EBU) to - * access external NOR flashes. If the board boots from NOR flash the - * internal BootROM does a blind read at address 0xB0000010 to read the - * initial configuration for that EBU in order to access the flash - * device with correct parameters. This config option is board-specific. - */ - .word CONFIG_SYS_XWAY_EBU_BOOTCFG - .word 0x0 -#elif defined(CONFIG_MALTA) - /* - * Linux expects the Board ID here. - */ - .word 0x00000420 # 0x420 (Malta Board with CoreLV) - .word 0x00000000 -#endif - - .org 0x200 - /* TLB refill, 32 bit task */ -1: b 1b - nop - - .org 0x280 - /* XTLB refill, 64 bit task */ -1: b 1b - nop - - .org 0x300 - /* Cache error exception */ -1: b 1b - nop - - .org 0x380 - /* General exception */ -1: b 1b - nop - - .org 0x400 - /* Catch interrupt exceptions */ -1: b 1b - nop - - .org 0x480 - /* EJTAG debug exception */ -1: b 1b - nop - - .align 4 -reset: - - /* Clear watch registers */ - MTC0 zero, CP0_WATCHLO - MTC0 zero, CP0_WATCHHI - - /* WP(Watch Pending), SW0/1 should be cleared */ - mtc0 zero, CP0_CAUSE - - setup_c0_status STATUS_SET 0 - - /* Init Timer */ - mtc0 zero, CP0_COUNT - mtc0 zero, CP0_COMPARE - -#ifndef CONFIG_SKIP_LOWLEVEL_INIT - /* CONFIG0 register */ - li t0, CONF_CM_UNCACHED - mtc0 t0, CP0_CONFIG -#endif - - /* - * Initialize $gp, force pointer sized alignment of bal instruction to - * forbid the compiler to put nop's between bal and _gp. This is - * required to keep _gp and ra aligned to 8 byte. - */ - .align PTRLOG - bal 1f - nop - PTR _gp -1: - PTR_L gp, 0(ra) - -#ifndef CONFIG_SKIP_LOWLEVEL_INIT - /* Initialize any external memory */ - PTR_LA t9, lowlevel_init - jalr t9 - nop - - /* Initialize caches... */ - PTR_LA t9, mips_cache_reset - jalr t9 - nop - - /* ... and enable them */ - li t0, CONFIG_SYS_MIPS_CACHE_MODE - mtc0 t0, CP0_CONFIG -#endif - - /* Set up temporary stack */ - PTR_LI t0, -16 - PTR_LI t1, CONFIG_SYS_INIT_SP_ADDR - and sp, t1, t0 # force 16 byte alignment - PTR_SUB sp, sp, GD_SIZE # reserve space for gd - and sp, sp, t0 # force 16 byte alignment - move k0, sp # save gd pointer -#ifdef CONFIG_SYS_MALLOC_F_LEN - PTR_LI t2, CONFIG_SYS_MALLOC_F_LEN - PTR_SUB sp, sp, t2 # reserve space for early malloc - and sp, sp, t0 # force 16 byte alignment -#endif - move fp, sp - - /* Clear gd */ - move t0, k0 -1: - sw zero, 0(t0) - blt t0, t1, 1b - PTR_ADDI t0, 4 - -#ifdef CONFIG_SYS_MALLOC_F_LEN - PTR_ADDU t0, k0, GD_MALLOC_BASE # gd->malloc_base offset - sw sp, 0(t0) -#endif - - PTR_LA t9, board_init_f - jr t9 - move ra, zero - -/* - * void relocate_code (addr_sp, gd, addr_moni) - * - * This "function" does not return, instead it continues in RAM - * after relocating the monitor code. - * - * a0 = addr_sp - * a1 = gd - * a2 = destination address - */ - .globl relocate_code - .ent relocate_code -relocate_code: - move sp, a0 # set new stack pointer - move fp, sp - - move s0, a1 # save gd in s0 - move s2, a2 # save destination address in s2 - - PTR_LI t0, CONFIG_SYS_MONITOR_BASE - PTR_SUB s1, s2, t0 # s1 <-- relocation offset - - PTR_LA t3, in_ram - PTR_L t2, -(3 * PTRSIZE)(t3) # t2 <-- __image_copy_end - move t1, a2 - - PTR_ADD gp, s1 # adjust gp - - /* - * t0 = source address - * t1 = target address - * t2 = source end address - */ -1: - lw t3, 0(t0) - sw t3, 0(t1) - PTR_ADDU t0, 4 - blt t0, t2, 1b - PTR_ADDU t1, 4 - - /* If caches were enabled, we would have to flush them here. */ - PTR_SUB a1, t1, s2 # a1 <-- size - PTR_LA t9, flush_cache - jalr t9 - move a0, s2 # a0 <-- destination address - - /* Jump to where we've relocated ourselves */ - PTR_ADDI t0, s2, in_ram - _start - jr t0 - nop - - PTR __rel_dyn_end - PTR __rel_dyn_start - PTR __image_copy_end - PTR _GLOBAL_OFFSET_TABLE_ - PTR num_got_entries - -in_ram: - /* - * Now we want to update GOT. - * - * GOT[0] is reserved. GOT[1] is also reserved for the dynamic object - * generated by GNU ld. Skip these reserved entries from relocation. - */ - PTR_L t3, -(1 * PTRSIZE)(t0) # t3 <-- num_got_entries - PTR_L t8, -(2 * PTRSIZE)(t0) # t8 <-- _GLOBAL_OFFSET_TABLE_ - PTR_ADD t8, s1 # t8 now holds relocated _G_O_T_ - PTR_ADDI t8, t8, 2 * PTRSIZE # skipping first two entries - PTR_LI t2, 2 -1: - PTR_L t1, 0(t8) - beqz t1, 2f - PTR_ADD t1, s1 - PTR_S t1, 0(t8) -2: - PTR_ADDI t2, 1 - blt t2, t3, 1b - PTR_ADDI t8, PTRSIZE - - /* Update dynamic relocations */ - PTR_L t1, -(4 * PTRSIZE)(t0) # t1 <-- __rel_dyn_start - PTR_L t2, -(5 * PTRSIZE)(t0) # t2 <-- __rel_dyn_end - - b 2f # skip first reserved entry - PTR_ADDI t1, 2 * PTRSIZE - -1: - lw t8, -4(t1) # t8 <-- relocation info - - PTR_LI t3, MIPS_RELOC - bne t8, t3, 2f # skip non-MIPS_RELOC entries - nop - - PTR_L t3, -(2 * PTRSIZE)(t1) # t3 <-- location to fix up in FLASH - - PTR_L t8, 0(t3) # t8 <-- original pointer - PTR_ADD t8, s1 # t8 <-- adjusted pointer - - PTR_ADD t3, s1 # t3 <-- location to fix up in RAM - PTR_S t8, 0(t3) - -2: - blt t1, t2, 1b - PTR_ADDI t1, 2 * PTRSIZE # each rel.dyn entry is 2*PTRSIZE bytes - - /* - * Clear BSS - * - * GOT is now relocated. Thus __bss_start and __bss_end can be - * accessed directly via $gp. - */ - PTR_LA t1, __bss_start # t1 <-- __bss_start - PTR_LA t2, __bss_end # t2 <-- __bss_end - -1: - PTR_S zero, 0(t1) - blt t1, t2, 1b - PTR_ADDI t1, PTRSIZE - - move a0, s0 # a0 <-- gd - move a1, s2 - PTR_LA t9, board_init_r - jr t9 - move ra, zero - - .end relocate_code diff --git a/arch/mips/cpu/mips32/time.c b/arch/mips/cpu/mips32/time.c deleted file mode 100644 index 553da5f..0000000 --- a/arch/mips/cpu/mips32/time.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * (C) Copyright 2003 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include - -unsigned long notrace timer_read_counter(void) -{ - return read_c0_count(); -} - -ulong notrace get_tbclk(void) -{ - return CONFIG_SYS_MIPS_TIMER_FREQ; -} diff --git a/arch/mips/cpu/mips64/Makefile b/arch/mips/cpu/mips64/Makefile deleted file mode 100644 index cb4db9c..0000000 --- a/arch/mips/cpu/mips64/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# (C) Copyright 2003-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -extra-y = start.o -obj-y = cpu.o interrupts.o time.o diff --git a/arch/mips/cpu/mips64/cpu.c b/arch/mips/cpu/mips64/cpu.c deleted file mode 100644 index 1d32705..0000000 --- a/arch/mips/cpu/mips64/cpu.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * (C) Copyright 2003 - * Wolfgang Denk, DENX Software Engineering, - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include - -void __attribute__((weak)) _machine_restart(void) -{ - fprintf(stderr, "*** reset failed ***\n"); - - while (1) - /* NOP */; -} - -int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - _machine_restart(); - - return 0; -} - -void write_one_tlb(int index, u32 pagemask, u32 hi, u32 low0, u32 low1) -{ - write_c0_entrylo0(low0); - write_c0_pagemask(pagemask); - write_c0_entrylo1(low1); - write_c0_entryhi(hi); - write_c0_index(index); - tlb_write_indexed(); -} diff --git a/arch/mips/cpu/mips64/interrupts.c b/arch/mips/cpu/mips64/interrupts.c deleted file mode 100644 index 275fcf5..0000000 --- a/arch/mips/cpu/mips64/interrupts.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * (C) Copyright 2003 - * Wolfgang Denk, DENX Software Engineering, - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include - -int interrupt_init(void) -{ - return 0; -} - -void enable_interrupts(void) -{ -} - -int disable_interrupts(void) -{ - return 0; -} diff --git a/arch/mips/cpu/mips64/start.S b/arch/mips/cpu/mips64/start.S deleted file mode 100644 index 471bc1e..0000000 --- a/arch/mips/cpu/mips64/start.S +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Startup Code for MIPS64 CPU-core - * - * Copyright (c) 2003 Wolfgang Denk - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include - -#ifndef CONFIG_SYS_MIPS_CACHE_MODE -#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT -#endif - -#ifndef CONFIG_SYS_INIT_SP_ADDR -#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + \ - CONFIG_SYS_INIT_SP_OFFSET) -#endif - -#ifdef CONFIG_SYS_LITTLE_ENDIAN -#define MIPS64_R_INFO(ssym, r_type3, r_type2, r_type) \ - (((r_type) << 24) | ((r_type2) << 16) | ((r_type3) << 8) | (ssym)) -#else -#define MIPS64_R_INFO(ssym, r_type3, r_type2, r_type) \ - ((r_type) | ((r_type2) << 8) | ((r_type3) << 16) | (ssym) << 24) -#endif - - /* - * For the moment disable interrupts, mark the kernel mode and - * set ST0_KX so that the CPU does not spit fire when using - * 64-bit addresses. - */ - .macro setup_c0_status set clr - .set push - mfc0 t0, CP0_STATUS - or t0, ST0_CU0 | \set | 0x1f | \clr - xor t0, 0x1f | \clr - mtc0 t0, CP0_STATUS - .set noreorder - sll zero, 3 # ehb - .set pop - .endm - - .set noreorder - - .globl _start - .text -_start: - /* U-boot entry point */ - b reset - nop - - .org 0x200 - /* TLB refill, 32 bit task */ -1: b 1b - nop - - .org 0x280 - /* XTLB refill, 64 bit task */ -1: b 1b - nop - - .org 0x300 - /* Cache error exception */ -1: b 1b - nop - - .org 0x380 - /* General exception */ -1: b 1b - nop - - .org 0x400 - /* Catch interrupt exceptions */ -1: b 1b - nop - - .org 0x480 - /* EJTAG debug exception */ -1: b 1b - nop - - .align 4 -reset: - - /* Clear watch registers */ - dmtc0 zero, CP0_WATCHLO - dmtc0 zero, CP0_WATCHHI - - /* WP(Watch Pending), SW0/1 should be cleared */ - mtc0 zero, CP0_CAUSE - - setup_c0_status ST0_KX 0 - - /* Init Timer */ - mtc0 zero, CP0_COUNT - mtc0 zero, CP0_COMPARE - -#ifndef CONFIG_SKIP_LOWLEVEL_INIT - /* CONFIG0 register */ - dli t0, CONF_CM_UNCACHED - mtc0 t0, CP0_CONFIG -#endif - - /* - * Initialize $gp, force 8 byte alignment of bal instruction to forbid - * the compiler to put nop's between bal and _gp. This is required to - * keep _gp and ra aligned to 8 byte. - */ - .align 3 - bal 1f - nop - .dword _gp -1: - ld gp, 0(ra) - -#ifndef CONFIG_SKIP_LOWLEVEL_INIT - /* Initialize any external memory */ - dla t9, lowlevel_init - jalr t9 - nop - - /* Initialize caches... */ - dla t9, mips_cache_reset - jalr t9 - nop - - /* ... and enable them */ - dli t0, CONFIG_SYS_MIPS_CACHE_MODE - mtc0 t0, CP0_CONFIG -#endif - - /* Set up temporary stack */ - dli t0, -16 - dli t1, CONFIG_SYS_INIT_SP_ADDR - and sp, t1, t0 # force 16 byte alignment - dsub sp, sp, GD_SIZE # reserve space for gd - and sp, sp, t0 # force 16 byte alignment - move k0, sp # save gd pointer -#ifdef CONFIG_SYS_MALLOC_F_LEN - dli t2, CONFIG_SYS_MALLOC_F_LEN - dsub sp, sp, t2 # reserve space for early malloc - and sp, sp, t0 # force 16 byte alignment -#endif - move fp, sp - - /* Clear gd */ - move t0, k0 -1: - sw zero, 0(t0) - blt t0, t1, 1b - daddi t0, 4 - -#ifdef CONFIG_SYS_MALLOC_F_LEN - daddu t0, k0, GD_MALLOC_BASE # gd->malloc_base offset - sw sp, 0(t0) -#endif - - dla t9, board_init_f - jr t9 - move ra, zero - -/* - * void relocate_code (addr_sp, gd, addr_moni) - * - * This "function" does not return, instead it continues in RAM - * after relocating the monitor code. - * - * a0 = addr_sp - * a1 = gd - * a2 = destination address - */ - .globl relocate_code - .ent relocate_code -relocate_code: - move sp, a0 # set new stack pointer - move fp, sp - - move s0, a1 # save gd in s0 - move s2, a2 # save destination address in s2 - - dli t0, CONFIG_SYS_MONITOR_BASE - dsub s1, s2, t0 # s1 <-- relocation offset - - dla t3, in_ram - ld t2, -24(t3) # t2 <-- __image_copy_end - move t1, a2 - - dadd gp, s1 # adjust gp - - /* - * t0 = source address - * t1 = target address - * t2 = source end address - */ -1: - lw t3, 0(t0) - sw t3, 0(t1) - daddu t0, 4 - blt t0, t2, 1b - daddu t1, 4 - - /* If caches were enabled, we would have to flush them here. */ - dsub a1, t1, s2 # a1 <-- size - dla t9, flush_cache - jalr t9 - move a0, s2 # a0 <-- destination address - - /* Jump to where we've relocated ourselves */ - daddi t0, s2, in_ram - _start - jr t0 - nop - - .dword __rel_dyn_end - .dword __rel_dyn_start - .dword __image_copy_end - .dword _GLOBAL_OFFSET_TABLE_ - .dword num_got_entries - -in_ram: - /* - * Now we want to update GOT. - * - * GOT[0] is reserved. GOT[1] is also reserved for the dynamic object - * generated by GNU ld. Skip these reserved entries from relocation. - */ - ld t3, -8(t0) # t3 <-- num_got_entries - ld t8, -16(t0) # t8 <-- _GLOBAL_OFFSET_TABLE_ - dadd t8, s1 # t8 now holds relocated _G_O_T_ - daddi t8, t8, 16 # skipping first two entries - dli t2, 2 -1: - ld t1, 0(t8) - beqz t1, 2f - dadd t1, s1 - sd t1, 0(t8) -2: - daddi t2, 1 - blt t2, t3, 1b - daddi t8, 8 - - /* Update dynamic relocations */ - ld t1, -32(t0) # t1 <-- __rel_dyn_start - ld t2, -40(t0) # t2 <-- __rel_dyn_end - - b 2f # skip first reserved entry - daddi t1, 16 - -1: - lw t8, -4(t1) # t8 <-- relocation info - - dli t3, MIPS64_R_INFO(0x00, 0x00, 0x12, 0x03) - bne t8, t3, 2f # skip non R_MIPS_REL32 entries - nop - - ld t3, -16(t1) # t3 <-- location to fix up in FLASH - - ld t8, 0(t3) # t8 <-- original pointer - dadd t8, s1 # t8 <-- adjusted pointer - - dadd t3, s1 # t3 <-- location to fix up in RAM - sd t8, 0(t3) - -2: - blt t1, t2, 1b - daddi t1, 16 # each rel.dyn entry is 16 bytes - - /* - * Clear BSS - * - * GOT is now relocated. Thus __bss_start and __bss_end can be - * accessed directly via $gp. - */ - dla t1, __bss_start # t1 <-- __bss_start - dla t2, __bss_end # t2 <-- __bss_end - -1: - sd zero, 0(t1) - blt t1, t2, 1b - daddi t1, 8 - - move a0, s0 # a0 <-- gd - move a1, s2 - dla t9, board_init_r - jr t9 - move ra, zero - - .end relocate_code diff --git a/arch/mips/cpu/mips64/time.c b/arch/mips/cpu/mips64/time.c deleted file mode 100644 index 553da5f..0000000 --- a/arch/mips/cpu/mips64/time.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * (C) Copyright 2003 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include - -unsigned long notrace timer_read_counter(void) -{ - return read_c0_count(); -} - -ulong notrace get_tbclk(void) -{ - return CONFIG_SYS_MIPS_TIMER_FREQ; -} diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S new file mode 100644 index 0000000..3b5b622 --- /dev/null +++ b/arch/mips/cpu/start.S @@ -0,0 +1,320 @@ +/* + * Startup Code for MIPS32 CPU-core + * + * Copyright (c) 2003 Wolfgang Denk + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +#ifndef CONFIG_SYS_MIPS_CACHE_MODE +#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT +#endif + +#ifndef CONFIG_SYS_INIT_SP_ADDR +#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + \ + CONFIG_SYS_INIT_SP_OFFSET) +#endif + +#ifdef CONFIG_32BIT +# define MIPS_RELOC 3 +# define STATUS_SET 0 +#endif + +#ifdef CONFIG_64BIT +# ifdef CONFIG_SYS_LITTLE_ENDIAN +# define MIPS64_R_INFO(ssym, r_type3, r_type2, r_type) \ + (((r_type) << 24) | ((r_type2) << 16) | ((r_type3) << 8) | (ssym)) +# else +# define MIPS64_R_INFO(ssym, r_type3, r_type2, r_type) \ + ((r_type) | ((r_type2) << 8) | ((r_type3) << 16) | (ssym) << 24) +# endif +# define MIPS_RELOC MIPS64_R_INFO(0x00, 0x00, 0x12, 0x03) +# define STATUS_SET ST0_KX +#endif + + /* + * For the moment disable interrupts, mark the kernel mode and + * set ST0_KX so that the CPU does not spit fire when using + * 64-bit addresses. + */ + .macro setup_c0_status set clr + .set push + mfc0 t0, CP0_STATUS + or t0, ST0_CU0 | \set | 0x1f | \clr + xor t0, 0x1f | \clr + mtc0 t0, CP0_STATUS + .set noreorder + sll zero, 3 # ehb + .set pop + .endm + + .set noreorder + + .globl _start + .text +_start: + /* U-boot entry point */ + b reset + nop + + .org 0x10 +#if defined(CONFIG_SYS_XWAY_EBU_BOOTCFG) + /* + * Almost all Lantiq XWAY SoC devices have an external bus unit (EBU) to + * access external NOR flashes. If the board boots from NOR flash the + * internal BootROM does a blind read at address 0xB0000010 to read the + * initial configuration for that EBU in order to access the flash + * device with correct parameters. This config option is board-specific. + */ + .word CONFIG_SYS_XWAY_EBU_BOOTCFG + .word 0x0 +#elif defined(CONFIG_MALTA) + /* + * Linux expects the Board ID here. + */ + .word 0x00000420 # 0x420 (Malta Board with CoreLV) + .word 0x00000000 +#endif + + .org 0x200 + /* TLB refill, 32 bit task */ +1: b 1b + nop + + .org 0x280 + /* XTLB refill, 64 bit task */ +1: b 1b + nop + + .org 0x300 + /* Cache error exception */ +1: b 1b + nop + + .org 0x380 + /* General exception */ +1: b 1b + nop + + .org 0x400 + /* Catch interrupt exceptions */ +1: b 1b + nop + + .org 0x480 + /* EJTAG debug exception */ +1: b 1b + nop + + .align 4 +reset: + + /* Clear watch registers */ + MTC0 zero, CP0_WATCHLO + MTC0 zero, CP0_WATCHHI + + /* WP(Watch Pending), SW0/1 should be cleared */ + mtc0 zero, CP0_CAUSE + + setup_c0_status STATUS_SET 0 + + /* Init Timer */ + mtc0 zero, CP0_COUNT + mtc0 zero, CP0_COMPARE + +#ifndef CONFIG_SKIP_LOWLEVEL_INIT + /* CONFIG0 register */ + li t0, CONF_CM_UNCACHED + mtc0 t0, CP0_CONFIG +#endif + + /* + * Initialize $gp, force pointer sized alignment of bal instruction to + * forbid the compiler to put nop's between bal and _gp. This is + * required to keep _gp and ra aligned to 8 byte. + */ + .align PTRLOG + bal 1f + nop + PTR _gp +1: + PTR_L gp, 0(ra) + +#ifndef CONFIG_SKIP_LOWLEVEL_INIT + /* Initialize any external memory */ + PTR_LA t9, lowlevel_init + jalr t9 + nop + + /* Initialize caches... */ + PTR_LA t9, mips_cache_reset + jalr t9 + nop + + /* ... and enable them */ + li t0, CONFIG_SYS_MIPS_CACHE_MODE + mtc0 t0, CP0_CONFIG +#endif + + /* Set up temporary stack */ + PTR_LI t0, -16 + PTR_LI t1, CONFIG_SYS_INIT_SP_ADDR + and sp, t1, t0 # force 16 byte alignment + PTR_SUB sp, sp, GD_SIZE # reserve space for gd + and sp, sp, t0 # force 16 byte alignment + move k0, sp # save gd pointer +#ifdef CONFIG_SYS_MALLOC_F_LEN + PTR_LI t2, CONFIG_SYS_MALLOC_F_LEN + PTR_SUB sp, sp, t2 # reserve space for early malloc + and sp, sp, t0 # force 16 byte alignment +#endif + move fp, sp + + /* Clear gd */ + move t0, k0 +1: + sw zero, 0(t0) + blt t0, t1, 1b + PTR_ADDI t0, 4 + +#ifdef CONFIG_SYS_MALLOC_F_LEN + PTR_ADDU t0, k0, GD_MALLOC_BASE # gd->malloc_base offset + sw sp, 0(t0) +#endif + + PTR_LA t9, board_init_f + jr t9 + move ra, zero + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * a0 = addr_sp + * a1 = gd + * a2 = destination address + */ + .globl relocate_code + .ent relocate_code +relocate_code: + move sp, a0 # set new stack pointer + move fp, sp + + move s0, a1 # save gd in s0 + move s2, a2 # save destination address in s2 + + PTR_LI t0, CONFIG_SYS_MONITOR_BASE + PTR_SUB s1, s2, t0 # s1 <-- relocation offset + + PTR_LA t3, in_ram + PTR_L t2, -(3 * PTRSIZE)(t3) # t2 <-- __image_copy_end + move t1, a2 + + PTR_ADD gp, s1 # adjust gp + + /* + * t0 = source address + * t1 = target address + * t2 = source end address + */ +1: + lw t3, 0(t0) + sw t3, 0(t1) + PTR_ADDU t0, 4 + blt t0, t2, 1b + PTR_ADDU t1, 4 + + /* If caches were enabled, we would have to flush them here. */ + PTR_SUB a1, t1, s2 # a1 <-- size + PTR_LA t9, flush_cache + jalr t9 + move a0, s2 # a0 <-- destination address + + /* Jump to where we've relocated ourselves */ + PTR_ADDI t0, s2, in_ram - _start + jr t0 + nop + + PTR __rel_dyn_end + PTR __rel_dyn_start + PTR __image_copy_end + PTR _GLOBAL_OFFSET_TABLE_ + PTR num_got_entries + +in_ram: + /* + * Now we want to update GOT. + * + * GOT[0] is reserved. GOT[1] is also reserved for the dynamic object + * generated by GNU ld. Skip these reserved entries from relocation. + */ + PTR_L t3, -(1 * PTRSIZE)(t0) # t3 <-- num_got_entries + PTR_L t8, -(2 * PTRSIZE)(t0) # t8 <-- _GLOBAL_OFFSET_TABLE_ + PTR_ADD t8, s1 # t8 now holds relocated _G_O_T_ + PTR_ADDI t8, t8, 2 * PTRSIZE # skipping first two entries + PTR_LI t2, 2 +1: + PTR_L t1, 0(t8) + beqz t1, 2f + PTR_ADD t1, s1 + PTR_S t1, 0(t8) +2: + PTR_ADDI t2, 1 + blt t2, t3, 1b + PTR_ADDI t8, PTRSIZE + + /* Update dynamic relocations */ + PTR_L t1, -(4 * PTRSIZE)(t0) # t1 <-- __rel_dyn_start + PTR_L t2, -(5 * PTRSIZE)(t0) # t2 <-- __rel_dyn_end + + b 2f # skip first reserved entry + PTR_ADDI t1, 2 * PTRSIZE + +1: + lw t8, -4(t1) # t8 <-- relocation info + + PTR_LI t3, MIPS_RELOC + bne t8, t3, 2f # skip non-MIPS_RELOC entries + nop + + PTR_L t3, -(2 * PTRSIZE)(t1) # t3 <-- location to fix up in FLASH + + PTR_L t8, 0(t3) # t8 <-- original pointer + PTR_ADD t8, s1 # t8 <-- adjusted pointer + + PTR_ADD t3, s1 # t3 <-- location to fix up in RAM + PTR_S t8, 0(t3) + +2: + blt t1, t2, 1b + PTR_ADDI t1, 2 * PTRSIZE # each rel.dyn entry is 2*PTRSIZE bytes + + /* + * Clear BSS + * + * GOT is now relocated. Thus __bss_start and __bss_end can be + * accessed directly via $gp. + */ + PTR_LA t1, __bss_start # t1 <-- __bss_start + PTR_LA t2, __bss_end # t2 <-- __bss_end + +1: + PTR_S zero, 0(t1) + blt t1, t2, 1b + PTR_ADDI t1, PTRSIZE + + move a0, s0 # a0 <-- gd + move a1, s2 + PTR_LA t9, board_init_r + jr t9 + move ra, zero + + .end relocate_code diff --git a/arch/mips/cpu/time.c b/arch/mips/cpu/time.c new file mode 100644 index 0000000..553da5f --- /dev/null +++ b/arch/mips/cpu/time.c @@ -0,0 +1,19 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +unsigned long notrace timer_read_counter(void) +{ + return read_c0_count(); +} + +ulong notrace get_tbclk(void) +{ + return CONFIG_SYS_MIPS_TIMER_FREQ; +} -- cgit v1.1