1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

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/openwrt@379 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
wbx
2005-03-16 13:50:00 +00:00
parent c7df5a6a2c
commit 4f531230a3
85 changed files with 18591 additions and 16 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1 @@
SECTIONS { .data : { code_start = .; *(.data) code_stop = .; }}

View File

@@ -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;
}

View File

@@ -0,0 +1,17 @@
OUTPUT_ARCH(mips)
ENTRY(load_and_run)
SECTIONS {
. = 0x81000000-0x4000;
.text : {
*(.text)
*(.rodata)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}

View File

@@ -0,0 +1,17 @@
OUTPUT_ARCH(mips)
ENTRY(load_and_run)
SECTIONS {
. = BZ_TEXT_START;
.text : {
*(.text)
*(.rodata)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}

View File

@@ -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)

View File

@@ -0,0 +1,17 @@
OUTPUT_ARCH(mips)
ENTRY(startup)
SECTIONS {
. = 0x80001000;
.text : {
*(.text)
*(.rodata)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}

View File

@@ -0,0 +1,17 @@
OUTPUT_ARCH(mips)
ENTRY(startup)
SECTIONS {
. = TEXT_START;
.text : {
*(.text)
*(.rodata)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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 = (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 }
};

View File

@@ -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)
{
}

View File

@@ -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)
{
}

View File

@@ -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(&regs[UART_MCR]);
W_REG(&regs[UART_MCR], UART_MCR_LOOP | 0x0a);
status1 = R_REG(&regs[UART_MSR]) & 0xf0;
W_REG(&regs[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;
}

View File

@@ -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(&regs->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(&regs->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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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)

View File

@@ -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
}