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:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
SECTIONS { .data : { code_start = .; *(.data) code_stop = .; }}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
OUTPUT_ARCH(mips)
|
||||
ENTRY(load_and_run)
|
||||
SECTIONS {
|
||||
. = 0x81000000-0x4000;
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
OUTPUT_ARCH(mips)
|
||||
ENTRY(load_and_run)
|
||||
SECTIONS {
|
||||
. = BZ_TEXT_START;
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -0,0 +1,17 @@
|
||||
OUTPUT_ARCH(mips)
|
||||
ENTRY(startup)
|
||||
SECTIONS {
|
||||
. = 0x80001000;
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
OUTPUT_ARCH(mips)
|
||||
ENTRY(startup)
|
||||
SECTIONS {
|
||||
. = TEXT_START;
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Low-Level PCI and SB support for BCM47xx (Linux support code)
|
||||
*
|
||||
* Copyright 2004, Broadcom Corporation
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
|
||||
* KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
|
||||
* SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/paccess.h>
|
||||
|
||||
#include <typedefs.h>
|
||||
#include <bcmutils.h>
|
||||
#include <sbconfig.h>
|
||||
#include <sbpci.h>
|
||||
#include <pcicfg.h>
|
||||
#include <sbutils.h>
|
||||
#include <bcmdevs.h>
|
||||
#include <bcmnvram.h>
|
||||
|
||||
/* Global SB handle */
|
||||
extern void *bcm947xx_sbh;
|
||||
extern spinlock_t bcm947xx_sbh_lock;
|
||||
|
||||
/* Convenience */
|
||||
#define sbh bcm947xx_sbh
|
||||
#define sbh_lock bcm947xx_sbh_lock
|
||||
|
||||
static int
|
||||
sbpci_read_config_byte(struct pci_dev *dev, int where, u8 *value)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&sbh_lock, flags);
|
||||
ret = sbpci_read_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, value, sizeof(*value));
|
||||
spin_unlock_irqrestore(&sbh_lock, flags);
|
||||
return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int
|
||||
sbpci_read_config_word(struct pci_dev *dev, int where, u16 *value)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&sbh_lock, flags);
|
||||
ret = sbpci_read_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, value, sizeof(*value));
|
||||
spin_unlock_irqrestore(&sbh_lock, flags);
|
||||
return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int
|
||||
sbpci_read_config_dword(struct pci_dev *dev, int where, u32 *value)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&sbh_lock, flags);
|
||||
ret = sbpci_read_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, value, sizeof(*value));
|
||||
spin_unlock_irqrestore(&sbh_lock, flags);
|
||||
return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int
|
||||
sbpci_write_config_byte(struct pci_dev *dev, int where, u8 value)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&sbh_lock, flags);
|
||||
ret = sbpci_write_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, &value, sizeof(value));
|
||||
spin_unlock_irqrestore(&sbh_lock, flags);
|
||||
return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int
|
||||
sbpci_write_config_word(struct pci_dev *dev, int where, u16 value)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&sbh_lock, flags);
|
||||
ret = sbpci_write_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, &value, sizeof(value));
|
||||
spin_unlock_irqrestore(&sbh_lock, flags);
|
||||
return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int
|
||||
sbpci_write_config_dword(struct pci_dev *dev, int where, u32 value)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&sbh_lock, flags);
|
||||
ret = sbpci_write_config(sbh, dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, &value, sizeof(value));
|
||||
spin_unlock_irqrestore(&sbh_lock, flags);
|
||||
return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static struct pci_ops pcibios_ops = {
|
||||
sbpci_read_config_byte,
|
||||
sbpci_read_config_word,
|
||||
sbpci_read_config_dword,
|
||||
sbpci_write_config_byte,
|
||||
sbpci_write_config_word,
|
||||
sbpci_write_config_dword
|
||||
};
|
||||
|
||||
|
||||
void __init
|
||||
pcibios_init(void)
|
||||
{
|
||||
ulong flags;
|
||||
|
||||
if (!(sbh = sb_kattach()))
|
||||
panic("sb_kattach failed");
|
||||
spin_lock_init(&sbh_lock);
|
||||
|
||||
spin_lock_irqsave(&sbh_lock, flags);
|
||||
sbpci_init(sbh);
|
||||
spin_unlock_irqrestore(&sbh_lock, flags);
|
||||
|
||||
set_io_port_base((unsigned long) ioremap_nocache(SB_PCI_MEM, 0x04000000));
|
||||
|
||||
/* Scan the SB bus */
|
||||
pci_scan_bus(0, &pcibios_ops, NULL);
|
||||
|
||||
}
|
||||
|
||||
char * __init
|
||||
pcibios_setup(char *str)
|
||||
{
|
||||
if (!strncmp(str, "ban=", 4)) {
|
||||
sbpci_ban(simple_strtoul(str + 4, NULL, 0));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (str);
|
||||
}
|
||||
|
||||
static u32 pci_iobase = 0x100;
|
||||
static u32 pci_membase = SB_PCI_DMA;
|
||||
|
||||
void __init
|
||||
pcibios_fixup_bus(struct pci_bus *b)
|
||||
{
|
||||
struct list_head *ln;
|
||||
struct pci_dev *d;
|
||||
struct resource *res;
|
||||
int pos, size;
|
||||
u32 *base;
|
||||
u8 irq;
|
||||
|
||||
printk("PCI: Fixing up bus %d\n", b->number);
|
||||
|
||||
/* Fix up SB */
|
||||
if (b->number == 0) {
|
||||
for (ln=b->devices.next; ln != &b->devices; ln=ln->next) {
|
||||
d = pci_dev_b(ln);
|
||||
/* Fix up interrupt lines */
|
||||
pci_read_config_byte(d, PCI_INTERRUPT_LINE, &irq);
|
||||
d->irq = irq + 2;
|
||||
pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix up external PCI */
|
||||
else {
|
||||
for (ln=b->devices.next; ln != &b->devices; ln=ln->next) {
|
||||
d = pci_dev_b(ln);
|
||||
/* Fix up resource bases */
|
||||
for (pos = 0; pos < 6; pos++) {
|
||||
res = &d->resource[pos];
|
||||
base = (res->flags & IORESOURCE_IO) ? &pci_iobase : &pci_membase;
|
||||
if (res->end) {
|
||||
size = res->end - res->start + 1;
|
||||
if (*base & (size - 1))
|
||||
*base = (*base + size) & ~(size - 1);
|
||||
res->start = *base;
|
||||
res->end = res->start + size - 1;
|
||||
*base += size;
|
||||
pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start);
|
||||
}
|
||||
/* Fix up PCI bridge BAR0 only */
|
||||
if (b->number == 1 && PCI_SLOT(d->devfn) == 0)
|
||||
break;
|
||||
}
|
||||
/* Fix up interrupt lines */
|
||||
if (pci_find_device(VENDOR_BROADCOM, SB_PCI, NULL))
|
||||
d->irq = (pci_find_device(VENDOR_BROADCOM, SB_PCI, NULL))->irq;
|
||||
pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int
|
||||
pcibios_assign_all_busses(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
pcibios_align_resource(void *data, struct resource *res,
|
||||
unsigned long size, unsigned long align)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
pcibios_enable_resources(struct pci_dev *dev)
|
||||
{
|
||||
u16 cmd, old_cmd;
|
||||
int idx;
|
||||
struct resource *r;
|
||||
|
||||
/* External PCI only */
|
||||
if (dev->bus->number == 0)
|
||||
return 0;
|
||||
|
||||
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
||||
old_cmd = cmd;
|
||||
for(idx=0; idx<6; idx++) {
|
||||
r = &dev->resource[idx];
|
||||
if (r->flags & IORESOURCE_IO)
|
||||
cmd |= PCI_COMMAND_IO;
|
||||
if (r->flags & IORESOURCE_MEM)
|
||||
cmd |= PCI_COMMAND_MEMORY;
|
||||
}
|
||||
if (dev->resource[PCI_ROM_RESOURCE].start)
|
||||
cmd |= PCI_COMMAND_MEMORY;
|
||||
if (cmd != old_cmd) {
|
||||
printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd);
|
||||
pci_write_config_word(dev, PCI_COMMAND, cmd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pcibios_enable_device(struct pci_dev *dev, int mask)
|
||||
{
|
||||
ulong flags;
|
||||
uint coreidx;
|
||||
|
||||
/* External PCI device enable */
|
||||
if (dev->bus->number != 0)
|
||||
return pcibios_enable_resources(dev);
|
||||
|
||||
/* These cores come out of reset enabled */
|
||||
if (dev->device == SB_MIPS ||
|
||||
dev->device == SB_MIPS33 ||
|
||||
dev->device == SB_EXTIF ||
|
||||
dev->device == SB_CC)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&sbh_lock, flags);
|
||||
coreidx = sb_coreidx(sbh);
|
||||
if (!sb_setcoreidx(sbh, PCI_SLOT(dev->devfn)))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
/*
|
||||
* The USB core requires a special bit to be set during core
|
||||
* reset to enable host (OHCI) mode. Resetting the SB core in
|
||||
* pcibios_enable_device() is a hack for compatibility with
|
||||
* vanilla usb-ohci so that it does not have to know about
|
||||
* SB. A driver that wants to use the USB core in device mode
|
||||
* should know about SB and should reset the bit back to 0
|
||||
* after calling pcibios_enable_device().
|
||||
*/
|
||||
if (sb_coreid(sbh) == SB_USB) {
|
||||
sb_core_disable(sbh, sb_coreflags(sbh, 0, 0));
|
||||
sb_core_reset(sbh, 1 << 29);
|
||||
} else
|
||||
sb_core_reset(sbh, 0);
|
||||
|
||||
sb_setcoreidx(sbh, coreidx);
|
||||
spin_unlock_irqrestore(&sbh_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
pcibios_update_resource(struct pci_dev *dev, struct resource *root,
|
||||
struct resource *res, int resource)
|
||||
{
|
||||
unsigned long where, size;
|
||||
u32 reg;
|
||||
|
||||
/* External PCI only */
|
||||
if (dev->bus->number == 0)
|
||||
return;
|
||||
|
||||
where = PCI_BASE_ADDRESS_0 + (resource * 4);
|
||||
size = res->end - res->start;
|
||||
pci_read_config_dword(dev, where, ®);
|
||||
reg = (reg & size) | (((u32)(res->start - root->start)) & ~size);
|
||||
pci_write_config_dword(dev, where, reg);
|
||||
}
|
||||
|
||||
static void __init
|
||||
quirk_sbpci_bridge(struct pci_dev *dev)
|
||||
{
|
||||
if (dev->bus->number != 1 || PCI_SLOT(dev->devfn) != 0)
|
||||
return;
|
||||
|
||||
printk("PCI: Fixing up bridge\n");
|
||||
|
||||
/* Enable PCI bridge bus mastering and memory space */
|
||||
pci_set_master(dev);
|
||||
pcibios_enable_resources(dev);
|
||||
|
||||
/* Enable PCI bridge BAR1 prefetch and burst */
|
||||
pci_write_config_dword(dev, PCI_BAR1_CONTROL, 3);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we set up a device for bus mastering, we need to check the latency
|
||||
* timer as certain crappy BIOSes forget to set it properly.
|
||||
*/
|
||||
unsigned int pcibios_max_latency = 255;
|
||||
|
||||
void pcibios_set_master(struct pci_dev *dev)
|
||||
{
|
||||
u8 lat;
|
||||
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
|
||||
if (lat < 16)
|
||||
lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
|
||||
else if (lat > pcibios_max_latency)
|
||||
lat = pcibios_max_latency;
|
||||
else
|
||||
return;
|
||||
printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n", dev->slot_name, lat);
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
|
||||
}
|
||||
|
||||
struct pci_fixup pcibios_fixups[] = {
|
||||
{ PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, quirk_sbpci_bridge },
|
||||
{ 0 }
|
||||
};
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,951 @@
|
||||
/*
|
||||
* BCM47XX Sonics SiliconBackplane MIPS core routines
|
||||
*
|
||||
* Copyright 2004, Broadcom Corporation
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
|
||||
* KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
|
||||
* SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <typedefs.h>
|
||||
#include <osl.h>
|
||||
#include <sbutils.h>
|
||||
#include <bcmdevs.h>
|
||||
#include <bcmnvram.h>
|
||||
#include <bcmutils.h>
|
||||
#include <hndmips.h>
|
||||
#include <sbconfig.h>
|
||||
#include <sbextif.h>
|
||||
#include <sbchipc.h>
|
||||
#include <sbmemc.h>
|
||||
|
||||
/*
|
||||
* Memory segments (32bit kernel mode addresses)
|
||||
*/
|
||||
#undef KUSEG
|
||||
#undef KSEG0
|
||||
#undef KSEG1
|
||||
#undef KSEG2
|
||||
#undef KSEG3
|
||||
#define KUSEG 0x00000000
|
||||
#define KSEG0 0x80000000
|
||||
#define KSEG1 0xa0000000
|
||||
#define KSEG2 0xc0000000
|
||||
#define KSEG3 0xe0000000
|
||||
|
||||
/*
|
||||
* Map an address to a certain kernel segment
|
||||
*/
|
||||
#undef KSEG0ADDR
|
||||
#undef KSEG1ADDR
|
||||
#undef KSEG2ADDR
|
||||
#undef KSEG3ADDR
|
||||
#define KSEG0ADDR(a) (((a) & 0x1fffffff) | KSEG0)
|
||||
#define KSEG1ADDR(a) (((a) & 0x1fffffff) | KSEG1)
|
||||
#define KSEG2ADDR(a) (((a) & 0x1fffffff) | KSEG2)
|
||||
#define KSEG3ADDR(a) (((a) & 0x1fffffff) | KSEG3)
|
||||
|
||||
/*
|
||||
* The following macros are especially useful for __asm__
|
||||
* inline assembler.
|
||||
*/
|
||||
#ifndef __STR
|
||||
#define __STR(x) #x
|
||||
#endif
|
||||
#ifndef STR
|
||||
#define STR(x) __STR(x)
|
||||
#endif
|
||||
|
||||
/* *********************************************************************
|
||||
* CP0 Registers
|
||||
********************************************************************* */
|
||||
|
||||
#define C0_INX 0 /* CP0: TLB Index */
|
||||
#define C0_RAND 1 /* CP0: TLB Random */
|
||||
#define C0_TLBLO0 2 /* CP0: TLB EntryLo0 */
|
||||
#define C0_TLBLO C0_TLBLO0 /* CP0: TLB EntryLo0 */
|
||||
#define C0_TLBLO1 3 /* CP0: TLB EntryLo1 */
|
||||
#define C0_CTEXT 4 /* CP0: Context */
|
||||
#define C0_PGMASK 5 /* CP0: TLB PageMask */
|
||||
#define C0_WIRED 6 /* CP0: TLB Wired */
|
||||
#define C0_BADVADDR 8 /* CP0: Bad Virtual Address */
|
||||
#define C0_COUNT 9 /* CP0: Count */
|
||||
#define C0_TLBHI 10 /* CP0: TLB EntryHi */
|
||||
#define C0_COMPARE 11 /* CP0: Compare */
|
||||
#define C0_SR 12 /* CP0: Processor Status */
|
||||
#define C0_STATUS C0_SR /* CP0: Processor Status */
|
||||
#define C0_CAUSE 13 /* CP0: Exception Cause */
|
||||
#define C0_EPC 14 /* CP0: Exception PC */
|
||||
#define C0_PRID 15 /* CP0: Processor Revision Indentifier */
|
||||
#define C0_CONFIG 16 /* CP0: Config */
|
||||
#define C0_LLADDR 17 /* CP0: LLAddr */
|
||||
#define C0_WATCHLO 18 /* CP0: WatchpointLo */
|
||||
#define C0_WATCHHI 19 /* CP0: WatchpointHi */
|
||||
#define C0_XCTEXT 20 /* CP0: XContext */
|
||||
#define C0_DIAGNOSTIC 22 /* CP0: Diagnostic */
|
||||
#define C0_BROADCOM C0_DIAGNOSTIC /* CP0: Broadcom Register */
|
||||
#define C0_ECC 26 /* CP0: ECC */
|
||||
#define C0_CACHEERR 27 /* CP0: CacheErr */
|
||||
#define C0_TAGLO 28 /* CP0: TagLo */
|
||||
#define C0_TAGHI 29 /* CP0: TagHi */
|
||||
#define C0_ERREPC 30 /* CP0: ErrorEPC */
|
||||
|
||||
/*
|
||||
* Macros to access the system control coprocessor
|
||||
*/
|
||||
|
||||
#define MFC0(source, sel) \
|
||||
({ \
|
||||
int __res; \
|
||||
__asm__ __volatile__( \
|
||||
".set\tnoreorder\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
".word\t"STR(0x40010000 | ((source)<<11) | (sel))"\n\t" \
|
||||
"move\t%0,$1\n\t" \
|
||||
".set\tat\n\t" \
|
||||
".set\treorder" \
|
||||
:"=r" (__res) \
|
||||
: \
|
||||
:"$1"); \
|
||||
__res; \
|
||||
})
|
||||
|
||||
#define MTC0(source, sel, value) \
|
||||
do { \
|
||||
__asm__ __volatile__( \
|
||||
".set\tnoreorder\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
"move\t$1,%z0\n\t" \
|
||||
".word\t"STR(0x40810000 | ((source)<<11) | (sel))"\n\t" \
|
||||
".set\tat\n\t" \
|
||||
".set\treorder" \
|
||||
: \
|
||||
:"Jr" (value) \
|
||||
:"$1"); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* R4x00 interrupt enable / cause bits
|
||||
*/
|
||||
#undef IE_SW0
|
||||
#undef IE_SW1
|
||||
#undef IE_IRQ0
|
||||
#undef IE_IRQ1
|
||||
#undef IE_IRQ2
|
||||
#undef IE_IRQ3
|
||||
#undef IE_IRQ4
|
||||
#undef IE_IRQ5
|
||||
#define IE_SW0 (1<< 8)
|
||||
#define IE_SW1 (1<< 9)
|
||||
#define IE_IRQ0 (1<<10)
|
||||
#define IE_IRQ1 (1<<11)
|
||||
#define IE_IRQ2 (1<<12)
|
||||
#define IE_IRQ3 (1<<13)
|
||||
#define IE_IRQ4 (1<<14)
|
||||
#define IE_IRQ5 (1<<15)
|
||||
|
||||
/*
|
||||
* Bitfields in the R4xx0 cp0 status register
|
||||
*/
|
||||
#define ST0_IE 0x00000001
|
||||
#define ST0_EXL 0x00000002
|
||||
#define ST0_ERL 0x00000004
|
||||
#define ST0_KSU 0x00000018
|
||||
# define KSU_USER 0x00000010
|
||||
# define KSU_SUPERVISOR 0x00000008
|
||||
# define KSU_KERNEL 0x00000000
|
||||
#define ST0_UX 0x00000020
|
||||
#define ST0_SX 0x00000040
|
||||
#define ST0_KX 0x00000080
|
||||
#define ST0_DE 0x00010000
|
||||
#define ST0_CE 0x00020000
|
||||
|
||||
/*
|
||||
* Status register bits available in all MIPS CPUs.
|
||||
*/
|
||||
#define ST0_IM 0x0000ff00
|
||||
#define ST0_CH 0x00040000
|
||||
#define ST0_SR 0x00100000
|
||||
#define ST0_TS 0x00200000
|
||||
#define ST0_BEV 0x00400000
|
||||
#define ST0_RE 0x02000000
|
||||
#define ST0_FR 0x04000000
|
||||
#define ST0_CU 0xf0000000
|
||||
#define ST0_CU0 0x10000000
|
||||
#define ST0_CU1 0x20000000
|
||||
#define ST0_CU2 0x40000000
|
||||
#define ST0_CU3 0x80000000
|
||||
#define ST0_XX 0x80000000 /* MIPS IV naming */
|
||||
|
||||
/*
|
||||
* Cache Operations
|
||||
*/
|
||||
|
||||
#ifndef Fill_I
|
||||
#define Fill_I 0x14
|
||||
#endif
|
||||
|
||||
#define cache_unroll(base,op) \
|
||||
__asm__ __volatile__(" \
|
||||
.set noreorder; \
|
||||
.set mips3; \
|
||||
cache %1, (%0); \
|
||||
.set mips0; \
|
||||
.set reorder" \
|
||||
: \
|
||||
: "r" (base), \
|
||||
"i" (op));
|
||||
|
||||
/*
|
||||
* These are the UART port assignments, expressed as offsets from the base
|
||||
* register. These assignments should hold for any serial port based on
|
||||
* a 8250, 16450, or 16550(A).
|
||||
*/
|
||||
|
||||
#define UART_MCR 4 /* Out: Modem Control Register */
|
||||
#define UART_MSR 6 /* In: Modem Status Register */
|
||||
#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
|
||||
|
||||
/*
|
||||
* Returns TRUE if an external UART exists at the given base
|
||||
* register.
|
||||
*/
|
||||
static bool
|
||||
serial_exists(uint8 *regs)
|
||||
{
|
||||
uint8 save_mcr, status1;
|
||||
|
||||
save_mcr = R_REG(®s[UART_MCR]);
|
||||
W_REG(®s[UART_MCR], UART_MCR_LOOP | 0x0a);
|
||||
status1 = R_REG(®s[UART_MSR]) & 0xf0;
|
||||
W_REG(®s[UART_MCR], save_mcr);
|
||||
|
||||
return (status1 == 0x90);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes UART access. The callback function will be called once
|
||||
* per found UART.
|
||||
*/
|
||||
void
|
||||
sb_serial_init(void *sbh, void (*add)(void *regs, uint irq, uint baud_base, uint reg_shift))
|
||||
{
|
||||
void *regs;
|
||||
ulong base;
|
||||
uint irq;
|
||||
int i, n;
|
||||
|
||||
if ((regs = sb_setcore(sbh, SB_EXTIF, 0))) {
|
||||
extifregs_t *eir = (extifregs_t *) regs;
|
||||
sbconfig_t *sb;
|
||||
|
||||
/* Determine external UART register base */
|
||||
sb = (sbconfig_t *)((ulong) eir + SBCONFIGOFF);
|
||||
base = EXTIF_CFGIF_BASE(sb_base(R_REG(&sb->sbadmatch1)));
|
||||
|
||||
/* Determine IRQ */
|
||||
irq = sb_irq(sbh);
|
||||
|
||||
/* Disable GPIO interrupt initially */
|
||||
W_REG(&eir->gpiointpolarity, 0);
|
||||
W_REG(&eir->gpiointmask, 0);
|
||||
|
||||
/* Search for external UARTs */
|
||||
n = 2;
|
||||
for (i = 0; i < 2; i++) {
|
||||
regs = (void *) REG_MAP(base + (i * 8), 8);
|
||||
if (serial_exists(regs)) {
|
||||
/* Set GPIO 1 to be the external UART IRQ */
|
||||
W_REG(&eir->gpiointmask, 2);
|
||||
if (add)
|
||||
add(regs, irq, 13500000, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add internal UART if enabled */
|
||||
if (R_REG(&eir->corecontrol) & CC_UE)
|
||||
if (add)
|
||||
add((void *) &eir->uartdata, irq, sb_clock(sbh), 2);
|
||||
} else if ((regs = sb_setcore(sbh, SB_CC, 0))) {
|
||||
chipcregs_t *cc = (chipcregs_t *) regs;
|
||||
uint32 rev, cap, pll, baud_base, div;
|
||||
|
||||
/* Determine core revision and capabilities */
|
||||
rev = sb_corerev(sbh);
|
||||
cap = R_REG(&cc->capabilities);
|
||||
pll = cap & CAP_PLL_MASK;
|
||||
|
||||
/* Determine IRQ */
|
||||
irq = sb_irq(sbh);
|
||||
|
||||
if (pll == PLL_TYPE1) {
|
||||
/* PLL clock */
|
||||
baud_base = sb_clock_rate(pll,
|
||||
R_REG(&cc->clockcontrol_n),
|
||||
R_REG(&cc->clockcontrol_m2));
|
||||
div = 1;
|
||||
} else if (rev >= 3) {
|
||||
/* Internal backplane clock */
|
||||
baud_base = sb_clock_rate(pll,
|
||||
R_REG(&cc->clockcontrol_n),
|
||||
R_REG(&cc->clockcontrol_sb));
|
||||
div = 2; /* Minimum divisor */
|
||||
W_REG(&cc->clkdiv, ((R_REG(&cc->clkdiv) & ~CLKD_UART) | div));
|
||||
} else {
|
||||
/* Fixed internal backplane clock */
|
||||
baud_base = 88000000;
|
||||
div = 48;
|
||||
}
|
||||
|
||||
/* Clock source depends on strapping if UartClkOverride is unset */
|
||||
if ((rev > 0) && ((R_REG(&cc->corecontrol) & CC_UARTCLKO) == 0)) {
|
||||
if ((cap & CAP_UCLKSEL) == CAP_UINTCLK) {
|
||||
/* Internal divided backplane clock */
|
||||
baud_base /= div;
|
||||
} else {
|
||||
/* Assume external clock of 1.8432 MHz */
|
||||
baud_base = 1843200;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add internal UARTs */
|
||||
n = cap & CAP_UARTS_MASK;
|
||||
for (i = 0; i < n; i++) {
|
||||
/* Register offset changed after revision 0 */
|
||||
if (rev)
|
||||
regs = (void *)((ulong) &cc->uart0data + (i * 256));
|
||||
else
|
||||
regs = (void *)((ulong) &cc->uart0data + (i * 8));
|
||||
|
||||
if (add)
|
||||
add(regs, irq, baud_base, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the SB interrupt flag of the current core. */
|
||||
uint32
|
||||
sb_flag(void *sbh)
|
||||
{
|
||||
void *regs;
|
||||
sbconfig_t *sb;
|
||||
|
||||
regs = sb_coreregs(sbh);
|
||||
sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF);
|
||||
|
||||
return (R_REG(&sb->sbtpsflag) & SBTPS_NUM0_MASK);
|
||||
}
|
||||
|
||||
static const uint32 sbips_int_mask[] = {
|
||||
0,
|
||||
SBIPS_INT1_MASK,
|
||||
SBIPS_INT2_MASK,
|
||||
SBIPS_INT3_MASK,
|
||||
SBIPS_INT4_MASK
|
||||
};
|
||||
|
||||
static const uint32 sbips_int_shift[] = {
|
||||
0,
|
||||
0,
|
||||
SBIPS_INT2_SHIFT,
|
||||
SBIPS_INT3_SHIFT,
|
||||
SBIPS_INT4_SHIFT
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the MIPS IRQ assignment of the current core. If unassigned,
|
||||
* 0 is returned.
|
||||
*/
|
||||
uint
|
||||
sb_irq(void *sbh)
|
||||
{
|
||||
uint idx;
|
||||
void *regs;
|
||||
sbconfig_t *sb;
|
||||
uint32 flag, sbipsflag;
|
||||
uint irq = 0;
|
||||
|
||||
flag = sb_flag(sbh);
|
||||
|
||||
idx = sb_coreidx(sbh);
|
||||
|
||||
if ((regs = sb_setcore(sbh, SB_MIPS, 0)) ||
|
||||
(regs = sb_setcore(sbh, SB_MIPS33, 0))) {
|
||||
sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF);
|
||||
|
||||
/* sbipsflag specifies which core is routed to interrupts 1 to 4 */
|
||||
sbipsflag = R_REG(&sb->sbipsflag);
|
||||
for (irq = 1; irq <= 4; irq++) {
|
||||
if (((sbipsflag & sbips_int_mask[irq]) >> sbips_int_shift[irq]) == flag)
|
||||
break;
|
||||
}
|
||||
if (irq == 5)
|
||||
irq = 0;
|
||||
}
|
||||
|
||||
sb_setcoreidx(sbh, idx);
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
/* Clears the specified MIPS IRQ. */
|
||||
static void
|
||||
sb_clearirq(void *sbh, uint irq)
|
||||
{
|
||||
void *regs;
|
||||
sbconfig_t *sb;
|
||||
|
||||
if (!(regs = sb_setcore(sbh, SB_MIPS, 0)) &&
|
||||
!(regs = sb_setcore(sbh, SB_MIPS33, 0)))
|
||||
ASSERT(regs);
|
||||
sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF);
|
||||
|
||||
if (irq == 0)
|
||||
W_REG(&sb->sbintvec, 0);
|
||||
else
|
||||
OR_REG(&sb->sbipsflag, sbips_int_mask[irq]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Assigns the specified MIPS IRQ to the specified core. Shared MIPS
|
||||
* IRQ 0 may be assigned more than once.
|
||||
*/
|
||||
static void
|
||||
sb_setirq(void *sbh, uint irq, uint coreid, uint coreunit)
|
||||
{
|
||||
void *regs;
|
||||
sbconfig_t *sb;
|
||||
uint32 flag;
|
||||
|
||||
regs = sb_setcore(sbh, coreid, coreunit);
|
||||
ASSERT(regs);
|
||||
flag = sb_flag(sbh);
|
||||
|
||||
if (!(regs = sb_setcore(sbh, SB_MIPS, 0)) &&
|
||||
!(regs = sb_setcore(sbh, SB_MIPS33, 0)))
|
||||
ASSERT(regs);
|
||||
sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF);
|
||||
|
||||
if (irq == 0)
|
||||
OR_REG(&sb->sbintvec, 1 << flag);
|
||||
else {
|
||||
flag <<= sbips_int_shift[irq];
|
||||
ASSERT(!(flag & ~sbips_int_mask[irq]));
|
||||
flag |= R_REG(&sb->sbipsflag) & ~sbips_int_mask[irq];
|
||||
W_REG(&sb->sbipsflag, flag);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes clocks and interrupts. SB and NVRAM access must be
|
||||
* initialized prior to calling.
|
||||
*/
|
||||
void
|
||||
sb_mips_init(void *sbh)
|
||||
{
|
||||
ulong hz, ns, tmp;
|
||||
extifregs_t *eir;
|
||||
chipcregs_t *cc;
|
||||
char *value;
|
||||
uint irq;
|
||||
|
||||
/* Figure out current SB clock speed */
|
||||
if ((hz = sb_clock(sbh)) == 0)
|
||||
hz = 100000000;
|
||||
ns = 1000000000 / hz;
|
||||
|
||||
/* Setup external interface timing */
|
||||
if ((eir = sb_setcore(sbh, SB_EXTIF, 0))) {
|
||||
/* Initialize extif so we can get to the LEDs and external UART */
|
||||
W_REG(&eir->prog_config, CF_EN);
|
||||
|
||||
/* Set timing for the flash */
|
||||
tmp = CEIL(10, ns) << FW_W3_SHIFT; /* W3 = 10nS */
|
||||
tmp = tmp | (CEIL(40, ns) << FW_W1_SHIFT); /* W1 = 40nS */
|
||||
tmp = tmp | CEIL(120, ns); /* W0 = 120nS */
|
||||
W_REG(&eir->prog_waitcount, tmp); /* 0x01020a0c for a 100Mhz clock */
|
||||
|
||||
/* Set programmable interface timing for external uart */
|
||||
tmp = CEIL(10, ns) << FW_W3_SHIFT; /* W3 = 10nS */
|
||||
tmp = tmp | (CEIL(20, ns) << FW_W2_SHIFT); /* W2 = 20nS */
|
||||
tmp = tmp | (CEIL(100, ns) << FW_W1_SHIFT); /* W1 = 100nS */
|
||||
tmp = tmp | CEIL(120, ns); /* W0 = 120nS */
|
||||
W_REG(&eir->prog_waitcount, tmp); /* 0x01020a0c for a 100Mhz clock */
|
||||
} else if ((cc = sb_setcore(sbh, SB_CC, 0))) {
|
||||
/* Set timing for the flash */
|
||||
tmp = CEIL(10, ns) << FW_W3_SHIFT; /* W3 = 10nS */
|
||||
tmp |= CEIL(10, ns) << FW_W1_SHIFT; /* W1 = 10nS */
|
||||
tmp |= CEIL(120, ns); /* W0 = 120nS */
|
||||
W_REG(&cc->flash_waitcount, tmp);
|
||||
|
||||
W_REG(&cc->pcmcia_memwait, tmp);
|
||||
}
|
||||
|
||||
/* Chip specific initialization */
|
||||
switch (sb_chip(sbh)) {
|
||||
case BCM4710_DEVICE_ID:
|
||||
/* Clear interrupt map */
|
||||
for (irq = 0; irq <= 4; irq++)
|
||||
sb_clearirq(sbh, irq);
|
||||
sb_setirq(sbh, 0, SB_CODEC, 0);
|
||||
sb_setirq(sbh, 0, SB_EXTIF, 0);
|
||||
sb_setirq(sbh, 2, SB_ENET, 1);
|
||||
sb_setirq(sbh, 3, SB_ILINE20, 0);
|
||||
sb_setirq(sbh, 4, SB_PCI, 0);
|
||||
ASSERT(eir);
|
||||
value = nvram_get("et0phyaddr");
|
||||
if (value && !strcmp(value, "31")) {
|
||||
/* Enable internal UART */
|
||||
W_REG(&eir->corecontrol, CC_UE);
|
||||
/* Give USB its own interrupt */
|
||||
sb_setirq(sbh, 1, SB_USB, 0);
|
||||
} else {
|
||||
/* Disable internal UART */
|
||||
W_REG(&eir->corecontrol, 0);
|
||||
/* Give Ethernet its own interrupt */
|
||||
sb_setirq(sbh, 1, SB_ENET, 0);
|
||||
sb_setirq(sbh, 0, SB_USB, 0);
|
||||
}
|
||||
break;
|
||||
case BCM4310_DEVICE_ID:
|
||||
MTC0(C0_BROADCOM, 0, MFC0(C0_BROADCOM, 0) & ~(1 << 22));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32
|
||||
sb_mips_clock(void *sbh)
|
||||
{
|
||||
extifregs_t *eir;
|
||||
chipcregs_t *cc;
|
||||
uint32 n, m;
|
||||
uint idx;
|
||||
uint32 pll_type, rate = 0;
|
||||
|
||||
/* get index of the current core */
|
||||
idx = sb_coreidx(sbh);
|
||||
pll_type = PLL_TYPE1;
|
||||
|
||||
/* switch to extif or chipc core */
|
||||
if ((eir = (extifregs_t *) sb_setcore(sbh, SB_EXTIF, 0))) {
|
||||
n = R_REG(&eir->clockcontrol_n);
|
||||
m = R_REG(&eir->clockcontrol_sb);
|
||||
} else if ((cc = (chipcregs_t *) sb_setcore(sbh, SB_CC, 0))) {
|
||||
pll_type = R_REG(&cc->capabilities) & CAP_PLL_MASK;
|
||||
n = R_REG(&cc->clockcontrol_n);
|
||||
if ((pll_type == PLL_TYPE2) || (pll_type == PLL_TYPE4))
|
||||
m = R_REG(&cc->clockcontrol_mips);
|
||||
else if (pll_type == PLL_TYPE3) {
|
||||
rate = 200000000;
|
||||
goto out;
|
||||
} else
|
||||
m = R_REG(&cc->clockcontrol_sb);
|
||||
} else
|
||||
goto out;
|
||||
|
||||
/* calculate rate */
|
||||
rate = sb_clock_rate(pll_type, n, m);
|
||||
|
||||
out:
|
||||
/* switch back to previous core */
|
||||
sb_setcoreidx(sbh, idx);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static void
|
||||
icache_probe(int *size, int *lsize)
|
||||
{
|
||||
uint32 config1;
|
||||
uint sets, ways;
|
||||
|
||||
config1 = MFC0(C0_CONFIG, 1);
|
||||
|
||||
/* Instruction Cache Size = Associativity * Line Size * Sets Per Way */
|
||||
if ((*lsize = ((config1 >> 19) & 7)))
|
||||
*lsize = 2 << *lsize;
|
||||
sets = 64 << ((config1 >> 22) & 7);
|
||||
ways = 1 + ((config1 >> 16) & 7);
|
||||
*size = *lsize * sets * ways;
|
||||
}
|
||||
|
||||
#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)
|
||||
|
||||
static void
|
||||
handler(void)
|
||||
{
|
||||
/* Step 11 */
|
||||
__asm__ (
|
||||
".set\tmips32\n\t"
|
||||
"ssnop\n\t"
|
||||
"ssnop\n\t"
|
||||
/* Disable interrupts */
|
||||
/* MTC0(C0_STATUS, 0, MFC0(C0_STATUS, 0) & ~(ALLINTS | STO_IE)); */
|
||||
"mfc0 $15, $12\n\t"
|
||||
"and $15, $15, -31746\n\t"
|
||||
"mtc0 $15, $12\n\t"
|
||||
"eret\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
".set\tmips0"
|
||||
);
|
||||
}
|
||||
|
||||
/* The following MUST come right after handler() */
|
||||
static void
|
||||
afterhandler(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the MIPS, backplane and PCI clocks as closely as possible.
|
||||
*/
|
||||
bool
|
||||
sb_mips_setclock(void *sbh, uint32 mipsclock, uint32 sbclock, uint32 pciclock)
|
||||
{
|
||||
extifregs_t *eir = NULL;
|
||||
chipcregs_t *cc = NULL;
|
||||
mipsregs_t *mipsr = NULL;
|
||||
volatile uint32 *clockcontrol_n, *clockcontrol_sb, *clockcontrol_pci;
|
||||
uint32 orig_n, orig_sb, orig_pci, orig_m2, orig_mips, orig_ratio_parm, new_ratio;
|
||||
uint32 pll_type, sync_mode;
|
||||
uint idx, i;
|
||||
typedef struct {
|
||||
uint32 mipsclock;
|
||||
uint16 n;
|
||||
uint32 sb;
|
||||
uint32 pci33;
|
||||
uint32 pci25;
|
||||
} n3m_table_t;
|
||||
static n3m_table_t type1_table[] = {
|
||||
{ 96000000, 0x0303, 0x04020011, 0x11030011, 0x11050011 }, /* 96.000 32.000 24.000 */
|
||||
{ 100000000, 0x0009, 0x04020011, 0x11030011, 0x11050011 }, /* 100.000 33.333 25.000 */
|
||||
{ 104000000, 0x0802, 0x04020011, 0x11050009, 0x11090009 }, /* 104.000 31.200 24.960 */
|
||||
{ 108000000, 0x0403, 0x04020011, 0x11050009, 0x02000802 }, /* 108.000 32.400 24.923 */
|
||||
{ 112000000, 0x0205, 0x04020011, 0x11030021, 0x02000403 }, /* 112.000 32.000 24.889 */
|
||||
{ 115200000, 0x0303, 0x04020009, 0x11030011, 0x11050011 }, /* 115.200 32.000 24.000 */
|
||||
{ 120000000, 0x0011, 0x04020011, 0x11050011, 0x11090011 }, /* 120.000 30.000 24.000 */
|
||||
{ 124800000, 0x0802, 0x04020009, 0x11050009, 0x11090009 }, /* 124.800 31.200 24.960 */
|
||||
{ 128000000, 0x0305, 0x04020011, 0x11050011, 0x02000305 }, /* 128.000 32.000 24.000 */
|
||||
{ 132000000, 0x0603, 0x04020011, 0x11050011, 0x02000305 }, /* 132.000 33.000 24.750 */
|
||||
{ 136000000, 0x0c02, 0x04020011, 0x11090009, 0x02000603 }, /* 136.000 32.640 24.727 */
|
||||
{ 140000000, 0x0021, 0x04020011, 0x11050021, 0x02000c02 }, /* 140.000 30.000 24.706 */
|
||||
{ 144000000, 0x0405, 0x04020011, 0x01020202, 0x11090021 }, /* 144.000 30.857 24.686 */
|
||||
{ 150857142, 0x0605, 0x04020021, 0x02000305, 0x02000605 }, /* 150.857 33.000 24.000 */
|
||||
{ 152000000, 0x0e02, 0x04020011, 0x11050021, 0x02000e02 }, /* 152.000 32.571 24.000 */
|
||||
{ 156000000, 0x0802, 0x04020005, 0x11050009, 0x11090009 }, /* 156.000 31.200 24.960 */
|
||||
{ 160000000, 0x0309, 0x04020011, 0x11090011, 0x02000309 }, /* 160.000 32.000 24.000 */
|
||||
{ 163200000, 0x0c02, 0x04020009, 0x11090009, 0x02000603 }, /* 163.200 32.640 24.727 */
|
||||
{ 168000000, 0x0205, 0x04020005, 0x11030021, 0x02000403 }, /* 168.000 32.000 24.889 */
|
||||
{ 176000000, 0x0602, 0x04020003, 0x11050005, 0x02000602 }, /* 176.000 33.000 24.000 */
|
||||
};
|
||||
typedef struct {
|
||||
uint32 mipsclock;
|
||||
uint32 sbclock;
|
||||
uint16 n;
|
||||
uint32 sb;
|
||||
uint32 pci33;
|
||||
uint32 m2;
|
||||
uint32 m3;
|
||||
uint32 ratio;
|
||||
uint32 ratio_parm;
|
||||
} n4m_table_t;
|
||||
|
||||
static n4m_table_t type2_table[] = {
|
||||
{ 180000000, 80000000, 0x0403, 0x01010000, 0x01020300, 0x01020600, 0x05000100, 0x94, 0x012a0115 },
|
||||
{ 180000000, 90000000, 0x0403, 0x01000100, 0x01020300, 0x01000100, 0x05000100, 0x21, 0x0aaa0555 },
|
||||
{ 200000000, 100000000, 0x0303, 0x01000000, 0x01000600, 0x01000000, 0x05000000, 0x21, 0x0aaa0555 },
|
||||
{ 211200000, 105600000, 0x0902, 0x01000200, 0x01030400, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 },
|
||||
{ 220800000, 110400000, 0x1500, 0x01000200, 0x01030400, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 },
|
||||
{ 230400000, 115200000, 0x0604, 0x01000200, 0x01020600, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 },
|
||||
{ 234000000, 104000000, 0x0b01, 0x01010000, 0x01010700, 0x01020600, 0x05000100, 0x94, 0x012a0115 },
|
||||
{ 240000000, 120000000, 0x0803, 0x01000200, 0x01020600, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 },
|
||||
{ 252000000, 126000000, 0x0504, 0x01000100, 0x01020500, 0x01000100, 0x05000100, 0x21, 0x0aaa0555 },
|
||||
{ 264000000, 132000000, 0x0903, 0x01000200, 0x01020700, 0x01000200, 0x05000200, 0x21, 0x0aaa0555 },
|
||||
{ 270000000, 120000000, 0x0703, 0x01010000, 0x01030400, 0x01020600, 0x05000100, 0x94, 0x012a0115 },
|
||||
{ 276000000, 122666666, 0x1500, 0x01010000, 0x01030400, 0x01020600, 0x05000100, 0x94, 0x012a0115 },
|
||||
{ 280000000, 140000000, 0x0503, 0x01000000, 0x01010600, 0x01000000, 0x05000000, 0x21, 0x0aaa0555 },
|
||||
{ 288000000, 128000000, 0x0604, 0x01010000, 0x01030400, 0x01020600, 0x05000100, 0x94, 0x012a0115 },
|
||||
{ 288000000, 144000000, 0x0404, 0x01000000, 0x01010600, 0x01000000, 0x05000000, 0x21, 0x0aaa0555 },
|
||||
{ 300000000, 133333333, 0x0803, 0x01010000, 0x01020600, 0x01020600, 0x05000100, 0x94, 0x012a0115 },
|
||||
{ 300000000, 150000000, 0x0803, 0x01000100, 0x01020600, 0x01000100, 0x05000100, 0x21, 0x0aaa0555 }
|
||||
};
|
||||
|
||||
static n4m_table_t type4_table[] = {
|
||||
{ 192000000, 96000000, 0x0702, 0x04020011, 0x11030011, 0x04020011, 0x04020003, 0x21, 0x0aaa0555 },
|
||||
{ 200000000, 100000000, 0x0009, 0x04020011, 0x11030011, 0x04020011, 0x04020003, 0x21, 0x0aaa0555 },
|
||||
{ 216000000, 108000000, 0x0111, 0x11020005, 0x01030303, 0x11020005, 0x04000005, 0x21, 0x0aaa0555 },
|
||||
{ 228000000, 101333333, 0x0e02, 0x11030003, 0x11210005, 0x11030305, 0x04000005, 0x94, 0x012a00a9 },
|
||||
{ 228000000, 114000000, 0x0e02, 0x11020005, 0x11210005, 0x11020005, 0x04000005, 0x21, 0x0aaa0555 },
|
||||
{ 240000000, 120000000, 0x0109, 0x11030002, 0x01050203, 0x11030002, 0x04000003, 0x21, 0x0aaa0555 },
|
||||
{ 252000000, 126000000, 0x0203, 0x04000005, 0x11050005, 0x04000005, 0x04000002, 0x21, 0x0aaa0555 },
|
||||
{ 264000000, 132000000, 0x0602, 0x04000005, 0x11050005, 0x04000005, 0x04000002, 0x21, 0x0aaa0555 },
|
||||
{ 272000000, 116571428, 0x0c02, 0x04000021, 0x02000909, 0x02000221, 0x04000003, 0x73, 0x254a14a9 },
|
||||
{ 280000000, 120000000, 0x0209, 0x04000021, 0x01030303, 0x02000221, 0x04000003, 0x73, 0x254a14a9 },
|
||||
{ 288000000, 123428571, 0x0111, 0x04000021, 0x01030303, 0x02000221, 0x04000003, 0x73, 0x254a14a9 },
|
||||
{ 300000000, 120000000, 0x0009, 0x04000009, 0x01030203, 0x02000902, 0x04000002, 0x52, 0x02520129 }
|
||||
};
|
||||
uint icache_size, ic_lsize;
|
||||
ulong start, end, dst;
|
||||
bool ret = FALSE;
|
||||
|
||||
/* get index of the current core */
|
||||
idx = sb_coreidx(sbh);
|
||||
|
||||
/* switch to extif or chipc core */
|
||||
if ((eir = (extifregs_t *) sb_setcore(sbh, SB_EXTIF, 0))) {
|
||||
pll_type = PLL_TYPE1;
|
||||
clockcontrol_n = &eir->clockcontrol_n;
|
||||
clockcontrol_sb = &eir->clockcontrol_sb;
|
||||
clockcontrol_pci = &eir->clockcontrol_pci;
|
||||
} else if ((cc = (chipcregs_t *) sb_setcore(sbh, SB_CC, 0))) {
|
||||
pll_type = R_REG(&cc->capabilities) & CAP_PLL_MASK;
|
||||
clockcontrol_n = &cc->clockcontrol_n;
|
||||
clockcontrol_sb = &cc->clockcontrol_sb;
|
||||
clockcontrol_pci = &cc->clockcontrol_pci;
|
||||
} else
|
||||
goto done;
|
||||
|
||||
/* Store the current clock register values */
|
||||
orig_n = R_REG(clockcontrol_n);
|
||||
orig_sb = R_REG(clockcontrol_sb);
|
||||
orig_pci = R_REG(clockcontrol_pci);
|
||||
|
||||
if (pll_type == PLL_TYPE1) {
|
||||
/* Keep the current PCI clock if not specified */
|
||||
if (pciclock == 0) {
|
||||
pciclock = sb_clock_rate(pll_type, R_REG(clockcontrol_n), R_REG(clockcontrol_pci));
|
||||
pciclock = (pciclock <= 25000000) ? 25000000 : 33000000;
|
||||
}
|
||||
|
||||
/* Search for the closest MIPS clock less than or equal to a preferred value */
|
||||
for (i = 0; i < ARRAYSIZE(type1_table); i++) {
|
||||
ASSERT(type1_table[i].mipsclock ==
|
||||
sb_clock_rate(pll_type, type1_table[i].n, type1_table[i].sb));
|
||||
if (type1_table[i].mipsclock > mipsclock)
|
||||
break;
|
||||
}
|
||||
if (i == 0) {
|
||||
ret = FALSE;
|
||||
goto done;
|
||||
} else {
|
||||
ret = TRUE;
|
||||
i--;
|
||||
}
|
||||
ASSERT(type1_table[i].mipsclock <= mipsclock);
|
||||
|
||||
/* No PLL change */
|
||||
if ((orig_n == type1_table[i].n) &&
|
||||
(orig_sb == type1_table[i].sb) &&
|
||||
(orig_pci == type1_table[i].pci33))
|
||||
goto done;
|
||||
|
||||
/* Set the PLL controls */
|
||||
W_REG(clockcontrol_n, type1_table[i].n);
|
||||
W_REG(clockcontrol_sb, type1_table[i].sb);
|
||||
if (pciclock == 25000000)
|
||||
W_REG(clockcontrol_pci, type1_table[i].pci25);
|
||||
else
|
||||
W_REG(clockcontrol_pci, type1_table[i].pci33);
|
||||
|
||||
/* Reset */
|
||||
sb_watchdog(sbh, 1);
|
||||
while (1);
|
||||
} else if ((pll_type == PLL_TYPE2) || (pll_type == PLL_TYPE4)) {
|
||||
n4m_table_t *table = (pll_type == PLL_TYPE2) ? type2_table : type4_table;
|
||||
uint tabsz = (pll_type == PLL_TYPE2) ? ARRAYSIZE(type2_table) : ARRAYSIZE(type4_table);
|
||||
|
||||
ASSERT(cc);
|
||||
|
||||
/* Store the current clock register values */
|
||||
orig_m2 = R_REG(&cc->clockcontrol_m2);
|
||||
orig_mips = R_REG(&cc->clockcontrol_mips);
|
||||
orig_ratio_parm = 0;
|
||||
|
||||
/* Look up current ratio */
|
||||
for (i = 0; i < tabsz; i++) {
|
||||
if ((orig_n == table[i].n) &&
|
||||
(orig_sb == table[i].sb) &&
|
||||
(orig_pci == table[i].pci33) &&
|
||||
(orig_m2 == table[i].m2) &&
|
||||
(orig_mips == table[i].m3)) {
|
||||
orig_ratio_parm = table[i].ratio_parm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Search for the closest MIPS clock greater or equal to a preferred value */
|
||||
for (i = 0; i < tabsz; i++) {
|
||||
ASSERT(table[i].mipsclock ==
|
||||
sb_clock_rate(pll_type, table[i].n, table[i].m3));
|
||||
if ((mipsclock <= table[i].mipsclock) &&
|
||||
((sbclock == 0) || (sbclock <= table[i].sbclock)))
|
||||
break;
|
||||
}
|
||||
if (i == tabsz) {
|
||||
ret = FALSE;
|
||||
goto done;
|
||||
} else {
|
||||
ret = TRUE;
|
||||
}
|
||||
|
||||
/* No PLL change */
|
||||
if ((orig_n == table[i].n) &&
|
||||
(orig_sb == table[i].sb) &&
|
||||
(orig_pci == table[i].pci33) &&
|
||||
(orig_m2 == table[i].m2) &&
|
||||
(orig_mips == table[i].m3))
|
||||
goto done;
|
||||
|
||||
/* Set the PLL controls */
|
||||
W_REG(clockcontrol_n, table[i].n);
|
||||
W_REG(clockcontrol_sb, table[i].sb);
|
||||
W_REG(clockcontrol_pci, table[i].pci33);
|
||||
W_REG(&cc->clockcontrol_m2, table[i].m2);
|
||||
W_REG(&cc->clockcontrol_mips, table[i].m3);
|
||||
|
||||
/* No ratio change */
|
||||
if (orig_ratio_parm == table[i].ratio_parm)
|
||||
goto end_fill;
|
||||
|
||||
new_ratio = table[i].ratio_parm;
|
||||
|
||||
icache_probe(&icache_size, &ic_lsize);
|
||||
|
||||
/* Preload the code into the cache */
|
||||
start = ((ulong) &&start_fill) & ~(ic_lsize - 1);
|
||||
end = ((ulong) &&end_fill + (ic_lsize - 1)) & ~(ic_lsize - 1);
|
||||
while (start < end) {
|
||||
cache_unroll(start, Fill_I);
|
||||
start += ic_lsize;
|
||||
}
|
||||
|
||||
/* Copy the handler */
|
||||
start = (ulong) &handler;
|
||||
end = (ulong) &afterhandler;
|
||||
dst = KSEG1ADDR(0x180);
|
||||
for (i = 0; i < (end - start); i += 4)
|
||||
*((ulong *)(dst + i)) = *((ulong *)(start + i));
|
||||
|
||||
/* Preload handler into the cache one line at a time */
|
||||
for (i = 0; i < (end - start); i += 4)
|
||||
cache_unroll(dst + i, Fill_I);
|
||||
|
||||
/* Clear BEV bit */
|
||||
MTC0(C0_STATUS, 0, MFC0(C0_STATUS, 0) & ~ST0_BEV);
|
||||
|
||||
/* Enable interrupts */
|
||||
MTC0(C0_STATUS, 0, MFC0(C0_STATUS, 0) | (ALLINTS | ST0_IE));
|
||||
|
||||
/* Enable MIPS timer interrupt */
|
||||
if (!(mipsr = sb_setcore(sbh, SB_MIPS, 0)) &&
|
||||
!(mipsr = sb_setcore(sbh, SB_MIPS33, 0)))
|
||||
ASSERT(mipsr);
|
||||
W_REG(&mipsr->intmask, 1);
|
||||
|
||||
start_fill:
|
||||
/* step 1, set clock ratios */
|
||||
MTC0(C0_BROADCOM, 3, new_ratio);
|
||||
MTC0(C0_BROADCOM, 1, 8);
|
||||
|
||||
/* step 2: program timer intr */
|
||||
W_REG(&mipsr->timer, 100);
|
||||
(void) R_REG(&mipsr->timer);
|
||||
|
||||
/* step 3, switch to async */
|
||||
sync_mode = MFC0(C0_BROADCOM, 4);
|
||||
MTC0(C0_BROADCOM, 4, 1 << 22);
|
||||
|
||||
/* step 4, set cfg active */
|
||||
MTC0(C0_BROADCOM, 2, 0x9);
|
||||
|
||||
|
||||
/* steps 5 & 6 */
|
||||
__asm__ __volatile__ (
|
||||
".set\tmips3\n\t"
|
||||
"wait\n\t"
|
||||
".set\tmips0"
|
||||
);
|
||||
|
||||
/* step 7, clear cfg_active */
|
||||
MTC0(C0_BROADCOM, 2, 0);
|
||||
|
||||
/* Additional Step: set back to orig sync mode */
|
||||
MTC0(C0_BROADCOM, 4, sync_mode);
|
||||
|
||||
/* step 8, fake soft reset */
|
||||
MTC0(C0_BROADCOM, 5, MFC0(C0_BROADCOM, 5) | 4);
|
||||
|
||||
end_fill:
|
||||
/* step 9 set watchdog timer */
|
||||
sb_watchdog(sbh, 20);
|
||||
(void) R_REG(&cc->chipid);
|
||||
|
||||
/* step 11 */
|
||||
__asm__ __volatile__ (
|
||||
".set\tmips3\n\t"
|
||||
"sync\n\t"
|
||||
"wait\n\t"
|
||||
".set\tmips0"
|
||||
);
|
||||
while (1);
|
||||
}
|
||||
|
||||
done:
|
||||
/* switch back to previous core */
|
||||
sb_setcoreidx(sbh, idx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* returns the ncdl value to be programmed into sdram_ncdl for calibration */
|
||||
uint32
|
||||
sb_memc_get_ncdl(void *sbh)
|
||||
{
|
||||
sbmemcregs_t *memc;
|
||||
uint32 ret = 0;
|
||||
uint32 config, rd, wr, misc, dqsg, cd, sm, sd;
|
||||
uint idx, rev;
|
||||
|
||||
idx = sb_coreidx(sbh);
|
||||
|
||||
memc = (sbmemcregs_t *)sb_setcore(sbh, SB_MEMC, 0);
|
||||
if (memc == 0)
|
||||
goto out;
|
||||
|
||||
rev = sb_corerev(sbh);
|
||||
|
||||
config = R_REG(&memc->config);
|
||||
wr = R_REG(&memc->wrncdlcor);
|
||||
rd = R_REG(&memc->rdncdlcor);
|
||||
misc = R_REG(&memc->miscdlyctl);
|
||||
dqsg = R_REG(&memc->dqsgatencdl);
|
||||
|
||||
rd &= MEMC_RDNCDLCOR_RD_MASK;
|
||||
wr &= MEMC_WRNCDLCOR_WR_MASK;
|
||||
dqsg &= MEMC_DQSGATENCDL_G_MASK;
|
||||
|
||||
if (config & MEMC_CONFIG_DDR) {
|
||||
ret = (wr << 16) | (rd << 8) | dqsg;
|
||||
} else {
|
||||
if (rev > 0)
|
||||
cd = rd;
|
||||
else
|
||||
cd = (rd == MEMC_CD_THRESHOLD) ? rd : (wr + MEMC_CD_THRESHOLD);
|
||||
sm = (misc & MEMC_MISC_SM_MASK) >> MEMC_MISC_SM_SHIFT;
|
||||
sd = (misc & MEMC_MISC_SD_MASK) >> MEMC_MISC_SD_SHIFT;
|
||||
ret = (sm << 16) | (sd << 8) | cd;
|
||||
}
|
||||
|
||||
out:
|
||||
/* switch back to previous core */
|
||||
sb_setcoreidx(sbh, idx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* Low-Level PCI and SB support for BCM47xx
|
||||
*
|
||||
* Copyright 2004, Broadcom Corporation
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
|
||||
* KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
|
||||
* SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <typedefs.h>
|
||||
#include <pcicfg.h>
|
||||
#include <bcmdevs.h>
|
||||
#include <sbconfig.h>
|
||||
#include <sbpci.h>
|
||||
#include <osl.h>
|
||||
#include <bcmendian.h>
|
||||
#include <bcmutils.h>
|
||||
#include <sbutils.h>
|
||||
#include <bcmnvram.h>
|
||||
#include <hndmips.h>
|
||||
|
||||
/* Can free sbpci_init() memory after boot */
|
||||
#ifndef linux
|
||||
#define __init
|
||||
#endif
|
||||
|
||||
/* Emulated configuration space */
|
||||
static pci_config_regs sb_config_regs[SB_MAXCORES];
|
||||
|
||||
/* Banned cores */
|
||||
static uint16 pci_ban[32] = { 0 };
|
||||
static uint pci_banned = 0;
|
||||
|
||||
/* CardBus mode */
|
||||
static bool cardbus = FALSE;
|
||||
|
||||
/* Disable PCI host core */
|
||||
static bool pci_disabled = FALSE;
|
||||
|
||||
/*
|
||||
* Functions for accessing external PCI configuration space
|
||||
*/
|
||||
|
||||
/* Assume one-hot slot wiring */
|
||||
#define PCI_SLOT_MAX 16
|
||||
|
||||
static uint32
|
||||
config_cmd(void *sbh, uint bus, uint dev, uint func, uint off)
|
||||
{
|
||||
uint coreidx;
|
||||
sbpciregs_t *regs;
|
||||
uint32 addr = 0;
|
||||
|
||||
/* CardBusMode supports only one device */
|
||||
if (cardbus && dev > 1)
|
||||
return 0;
|
||||
|
||||
coreidx = sb_coreidx(sbh);
|
||||
regs = (sbpciregs_t *) sb_setcore(sbh, SB_PCI, 0);
|
||||
|
||||
/* Type 0 transaction */
|
||||
if (bus == 1) {
|
||||
/* Skip unwired slots */
|
||||
if (dev < PCI_SLOT_MAX) {
|
||||
/* Slide the PCI window to the appropriate slot */
|
||||
W_REG(®s->sbtopci1, SBTOPCI_CFG0 | ((1 << (dev + 16)) & SBTOPCI1_MASK));
|
||||
addr = SB_PCI_CFG | ((1 << (dev + 16)) & ~SBTOPCI1_MASK) |
|
||||
(func << 8) | (off & ~3);
|
||||
}
|
||||
}
|
||||
|
||||
/* Type 1 transaction */
|
||||
else {
|
||||
W_REG(®s->sbtopci1, SBTOPCI_CFG1);
|
||||
addr = SB_PCI_CFG | (bus << 16) | (dev << 11) | (func << 8) | (off & ~3);
|
||||
}
|
||||
|
||||
sb_setcoreidx(sbh, coreidx);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int
|
||||
extpci_read_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len)
|
||||
{
|
||||
uint32 addr, *reg = NULL, val;
|
||||
int ret = 0;
|
||||
|
||||
if (pci_disabled ||
|
||||
!(addr = config_cmd(sbh, bus, dev, func, off)) ||
|
||||
!(reg = (uint32 *) REG_MAP(addr, len)) ||
|
||||
BUSPROBE(val, reg))
|
||||
val = 0xffffffff;
|
||||
|
||||
val >>= 8 * (off & 3);
|
||||
if (len == 4)
|
||||
*((uint32 *) buf) = val;
|
||||
else if (len == 2)
|
||||
*((uint16 *) buf) = (uint16) val;
|
||||
else if (len == 1)
|
||||
*((uint8 *) buf) = (uint8) val;
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
if (reg)
|
||||
REG_UNMAP(reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
extpci_write_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len)
|
||||
{
|
||||
uint32 addr, *reg = NULL, val;
|
||||
int ret = 0;
|
||||
|
||||
if (pci_disabled ||
|
||||
!(addr = config_cmd(sbh, bus, dev, func, off)) ||
|
||||
!(reg = (uint32 *) REG_MAP(addr, len)) ||
|
||||
BUSPROBE(val, reg))
|
||||
goto done;
|
||||
|
||||
if (len == 4)
|
||||
val = *((uint32 *) buf);
|
||||
else if (len == 2) {
|
||||
val &= ~(0xffff << (8 * (off & 3)));
|
||||
val |= *((uint16 *) buf) << (8 * (off & 3));
|
||||
} else if (len == 1) {
|
||||
val &= ~(0xff << (8 * (off & 3)));
|
||||
val |= *((uint8 *) buf) << (8 * (off & 3));
|
||||
} else
|
||||
ret = -1;
|
||||
|
||||
W_REG(reg, val);
|
||||
|
||||
done:
|
||||
if (reg)
|
||||
REG_UNMAP(reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for accessing translated SB configuration space
|
||||
*/
|
||||
|
||||
static int
|
||||
sb_read_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len)
|
||||
{
|
||||
pci_config_regs *cfg;
|
||||
|
||||
if (dev >= SB_MAXCORES || (off + len) > sizeof(pci_config_regs))
|
||||
return -1;
|
||||
cfg = &sb_config_regs[dev];
|
||||
|
||||
ASSERT(ISALIGNED(off, len));
|
||||
ASSERT(ISALIGNED(buf, len));
|
||||
|
||||
if (len == 4)
|
||||
*((uint32 *) buf) = ltoh32(*((uint32 *)((ulong) cfg + off)));
|
||||
else if (len == 2)
|
||||
*((uint16 *) buf) = ltoh16(*((uint16 *)((ulong) cfg + off)));
|
||||
else if (len == 1)
|
||||
*((uint8 *) buf) = *((uint8 *)((ulong) cfg + off));
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sb_write_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len)
|
||||
{
|
||||
uint coreidx, n;
|
||||
void *regs;
|
||||
sbconfig_t *sb;
|
||||
pci_config_regs *cfg;
|
||||
|
||||
if (dev >= SB_MAXCORES || (off + len) > sizeof(pci_config_regs))
|
||||
return -1;
|
||||
cfg = &sb_config_regs[dev];
|
||||
|
||||
ASSERT(ISALIGNED(off, len));
|
||||
ASSERT(ISALIGNED(buf, len));
|
||||
|
||||
/* Emulate BAR sizing */
|
||||
if (off >= OFFSETOF(pci_config_regs, base[0]) && off <= OFFSETOF(pci_config_regs, base[3]) &&
|
||||
len == 4 && *((uint32 *) buf) == ~0) {
|
||||
coreidx = sb_coreidx(sbh);
|
||||
if ((regs = sb_setcoreidx(sbh, dev))) {
|
||||
sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF);
|
||||
/* Highest numbered address match register */
|
||||
n = (R_REG(&sb->sbidlow) & SBIDL_AR_MASK) >> SBIDL_AR_SHIFT;
|
||||
if (off == OFFSETOF(pci_config_regs, base[0]))
|
||||
cfg->base[0] = ~(sb_size(R_REG(&sb->sbadmatch0)) - 1);
|
||||
else if (off == OFFSETOF(pci_config_regs, base[1]) && n >= 1)
|
||||
cfg->base[1] = ~(sb_size(R_REG(&sb->sbadmatch1)) - 1);
|
||||
else if (off == OFFSETOF(pci_config_regs, base[2]) && n >= 2)
|
||||
cfg->base[2] = ~(sb_size(R_REG(&sb->sbadmatch2)) - 1);
|
||||
else if (off == OFFSETOF(pci_config_regs, base[3]) && n >= 3)
|
||||
cfg->base[3] = ~(sb_size(R_REG(&sb->sbadmatch3)) - 1);
|
||||
}
|
||||
sb_setcoreidx(sbh, coreidx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len == 4)
|
||||
*((uint32 *)((ulong) cfg + off)) = htol32(*((uint32 *) buf));
|
||||
else if (len == 2)
|
||||
*((uint16 *)((ulong) cfg + off)) = htol16(*((uint16 *) buf));
|
||||
else if (len == 1)
|
||||
*((uint8 *)((ulong) cfg + off)) = *((uint8 *) buf);
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sbpci_read_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len)
|
||||
{
|
||||
if (bus == 0)
|
||||
return sb_read_config(sbh, bus, dev, func, off, buf, len);
|
||||
else
|
||||
return extpci_read_config(sbh, bus, dev, func, off, buf, len);
|
||||
}
|
||||
|
||||
int
|
||||
sbpci_write_config(void *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len)
|
||||
{
|
||||
if (bus == 0)
|
||||
return sb_write_config(sbh, bus, dev, func, off, buf, len);
|
||||
else
|
||||
return extpci_write_config(sbh, bus, dev, func, off, buf, len);
|
||||
}
|
||||
|
||||
void
|
||||
sbpci_ban(uint16 core)
|
||||
{
|
||||
if (pci_banned < ARRAYSIZE(pci_ban))
|
||||
pci_ban[pci_banned++] = core;
|
||||
}
|
||||
//#define CT4712_WR 1 /* Workaround for 4712 */
|
||||
|
||||
int __init
|
||||
sbpci_init(void *sbh)
|
||||
{
|
||||
uint chip, chiprev, chippkg, coreidx, host, i;
|
||||
uint32 boardflags;
|
||||
sbpciregs_t *pci;
|
||||
sbconfig_t *sb;
|
||||
pci_config_regs *cfg;
|
||||
void *regs;
|
||||
char varname[8];
|
||||
int CT4712_WR;
|
||||
uint wlidx = 0;
|
||||
uint16 vendor, core;
|
||||
uint8 class, subclass, progif;
|
||||
uint32 val;
|
||||
uint32 sbips_int_mask[] = { 0, SBIPS_INT1_MASK, SBIPS_INT2_MASK, SBIPS_INT3_MASK, SBIPS_INT4_MASK };
|
||||
uint32 sbips_int_shift[] = { 0, 0, SBIPS_INT2_SHIFT, SBIPS_INT3_SHIFT, SBIPS_INT4_SHIFT };
|
||||
|
||||
chip = sb_chip(sbh);
|
||||
chiprev = sb_chiprev(sbh);
|
||||
chippkg = sb_chippkg(sbh);
|
||||
coreidx = sb_coreidx(sbh);
|
||||
|
||||
if (!(pci = (sbpciregs_t *) sb_setcore(sbh, SB_PCI, 0)))
|
||||
return -1;
|
||||
sb_core_reset(sbh, 0);
|
||||
|
||||
/* In some board, */
|
||||
if(nvram_match("boardtype", "bcm94710dev"))
|
||||
CT4712_WR = 0;
|
||||
else
|
||||
CT4712_WR = 1;
|
||||
|
||||
boardflags = (uint32) getintvar(NULL, "boardflags");
|
||||
|
||||
if ((chip == BCM4310_DEVICE_ID) && (chiprev == 0))
|
||||
pci_disabled = TRUE;
|
||||
|
||||
/*
|
||||
* The 200-pin BCM4712 package does not bond out PCI. Even when
|
||||
* PCI is bonded out, some boards may leave the pins
|
||||
* floating.
|
||||
*/
|
||||
if (((chip == BCM4712_DEVICE_ID) && (chippkg == BCM4712SMALL_PKG_ID)) ||
|
||||
(boardflags & BFL_NOPCI) || CT4712_WR)
|
||||
pci_disabled = TRUE;
|
||||
|
||||
/*
|
||||
* If the PCI core should not be touched (disabled, not bonded
|
||||
* out, or pins floating), do not even attempt to access core
|
||||
* registers. Otherwise, try to determine if it is in host
|
||||
* mode.
|
||||
*/
|
||||
if (pci_disabled)
|
||||
host = 0;
|
||||
else
|
||||
host = !BUSPROBE(val, &pci->control);
|
||||
|
||||
if (!host) {
|
||||
/* Disable PCI interrupts in client mode */
|
||||
sb = (sbconfig_t *)((ulong) pci + SBCONFIGOFF);
|
||||
W_REG(&sb->sbintvec, 0);
|
||||
|
||||
/* Disable the PCI bridge in client mode */
|
||||
sbpci_ban(SB_PCI);
|
||||
printf("PCI: Disabled\n");
|
||||
} else {
|
||||
/* Reset the external PCI bus and enable the clock */
|
||||
W_REG(&pci->control, 0x5); /* enable the tristate drivers */
|
||||
W_REG(&pci->control, 0xd); /* enable the PCI clock */
|
||||
OSL_DELAY(150); /* delay > 100 us */
|
||||
W_REG(&pci->control, 0xf); /* deassert PCI reset */
|
||||
W_REG(&pci->arbcontrol, PCI_INT_ARB); /* use internal arbiter */
|
||||
OSL_DELAY(1); /* delay 1 us */
|
||||
|
||||
/* Enable CardBusMode */
|
||||
cardbus = nvram_match("cardbus", "1");
|
||||
if (cardbus) {
|
||||
printf("PCI: Enabling CardBus\n");
|
||||
/* GPIO 1 resets the CardBus device on bcm94710ap */
|
||||
sb_gpioout(sbh, 1, 1);
|
||||
sb_gpioouten(sbh, 1, 1);
|
||||
W_REG(&pci->sprom[0], R_REG(&pci->sprom[0]) | 0x400);
|
||||
}
|
||||
|
||||
/* 64 MB I/O access window */
|
||||
W_REG(&pci->sbtopci0, SBTOPCI_IO);
|
||||
/* 64 MB configuration access window */
|
||||
W_REG(&pci->sbtopci1, SBTOPCI_CFG0);
|
||||
/* 1 GB memory access window */
|
||||
W_REG(&pci->sbtopci2, SBTOPCI_MEM | SB_PCI_DMA);
|
||||
|
||||
/* Enable PCI bridge BAR0 prefetch and burst */
|
||||
val = 6;
|
||||
sbpci_write_config(sbh, 1, 0, 0, PCI_CFG_CMD, &val, sizeof(val));
|
||||
|
||||
/* Enable PCI interrupts */
|
||||
W_REG(&pci->intmask, PCI_INTA);
|
||||
}
|
||||
|
||||
/* Scan the SB bus */
|
||||
bzero(sb_config_regs, sizeof(sb_config_regs));
|
||||
for (cfg = sb_config_regs; cfg < &sb_config_regs[SB_MAXCORES]; cfg++) {
|
||||
cfg->vendor = 0xffff;
|
||||
if (!(regs = sb_setcoreidx(sbh, cfg - sb_config_regs)))
|
||||
continue;
|
||||
sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF);
|
||||
|
||||
/* Read ID register and parse vendor and core */
|
||||
val = R_REG(&sb->sbidhigh);
|
||||
vendor = (val & SBIDH_VC_MASK) >> SBIDH_VC_SHIFT;
|
||||
core = (val & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT;
|
||||
progif = 0;
|
||||
|
||||
/* Check if this core is banned */
|
||||
for (i = 0; i < pci_banned; i++)
|
||||
if (core == pci_ban[i])
|
||||
break;
|
||||
if (i < pci_banned)
|
||||
continue;
|
||||
|
||||
/* Known vendor translations */
|
||||
switch (vendor) {
|
||||
case SB_VEND_BCM:
|
||||
vendor = VENDOR_BROADCOM;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Determine class based on known core codes */
|
||||
switch (core) {
|
||||
case SB_ILINE20:
|
||||
class = PCI_CLASS_NET;
|
||||
subclass = PCI_NET_ETHER;
|
||||
core = BCM47XX_ILINE_ID;
|
||||
break;
|
||||
case SB_ILINE100:
|
||||
class = PCI_CLASS_NET;
|
||||
subclass = PCI_NET_ETHER;
|
||||
core = BCM4610_ILINE_ID;
|
||||
break;
|
||||
case SB_ENET:
|
||||
class = PCI_CLASS_NET;
|
||||
subclass = PCI_NET_ETHER;
|
||||
core = BCM47XX_ENET_ID;
|
||||
break;
|
||||
case SB_SDRAM:
|
||||
case SB_MEMC:
|
||||
class = PCI_CLASS_MEMORY;
|
||||
subclass = PCI_MEMORY_RAM;
|
||||
break;
|
||||
case SB_PCI:
|
||||
class = PCI_CLASS_BRIDGE;
|
||||
subclass = PCI_BRIDGE_PCI;
|
||||
break;
|
||||
case SB_MIPS:
|
||||
case SB_MIPS33:
|
||||
class = PCI_CLASS_CPU;
|
||||
subclass = PCI_CPU_MIPS;
|
||||
break;
|
||||
case SB_CODEC:
|
||||
class = PCI_CLASS_COMM;
|
||||
subclass = PCI_COMM_MODEM;
|
||||
core = BCM47XX_V90_ID;
|
||||
break;
|
||||
case SB_USB:
|
||||
class = PCI_CLASS_SERIAL;
|
||||
subclass = PCI_SERIAL_USB;
|
||||
progif = 0x10; /* OHCI */
|
||||
core = BCM47XX_USB_ID;
|
||||
break;
|
||||
case SB_USB11H:
|
||||
class = PCI_CLASS_SERIAL;
|
||||
subclass = PCI_SERIAL_USB;
|
||||
progif = 0x10; /* OHCI */
|
||||
core = BCM47XX_USBH_ID;
|
||||
break;
|
||||
case SB_USB11D:
|
||||
class = PCI_CLASS_SERIAL;
|
||||
subclass = PCI_SERIAL_USB;
|
||||
core = BCM47XX_USBD_ID;
|
||||
break;
|
||||
case SB_IPSEC:
|
||||
class = PCI_CLASS_CRYPT;
|
||||
subclass = PCI_CRYPT_NETWORK;
|
||||
core = BCM47XX_IPSEC_ID;
|
||||
break;
|
||||
case SB_EXTIF:
|
||||
case SB_CC:
|
||||
class = PCI_CLASS_MEMORY;
|
||||
subclass = PCI_MEMORY_FLASH;
|
||||
break;
|
||||
case SB_D11:
|
||||
class = PCI_CLASS_NET;
|
||||
subclass = PCI_NET_OTHER;
|
||||
/* Let an nvram variable override this */
|
||||
sprintf(varname, "wl%did", wlidx);
|
||||
wlidx++;
|
||||
if ((core = getintvar(NULL, varname)) == 0) {
|
||||
if (chip == BCM4712_DEVICE_ID) {
|
||||
if (chippkg == BCM4712SMALL_PKG_ID)
|
||||
core = BCM4306_D11G_ID;
|
||||
else
|
||||
core = BCM4306_D11DUAL_ID;
|
||||
} else {
|
||||
/* 4310 */
|
||||
core = BCM4310_D11B_ID;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
class = subclass = progif = 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Supported translations */
|
||||
cfg->vendor = htol16(vendor);
|
||||
cfg->device = htol16(core);
|
||||
cfg->rev_id = chiprev;
|
||||
cfg->prog_if = progif;
|
||||
cfg->sub_class = subclass;
|
||||
cfg->base_class = class;
|
||||
cfg->base[0] = htol32(sb_base(R_REG(&sb->sbadmatch0)));
|
||||
cfg->base[1] = htol32(sb_base(R_REG(&sb->sbadmatch1)));
|
||||
cfg->base[2] = htol32(sb_base(R_REG(&sb->sbadmatch2)));
|
||||
cfg->base[3] = htol32(sb_base(R_REG(&sb->sbadmatch3)));
|
||||
cfg->base[4] = 0;
|
||||
cfg->base[5] = 0;
|
||||
if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI)
|
||||
cfg->header_type = PCI_HEADER_BRIDGE;
|
||||
else
|
||||
cfg->header_type = PCI_HEADER_NORMAL;
|
||||
/* Save core interrupt flag */
|
||||
cfg->int_pin = R_REG(&sb->sbtpsflag) & SBTPS_NUM0_MASK;
|
||||
/* Default to MIPS shared interrupt 0 */
|
||||
cfg->int_line = 0;
|
||||
/* MIPS sbipsflag maps core interrupt flags to interrupts 1 through 4 */
|
||||
if ((regs = sb_setcore(sbh, SB_MIPS, 0)) ||
|
||||
(regs = sb_setcore(sbh, SB_MIPS33, 0))) {
|
||||
sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF);
|
||||
val = R_REG(&sb->sbipsflag);
|
||||
for (cfg->int_line = 1; cfg->int_line <= 4; cfg->int_line++) {
|
||||
if (((val & sbips_int_mask[cfg->int_line]) >> sbips_int_shift[cfg->int_line]) == cfg->int_pin)
|
||||
break;
|
||||
}
|
||||
if (cfg->int_line > 4)
|
||||
cfg->int_line = 0;
|
||||
}
|
||||
/* Emulated core */
|
||||
*((uint32 *) &cfg->sprom_control) = 0xffffffff;
|
||||
}
|
||||
|
||||
sb_setcoreidx(sbh, coreidx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
sbpci_check(void *sbh)
|
||||
{
|
||||
uint coreidx;
|
||||
sbpciregs_t *pci;
|
||||
uint32 sbtopci1;
|
||||
uint32 buf[64], *ptr, i;
|
||||
ulong pa;
|
||||
volatile uint j;
|
||||
|
||||
coreidx = sb_coreidx(sbh);
|
||||
pci = (sbpciregs_t *) sb_setcore(sbh, SB_PCI, 0);
|
||||
|
||||
/* Clear the test array */
|
||||
pa = (ulong) DMA_MAP(NULL, buf, sizeof(buf), DMA_RX, NULL);
|
||||
ptr = (uint32 *) OSL_UNCACHED(&buf[0]);
|
||||
memset(ptr, 0, sizeof(buf));
|
||||
|
||||
/* Point PCI window 1 to memory */
|
||||
sbtopci1 = R_REG(&pci->sbtopci1);
|
||||
W_REG(&pci->sbtopci1, SBTOPCI_MEM | (pa & SBTOPCI1_MASK));
|
||||
|
||||
/* Fill the test array via PCI window 1 */
|
||||
ptr = (uint32 *) REG_MAP(SB_PCI_CFG + (pa & ~SBTOPCI1_MASK), sizeof(buf));
|
||||
for (i = 0; i < ARRAYSIZE(buf); i++) {
|
||||
for (j = 0; j < 2; j++);
|
||||
W_REG(&ptr[i], i);
|
||||
}
|
||||
REG_UNMAP(ptr);
|
||||
|
||||
/* Restore PCI window 1 */
|
||||
W_REG(&pci->sbtopci1, sbtopci1);
|
||||
|
||||
/* Check the test array */
|
||||
DMA_UNMAP(NULL, pa, sizeof(buf), DMA_RX, NULL);
|
||||
ptr = (uint32 *) OSL_UNCACHED(&buf[0]);
|
||||
for (i = 0; i < ARRAYSIZE(buf); i++) {
|
||||
if (ptr[i] != i)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Change the clock if the test fails */
|
||||
if (i < ARRAYSIZE(buf)) {
|
||||
uint32 req, cur;
|
||||
|
||||
cur = sb_clock(sbh);
|
||||
printf("PCI: Test failed at %d MHz\n", (cur + 500000) / 1000000);
|
||||
for (req = 104000000; req < 176000000; req += 4000000) {
|
||||
printf("PCI: Resetting to %d MHz\n", (req + 500000) / 1000000);
|
||||
/* This will only reset if the clocks are valid and have changed */
|
||||
sb_mips_setclock(sbh, req, 0, 0);
|
||||
}
|
||||
/* Should not reach here */
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
sb_setcoreidx(sbh, coreidx);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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)
|
||||
130
package/linux/kernel-source/arch/mips/brcm-boards/generic/irq.c
Normal file
130
package/linux/kernel-source/arch/mips/brcm-boards/generic/irq.c
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user