diff --git a/common/cmd_fpga.c b/common/cmd_fpga.c new file mode 100644 index 0000000..4e4c4ab --- /dev/null +++ b/common/cmd_fpga.c @@ -0,0 +1,158 @@ +/* + * (C) Copyright 2000, 2001 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +/* + * FPGA support + */ +#include +#include +#include +#include +#if (CONFIG_COMMANDS & CFG_CMD_NET) +#include +#endif + +#if 0 +#define FPGA_DEBUG +#endif + +#ifdef FPGA_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +#if defined (CONFIG_FPGA) && ( CONFIG_COMMANDS & CFG_CMD_FPGA ) + +/* Local functions */ +static void fpga_usage ( cmd_tbl_t *cmdtp ); +static int fpga_get_op( char *opstr ); + +/* Local defines */ +#define FPGA_NONE -1 +#define FPGA_INFO 0 +#define FPGA_LOAD 1 +#define FPGA_DUMP 3 + +/* ------------------------------------------------------------------------- */ +/* command form: + * fpga + * where op is 'load', 'dump', or 'info' + * If there is no device number field, the fpga environment variable is used. + * If there is no data addr field, the fpgadata environment variable is used. + * The info command requires no data address field. + */ +int +do_fpga (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int op, dev = FPGA_INVALID_DEVICE; + size_t data_size = 0; + void *fpga_data = NULL; + char *devstr = getenv("fpga"); + char *datastr = getenv("fpgadata"); + + if ( devstr ) dev = (int)simple_strtoul( devstr, NULL, 16 ); + if ( datastr ) fpga_data = (void *)simple_strtoul( datastr, NULL, 16 ); + + switch ( argc ) + { + case 5: /* fpga */ + data_size = simple_strtoul( argv[4], NULL, 16 ); + case 4: /* fpga */ + fpga_data = (void *)simple_strtoul( argv[3], NULL, 16 ); + PRINTF(__FUNCTION__": fpga_data = 0x%x\n", (uint)fpga_data ); + case 3: /* fpga */ + dev = (int)simple_strtoul( argv[2], NULL, 16 ); + PRINTF(__FUNCTION__": device = %d\n", dev ); + /* FIXME - this is a really weak test */ + if (( argc == 3 ) && ( dev > fpga_count() )) { /* must be buffer ptr */ + PRINTF(__FUNCTION__": Assuming buffer pointer in arg 3\n"); + fpga_data = (void *)dev; + PRINTF(__FUNCTION__": fpga_data = 0x%x\n", (uint)fpga_data ); + dev = FPGA_INVALID_DEVICE; /* reset device num */ + } + case 2: /* fpga */ + op = (int)fpga_get_op( argv[1] ); + break; + default: + PRINTF(__FUNCTION__": Too many or too few args (%d)\n", argc ); + op = FPGA_NONE; /* force usage display */ + break; + } + + switch ( op ) { + case FPGA_NONE: + fpga_usage( cmdtp ); + break; + + case FPGA_INFO: + fpga_info( dev ); + break; + + case FPGA_LOAD: + fpga_load( dev, fpga_data, data_size ); + break; + + case FPGA_DUMP: + fpga_dump( dev, fpga_data, data_size ); + break; + + default: + printf( "Unknown operation.\n" ); + fpga_usage( cmdtp ); + break; + } + return 0; +} + +static void fpga_usage ( cmd_tbl_t *cmdtp ) +{ + printf( "Usage:\n%s\n", cmdtp->usage ); +} + +/* + * Map op to supported operations. We don't use a table since we + * would just have to relocate it from flash anyway. + */ +static int fpga_get_op( char *opstr ) +{ + int op = FPGA_NONE; + + if (!strcmp ("info", opstr)) { + op = FPGA_INFO; + } + else if (!strcmp ("load", opstr)) { + op = FPGA_LOAD; + } + else if (!strcmp ("dump", opstr)) { + op = FPGA_DUMP; + } + + if ( op == FPGA_NONE ) { + printf ("Unknown fpga operation \"%s\"\n", opstr); + } + return op; +} + +#endif /* CONFIG_FPGA && CONFIG_COMMANDS & CFG_CMD_FPGA */ diff --git a/common/kgdb.c b/common/kgdb.c new file mode 100644 index 0000000..b563094 --- /dev/null +++ b/common/kgdb.c @@ -0,0 +1,580 @@ +/* taken from arch/ppc/kernel/ppc-stub.c */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or its performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include + +#include +#include + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + +#undef KGDB_DEBUG + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + */ +#define BUFMAX 1024 +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static char remcomRegBuffer[BUFMAX]; + +static int initialized = 0; +static int kgdb_active = 0, first_entry = 1; +static struct pt_regs entry_regs; +static u_int error_jmp_buf[BUFMAX/2]; +static int longjmp_on_fault = 0; +#ifdef KGDB_DEBUG +static int kdebug = 1; +#endif + +static const char hexchars[]="0123456789abcdef"; + +/* Convert ch from a hex digit to an int */ +static int +hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null). + */ +static unsigned char * +mem2hex(char *mem, char *buf, int count) +{ + unsigned char ch; + + longjmp_on_fault = 1; + while (count-- > 0) { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + *buf = 0; + longjmp_on_fault = 0; + return buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte fetched from buf. +*/ +static char * +hex2mem(char *buf, char *mem, int count) +{ + int i, hexValue; + unsigned char ch; + char *mem_start = mem; + + longjmp_on_fault = 1; + for (i=0; i# */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other + * characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') { +#ifdef KGDB_DEBUG + if (kdebug) + putc(ch); +#endif + ; + } + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ +static void +putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch, recv; + + /* $#. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + recv = getDebugChar(); + } while ((recv & 0x7f) != '+'); +} + +/* + * This function does all command processing for interfacing to gdb. + */ +static int +handle_exception (struct pt_regs *regs) +{ + int addr; + int length; + char *ptr; + kgdb_data kd; + int i; + + if (!initialized) { + printf("kgdb: exception before kgdb is initialized! huh?\n"); + return (0); + } + + /* probably should check which exception occured as well */ + if (longjmp_on_fault) { + longjmp_on_fault = 0; + kgdb_longjmp((long*)error_jmp_buf, KGDBERR_MEMFAULT); + panic("kgdb longjump failed!\n"); + } + + if (kgdb_active) { + printf("kgdb: unexpected exception from within kgdb\n"); + return (0); + } + kgdb_active = 1; + + kgdb_interruptible(0); + + printf("kgdb: handle_exception; trap [0x%x]\n", kgdb_trap(regs)); + + if (kgdb_setjmp((long*)error_jmp_buf) != 0) + panic("kgdb: error or fault in entry init!\n"); + + kgdb_enter(regs, &kd); + + if (first_entry) { + /* + * the first time we enter kgdb, we save the processor + * state so that we can return to the monitor if the + * remote end quits gdb (or at least, tells us to quit + * with the 'k' packet) + */ + entry_regs = *regs; + first_entry = 0; + } + + ptr = remcomOutBuffer; + + *ptr++ = 'T'; + + *ptr++ = hexchars[kd.sigval >> 4]; + *ptr++ = hexchars[kd.sigval & 0xf]; + + for (i = 0; i < kd.nregs; i++) { + kgdb_reg *rp = &kd.regs[i]; + + *ptr++ = hexchars[rp->num >> 4]; + *ptr++ = hexchars[rp->num & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)&rp->val, ptr, 4); + *ptr++ = ';'; + } + + *ptr = 0; + +#ifdef KGDB_DEBUG + if (kdebug) + printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); +#endif + + putpacket(remcomOutBuffer); + + while (1) { + volatile int errnum; + + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + ptr = &remcomInBuffer[1]; + +#ifdef KGDB_DEBUG + if (kdebug) + printf("kgdb: remcomInBuffer: %s\n", remcomInBuffer); +#endif + + errnum = kgdb_setjmp((long*)error_jmp_buf); + + if (errnum == 0) switch (remcomInBuffer[0]) { + + case '?': /* report most recent signal */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[kd.sigval >> 4]; + remcomOutBuffer[2] = hexchars[kd.sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; + +#ifdef KGDB_DEBUG + case 'd': + /* toggle debug flag */ + kdebug ^= 1; + break; +#endif + + case 'g': /* return the value of the CPU registers. */ + length = kgdb_getregs(regs, remcomRegBuffer, BUFMAX); + mem2hex(remcomRegBuffer, remcomOutBuffer, length); + break; + + case 'G': /* set the value of the CPU registers */ + length = strlen(ptr); + if ((length & 1) != 0) kgdb_error(KGDBERR_BADPARAMS); + hex2mem(ptr, remcomRegBuffer, length/2); + kgdb_putregs(regs, remcomRegBuffer, length/2); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + mem2hex((char *)addr, remcomOutBuffer, length); + } else { + kgdb_error(KGDBERR_BADPARAMS); + } + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + hex2mem(ptr, (char *)addr, length); + strcpy(remcomOutBuffer, "OK"); + } else { + kgdb_error(KGDBERR_BADPARAMS); + } + break; + + + case 'k': /* kill the program, actually return to monitor */ + kd.extype = KGDBEXIT_KILL; + *regs = entry_regs; + first_entry = 1; + goto doexit; + + case 'C': /* CSS continue with signal SS */ + *ptr = '\0'; /* ignore the signal number for now */ + /* fall through */ + + case 'c': /* cAA..AA Continue; address AA..AA optional */ + /* try to read optional parameter, pc unchanged if no parm */ + kd.extype = KGDBEXIT_CONTINUE; + + if (hexToInt(&ptr, &addr)) { + kd.exaddr = addr; + kd.extype |= KGDBEXIT_WITHADDR; + } + + goto doexit; + + case 'S': /* SSS single step with signal SS */ + *ptr = '\0'; /* ignore the signal number for now */ + /* fall through */ + + case 's': + kd.extype = KGDBEXIT_SINGLE; + + if (hexToInt(&ptr, &addr)) { + kd.exaddr = addr; + kd.extype |= KGDBEXIT_WITHADDR; + } + + doexit: +/* Need to flush the instruction cache here, as we may have deposited a + * breakpoint, and the icache probably has no way of knowing that a data ref to + * some location may have changed something that is in the instruction cache. + */ + kgdb_flush_cache_all(); + kgdb_exit(regs, &kd); + kgdb_active = 0; + kgdb_interruptible(1); + return (1); + + case 'r': /* Reset (if user process..exit ???)*/ + panic("kgdb reset."); + break; + + case 'P': /* Pr=v set reg r to value v (r and v are hex) */ + if (hexToInt(&ptr, &addr) + && *ptr++ == '=' + && ((length = strlen(ptr)) & 1) == 0) { + hex2mem(ptr, remcomRegBuffer, length/2); + kgdb_putreg(regs, addr, + remcomRegBuffer, length/2); + strcpy(remcomOutBuffer,"OK"); + } else { + kgdb_error(KGDBERR_BADPARAMS); + } + break; + } /* switch */ + + if (errnum != 0) + sprintf(remcomOutBuffer, "E%02d", errnum); + +#ifdef KGDB_DEBUG + if (kdebug) + printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); +#endif + + /* reply to the request */ + putpacket(remcomOutBuffer); + + } /* while(1) */ +} + +/* + * kgdb_init must be called *after* the + * monitor is relocated into ram + */ +void +kgdb_init(void) +{ + kgdb_serial_init(); + debugger_exception_handler = handle_exception; + initialized = 1; + + putDebugStr("kgdb ready\n"); + puts("ready\n"); +} + +void +kgdb_error(int errnum) +{ + longjmp_on_fault = 0; + kgdb_longjmp((long*)error_jmp_buf, errnum); + panic("kgdb_error: longjmp failed!\n"); +} + +/* Output string in GDB O-packet format if GDB has connected. If nothing + output, returns 0 (caller must then handle output). */ +int +kgdb_output_string (const char* s, unsigned int count) +{ + char buffer[512]; + + count = (count <= (sizeof(buffer) / 2 - 2)) + ? count : (sizeof(buffer) / 2 - 2); + + buffer[0] = 'O'; + mem2hex ((char *)s, &buffer[1], count); + putpacket(buffer); + + return 1; +} + +void +breakpoint(void) +{ + if (!initialized) { + printf("breakpoint() called b4 kgdb init\n"); + return; + } + + kgdb_breakpoint(0, 0); +} + +int +do_kgdb(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + printf("Entering KGDB mode via exception handler...\n\n"); + kgdb_breakpoint(argc - 1, argv + 1); + printf("\nReturned from KGDB mode\n"); + return 0; +} + +#else + +int kgdb_not_configured = 1; + +#endif /* CFG_CMD_KGDB */ diff --git a/cpu/74xx_7xx/cpu_init.c b/cpu/74xx_7xx/cpu_init.c new file mode 100644 index 0000000..2843e26 --- /dev/null +++ b/cpu/74xx_7xx/cpu_init.c @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2001 + * Josh Huber , Mission Critical Linux, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * cpu_init.c - low level cpu init + * + * there's really nothing going on here yet. future work area? + */ + +#include +#include <74xx_7xx.h> + +/* + * Breath some life into the CPU... + * + * there's basically nothing to do here since the memory controller + * isn't on the CPU in this case. + */ +void +cpu_init_f (void) +{ + if (get_cpu_type() == CPU_7450) { + /* enable the timebase bit in HID0 */ + set_hid0(get_hid0() | 0x4000000); + } +} + +/* + * initialize higher level parts of CPU like timers + */ +int cpu_init_r (void) +{ + return (0); +} diff --git a/cpu/74xx_7xx/speed.c b/cpu/74xx_7xx/speed.c new file mode 100644 index 0000000..fe553f1 --- /dev/null +++ b/cpu/74xx_7xx/speed.c @@ -0,0 +1,76 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include <74xx_7xx.h> +#include + +static const int hid1_multipliers_x_10[] = { + 25, /* 0000 - 2.5x */ + 75, /* 0001 - 7.5x */ + 70, /* 0010 - 7x */ + 10, /* 0011 - bypass */ + 20, /* 0100 - 2x */ + 65, /* 0101 - 6.5x */ + 100, /* 0110 - 10x */ + 45, /* 0111 - 4.5x */ + 30, /* 1000 - 3x */ + 55, /* 1001 - 5.5x */ + 40, /* 1010 - 4x */ + 50, /* 1011 - 5x */ + 80, /* 1100 - 8x */ + 60, /* 1101 - 6x */ + 35, /* 1110 - 3.5x */ + 0 /* 1111 - off */ +}; + +/* ------------------------------------------------------------------------- */ + +/* + * Measure CPU clock speed (core clock GCLK1, GCLK2) + * + * (Approx. GCLK frequency in Hz) + */ + +int get_clocks (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + ulong clock = CFG_BUS_CLK * \ + hid1_multipliers_x_10[get_hid1 () >> 28] / 10; + gd->cpu_clk = clock; + gd->bus_clk = CFG_BUS_CLK; + + return (0); +} + +/* ------------------------------------------------------------------------- */ + +#if 0 /* disabled XXX - use global data instead */ +ulong get_bus_freq (ulong gclk_freq) +{ + return CFG_BUS_CLK; +} +#endif /* 0 */ + +/* ------------------------------------------------------------------------- */ diff --git a/cpu/mpc824x/cpu.c b/cpu/mpc824x/cpu.c new file mode 100644 index 0000000..0d822d5 --- /dev/null +++ b/cpu/mpc824x/cpu.c @@ -0,0 +1,281 @@ +/* + * (C) Copyright 2000 - 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include + +int checkcpu (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned int pvr = get_pvr (); + unsigned int version = pvr >> 16; + unsigned char revision; + ulong clock = gd->cpu_clk; + char buf[32]; + + puts ("CPU: "); + + switch (version) { + case CPU_TYPE_8240: + puts ("MPC8240"); + break; + + case CPU_TYPE_8245: + puts ("MPC8245"); + break; + + default: + return -1; /*not valid for this source */ + } + + CONFIG_READ_BYTE (REVID, revision); + + if (revision) { + printf (" Revision %d.%d", + (revision & 0xf0) >> 4, + (revision & 0x0f)); + } else { + return -1; /* no valid CPU revision info */ + } + + printf (" at %s MHz:", strmhz (buf, clock)); + + printf (" %u kB I-Cache", checkicache () >> 10); + printf (" %u kB D-Cache", checkdcache () >> 10); + + puts ("\n"); + + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* L1 i-cache */ + +int checkicache (void) +{ + /*TODO*/ + return 128 * 4 * 32; +}; + +/* ------------------------------------------------------------------------- */ +/* L1 d-cache */ + +int checkdcache (void) +{ + /*TODO*/ + return 128 * 4 * 32; + +}; + +/*------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, + char *argv[]) +{ + ulong msr, addr; + + /* Interrupts and MMU off */ + __asm__ ("mtspr 81, 0"); + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~0x1030; + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ +#ifdef CFG_RESET_ADDRESS + addr = CFG_RESET_ADDRESS; +#else + /* + * note: when CFG_MONITOR_BASE points to a RAM address, + * CFG_MONITOR_BASE - sizeof (ulong) is usually a valid + * address. Better pick an address known to be invalid on + * your system and assign it to CFG_RESET_ADDRESS. + * "(ulong)-1" used to be a good choice for many systems... + */ + addr = CFG_MONITOR_BASE - sizeof (ulong); +#endif + ((void (*)(void)) addr) (); + return 1; + +} + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * This is the sys_logic_clk (memory bus) divided by 4 + */ +unsigned long get_tbclk (void) +{ + return ((get_bus_freq (0) + 2L) / 4L); +} + +/* ------------------------------------------------------------------------- */ + +/* + * The MPC824x has an integrated PCI controller known as the MPC107. + * The following are MPC107 Bridge Controller and PCI Support functions + * + */ + +/* + * This procedure reads a 32-bit address MPC107 register, and returns + * a 32 bit value. It swaps the address to little endian before + * writing it to config address, and swaps the value to big endian + * before returning to the caller. + */ +unsigned int mpc824x_mpc107_getreg (unsigned int regNum) +{ + unsigned int temp; + + /* swap the addr. to little endian */ + *(volatile unsigned int *) CHRP_REG_ADDR = PCISWAP (regNum); + temp = *(volatile unsigned int *) CHRP_REG_DATA; + return PCISWAP (temp); /* swap the data upon return */ +} + +/* + * This procedure writes a 32-bit address MPC107 register. It swaps + * the address to little endian before writing it to config address. + */ + +void mpc824x_mpc107_setreg (unsigned int regNum, unsigned int regVal) +{ + /* swap the addr. to little endian */ + *(volatile unsigned int *) CHRP_REG_ADDR = PCISWAP (regNum); + *(volatile unsigned int *) CHRP_REG_DATA = PCISWAP (regVal); + return; +} + + +/* + * Write a byte (8 bits) to a memory location. + */ +void mpc824x_mpc107_write8 (unsigned int addr, unsigned char data) +{ + *(unsigned char *) addr = data; + __asm__ ("sync"); +} + +/* + * Write a word (16 bits) to a memory location after the value + * has been byte swapped (big to little endian or vice versa) + */ + +void mpc824x_mpc107_write16 (unsigned int address, unsigned short data) +{ + *(volatile unsigned short *) address = BYTE_SWAP_16_BIT (data); + __asm__ ("sync"); +} + +/* + * Write a long word (32 bits) to a memory location after the value + * has been byte swapped (big to little endian or vice versa) + */ + +void mpc824x_mpc107_write32 (unsigned int address, unsigned int data) +{ + *(volatile unsigned int *) address = LONGSWAP (data); + __asm__ ("sync"); +} + +/* + * Read a byte (8 bits) from a memory location. + */ +unsigned char mpc824x_mpc107_read8 (unsigned int addr) +{ + return *(volatile unsigned char *) addr; +} + + +/* + * Read a word (16 bits) from a memory location, and byte swap the + * value before returning to the caller. + */ +unsigned short mpc824x_mpc107_read16 (unsigned int address) +{ + unsigned short retVal; + + retVal = BYTE_SWAP_16_BIT (*(unsigned short *) address); + return retVal; +} + + +/* + * Read a long word (32 bits) from a memory location, and byte + * swap the value before returning to the caller. + */ +unsigned int mpc824x_mpc107_read32 (unsigned int address) +{ + unsigned int retVal; + + retVal = LONGSWAP (*(unsigned int *) address); + return (retVal); +} + + +/* + * Read a register in the Embedded Utilities Memory Block address + * space. + * Input: regNum - register number + utility base address. Example, + * the base address of EPIC is 0x40000, the register number + * being passed is 0x40000+the address of the target register. + * (See epic.h for register addresses). + * Output: The 32 bit little endian value of the register. + */ + +unsigned int mpc824x_eummbar_read (unsigned int regNum) +{ + unsigned int temp; + + temp = *(volatile unsigned int *) (EUMBBAR_VAL + regNum); + temp = PCISWAP (temp); + return temp; +} + + +/* + * Write a value to a register in the Embedded Utilities Memory + * Block address space. + * Input: regNum - register number + utility base address. Example, + * the base address of EPIC is 0x40000, the register + * number is 0x40000+the address of the target register. + * (See epic.h for register addresses). + * regVal - value to be written to the register. + */ + +void mpc824x_eummbar_write (unsigned int regNum, unsigned int regVal) +{ + *(volatile unsigned int *) (EUMBBAR_VAL + regNum) = PCISWAP (regVal); + return; +} + +/* ------------------------------------------------------------------------- */ diff --git a/cpu/mpc824x/interrupts.c b/cpu/mpc824x/interrupts.c new file mode 100644 index 0000000..2011671 --- /dev/null +++ b/cpu/mpc824x/interrupts.c @@ -0,0 +1,185 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Rob Taylor, Flying Pig Systems. robt@flyingpig.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "drivers/epic.h" + +/****************************************************************************/ + +unsigned decrementer_count; /* count val for 1e6/HZ microseconds */ + +static __inline__ unsigned long get_msr (void) +{ + unsigned long msr; + + asm volatile ("mfmsr %0":"=r" (msr):); + + return msr; +} + +static __inline__ void set_msr (unsigned long msr) +{ + asm volatile ("mtmsr %0"::"r" (msr)); +} + +static __inline__ unsigned long get_dec (void) +{ + unsigned long val; + + asm volatile ("mfdec %0":"=r" (val):); + + return val; +} + + +static __inline__ void set_dec (unsigned long val) +{ + asm volatile ("mtdec %0"::"r" (val)); +} + + +void enable_interrupts (void) +{ + set_msr (get_msr () | MSR_EE); +} + +/* returns flag if MSR_EE was set before */ +int disable_interrupts (void) +{ + ulong msr = get_msr (); + + set_msr (msr & ~MSR_EE); + return ((msr & MSR_EE) != 0); +} + +/****************************************************************************/ + +int interrupt_init (void) +{ + decrementer_count = (get_bus_freq (0) / 4) / CFG_HZ; + + /* + * It's all broken at the moment and I currently don't need + * interrupts. If you want to fix it, have a look at the epic + * drivers in dink32 v12. They do everthing and Motorola said + * I could use the dink source in this project as long as + * copyright notices remain intact. + */ + + epicInit (EPIC_DIRECT_IRQ, 0); + + set_dec (decrementer_count); + + set_msr (get_msr () | MSR_EE); + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + register unsigned long temp; + + pci_readl (CFG_EUMB_ADDR + EPIC_PROC_INT_ACK_REG, temp); + sync (); /* i'm not convinced this is needed, but dink source has it */ + temp &= 0xff; /*get vector */ + + /*TODO: handle them -... */ + epicEOI (); +} + +/****************************************************************************/ + +/* + * blank int handlers. + */ + +void +irq_install_handler (int vec, interrupt_handler_t * handler, void *arg) +{ +} + +void irq_free_handler (int vec) +{ + +} + +/*TODO: some handlers for winbond and 87308 interrupts + and what about generic pci inteerupts? + vga? + */ + +volatile ulong timestamp = 0; + +void timer_interrupt (struct pt_regs *regs) +{ + /* Restore Decrementer Count */ + set_dec (decrementer_count); + + timestamp++; + +#if defined(CONFIG_WATCHDOG) + if ((timestamp % (CFG_HZ / 2)) == 0) { +#if defined(CONFIG_OXC) + { + extern void oxc_wdt_reset (void); + + oxc_wdt_reset (); + } +#endif + } +#endif /* CONFIG_WATCHDOG */ +#if defined(CONFIG_SHOW_ACTIVITY) && defined(CONFIG_OXC) + if ((timestamp % (CFG_HZ / 10)) == 0) { + { + extern void oxc_toggle_activeled (void); + + oxc_toggle_activeled (); + } + } +#endif +} + +void reset_timer (void) +{ + timestamp = 0; +} + +ulong get_timer (ulong base) +{ + return (timestamp - base); +} + +void set_timer (ulong t) +{ + timestamp = t; +} diff --git a/cpu/mpc8260/cpu.c b/cpu/mpc8260/cpu.c new file mode 100644 index 0000000..73881ee --- /dev/null +++ b/cpu/mpc8260/cpu.c @@ -0,0 +1,243 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * CPU specific code for the MPC8260 + * + * written or collected and sometimes rewritten by + * Magnus Damm + * + * minor modifications by + * Wolfgang Denk + * + * modified for 8260 by + * Murray Jensen + * + * added 8260 masks by + * Marius Groeger + */ + +#include +#include +#include +#include +#include +#include + +int checkcpu (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immap = (immap_t *) CFG_IMMR; + ulong clock = gd->cpu_clk; + uint pvr = get_pvr (); + uint immr, rev, m, k; + char buf[32]; + + puts ("CPU: "); + + if (((pvr >> 16) & 0xff) != 0x81) + return -1; /* whoops! not an MPC8260 */ + rev = pvr & 0xff; + + immr = immap->im_memctl.memc_immr; + if ((immr & IMMR_ISB_MSK) != CFG_IMMR) + return -1; /* whoops! someone moved the IMMR */ + + printf ("MPC8260 (Rev %02x, Mask ", rev); + + /* + * the bottom 16 bits of the immr are the Part Number and Mask Number + * (4-34); the 16 bits at PROFF_REVNUM (0x8af0) in dual port ram is the + * RISC Microcode Revision Number (13-10). + * For the 8260, Motorola doesn't include the Microcode Revision + * in the mask. + */ + m = immr & (IMMR_PARTNUM_MSK | IMMR_MASKNUM_MSK); + k = *((ushort *) & immap->im_dprambase[PROFF_REVNUM]); + + switch (m) { + case 0x0000: + printf ("0.2 2J24M"); + break; + case 0x0010: + printf ("A.0 K22A"); + break; + case 0x0011: + printf ("A.1 1K22A-XC"); + break; + case 0x0001: + printf ("B.1 1K23A"); + break; + case 0x0021: + printf ("B.2 2K23A-XC"); + break; + case 0x0023: + printf ("B.3 3K23A"); + break; + case 0x0024: + printf ("C.2 6K23A"); + break; + case 0x0060: + printf ("A.0(A) 2K25A"); + break; + default: + printf ("unknown [immr=0x%04x,k=0x%04x]", m, k); + break; + } + + printf (") at %s MHz\n", strmhz (buf, clock)); + + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* configures a UPM by writing into the UPM RAM array */ +/* uses bank 11 and a dummy physical address (=BRx_BA_MSK) */ +/* NOTE: the physical address chosen must not overlap into any other area */ +/* mapped by the memory controller because bank 11 has the lowest priority */ + +void upmconfig (uint upm, uint * table, uint size) +{ + volatile immap_t *immap = (immap_t *) CFG_IMMR; + volatile memctl8260_t *memctl = &immap->im_memctl; + volatile uchar *dummy = (uchar *) BRx_BA_MSK; /* set all BA bits */ + uint i; + + /* first set up bank 11 to reference the correct UPM at a dummy address */ + + memctl->memc_or11 = ORxU_AM_MSK; /* set all AM bits */ + + switch (upm) { + + case UPMA: + memctl->memc_br11 = + ((uint)dummy & BRx_BA_MSK) | BRx_PS_32 | BRx_MS_UPMA | + BRx_V; + memctl->memc_mamr = MxMR_OP_WARR; + break; + + case UPMB: + memctl->memc_br11 = + ((uint)dummy & BRx_BA_MSK) | BRx_PS_32 | BRx_MS_UPMB | + BRx_V; + memctl->memc_mbmr = MxMR_OP_WARR; + break; + + case UPMC: + memctl->memc_br11 = + ((uint)dummy & BRx_BA_MSK) | BRx_PS_32 | BRx_MS_UPMC | + BRx_V; + memctl->memc_mcmr = MxMR_OP_WARR; + break; + + default: + panic ("upmconfig passed invalid UPM number (%u)\n", upm); + break; + + } + + /* + * at this point, the dummy address is set up to access the selected UPM, + * the MAD pointer is zero, and the MxMR OP is set for writing to RAM + * + * now we simply load the mdr with each word and poke the dummy address. + * the MAD is incremented on each access. + */ + + for (i = 0; i < size; i++) { + memctl->memc_mdr = table[i]; + *dummy = 0; + } + + /* now kill bank 11 */ + memctl->memc_br11 = 0; +} + +/* ------------------------------------------------------------------------- */ + +int +do_reset (cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[]) +{ + ulong msr, addr; + + volatile immap_t *immap = (immap_t *) CFG_IMMR; + + immap->im_clkrst.car_rmr = RMR_CSRE; /* Checkstop Reset enable */ + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ +#ifdef CFG_RESET_ADDRESS + addr = CFG_RESET_ADDRESS; +#else + /* + * note: when CFG_MONITOR_BASE points to a RAM address, CFG_MONITOR_BASE + * - sizeof (ulong) is usually a valid address. Better pick an address + * known to be invalid on your system and assign it to CFG_RESET_ADDRESS. + */ + addr = CFG_MONITOR_BASE - sizeof (ulong); +#endif + ((void (*)(void)) addr) (); + return 1; + +} + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * + */ +unsigned long get_tbclk (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + ulong tbclk; + + tbclk = (gd->bus_clk + 3L) / 4L; + + return (tbclk); +} + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts (); + + reset_8260_watchdog ((immap_t *) CFG_IMMR); + if (re_enable) + enable_interrupts (); +} +#endif /* CONFIG_WATCHDOG */ + +/* ------------------------------------------------------------------------- */ diff --git a/cpu/mpc8260/interrupts.c b/cpu/mpc8260/interrupts.c new file mode 100644 index 0000000..d804408 --- /dev/null +++ b/cpu/mpc8260/interrupts.c @@ -0,0 +1,383 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 22-Oct-00 + */ + +#include +#include +#include +#include +#include +#include + +/****************************************************************************/ + +unsigned decrementer_count; /* count val for 1e6/HZ microseconds */ + +struct irq_action { + interrupt_handler_t *handler; + void *arg; + ulong count; +}; + +static struct irq_action irq_handlers[NR_IRQS]; + +static ulong ppc_cached_irq_mask[NR_MASK_WORDS]; + +/****************************************************************************/ +/* this section was ripped out of arch/ppc/kernel/ppc8260_pic.c in the */ +/* Linux/PPC 2.4.x source. There was no copyright notice in that file. */ + +/* The 8260 internal interrupt controller. It is usually + * the only interrupt controller. + * There are two 32-bit registers (high/low) for up to 64 + * possible interrupts. + * + * Now, the fun starts.....Interrupt Numbers DO NOT MAP + * in a simple arithmetic fashion to mask or pending registers. + * That is, interrupt 4 does not map to bit position 4. + * We create two tables, indexed by vector number, to indicate + * which register to use and which bit in the register to use. + */ +static u_char irq_to_siureg[] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static u_char irq_to_siubit[] = { + 31, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, + 29, 30, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 31, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0 +}; + +static void m8260_mask_irq (unsigned int irq_nr) +{ + volatile immap_t *immr = (immap_t *) CFG_IMMR; + int bit, word; + volatile uint *simr; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; +} + +static void m8260_unmask_irq (unsigned int irq_nr) +{ + volatile immap_t *immr = (immap_t *) CFG_IMMR; + int bit, word; + volatile uint *simr; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + ppc_cached_irq_mask[word] |= (1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; +} + +static void m8260_mask_and_ack (unsigned int irq_nr) +{ + volatile immap_t *immr = (immap_t *) CFG_IMMR; + int bit, word; + volatile uint *simr, *sipnr; + + bit = irq_to_siubit[irq_nr]; + word = irq_to_siureg[irq_nr]; + + simr = &(immr->im_intctl.ic_simrh); + sipnr = &(immr->im_intctl.ic_sipnrh); + ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); + simr[word] = ppc_cached_irq_mask[word]; + sipnr[word] = 1 << (31 - bit); +} + +static int m8260_get_irq (struct pt_regs *regs) +{ + volatile immap_t *immr = (immap_t *) CFG_IMMR; + int irq; + unsigned long bits; + + /* For MPC8260, read the SIVEC register and shift the bits down + * to get the irq number. */ + bits = immr->im_intctl.ic_sivec; + irq = bits >> 26; + return irq; +} + +/* end of code ripped out of arch/ppc/kernel/ppc8260_pic.c */ +/****************************************************************************/ + +static __inline__ unsigned long get_msr (void) +{ + unsigned long msr; + + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + return msr; +} + +static __inline__ void set_msr (unsigned long msr) +{ + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); +} + +static __inline__ unsigned long get_dec (void) +{ + unsigned long val; + + __asm__ __volatile__ ("mfdec %0":"=r" (val):); + + return val; +} + +static __inline__ void set_dec (unsigned long val) +{ + __asm__ __volatile__ ("mtdec %0"::"r" (val)); +} + +void enable_interrupts (void) +{ + set_msr (get_msr () | MSR_EE); +} + +/* returns flag if MSR_EE was set before */ +int disable_interrupts (void) +{ + ulong msr = get_msr (); + + set_msr (msr & ~MSR_EE); + return ((msr & MSR_EE) != 0); +} + +/****************************************************************************/ + +int interrupt_init (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immr = (immap_t *) CFG_IMMR; + + decrementer_count = (gd->bus_clk / 4) / CFG_HZ; + + /* Initialize the default interrupt mapping priorities */ + immr->im_intctl.ic_sicr = 0; + immr->im_intctl.ic_siprr = 0x05309770; + immr->im_intctl.ic_scprrh = 0x05309770; + immr->im_intctl.ic_scprrl = 0x05309770; + + /* disable all interrupts and clear all pending bits */ + immr->im_intctl.ic_simrh = ppc_cached_irq_mask[0] = 0; + immr->im_intctl.ic_simrl = ppc_cached_irq_mask[1] = 0; + immr->im_intctl.ic_sipnrh = 0xffffffff; + immr->im_intctl.ic_sipnrl = 0xffffffff; + + set_dec (decrementer_count); + + set_msr (get_msr () | MSR_EE); + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + int irq, unmask = 1; + + irq = m8260_get_irq (regs); + + m8260_mask_and_ack (irq); + + set_msr (get_msr () | MSR_EE); + + if (irq_handlers[irq].handler != NULL) + (*irq_handlers[irq].handler) (irq_handlers[irq].arg); + else { + printf ("\nBogus External Interrupt IRQ %d\n", irq); + /* + * turn off the bogus interrupt, otherwise it + * might repeat forever + */ + unmask = 0; + } + + if (unmask) + m8260_unmask_irq (irq); +} + +/****************************************************************************/ + +/* + * Install and free an interrupt handler. + */ + +void +irq_install_handler (int irq, interrupt_handler_t * handler, void *arg) +{ + if (irq < 0 || irq >= NR_IRQS) { + printf ("irq_install_handler: bad irq number %d\n", irq); + return; + } + + if (irq_handlers[irq].handler != NULL) + printf ("irq_install_handler: 0x%08lx replacing 0x%08lx\n", + (ulong) handler, (ulong) irq_handlers[irq].handler); + + irq_handlers[irq].handler = handler; + irq_handlers[irq].arg = arg; + + m8260_unmask_irq (irq); +} + +void irq_free_handler (int irq) +{ + if (irq < 0 || irq >= NR_IRQS) { + printf ("irq_free_handler: bad irq number %d\n", irq); + return; + } + + m8260_mask_irq (irq); + + irq_handlers[irq].handler = NULL; + irq_handlers[irq].arg = NULL; +} + +/****************************************************************************/ + +volatile ulong timestamp = 0; + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * Trivial implementation - no need to be really accurate. + */ +void timer_interrupt (struct pt_regs *regs) +{ +#if defined(CONFIG_WATCHDOG) || defined(CFG_HYMOD_DBLEDS) + volatile immap_t *immr = (immap_t *) CFG_IMMR; +#endif /* CONFIG_WATCHDOG */ + + /* Restore Decrementer Count */ + set_dec (decrementer_count); + + timestamp++; + +#if defined(CONFIG_WATCHDOG) || \ + defined(CFG_CMA_LCD_HEARTBEAT) || \ + defined(CFG_HYMOD_DBLEDS) + + if ((timestamp % CFG_HZ) == 0) { +#if defined(CFG_CMA_LCD_HEARTBEAT) + extern void lcd_heartbeat (void); +#endif /* CFG_CMA_LCD_HEARTBEAT */ +#if defined(CFG_HYMOD_DBLEDS) + volatile iop8260_t *iop = &immr->im_ioport; + static int shift = 0; +#endif /* CFG_HYMOD_DBLEDS */ + +#if defined(CFG_CMA_LCD_HEARTBEAT) + lcd_heartbeat (); +#endif /* CFG_CMA_LCD_HEARTBEAT */ + +#if defined(CONFIG_WATCHDOG) + reset_8260_watchdog (immr); +#endif /* CONFIG_WATCHDOG */ + +#if defined(CFG_HYMOD_DBLEDS) + /* hymod daughter board LEDs */ + if (++shift > 3) + shift = 0; + iop->iop_pdatd = + (iop->iop_pdatd & ~0x0f000000) | (1 << (24 + shift)); +#endif /* CFG_HYMOD_DBLEDS */ + } +#endif /* CONFIG_WATCHDOG || CFG_CMA_LCD_HEARTBEAT */ +} + +/****************************************************************************/ + +void reset_timer (void) +{ + timestamp = 0; +} + +ulong get_timer (ulong base) +{ + return (timestamp - base); +} + +void set_timer (ulong t) +{ + timestamp = t; +} + +/****************************************************************************/ + +#if (CONFIG_COMMANDS & CFG_CMD_IRQ) + +/* ripped this out of ppc4xx/interrupts.c */ + +/******************************************************************************* +* +* irqinfo - print information about PCI devices +* +*/ +void +do_irqinfo (cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[]) +{ + int irq, re_enable; + + re_enable = disable_interrupts (); + + printf ("\nInterrupt-Information:\n"); + printf ("Nr Routine Arg Count\n"); + + for (irq = 0; irq < 32; irq++) + if (irq_handlers[irq].handler != NULL) + printf ("%02d %08lx %08lx %ld\n", irq, + (ulong) irq_handlers[irq].handler, + (ulong) irq_handlers[irq].arg, + irq_handlers[irq].count); + + if (re_enable) + enable_interrupts (); +} + +#endif /* CONFIG_COMMANDS & CFG_CMD_IRQ */ diff --git a/cpu/mpc8260/serial_scc.c b/cpu/mpc8260/serial_scc.c new file mode 100644 index 0000000..ca76302 --- /dev/null +++ b/cpu/mpc8260/serial_scc.c @@ -0,0 +1,498 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 19-Oct-00. + */ + +/* + * Minimal serial functions needed to use one of the SCC ports + * as serial console interface. + */ + +#include +#include +#include + +#if defined(CONFIG_CONS_ON_SCC) + +#if CONFIG_CONS_INDEX == 1 /* Console on SCC1 */ + +#define SCC_INDEX 0 +#define PROFF_SCC PROFF_SCC1 +#define CMXSCR_MASK (CMXSCR_GR1|CMXSCR_SC1|\ + CMXSCR_RS1CS_MSK|CMXSCR_TS1CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS1CS_BRG1|CMXSCR_TS1CS_BRG1) +#define CPM_CR_SCC_PAGE CPM_CR_SCC1_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC1_SBLOCK + +#elif CONFIG_CONS_INDEX == 2 /* Console on SCC2 */ + +#define SCC_INDEX 1 +#define PROFF_SCC PROFF_SCC2 +#define CMXSCR_MASK (CMXSCR_GR2|CMXSCR_SC2|\ + CMXSCR_RS2CS_MSK|CMXSCR_TS2CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS2CS_BRG2|CMXSCR_TS2CS_BRG2) +#define CPM_CR_SCC_PAGE CPM_CR_SCC2_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC2_SBLOCK + +#elif CONFIG_CONS_INDEX == 3 /* Console on SCC3 */ + +#define SCC_INDEX 2 +#define PROFF_SCC PROFF_SCC3 +#define CMXSCR_MASK (CMXSCR_GR3|CMXSCR_SC3|\ + CMXSCR_RS3CS_MSK|CMXSCR_TS3CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS3CS_BRG3|CMXSCR_TS3CS_BRG3) +#define CPM_CR_SCC_PAGE CPM_CR_SCC3_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC3_SBLOCK + +#elif CONFIG_CONS_INDEX == 4 /* Console on SCC4 */ + +#define SCC_INDEX 3 +#define PROFF_SCC PROFF_SCC4 +#define CMXSCR_MASK (CMXSCR_GR4|CMXSCR_SC4|\ + CMXSCR_RS4CS_MSK|CMXSCR_TS4CS_MSK) +#define CMXSCR_VALUE (CMXSCR_RS4CS_BRG4|CMXSCR_TS4CS_BRG4) +#define CPM_CR_SCC_PAGE CPM_CR_SCC4_PAGE +#define CPM_CR_SCC_SBLOCK CPM_CR_SCC4_SBLOCK + +#else + +#error "console not correctly defined" + +#endif + +int serial_init (void) +{ + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr; + + /* initialize pointers to SCC */ + + sp = (scc_t *) &(im->im_scc[SCC_INDEX]); + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + + /* Disable transmitter/receiver. + */ + sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* put the SCC channel into NMSI (non multiplexd serial interface) + * mode and wire the selected SCC Tx and Rx clocks to BRGx (15-15). + */ + im->im_cpmux.cmx_scr = (im->im_cpmux.cmx_scr&~CMXSCR_MASK)|CMXSCR_VALUE; + + /* Set up the baud rate generator. + */ + serial_setbrg (); + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + up->scc_genscc.scc_rbase = dpaddr; + up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + up->scc_genscc.scc_rfcr = CPMFCR_EB; + up->scc_genscc.scc_tfcr = CPMFCR_EB; + up->scc_genscc.scc_mrblr = 1; + up->scc_maxidl = 0; + up->scc_brkcr = 1; + up->scc_parec = 0; + up->scc_frmec = 0; + up->scc_nosec = 0; + up->scc_brkec = 0; + up->scc_uaddr1 = 0; + up->scc_uaddr2 = 0; + up->scc_toseq = 0; + up->scc_char1 = up->scc_char2 = up->scc_char3 = up->scc_char4 = 0x8000; + up->scc_char5 = up->scc_char6 = up->scc_char7 = up->scc_char8 = 0x8000; + up->scc_rccm = 0xc0ff; + + /* Mask all interrupts and remove anything pending. + */ + sp->scc_sccm = 0; + sp->scc_scce = 0xffff; + + /* Set 8 bit FIFO, 16 bit oversampling and UART mode. + */ + sp->scc_gsmrh = SCC_GSMRH_RFW; /* 8 bit FIFO */ + sp->scc_gsmrl = \ + SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16 | SCC_GSMRL_MODE_UART; + + /* Set CTS flow control, 1 stop bit, 8 bit character length, + * normal async UART mode, no parity + */ + sp->scc_psmr = SCU_PSMR_FLC | SCU_PSMR_CL; + + /* execute the "Init Rx and Tx params" CP command. + */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SCC_PAGE, CPM_CR_SCC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->scc_gsmrl |= SCC_GSMRL_ENR | SCC_GSMRL_ENT; + + return (0); +} + +void +serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CONS_USE_EXTC) + m8260_cpm_extcbrg(SCC_INDEX, gd->baudrate, + CONFIG_CONS_EXTC_RATE, CONFIG_CONS_EXTC_PINSEL); +#else + m8260_cpm_setbrg(SCC_INDEX, gd->baudrate); +#endif +} + +void +serial_putc(const char c) +{ + volatile scc_uart_t *up; + volatile cbd_t *tbdf; + volatile immap_t *im; + + if (c == '\n') + serial_putc ('\r'); + + im = (immap_t *)CFG_IMMR; + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + tbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_tbase]; + + /* Wait for last character to go. + */ + while (tbdf->cbd_sc & BD_SC_READY) + ; + + /* Load the character into the transmit buffer. + */ + *(volatile char *)tbdf->cbd_bufaddr = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int +serial_getc(void) +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im; + unsigned char c; + + im = (immap_t *)CFG_IMMR; + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + rbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_rbase]; + + /* Wait for character to show up. + */ + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + + /* Grab the char and clear the buffer again. + */ + c = *(volatile unsigned char *)rbdf->cbd_bufaddr; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return (c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im; + + im = (immap_t *)CFG_IMMR; + up = (scc_uart_t *)&im->im_dprambase[PROFF_SCC]; + rbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_rbase]; + + return ((rbdf->cbd_sc & BD_SC_EMPTY) == 0); +} + +#endif /* CONFIG_CONS_ON_SCC */ + +#if defined(CONFIG_KGDB_ON_SCC) + +#if defined(CONFIG_CONS_ON_SCC) && CONFIG_KGDB_INDEX == CONFIG_CONS_INDEX +#error Whoops! serial console and kgdb are on the same scc serial port +#endif + +#if CONFIG_KGDB_INDEX == 1 /* KGDB Port on SCC1 */ + +#define KGDB_SCC_INDEX 0 +#define KGDB_PROFF_SCC PROFF_SCC1 +#define KGDB_CMXSCR_MASK (CMXSCR_GR1|CMXSCR_SC1|\ + CMXSCR_RS1CS_MSK|CMXSCR_TS1CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS1CS_BRG1|CMXSCR_TS1CS_BRG1) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC1_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC1_SBLOCK + +#elif CONFIG_KGDB_INDEX == 2 /* KGDB Port on SCC2 */ + +#define KGDB_SCC_INDEX 1 +#define KGDB_PROFF_SCC PROFF_SCC2 +#define KGDB_CMXSCR_MASK (CMXSCR_GR2|CMXSCR_SC2|\ + CMXSCR_RS2CS_MSK|CMXSCR_TS2CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS2CS_BRG2|CMXSCR_TS2CS_BRG2) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC2_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC2_SBLOCK + +#elif CONFIG_KGDB_INDEX == 3 /* KGDB Port on SCC3 */ + +#define KGDB_SCC_INDEX 2 +#define KGDB_PROFF_SCC PROFF_SCC3 +#define KGDB_CMXSCR_MASK (CMXSCR_GR3|CMXSCR_SC3|\ + CMXSCR_RS3CS_MSK|CMXSCR_TS3CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS3CS_BRG3|CMXSCR_TS3CS_BRG3) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC3_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC3_SBLOCK + +#elif CONFIG_KGDB_INDEX == 4 /* KGDB Port on SCC4 */ + +#define KGDB_SCC_INDEX 3 +#define KGDB_PROFF_SCC PROFF_SCC4 +#define KGDB_CMXSCR_MASK (CMXSCR_GR4|CMXSCR_SC4|\ + CMXSCR_RS4CS_MSK|CMXSCR_TS4CS_MSK) +#define KGDB_CMXSCR_VALUE (CMXSCR_RS4CS_BRG4|CMXSCR_TS4CS_BRG4) +#define KGDB_CPM_CR_SCC_PAGE CPM_CR_SCC4_PAGE +#define KGDB_CPM_CR_SCC_SBLOCK CPM_CR_SCC4_SBLOCK + +#else + +#error "kgdb serial port not correctly defined" + +#endif + +void +kgdb_serial_init (void) +{ + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr, speed = CONFIG_KGDB_BAUDRATE; + char *s, *e; + + if ((s = getenv("kgdbrate")) != NULL && *s != '\0') { + ulong rate = simple_strtoul(s, &e, 10); + if (e > s && *e == '\0') + speed = rate; + } + + /* initialize pointers to SCC */ + + sp = (scc_t *) &(im->im_scc[KGDB_SCC_INDEX]); + up = (scc_uart_t *)&im->im_dprambase[KGDB_PROFF_SCC]; + + /* Disable transmitter/receiver. + */ + sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + /* put the SCC channel into NMSI (non multiplexd serial interface) + * mode and wire the selected SCC Tx and Rx clocks to BRGx (15-15). + */ + im->im_cpmux.cmx_scr = \ + (im->im_cpmux.cmx_scr & ~KGDB_CMXSCR_MASK) | KGDB_CMXSCR_VALUE; + + /* Set up the baud rate generator. + */ +#if defined(CONFIG_KGDB_USE_EXTC) + m8260_cpm_extcbrg(KGDB_SCC_INDEX, speed, + CONFIG_KGDB_EXTC_RATE, CONFIG_KGDB_EXTC_PINSEL); +#else + m8260_cpm_setbrg(KGDB_SCC_INDEX, speed); +#endif + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + up->scc_genscc.scc_rbase = dpaddr; + up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + up->scc_genscc.scc_rfcr = CPMFCR_EB; + up->scc_genscc.scc_tfcr = CPMFCR_EB; + up->scc_genscc.scc_mrblr = 1; + up->scc_maxidl = 0; + up->scc_brkcr = 1; + up->scc_parec = 0; + up->scc_frmec = 0; + up->scc_nosec = 0; + up->scc_brkec = 0; + up->scc_uaddr1 = 0; + up->scc_uaddr2 = 0; + up->scc_toseq = 0; + up->scc_char1 = up->scc_char2 = up->scc_char3 = up->scc_char4 = 0x8000; + up->scc_char5 = up->scc_char6 = up->scc_char7 = up->scc_char8 = 0x8000; + up->scc_rccm = 0xc0ff; + + /* Mask all interrupts and remove anything pending. + */ + sp->scc_sccm = 0; + sp->scc_scce = 0xffff; + + /* Set 8 bit FIFO, 16 bit oversampling and UART mode. + */ + sp->scc_gsmrh = SCC_GSMRH_RFW; /* 8 bit FIFO */ + sp->scc_gsmrl = \ + SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16 | SCC_GSMRL_MODE_UART; + + /* Set CTS flow control, 1 stop bit, 8 bit character length, + * normal async UART mode, no parity + */ + sp->scc_psmr = SCU_PSMR_FLC | SCU_PSMR_CL; + + /* execute the "Init Rx and Tx params" CP command. + */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(KGDB_CPM_CR_SCC_PAGE, KGDB_CPM_CR_SCC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->scc_gsmrl |= SCC_GSMRL_ENR | SCC_GSMRL_ENT; + + printf("SCC%d at %dbps ", CONFIG_KGDB_INDEX, speed); +} + +void +putDebugChar(const char c) +{ + volatile scc_uart_t *up; + volatile cbd_t *tbdf; + volatile immap_t *im; + + if (c == '\n') + putDebugChar ('\r'); + + im = (immap_t *)CFG_IMMR; + up = (scc_uart_t *)&im->im_dprambase[KGDB_PROFF_SCC]; + tbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_tbase]; + + /* Wait for last character to go. + */ + while (tbdf->cbd_sc & BD_SC_READY) + ; + + /* Load the character into the transmit buffer. + */ + *(volatile char *)tbdf->cbd_bufaddr = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +putDebugStr (const char *s) +{ + while (*s) { + putDebugChar (*s++); + } +} + +int +getDebugChar(void) +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im; + unsigned char c; + + im = (immap_t *)CFG_IMMR; + up = (scc_uart_t *)&im->im_dprambase[KGDB_PROFF_SCC]; + rbdf = (cbd_t *)&im->im_dprambase[up->scc_genscc.scc_rbase]; + + /* Wait for character to show up. + */ + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + + /* Grab the char and clear the buffer again. + */ + c = *(volatile unsigned char *)rbdf->cbd_bufaddr; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return (c); +} + +void +kgdb_interruptible(int yes) +{ + return; +} + +#endif /* CONFIG_KGDB_ON_SCC */ diff --git a/cpu/mpc8260/serial_smc.c b/cpu/mpc8260/serial_smc.c new file mode 100644 index 0000000..b0e1ce4 --- /dev/null +++ b/cpu/mpc8260/serial_smc.c @@ -0,0 +1,462 @@ +/* + * (C) Copyright 2000, 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 19-Oct-00, with + * changes based on the file arch/ppc/mbxboot/m8260_tty.c from the + * Linux/PPC sources (m8260_tty.c had no copyright info in it). + */ + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +#include +#include +#include + +#if defined(CONFIG_CONS_ON_SMC) + +#if CONFIG_CONS_INDEX == 1 /* Console on SMC1 */ + +#define SMC_INDEX 0 +#define PROFF_SMC_BASE PROFF_SMC1_BASE +#define PROFF_SMC PROFF_SMC1 +#define CPM_CR_SMC_PAGE CPM_CR_SMC1_PAGE +#define CPM_CR_SMC_SBLOCK CPM_CR_SMC1_SBLOCK +#define CMXSMR_MASK (CMXSMR_SMC1|CMXSMR_SMC1CS_MSK) +#define CMXSMR_VALUE CMXSMR_SMC1CS_BRG7 + +#elif CONFIG_CONS_INDEX == 2 /* Console on SMC2 */ + +#define SMC_INDEX 1 +#define PROFF_SMC_BASE PROFF_SMC2_BASE +#define PROFF_SMC PROFF_SMC2 +#define CPM_CR_SMC_PAGE CPM_CR_SMC2_PAGE +#define CPM_CR_SMC_SBLOCK CPM_CR_SMC2_SBLOCK +#define CMXSMR_MASK (CMXSMR_SMC2|CMXSMR_SMC2CS_MSK) +#define CMXSMR_VALUE CMXSMR_SMC2CS_BRG8 + +#else + +#error "console not correctly defined" + +#endif + +/* map rs_table index to baud rate generator index */ +static unsigned char brg_map[] = { + 6, /* BRG7 for SMC1 */ + 7, /* BRG8 for SMC2 */ + 0, /* BRG1 for SCC1 */ + 1, /* BRG1 for SCC2 */ + 2, /* BRG1 for SCC3 */ + 3, /* BRG1 for SCC4 */ +}; + +int serial_init (void) +{ + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr; + + /* initialize pointers to SMC */ + + sp = (smc_t *) &(im->im_smc[SMC_INDEX]); + *(ushort *)(&im->im_dprambase[PROFF_SMC_BASE]) = PROFF_SMC; + up = (smc_uart_t *)&im->im_dprambase[PROFF_SMC]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* NOTE: I/O port pins are set up via the iop_conf_tab[] table */ + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = CPMFCR_EB; + up->smc_tfcr = CPMFCR_EB; + up->smc_brklen = 0; + up->smc_brkec = 0; + up->smc_brkcr = 0; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* put the SMC channel into NMSI (non multiplexd serial interface) + * mode and wire either BRG7 to SMC1 or BRG8 to SMC2 (15-17). + */ + im->im_cpmux.cmx_smr = (im->im_cpmux.cmx_smr&~CMXSMR_MASK)|CMXSMR_VALUE; + + /* Set up the baud rate generator. + */ + serial_setbrg (); + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. + */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. + */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_SMC_PAGE, CPM_CR_SMC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + return (0); +} + +void +serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CONS_USE_EXTC) + m8260_cpm_extcbrg(brg_map[SMC_INDEX], gd->baudrate, + CONFIG_CONS_EXTC_RATE, CONFIG_CONS_EXTC_PINSEL); +#else + m8260_cpm_setbrg(brg_map[SMC_INDEX], gd->baudrate); +#endif +} + +void +serial_putc(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + + if (c == '\n') + serial_putc ('\r'); + + up = (smc_uart_t *)&(im->im_dprambase[PROFF_SMC]); + + tbdf = (cbd_t *)&im->im_dprambase[up->smc_tbase]; + + /* Wait for last character to go. + */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY) + ; + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int +serial_getc(void) +{ + volatile cbd_t *rbdf; + volatile unsigned char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + unsigned char c; + + up = (smc_uart_t *)&(im->im_dprambase[PROFF_SMC]); + + rbdf = (cbd_t *)&im->im_dprambase[up->smc_rbase]; + + /* Wait for character to show up. + */ + buf = (unsigned char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + + up = (smc_uart_t *)&(im->im_dprambase[PROFF_SMC]); + + rbdf = (cbd_t *)&im->im_dprambase[up->smc_rbase]; + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} + +#endif /* CONFIG_CONS_ON_SMC */ + +#if defined(CONFIG_KGDB_ON_SMC) + +#if defined(CONFIG_CONS_ON_SMC) && CONFIG_KGDB_INDEX == CONFIG_CONS_INDEX +#error Whoops! serial console and kgdb are on the same smc serial port +#endif + +#if CONFIG_KGDB_INDEX == 1 /* KGDB Port on SMC1 */ + +#define KGDB_SMC_INDEX 0 +#define KGDB_PROFF_SMC_BASE PROFF_SMC1_BASE +#define KGDB_PROFF_SMC PROFF_SMC1 +#define KGDB_CPM_CR_SMC_PAGE CPM_CR_SMC1_PAGE +#define KGDB_CPM_CR_SMC_SBLOCK CPM_CR_SMC1_SBLOCK +#define KGDB_CMXSMR_MASK (CMXSMR_SMC1|CMXSMR_SMC1CS_MSK) +#define KGDB_CMXSMR_VALUE CMXSMR_SMC1CS_BRG7 + +#elif CONFIG_KGDB_INDEX == 2 /* KGDB Port on SMC2 */ + +#define KGDB_SMC_INDEX 1 +#define KGDB_PROFF_SMC_BASE PROFF_SMC2_BASE +#define KGDB_PROFF_SMC PROFF_SMC2 +#define KGDB_CPM_CR_SMC_PAGE CPM_CR_SMC2_PAGE +#define KGDB_CPM_CR_SMC_SBLOCK CPM_CR_SMC2_SBLOCK +#define KGDB_CMXSMR_MASK (CMXSMR_SMC2|CMXSMR_SMC2CS_MSK) +#define KGDB_CMXSMR_VALUE CMXSMR_SMC2CS_BRG8 + +#else + +#error "console not correctly defined" + +#endif + +void +kgdb_serial_init (void) +{ + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8260_t *cp = &(im->im_cpm); + uint dpaddr, speed = CONFIG_KGDB_BAUDRATE; + char *s, *e; + + if ((s = getenv("kgdbrate")) != NULL && *s != '\0') { + ulong rate = simple_strtoul(s, &e, 10); + if (e > s && *e == '\0') + speed = rate; + } + + /* initialize pointers to SMC */ + + sp = (smc_t *) &(im->im_smc[KGDB_SMC_INDEX]); + *(ushort *)(&im->im_dprambase[KGDB_PROFF_SMC_BASE]) = KGDB_PROFF_SMC; + up = (smc_uart_t *)&im->im_dprambase[KGDB_PROFF_SMC]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* NOTE: I/O port pins are set up via the iop_conf_tab[] table */ + + /* Allocate space for two buffer descriptors in the DP ram. + * damm: allocating space after the two buffers for rx/tx data + */ + + dpaddr = m8260_cpm_dpalloc((2 * sizeof (cbd_t)) + 2, 16); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + rbdf = (cbd_t *)&im->im_dprambase[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = CPMFCR_EB; + up->smc_tfcr = CPMFCR_EB; + up->smc_brklen = 0; + up->smc_brkec = 0; + up->smc_brkcr = 0; + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* put the SMC channel into NMSI (non multiplexd serial interface) + * mode and wire either BRG7 to SMC1 or BRG8 to SMC2 (15-17). + */ + im->im_cpmux.cmx_smr = + (im->im_cpmux.cmx_smr & ~KGDB_CMXSMR_MASK) | KGDB_CMXSMR_VALUE; + + /* Set up the baud rate generator. + */ +#if defined(CONFIG_KGDB_USE_EXTC) + m8260_cpm_extcbrg(KGDB_SMC_INDEX, speed, + CONFIG_KGDB_EXTC_RATE, CONFIG_KGDB_EXTC_PINSEL); +#else + m8260_cpm_setbrg(KGDB_SMC_INDEX, speed); +#endif + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. + */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. + */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(KGDB_CPM_CR_SMC_PAGE, KGDB_CPM_CR_SMC_SBLOCK, + 0, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + printf("SMC%d at %dbps ", CONFIG_KGDB_INDEX, speed); +} + +void +putDebugChar(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + + if (c == '\n') + putDebugChar ('\r'); + + up = (smc_uart_t *)&(im->im_dprambase[KGDB_PROFF_SMC]); + + tbdf = (cbd_t *)&im->im_dprambase[up->smc_tbase]; + + /* Wait for last character to go. + */ + buf = (char *)tbdf->cbd_bufaddr; + while (tbdf->cbd_sc & BD_SC_READY) + ; + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; +} + +void +putDebugStr (const char *s) +{ + while (*s) { + putDebugChar (*s++); + } +} + +int +getDebugChar(void) +{ + volatile cbd_t *rbdf; + volatile unsigned char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + unsigned char c; + + up = (smc_uart_t *)&(im->im_dprambase[KGDB_PROFF_SMC]); + + rbdf = (cbd_t *)&im->im_dprambase[up->smc_rbase]; + + /* Wait for character to show up. + */ + buf = (unsigned char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +void +kgdb_interruptible(int yes) +{ + return; +} + +#endif /* CONFIG_KGDB_ON_SMC */ diff --git a/cpu/mpc8260/speed.c b/cpu/mpc8260/speed.c new file mode 100644 index 0000000..2bf2c4a --- /dev/null +++ b/cpu/mpc8260/speed.c @@ -0,0 +1,211 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +/* ------------------------------------------------------------------------- */ + +/* Bus-to-Core Multiplier */ +#define _1x 2 +#define _1_5x 3 +#define _2x 4 +#define _2_5x 5 +#define _3x 6 +#define _3_5x 7 +#define _4x 8 +#define _4_5x 9 +#define _5x 10 +#define _5_5x 11 +#define _6x 12 +#define _6_5x 13 +#define _7x 14 +#define _7_5x 15 +#define _8x 16 +#define _byp -1 +#define _off -2 +#define _unk -3 + +typedef struct { + int b2c_mult; + int vco_div; + char *freq_60x; + char *freq_core; +} corecnf_t; + +/* + * this table based on "Errata to MPC8260 PowerQUICC II User's Manual", + * Rev. 1, 8/2000, page 10. + */ +corecnf_t corecnf_tab[] = { + { _1_5x, 4, " 33-100", " 33-100" }, /* 0x00 */ + { _1x, 4, " 50-150", " 50-150" }, /* 0x01 */ + { _1x, 8, " 25-75 ", " 25-75 " }, /* 0x02 */ + { _byp, -1, " ?-? ", " ?-? " }, /* 0x03 */ + { _2x, 2, " 50-150", "100-300" }, /* 0x04 */ + { _2x, 4, " 25-75 ", " 50-150" }, /* 0x05 */ + { _2_5x, 2, " 40-120", "100-240" }, /* 0x06 */ + { _4_5x, 2, " 22-65 ", "100-300" }, /* 0x07 */ + { _3x, 2, " 33-100", "100-300" }, /* 0x08 */ + { _5_5x, 2, " 18-55 ", "100-300" }, /* 0x09 */ + { _4x, 2, " 25-75 ", "100-300" }, /* 0x0A */ + { _5x, 2, " 20-60 ", "100-300" }, /* 0x0B */ + { _1_5x, 8, " 16-50 ", " 16-50 " }, /* 0x0C */ + { _6x, 2, " 16-50 ", "100-300" }, /* 0x0D */ + { _3_5x, 2, " 30-85 ", "100-300" }, /* 0x0E */ + { _off, -1, " ?-? ", " ?-? " }, /* 0x0F */ + { _3x, 4, " 16-50 ", " 50-150" }, /* 0x10 */ + { _2_5x, 4, " 20-60 ", " 50-120" }, /* 0x11 */ + { _6_5x, 2, " 15-46 ", "100-300" }, /* 0x12 */ + { _byp, -1, " ?-? ", " ?-? " }, /* 0x13 */ + { _7x, 2, " 14-43 ", "100-300" }, /* 0x14 */ + { _2x, 4, " 25-75 ", " 50-150" }, /* 0x15 */ + { _7_5x, 2, " 13-40 ", "100-300" }, /* 0x16 */ + { _4_5x, 2, " 22-65 ", "100-300" }, /* 0x17 */ + { _unk, -1, " ?-? ", " ?-? " }, /* 0x18 */ + { _5_5x, 2, " 18-55 ", "100-300" }, /* 0x19 */ + { _4x, 2, " 25-75 ", "100-300" }, /* 0x1A */ + { _5x, 2, " 20-60 ", "100-300" }, /* 0x1B */ + { _8x, 2, " 12-38 ", "100-300" }, /* 0x1C */ + { _6x, 2, " 16-50 ", "100-300" }, /* 0x1D */ + { _3_5x, 2, " 30-85 ", "100-300" }, /* 0x1E */ + { _off, -1, " ?-? ", " ?-? " }, /* 0x1F */ +}; + +/* ------------------------------------------------------------------------- */ + +/* + * + */ + +int get_clocks (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immap = (immap_t *) CFG_IMMR; + ulong clkin; + ulong sccr, dfbrg; + ulong scmr, corecnf, busdf, cpmdf, plldf, pllmf; + corecnf_t *cp; + +#if !defined(CONFIG_8260_CLKIN) +#error clock measuring not implemented yet - define CONFIG_8260_CLKIN +#else + clkin = CONFIG_8260_CLKIN; +#endif + + sccr = immap->im_clkrst.car_sccr; + dfbrg = (sccr & SCCR_DFBRG_MSK) >> SCCR_DFBRG_SHIFT; + + scmr = immap->im_clkrst.car_scmr; + corecnf = (scmr & SCMR_CORECNF_MSK) >> SCMR_CORECNF_SHIFT; + busdf = (scmr & SCMR_BUSDF_MSK) >> SCMR_BUSDF_SHIFT; + cpmdf = (scmr & SCMR_CPMDF_MSK) >> SCMR_CPMDF_SHIFT; + plldf = (scmr & SCMR_PLLDF) ? 1 : 0; + pllmf = (scmr & SCMR_PLLMF_MSK) >> SCMR_PLLMF_SHIFT; + + cp = &corecnf_tab[corecnf]; + + gd->vco_out = (clkin * 2 * (pllmf + 1)) / (plldf + 1); + +#if 0 + if (gd->vco_out / (busdf + 1) != clkin) { + /* aaarrrggghhh!!! */ + return (1); + } +#endif + + gd->cpm_clk = gd->vco_out / 2; + gd->bus_clk = clkin; + gd->scc_clk = gd->vco_out / 4; + gd->brg_clk = gd->vco_out / (1 << (2 * (dfbrg + 1))); + + if (cp->b2c_mult > 0) { + gd->cpu_clk = (clkin * cp->b2c_mult) / 2; + } else { + gd->cpu_clk = clkin; + } + + return (0); +} + +int prt_8260_clks (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immap = (immap_t *) CFG_IMMR; + ulong sccr, dfbrg; + ulong scmr, corecnf, busdf, cpmdf, plldf, pllmf; + corecnf_t *cp; + + sccr = immap->im_clkrst.car_sccr; + dfbrg = (sccr & SCCR_DFBRG_MSK) >> SCCR_DFBRG_SHIFT; + + scmr = immap->im_clkrst.car_scmr; + corecnf = (scmr & SCMR_CORECNF_MSK) >> SCMR_CORECNF_SHIFT; + busdf = (scmr & SCMR_BUSDF_MSK) >> SCMR_BUSDF_SHIFT; + cpmdf = (scmr & SCMR_CPMDF_MSK) >> SCMR_CPMDF_SHIFT; + plldf = (scmr & SCMR_PLLDF) ? 1 : 0; + pllmf = (scmr & SCMR_PLLMF_MSK) >> SCMR_PLLMF_SHIFT; + + cp = &corecnf_tab[corecnf]; + + printf ("MPC8260 Clock Configuration\n - Bus-to-Core Mult "); + + switch (cp->b2c_mult) { + case _byp: + printf ("BYPASS"); + break; + + case _off: + printf ("OFF"); + break; + + case _unk: + printf ("UNKNOWN"); + break; + + default: + printf ("%d%sx", + cp->b2c_mult / 2, + (cp->b2c_mult % 2) ? ".5" : ""); + break; + } + + printf (", VCO Div %d, 60x Bus Freq %s, Core Freq %s\n", + cp->vco_div, cp->freq_60x, cp->freq_core); + + printf (" - dfbrg %ld, corecnf 0x%02lx, busdf %ld, cpmdf %ld, " + "plldf %ld, pllmf %ld\n", dfbrg, corecnf, busdf, cpmdf, plldf, + pllmf); + + printf (" - vco_out %10ld, scc_clk %10ld, brg_clk %10ld\n", + gd->vco_out, gd->scc_clk, gd->brg_clk); + + printf (" - cpu_clk %10ld, cpm_clk %10ld, bus_clk %10ld\n\n", + gd->cpu_clk, gd->cpm_clk, gd->bus_clk); + return (0); +} + +/* ------------------------------------------------------------------------- */ diff --git a/cpu/mpc8xx/cpu_init.c b/cpu/mpc8xx/cpu_init.c new file mode 100644 index 0000000..abe6209 --- /dev/null +++ b/cpu/mpc8xx/cpu_init.c @@ -0,0 +1,262 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include + +#include +#include + +#if defined(CFG_I2C_UCODE_PATCH) || defined(CFG_SPI_UCODE_PATCH) +void cpm_load_patch (volatile immap_t * immr); +#endif + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * immr) +{ +#ifndef CONFIG_MBX + volatile memctl8xx_t *memctl = &immr->im_memctl; + ulong reg; +#endif + + /* SYPCR - contains watchdog control (11-9) */ + + immr->im_siu_conf.sc_sypcr = CFG_SYPCR; + +#if defined(CONFIG_WATCHDOG) + reset_8xx_watchdog (immr); +#endif /* CONFIG_WATCHDOG */ + + /* SIUMCR - contains debug pin configuration (11-6) */ + + immr->im_siu_conf.sc_siumcr |= CFG_SIUMCR; + + /* initialize timebase status and control register (11-26) */ + /* unlock TBSCRK */ + + immr->im_sitk.sitk_tbscrk = KAPWR_KEY; + immr->im_sit.sit_tbscr = CFG_TBSCR; + + /* initialize the PIT (11-31) */ + + immr->im_sitk.sitk_piscrk = KAPWR_KEY; + immr->im_sit.sit_piscr = CFG_PISCR; + + /* PLL (CPU clock) settings (15-30) */ + + immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + +#ifndef CONFIG_MBX /* MBX board does things different */ + + /* If CFG_PLPRCR (set in the various *_config.h files) tries to + * set the MF field, then just copy CFG_PLPRCR over car_plprcr, + * otherwise OR in CFG_PLPRCR so we do not change the currentMF + * field value. + */ +#if ((CFG_PLPRCR & PLPRCR_MF_MSK) != 0) + reg = CFG_PLPRCR; /* reset control bits */ +#else + reg = immr->im_clkrst.car_plprcr; + reg &= PLPRCR_MF_MSK; /* isolate MF field */ + reg |= CFG_PLPRCR; /* reset control bits */ +#endif + immr->im_clkrst.car_plprcr = reg; + + /* System integration timers. Don't change EBDF! (15-27) */ + + immr->im_clkrstk.cark_sccrk = KAPWR_KEY; + reg = immr->im_clkrst.car_sccr; + reg &= SCCR_MASK; + reg |= CFG_SCCR; + immr->im_clkrst.car_sccr = reg; + + /* + * Memory Controller: + */ + + /* perform BR0 reset that MPC850 Rev. A can't guarantee */ + reg = memctl->memc_br0; + reg &= BR_PS_MSK; /* Clear everything except Port Size bits */ + reg |= BR_V; /* then add just the "Bank Valid" bit */ + memctl->memc_br0 = reg; + + /* Map banks 0 (and maybe 1) to the FLASH banks 0 (and 1) at + * preliminary addresses - these have to be modified later + * when FLASH size has been determined + * + * Depending on the size of the memory region defined by + * CFG_OR0_REMAP some boards (wide address mask) allow to map the + * CFG_MONITOR_BASE, while others (narrower address mask) can't + * map CFG_MONITOR_BASE. + * + * For example, for CONFIG_IVMS8, the CFG_MONITOR_BASE is + * 0xff000000, but CFG_OR0_REMAP's address mask is 0xfff80000. + * + * If BR0 wasn't loaded with address base 0xff000000, then BR0's + * base address remains as 0x00000000. However, the address mask + * have been narrowed to 512Kb, so CFG_MONITOR_BASE wasn't mapped + * into the Bank0. + * + * This is why CONFIG_IVMS8 and similar boards must load BR0 with + * CFG_BR0_PRELIM in advance. + * + * [Thanks to Michael Liao for this explanation. + * I owe him a free beer. - wd] + */ + +#if defined(CONFIG_GTH) || \ + defined(CONFIG_HERMES) || \ + defined(CONFIG_ICU862) || \ + defined(CONFIG_IP860) || \ + defined(CONFIG_IVML24) || \ + defined(CONFIG_IVMS8) || \ + defined(CONFIG_LWMON) || \ + defined(CONFIG_MHPC) || \ + defined(CONFIG_PCU_E) || \ + defined(CONFIG_R360MPI) || \ + defined(CONFIG_RPXCLASSIC) || \ + defined(CONFIG_RPXLITE) || \ + defined(CONFIG_SPD823TS) || \ + (defined(CONFIG_MPC860T) && defined(CONFIG_FADS)) + + memctl->memc_br0 = CFG_BR0_PRELIM; +#endif + +#if defined(CFG_OR0_REMAP) + memctl->memc_or0 = CFG_OR0_REMAP; +#endif +#if defined(CFG_OR1_REMAP) + memctl->memc_or1 = CFG_OR1_REMAP; +#endif +#if defined(CFG_OR5_REMAP) + memctl->memc_or5 = CFG_OR5_REMAP; +#endif + + /* now restrict to preliminary range */ + memctl->memc_br0 = CFG_BR0_PRELIM; + memctl->memc_or0 = CFG_OR0_PRELIM; + +#if (defined(CFG_OR1_PRELIM) && defined(CFG_BR1_PRELIM)) + memctl->memc_or1 = CFG_OR1_PRELIM; + memctl->memc_br1 = CFG_BR1_PRELIM; +#endif + +#if defined(CONFIG_IP860) /* disable CS0 now that Flash is mapped on CS1 */ + memctl->memc_br0 = 0; +#endif + +#if defined(CFG_OR2_PRELIM) && defined(CFG_BR2_PRELIM) + memctl->memc_or2 = CFG_OR2_PRELIM; + memctl->memc_br2 = CFG_BR2_PRELIM; +#endif + +#if defined(CFG_OR3_PRELIM) && defined(CFG_BR3_PRELIM) + memctl->memc_or3 = CFG_OR3_PRELIM; + memctl->memc_br3 = CFG_BR3_PRELIM; +#endif + +#if defined(CFG_OR4_PRELIM) && defined(CFG_BR4_PRELIM) + memctl->memc_or4 = CFG_OR4_PRELIM; + memctl->memc_br4 = CFG_BR4_PRELIM; +#endif + +#if defined(CFG_OR5_PRELIM) && defined(CFG_BR5_PRELIM) + memctl->memc_or5 = CFG_OR5_PRELIM; + memctl->memc_br5 = CFG_BR5_PRELIM; +#endif + +#if defined(CFG_OR6_PRELIM) && defined(CFG_BR6_PRELIM) + memctl->memc_or6 = CFG_OR6_PRELIM; + memctl->memc_br6 = CFG_BR6_PRELIM; +#endif + +#if defined(CFG_OR7_PRELIM) && defined(CFG_BR7_PRELIM) + memctl->memc_or7 = CFG_OR7_PRELIM; + memctl->memc_br7 = CFG_BR7_PRELIM; +#endif + +#endif /* ! CONFIG_MBX */ + + /* + * Reset CPM + */ + immr->im_cpm.cp_cpcr = CPM_CR_RST | CPM_CR_FLG; + do { /* Spin until command processed */ + __asm__ ("eieio"); + } while (immr->im_cpm.cp_cpcr & CPM_CR_FLG); + +#ifdef CONFIG_MBX + /* + * on the MBX, things are a little bit different: + * - we need to read the VPD to get board information + * - the plprcr is set up dynamically + * - the memory controller is set up dynamically + */ + mbx_init (); +#endif /* CONFIG_MBX */ + +#ifdef CONFIG_RPXCLASSIC + rpxclassic_init (); +#endif + +#ifdef CFG_RCCR /* must be done before cpm_load_patch() */ + /* write config value */ + immr->im_cpm.cp_rccr = CFG_RCCR; +#endif + +#if defined(CFG_I2C_UCODE_PATCH) || defined(CFG_SPI_UCODE_PATCH) + cpm_load_patch (immr); /* load mpc8xx microcode patch */ +#endif +} + +/* + * initialize higher level parts of CPU like timers + */ +int cpu_init_r (void) +{ +#if defined(CFG_RTCSC) || defined(CFG_RMDS) + DECLARE_GLOBAL_DATA_PTR; + + bd_t *bd = gd->bd; + volatile immap_t *immr = (volatile immap_t *) (bd->bi_immr_base); +#endif + +#ifdef CFG_RTCSC + /* Unlock RTSC register */ + immr->im_sitk.sitk_rtcsck = KAPWR_KEY; + /* write config value */ + immr->im_sit.sit_rtcsc = CFG_RTCSC; +#endif + +#ifdef CFG_RMDS + /* write config value */ + immr->im_cpm.cp_rmds = CFG_RMDS; +#endif + return (0); +} diff --git a/cpu/mpc8xx/serial.c b/cpu/mpc8xx/serial.c new file mode 100644 index 0000000..0690300 --- /dev/null +++ b/cpu/mpc8xx/serial.c @@ -0,0 +1,640 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +#if !defined(CONFIG_8xx_CONS_NONE) /* No Console at all */ + +#if defined(CONFIG_8xx_CONS_SMC1) /* Console on SMC1 */ +#define SMC_INDEX 0 +#undef SCC_INDEX +#define PROFF_SMC PROFF_SMC1 +#define CPM_CR_CH_SMC CPM_CR_CH_SMC1 + +#elif defined(CONFIG_8xx_CONS_SMC2) /* Console on SMC2 */ +#define SMC_INDEX 1 +#undef SCC_INDEX +#define PROFF_SMC PROFF_SMC2 +#define CPM_CR_CH_SMC CPM_CR_CH_SMC2 + +#elif defined(CONFIG_8xx_CONS_SCC1) /* Console on SCC1 */ +#undef SMC_INDEX +#define SCC_INDEX 0 +#define PROFF_SCC PROFF_SCC1 +#define CPM_CR_CH_SCC CPM_CR_CH_SCC1 + +#elif defined(CONFIG_8xx_CONS_SCC2) /* Console on SCC2 */ +#undef SMC_INDEX +#define SCC_INDEX 1 +#define PROFF_SCC PROFF_SCC2 +#define CPM_CR_CH_SCC CPM_CR_CH_SCC2 + +#elif defined(CONFIG_8xx_CONS_SCC3) /* Console on SCC3 */ +#undef SMC_INDEX +#define SCC_INDEX 2 +#define PROFF_SCC PROFF_SCC3 +#define CPM_CR_CH_SCC CPM_CR_CH_SCC3 + +#elif defined(CONFIG_8xx_CONS_SCC4) /* Console on SCC4 */ +#undef SMC_INDEX +#define SCC_INDEX 3 +#define PROFF_SCC PROFF_SCC4 +#define CPM_CR_CH_SCC CPM_CR_CH_SCC4 + +#else /* CONFIG_8xx_CONS_? */ +#error "console not correctly defined" +#endif + +#if (defined (CONFIG_8xx_CONS_SMC1) || defined (CONFIG_8xx_CONS_SMC2)) + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +int serial_init (void) +{ + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile smc_t *sp; + volatile smc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp = &(im->im_cpm); +#if (!defined(CONFIG_8xx_CONS_SMC1)) && (defined(CONFIG_MPC823) || defined(CONFIG_MPC850)) + volatile iop8xx_t *ip = (iop8xx_t *)&(im->im_ioport); +#endif + uint dpaddr; + + /* initialize pointers to SMC */ + + sp = (smc_t *) &(cp->cp_smc[SMC_INDEX]); + up = (smc_uart_t *) &cp->cp_dparam[PROFF_SMC]; + + /* Disable transmitter/receiver. + */ + sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + + /* Enable SDMA. + */ + im->im_siu_conf.sc_sdcr = 1; + + /* clear error conditions */ +#ifdef CFG_SDSR + im->im_sdma.sdma_sdsr = CFG_SDSR; +#else + im->im_sdma.sdma_sdsr = 0x83; +#endif + + /* clear SDMA interrupt mask */ +#ifdef CFG_SDMR + im->im_sdma.sdma_sdmr = CFG_SDMR; +#else + im->im_sdma.sdma_sdmr = 0x00; +#endif + +#if defined(CONFIG_8xx_CONS_SMC1) + /* Use Port B for SMC1 instead of other functions. + */ + cp->cp_pbpar |= 0x000000c0; + cp->cp_pbdir &= ~0x000000c0; + cp->cp_pbodr &= ~0x000000c0; +#else /* CONFIG_8xx_CONS_SMC2 */ +# if defined(CONFIG_MPC823) || defined(CONFIG_MPC850) + /* Use Port A for SMC2 instead of other functions. + */ + ip->iop_papar |= 0x00c0; + ip->iop_padir &= ~0x00c0; + ip->iop_paodr &= ~0x00c0; +# else /* must be a 860 then */ + /* Use Port B for SMC2 instead of other functions. + */ + cp->cp_pbpar |= 0x00000c00; + cp->cp_pbdir &= ~0x00000c00; + cp->cp_pbodr &= ~0x00000c00; +# endif +#endif + +#if defined(CONFIG_FADS) + /* Enable RS232 */ +#if defined(CONFIG_8xx_CONS_SMC1) + *((uint *) BCSR1) &= ~BCSR1_RS232EN_1; +#else + *((uint *) BCSR1) &= ~BCSR1_RS232EN_2; +#endif +#endif /* CONFIG_FADS */ + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) + /* Enable Monitor Port Transceiver */ + *((uchar *) BCSR0) |= BCSR0_ENMONXCVR ; +#endif /* CONFIG_RPXLITE */ + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + +#ifdef CFG_ALLOC_DPRAM + dpaddr = dpram_alloc_align (sizeof(cbd_t)*2 + 2, 8) ; +#else + dpaddr = CPM_SERIAL_BASE ; +#endif + + /* Allocate space for two buffer descriptors in the DP ram. + * For now, this address seems OK, but it may have to + * change with newer versions of the firmware. + * damm: allocating space after the two buffers for rx/tx data + */ + + rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = 0; + + /* Set up the uart parameters in the parameter ram. + */ + up->smc_rbase = dpaddr; + up->smc_tbase = dpaddr+sizeof(cbd_t); + up->smc_rfcr = SMC_EB; + up->smc_tfcr = SMC_EB; + +#if defined(CONFIG_MBX) + board_serial_init(); +#endif /* CONFIG_MBX */ + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Mask all interrupts and remove anything pending. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* Set up the baud rate generator. + */ + serial_setbrg (); + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Single character receive. + */ + up->smc_mrblr = 1; + up->smc_maxidl = 0; + + /* Initialize Tx/Rx parameters. + */ + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + /* Enable transmitter/receiver. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + + return (0); +} + +void +serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cp = &(im->im_cpm); + + /* Set up the baud rate generator. + * See 8xx_io/commproc.c for details. + * + * Wire BRG1 to SMCx + */ + + cp->cp_simode = 0x00000000; + + cp->cp_brgc1 = + (((gd->cpu_clk / 16 / gd->baudrate)-1) << 1) | CPM_BRG_EN; +} + +void +serial_putc(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + + if (c == '\n') + serial_putc ('\r'); + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; + + tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + + /* Wait for last character to go. + */ + + buf = (char *)tbdf->cbd_bufaddr; +#if 0 + __asm__("eieio"); + while (tbdf->cbd_sc & BD_SC_READY) + __asm__("eieio"); +#endif + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; + __asm__("eieio"); +#if 1 + while (tbdf->cbd_sc & BD_SC_READY) + __asm__("eieio"); +#endif +} + +int +serial_getc(void) +{ + volatile cbd_t *rbdf; + volatile unsigned char *buf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + unsigned char c; + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; + + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + /* Wait for character to show up. + */ + buf = (unsigned char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile smc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + + up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; + + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} + +#else /* ! CONFIG_8xx_CONS_SMC1, CONFIG_8xx_CONS_SMC2 */ + +int serial_init (void) +{ + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile scc_t *sp; + volatile scc_uart_t *up; + volatile cbd_t *tbdf, *rbdf; + volatile cpm8xx_t *cp = &(im->im_cpm); + uint dpaddr; +#if (SCC_INDEX != 2) || !defined(CONFIG_MPC850) + volatile iop8xx_t *ip = (iop8xx_t *)&(im->im_ioport); +#endif + + /* initialize pointers to SCC */ + + sp = (scc_t *) &(cp->cp_scc[SCC_INDEX]); + up = (scc_uart_t *) &cp->cp_dparam[PROFF_SCC]; + +#if defined(CONFIG_LWMON) && defined(CONFIG_8xx_CONS_SCC2) + { /* Disable Ethernet, enable Serial */ + uchar c; + + c = pic_read (0x61); + c &= ~0x40; /* enable COM3 */ + c |= 0x80; /* disable Ethernet */ + pic_write (0x61, c); + + /* enable RTS2 */ + cp->cp_pbpar |= 0x2000; + cp->cp_pbdat |= 0x2000; + cp->cp_pbdir |= 0x2000; + } +#endif /* CONFIG_LWMON */ + + /* Disable transmitter/receiver. + */ + sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + +#if (SCC_INDEX == 2) && defined(CONFIG_MPC850) + /* + * The MPC850 has SCC3 on Port B + */ + cp->cp_pbpar |= 0x06; + cp->cp_pbdir &= ~0x06; + cp->cp_pbodr &= ~0x06; + +#elif (SCC_INDEX < 2) || !defined(CONFIG_IP860) + /* + * Standard configuration for SCC's is on Part A + */ + ip->iop_papar |= ((3 << (2 * SCC_INDEX))); + ip->iop_padir &= ~((3 << (2 * SCC_INDEX))); + ip->iop_paodr &= ~((3 << (2 * SCC_INDEX))); +#else + /* + * The IP860 has SCC3 and SCC4 on Port D + */ + ip->iop_pdpar |= ((3 << (2 * SCC_INDEX))); +#endif + + /* Allocate space for two buffer descriptors in the DP ram. + */ + +#ifdef CFG_ALLOC_DPRAM + dpaddr = dpram_alloc_align (sizeof(cbd_t)*2 + 2, 8) ; +#else + dpaddr = CPM_SERIAL_BASE ; +#endif + + /* Enable SDMA. + */ + im->im_siu_conf.sc_sdcr = 0x0001; + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + + rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; + rbdf->cbd_bufaddr = (uint) (rbdf+2); + rbdf->cbd_sc = 0; + tbdf = rbdf + 1; + tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; + tbdf->cbd_sc = 0; + + /* Set up the baud rate generator. + */ + serial_setbrg (); + + /* Set up the uart parameters in the parameter ram. + */ + up->scc_genscc.scc_rbase = dpaddr; + up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + + /* Initialize Tx/Rx parameters. + */ + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC, CPM_CR_INIT_TRX) | CPM_CR_FLG; + + while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */ + ; + + up->scc_genscc.scc_rfcr = SCC_EB | 0x05; + up->scc_genscc.scc_tfcr = SCC_EB | 0x05; + + up->scc_genscc.scc_mrblr = 1; /* Single character receive */ + up->scc_maxidl = 0; /* disable max idle */ + up->scc_brkcr = 1; /* send one break character on stop TX */ + up->scc_parec = 0; + up->scc_frmec = 0; + up->scc_nosec = 0; + up->scc_brkec = 0; + up->scc_uaddr1 = 0; + up->scc_uaddr2 = 0; + up->scc_toseq = 0; + up->scc_char1 = 0x8000; + up->scc_char2 = 0x8000; + up->scc_char3 = 0x8000; + up->scc_char4 = 0x8000; + up->scc_char5 = 0x8000; + up->scc_char6 = 0x8000; + up->scc_char7 = 0x8000; + up->scc_char8 = 0x8000; + up->scc_rccm = 0xc0ff; + + /* Set low latency / small fifo. + */ + sp->scc_gsmrh = SCC_GSMRH_RFW; + + /* Set SCC(x) clock mode to 16x + * See 8xx_io/commproc.c for details. + * + * Wire BRG1 to SCCn + */ + + /* Set UART mode, clock divider 16 on Tx and Rx + */ + sp->scc_gsmrl |= + (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + + sp->scc_psmr |= SCU_PSMR_CL; + + /* Mask all interrupts and remove anything pending. + */ + sp->scc_sccm = 0; + sp->scc_scce = 0xffff; + sp->scc_dsr = 0x7e7e; + sp->scc_psmr = 0x3000; + + /* Make the first buffer the only buffer. + */ + tbdf->cbd_sc |= BD_SC_WRAP; + rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + + /* Enable transmitter/receiver. + */ + sp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + return (0); +} + +void +serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cp = &(im->im_cpm); + + /* Set up the baud rate generator. + * See 8xx_io/commproc.c for details. + * + * Wire BRG1 to SCCx + */ + + cp->cp_sicr &= ~(0x000000FF << (8 * SCC_INDEX)); + /* no |= needed, since BRG1 is 000 */ + + cp->cp_brgc1 = + (((gd->cpu_clk / 16 / gd->baudrate)-1) << 1) | CPM_BRG_EN; +} + +void +serial_putc(const char c) +{ + volatile cbd_t *tbdf; + volatile char *buf; + volatile scc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + + if (c == '\n') + serial_putc ('\r'); + + up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + + tbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_tbase]; + + /* Wait for last character to go. + */ + + buf = (char *)tbdf->cbd_bufaddr; +#if 0 + __asm__("eieio"); + while (tbdf->cbd_sc & BD_SC_READY) + __asm__("eieio"); +#endif + + *buf = c; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc |= BD_SC_READY; + __asm__("eieio"); +#if 1 + while (tbdf->cbd_sc & BD_SC_READY) + __asm__("eieio"); +#endif +} + +int +serial_getc(void) +{ + volatile cbd_t *rbdf; + volatile unsigned char *buf; + volatile scc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + unsigned char c; + + up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_rbase]; + + /* Wait for character to show up. + */ + buf = (unsigned char *)rbdf->cbd_bufaddr; + while (rbdf->cbd_sc & BD_SC_EMPTY) + ; + c = *buf; + rbdf->cbd_sc |= BD_SC_EMPTY; + + return(c); +} + +int +serial_tstc() +{ + volatile cbd_t *rbdf; + volatile scc_uart_t *up; + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cpmp = &(im->im_cpm); + + up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + + rbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_rbase]; + + return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} + +#endif /* CONFIG_8xx_CONS_SMC1, CONFIG_8xx_CONS_SMC2 */ + + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + +void +kgdb_serial_init(void) +{ +#if defined(CONFIG_8xx_CONS_SMC1) + serial_printf("[on SMC1] "); +#elif defined(CONFIG_8xx_CONS_SMC2) + serial_printf("[on SMC2] "); +#elif defined(CONFIG_8xx_CONS_SCC1) + serial_printf("[on SCC1] "); +#elif defined(CONFIG_8xx_CONS_SCC2) + serial_printf("[on SCC2] "); +#elif defined(CONFIG_8xx_CONS_SCC3) + serial_printf("[on SCC3] "); +#elif defined(CONFIG_8xx_CONS_SCC4) + serial_printf("[on SCC4] "); +#endif +} + +void +putDebugChar (int c) +{ + serial_putc (c); +} + +void +putDebugStr (const char *str) +{ + serial_puts (str); +} + +int +getDebugChar (void) +{ + return serial_getc(); +} + +void +kgdb_interruptible (int yes) +{ + return; +} +#endif /* CFG_CMD_KGDB */ + +#endif /* CONFIG_8xx_CONS_NONE */ diff --git a/cpu/mpc8xx/speed.c b/cpu/mpc8xx/speed.c new file mode 100644 index 0000000..ef32371 --- /dev/null +++ b/cpu/mpc8xx/speed.c @@ -0,0 +1,187 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +#define PITC_SHIFT 16 +#define PITR_SHIFT 16 +/* pitc values to time for 58/8192 seconds (about 70.8 milliseconds) */ +#define SPEED_PIT_COUNTS 58 +#define SPEED_PITC ((SPEED_PIT_COUNTS - 1) << PITC_SHIFT) +#define SPEED_PITC_INIT ((SPEED_PIT_COUNTS + 1) << PITC_SHIFT) + +#if !defined(CONFIG_8xx_GCLK_FREQ) +/* Access functions for the Machine State Register */ +static __inline__ unsigned long get_msr(void) +{ + unsigned long msr; + + asm volatile("mfmsr %0" : "=r" (msr) :); + return msr; +} + +static __inline__ void set_msr(unsigned long msr) +{ + asm volatile("mtmsr %0" : : "r" (msr)); +} +#endif + +/* ------------------------------------------------------------------------- */ + +/* + * Measure CPU clock speed (core clock GCLK1, GCLK2), + * also determine bus clock speed (checking bus divider factor) + * + * (Approx. GCLK frequency in Hz) + * + * Initializes timer 2 and PIT, but disables them before return. + * [Use timer 2, because MPC823 CPUs mask 0.x do not have timers 3 and 4] + * + * When measuring the CPU clock against the PIT, we count cpu clocks + * for 58/8192 seconds with a prescale divide by 177 for the cpu clock. + * These strange values for the timing interval and prescaling are used + * because the formula for the CPU clock is: + * + * CPU clock = count * (177 * (8192 / 58)) + * + * = count * 24999.7241 + * + * which is very close to + * + * = count * 25000 + * + * Since the count gives the CPU clock divided by 25000, we can get + * the CPU clock rounded to the nearest 0.1 MHz by + * + * CPU clock = ((count + 2) / 4) * 100000; + * + * The rounding is important since the measurement is sometimes going + * to be high or low by 0.025 MHz, depending on exactly how the clocks + * and counters interact. By rounding we get the exact answer for any + * CPU clock that is an even multiple of 0.1 MHz. + */ + +int get_clocks (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immr = (immap_t *) CFG_IMMR; +#ifndef CONFIG_8xx_GCLK_FREQ + volatile cpmtimer8xx_t *timerp = &immr->im_cpmtimer; + ulong timer2_val; + ulong msr_val; + + /* Reset + Stop Timer 2, no cascading + */ + timerp->cpmt_tgcr &= ~(TGCR_CAS2 | TGCR_RST2); + + /* Keep stopped, halt in debug mode + */ + timerp->cpmt_tgcr |= (TGCR_FRZ2 | TGCR_STP2); + + /* Timer 2 setup: + * Output ref. interrupt disable, int. clock + * Prescale by 177. Note that prescaler divides by value + 1 + * so we must subtract 1 here. + */ + timerp->cpmt_tmr2 = ((177 - 1) << TMR_PS_SHIFT) | TMR_ICLK_IN_GEN; + + timerp->cpmt_tcn2 = 0; /* reset state */ + timerp->cpmt_tgcr |= TGCR_RST2; /* enable timer 2 */ + + /* + * PIT setup: + * + * We want to time for SPEED_PITC_COUNTS counts (of 8192 Hz), + * so the count value would be SPEED_PITC_COUNTS - 1. + * But there would be an uncertainty in the start time of 1/4 + * count since when we enable the PIT the count is not + * synchronized to the 32768 Hz oscillator. The trick here is + * to start the count higher and wait until the PIT count + * changes to the required value before starting timer 2. + * + * One count high should be enough, but occasionally the start + * is off by 1 or 2 counts of 32768 Hz. With the start value + * set two counts high it seems very reliable. + */ + + immr->im_sitk.sitk_pitck = KAPWR_KEY; /* PIT initialization */ + immr->im_sit.sit_pitc = SPEED_PITC_INIT; + + immr->im_sitk.sitk_piscrk = KAPWR_KEY; + immr->im_sit.sit_piscr = CFG_PISCR; + + /* + * Start measurement - disable interrupts, just in case + */ + msr_val = get_msr (); + set_msr (msr_val & ~MSR_EE); + + immr->im_sit.sit_piscr |= PISCR_PTE; + + /* spin until get exact count when we want to start */ + while (immr->im_sit.sit_pitr > SPEED_PITC); + + timerp->cpmt_tgcr &= ~TGCR_STP2; /* Start Timer 2 */ + while ((immr->im_sit.sit_piscr & PISCR_PS) == 0); + timerp->cpmt_tgcr |= TGCR_STP2; /* Stop Timer 2 */ + + /* re-enable external interrupts if they were on */ + set_msr (msr_val); + + /* Disable timer and PIT + */ + timer2_val = timerp->cpmt_tcn2; /* save before reset timer */ + + timerp->cpmt_tgcr &= ~(TGCR_RST2 | TGCR_FRZ2 | TGCR_STP2); + immr->im_sit.sit_piscr &= ~PISCR_PTE; + + gd->cpu_clk = ((timer2_val + 2) / 4) * 100000L; /* convert to Hz */ + +#else /* CONFIG_8xx_GCLK_FREQ */ + + /* + * If for some reason measuring the gclk frequency won't + * work, we return the hardwired value. + * (For example, the cogent CMA286-60 CPU module has no + * separate oscillator for PITRTCLK) + */ + + gd->cpu_clk = CONFIG_8xx_GCLK_FREQ; + +#endif /* CONFIG_8xx_GCLK_FREQ */ + + if ((immr->im_clkrst.car_sccr & SCCR_EBDF11) == 0) { + /* No Bus Divider active */ + gd->bus_clk = gd->cpu_clk; + } else { + /* The MPC8xx has only one BDF: half clock speed */ + gd->bus_clk = gd->cpu_clk / 2; + } + + return (0); +} + +/* ------------------------------------------------------------------------- */ diff --git a/cpu/mpc8xx/wlkbd.c b/cpu/mpc8xx/wlkbd.c new file mode 100644 index 0000000..13009e2 --- /dev/null +++ b/cpu/mpc8xx/wlkbd.c @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include + +#ifdef CONFIG_WL_4PPM_KEYBOARD + +/* WIP: Wireless keyboard on SMC + */ +int drv_wlkbd_init (void) +{ + return 0 ; +} + +#endif /* CONFIG_WL_4PPM_KEYBOARD */ diff --git a/cpu/ppc4xx/cpu_init.c b/cpu/ppc4xx/cpu_init.c new file mode 100644 index 0000000..52d4650 --- /dev/null +++ b/cpu/ppc4xx/cpu_init.c @@ -0,0 +1,148 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include <405gp_enet.h> +#include +#include + + +#define mtebc(reg, data) mtdcr(ebccfga,reg);mtdcr(ebccfgd,data) + + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers + */ +void +cpu_init_f (void) +{ + /* + * External Bus Controller (EBC) Setup + */ +#if (defined(CFG_EBC_PB0AP) && defined(CFG_EBC_PB0CR)) + /* + * Move the next instructions into icache, since these modify the flash + * we are running from! + */ + asm volatile(" bl 0f" ::: "lr"); + asm volatile("0: mflr 3" ::: "r3"); + asm volatile(" addi 4, 0, 14" ::: "r4"); + asm volatile(" mtctr 4" ::: "ctr"); + asm volatile("1: icbt 0, 3"); + asm volatile(" addi 3, 3, 32" ::: "r3"); + asm volatile(" bdnz 1b" ::: "ctr", "cr0"); + asm volatile(" addis 3, 0, 0x0" ::: "r3"); + asm volatile(" ori 3, 3, 0xA000" ::: "r3"); + asm volatile(" mtctr 3" ::: "ctr"); + asm volatile("2: bdnz 2b" ::: "ctr", "cr0"); + + mtebc(pb0ap, CFG_EBC_PB0AP); + mtebc(pb0cr, CFG_EBC_PB0CR); +#endif + +#if (defined(CFG_EBC_PB1AP) && defined(CFG_EBC_PB1CR)) + mtebc(pb1ap, CFG_EBC_PB1AP); + mtebc(pb1cr, CFG_EBC_PB1CR); +#endif + +#if (defined(CFG_EBC_PB2AP) && defined(CFG_EBC_PB2CR)) + mtebc(pb2ap, CFG_EBC_PB2AP); + mtebc(pb2cr, CFG_EBC_PB2CR); +#endif + +#if (defined(CFG_EBC_PB3AP) && defined(CFG_EBC_PB3CR)) + mtebc(pb3ap, CFG_EBC_PB3AP); + mtebc(pb3cr, CFG_EBC_PB3CR); +#endif + +#if (defined(CFG_EBC_PB4AP) && defined(CFG_EBC_PB4CR)) + mtebc(pb4ap, CFG_EBC_PB4AP); + mtebc(pb4cr, CFG_EBC_PB4CR); +#endif + +#if (defined(CFG_EBC_PB5AP) && defined(CFG_EBC_PB5CR)) + mtebc(pb5ap, CFG_EBC_PB5AP); + mtebc(pb5cr, CFG_EBC_PB5CR); +#endif + +#if (defined(CFG_EBC_PB6AP) && defined(CFG_EBC_PB6CR)) + mtebc(pb6ap, CFG_EBC_PB6AP); + mtebc(pb6cr, CFG_EBC_PB6CR); +#endif + +#if (defined(CFG_EBC_PB7AP) && defined(CFG_EBC_PB7CR)) + mtebc(pb7ap, CFG_EBC_PB7AP); + mtebc(pb7cr, CFG_EBC_PB7CR); +#endif + +#if defined(CONFIG_WATCHDOG) + unsigned long val; + + val = mfspr(tcr); + val |= 0xf0000000; /* generate system reset after 2.684 seconds */ + mtspr(tcr, val); + + val = mfspr(tsr); + val |= 0x80000000; /* enable watchdog timer */ + mtspr(tsr, val); + + reset_4xx_watchdog(); +#endif /* CONFIG_WATCHDOG */ +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ +#ifdef CONFIG_405GP + DECLARE_GLOBAL_DATA_PTR; + + bd_t *bd = gd->bd; + unsigned long reg; + + /* + * Write Ethernetaddress into on-chip register + */ + reg = 0x00000000; + reg |= bd->bi_enetaddr[0]; /* set high address */ + reg = reg << 8; + reg |= bd->bi_enetaddr[1]; + out32 (EMAC_IAH, reg); + + reg = 0x00000000; + reg |= bd->bi_enetaddr[2]; /* set low address */ + reg = reg << 8; + reg |= bd->bi_enetaddr[3]; + reg = reg << 8; + reg |= bd->bi_enetaddr[4]; + reg = reg << 8; + reg |= bd->bi_enetaddr[5]; + out32 (EMAC_IAL, reg); +#endif /* CONFIG_405GP */ + return (0); +} diff --git a/cpu/sa1100/cpu.c b/cpu/sa1100/cpu.c new file mode 100644 index 0000000..370ea6c --- /dev/null +++ b/cpu/sa1100/cpu.c @@ -0,0 +1,147 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Alex Zuepke + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * CPU specific code + */ + +#include +#include + +int cpu_init (void) +{ + /* + * setup up stack if necessary + */ +#ifdef CONFIG_USE_IRQ + IRQ_STACK_START = _armboot_end + + CONFIG_STACKSIZE + CONFIG_STACKSIZE_IRQ - 4; + FIQ_STACK_START = IRQ_STACK_START + CONFIG_STACKSIZE_FIQ; + _armboot_real_end = FIQ_STACK_START + 4; +#else + _armboot_real_end = _armboot_end + CONFIG_STACKSIZE; +#endif + return (0); +} + +int cleanup_before_linux (void) +{ + /* + * this function is called just before we call linux + * it prepares the processor for linux + * + * just disable everything that can disturb booting linux + */ + + unsigned long i; + + disable_interrupts (); + + /* turn off I-cache */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + i &= ~0x1000; + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); + + /* flush I-cache */ + asm ("mcr p15, 0, %0, c7, c5, 0": :"r" (i)); + + return (0); +} + +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + extern void reset_cpu (ulong addr); + + printf ("resetting ...\n"); + + udelay (50000); /* wait 50 ms */ + disable_interrupts (); + reset_cpu (0); + + /*NOTREACHED*/ + return (0); +} + +/* taken from blob */ +void icache_enable (void) +{ + register u32 i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + /* set i-cache */ + i |= 0x1000; + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); +} + +void icache_disable (void) +{ + register u32 i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + /* clear i-cache */ + i &= ~0x1000; + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); + + /* flush i-cache */ + asm ("mcr p15, 0, %0, c7, c5, 0": :"r" (i)); +} + +int icache_status (void) +{ + register u32 i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + /* return bit */ + return (i & 0x1000); +} + +/* we will never enable dcache, because we have to setup MMU first */ +void dcache_enable (void) +{ + return; +} + +void dcache_disable (void) +{ + return; +} + +int dcache_status (void) +{ + return 0; /* always off */ +}