diff options
author | wbx <wbx@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2005-03-16 13:50:00 +0000 |
---|---|---|
committer | wbx <wbx@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2005-03-16 13:50:00 +0000 |
commit | 2cea1e6b9aa20af4040e87e88f9d4d2017cc2233 (patch) | |
tree | 5cd5a5f93b8b203d2343d5ade6e35ebd6b99f55d /openwrt/package/linux/kernel-source/arch/mips | |
parent | cfb6561e9bd4c5db6a6323525f0b5383e0649c5f (diff) |
add all source code from linksys/broadcom which is free, to cvs for better maintainence inside
openwrt. this gives us the ability to better support different hardware models, without changing
any external tar-balls. only et.o and wl.o is missing and is fetched from my webserver.
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@379 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'openwrt/package/linux/kernel-source/arch/mips')
25 files changed, 5124 insertions, 0 deletions
diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/Makefile b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/Makefile new file mode 100644 index 0000000000..eae8eba49c --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for Broadcom BCM947XX boards +# +# Copyright 2004, Broadcom Corporation +# All Rights Reserved. +# +# THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY +# KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM +# SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. +# +# $Id$ +# + +O_TARGET := bcm947xx.o + +export-objs := nvram_linux.o setup.o +obj-y := prom.o setup.o time.o sbmips.o sbpci.o pcibios.o perfcntr.o gpio.o +obj-y += sflash.o nvram.o nvram_linux.o + +vpath %.c $(SRCBASE)/shared $(SRCBASE)/shared/nvram + +include $(TOPDIR)/Rules.make diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/Makefile b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/Makefile new file mode 100644 index 0000000000..4004653268 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/Makefile @@ -0,0 +1,100 @@ +# +# Makefile for Broadcom BCM947XX boards +# +# Copyright 2001-2003, Broadcom Corporation +# All Rights Reserved. +# +# THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY +# KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM +# SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. +# +# $Id$ +# +# Copyright 2004 Manuel Novoa III <mjn3@codepoet.org> +# Modified to support bzip'd kernels. +# Of course, it would be better to integrate bunzip capability into CFE. +# + +# Link at 3 MB offset in RAM +#TEXT_START ?= 0x80300000 +TEXT_START ?= 0x80001000 +BZ_MEM_TOP := 0x81000000 +BZ_TEXT_START := BZ_MEM_TOP-0x4000 +BZ_STACK_TOP := BZ_TEXT_START-4 + +OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .reginfo -R .note -R .comment -R .mdebug -S + +SRCBASE := $(TOPDIR)/../.. +VPATH := $(SRCBASE)/shared +ASFLAGS += -D__ASSEMBLY__ -I$(SRCBASE)/include -DLOADADDR=$(LOADADDR) +ASFLAGS += -DBZ_MEM_TOP=$(BZ_MEM_TOP) +ASFLAGS += -DBZ_TEXT_START=$(BZ_TEXT_START) +ASFLAGS += -DBZ_STACK_TOP=$(BZ_STACK_TOP) +CFLAGS += -I$(SRCBASE)/include -DLOADADDR=$(LOADADDR) +CFLAGS += -DBZ_MEM_TOP=$(BZ_MEM_TOP) +CFLAGS += -DBZ_TEXT_START=$(BZ_TEXT_START) +CFLAGS += -DBZ_STACK_TOP=$(BZ_STACK_TOP) +ifdef CONFIG_MCOUNT +CFLAGS := $(subst -pg,,$(CFLAGS)) +endif +CFLAGS += -ffunction-sections $(call check_gcc, -fvtable-gc, ) +SEDFLAGS := s/BZ_TEXT_START/$(BZ_TEXT_START)/;s/BZ_MEM_TOP/$(BZ_MEM_TOP)/;s/TEXT_START/$(TEXT_START)/ + +SYSTEM ?= $(TOPDIR)/vmlinux +OBJECTS := head.o data.o + +all: bzImage vmlinuz + +# Don't build dependencies, this may die if $(CC) isn't gcc +dep: + +# Create a gzipped version named vmlinuz for compatibility +vmlinuz: piggy + gzip -c9 $< > $@ + +# Our bzImage is a gzip'd binary that decompresses and runs +# the appended bzip'd kernel. +bzImage: bzLoaderImage.gz piggz + cat bzLoaderImage.gz piggz > $@ + +bzLoaderImage.gz: bzLoaderImage + gzip -nc9 $< > $@ + +bzLoaderImage: bzLoader + $(OBJCOPY) $< $@ + +bzLoader: vmlinux.lds $(OBJECTS) + $(LD) -static --gc-sections -no-warn-mismatch -T vmlinux.lds -o $@ $(OBJECTS) + +vmlinux.lds: vmlinux.lds.in Makefile + @sed "$(SEDFLAGS)" < $< > $@ + +piggz: piggy + bzip2 -c9 $< > $@ + +piggy: $(SYSTEM) + $(OBJCOPY) $< $@ + +data.o: data.lds data.image + $(LD) -no-warn-mismatch -T data.lds -r -o $@ -b binary data.image -b elf32-tradlittlemips + +data.lds: + @echo "SECTIONS { .data : { code_start = .; *(.data) code_stop = .; }}" > $@ + +data.image: decompress_bunzip2.image + $(OBJCOPY) $< $@ + +decompress_bunzip2.image: decompress_bunzip2.lds decompress_bunzip2.o + $(LD) -static --gc-sections -no-warn-mismatch -T decompress_bunzip2.lds -o $@ decompress_bunzip2.o + +decompress_bunzip2.lds: decompress_bunzip2.lds.in Makefile + @sed "$(SEDFLAGS)" < $< > $@ + +mrproper: clean + +clean: + rm -f vmlinux vmlinuz piggz piggy *.lds *.o \ + bzLoader bzLoaderImage bzLoaderImage.gz bzImage \ + data.lds data.image \ + decompress_bunzip2.lds decompress_bunzip2.image diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/README.mjn3 b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/README.mjn3 new file mode 100644 index 0000000000..0e93e55f90 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/README.mjn3 @@ -0,0 +1,20 @@ + +FIRST... See the dedication in the decompress_bunzip2.c file as it applies +here too. Donations to hospice in Toni's memory would be appreciated. + +As far as the code goes... the bzImage is just a bzip'd kernel image with +a small gzip'd decompressor/loader stuck on front. CFE ungzip's the loader +app which then relocates the bunzip decompressor into higher memory and +bunzip's the compressed kernel directly from flash. Then the instruction +cache is flushed (to remove traces of the loader) and the kernel is executed. + +Of course, a better approach would be to add bunzip decompression to CFE. + +Notes... + 1) Instruction cache size and linesize are hardcoded (see the #warning). + 2) Currently assumes at least 16M or ram. + 3) Thanks to Mike Baker mbm at alt dot org for bouncing ideas back + and forth as well as diagnosing the last (icache) bug. + +Manuel Novoa III <mjn3@codepoet.org> May 30, 2004 + diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/data.lds b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/data.lds new file mode 100644 index 0000000000..ec48b2dfdf --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/data.lds @@ -0,0 +1 @@ +SECTIONS { .data : { code_start = .; *(.data) code_stop = .; }} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/decompress_bunzip2.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/decompress_bunzip2.c new file mode 100644 index 0000000000..9a120796ff --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/decompress_bunzip2.c @@ -0,0 +1,758 @@ +/* vi: set sw=4 ts=4: */ +/* Small bzip2 deflate implementation, by Rob Landley (rob@landley.net). + + Based on bzip2 decompression code by Julian R Seward (jseward@acm.org), + which also acknowledges contributions by Mike Burrows, David Wheeler, + Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten, + Robert Sedgewick, and Jon L. Bentley. + + This code is licensed under the LGPLv2: + LGPL (http://www.gnu.org/copyleft/lgpl.html +*/ + +/* + Size and speed optimizations by Manuel Novoa III (mjn3@codepoet.org). + + More efficient reading of huffman codes, a streamlined read_bunzip() + function, and various other tweaks. In (limited) tests, approximately + 20% faster than bzcat on x86 and about 10% faster on arm. + + Note that about 2/3 of the time is spent in read_unzip() reversing + the Burrows-Wheeler transformation. Much of that time is delay + resulting from cache misses. + + I would ask that anyone benefiting from this work, especially those + using it in commercial products, consider making a donation to my local + non-profit hospice organization in the name of the woman I loved, who + passed away Feb. 12, 2003. + + In memory of Toni W. Hagan + + Hospice of Acadiana, Inc. + 2600 Johnston St., Suite 200 + Lafayette, LA 70503-3240 + + Phone (337) 232-1234 or 1-800-738-2226 + Fax (337) 232-1297 + + http://www.hospiceacadiana.com/ + + Manuel + */ + +/* May 21, 2004 Manuel Novoa III + * Modified to load a bzip'd kernel on the linksys wrt54g. + * + * May 30, 2004 + * Further size reduction via inlining and disabling len check code. + */ + +/**********************************************************************/ + +/* Note... the LED code is specific to the v2.0 (and GS?) unit. */ +#undef ENABLE_LEDS +/* #define ENABLE_LEDS 1 */ + +/* Do we want to bother with checking the bzip'd data for errors? */ +#undef ENABLE_BUNZIP_CHECKING +/* #define ENABLE_BUNZIP_CHECKING 1 */ + +/**********************************************************************/ +/* #include <bcm4710.h> */ +#define BCM4710_FLASH 0x1fc00000 /* Flash */ + +#define KSEG0 0x80000000 +#define KSEG1 0xa0000000 + +#define KSEG1ADDR(a) ((((unsigned)(a)) & 0x1fffffffU) | KSEG1) + +/* The following cache code was taken from the file bcm4710_cache.h + * which was necessarily GPL as it was used to build the linksys + * kernel for the wrt54g. */ + +#warning icache [l]size hardcoded + +#define icache_size 8192 +#define ic_lsize 16 + +#define Index_Invalidate_I 0x00 + +#define cache_unroll(base,op) \ + __asm__ __volatile__( \ + ".set noreorder;\n" \ + ".set mips3;\n" \ + "cache %1, (%0);\n" \ + ".set mips0;\n" \ + ".set reorder\n" \ + : \ + : "r" (base), \ + "i" (op)); + +static __inline__ void blast_icache(void) +{ + unsigned long start = KSEG0; + unsigned long end = (start + icache_size); + + while(start < end) { + cache_unroll(start,Index_Invalidate_I); + start += ic_lsize; + } +} + +/**********************************************************************/ +#ifndef INT_MAX +#define INT_MAX (((1 << 30)-1)*2 + 1) +#endif +/**********************************************************************/ +#ifdef ENABLE_BUNZIP_CHECKING + +#define REBOOT do {} while (1) + +#else + +#define REBOOT ((void) 0) + +#endif +/**********************************************************************/ +#ifdef ENABLE_LEDS + +#define LED_POWER_ON 0x02 /* OFF == flashing */ +#define LED_DMZ_OFF 0x80 +#define LED_WLAN_OFF 0x01 + +#define LED_CODE_0 (LED_POWER_ON | LED_DMZ_OFF | LED_WLAN_OFF) +#define LED_CODE_1 (LED_POWER_ON | LED_DMZ_OFF) +#define LED_CODE_2 (LED_POWER_ON | LED_WLAN_OFF) +#define LED_CODE_3 (LED_POWER_ON) + +#define SET_LED_ERROR(X) \ + do { \ + *(volatile u8*)(KSEG1ADDR(0x18000064))=(X & ~LED_POWER_ON); \ + *(volatile u8*)(KSEG1ADDR(0x18000068))=0; /* Disable changes */ \ + REBOOT; \ + } while (0) + +#define SET_LED(X) *(volatile u8*)(KSEG1ADDR(0x18000064))=X; + + +typedef unsigned char u8; + +#else + +#define SET_LED_ERROR(X) REBOOT +#define SET_LED(X) ((void)0) + +#endif + +/**********************************************************************/ + +/* Constants for huffman coding */ +#define MAX_GROUPS 6 +#define GROUP_SIZE 50 /* 64 would have been more efficient */ +#define MAX_HUFCODE_BITS 20 /* Longest huffman code allowed */ +#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */ +#define SYMBOL_RUNA 0 +#define SYMBOL_RUNB 1 + +/* Status return values */ +#define RETVAL_OK 0 +#define RETVAL_LAST_BLOCK (-1) +#define RETVAL_NOT_BZIP_DATA (-2) +#define RETVAL_UNEXPECTED_INPUT_EOF (-3) +#define RETVAL_UNEXPECTED_OUTPUT_EOF (-4) +#define RETVAL_DATA_ERROR (-5) +#define RETVAL_OUT_OF_MEMORY (-6) +#define RETVAL_OBSOLETE_INPUT (-7) + +/* Other housekeeping constants */ +#define IOBUF_SIZE 4096 + +/* This is what we know about each huffman coding group */ +struct group_data { + /* We have an extra slot at the end of limit[] for a sentinal value. */ + int limit[MAX_HUFCODE_BITS+1],base[MAX_HUFCODE_BITS],permute[MAX_SYMBOLS]; + int minLen, maxLen; +}; + +/* Structure holding all the housekeeping data, including IO buffers and + memory that persists between calls to bunzip */ +typedef struct { + /* State for interrupting output loop */ + int writeCopies,writePos,writeRunCountdown,writeCount,writeCurrent; + /* I/O tracking data (file handles, buffers, positions, etc.) */ +#if defined(ENABLE_BUNZIP_CHECKING) + int /*in_fd,out_fd,*/ inbufCount,inbufPos /*,outbufPos*/; +#else + int /*in_fd,out_fd,inbufCount,*/ inbufPos /*,outbufPos*/; +#endif + unsigned char *inbuf /*,*outbuf*/; + unsigned int inbufBitCount, inbufBits; + /* The CRC values stored in the block header and calculated from the data */ +#ifdef ENABLE_BUNZIP_CHECKING + unsigned int crc32Table[256],headerCRC, totalCRC, writeCRC; + /* Intermediate buffer and its size (in bytes) */ + unsigned int *dbuf, dbufSize; +#else + unsigned int *dbuf; +#endif + /* These things are a bit too big to go on the stack */ + unsigned char selectors[32768]; /* nSelectors=15 bits */ + struct group_data groups[MAX_GROUPS]; /* huffman coding tables */ +} bunzip_data; + +static int get_next_block(bunzip_data *bd); + +/**********************************************************************/ +/* Undo burrows-wheeler transform on intermediate buffer to produce output. + If start_bunzip was initialized with out_fd=-1, then up to len bytes of + data are written to outbuf. Return value is number of bytes written or + error (all errors are negative numbers). If out_fd!=-1, outbuf and len + are ignored, data is written to out_fd and return is RETVAL_OK or error. +*/ + +static __inline__ int read_bunzip(bunzip_data *bd, char *outbuf, int len) +{ + const unsigned int *dbuf; + int pos,current,previous,gotcount; +#ifdef ENABLE_LEDS + int led_state = LED_CODE_2; +#endif + + /* If last read was short due to end of file, return last block now */ + if(bd->writeCount<0) return bd->writeCount; + + gotcount = 0; + dbuf=bd->dbuf; + pos=bd->writePos; + current=bd->writeCurrent; + + /* We will always have pending decoded data to write into the output + buffer unless this is the very first call (in which case we haven't + huffman-decoded a block into the intermediate buffer yet). */ + + if (bd->writeCopies) { + /* Inside the loop, writeCopies means extra copies (beyond 1) */ + --bd->writeCopies; + /* Loop outputting bytes */ + for(;;) { +#if 0 /* Might want to enable this if passing a limiting size. */ +/* #ifdef ENABLE_BUNZIP_CHECKING */ + /* If the output buffer is full, snapshot state and return */ + if(gotcount >= len) { + bd->writePos=pos; + bd->writeCurrent=current; + bd->writeCopies++; + return len; + } +#endif + /* Write next byte into output buffer, updating CRC */ + outbuf[gotcount++] = current; +#ifdef ENABLE_BUNZIP_CHECKING + bd->writeCRC=(((bd->writeCRC)<<8) + ^bd->crc32Table[((bd->writeCRC)>>24)^current]); +#endif + /* Loop now if we're outputting multiple copies of this byte */ + if (bd->writeCopies) { + --bd->writeCopies; + continue; + } +decode_next_byte: + if (!bd->writeCount--) break; + /* Follow sequence vector to undo Burrows-Wheeler transform */ + previous=current; + pos=dbuf[pos]; + current=pos&0xff; + pos>>=8; + /* After 3 consecutive copies of the same byte, the 4th is a repeat + count. We count down from 4 instead + * of counting up because testing for non-zero is faster */ + if(--bd->writeRunCountdown) { + if(current!=previous) bd->writeRunCountdown=4; + } else { + /* We have a repeated run, this byte indicates the count */ + bd->writeCopies=current; + current=previous; + bd->writeRunCountdown=5; + /* Sometimes there are just 3 bytes (run length 0) */ + if(!bd->writeCopies) goto decode_next_byte; + /* Subtract the 1 copy we'd output anyway to get extras */ + --bd->writeCopies; + } + } +#ifdef ENABLE_BUNZIP_CHECKING + /* Decompression of this block completed successfully */ + bd->writeCRC=~bd->writeCRC; + bd->totalCRC=((bd->totalCRC<<1) | (bd->totalCRC>>31)) ^ bd->writeCRC; + /* If this block had a CRC error, force file level CRC error. */ + if(bd->writeCRC!=bd->headerCRC) { + bd->totalCRC=bd->headerCRC+1; + return RETVAL_LAST_BLOCK; + } +#endif + } + +#ifdef ENABLE_LEDS + if (led_state == LED_CODE_2) { + led_state = LED_CODE_1; + } else { + led_state = LED_CODE_2; + } + SET_LED(led_state); +#endif + + /* Refill the intermediate buffer by huffman-decoding next block of input */ + /* (previous is just a convenient unused temp variable here) */ + previous=get_next_block(bd); +#ifdef ENABLE_BUNZIP_CHECKING + if(previous) { + bd->writeCount=previous; + return (previous!=RETVAL_LAST_BLOCK) ? previous : gotcount; + } + bd->writeCRC=0xffffffffUL; +#else + if (previous) return gotcount; +#endif + pos=bd->writePos; + current=bd->writeCurrent; + goto decode_next_byte; +} + +/**********************************************************************/ +/* WARNING!!! Must be the first function!!! */ + +void load_and_run(unsigned long ra) +{ + int dbuf[900000]; /* Maximum requred */ + bunzip_data bd; + + unsigned int i; +#ifdef ENABLE_BUNZIP_CHECKING + unsigned int j, c; + int r; +#endif + char *p; + +#ifdef ENABLE_LEDS + *(volatile u8*)(KSEG1ADDR(0x18000068))=0x83; /* Allow all bits to change */ + SET_LED(LED_CODE_0); +#endif + +/* memset(&bd,0,sizeof(bunzip_data)); */ + p = (char *) &bd; + for (i = 0 ; i < sizeof(bunzip_data) ; i++) { + p[i] = 0; + } + + /* Find start of flash and adjust for pmon partition. */ + p = ((char *) KSEG1ADDR(BCM4710_FLASH)) + 0x10000; + + SET_LED(LED_CODE_1); + /* Find the start of the bzip'd data. */ + while ((p[0]!='B') || (p[1]!='Z') || (p[2]!='h') /*|| (p[3]!='9')*/) ++p; + SET_LED(LED_CODE_2); + + /* Setup input buffer */ + bd.inbuf=p+4; /* Skip the "BZh9" header. */ +#ifdef ENABLE_BUNZIP_CHECKING + bd.inbufCount=INT_MAX; + /* Init the CRC32 table (big endian) */ + for(i=0;i<256;i++) { + c=i<<24; + for(j=8;j;j--) + c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1); + bd.crc32Table[i]=c; + } + + bd.dbufSize=900000; +#endif + bd.dbuf=dbuf; + + /* Actually do the bunzip */ +#ifdef ENABLE_BUNZIP_CHECKING + r = read_bunzip(&bd, ((char *) LOADADDR), INT_MAX); + if (r > 0) { + if (bd.headerCRC==bd.totalCRC) { + SET_LED(LED_CODE_3); + { + int code = LED_WLAN_OFF; + int i, j; + for (j=0 ; j < 4 ; j++) { + for (i=0; i<(1<<27) ; i++) {} + SET_LED(code); + code ^= LED_WLAN_OFF; + } + } + blast_icache(); + /* Jump to load address */ + ((void (*)(void)) LOADADDR)(); + } else { + SET_LED_ERROR(LED_CODE_3); + } + } else { + SET_LED_ERROR(LED_CODE_2); + } +#else + read_bunzip(&bd, ((char *) LOADADDR), INT_MAX); + blast_icache(); + /* Jump to load address */ + ((void (*)(void)) LOADADDR)(); +#endif +} + +/**********************************************************************/ +/* Return the next nnn bits of input. All reads from the compressed input + are done through this function. All reads are big endian */ +static unsigned int get_bits(bunzip_data *bd, char bits_wanted) +{ + unsigned int bits=0; + + /* If we need to get more data from the byte buffer, do so. (Loop getting + one byte at a time to enforce endianness and avoid unaligned access.) */ + while (bd->inbufBitCount<bits_wanted) { + /* If we need to read more data from file into byte buffer, do so */ +#ifdef ENABLE_BUNZIP_CHECKING + if(bd->inbufPos==bd->inbufCount) { + SET_LED_ERROR(LED_CODE_0); + } +#endif + /* Avoid 32-bit overflow (dump bit buffer to top of output) */ + if(bd->inbufBitCount>=24) { + bits=bd->inbufBits&((1<<bd->inbufBitCount)-1); + bits_wanted-=bd->inbufBitCount; + bits<<=bits_wanted; + bd->inbufBitCount=0; + } + /* Grab next 8 bits of input from buffer. */ + bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++]; + bd->inbufBitCount+=8; + } + /* Calculate result */ + bd->inbufBitCount-=bits_wanted; + bits|=(bd->inbufBits>>bd->inbufBitCount)&((1<<bits_wanted)-1); + + return bits; +} + +/* Unpacks the next block and sets up for the inverse burrows-wheeler step. */ + +static int get_next_block(bunzip_data *bd) +{ + struct group_data *hufGroup; +#ifdef ENABLE_BUNZIP_CHECKING + int dbufCount,nextSym,dbufSize,groupCount,*base,*limit,selector, + i,j,k,t,runPos,symCount,symTotal,nSelectors,byteCount[256]; +#else + int dbufCount,nextSym,/*dbufSize,*/groupCount,*base,*limit,selector, + i,j,k,t,runPos,symCount,symTotal,nSelectors,byteCount[256]; +#endif + unsigned char uc, symToByte[256], mtfSymbol[256], *selectors; + unsigned int *dbuf,origPtr; + + dbuf=bd->dbuf; +#ifdef ENABLE_BUNZIP_CHECKING + dbufSize=bd->dbufSize; +#endif + selectors=bd->selectors; + /* Read in header signature and CRC, then validate signature. + (last block signature means CRC is for whole file, return now) */ + i = get_bits(bd,24); + j = get_bits(bd,24); +#ifdef ENABLE_BUNZIP_CHECKING + bd->headerCRC=get_bits(bd,32); + if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK; + if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA; + /* We can add support for blockRandomised if anybody complains. There was + some code for this in busybox 1.0.0-pre3, but nobody ever noticed that + it didn't actually work. */ + if(get_bits(bd,1)) return RETVAL_OBSOLETE_INPUT; + if((origPtr=get_bits(bd,24)) > dbufSize) return RETVAL_DATA_ERROR; +#else + get_bits(bd,32); + if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK; + get_bits(bd,1); + origPtr=get_bits(bd,24); +#endif + /* mapping table: if some byte values are never used (encoding things + like ascii text), the compression code removes the gaps to have fewer + symbols to deal with, and writes a sparse bitfield indicating which + values were present. We make a translation table to convert the symbols + back to the corresponding bytes. */ + t=get_bits(bd, 16); + symTotal=0; + for (i=0;i<16;i++) { + if(t&(1<<(15-i))) { + k=get_bits(bd,16); + for(j=0;j<16;j++) + if(k&(1<<(15-j))) symToByte[symTotal++]=(16*i)+j; + } + } + /* How many different huffman coding groups does this block use? */ + groupCount=get_bits(bd,3); +#ifdef ENABLE_BUNZIP_CHECKING + if (groupCount<2 || groupCount>MAX_GROUPS) return RETVAL_DATA_ERROR; +#endif + /* nSelectors: Every GROUP_SIZE many symbols we select a new huffman coding + group. Read in the group selector list, which is stored as MTF encoded + bit runs. (MTF=Move To Front, as each value is used it's moved to the + start of the list.) */ +#ifdef ENABLE_BUNZIP_CHECKING + if(!(nSelectors=get_bits(bd, 15))) return RETVAL_DATA_ERROR; +#else + nSelectors=get_bits(bd, 15); +#endif + for(i=0; i<groupCount; i++) mtfSymbol[i] = i; + for(i=0; i<nSelectors; i++) { + /* Get next value */ +#ifdef ENABLE_BUNZIP_CHECKING + for(j=0;get_bits(bd,1);j++) if (j>=groupCount) return RETVAL_DATA_ERROR; +#else + for(j=0;get_bits(bd,1);j++) ; +#endif + /* Decode MTF to get the next selector */ + uc = mtfSymbol[j]; + for(;j;j--) mtfSymbol[j] = mtfSymbol[j-1]; + mtfSymbol[0]=selectors[i]=uc; + } + /* Read the huffman coding tables for each group, which code for symTotal + literal symbols, plus two run symbols (RUNA, RUNB) */ + symCount=symTotal+2; + for (j=0; j<groupCount; j++) { + unsigned char length[MAX_SYMBOLS],temp[MAX_HUFCODE_BITS+1]; + int minLen, maxLen, pp; + /* Read huffman code lengths for each symbol. They're stored in + a way similar to mtf; record a starting value for the first symbol, + and an offset from the previous value for everys symbol after that. + (Subtracting 1 before the loop and then adding it back at the end is + an optimization that makes the test inside the loop simpler: symbol + length 0 becomes negative, so an unsigned inequality catches it.) */ + t=get_bits(bd, 5)-1; + for (i = 0; i < symCount; i++) { + for(;;) { +#ifdef ENABLE_BUNZIP_CHECKING + if (((unsigned)t) > (MAX_HUFCODE_BITS-1)) + return RETVAL_DATA_ERROR; +#endif + /* If first bit is 0, stop. Else second bit indicates whether + to increment or decrement the value. Optimization: grab 2 + bits and unget the second if the first was 0. */ + k = get_bits(bd,2); + if (k < 2) { + bd->inbufBitCount++; + break; + } + /* Add one if second bit 1, else subtract 1. Avoids if/else */ + t+=(((k+1)&2)-1); + } + /* Correct for the initial -1, to get the final symbol length */ + length[i]=t+1; + } + /* Find largest and smallest lengths in this group */ + minLen=maxLen=length[0]; + for(i = 1; i < symCount; i++) { + if(length[i] > maxLen) maxLen = length[i]; + else if(length[i] < minLen) minLen = length[i]; + } + /* Calculate permute[], base[], and limit[] tables from length[]. + * + * permute[] is the lookup table for converting huffman coded symbols + * into decoded symbols. base[] is the amount to subtract from the + * value of a huffman symbol of a given length when using permute[]. + * + * limit[] indicates the largest numerical value a symbol with a given + * number of bits can have. This is how the huffman codes can vary in + * length: each code with a value>limit[length] needs another bit. + */ + hufGroup=bd->groups+j; + hufGroup->minLen = minLen; + hufGroup->maxLen = maxLen; + /* Note that minLen can't be smaller than 1, so we adjust the base + and limit array pointers so we're not always wasting the first + entry. We do this again when using them (during symbol decoding).*/ + base=hufGroup->base-1; + limit=hufGroup->limit-1; + /* Calculate permute[]. Concurently, initialize temp[] and limit[]. */ + pp=0; + for(i=minLen;i<=maxLen;i++) { + temp[i]=limit[i]=0; + for(t=0;t<symCount;t++) + if(length[t]==i) hufGroup->permute[pp++] = t; + } + /* Count symbols coded for at each bit length */ + for (i=0;i<symCount;i++) temp[length[i]]++; + /* Calculate limit[] (the largest symbol-coding value at each bit + * length, which is (previous limit<<1)+symbols at this level), and + * base[] (number of symbols to ignore at each bit length, which is + * limit minus the cumulative count of symbols coded for already). */ + pp=t=0; + for (i=minLen; i<maxLen; i++) { + pp+=temp[i]; + /* We read the largest possible symbol size and then unget bits + after determining how many we need, and those extra bits could + be set to anything. (They're noise from future symbols.) At + each level we're really only interested in the first few bits, + so here we set all the trailing to-be-ignored bits to 1 so they + don't affect the value>limit[length] comparison. */ + limit[i]= (pp << (maxLen - i)) - 1; + pp<<=1; + base[i+1]=pp-(t+=temp[i]); + } + limit[maxLen+1] = INT_MAX; /* Sentinal value for reading next sym. */ + limit[maxLen]=pp+temp[maxLen]-1; + base[minLen]=0; + } + /* We've finished reading and digesting the block header. Now read this + block's huffman coded symbols from the file and undo the huffman coding + and run length encoding, saving the result into dbuf[dbufCount++]=uc */ + + /* Initialize symbol occurrence counters and symbol Move To Front table */ + for(i=0;i<256;i++) { + byteCount[i] = 0; + mtfSymbol[i]=(unsigned char)i; + } + /* Loop through compressed symbols. */ + runPos=dbufCount=symCount=selector=0; + for(;;) { + /* Determine which huffman coding group to use. */ + if(!(symCount--)) { + symCount=GROUP_SIZE-1; +#ifdef ENABLE_BUNZIP_CHECKING + if(selector>=nSelectors) return RETVAL_DATA_ERROR; +#endif + hufGroup=bd->groups+selectors[selector++]; + base=hufGroup->base-1; + limit=hufGroup->limit-1; + } + /* Read next huffman-coded symbol. */ + /* Note: It is far cheaper to read maxLen bits and back up than it is + to read minLen bits and then an additional bit at a time, testing + as we go. Because there is a trailing last block (with file CRC), + there is no danger of the overread causing an unexpected EOF for a + valid compressed file. As a further optimization, we do the read + inline (falling back to a call to get_bits if the buffer runs + dry). The following (up to got_huff_bits:) is equivalent to + j=get_bits(bd,hufGroup->maxLen); + */ + while (bd->inbufBitCount<hufGroup->maxLen) { +#ifdef ENABLE_BUNZIP_CHECKING + if(bd->inbufPos==bd->inbufCount) { + j = get_bits(bd,hufGroup->maxLen); + goto got_huff_bits; + } +#endif + bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++]; + bd->inbufBitCount+=8; + }; + bd->inbufBitCount-=hufGroup->maxLen; + j = (bd->inbufBits>>bd->inbufBitCount)&((1<<hufGroup->maxLen)-1); +got_huff_bits: + /* Figure how how many bits are in next symbol and unget extras */ + i=hufGroup->minLen; + while(j>limit[i]) ++i; + bd->inbufBitCount += (hufGroup->maxLen - i); + /* Huffman decode value to get nextSym (with bounds checking) */ +#ifdef ENABLE_BUNZIP_CHECKING + if ((i > hufGroup->maxLen) + || (((unsigned)(j=(j>>(hufGroup->maxLen-i))-base[i])) + >= MAX_SYMBOLS)) + return RETVAL_DATA_ERROR; +#else + j=(j>>(hufGroup->maxLen-i))-base[i]; +#endif + nextSym = hufGroup->permute[j]; + /* We have now decoded the symbol, which indicates either a new literal + byte, or a repeated run of the most recent literal byte. First, + check if nextSym indicates a repeated run, and if so loop collecting + how many times to repeat the last literal. */ + if (((unsigned)nextSym) <= SYMBOL_RUNB) { /* RUNA or RUNB */ + /* If this is the start of a new run, zero out counter */ + if(!runPos) { + runPos = 1; + t = 0; + } + /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at + each bit position, add 1 or 2 instead. For example, + 1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2. + You can make any bit pattern that way using 1 less symbol than + the basic or 0/1 method (except all bits 0, which would use no + symbols, but a run of length 0 doesn't mean anything in this + context). Thus space is saved. */ + t += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */ + runPos <<= 1; + continue; + } + /* When we hit the first non-run symbol after a run, we now know + how many times to repeat the last literal, so append that many + copies to our buffer of decoded symbols (dbuf) now. (The last + literal used is the one at the head of the mtfSymbol array.) */ + if(runPos) { + runPos=0; +#ifdef ENABLE_BUNZIP_CHECKING + if(dbufCount+t>=dbufSize) return RETVAL_DATA_ERROR; +#endif + + uc = symToByte[mtfSymbol[0]]; + byteCount[uc] += t; + while(t--) dbuf[dbufCount++]=uc; + } + /* Is this the terminating symbol? */ + if(nextSym>symTotal) break; + /* At this point, nextSym indicates a new literal character. Subtract + one to get the position in the MTF array at which this literal is + currently to be found. (Note that the result can't be -1 or 0, + because 0 and 1 are RUNA and RUNB. But another instance of the + first symbol in the mtf array, position 0, would have been handled + as part of a run above. Therefore 1 unused mtf position minus + 2 non-literal nextSym values equals -1.) */ +#ifdef ENABLE_BUNZIP_CHECKING + if(dbufCount>=dbufSize) return RETVAL_DATA_ERROR; +#endif + i = nextSym - 1; + uc = mtfSymbol[i]; + /* Adjust the MTF array. Since we typically expect to move only a + * small number of symbols, and are bound by 256 in any case, using + * memmove here would typically be bigger and slower due to function + * call overhead and other assorted setup costs. */ + do { + mtfSymbol[i] = mtfSymbol[i-1]; + } while (--i); + mtfSymbol[0] = uc; + uc=symToByte[uc]; + /* We have our literal byte. Save it into dbuf. */ + byteCount[uc]++; + dbuf[dbufCount++] = (unsigned int)uc; + } + /* At this point, we've read all the huffman-coded symbols (and repeated + runs) for this block from the input stream, and decoded them into the + intermediate buffer. There are dbufCount many decoded bytes in dbuf[]. + Now undo the Burrows-Wheeler transform on dbuf. + See http://dogma.net/markn/articles/bwt/bwt.htm + */ + /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */ + j=0; + for(i=0;i<256;i++) { + k=j+byteCount[i]; + byteCount[i] = j; + j=k; + } + /* Figure out what order dbuf would be in if we sorted it. */ + for (i=0;i<dbufCount;i++) { + uc=(unsigned char)(dbuf[i] & 0xff); + dbuf[byteCount[uc]] |= (i << 8); + byteCount[uc]++; + } + /* Decode first byte by hand to initialize "previous" byte. Note that it + doesn't get output, and if the first three characters are identical + it doesn't qualify as a run (hence writeRunCountdown=5). */ + if(dbufCount) { +#ifdef ENABLE_BUNZIP_CHECKING + if(origPtr>=dbufCount) return RETVAL_DATA_ERROR; +#endif + bd->writePos=dbuf[origPtr]; + bd->writeCurrent=(unsigned char)(bd->writePos&0xff); + bd->writePos>>=8; + bd->writeRunCountdown=5; + } + bd->writeCount=dbufCount; + + return RETVAL_OK; +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/decompress_bunzip2.lds b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/decompress_bunzip2.lds new file mode 100644 index 0000000000..6a91b49c25 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/decompress_bunzip2.lds @@ -0,0 +1,17 @@ +OUTPUT_ARCH(mips) +ENTRY(load_and_run) +SECTIONS { + . = 0x81000000-0x4000; + .text : { + *(.text) + *(.rodata) + } + + .data : { + *(.data) + } + + .bss : { + *(.bss) + } +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/decompress_bunzip2.lds.in b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/decompress_bunzip2.lds.in new file mode 100644 index 0000000000..95cf83ebd1 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/decompress_bunzip2.lds.in @@ -0,0 +1,17 @@ +OUTPUT_ARCH(mips) +ENTRY(load_and_run) +SECTIONS { + . = BZ_TEXT_START; + .text : { + *(.text) + *(.rodata) + } + + .data : { + *(.data) + } + + .bss : { + *(.bss) + } +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/head.S b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/head.S new file mode 100644 index 0000000000..c3ecdc47f4 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/head.S @@ -0,0 +1,26 @@ +/* Copyright 2004 Manuel Novoa III (mjn3@codepoet.org) */ +/* Licensed under the linux kernel's version of the GPL. */ + +#include <asm/asm.h> +#include <asm/regdef.h> + + .text + LEAF(startup) + .set noreorder + + li t1, BZ_TEXT_START + add a0, t1, 0 + la a1, code_start + la a2, code_stop +$L1: + lw t0, 0(a1) + sw t0, 0(a0) + add a1, 4 + add a0, 4 + blt a1, a2, $L1 + + add sp, t1, -4 + jal t1 + + .set reorder + END(startup) diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/vmlinux.lds b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/vmlinux.lds new file mode 100644 index 0000000000..9d95adbfa8 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/vmlinux.lds @@ -0,0 +1,17 @@ +OUTPUT_ARCH(mips) +ENTRY(startup) +SECTIONS { + . = 0x80001000; + .text : { + *(.text) + *(.rodata) + } + + .data : { + *(.data) + } + + .bss : { + *(.bss) + } +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/vmlinux.lds.in b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/vmlinux.lds.in new file mode 100644 index 0000000000..20f2ea98ec --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/compressed/vmlinux.lds.in @@ -0,0 +1,17 @@ +OUTPUT_ARCH(mips) +ENTRY(startup) +SECTIONS { + . = TEXT_START; + .text : { + *(.text) + *(.rodata) + } + + .data : { + *(.data) + } + + .bss : { + *(.bss) + } +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/gpio.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/gpio.c new file mode 100644 index 0000000000..d20b763338 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/gpio.c @@ -0,0 +1,158 @@ +/* + * GPIO char driver + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <asm/uaccess.h> + +#include <typedefs.h> +#include <bcmutils.h> +#include <sbutils.h> +#include <bcmdevs.h> + +static void *gpio_sbh; +static int gpio_major; +static devfs_handle_t gpio_dir; +static struct { + char *name; + devfs_handle_t handle; +} gpio_file[] = { + { "in", NULL }, + { "out", NULL }, + { "outen", NULL }, + { "control", NULL } +}; + +static int +gpio_open(struct inode *inode, struct file * file) +{ + if (MINOR(inode->i_rdev) > ARRAYSIZE(gpio_file)) + return -ENODEV; + + MOD_INC_USE_COUNT; + return 0; +} + +static int +gpio_release(struct inode *inode, struct file * file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static ssize_t +gpio_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + u32 val; + + switch (MINOR(file->f_dentry->d_inode->i_rdev)) { + case 0: + val = sb_gpioin(gpio_sbh); + break; + case 1: + val = sb_gpioout(gpio_sbh, 0, 0); + break; + case 2: + val = sb_gpioouten(gpio_sbh, 0, 0); + break; + case 3: + val = sb_gpiocontrol(gpio_sbh, 0, 0); + break; + default: + return -ENODEV; + } + + if (put_user(val, (u32 *) buf)) + return -EFAULT; + + return sizeof(val); +} + +static ssize_t +gpio_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + u32 val; + + if (get_user(val, (u32 *) buf)) + return -EFAULT; + + switch (MINOR(file->f_dentry->d_inode->i_rdev)) { + case 0: + return -EACCES; + case 1: + sb_gpioout(gpio_sbh, ~0, val); + break; + case 2: + sb_gpioouten(gpio_sbh, ~0, val); + break; + case 3: + sb_gpiocontrol(gpio_sbh, ~0, val); + break; + default: + return -ENODEV; + } + + return sizeof(val); +} + +static struct file_operations gpio_fops = { + owner: THIS_MODULE, + open: gpio_open, + release: gpio_release, + read: gpio_read, + write: gpio_write, +}; + +static int __init +gpio_init(void) +{ + int i; + + if (!(gpio_sbh = sb_kattach())) + return -ENODEV; + + sb_gpiosetcore(gpio_sbh); + + if ((gpio_major = devfs_register_chrdev(0, "gpio", &gpio_fops)) < 0) + return gpio_major; + + gpio_dir = devfs_mk_dir(NULL, "gpio", NULL); + + for (i = 0; i < ARRAYSIZE(gpio_file); i++) { + gpio_file[i].handle = devfs_register(gpio_dir, + gpio_file[i].name, + DEVFS_FL_DEFAULT, gpio_major, i, + S_IFCHR | S_IRUGO | S_IWUGO, + &gpio_fops, NULL); + } + + return 0; +} + +static void __exit +gpio_exit(void) +{ + int i; + + for (i = 0; i < ARRAYSIZE(gpio_file); i++) + devfs_unregister(gpio_file[i].handle); + devfs_unregister(gpio_dir); + devfs_unregister_chrdev(gpio_major, "gpio"); + sb_detach(gpio_sbh); +} + +module_init(gpio_init); +module_exit(gpio_exit); diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/nvram.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/nvram.c new file mode 100644 index 0000000000..af12ced3ff --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/nvram.c @@ -0,0 +1,317 @@ +/* + * NVRAM variable manipulation (common) + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <typedefs.h> +#include <osl.h> +#include <bcmendian.h> +#include <bcmnvram.h> +#include <bcmutils.h> +#include <sbsdram.h> + +extern struct nvram_tuple * _nvram_realloc(struct nvram_tuple *t, const char *name, const char *value); +extern void _nvram_free(struct nvram_tuple *t); +extern int _nvram_read(void *buf); + +char * _nvram_get(const char *name); +int _nvram_set(const char *name, const char *value); +int _nvram_unset(const char *name); +int _nvram_getall(char *buf, int count); +int _nvram_commit(struct nvram_header *header); +int _nvram_init(void); +void _nvram_exit(void); + +static struct nvram_tuple * nvram_hash[257]; +static struct nvram_tuple * nvram_dead; + +/* Free all tuples. Should be locked. */ +static void +nvram_free(void) +{ + uint i; + struct nvram_tuple *t, *next; + + /* Free hash table */ + for (i = 0; i < ARRAYSIZE(nvram_hash); i++) { + for (t = nvram_hash[i]; t; t = next) { + next = t->next; + _nvram_free(t); + } + nvram_hash[i] = NULL; + } + + /* Free dead table */ + for (t = nvram_dead; t; t = next) { + next = t->next; + _nvram_free(t); + } + nvram_dead = NULL; + + /* Indicate to per-port code that all tuples have been freed */ + _nvram_free(NULL); +} + +/* String hash */ +static INLINE uint +hash(const char *s) +{ + uint hash = 0; + + while (*s) + hash = 31 * hash + *s++; + + return hash; +} + +/* (Re)initialize the hash table. Should be locked. */ +static int +nvram_rehash(struct nvram_header *header) +{ + char buf[] = "0xXXXXXXXX", *name, *value, *end, *eq; + + /* (Re)initialize hash table */ + nvram_free(); + + /* Parse and set "name=value\0 ... \0\0" */ + name = (char *) &header[1]; + end = (char *) header + NVRAM_SPACE - 2; + end[0] = end[1] = '\0'; + for (; *name; name = value + strlen(value) + 1) { + if (!(eq = strchr(name, '='))) + break; + *eq = '\0'; + value = eq + 1; + _nvram_set(name, value); + *eq = '='; + } + + /* Set special SDRAM parameters */ + if (!_nvram_get("sdram_init")) { + sprintf(buf, "0x%04X", (uint16)(header->crc_ver_init >> 16)); + _nvram_set("sdram_init", buf); + } + if (!_nvram_get("sdram_config")) { + sprintf(buf, "0x%04X", (uint16)(header->config_refresh & 0xffff)); + _nvram_set("sdram_config", buf); + } + if (!_nvram_get("sdram_refresh")) { + sprintf(buf, "0x%04X", (uint16)((header->config_refresh >> 16) & 0xffff)); + _nvram_set("sdram_refresh", buf); + } + if (!_nvram_get("sdram_ncdl")) { + sprintf(buf, "0x%08X", header->config_ncdl); + _nvram_set("sdram_ncdl", buf); + } + + return 0; +} + +/* Get the value of an NVRAM variable. Should be locked. */ +char * +_nvram_get(const char *name) +{ + uint i; + struct nvram_tuple *t; + char *value; + + if (!name) + return NULL; + + /* Hash the name */ + i = hash(name) % ARRAYSIZE(nvram_hash); + + /* Find the associated tuple in the hash table */ + for (t = nvram_hash[i]; t && strcmp(t->name, name); t = t->next); + + value = t ? t->value : NULL; + + return value; +} + +/* Get the value of an NVRAM variable. Should be locked. */ +int +_nvram_set(const char *name, const char *value) +{ + uint i; + struct nvram_tuple *t, *u, **prev; + + /* Hash the name */ + i = hash(name) % ARRAYSIZE(nvram_hash); + + /* Find the associated tuple in the hash table */ + for (prev = &nvram_hash[i], t = *prev; t && strcmp(t->name, name); prev = &t->next, t = *prev); + + /* (Re)allocate tuple */ + if (!(u = _nvram_realloc(t, name, value))) + return -12; /* -ENOMEM */ + + /* Value reallocated */ + if (t && t == u) + return 0; + + /* Move old tuple to the dead table */ + if (t) { + *prev = t->next; + t->next = nvram_dead; + nvram_dead = t; + } + + /* Add new tuple to the hash table */ + u->next = nvram_hash[i]; + nvram_hash[i] = u; + + return 0; +} + +/* Unset the value of an NVRAM variable. Should be locked. */ +int +_nvram_unset(const char *name) +{ + uint i; + struct nvram_tuple *t, **prev; + + if (!name) + return 0; + + /* Hash the name */ + i = hash(name) % ARRAYSIZE(nvram_hash); + + /* Find the associated tuple in the hash table */ + for (prev = &nvram_hash[i], t = *prev; t && strcmp(t->name, name); prev = &t->next, t = *prev); + + /* Move it to the dead table */ + if (t) { + *prev = t->next; + t->next = nvram_dead; + nvram_dead = t; + } + + return 0; +} + +/* Get all NVRAM variables. Should be locked. */ +int +_nvram_getall(char *buf, int count) +{ + uint i; + struct nvram_tuple *t; + int len = 0; + + bzero(buf, count); + + /* Write name=value\0 ... \0\0 */ + for (i = 0; i < ARRAYSIZE(nvram_hash); i++) { + for (t = nvram_hash[i]; t; t = t->next) { + if ((count - len) > (strlen(t->name) + 1 + strlen(t->value) + 1)) + len += sprintf(buf + len, "%s=%s", t->name, t->value) + 1; + else + break; + } + } + + return 0; +} + +/* Regenerate NVRAM. Should be locked. */ +int +_nvram_commit(struct nvram_header *header) +{ + char *init, *config, *refresh, *ncdl; + char *ptr, *end; + int i; + struct nvram_tuple *t; + struct nvram_header tmp; + uint8 crc; + + /* Regenerate header */ + header->magic = NVRAM_MAGIC; + header->crc_ver_init = (NVRAM_VERSION << 8); + if (!(init = _nvram_get("sdram_init")) || + !(config = _nvram_get("sdram_config")) || + !(refresh = _nvram_get("sdram_refresh")) || + !(ncdl = _nvram_get("sdram_ncdl"))) { + header->crc_ver_init |= SDRAM_INIT << 16; + header->config_refresh = SDRAM_CONFIG; + header->config_refresh |= SDRAM_REFRESH << 16; + header->config_ncdl = 0; + } else { + header->crc_ver_init |= (bcm_strtoul(init, NULL, 0) & 0xffff) << 16; + header->config_refresh = bcm_strtoul(config, NULL, 0) & 0xffff; + header->config_refresh |= (bcm_strtoul(refresh, NULL, 0) & 0xffff) << 16; + header->config_ncdl = bcm_strtoul(ncdl, NULL, 0); + } + + /* Clear data area */ + ptr = (char *) header + sizeof(struct nvram_header); + bzero(ptr, NVRAM_SPACE - sizeof(struct nvram_header)); + + /* Leave space for a double NUL at the end */ + end = (char *) header + NVRAM_SPACE - 2; + + /* Write out all tuples */ + for (i = 0; i < ARRAYSIZE(nvram_hash); i++) { + for (t = nvram_hash[i]; t; t = t->next) { + if ((ptr + strlen(t->name) + 1 + strlen(t->value) + 1) > end) + break; + ptr += sprintf(ptr, "%s=%s", t->name, t->value) + 1; + } + } + + /* End with a double NUL */ + ptr += 2; + + /* Set new length */ + header->len = ROUNDUP(ptr - (char *) header, 4); + + /* Little-endian CRC8 over the last 11 bytes of the header */ + tmp.crc_ver_init = htol32(header->crc_ver_init); + tmp.config_refresh = htol32(header->config_refresh); + tmp.config_ncdl = htol32(header->config_ncdl); + crc = crc8((char *) &tmp + 9, sizeof(struct nvram_header) - 9, CRC8_INIT_VALUE); + + /* Continue CRC8 over data bytes */ + crc = crc8((char *) &header[1], header->len - sizeof(struct nvram_header), crc); + + /* Set new CRC8 */ + header->crc_ver_init |= crc; + + /* Reinitialize hash table */ + return nvram_rehash(header); +} + +/* Initialize hash table. Should be locked. */ +int +_nvram_init(void) +{ + struct nvram_header *header; + int ret; + + if (!(header = (struct nvram_header *) MALLOC(NVRAM_SPACE))) { + printf("nvram_init: out of memory\n"); + return -12; /* -ENOMEM */ + } + + if ((ret = _nvram_read(header)) == 0 && + header->magic == NVRAM_MAGIC) + nvram_rehash(header); + + MFREE(header, NVRAM_SPACE); + return ret; +} + +/* Free hash table. Should be locked. */ +void +_nvram_exit(void) +{ + nvram_free(); +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/nvram_linux.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/nvram_linux.c new file mode 100644 index 0000000000..bbbc1eb735 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/nvram_linux.c @@ -0,0 +1,638 @@ +/* + * NVRAM variable manipulation (Linux kernel half) + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/bootmem.h> +#include <linux/wrapper.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/mtd/mtd.h> +#include <asm/addrspace.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +#include <typedefs.h> +#include <bcmendian.h> +#include <bcmnvram.h> +#include <bcmutils.h> +#include <sbconfig.h> +#include <sbchipc.h> +#include <sbutils.h> +#include <sbmips.h> +#include <sflash.h> + +/* In BSS to minimize text size and page aligned so it can be mmap()-ed */ +static char nvram_buf[NVRAM_SPACE] __attribute__((aligned(PAGE_SIZE))); + +#ifdef MODULE + +#define early_nvram_get(name) nvram_get(name) + +#else /* !MODULE */ + +/* Global SB handle */ +extern void *bcm947xx_sbh; +extern spinlock_t bcm947xx_sbh_lock; + +/* Convenience */ +#define sbh bcm947xx_sbh +#define sbh_lock bcm947xx_sbh_lock +#define KB * 1024 +#define MB * 1024 * 1024 + +/* Probe for NVRAM header */ +static void __init +early_nvram_init(void) +{ + struct nvram_header *header; + chipcregs_t *cc; + struct sflash *info = NULL; + int i; + uint32 base, off, lim; + + if ((cc = sb_setcore(sbh, SB_CC, 0)) != NULL) { + base = CC_FLASH_BASE; + switch (readl(&cc->capabilities) & CAP_FLASH_MASK) { + case PFLASH: + lim = CC_FLASH_MAX; + break; + + case SFLASH_ST: + case SFLASH_AT: + if ((info = sflash_init(cc)) == NULL) + return; + lim = info->size; + break; + + case FLASH_NONE: + default: + return; + } + } else { + /* extif assumed, Stop at 4 MB */ + base = FLASH_BASE; + lim = FLASH_MAX; + } + + off = FLASH_MIN; + while (off <= lim) { + /* Windowed flash access */ + header = (struct nvram_header *) KSEG1ADDR(base + off - NVRAM_SPACE); + if (header->magic == NVRAM_MAGIC) { + u32 *src = (u32 *) header; + u32 *dst = (u32 *) nvram_buf; + for (i = 0; i < sizeof(struct nvram_header); i += 4) + *dst++ = *src++; + for (; i < header->len && i < NVRAM_SPACE; i += 4) + *dst++ = ltoh32(*src++); + return; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ + if (off == 1 KB) + break; + else if (off == 4 KB) + off = 1 KB; + else if (off == lim) + off = 4 KB; + else + off <<= 1; + } +} + +/* Early (before mm or mtd) read-only access to NVRAM */ +static char * __init +early_nvram_get(const char *name) +{ + char *var, *value, *end, *eq; + + if (!name) + return NULL; + + if (!nvram_buf[0]) + early_nvram_init(); + + /* Look for name=value and return value */ + var = &nvram_buf[sizeof(struct nvram_header)]; + end = nvram_buf + sizeof(nvram_buf) - 2; + end[0] = end[1] = '\0'; + for (; *var; var = value + strlen(value) + 1) { + if (!(eq = strchr(var, '='))) + break; + value = eq + 1; + if ((eq - var) == strlen(name) && strncmp(var, name, (eq - var)) == 0) + return value; + } + + return NULL; +} + +#endif /* !MODULE */ + +extern char * _nvram_get(const char *name); +extern int _nvram_set(const char *name, const char *value); +extern int _nvram_unset(const char *name); +extern int _nvram_getall(char *buf, int count); +extern int _nvram_commit(struct nvram_header *header); +extern int _nvram_init(void); +extern void _nvram_exit(void); + +/* Globals */ +static spinlock_t nvram_lock = SPIN_LOCK_UNLOCKED; +static struct semaphore nvram_sem; +static unsigned long nvram_offset = 0; +static int nvram_major = -1; +static devfs_handle_t nvram_handle = NULL; +static struct mtd_info *nvram_mtd = NULL; + +int +_nvram_read(char *buf) +{ + struct nvram_header *header = (struct nvram_header *) buf; + size_t len; + + if (!nvram_mtd || + MTD_READ(nvram_mtd, nvram_mtd->size - NVRAM_SPACE, NVRAM_SPACE, &len, buf) || + len != NVRAM_SPACE || + header->magic != NVRAM_MAGIC) { + /* Maybe we can recover some data from early initialization */ + memcpy(buf, nvram_buf, NVRAM_SPACE); + } + + return 0; +} + +struct nvram_tuple * +_nvram_realloc(struct nvram_tuple *t, const char *name, const char *value) +{ + if ((nvram_offset + strlen(value) + 1) > NVRAM_SPACE) + return NULL; + + if (!t) { + if (!(t = kmalloc(sizeof(struct nvram_tuple) + strlen(name) + 1, GFP_ATOMIC))) + return NULL; + + /* Copy name */ + t->name = (char *) &t[1]; + strcpy(t->name, name); + + t->value = NULL; + } + + /* Copy value */ + if (!t->value || strcmp(t->value, value)) { + t->value = &nvram_buf[nvram_offset]; + strcpy(t->value, value); + nvram_offset += strlen(value) + 1; + } + + return t; +} + +void +_nvram_free(struct nvram_tuple *t) +{ + if (!t) + nvram_offset = 0; + else + kfree(t); +} + +int +nvram_set(const char *name, const char *value) +{ + unsigned long flags; + int ret; + struct nvram_header *header; + + spin_lock_irqsave(&nvram_lock, flags); + if ((ret = _nvram_set(name, value))) { + /* Consolidate space and try again */ + if ((header = kmalloc(NVRAM_SPACE, GFP_ATOMIC))) { + if (_nvram_commit(header) == 0) + ret = _nvram_set(name, value); + kfree(header); + } + } + spin_unlock_irqrestore(&nvram_lock, flags); + + return ret; +} + +char * +real_nvram_get(const char *name) +{ + unsigned long flags; + char *value; + + spin_lock_irqsave(&nvram_lock, flags); + value = _nvram_get(name); + spin_unlock_irqrestore(&nvram_lock, flags); + + return value; +} + +char * +nvram_get(const char *name) +{ + if (nvram_major >= 0) + return real_nvram_get(name); + else + return early_nvram_get(name); +} + +int +nvram_unset(const char *name) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&nvram_lock, flags); + ret = _nvram_unset(name); + spin_unlock_irqrestore(&nvram_lock, flags); + + return ret; +} + +static void +erase_callback(struct erase_info *done) +{ + wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv; + wake_up(wait_q); +} + +int +nvram_commit(void) +{ + char *buf; + size_t erasesize, len; + unsigned int i; + int ret; + struct nvram_header *header; + unsigned long flags; + u_int32_t offset; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; + struct erase_info erase; + + printk("nvram_commit(): init\n"); + + if (!nvram_mtd) { + printk("nvram_commit: NVRAM not found\n"); + return -ENODEV; + } + + if (in_interrupt()) { + printk("nvram_commit: not committing in interrupt\n"); + return -EINVAL; + } + + /* Backup sector blocks to be erased */ + erasesize = ROUNDUP(NVRAM_SPACE, nvram_mtd->erasesize); + if (!(buf = kmalloc(erasesize, GFP_KERNEL))) { + printk("nvram_commit: out of memory\n"); + return -ENOMEM; + } + + down(&nvram_sem); +#if 0 + offset = nvram_mtd->size - erasesize; + i = erasesize - NVRAM_SPACE; + ret = MTD_READ(nvram_mtd, offset, i, &len, buf); + if (ret || len != i) { + printk("nvram_commit: read error\n"); + ret = -EIO; + goto done; +#endif + if ((i = erasesize - NVRAM_SPACE) > 0) { + offset = nvram_mtd->size - erasesize; + len = 0; + ret = MTD_READ(nvram_mtd, offset, i, &len, buf); + if (ret || len != i) { + printk("nvram_commit: read error ret = %d, len = %d/%d\n", ret, len, i); + ret = -EIO; + goto done; + } + header = (struct nvram_header *)(buf + i); + } else { + offset = nvram_mtd->size - NVRAM_SPACE; + header = (struct nvram_header *)buf; + } + + /* Regenerate NVRAM */ + spin_lock_irqsave(&nvram_lock, flags); + ret = _nvram_commit(header); + spin_unlock_irqrestore(&nvram_lock, flags); + if (ret) + goto done; + + /* Erase sector blocks */ + init_waitqueue_head(&wait_q); + for (; offset < nvram_mtd->size - NVRAM_SPACE + header->len; offset += nvram_mtd->erasesize) { + erase.mtd = nvram_mtd; + erase.addr = offset; + erase.len = nvram_mtd->erasesize; + erase.callback = erase_callback; + erase.priv = (u_long) &wait_q; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + /* Unlock sector blocks */ + if (nvram_mtd->unlock) + nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize); + + if ((ret = MTD_ERASE(nvram_mtd, &erase))) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + printk("nvram_commit: erase error\n"); + goto done; + } + + /* Wait for erase to finish */ + schedule(); + remove_wait_queue(&wait_q, &wait); + } + + /* Write partition up to end of data area */ + offset = nvram_mtd->size - erasesize; + i = erasesize - NVRAM_SPACE + header->len; + ret = MTD_WRITE(nvram_mtd, offset, i, &len, buf); + if (ret || len != i) { + printk("nvram_commit: write error\n"); + ret = -EIO; + goto done; + } + /* + * Reading a few bytes back here will put the device + * back to the correct mode on certain flashes */ + + offset = nvram_mtd->size - erasesize; + ret = MTD_READ(nvram_mtd, offset, 4, &len, buf); + + done: + up(&nvram_sem); + kfree(buf); + printk("nvram_commit(): end\n"); + return ret; +} + +int +nvram_getall(char *buf, int count) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&nvram_lock, flags); + ret = _nvram_getall(buf, count); + spin_unlock_irqrestore(&nvram_lock, flags); + + return ret; +} + +EXPORT_SYMBOL(nvram_get); +EXPORT_SYMBOL(nvram_getall); +EXPORT_SYMBOL(nvram_set); +EXPORT_SYMBOL(nvram_unset); +EXPORT_SYMBOL(nvram_commit); + +/* User mode interface below */ + +static ssize_t +dev_nvram_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + char tmp[100], *name = tmp, *value; + ssize_t ret; + unsigned long off; + + if (count > sizeof(tmp)) { + if (!(name = kmalloc(count, GFP_KERNEL))) + return -ENOMEM; + } + + if (copy_from_user(name, buf, count)) { + ret = -EFAULT; + goto done; + } + + if (*name == '\0') { + /* Get all variables */ + ret = nvram_getall(name, count); + if (ret == 0) { + if (copy_to_user(buf, name, count)) { + ret = -EFAULT; + goto done; + } + ret = count; + } + } else { + if (!(value = nvram_get(name))) { + ret = 0; + goto done; + } + + /* Provide the offset into mmap() space */ + off = (unsigned long) value - (unsigned long) nvram_buf; + + if (put_user(off, (unsigned long *) buf)) { + ret = -EFAULT; + goto done; + } + + ret = sizeof(unsigned long); + } + + flush_cache_all(); + +done: + if (name != tmp) + kfree(name); + + return ret; +} + +static ssize_t +dev_nvram_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + char tmp[100], *name = tmp, *value; + ssize_t ret; + + if (count > sizeof(tmp)) { + if (!(name = kmalloc(count, GFP_KERNEL))) + return -ENOMEM; + } + + if (copy_from_user(name, buf, count)) { + ret = -EFAULT; + goto done; + } + + value = name; + name = strsep(&value, "="); + if (value) + ret = nvram_set(name, value) ? : count; + else + ret = nvram_unset(name) ? : count; + + done: + if (name != tmp) + kfree(name); + + return ret; +} + +static int +dev_nvram_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + if (cmd != NVRAM_MAGIC) + return -EINVAL; + return nvram_commit(); +} + +static int +dev_nvram_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long offset = virt_to_phys(nvram_buf); + + if (remap_page_range(vma->vm_start, offset, vma->vm_end-vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static int +dev_nvram_open(struct inode *inode, struct file * file) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int +dev_nvram_release(struct inode *inode, struct file * file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static struct file_operations dev_nvram_fops = { + owner: THIS_MODULE, + open: dev_nvram_open, + release: dev_nvram_release, + read: dev_nvram_read, + write: dev_nvram_write, + ioctl: dev_nvram_ioctl, + mmap: dev_nvram_mmap, +}; + +static void +dev_nvram_exit(void) +{ + int order = 0; + struct page *page, *end; + + if (nvram_handle) + devfs_unregister(nvram_handle); + + if (nvram_major >= 0) + devfs_unregister_chrdev(nvram_major, "nvram"); + + if (nvram_mtd) + put_mtd_device(nvram_mtd); + + while ((PAGE_SIZE << order) < NVRAM_SPACE) + order++; + end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(nvram_buf); page <= end; page++) + mem_map_unreserve(page); + + _nvram_exit(); +} + +static int __init +dev_nvram_init(void) +{ + int order = 0, ret = 0; + struct page *page, *end; + unsigned int i; + + /* Allocate and reserve memory to mmap() */ + while ((PAGE_SIZE << order) < NVRAM_SPACE) + order++; + end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(nvram_buf); page <= end; page++) + mem_map_reserve(page); + +#ifdef CONFIG_MTD + /* Find associated MTD device */ + for (i = 0; i < MAX_MTD_DEVICES; i++) { + nvram_mtd = get_mtd_device(NULL, i); + if (nvram_mtd) { + if (!strcmp(nvram_mtd->name, "nvram") && + nvram_mtd->size >= NVRAM_SPACE) + break; + put_mtd_device(nvram_mtd); + } + } + if (i >= MAX_MTD_DEVICES) + nvram_mtd = NULL; +#endif + + /* Initialize hash table lock */ + spin_lock_init(&nvram_lock); + + /* Initialize commit semaphore */ + init_MUTEX(&nvram_sem); + + /* Register char device */ + if ((nvram_major = devfs_register_chrdev(0, "nvram", &dev_nvram_fops)) < 0) { + ret = nvram_major; + goto err; + } + + /* Initialize hash table */ + _nvram_init(); + + /* Create /dev/nvram handle */ + nvram_handle = devfs_register(NULL, "nvram", DEVFS_FL_NONE, nvram_major, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, &dev_nvram_fops, NULL); + + /* Set the SDRAM NCDL value into NVRAM if not already done */ + if (getintvar(NULL, "sdram_ncdl") == 0) { + unsigned int ncdl; + char buf[] = "0x00000000"; + + if ((ncdl = sb_memc_get_ncdl(sbh))) { + sprintf(buf, "0x%08x", ncdl); + nvram_set("sdram_ncdl", buf); + nvram_commit(); + } + } + + return 0; + + err: + dev_nvram_exit(); + return ret; +} + +module_init(dev_nvram_init); +module_exit(dev_nvram_exit); diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/pcibios.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/pcibios.c new file mode 100644 index 0000000000..4077a79f34 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/pcibios.c @@ -0,0 +1,352 @@ +/* + * Low-Level PCI and SB support for BCM47xx (Linux support code) + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/paccess.h> + +#include <typedefs.h> +#include <bcmutils.h> +#include <sbconfig.h> +#include <sbpci.h> +#include <pcicfg.h> +#include <sbutils.h> +#include <bcmdevs.h> +#include <bcmnvram.h> + +/* Global SB handle */ +extern void *bcm947xx_sbh; +extern spinlock_t bcm947xx_sbh_lock; + +/* Convenience */ +#define sbh bcm947xx_sbh +#define sbh_lock bcm947xx_sbh_lock + +static int +sbpci_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sbh_lock, flags); + ret = sbpci_read_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, value, sizeof(*value)); + spin_unlock_irqrestore(&sbh_lock, flags); + return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static int +sbpci_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sbh_lock, flags); + ret = sbpci_read_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, value, sizeof(*value)); + spin_unlock_irqrestore(&sbh_lock, flags); + return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static int +sbpci_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sbh_lock, flags); + ret = sbpci_read_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, value, sizeof(*value)); + spin_unlock_irqrestore(&sbh_lock, flags); + return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static int +sbpci_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sbh_lock, flags); + ret = sbpci_write_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, &value, sizeof(value)); + spin_unlock_irqrestore(&sbh_lock, flags); + return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static int +sbpci_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sbh_lock, flags); + ret = sbpci_write_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, &value, sizeof(value)); + spin_unlock_irqrestore(&sbh_lock, flags); + return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static int +sbpci_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sbh_lock, flags); + ret = sbpci_write_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, &value, sizeof(value)); + spin_unlock_irqrestore(&sbh_lock, flags); + return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops pcibios_ops = { + sbpci_read_config_byte, + sbpci_read_config_word, + sbpci_read_config_dword, + sbpci_write_config_byte, + sbpci_write_config_word, + sbpci_write_config_dword +}; + + +void __init +pcibios_init(void) +{ + ulong flags; + + if (!(sbh = sb_kattach())) + panic("sb_kattach failed"); + spin_lock_init(&sbh_lock); + + spin_lock_irqsave(&sbh_lock, flags); + sbpci_init(sbh); + spin_unlock_irqrestore(&sbh_lock, flags); + + set_io_port_base((unsigned long) ioremap_nocache(SB_PCI_MEM, 0x04000000)); + + /* Scan the SB bus */ + pci_scan_bus(0, &pcibios_ops, NULL); + +} + +char * __init +pcibios_setup(char *str) +{ + if (!strncmp(str, "ban=", 4)) { + sbpci_ban(simple_strtoul(str + 4, NULL, 0)); + return NULL; + } + + return (str); +} + +static u32 pci_iobase = 0x100; +static u32 pci_membase = SB_PCI_DMA; + +void __init +pcibios_fixup_bus(struct pci_bus *b) +{ + struct list_head *ln; + struct pci_dev *d; + struct resource *res; + int pos, size; + u32 *base; + u8 irq; + + printk("PCI: Fixing up bus %d\n", b->number); + + /* Fix up SB */ + if (b->number == 0) { + for (ln=b->devices.next; ln != &b->devices; ln=ln->next) { + d = pci_dev_b(ln); + /* Fix up interrupt lines */ + pci_read_config_byte(d, PCI_INTERRUPT_LINE, &irq); + d->irq = irq + 2; + pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq); + } + } + + /* Fix up external PCI */ + else { + for (ln=b->devices.next; ln != &b->devices; ln=ln->next) { + d = pci_dev_b(ln); + /* Fix up resource bases */ + for (pos = 0; pos < 6; pos++) { + res = &d->resource[pos]; + base = (res->flags & IORESOURCE_IO) ? &pci_iobase : &pci_membase; + if (res->end) { + size = res->end - res->start + 1; + if (*base & (size - 1)) + *base = (*base + size) & ~(size - 1); + res->start = *base; + res->end = res->start + size - 1; + *base += size; + pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start); + } + /* Fix up PCI bridge BAR0 only */ + if (b->number == 1 && PCI_SLOT(d->devfn) == 0) + break; + } + /* Fix up interrupt lines */ + if (pci_find_device(VENDOR_BROADCOM, SB_PCI, NULL)) + d->irq = (pci_find_device(VENDOR_BROADCOM, SB_PCI, NULL))->irq; + pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq); + } + } +} + +unsigned int +pcibios_assign_all_busses(void) +{ + return 1; +} + +void +pcibios_align_resource(void *data, struct resource *res, + unsigned long size, unsigned long align) +{ +} + +int +pcibios_enable_resources(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + /* External PCI only */ + if (dev->bus->number == 0) + return 0; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +int +pcibios_enable_device(struct pci_dev *dev, int mask) +{ + ulong flags; + uint coreidx; + + /* External PCI device enable */ + if (dev->bus->number != 0) + return pcibios_enable_resources(dev); + + /* These cores come out of reset enabled */ + if (dev->device == SB_MIPS || + dev->device == SB_MIPS33 || + dev->device == SB_EXTIF || + dev->device == SB_CC) + return 0; + + spin_lock_irqsave(&sbh_lock, flags); + coreidx = sb_coreidx(sbh); + if (!sb_setcoreidx(sbh, PCI_SLOT(dev->devfn))) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * The USB core requires a special bit to be set during core + * reset to enable host (OHCI) mode. Resetting the SB core in + * pcibios_enable_device() is a hack for compatibility with + * vanilla usb-ohci so that it does not have to know about + * SB. A driver that wants to use the USB core in device mode + * should know about SB and should reset the bit back to 0 + * after calling pcibios_enable_device(). + */ + if (sb_coreid(sbh) == SB_USB) { + sb_core_disable(sbh, sb_coreflags(sbh, 0, 0)); + sb_core_reset(sbh, 1 << 29); + } else + sb_core_reset(sbh, 0); + + sb_setcoreidx(sbh, coreidx); + spin_unlock_irqrestore(&sbh_lock, flags); + + return 0; +} + +void +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + unsigned long where, size; + u32 reg; + + /* External PCI only */ + if (dev->bus->number == 0) + return; + + where = PCI_BASE_ADDRESS_0 + (resource * 4); + size = res->end - res->start; + pci_read_config_dword(dev, where, ®); + reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); + pci_write_config_dword(dev, where, reg); +} + +static void __init +quirk_sbpci_bridge(struct pci_dev *dev) +{ + if (dev->bus->number != 1 || PCI_SLOT(dev->devfn) != 0) + return; + + printk("PCI: Fixing up bridge\n"); + + /* Enable PCI bridge bus mastering and memory space */ + pci_set_master(dev); + pcibios_enable_resources(dev); + + /* Enable PCI bridge BAR1 prefetch and burst */ + pci_write_config_dword(dev, PCI_BAR1_CONTROL, 3); +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n", dev->slot_name, lat); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +struct pci_fixup pcibios_fixups[] = { + { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, quirk_sbpci_bridge }, + { 0 } +}; diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/perfcntr.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/perfcntr.c new file mode 100644 index 0000000000..aa0608e6e3 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/perfcntr.c @@ -0,0 +1,67 @@ +/* + * Broadcom BCM47xx Performance Counter /proc/cpuinfo support + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <asm/mipsregs.h> + +/* + * BCM4710 performance counter register select values + * No even-odd control-counter mapping, just counters + */ +#define PERF_DCACHE_HIT 0 +#define PERF_DCACHE_MISS 1 +#define PERF_ICACHE_HIT 2 +#define PERF_ICACHE_MISS 3 +#define PERF_ICOUNT 4 + +/* + * Move from Coprocessor 0 Register 25 Select n + * data <- CPR[0,25,n] + * GPR[1] <- data + */ +#define read_bcm4710_perf_cntr(n) \ +({ int __res; \ + __asm__ __volatile__( \ + ".set\tnoreorder\n\t" \ + ".set\tnoat\n\t" \ + ".word\t"STR(0x4001c800|(n))"\n\t" \ + "move\t%0,$1\n\t" \ + ".set\tat\n\t" \ + ".set\treorder" \ + :"=r" (__res)); \ + __res;}) + +asmlinkage unsigned int read_perf_cntr(unsigned int counter) +{ + switch (counter) { + case PERF_DCACHE_HIT: return read_bcm4710_perf_cntr(PERF_DCACHE_HIT); + case PERF_DCACHE_MISS: return read_bcm4710_perf_cntr(PERF_DCACHE_MISS); + case PERF_ICACHE_HIT: return read_bcm4710_perf_cntr(PERF_ICACHE_HIT); + case PERF_ICACHE_MISS: return read_bcm4710_perf_cntr(PERF_ICACHE_MISS); + case PERF_ICOUNT: return read_bcm4710_perf_cntr(PERF_ICOUNT); + } + return 0; +} + +asmlinkage void write_perf_cntr(unsigned int counter, unsigned int val) +{ +} + +asmlinkage unsigned int read_perf_cntl(unsigned int counter) +{ + return 0; +} + +asmlinkage void write_perf_cntl(unsigned int counter, unsigned int val) +{ +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/prom.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/prom.c new file mode 100644 index 0000000000..e41f44b9e2 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/prom.c @@ -0,0 +1,41 @@ +/* + * Early initialization code for BCM94710 boards + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/bootinfo.h> + +void __init +prom_init(int argc, const char **argv) +{ + unsigned long mem; + + mips_machgroup = MACH_GROUP_BRCM; + mips_machtype = MACH_BCM947XX; + + /* Figure out memory size by finding aliases */ + for (mem = (1 << 20); mem < (128 << 20); mem += (1 << 20)) { + if (*(unsigned long *)((unsigned long)(prom_init) + mem) == + *(unsigned long *)(prom_init)) + break; + } + add_memory_region(0, mem, BOOT_MEM_RAM); +} + +void __init +prom_free_prom_memory(void) +{ +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/sbmips.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/sbmips.c new file mode 100644 index 0000000000..6daaeb78c0 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/sbmips.c @@ -0,0 +1,951 @@ +/* + * BCM47XX Sonics SiliconBackplane MIPS core routines + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <typedefs.h> +#include <osl.h> +#include <sbutils.h> +#include <bcmdevs.h> +#include <bcmnvram.h> +#include <bcmutils.h> +#include <hndmips.h> +#include <sbconfig.h> +#include <sbextif.h> +#include <sbchipc.h> +#include <sbmemc.h> + +/* + * Memory segments (32bit kernel mode addresses) + */ +#undef KUSEG +#undef KSEG0 +#undef KSEG1 +#undef KSEG2 +#undef KSEG3 +#define KUSEG 0x00000000 +#define KSEG0 0x80000000 +#define KSEG1 0xa0000000 +#define KSEG2 0xc0000000 +#define KSEG3 0xe0000000 + +/* + * Map an address to a certain kernel segment + */ +#undef KSEG0ADDR +#undef KSEG1ADDR +#undef KSEG2ADDR +#undef KSEG3ADDR +#define KSEG0ADDR(a) (((a) & 0x1fffffff) | KSEG0) +#define KSEG1ADDR(a) (((a) & 0x1fffffff) | KSEG1) +#define KSEG2ADDR(a) (((a) & 0x1fffffff) | KSEG2) +#define KSEG3ADDR(a) (((a) & 0x1fffffff) | KSEG3) + +/* + * The following macros are especially useful for __asm__ + * inline assembler. + */ +#ifndef __STR +#define __STR(x) #x +#endif +#ifndef STR +#define STR(x) __STR(x) +#endif + +/* ********************************************************************* + * CP0 Registers + ********************************************************************* */ + +#define C0_INX 0 /* CP0: TLB Index */ +#define C0_RAND 1 /* CP0: TLB Random */ +#define C0_TLBLO0 2 /* CP0: TLB EntryLo0 */ +#define C0_TLBLO C0_TLBLO0 /* CP0: TLB EntryLo0 */ +#define C0_TLBLO1 3 /* CP0: TLB EntryLo1 */ +#define C0_CTEXT 4 /* CP0: Context */ +#define C0_PGMASK 5 /* CP0: TLB PageMask */ +#define C0_WIRED 6 /* CP0: TLB Wired */ +#define C0_BADVADDR 8 /* CP0: Bad Virtual Address */ +#define C0_COUNT 9 /* CP0: Count */ +#define C0_TLBHI 10 /* CP0: TLB EntryHi */ +#define C0_COMPARE 11 /* CP0: Compare */ +#define C0_SR 12 /* CP0: Processor Status */ +#define C0_STATUS C0_SR /* CP0: Processor Status */ +#define C0_CAUSE 13 /* CP0: Exception Cause */ +#define C0_EPC 14 /* CP0: Exception PC */ +#define C0_PRID 15 /* CP0: Processor Revision Indentifier */ +#define C0_CONFIG 16 /* CP0: Config */ +#define C0_LLADDR 17 /* CP0: LLAddr */ +#define C0_WATCHLO 18 /* CP0: WatchpointLo */ +#define C0_WATCHHI 19 /* CP0: WatchpointHi */ +#define C0_XCTEXT 20 /* CP0: XContext */ +#define C0_DIAGNOSTIC 22 /* CP0: Diagnostic */ +#define C0_BROADCOM C0_DIAGNOSTIC /* CP0: Broadcom Register */ +#define C0_ECC 26 /* CP0: ECC */ +#define C0_CACHEERR 27 /* CP0: CacheErr */ +#define C0_TAGLO 28 /* CP0: TagLo */ +#define C0_TAGHI 29 /* CP0: TagHi */ +#define C0_ERREPC 30 /* CP0: ErrorEPC */ + +/* + * Macros to access the system control coprocessor + */ + +#define MFC0(source, sel) \ +({ \ + int __res; \ + __asm__ __volatile__( \ + ".set\tnoreorder\n\t" \ + ".set\tnoat\n\t" \ + ".word\t"STR(0x40010000 | ((source)<<11) | (sel))"\n\t" \ + "move\t%0,$1\n\t" \ + ".set\tat\n\t" \ + ".set\treorder" \ + :"=r" (__res) \ + : \ + :"$1"); \ + __res; \ +}) + +#define MTC0(source, sel, value) \ +do { \ + __asm__ __volatile__( \ + ".set\tnoreorder\n\t" \ + ".set\tnoat\n\t" \ + "move\t$1,%z0\n\t" \ + ".word\t"STR(0x40810000 | ((source)<<11) | (sel))"\n\t" \ + ".set\tat\n\t" \ + ".set\treorder" \ + : \ + :"Jr" (value) \ + :"$1"); \ +} while (0) + +/* + * R4x00 interrupt enable / cause bits + */ +#undef IE_SW0 +#undef IE_SW1 +#undef IE_IRQ0 +#undef IE_IRQ1 +#undef IE_IRQ2 +#undef IE_IRQ3 +#undef IE_IRQ4 +#undef IE_IRQ5 +#define IE_SW0 (1<< 8) +#define IE_SW1 (1<< 9) +#define IE_IRQ0 (1<<10) +#define IE_IRQ1 (1<<11) +#define IE_IRQ2 (1<<12) +#define IE_IRQ3 (1<<13) +#define IE_IRQ4 (1<<14) +#define IE_IRQ5 (1<<15) + +/* + * Bitfields in the R4xx0 cp0 status register + */ +#define ST0_IE 0x00000001 +#define ST0_EXL 0x00000002 +#define ST0_ERL 0x00000004 +#define ST0_KSU 0x00000018 +# define KSU_USER 0x00000010 +# define KSU_SUPERVISOR 0x00000008 +# define KSU_KERNEL 0x00000000 +#define ST0_UX 0x00000020 +#define ST0_SX 0x00000040 +#define ST0_KX 0x00000080 +#define ST0_DE 0x00010000 +#define ST0_CE 0x00020000 + +/* + * Status register bits available in all MIPS CPUs. + */ +#define ST0_IM 0x0000ff00 +#define ST0_CH 0x00040000 +#define ST0_SR 0x00100000 +#define ST0_TS 0x00200000 +#define ST0_BEV 0x00400000 +#define ST0_RE 0x02000000 +#define ST0_FR 0x04000000 +#define ST0_CU 0xf0000000 +#define ST0_CU0 0x10000000 +#define ST0_CU1 0x20000000 +#define ST0_CU2 0x40000000 +#define ST0_CU3 0x80000000 +#define ST0_XX 0x80000000 /* MIPS IV naming */ + +/* + * Cache Operations + */ + +#ifndef Fill_I +#define Fill_I 0x14 +#endif + +#define cache_unroll(base,op) \ + __asm__ __volatile__(" \ + .set noreorder; \ + .set mips3; \ + cache %1, (%0); \ + .set mips0; \ + .set reorder" \ + : \ + : "r" (base), \ + "i" (op)); + +/* + * These are the UART port assignments, expressed as offsets from the base + * register. These assignments should hold for any serial port based on + * a 8250, 16450, or 16550(A). + */ + +#define UART_MCR 4 /* Out: Modem Control Register */ +#define UART_MSR 6 /* In: Modem Status Register */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ + +/* + * Returns TRUE if an external UART exists at the given base + * register. + */ +static bool +serial_exists(uint8 *regs) +{ + uint8 save_mcr, status1; + + save_mcr = R_REG(®s[UART_MCR]); + W_REG(®s[UART_MCR], UART_MCR_LOOP | 0x0a); + status1 = R_REG(®s[UART_MSR]) & 0xf0; + W_REG(®s[UART_MCR], save_mcr); + + return (status1 == 0x90); +} + +/* + * Initializes UART access. The callback function will be called once + * per found UART. +*/ +void +sb_serial_init(void *sbh, void (*add)(void *regs, uint irq, uint baud_base, uint reg_shift)) +{ + void *regs; + ulong base; + uint irq; + int i, n; + + if ((regs = sb_setcore(sbh, SB_EXTIF, 0))) { + extifregs_t *eir = (extifregs_t *) regs; + sbconfig_t *sb; + + /* Determine external UART register base */ + sb = (sbconfig_t *)((ulong) eir + SBCONFIGOFF); + base = EXTIF_CFGIF_BASE(sb_base(R_REG(&sb->sbadmatch1))); + + /* Determine IRQ */ + irq = sb_irq(sbh); + + /* Disable GPIO interrupt initially */ + W_REG(&eir->gpiointpolarity, 0); + W_REG(&eir->gpiointmask, 0); + + /* Search for external UARTs */ + n = 2; + for (i = 0; i < 2; i++) { + regs = (void *) REG_MAP(base + (i * 8), 8); + if (serial_exists(regs)) { + /* Set GPIO 1 to be the external UART IRQ */ + W_REG(&eir->gpiointmask, 2); + if (add) + add(regs, irq, 13500000, 0); + } + } + + /* Add internal UART if enabled */ + if (R_REG(&eir->corecontrol) & CC_UE) + if (add) + add((void *) &eir->uartdata, irq, sb_clock(sbh), 2); + } else if ((regs = sb_setcore(sbh, SB_CC, 0))) { + chipcregs_t *cc = (chipcregs_t *) regs; + uint32 rev, cap, pll, baud_base, div; + + /* Determine core revision and capabilities */ + rev = sb_corerev(sbh); + cap = R_REG(&cc->capabilities); + pll = cap & CAP_PLL_MASK; + + /* Determine IRQ */ + irq = sb_irq(sbh); + + if (pll == PLL_TYPE1) { + /* PLL clock */ + baud_base = sb_clock_rate(pll, + R_REG(&cc->clockcontrol_n), + R_REG(&cc->clockcontrol_m2)); + div = 1; + } else if (rev >= 3) { + /* Internal backplane clock */ + baud_base = sb_clock_rate(pll, + R_REG(&cc->clockcontrol_n), + R_REG(&cc->clockcontrol_sb)); + div = 2; /* Minimum divisor */ + W_REG(&cc->clkdiv, ((R_REG(&cc->clkdiv) & ~CLKD_UART) | div)); + } else { + /* Fixed internal backplane clock */ + baud_base = 88000000; + div = 48; + } + + /* Clock source depends on strapping if UartClkOverride is unset */ + if ((rev > 0) && ((R_REG(&cc->corecontrol) & CC_UARTCLKO) == 0)) { + if ((cap & CAP_UCLKSEL) == CAP_UINTCLK) { + /* Internal divided backplane clock */ + baud_base /= div; + } else { + /* Assume external clock of 1.8432 MHz */ + baud_base = 1843200; + } + } + + /* Add internal UARTs */ + n = cap & CAP_UARTS_MASK; + for (i = 0; i < n; i++) { + /* Register offset changed after revision 0 */ + if (rev) + regs = (void *)((ulong) &cc->uart0data + (i * 256)); + else + regs = (void *)((ulong) &cc->uart0data + (i * 8)); + + if (add) + add(regs, irq, baud_base, 0); + } + } +} + +/* Returns the SB interrupt flag of the current core. */ +uint32 +sb_flag(void *sbh) +{ + void *regs; + sbconfig_t *sb; + + regs = sb_coreregs(sbh); + sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF); + + return (R_REG(&sb->sbtpsflag) & SBTPS_NUM0_MASK); +} + +static const uint32 sbips_int_mask[] = { + 0, + SBIPS_INT1_MASK, + SBIPS_INT2_MASK, + SBIPS_INT3_MASK, + SBIPS_INT4_MASK +}; + +static const uint32 sbips_int_shift[] = { + 0, + 0, + SBIPS_INT2_SHIFT, + SBIPS_INT3_SHIFT, + SBIPS_INT4_SHIFT +}; + +/* + * Returns the MIPS IRQ assignment of the current core. If unassigned, + * 0 is returned. + */ +uint +sb_irq(void *sbh) +{ + uint idx; + void *regs; + sbconfig_t *sb; + uint32 flag, sbipsflag; + uint irq = 0; + + flag = sb_flag(sbh); + + idx = sb_coreidx(sbh); + + if ((regs = sb_setcore(sbh, SB_MIPS, 0)) || + (regs = sb_setcore(sbh, SB_MIPS33, 0))) { + sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF); + + /* sbipsflag specifies which core is routed to interrupts 1 to 4 */ + sbipsflag = R_REG(&sb->sbipsflag); + for (irq = 1; irq <= 4; irq++) { + if (((sbipsflag & sbips_int_mask[irq]) >> sbips_int_shift[irq]) == flag) + break; + } + if (irq == 5) + irq = 0; + } + + sb_setcoreidx(sbh, idx); + + return irq; +} + +/* Clears the specified MIPS IRQ. */ +static void +sb_clearirq(void *sbh, uint irq) +{ + void *regs; + sbconfig_t *sb; + + if (!(regs = sb_setcore(sbh, SB_MIPS, 0)) && + !(regs = sb_setcore(sbh, SB_MIPS33, 0))) + ASSERT(regs); + sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF); + + if (irq == 0) + W_REG(&sb->sbintvec, 0); + else + OR_REG(&sb->sbipsflag, sbips_int_mask[irq]); +} + +/* + * Assigns the specified MIPS IRQ to the specified core. Shared MIPS + * IRQ 0 may be assigned more than once. + */ +static void +sb_setirq(void *sbh, uint irq, uint coreid, uint coreunit) +{ + void *regs; + sbconfig_t *sb; + uint32 flag; + + regs = sb_setcore(sbh, coreid, coreunit); + ASSERT(regs); + flag = sb_flag(sbh); + + if (!(regs = sb_setcore(sbh, SB_MIPS, 0)) && + !(regs = sb_setcore(sbh, SB_MIPS33, 0))) + ASSERT(regs); + sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF); + + if (irq == 0) + OR_REG(&sb->sbintvec, 1 << flag); + else { + flag <<= sbips_int_shift[irq]; + ASSERT(!(flag & ~sbips_int_mask[irq])); + flag |= R_REG(&sb->sbipsflag) & ~sbips_int_mask[irq]; + W_REG(&sb->sbipsflag, flag); + } +} + +/* + * Initializes clocks and interrupts. SB and NVRAM access must be + * initialized prior to calling. + */ +void +sb_mips_init(void *sbh) +{ + ulong hz, ns, tmp; + extifregs_t *eir; + chipcregs_t *cc; + char *value; + uint irq; + + /* Figure out current SB clock speed */ + if ((hz = sb_clock(sbh)) == 0) + hz = 100000000; + ns = 1000000000 / hz; + + /* Setup external interface timing */ + if ((eir = sb_setcore(sbh, SB_EXTIF, 0))) { + /* Initialize extif so we can get to the LEDs and external UART */ + W_REG(&eir->prog_config, CF_EN); + + /* Set timing for the flash */ + tmp = CEIL(10, ns) << FW_W3_SHIFT; /* W3 = 10nS */ + tmp = tmp | (CEIL(40, ns) << FW_W1_SHIFT); /* W1 = 40nS */ + tmp = tmp | CEIL(120, ns); /* W0 = 120nS */ + W_REG(&eir->prog_waitcount, tmp); /* 0x01020a0c for a 100Mhz clock */ + + /* Set programmable interface timing for external uart */ + tmp = CEIL(10, ns) << FW_W3_SHIFT; /* W3 = 10nS */ + tmp = tmp | (CEIL(20, ns) << FW_W2_SHIFT); /* W2 = 20nS */ + tmp = tmp | (CEIL(100, ns) << FW_W1_SHIFT); /* W1 = 100nS */ + tmp = tmp | CEIL(120, ns); /* W0 = 120nS */ + W_REG(&eir->prog_waitcount, tmp); /* 0x01020a0c for a 100Mhz clock */ + } else if ((cc = sb_setcore(sbh, SB_CC, 0))) { + /* Set timing for the flash */ + tmp = CEIL(10, ns) << FW_W3_SHIFT; /* W3 = 10nS */ + tmp |= CEIL(10, ns) << FW_W1_SHIFT; /* W1 = 10nS */ + tmp |= CEIL(120, ns); /* W0 = 120nS */ + W_REG(&cc->flash_waitcount, tmp); + + W_REG(&cc->pcmcia_memwait, tmp); + } + + /* Chip specific initialization */ + switch (sb_chip(sbh)) { + case BCM4710_DEVICE_ID: + /* Clear interrupt map */ + for (irq = 0; irq <= 4; irq++) + sb_clearirq(sbh, irq); + sb_setirq(sbh, 0, SB_CODEC, 0); + sb_setirq(sbh, 0, SB_EXTIF, 0); + sb_setirq(sbh, 2, SB_ENET, 1); + sb_setirq(sbh, 3, SB_ILINE20, 0); + sb_setirq(sbh, 4, SB_PCI, 0); + ASSERT(eir); + value = nvram_get("et0phyaddr"); + if (value && !strcmp(value, "31")) { + /* Enable internal UART */ + W_REG(&eir->corecontrol, CC_UE); + /* Give USB its own interrupt */ + sb_setirq(sbh, 1, SB_USB, 0); + } else { + /* Disable internal UART */ + W_REG(&eir->corecontrol, 0); + /* Give Ethernet its own interrupt */ + sb_setirq(sbh, 1, SB_ENET, 0); + sb_setirq(sbh, 0, SB_USB, 0); + } + break; + case BCM4310_DEVICE_ID: + MTC0(C0_BROADCOM, 0, MFC0(C0_BROADCOM, 0) & ~(1 << 22)); + break; + } +} + +uint32 +sb_mips_clock(void *sbh) +{ + extifregs_t *eir; + chipcregs_t *cc; + uint32 n, m; + uint idx; + uint32 pll_type, rate = 0; + + /* get index of the current core */ + idx = sb_coreidx(sbh); + pll_type = PLL_TYPE1; + + /* switch to extif or chipc core */ + if ((eir = (extifregs_t *) sb_setcore(sbh, SB_EXTIF, 0))) { + n = R_REG(&eir->clockcontrol_n); + m = R_REG(&eir->clockcontrol_sb); + } else if ((cc = (chipcregs_t *) sb_setcore(sbh, SB_CC, 0))) { + pll_type = R_REG(&cc->capabilities) & CAP_PLL_MASK; + n = R_REG(&cc->clockcontrol_n); + if ((pll_type == PLL_TYPE2) || (pll_type == PLL_TYPE4)) + m = R_REG(&cc->clockcontrol_mips); + else if (pll_type == PLL_TYPE3) { + rate = 200000000; + goto out; + } else + m = R_REG(&cc->clockcontrol_sb); + } else + goto out; + + /* calculate rate */ + rate = sb_clock_rate(pll_type, n, m); + +out: + /* switch back to previous core */ + sb_setcoreidx(sbh, idx); + + return rate; +} + +static void +icache_probe(int *size, int *lsize) +{ + uint32 config1; + uint sets, ways; + + config1 = MFC0(C0_CONFIG, 1); + + /* Instruction Cache Size = Associativity * Line Size * Sets Per Way */ + if ((*lsize = ((config1 >> 19) & 7))) + *lsize = 2 << *lsize; + sets = 64 << ((config1 >> 22) & 7); + ways = 1 + ((config1 >> 16) & 7); + *size = *lsize * sets * ways; +} + +#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4) + +static void +handler(void) +{ + /* Step 11 */ + __asm__ ( + ".set\tmips32\n\t" + "ssnop\n\t" + "ssnop\n\t" + /* Disable interrupts */ + /* MTC0(C0_STATUS, 0, MFC0(C0_STATUS, 0) & ~(ALLINTS | STO_IE)); */ + "mfc0 $15, $12\n\t" + "and $15, $15, -31746\n\t" + "mtc0 $15, $12\n\t" + "eret\n\t" + "nop\n\t" + "nop\n\t" + ".set\tmips0" + ); +} + +/* The following MUST come right after handler() */ +static void +afterhandler(void) +{ +} + +/* + * Set the MIPS, backplane and PCI clocks as closely as possible. + */ +bool +sb_mips_setclock(void *sbh, uint32 mipsclock, uint32 sbclock, uint32 pciclock) +{ + extifregs_t *eir = NULL; + chipcregs_t *cc = NULL; + mipsregs_t *mipsr = NULL; + volatile uint32 *clockcontrol_n, *clockcontrol_sb, *clockcontrol_pci; + uint32 orig_n, orig_sb, orig_pci, orig_m2, orig_mips, orig_ratio_parm, new_ratio; + uint32 pll_type, sync_mode; + uint idx, i; + typedef struct { + uint32 mipsclock; + uint16 n; + uint32 sb; + uint32 pci33; + uint32 pci25; + } n3m_table_t; + static n3m_table_t type1_table[] = { + { 96000000, 0x0303, 0x04020011, 0x11030011, 0x11050011 }, /* 96.000 32.000 24.000 */ + { 100000000, 0x0009, 0x04020011, 0x11030011, 0x11050011 }, /* 100.000 33.333 25.000 */ + { 104000000, 0x0802, 0x04020011, 0x11050009, 0x11090009 }, /* 104.000 31.200 24.960 */ + { 108000000, 0x0403, 0x04020011, 0x11050009, 0x02000802 }, /* 108.000 32.400 24.923 */ + { 112000000, 0x0205, 0x04020011, 0x11030021, 0x02000403 }, /* 112.000 32.000 24.889 */ + { 115200000, 0x0303, 0x04020009, 0x11030011, 0x11050011 }, /* 115.200 32.000 24.000 */ + { 120000000, 0x0011, 0x04020011, 0x11050011, 0x11090011 }, /* 120.000 30.000 24.000 */ + { 124800000, 0x0802, 0x04020009, 0x11050009, 0x11090009 }, /* 124.800 31.200 24.960 */ + { 128000000, 0x0305, 0x04020011, 0x11050011, 0x02000305 }, /* 128.000 32.000 24.000 */ + { 132000000, 0x0603, 0x04020011, 0x11050011, 0x02000305 }, /* 132.000 33.000 24.750 */ + { 136000000, 0x0c02, 0x04020011, 0x11090009, 0x02000603 }, /* 136.000 32.640 24.727 */ + { 140000000, 0x0021, 0x04020011, 0x11050021, 0x02000c02 }, /* 140.000 30.000 24.706 */ + { 144000000, 0x0405, 0x04020011, 0x01020202, 0x11090021 }, /* 144.000 30.857 24.686 */ + { 150857142, 0x0605, 0x04020021, 0x02000305, 0x02000605 }, /* 150.857 33.000 24.000 */ + { 152000000, 0x0e02, 0x04020011, 0x11050021, 0x02000e02 }, /* 152.000 32.571 24.000 */ + { 156000000, 0x0802, 0x04020005, 0x11050009, 0x11090009 }, /* 156.000 31.200 24.960 */ + { 160000000, 0x0309, 0x04020011, 0x11090011, 0x02000309 }, /* 160.000 32.000 24.000 */ + { 163200000, 0x0c02, 0x04020009, 0x11090009, 0x02000603 }, /* 163.200 32.640 24.727 */ + { 168000000, 0x0205, 0x04020005, 0x11030021, 0x02000403 }, /* 168.000 32.000 24.889 */ + { 176000000, 0x0602, 0x04020003, 0x11050005, 0x02000602 }, /* 176.000 33.000 24.000 */ + }; + typedef struct { + uint32 mipsclock; + uint32 sbclock; + uint16 n; + uint32 sb; + uint32 pci33; + uint32 m2; + uint32 m3; + uint32 ratio; + uint32 ratio_parm; + } n4m_table_t; + + static n4m_table_t type2_table[] = { + { 180000000, 80000000, 0x0403, 0x01010000, 0x01020300, 0x01020600, 0x05000100, 0x94, 0x012a0115 }, + { 180000000, 90000000, 0x0403, 0x01000100, 0x01020300, 0x01000100, 0x05000100, 0x21, 0x0aaa0555 }, + { 200000000, 100000000, 0x0303, 0x01000000, 0x01000600, 0x01000000, 0x05000000, 0x21, 0x0aaa0555 }, + { 211200000, 105600000, 0x0902, 0x01000200, 0x01030400, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 }, + { 220800000, 110400000, 0x1500, 0x01000200, 0x01030400, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 }, + { 230400000, 115200000, 0x0604, 0x01000200, 0x01020600, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 }, + { 234000000, 104000000, 0x0b01, 0x01010000, 0x01010700, 0x01020600, 0x05000100, 0x94, 0x012a0115 }, + { 240000000, 120000000, 0x0803, 0x01000200, 0x01020600, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 }, + { 252000000, 126000000, 0x0504, 0x01000100, 0x01020500, 0x01000100, 0x05000100, 0x21, 0x0aaa0555 }, + { 264000000, 132000000, 0x0903, 0x01000200, 0x01020700, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 }, + { 270000000, 120000000, 0x0703, 0x01010000, 0x01030400, 0x01020600, 0x05000100, 0x94, 0x012a0115 }, + { 276000000, 122666666, 0x1500, 0x01010000, 0x01030400, 0x01020600, 0x05000100, 0x94, 0x012a0115 }, + { 280000000, 140000000, 0x0503, 0x01000000, 0x01010600, 0x01000000, 0x05000000, 0x21, 0x0aaa0555 }, + { 288000000, 128000000, 0x0604, 0x01010000, 0x01030400, 0x01020600, 0x05000100, 0x94, 0x012a0115 }, + { 288000000, 144000000, 0x0404, 0x01000000, 0x01010600, 0x01000000, 0x05000000, 0x21, 0x0aaa0555 }, + { 300000000, 133333333, 0x0803, 0x01010000, 0x01020600, 0x01020600, 0x05000100, 0x94, 0x012a0115 }, + { 300000000, 150000000, 0x0803, 0x01000100, 0x01020600, 0x01000100, 0x05000100, 0x21, 0x0aaa0555 } + }; + + static n4m_table_t type4_table[] = { + { 192000000, 96000000, 0x0702, 0x04020011, 0x11030011, 0x04020011, 0x04020003, 0x21, 0x0aaa0555 }, + { 200000000, 100000000, 0x0009, 0x04020011, 0x11030011, 0x04020011, 0x04020003, 0x21, 0x0aaa0555 }, + { 216000000, 108000000, 0x0111, 0x11020005, 0x01030303, 0x11020005, 0x04000005, 0x21, 0x0aaa0555 }, + { 228000000, 101333333, 0x0e02, 0x11030003, 0x11210005, 0x11030305, 0x04000005, 0x94, 0x012a00a9 }, + { 228000000, 114000000, 0x0e02, 0x11020005, 0x11210005, 0x11020005, 0x04000005, 0x21, 0x0aaa0555 }, + { 240000000, 120000000, 0x0109, 0x11030002, 0x01050203, 0x11030002, 0x04000003, 0x21, 0x0aaa0555 }, + { 252000000, 126000000, 0x0203, 0x04000005, 0x11050005, 0x04000005, 0x04000002, 0x21, 0x0aaa0555 }, + { 264000000, 132000000, 0x0602, 0x04000005, 0x11050005, 0x04000005, 0x04000002, 0x21, 0x0aaa0555 }, + { 272000000, 116571428, 0x0c02, 0x04000021, 0x02000909, 0x02000221, 0x04000003, 0x73, 0x254a14a9 }, + { 280000000, 120000000, 0x0209, 0x04000021, 0x01030303, 0x02000221, 0x04000003, 0x73, 0x254a14a9 }, + { 288000000, 123428571, 0x0111, 0x04000021, 0x01030303, 0x02000221, 0x04000003, 0x73, 0x254a14a9 }, + { 300000000, 120000000, 0x0009, 0x04000009, 0x01030203, 0x02000902, 0x04000002, 0x52, 0x02520129 } + }; + uint icache_size, ic_lsize; + ulong start, end, dst; + bool ret = FALSE; + + /* get index of the current core */ + idx = sb_coreidx(sbh); + + /* switch to extif or chipc core */ + if ((eir = (extifregs_t *) sb_setcore(sbh, SB_EXTIF, 0))) { + pll_type = PLL_TYPE1; + clockcontrol_n = &eir->clockcontrol_n; + clockcontrol_sb = &eir->clockcontrol_sb; + clockcontrol_pci = &eir->clockcontrol_pci; + } else if ((cc = (chipcregs_t *) sb_setcore(sbh, SB_CC, 0))) { + pll_type = R_REG(&cc->capabilities) & CAP_PLL_MASK; + clockcontrol_n = &cc->clockcontrol_n; + clockcontrol_sb = &cc->clockcontrol_sb; + clockcontrol_pci = &cc->clockcontrol_pci; + } else + goto done; + + /* Store the current clock register values */ + orig_n = R_REG(clockcontrol_n); + orig_sb = R_REG(clockcontrol_sb); + orig_pci = R_REG(clockcontrol_pci); + + if (pll_type == PLL_TYPE1) { + /* Keep the current PCI clock if not specified */ + if (pciclock == 0) { + pciclock = sb_clock_rate(pll_type, R_REG(clockcontrol_n), R_REG(clockcontrol_pci)); + pciclock = (pciclock <= 25000000) ? 25000000 : 33000000; + } + + /* Search for the closest MIPS clock less than or equal to a preferred value */ + for (i = 0; i < ARRAYSIZE(type1_table); i++) { + ASSERT(type1_table[i].mipsclock == + sb_clock_rate(pll_type, type1_table[i].n, type1_table[i].sb)); + if (type1_table[i].mipsclock > mipsclock) + break; + } + if (i == 0) { + ret = FALSE; + goto done; + } else { + ret = TRUE; + i--; + } + ASSERT(type1_table[i].mipsclock <= mipsclock); + + /* No PLL change */ + if ((orig_n == type1_table[i].n) && + (orig_sb == type1_table[i].sb) && + (orig_pci == type1_table[i].pci33)) + goto done; + + /* Set the PLL controls */ + W_REG(clockcontrol_n, type1_table[i].n); + W_REG(clockcontrol_sb, type1_table[i].sb); + if (pciclock == 25000000) + W_REG(clockcontrol_pci, type1_table[i].pci25); + else + W_REG(clockcontrol_pci, type1_table[i].pci33); + + /* Reset */ + sb_watchdog(sbh, 1); + while (1); + } else if ((pll_type == PLL_TYPE2) || (pll_type == PLL_TYPE4)) { + n4m_table_t *table = (pll_type == PLL_TYPE2) ? type2_table : type4_table; + uint tabsz = (pll_type == PLL_TYPE2) ? ARRAYSIZE(type2_table) : ARRAYSIZE(type4_table); + + ASSERT(cc); + + /* Store the current clock register values */ + orig_m2 = R_REG(&cc->clockcontrol_m2); + orig_mips = R_REG(&cc->clockcontrol_mips); + orig_ratio_parm = 0; + + /* Look up current ratio */ + for (i = 0; i < tabsz; i++) { + if ((orig_n == table[i].n) && + (orig_sb == table[i].sb) && + (orig_pci == table[i].pci33) && + (orig_m2 == table[i].m2) && + (orig_mips == table[i].m3)) { + orig_ratio_parm = table[i].ratio_parm; + break; + } + } + + /* Search for the closest MIPS clock greater or equal to a preferred value */ + for (i = 0; i < tabsz; i++) { + ASSERT(table[i].mipsclock == + sb_clock_rate(pll_type, table[i].n, table[i].m3)); + if ((mipsclock <= table[i].mipsclock) && + ((sbclock == 0) || (sbclock <= table[i].sbclock))) + break; + } + if (i == tabsz) { + ret = FALSE; + goto done; + } else { + ret = TRUE; + } + + /* No PLL change */ + if ((orig_n == table[i].n) && + (orig_sb == table[i].sb) && + (orig_pci == table[i].pci33) && + (orig_m2 == table[i].m2) && + (orig_mips == table[i].m3)) + goto done; + + /* Set the PLL controls */ + W_REG(clockcontrol_n, table[i].n); + W_REG(clockcontrol_sb, table[i].sb); + W_REG(clockcontrol_pci, table[i].pci33); + W_REG(&cc->clockcontrol_m2, table[i].m2); + W_REG(&cc->clockcontrol_mips, table[i].m3); + + /* No ratio change */ + if (orig_ratio_parm == table[i].ratio_parm) + goto end_fill; + + new_ratio = table[i].ratio_parm; + + icache_probe(&icache_size, &ic_lsize); + + /* Preload the code into the cache */ + start = ((ulong) &&start_fill) & ~(ic_lsize - 1); + end = ((ulong) &&end_fill + (ic_lsize - 1)) & ~(ic_lsize - 1); + while (start < end) { + cache_unroll(start, Fill_I); + start += ic_lsize; + } + + /* Copy the handler */ + start = (ulong) &handler; + end = (ulong) &afterhandler; + dst = KSEG1ADDR(0x180); + for (i = 0; i < (end - start); i += 4) + *((ulong *)(dst + i)) = *((ulong *)(start + i)); + + /* Preload handler into the cache one line at a time */ + for (i = 0; i < (end - start); i += 4) + cache_unroll(dst + i, Fill_I); + + /* Clear BEV bit */ + MTC0(C0_STATUS, 0, MFC0(C0_STATUS, 0) & ~ST0_BEV); + + /* Enable interrupts */ + MTC0(C0_STATUS, 0, MFC0(C0_STATUS, 0) | (ALLINTS | ST0_IE)); + + /* Enable MIPS timer interrupt */ + if (!(mipsr = sb_setcore(sbh, SB_MIPS, 0)) && + !(mipsr = sb_setcore(sbh, SB_MIPS33, 0))) + ASSERT(mipsr); + W_REG(&mipsr->intmask, 1); + + start_fill: + /* step 1, set clock ratios */ + MTC0(C0_BROADCOM, 3, new_ratio); + MTC0(C0_BROADCOM, 1, 8); + + /* step 2: program timer intr */ + W_REG(&mipsr->timer, 100); + (void) R_REG(&mipsr->timer); + + /* step 3, switch to async */ + sync_mode = MFC0(C0_BROADCOM, 4); + MTC0(C0_BROADCOM, 4, 1 << 22); + + /* step 4, set cfg active */ + MTC0(C0_BROADCOM, 2, 0x9); + + + /* steps 5 & 6 */ + __asm__ __volatile__ ( + ".set\tmips3\n\t" + "wait\n\t" + ".set\tmips0" + ); + + /* step 7, clear cfg_active */ + MTC0(C0_BROADCOM, 2, 0); + + /* Additional Step: set back to orig sync mode */ + MTC0(C0_BROADCOM, 4, sync_mode); + + /* step 8, fake soft reset */ + MTC0(C0_BROADCOM, 5, MFC0(C0_BROADCOM, 5) | 4); + + end_fill: + /* step 9 set watchdog timer */ + sb_watchdog(sbh, 20); + (void) R_REG(&cc->chipid); + + /* step 11 */ + __asm__ __volatile__ ( + ".set\tmips3\n\t" + "sync\n\t" + "wait\n\t" + ".set\tmips0" + ); + while (1); + } + +done: + /* switch back to previous core */ + sb_setcoreidx(sbh, idx); + + return ret; +} + + +/* returns the ncdl value to be programmed into sdram_ncdl for calibration */ +uint32 +sb_memc_get_ncdl(void *sbh) +{ + sbmemcregs_t *memc; + uint32 ret = 0; + uint32 config, rd, wr, misc, dqsg, cd, sm, sd; + uint idx, rev; + + idx = sb_coreidx(sbh); + + memc = (sbmemcregs_t *)sb_setcore(sbh, SB_MEMC, 0); + if (memc == 0) + goto out; + + rev = sb_corerev(sbh); + + config = R_REG(&memc->config); + wr = R_REG(&memc->wrncdlcor); + rd = R_REG(&memc->rdncdlcor); + misc = R_REG(&memc->miscdlyctl); + dqsg = R_REG(&memc->dqsgatencdl); + + rd &= MEMC_RDNCDLCOR_RD_MASK; + wr &= MEMC_WRNCDLCOR_WR_MASK; + dqsg &= MEMC_DQSGATENCDL_G_MASK; + + if (config & MEMC_CONFIG_DDR) { + ret = (wr << 16) | (rd << 8) | dqsg; + } else { + if (rev > 0) + cd = rd; + else + cd = (rd == MEMC_CD_THRESHOLD) ? rd : (wr + MEMC_CD_THRESHOLD); + sm = (misc & MEMC_MISC_SM_MASK) >> MEMC_MISC_SM_SHIFT; + sd = (misc & MEMC_MISC_SD_MASK) >> MEMC_MISC_SD_SHIFT; + ret = (sm << 16) | (sd << 8) | cd; + } + +out: + /* switch back to previous core */ + sb_setcoreidx(sbh, idx); + + return ret; +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/sbpci.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/sbpci.c new file mode 100644 index 0000000000..b3469134eb --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/sbpci.c @@ -0,0 +1,563 @@ +/* + * Low-Level PCI and SB support for BCM47xx + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <typedefs.h> +#include <pcicfg.h> +#include <bcmdevs.h> +#include <sbconfig.h> +#include <sbpci.h> +#include <osl.h> +#include <bcmendian.h> +#include <bcmutils.h> +#include <sbutils.h> +#include <bcmnvram.h> +#include <hndmips.h> + +/* Can free sbpci_init() memory after boot */ +#ifndef linux +#define __init +#endif + +/* Emulated configuration space */ +static pci_config_regs sb_config_regs[SB_MAXCORES]; + +/* Banned cores */ +static uint16 pci_ban[32] = { 0 }; +static uint pci_banned = 0; + +/* CardBus mode */ +static bool cardbus = FALSE; + +/* Disable PCI host core */ +static bool pci_disabled = FALSE; + +/* + * Functions for accessing external PCI configuration space + */ + +/* Assume one-hot slot wiring */ +#define PCI_SLOT_MAX 16 + +static uint32 +config_cmd(void *sbh, uint bus, uint dev, uint func, uint off) +{ + uint coreidx; + sbpciregs_t *regs; + uint32 addr = 0; + + /* CardBusMode supports only one device */ + if (cardbus && dev > 1) + return 0; + + coreidx = sb_coreidx(sbh); + regs = (sbpciregs_t *) sb_setcore(sbh, SB_PCI, 0); + + /* Type 0 transaction */ + if (bus == 1) { + /* Skip unwired slots */ + if (dev < PCI_SLOT_MAX) { + /* Slide the PCI window to the appropriate slot */ + W_REG(®s->sbtopci1, SBTOPCI_CFG0 | ((1 << (dev + 16)) & SBTOPCI1_MASK)); + addr = SB_PCI_CFG | ((1 << (dev + 16)) & ~SBTOPCI1_MASK) | + (func << 8) | (off & ~3); + } + } + + /* Type 1 transaction */ + else { + W_REG(®s->sbtopci1, SBTOPCI_CFG1); + addr = SB_PCI_CFG | (bus << 16) | (dev << 11) | (func << 8) | (off & ~3); + } + + sb_setcoreidx(sbh, coreidx); + + return addr; +} + +static int +extpci_read_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len) +{ + uint32 addr, *reg = NULL, val; + int ret = 0; + + if (pci_disabled || + !(addr = config_cmd(sbh, bus, dev, func, off)) || + !(reg = (uint32 *) REG_MAP(addr, len)) || + BUSPROBE(val, reg)) + val = 0xffffffff; + + val >>= 8 * (off & 3); + if (len == 4) + *((uint32 *) buf) = val; + else if (len == 2) + *((uint16 *) buf) = (uint16) val; + else if (len == 1) + *((uint8 *) buf) = (uint8) val; + else + ret = -1; + + if (reg) + REG_UNMAP(reg); + + return ret; +} + +static int +extpci_write_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len) +{ + uint32 addr, *reg = NULL, val; + int ret = 0; + + if (pci_disabled || + !(addr = config_cmd(sbh, bus, dev, func, off)) || + !(reg = (uint32 *) REG_MAP(addr, len)) || + BUSPROBE(val, reg)) + goto done; + + if (len == 4) + val = *((uint32 *) buf); + else if (len == 2) { + val &= ~(0xffff << (8 * (off & 3))); + val |= *((uint16 *) buf) << (8 * (off & 3)); + } else if (len == 1) { + val &= ~(0xff << (8 * (off & 3))); + val |= *((uint8 *) buf) << (8 * (off & 3)); + } else + ret = -1; + + W_REG(reg, val); + + done: + if (reg) + REG_UNMAP(reg); + + return ret; +} + +/* + * Functions for accessing translated SB configuration space + */ + +static int +sb_read_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len) +{ + pci_config_regs *cfg; + + if (dev >= SB_MAXCORES || (off + len) > sizeof(pci_config_regs)) + return -1; + cfg = &sb_config_regs[dev]; + + ASSERT(ISALIGNED(off, len)); + ASSERT(ISALIGNED(buf, len)); + + if (len == 4) + *((uint32 *) buf) = ltoh32(*((uint32 *)((ulong) cfg + off))); + else if (len == 2) + *((uint16 *) buf) = ltoh16(*((uint16 *)((ulong) cfg + off))); + else if (len == 1) + *((uint8 *) buf) = *((uint8 *)((ulong) cfg + off)); + else + return -1; + + return 0; +} + +static int +sb_write_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len) +{ + uint coreidx, n; + void *regs; + sbconfig_t *sb; + pci_config_regs *cfg; + + if (dev >= SB_MAXCORES || (off + len) > sizeof(pci_config_regs)) + return -1; + cfg = &sb_config_regs[dev]; + + ASSERT(ISALIGNED(off, len)); + ASSERT(ISALIGNED(buf, len)); + + /* Emulate BAR sizing */ + if (off >= OFFSETOF(pci_config_regs, base[0]) && off <= OFFSETOF(pci_config_regs, base[3]) && + len == 4 && *((uint32 *) buf) == ~0) { + coreidx = sb_coreidx(sbh); + if ((regs = sb_setcoreidx(sbh, dev))) { + sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF); + /* Highest numbered address match register */ + n = (R_REG(&sb->sbidlow) & SBIDL_AR_MASK) >> SBIDL_AR_SHIFT; + if (off == OFFSETOF(pci_config_regs, base[0])) + cfg->base[0] = ~(sb_size(R_REG(&sb->sbadmatch0)) - 1); + else if (off == OFFSETOF(pci_config_regs, base[1]) && n >= 1) + cfg->base[1] = ~(sb_size(R_REG(&sb->sbadmatch1)) - 1); + else if (off == OFFSETOF(pci_config_regs, base[2]) && n >= 2) + cfg->base[2] = ~(sb_size(R_REG(&sb->sbadmatch2)) - 1); + else if (off == OFFSETOF(pci_config_regs, base[3]) && n >= 3) + cfg->base[3] = ~(sb_size(R_REG(&sb->sbadmatch3)) - 1); + } + sb_setcoreidx(sbh, coreidx); + return 0; + } + + if (len == 4) + *((uint32 *)((ulong) cfg + off)) = htol32(*((uint32 *) buf)); + else if (len == 2) + *((uint16 *)((ulong) cfg + off)) = htol16(*((uint16 *) buf)); + else if (len == 1) + *((uint8 *)((ulong) cfg + off)) = *((uint8 *) buf); + else + return -1; + + return 0; +} + +int +sbpci_read_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len) +{ + if (bus == 0) + return sb_read_config(sbh, bus, dev, func, off, buf, len); + else + return extpci_read_config(sbh, bus, dev, func, off, buf, len); +} + +int +sbpci_write_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len) +{ + if (bus == 0) + return sb_write_config(sbh, bus, dev, func, off, buf, len); + else + return extpci_write_config(sbh, bus, dev, func, off, buf, len); +} + +void +sbpci_ban(uint16 core) +{ + if (pci_banned < ARRAYSIZE(pci_ban)) + pci_ban[pci_banned++] = core; +} +//#define CT4712_WR 1 /* Workaround for 4712 */ + +int __init +sbpci_init(void *sbh) +{ + uint chip, chiprev, chippkg, coreidx, host, i; + uint32 boardflags; + sbpciregs_t *pci; + sbconfig_t *sb; + pci_config_regs *cfg; + void *regs; + char varname[8]; + int CT4712_WR; + uint wlidx = 0; + uint16 vendor, core; + uint8 class, subclass, progif; + uint32 val; + uint32 sbips_int_mask[] = { 0, SBIPS_INT1_MASK, SBIPS_INT2_MASK, SBIPS_INT3_MASK, SBIPS_INT4_MASK }; + uint32 sbips_int_shift[] = { 0, 0, SBIPS_INT2_SHIFT, SBIPS_INT3_SHIFT, SBIPS_INT4_SHIFT }; + + chip = sb_chip(sbh); + chiprev = sb_chiprev(sbh); + chippkg = sb_chippkg(sbh); + coreidx = sb_coreidx(sbh); + + if (!(pci = (sbpciregs_t *) sb_setcore(sbh, SB_PCI, 0))) + return -1; + sb_core_reset(sbh, 0); + + /* In some board, */ + if(nvram_match("boardtype", "bcm94710dev")) + CT4712_WR = 0; + else + CT4712_WR = 1; + + boardflags = (uint32) getintvar(NULL, "boardflags"); + + if ((chip == BCM4310_DEVICE_ID) && (chiprev == 0)) + pci_disabled = TRUE; + + /* + * The 200-pin BCM4712 package does not bond out PCI. Even when + * PCI is bonded out, some boards may leave the pins + * floating. + */ + if (((chip == BCM4712_DEVICE_ID) && (chippkg == BCM4712SMALL_PKG_ID)) || + (boardflags & BFL_NOPCI) || CT4712_WR) + pci_disabled = TRUE; + + /* + * If the PCI core should not be touched (disabled, not bonded + * out, or pins floating), do not even attempt to access core + * registers. Otherwise, try to determine if it is in host + * mode. + */ + if (pci_disabled) + host = 0; + else + host = !BUSPROBE(val, &pci->control); + + if (!host) { + /* Disable PCI interrupts in client mode */ + sb = (sbconfig_t *)((ulong) pci + SBCONFIGOFF); + W_REG(&sb->sbintvec, 0); + + /* Disable the PCI bridge in client mode */ + sbpci_ban(SB_PCI); + printf("PCI: Disabled\n"); + } else { + /* Reset the external PCI bus and enable the clock */ + W_REG(&pci->control, 0x5); /* enable the tristate drivers */ + W_REG(&pci->control, 0xd); /* enable the PCI clock */ + OSL_DELAY(150); /* delay > 100 us */ + W_REG(&pci->control, 0xf); /* deassert PCI reset */ + W_REG(&pci->arbcontrol, PCI_INT_ARB); /* use internal arbiter */ + OSL_DELAY(1); /* delay 1 us */ + + /* Enable CardBusMode */ + cardbus = nvram_match("cardbus", "1"); + if (cardbus) { + printf("PCI: Enabling CardBus\n"); + /* GPIO 1 resets the CardBus device on bcm94710ap */ + sb_gpioout(sbh, 1, 1); + sb_gpioouten(sbh, 1, 1); + W_REG(&pci->sprom[0], R_REG(&pci->sprom[0]) | 0x400); + } + + /* 64 MB I/O access window */ + W_REG(&pci->sbtopci0, SBTOPCI_IO); + /* 64 MB configuration access window */ + W_REG(&pci->sbtopci1, SBTOPCI_CFG0); + /* 1 GB memory access window */ + W_REG(&pci->sbtopci2, SBTOPCI_MEM | SB_PCI_DMA); + + /* Enable PCI bridge BAR0 prefetch and burst */ + val = 6; + sbpci_write_config(sbh, 1, 0, 0, PCI_CFG_CMD, &val, sizeof(val)); + + /* Enable PCI interrupts */ + W_REG(&pci->intmask, PCI_INTA); + } + + /* Scan the SB bus */ + bzero(sb_config_regs, sizeof(sb_config_regs)); + for (cfg = sb_config_regs; cfg < &sb_config_regs[SB_MAXCORES]; cfg++) { + cfg->vendor = 0xffff; + if (!(regs = sb_setcoreidx(sbh, cfg - sb_config_regs))) + continue; + sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF); + + /* Read ID register and parse vendor and core */ + val = R_REG(&sb->sbidhigh); + vendor = (val & SBIDH_VC_MASK) >> SBIDH_VC_SHIFT; + core = (val & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT; + progif = 0; + + /* Check if this core is banned */ + for (i = 0; i < pci_banned; i++) + if (core == pci_ban[i]) + break; + if (i < pci_banned) + continue; + + /* Known vendor translations */ + switch (vendor) { + case SB_VEND_BCM: + vendor = VENDOR_BROADCOM; + break; + } + + /* Determine class based on known core codes */ + switch (core) { + case SB_ILINE20: + class = PCI_CLASS_NET; + subclass = PCI_NET_ETHER; + core = BCM47XX_ILINE_ID; + break; + case SB_ILINE100: + class = PCI_CLASS_NET; + subclass = PCI_NET_ETHER; + core = BCM4610_ILINE_ID; + break; + case SB_ENET: + class = PCI_CLASS_NET; + subclass = PCI_NET_ETHER; + core = BCM47XX_ENET_ID; + break; + case SB_SDRAM: + case SB_MEMC: + class = PCI_CLASS_MEMORY; + subclass = PCI_MEMORY_RAM; + break; + case SB_PCI: + class = PCI_CLASS_BRIDGE; + subclass = PCI_BRIDGE_PCI; + break; + case SB_MIPS: + case SB_MIPS33: + class = PCI_CLASS_CPU; + subclass = PCI_CPU_MIPS; + break; + case SB_CODEC: + class = PCI_CLASS_COMM; + subclass = PCI_COMM_MODEM; + core = BCM47XX_V90_ID; + break; + case SB_USB: + class = PCI_CLASS_SERIAL; + subclass = PCI_SERIAL_USB; + progif = 0x10; /* OHCI */ + core = BCM47XX_USB_ID; + break; + case SB_USB11H: + class = PCI_CLASS_SERIAL; + subclass = PCI_SERIAL_USB; + progif = 0x10; /* OHCI */ + core = BCM47XX_USBH_ID; + break; + case SB_USB11D: + class = PCI_CLASS_SERIAL; + subclass = PCI_SERIAL_USB; + core = BCM47XX_USBD_ID; + break; + case SB_IPSEC: + class = PCI_CLASS_CRYPT; + subclass = PCI_CRYPT_NETWORK; + core = BCM47XX_IPSEC_ID; + break; + case SB_EXTIF: + case SB_CC: + class = PCI_CLASS_MEMORY; + subclass = PCI_MEMORY_FLASH; + break; + case SB_D11: + class = PCI_CLASS_NET; + subclass = PCI_NET_OTHER; + /* Let an nvram variable override this */ + sprintf(varname, "wl%did", wlidx); + wlidx++; + if ((core = getintvar(NULL, varname)) == 0) { + if (chip == BCM4712_DEVICE_ID) { + if (chippkg == BCM4712SMALL_PKG_ID) + core = BCM4306_D11G_ID; + else + core = BCM4306_D11DUAL_ID; + } else { + /* 4310 */ + core = BCM4310_D11B_ID; + } + } + break; + + default: + class = subclass = progif = 0xff; + break; + } + + /* Supported translations */ + cfg->vendor = htol16(vendor); + cfg->device = htol16(core); + cfg->rev_id = chiprev; + cfg->prog_if = progif; + cfg->sub_class = subclass; + cfg->base_class = class; + cfg->base[0] = htol32(sb_base(R_REG(&sb->sbadmatch0))); + cfg->base[1] = htol32(sb_base(R_REG(&sb->sbadmatch1))); + cfg->base[2] = htol32(sb_base(R_REG(&sb->sbadmatch2))); + cfg->base[3] = htol32(sb_base(R_REG(&sb->sbadmatch3))); + cfg->base[4] = 0; + cfg->base[5] = 0; + if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) + cfg->header_type = PCI_HEADER_BRIDGE; + else + cfg->header_type = PCI_HEADER_NORMAL; + /* Save core interrupt flag */ + cfg->int_pin = R_REG(&sb->sbtpsflag) & SBTPS_NUM0_MASK; + /* Default to MIPS shared interrupt 0 */ + cfg->int_line = 0; + /* MIPS sbipsflag maps core interrupt flags to interrupts 1 through 4 */ + if ((regs = sb_setcore(sbh, SB_MIPS, 0)) || + (regs = sb_setcore(sbh, SB_MIPS33, 0))) { + sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF); + val = R_REG(&sb->sbipsflag); + for (cfg->int_line = 1; cfg->int_line <= 4; cfg->int_line++) { + if (((val & sbips_int_mask[cfg->int_line]) >> sbips_int_shift[cfg->int_line]) == cfg->int_pin) + break; + } + if (cfg->int_line > 4) + cfg->int_line = 0; + } + /* Emulated core */ + *((uint32 *) &cfg->sprom_control) = 0xffffffff; + } + + sb_setcoreidx(sbh, coreidx); + return 0; +} + +void +sbpci_check(void *sbh) +{ + uint coreidx; + sbpciregs_t *pci; + uint32 sbtopci1; + uint32 buf[64], *ptr, i; + ulong pa; + volatile uint j; + + coreidx = sb_coreidx(sbh); + pci = (sbpciregs_t *) sb_setcore(sbh, SB_PCI, 0); + + /* Clear the test array */ + pa = (ulong) DMA_MAP(NULL, buf, sizeof(buf), DMA_RX, NULL); + ptr = (uint32 *) OSL_UNCACHED(&buf[0]); + memset(ptr, 0, sizeof(buf)); + + /* Point PCI window 1 to memory */ + sbtopci1 = R_REG(&pci->sbtopci1); + W_REG(&pci->sbtopci1, SBTOPCI_MEM | (pa & SBTOPCI1_MASK)); + + /* Fill the test array via PCI window 1 */ + ptr = (uint32 *) REG_MAP(SB_PCI_CFG + (pa & ~SBTOPCI1_MASK), sizeof(buf)); + for (i = 0; i < ARRAYSIZE(buf); i++) { + for (j = 0; j < 2; j++); + W_REG(&ptr[i], i); + } + REG_UNMAP(ptr); + + /* Restore PCI window 1 */ + W_REG(&pci->sbtopci1, sbtopci1); + + /* Check the test array */ + DMA_UNMAP(NULL, pa, sizeof(buf), DMA_RX, NULL); + ptr = (uint32 *) OSL_UNCACHED(&buf[0]); + for (i = 0; i < ARRAYSIZE(buf); i++) { + if (ptr[i] != i) + break; + } + + /* Change the clock if the test fails */ + if (i < ARRAYSIZE(buf)) { + uint32 req, cur; + + cur = sb_clock(sbh); + printf("PCI: Test failed at %d MHz\n", (cur + 500000) / 1000000); + for (req = 104000000; req < 176000000; req += 4000000) { + printf("PCI: Resetting to %d MHz\n", (req + 500000) / 1000000); + /* This will only reset if the clocks are valid and have changed */ + sb_mips_setclock(sbh, req, 0, 0); + } + /* Should not reach here */ + ASSERT(0); + } + + sb_setcoreidx(sbh, coreidx); +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/setup.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/setup.c new file mode 100644 index 0000000000..e1b0050a45 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/setup.c @@ -0,0 +1,251 @@ +/* + * Generic setup routines for Broadcom MIPS boards + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/serialP.h> +#include <linux/ide.h> +#include <asm/bootinfo.h> +#include <asm/time.h> +#include <asm/reboot.h> + +#ifdef CONFIG_MTD_PARTITIONS +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#endif + +#include <typedefs.h> +#include <bcmutils.h> +#include <bcmnvram.h> +#include <sbmips.h> +#include <sbutils.h> +#include <trxhdr.h> + +extern void bcm947xx_time_init(void); +extern void bcm947xx_timer_setup(struct irqaction *irq); + +#ifdef CONFIG_REMOTE_DEBUG +extern void set_debug_traps(void); +extern void rs_kgdb_hook(struct serial_state *); +extern void breakpoint(void); +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +extern struct ide_ops std_ide_ops; +#endif + +/* Global SB handle */ +void *bcm947xx_sbh = NULL; +spinlock_t bcm947xx_sbh_lock = SPIN_LOCK_UNLOCKED; +EXPORT_SYMBOL(bcm947xx_sbh); +EXPORT_SYMBOL(bcm947xx_sbh_lock); + +/* Convenience */ +#define sbh bcm947xx_sbh +#define sbh_lock bcm947xx_sbh_lock + +/* Kernel command line */ +char arcs_cmdline[CL_SIZE] __initdata = CONFIG_CMDLINE; + +void +bcm947xx_machine_restart(char *command) +{ + printk("Please stand by while rebooting the system...\n"); + + /* Set the watchdog timer to reset immediately */ + __cli(); + sb_watchdog(sbh, 1); + while (1); +} + +void +bcm947xx_machine_halt(void) +{ + printk("System halted\n"); + + /* Disable interrupts and watchdog and spin forever */ + __cli(); + sb_watchdog(sbh, 0); + while (1); +} + +#ifdef CONFIG_SERIAL + +static struct serial_struct rs = { + line: 0, + flags: ASYNC_BOOT_AUTOCONF, + io_type: SERIAL_IO_MEM, +}; + +static void __init +serial_add(void *regs, uint irq, uint baud_base, uint reg_shift) +{ + rs.iomem_base = regs; + rs.irq = irq + 2; + rs.baud_base = baud_base / 16; + rs.iomem_reg_shift = reg_shift; + + early_serial_setup(&rs); + + rs.line++; +} + +static void __init +serial_setup(void *sbh) +{ + sb_serial_init(sbh, serial_add); + +#ifdef CONFIG_REMOTE_DEBUG + /* Use the last port for kernel debugging */ + if (rs.iomem_base) + rs_kgdb_hook(&rs); +#endif +} + +#endif /* CONFIG_SERIAL */ + +void __init +brcm_setup(void) +{ + char *value; + + /* Get global SB handle */ + sbh = sb_kattach(); + + /* Initialize clocks and interrupts */ + sb_mips_init(sbh); + +#ifdef CONFIG_SERIAL + /* Initialize UARTs */ + serial_setup(sbh); +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ide_ops = &std_ide_ops; +#endif + + /* Override default command line arguments */ + value = nvram_get("kernel_args"); + if (value && strlen(value) && strncmp(value, "empty", 5)) + strncpy(arcs_cmdline, value, sizeof(arcs_cmdline)); + + + /* Generic setup */ + _machine_restart = bcm947xx_machine_restart; + _machine_halt = bcm947xx_machine_halt; + _machine_power_off = bcm947xx_machine_halt; + + board_time_init = bcm947xx_time_init; + board_timer_setup = bcm947xx_timer_setup; +} + +const char * +get_system_type(void) +{ + return "Broadcom BCM947XX"; +} + +void __init +bus_error_init(void) +{ +} + +#ifdef CONFIG_MTD_PARTITIONS + +static struct mtd_partition bcm947xx_parts[] = { + { name: "pmon", offset: 0, size: 0, mask_flags: MTD_WRITEABLE, }, + { name: "linux", offset: 0, size: 0, }, + { name: "rootfs", offset: 0, size: 0, }, + { name: "nvram", offset: 0, size: 0, }, + { name: "OpenWrt", offset: 0, size: 0, }, + { name: NULL, }, +}; + +static int __init +find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) +{ + struct trx_header *trx; + unsigned char buf[512]; + int off; + size_t len; + + trx = (struct trx_header *) buf; + + for (off = (256*1024); off < size; off += mtd->erasesize) { + memset(buf, 0xe5, sizeof(buf)); + + /* + * Read into buffer + */ + if (MTD_READ(mtd, off, sizeof(buf), &len, buf) || + len != sizeof(buf)) + continue; + + /* found a TRX header */ + if (le32_to_cpu(trx->magic) == TRX_MAGIC) { + part->offset = le32_to_cpu(trx->offsets[1]); + part->size = le32_to_cpu(trx->len); + + part->size -= part->offset; + part->offset += off; + + goto done; + } + } + + printk(KERN_NOTICE + "%s: Couldn't find root filesystem\n", + mtd->name); + return -1; + + done: + return part->size; +} + +struct mtd_partition * __init +init_mtd_partitions(struct mtd_info *mtd, size_t size) +{ + + /* boot loader */ + bcm947xx_parts[0].offset=0; + bcm947xx_parts[0].size=256*1024; + + /* nvram */ + bcm947xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize); + bcm947xx_parts[3].size = size - bcm947xx_parts[3].offset; + + /* Size linux (kernel and rootfs) */ + bcm947xx_parts[1].offset = bcm947xx_parts[0].size; + bcm947xx_parts[1].size = bcm947xx_parts[3].offset - bcm947xx_parts[1].offset; + + /* Find and size rootfs */ + if (find_root(mtd,size,&bcm947xx_parts[2])==0) { + /* entirely jffs2 */ + bcm947xx_parts[2].size = bcm947xx_parts[3].offset - bcm947xx_parts[2].offset; + bcm947xx_parts[4].name = NULL; + } else { + /* legacy setup */ + /* calculate leftover flash, and assign it to the jffs2 partition */ + bcm947xx_parts[4].offset = bcm947xx_parts[2].offset + bcm947xx_parts[2].size; + bcm947xx_parts[4].offset = ROUNDUP(bcm947xx_parts[4].offset, mtd->erasesize); + bcm947xx_parts[4].size = bcm947xx_parts[3].offset - bcm947xx_parts[4].offset; + } + + return bcm947xx_parts; +} + +EXPORT_SYMBOL(init_mtd_partitions); + +#endif diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/sflash.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/sflash.c new file mode 100644 index 0000000000..4f69880cac --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/sflash.c @@ -0,0 +1,346 @@ +/* + * Broadcom SiliconBackplane chipcommon serial flash interface + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <typedefs.h> +#include <bcmutils.h> +#include <osl.h> +#include <sbchipc.h> +#include <sflash.h> + +/* Private global state */ +static struct sflash sflash; + +/* Issue a serial flash command */ +static INLINE void +sflash_cmd(chipcregs_t *cc, uint opcode) +{ + W_REG(&cc->flashcontrol, SFLASH_START | opcode); + while (R_REG(&cc->flashcontrol) & SFLASH_BUSY); +} + +/* Initialize serial flash access */ +struct sflash * +sflash_init(chipcregs_t *cc) +{ + uint32 id, id2; + + bzero(&sflash, sizeof(sflash)); + + sflash.type = R_REG(&cc->capabilities) & CAP_FLASH_MASK; + + switch (sflash.type) { + case SFLASH_ST: + /* Probe for ST chips */ + sflash_cmd(cc, SFLASH_ST_DP); + sflash_cmd(cc, SFLASH_ST_RES); + id = R_REG(&cc->flashdata); + switch (id) { + case 0x11: + /* ST M25P20 2 Mbit Serial Flash */ + sflash.blocksize = 64 * 1024; + sflash.numblocks = 4; + break; + case 0x12: + /* ST M25P40 4 Mbit Serial Flash */ + sflash.blocksize = 64 * 1024; + sflash.numblocks = 8; + break; + case 0x13: + /* ST M25P80 8 Mbit Serial Flash */ + sflash.blocksize = 64 * 1024; + sflash.numblocks = 16; + break; + case 0x14: + /* ST M25P16 16 Mbit Serial Flash */ + sflash.blocksize = 64 * 1024; + sflash.numblocks = 32; + break; + case 0xbf: + W_REG(&cc->flashaddress, 1); + sflash_cmd(cc, SFLASH_ST_RES); + id2 = R_REG(&cc->flashdata); + if (id2 == 0x44) { + /* SST M25VF80 4 Mbit Serial Flash */ + sflash.blocksize = 64 * 1024; + sflash.numblocks = 8; + } + break; + } + break; + + case SFLASH_AT: + /* Probe for Atmel chips */ + sflash_cmd(cc, SFLASH_AT_STATUS); + id = R_REG(&cc->flashdata) & 0x3c; + switch (id) { + case 0x2c: + /* Atmel AT45DB161 16Mbit Serial Flash */ + sflash.blocksize = 512; + sflash.numblocks = 4096; + break; + case 0x34: + /* Atmel AT45DB321 32Mbit Serial Flash */ + sflash.blocksize = 512; + sflash.numblocks = 8192; + break; + case 0x3c: + /* Atmel AT45DB642 64Mbit Serial Flash */ + sflash.blocksize = 1024; + sflash.numblocks = 8192; + break; + } + break; + } + + sflash.size = sflash.blocksize * sflash.numblocks; + return sflash.size ? &sflash : NULL; +} + +/* Read len bytes starting at offset into buf. Returns number of bytes read. */ +int +sflash_read(chipcregs_t *cc, uint offset, uint len, uchar *buf) +{ + int cnt; + uint32 *from, *to; + + if (!len) + return 0; + + if ((offset + len) > sflash.size) + return -22; + + if ((len >= 4) && (offset & 3)) + cnt = 4 - (offset & 3); + else if ((len >= 4) && ((uint32)buf & 3)) + cnt = 4 - ((uint32)buf & 3); + else + cnt = len; + + from = (uint32 *)(CC_FLASH_BASE + offset); + to = (uint32 *)buf; + + if (cnt < 4) { + bcopy(from, to, cnt); + return cnt; + } + + while (cnt >= 4) { + *to++ = *from++; + cnt -= 4; + } + + return (len - cnt); +} + +/* Poll for command completion. Returns zero when complete. */ +int +sflash_poll(chipcregs_t *cc, uint offset) +{ + if (offset >= sflash.size) + return -22; + + switch (sflash.type) { + case SFLASH_ST: + /* Check for ST Write In Progress bit */ + sflash_cmd(cc, SFLASH_ST_RDSR); + return R_REG(&cc->flashdata) & SFLASH_ST_WIP; + case SFLASH_AT: + /* Check for Atmel Ready bit */ + sflash_cmd(cc, SFLASH_AT_STATUS); + return !(R_REG(&cc->flashdata) & SFLASH_AT_READY); + } + + return 0; +} + +/* Write len bytes starting at offset into buf. Returns number of bytes + * written. Caller should poll for completion. + */ +int +sflash_write(chipcregs_t *cc, uint offset, uint len, const uchar *buf) +{ + struct sflash *sfl; + int ret = 0; + uint32 page, byte, mask; + + if (!len) + return 0; + + if ((offset + len) > sflash.size) + return -22; + + sfl = &sflash; + switch (sfl->type) { + case SFLASH_ST: + ret = 1; + /* Enable writes */ + sflash_cmd(cc, SFLASH_ST_WREN); + W_REG(&cc->flashaddress, offset); + W_REG(&cc->flashdata, *buf); + /* Page program */ + sflash_cmd(cc, SFLASH_ST_PP); + break; + case SFLASH_AT: + mask = sfl->blocksize - 1; + page = (offset & ~mask) << 1; + byte = offset & mask; + /* Read main memory page into buffer 1 */ + if (byte || len < sfl->blocksize) { + W_REG(&cc->flashaddress, page); + sflash_cmd(cc, SFLASH_AT_BUF1_LOAD); + /* 250 us for AT45DB321B */ + SPINWAIT(sflash_poll(cc, offset), 1000); + ASSERT(!sflash_poll(cc, offset)); + } + /* Write into buffer 1 */ + for (ret = 0; ret < len && byte < sfl->blocksize; ret++) { + W_REG(&cc->flashaddress, byte++); + W_REG(&cc->flashdata, *buf++); + sflash_cmd(cc, SFLASH_AT_BUF1_WRITE); + } + /* Write buffer 1 into main memory page */ + W_REG(&cc->flashaddress, page); + sflash_cmd(cc, SFLASH_AT_BUF1_PROGRAM); + break; + } + + return ret; +} + +/* Erase a region. Returns number of bytes scheduled for erasure. + * Caller should poll for completion. + */ +int +sflash_erase(chipcregs_t *cc, uint offset) +{ + struct sflash *sfl; + + if (offset >= sflash.size) + return -22; + + sfl = &sflash; + switch (sfl->type) { + case SFLASH_ST: + sflash_cmd(cc, SFLASH_ST_WREN); + W_REG(&cc->flashaddress, offset); + sflash_cmd(cc, SFLASH_ST_SE); + return sfl->blocksize; + case SFLASH_AT: + W_REG(&cc->flashaddress, offset << 1); + sflash_cmd(cc, SFLASH_AT_PAGE_ERASE); + return sfl->blocksize; + } + + return 0; +} + +/* + * writes the appropriate range of flash, a NULL buf simply erases + * the region of flash + */ +int +sflash_commit(chipcregs_t *cc, uint offset, uint len, const uchar *buf) +{ + struct sflash *sfl; + uchar *block = NULL, *cur_ptr, *blk_ptr; + uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder; + uint blk_offset, blk_len, copied; + int bytes, ret = 0; + + /* Check address range */ + if (len <= 0) + return 0; + + sfl = &sflash; + if ((offset + len) > sfl->size) + return -1; + + blocksize = sfl->blocksize; + mask = blocksize - 1; + + /* Allocate a block of mem */ + if (!(block = MALLOC(blocksize))) + return -1; + + while (len) { + /* Align offset */ + cur_offset = offset & ~mask; + cur_length = blocksize; + cur_ptr = block; + + remainder = blocksize - (offset & mask); + if (len < remainder) + cur_retlen = len; + else + cur_retlen = remainder; + + /* buf == NULL means erase only */ + if (buf) { + /* Copy existing data into holding block if necessary */ + if ((offset & mask) || (len < blocksize)) { + blk_offset = cur_offset; + blk_len = cur_length; + blk_ptr = cur_ptr; + + /* Copy entire block */ + while(blk_len) { + copied = sflash_read(cc, blk_offset, blk_len, blk_ptr); + blk_offset += copied; + blk_len -= copied; + blk_ptr += copied; + } + } + + /* Copy input data into holding block */ + memcpy(cur_ptr + (offset & mask), buf, cur_retlen); + } + + /* Erase block */ + if ((ret = sflash_erase(cc, (uint) cur_offset)) < 0) + goto done; + while (sflash_poll(cc, (uint) cur_offset)); + + /* buf == NULL means erase only */ + if (!buf) { + offset += cur_retlen; + len -= cur_retlen; + continue; + } + + /* Write holding block */ + while (cur_length > 0) { + if ((bytes = sflash_write(cc, + (uint) cur_offset, + (uint) cur_length, + (uchar *) cur_ptr)) < 0) { + ret = bytes; + goto done; + } + while (sflash_poll(cc, (uint) cur_offset)); + cur_offset += bytes; + cur_length -= bytes; + cur_ptr += bytes; + } + + offset += cur_retlen; + len -= cur_retlen; + buf += cur_retlen; + } + +done: + if (block) + MFREE(block, blocksize); + return ret; +} + diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/time.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/time.c new file mode 100644 index 0000000000..9b7574663d --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/bcm947xx/time.c @@ -0,0 +1,117 @@ +/* + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/serial_reg.h> +#include <linux/interrupt.h> +#include <asm/addrspace.h> +#include <asm/io.h> +#include <asm/time.h> + +#include <typedefs.h> +#include <bcmnvram.h> +#include <sbconfig.h> +#include <sbextif.h> +#include <sbutils.h> +#include <sbmips.h> + +/* Global SB handle */ +extern void *bcm947xx_sbh; +extern spinlock_t bcm947xx_sbh_lock; + +/* Convenience */ +#define sbh bcm947xx_sbh +#define sbh_lock bcm947xx_sbh_lock + +extern int panic_timeout; +static int watchdog = 0; +static u8 *mcr = NULL; + +void __init +bcm947xx_time_init(void) +{ + unsigned int hz; + extifregs_t *eir; + + /* + * Use deterministic values for initial counter interrupt + * so that calibrate delay avoids encountering a counter wrap. + */ + write_c0_count(0); + write_c0_compare(0xffff); + + if (!(hz = sb_mips_clock(sbh))) + hz = 100000000; + + printk("CPU: BCM%04x rev %d at %d MHz\n", sb_chip(sbh), sb_chiprev(sbh), + (hz + 500000) / 1000000); + + /* Set MIPS counter frequency for fixed_rate_gettimeoffset() */ + mips_hpt_frequency = hz / 2; + + /* Set watchdog interval in ms */ + watchdog = simple_strtoul(nvram_safe_get("watchdog"), NULL, 0); + + /* Please set the watchdog to 3 sec if it is less than 3 but not equal to 0 */ + if (watchdog > 0) { + if (watchdog < 3000) + watchdog = 3000; + } + + + /* Set panic timeout in seconds */ + panic_timeout = watchdog / 1000; + + /* Setup blink */ + if ((eir = sb_setcore(sbh, SB_EXTIF, 0))) { + sbconfig_t *sb = (sbconfig_t *)((unsigned int) eir + SBCONFIGOFF); + unsigned long base = EXTIF_CFGIF_BASE(sb_base(readl(&sb->sbadmatch1))); + mcr = (u8 *) ioremap_nocache(base + UART_MCR, 1); + } +} + +static void +bcm947xx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* Generic MIPS timer code */ + timer_interrupt(irq, dev_id, regs); + + /* Set the watchdog timer to reset after the specified number of ms */ + if (watchdog > 0) + sb_watchdog(sbh, WATCHDOG_CLOCK / 1000 * watchdog); + +#ifdef CONFIG_HWSIM + (*((int *)0xa0000f1c))++; +#else + /* Blink one of the LEDs in the external UART */ + if (mcr && !(jiffies % (HZ/2))) + writeb(readb(mcr) ^ UART_MCR_OUT2, mcr); +#endif +} + +static struct irqaction bcm947xx_timer_irqaction = { + bcm947xx_timer_interrupt, + SA_INTERRUPT, + 0, + "timer", + NULL, + NULL +}; + +void __init +bcm947xx_timer_setup(struct irqaction *irq) +{ + /* Enable the timer interrupt */ + setup_irq(7, &bcm947xx_timer_irqaction); +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/Makefile b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/Makefile new file mode 100644 index 0000000000..e16a80e56b --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for generic Broadcom MIPS boards +# +# Copyright 2004, Broadcom Corporation +# All Rights Reserved. +# +# THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY +# KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM +# SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. +# +# $Id$ +# + +.S.s: + $(CPP) $(AFLAGS) $< -o $*.s +.S.o: + $(CC) $(AFLAGS) -c $< -o $*.o + +O_TARGET := brcm.o + +obj-y := int-handler.o irq.o + +obj-$(CONFIG_REMOTE_DEBUG) += gdb_hook.o + +include $(TOPDIR)/Rules.make diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/gdb_hook.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/gdb_hook.c new file mode 100644 index 0000000000..1345289bbc --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/gdb_hook.c @@ -0,0 +1,120 @@ +/* + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * ######################################################################## + * + * This is the interface to the remote debugger stub. + * + */ + +#include <linux/serialP.h> +#include <linux/serial_reg.h> + +#include <asm/serial.h> +#include <asm/io.h> + +static struct async_struct kdb_port_info = {0}; + +static __inline__ unsigned int serial_in(struct async_struct *info, int offset) +{ + return readb((unsigned long) info->iomem_base + + (offset<<info->iomem_reg_shift)); +} + +static __inline__ void serial_out(struct async_struct *info, int offset, + int value) +{ + writeb(value, (unsigned long) info->iomem_base + + (offset<<info->iomem_reg_shift)); +} + +void rs_kgdb_hook(struct serial_state *ser) { + int t; + + kdb_port_info.state = ser; + kdb_port_info.magic = SERIAL_MAGIC; + kdb_port_info.port = ser->port; + kdb_port_info.flags = ser->flags; + kdb_port_info.iomem_base = ser->iomem_base; + kdb_port_info.iomem_reg_shift = ser->iomem_reg_shift; + kdb_port_info.MCR = UART_MCR_DTR | UART_MCR_RTS; + + /* + * Clear all interrupts + */ + serial_in(&kdb_port_info, UART_LSR); + serial_in(&kdb_port_info, UART_RX); + serial_in(&kdb_port_info, UART_IIR); + serial_in(&kdb_port_info, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_out(&kdb_port_info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + serial_out(&kdb_port_info, UART_MCR, kdb_port_info.MCR); + + /* + * and set the speed of the serial port + * (currently hardwired to 115200 8N1 + */ + + /* baud rate is fixed to 115200 (is this sufficient?)*/ + t = kdb_port_info.state->baud_base / 115200; + /* set DLAB */ + serial_out(&kdb_port_info, UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); + serial_out(&kdb_port_info, UART_DLL, t & 0xff);/* LS of divisor */ + serial_out(&kdb_port_info, UART_DLM, t >> 8); /* MS of divisor */ + /* reset DLAB */ + serial_out(&kdb_port_info, UART_LCR, UART_LCR_WLEN8); +} + +int putDebugChar(char c) +{ + + if (!kdb_port_info.state) { /* need to init device first */ + return 0; + } + + while ((serial_in(&kdb_port_info, UART_LSR) & UART_LSR_THRE) == 0) + ; + + serial_out(&kdb_port_info, UART_TX, c); + + return 1; +} + +char getDebugChar(void) +{ + if (!kdb_port_info.state) { /* need to init device first */ + return 0; + } + + while (!(serial_in(&kdb_port_info, UART_LSR) & 1)) + ; + + return(serial_in(&kdb_port_info, UART_RX)); +} diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/int-handler.S b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/int-handler.S new file mode 100644 index 0000000000..de8f115c29 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/int-handler.S @@ -0,0 +1,51 @@ +/* + * Generic interrupt handler for Broadcom MIPS boards + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <linux/config.h> + +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> + +/* + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 Combined hardware interrupt (hw0) + * 3 Hardware + * 4 Hardware + * 5 Hardware + * 6 Hardware + * 7 R4k timer + */ + + .text + .set noreorder + .set noat + .align 5 + NESTED(brcmIRQ, PT_SIZE, sp) + SAVE_ALL + CLI + .set at + .set noreorder + + jal brcm_irq_dispatch + move a0, sp + + j ret_from_irq + nop + + END(brcmIRQ) diff --git a/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/irq.c b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/irq.c new file mode 100644 index 0000000000..157c6b3803 --- /dev/null +++ b/openwrt/package/linux/kernel-source/arch/mips/brcm-boards/generic/irq.c @@ -0,0 +1,130 @@ +/* + * Generic interrupt control functions for Broadcom MIPS boards + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/irq.h> + +#include <asm/irq.h> +#include <asm/mipsregs.h> +#include <asm/gdb-stub.h> + +#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) + +extern asmlinkage void brcmIRQ(void); +extern asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs); + +void +brcm_irq_dispatch(struct pt_regs *regs) +{ + u32 cause; + + cause = read_c0_cause() & + read_c0_status() & + CAUSEF_IP; + +#ifdef CONFIG_KERNPROF + change_c0_status(cause | 1, 1); +#else + clear_c0_status(cause); +#endif + + if (cause & CAUSEF_IP7) + do_IRQ(7, regs); + if (cause & CAUSEF_IP2) + do_IRQ(2, regs); + if (cause & CAUSEF_IP3) + do_IRQ(3, regs); + if (cause & CAUSEF_IP4) + do_IRQ(4, regs); + if (cause & CAUSEF_IP5) + do_IRQ(5, regs); + if (cause & CAUSEF_IP6) + do_IRQ(6, regs); +} + +static void +enable_brcm_irq(unsigned int irq) +{ + if (irq < 8) + set_c0_status(1 << (irq + 8)); + else + set_c0_status(IE_IRQ0); +} + +static void +disable_brcm_irq(unsigned int irq) +{ + if (irq < 8) + clear_c0_status(1 << (irq + 8)); + else + clear_c0_status(IE_IRQ0); +} + +static void +ack_brcm_irq(unsigned int irq) +{ + /* Already done in brcm_irq_dispatch */ +} + +static unsigned int +startup_brcm_irq(unsigned int irq) +{ + enable_brcm_irq(irq); + + return 0; /* never anything pending */ +} + +static void +end_brcm_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_brcm_irq(irq); +} + +static struct hw_interrupt_type brcm_irq_type = { + typename: "MIPS", + startup: startup_brcm_irq, + shutdown: disable_brcm_irq, + enable: enable_brcm_irq, + disable: disable_brcm_irq, + ack: ack_brcm_irq, + end: end_brcm_irq, + NULL +}; + +void __init +init_IRQ(void) +{ + int i; + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + irq_desc[i].handler = &brcm_irq_type; + } + + set_except_vector(0, brcmIRQ); + change_c0_status(ST0_IM, ALLINTS); + +#ifdef CONFIG_REMOTE_DEBUG + printk("Breaking into debugger...\n"); + set_debug_traps(); + breakpoint(); +#endif +} |