diff options
Diffstat (limited to 'cpu/mpc824x')
-rw-r--r-- | cpu/mpc824x/cpu_init.c | 383 | ||||
-rw-r--r-- | cpu/mpc824x/drivers/i2c/i2c1.c | 1228 | ||||
-rw-r--r-- | cpu/mpc824x/pci.c | 78 | ||||
-rw-r--r-- | cpu/mpc824x/start.S | 835 |
4 files changed, 2524 insertions, 0 deletions
diff --git a/cpu/mpc824x/cpu_init.c b/cpu/mpc824x/cpu_init.c new file mode 100644 index 00000000000..602f65d30b3 --- /dev/null +++ b/cpu/mpc824x/cpu_init.c @@ -0,0 +1,383 @@ +/* + * (C) Copyright 2000 + * 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 <common.h> +#include <asm/processor.h> +#include <mpc824x.h> + +#ifndef CFG_BANK0_ROW +#define CFG_BANK0_ROW 0 +#endif +#ifndef CFG_BANK1_ROW +#define CFG_BANK1_ROW 0 +#endif +#ifndef CFG_BANK2_ROW +#define CFG_BANK2_ROW 0 +#endif +#ifndef CFG_BANK3_ROW +#define CFG_BANK3_ROW 0 +#endif +#ifndef CFG_BANK4_ROW +#define CFG_BANK4_ROW 0 +#endif +#ifndef CFG_BANK5_ROW +#define CFG_BANK5_ROW 0 +#endif +#ifndef CFG_BANK6_ROW +#define CFG_BANK6_ROW 0 +#endif +#ifndef CFG_BANK7_ROW +#define CFG_BANK7_ROW 0 +#endif +#ifndef CFG_DBUS_SIZE2 +#define CFG_DBUS_SIZE2 0 +#endif + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + */ +void +cpu_init_f (void) +{ +/* MOUSSE board is initialized in asm */ +#if !defined(CONFIG_MOUSSE) && !defined(CONFIG_BMW) + register unsigned long val; + CONFIG_WRITE_HALFWORD(PCICR, 0x06); /* Bus Master, respond to PCI memory space acesses*/ +/* CONFIG_WRITE_HALFWORD(PCISR, 0xffff); */ /*reset PCISR*/ + +#if defined(CONFIG_MUSENKI) || defined(CONFIG_PN62) +/* Why is this here, you ask? Try, just try setting 0x8000 + * in PCIACR with CONFIG_WRITE_HALFWORD() + * this one was a stumper, and we are annoyed + */ + +#define M_CONFIG_WRITE_HALFWORD( addr, data ) \ + __asm__ __volatile__( \ + " \ + stw %2,0(%0)\n \ + sync\n \ + sth %3,2(%1)\n \ + sync\n \ + " \ + : /* no output */ \ + : "r" (CONFIG_ADDR), "r" (CONFIG_DATA), \ + "r" (PCISWAP(addr & ~3)), "r" (PCISWAP(data << 16)) \ + ); + + M_CONFIG_WRITE_HALFWORD(PCIACR, 0x8000); +#endif + + CONFIG_WRITE_BYTE(PCLSR, 0x8); /* set PCI cache line size */ + + /* + * Note that although this bit is cleared after a hard reset, it + * must be explicitly set and then cleared by software during + * initialization in order to guarantee correct operation of the + * DLL and the SDRAM_CLK[0:3] signals (if they are used). + */ + CONFIG_READ_BYTE (AMBOR, val); + CONFIG_WRITE_BYTE(AMBOR, val & 0xDF); + CONFIG_WRITE_BYTE(AMBOR, val | 0x20); + CONFIG_WRITE_BYTE(AMBOR, val & 0xDF); + + CONFIG_READ_WORD(PICR1, val); +#if defined(CONFIG_MPC8240) + CONFIG_WRITE_WORD( PICR1, + (val & (PICR1_ADDRESS_MAP | PICR1_RCS0)) | + PIRC1_MSK | PICR1_PROC_TYPE_603E | + PICR1_FLASH_WR_EN | PICR1_MCP_EN | + PICR1_CF_DPARK | PICR1_EN_PCS | + PICR1_CF_APARK ); +#elif defined(CONFIG_MPC8245) + CONFIG_WRITE_WORD( PICR1, + (val & (PICR1_RCS0)) | + PICR1_PROC_TYPE_603E | + PICR1_FLASH_WR_EN | PICR1_MCP_EN | + PICR1_CF_DPARK | PICR1_NO_BUSW_CK | + PICR1_DEC| PICR1_CF_APARK | 0x10); /* 8245 UM says bit 4 must be set */ +#else +#error Specific type of MPC824x must be defined (i.e. CONFIG_MPC8240) +#endif + + CONFIG_READ_WORD(PICR2, val); + val= val & ~ (PICR2_CF_SNOOP_WS_MASK | PICR2_CF_APHASE_WS_MASK); /*mask off waitstate bits*/ +#ifndef CONFIG_PN62 + val |= PICR2_CF_SNOOP_WS_1WS | PICR2_CF_APHASE_WS_1WS; /*1 wait state*/ +#endif + CONFIG_WRITE_WORD(PICR2, val); + + CONFIG_WRITE_WORD(EUMBBAR, CFG_EUMB_ADDR); +#ifndef CFG_RAMBOOT + CONFIG_WRITE_WORD(MCCR1, (CFG_ROMNAL << MCCR1_ROMNAL_SHIFT) | + (CFG_BANK0_ROW) | + (CFG_BANK1_ROW << MCCR1_BANK1ROW_SHIFT) | + (CFG_BANK2_ROW << MCCR1_BANK2ROW_SHIFT) | + (CFG_BANK3_ROW << MCCR1_BANK3ROW_SHIFT) | + (CFG_BANK4_ROW << MCCR1_BANK4ROW_SHIFT) | + (CFG_BANK5_ROW << MCCR1_BANK5ROW_SHIFT) | + (CFG_BANK6_ROW << MCCR1_BANK6ROW_SHIFT) | + (CFG_BANK7_ROW << MCCR1_BANK7ROW_SHIFT) | + (CFG_ROMFAL << MCCR1_ROMFAL_SHIFT)); +#endif + +#if defined(CFG_ASRISE) && defined(CFG_ASFALL) + CONFIG_WRITE_WORD(MCCR2, CFG_REFINT << MCCR2_REFINT_SHIFT | + CFG_ASRISE << MCCR2_ASRISE_SHIFT | + CFG_ASFALL << MCCR2_ASFALL_SHIFT); +#else + CONFIG_WRITE_WORD(MCCR2, CFG_REFINT << MCCR2_REFINT_SHIFT); +#endif + +#if defined(CONFIG_MPC8240) + CONFIG_WRITE_WORD(MCCR3, + (((CFG_BSTOPRE & 0x003c) >> 2) << MCCR3_BSTOPRE2TO5_SHIFT) | + (CFG_REFREC << MCCR3_REFREC_SHIFT) | + (CFG_RDLAT << MCCR3_RDLAT_SHIFT)); +#elif defined(CONFIG_MPC8245) + CONFIG_WRITE_WORD(MCCR3, + (((CFG_BSTOPRE & 0x003c) >> 2) << MCCR3_BSTOPRE2TO5_SHIFT) | + (CFG_REFREC << MCCR3_REFREC_SHIFT)); +#else +#error Specific type of MPC824x must be defined (i.e. CONFIG_MPC8240) +#endif + +/* this is gross. We think these should all be the same, and various boards + * should define CFG_ACTORW to 0 if they don't want to set it, or even, if + * its not set, we define it to zero in this file + */ +#if defined(CONFIG_CU824) || defined(CONFIG_PN62) + CONFIG_WRITE_WORD(MCCR4, + (CFG_PRETOACT << MCCR4_PRETOACT_SHIFT) | + (CFG_ACTTOPRE << MCCR4_ACTTOPRE_SHIFT) | + MCCR4_BIT21 | + (CFG_REGISTERD_TYPE_BUFFER ? MCCR4_REGISTERED: 0) | + ((CFG_BSTOPRE & 0x0003) <<MCCR4_BSTOPRE0TO1_SHIFT ) | + (((CFG_SDMODE_CAS_LAT <<4) | (CFG_SDMODE_WRAP <<3) | + CFG_SDMODE_BURSTLEN) << MCCR4_SDMODE_SHIFT) | + (CFG_ACTORW << MCCR4_ACTTORW_SHIFT) | + (((CFG_BSTOPRE & 0x03c0) >> 6) << MCCR4_BSTOPRE6TO9_SHIFT)); +#elif defined(CONFIG_MPC8240) + CONFIG_WRITE_WORD(MCCR4, + (CFG_PRETOACT << MCCR4_PRETOACT_SHIFT) | + (CFG_ACTTOPRE << MCCR4_ACTTOPRE_SHIFT) | + MCCR4_BIT21 | + (CFG_REGISTERD_TYPE_BUFFER ? MCCR4_REGISTERED: 0) | + ((CFG_BSTOPRE & 0x0003) <<MCCR4_BSTOPRE0TO1_SHIFT ) | + (((CFG_SDMODE_CAS_LAT <<4) | (CFG_SDMODE_WRAP <<3) | + (CFG_SDMODE_BURSTLEN)) <<MCCR4_SDMODE_SHIFT) | + (((CFG_BSTOPRE & 0x03c0) >> 6) <<MCCR4_BSTOPRE6TO9_SHIFT )); +#elif defined(CONFIG_MPC8245) + CONFIG_READ_WORD(MCCR1, val); + val &= MCCR1_DBUS_SIZE0; /* test for 64-bit mem bus */ + + CONFIG_WRITE_WORD(MCCR4, + (CFG_PRETOACT << MCCR4_PRETOACT_SHIFT) | + (CFG_ACTTOPRE << MCCR4_ACTTOPRE_SHIFT) | + (CFG_EXTROM ? MCCR4_EXTROM : 0) | + (CFG_REGDIMM ? MCCR4_REGDIMM : 0) | + (CFG_REGISTERD_TYPE_BUFFER ? MCCR4_REGISTERED: 0) | + ((CFG_BSTOPRE & 0x0003) <<MCCR4_BSTOPRE0TO1_SHIFT ) | + (CFG_DBUS_SIZE2 << MCCR4_DBUS_SIZE2_SHIFT) | + (((CFG_SDMODE_CAS_LAT <<4) | (CFG_SDMODE_WRAP <<3) | + (val ? 2 : 3)) << MCCR4_SDMODE_SHIFT) | + (CFG_ACTORW << MCCR4_ACTTORW_SHIFT) | + (((CFG_BSTOPRE & 0x03c0) >> 6) <<MCCR4_BSTOPRE6TO9_SHIFT )); +#else +#error Specific type of MPC824x must be defined (i.e. CONFIG_MPC8240) +#endif + + CONFIG_WRITE_WORD(MSAR1, + ( (CFG_BANK0_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CFG_BANK1_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CFG_BANK2_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CFG_BANK3_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMSAR1, + ( (CFG_BANK0_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CFG_BANK1_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CFG_BANK2_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CFG_BANK3_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(MSAR2, + ( (CFG_BANK4_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CFG_BANK5_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CFG_BANK6_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CFG_BANK7_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMSAR2, + ( (CFG_BANK4_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CFG_BANK5_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CFG_BANK6_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CFG_BANK7_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(MEAR1, + ( (CFG_BANK0_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CFG_BANK1_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CFG_BANK2_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CFG_BANK3_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMEAR1, + ( (CFG_BANK0_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CFG_BANK1_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CFG_BANK2_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CFG_BANK3_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(MEAR2, + ( (CFG_BANK4_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CFG_BANK5_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CFG_BANK6_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CFG_BANK7_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMEAR2, + ( (CFG_BANK4_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CFG_BANK5_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CFG_BANK6_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CFG_BANK7_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + + CONFIG_WRITE_BYTE(ODCR, CFG_ODCR); +#ifdef CFG_DLL_MAX_DELAY + CONFIG_WRITE_BYTE(MIOCR1, CFG_DLL_MAX_DELAY); /* needed to make DLL lock */ +#endif +#if defined(CFG_DLL_EXTEND) && defined(CFG_PCI_HOLD_DEL) + CONFIG_WRITE_BYTE(PMCR2, CFG_DLL_EXTEND | CFG_PCI_HOLD_DEL); +#endif +#if defined(MIOCR2) && defined(CFG_SDRAM_DSCD) + CONFIG_WRITE_BYTE(MIOCR2, CFG_SDRAM_DSCD); /* change memory input */ +#endif /* setup & hold time */ + + CONFIG_WRITE_BYTE(MBER, + CFG_BANK0_ENABLE | + (CFG_BANK1_ENABLE << 1) | + (CFG_BANK2_ENABLE << 2) | + (CFG_BANK3_ENABLE << 3) | + (CFG_BANK4_ENABLE << 4) | + (CFG_BANK5_ENABLE << 5) | + (CFG_BANK6_ENABLE << 6) | + (CFG_BANK7_ENABLE << 7)); + +#ifdef CFG_PGMAX + CONFIG_WRITE_BYTE(MPMR, CFG_PGMAX); +#endif + + /* ! Wait 200us before initialize other registers */ + /*FIXME: write a decent udelay wait */ + __asm__ __volatile__( + " mtctr %0 \n \ + 0: bdnz 0b\n" + : + : "r" (0x10000)); + + CONFIG_READ_WORD(MCCR1, val); + CONFIG_WRITE_WORD(MCCR1, val | MCCR1_MEMGO); /* set memory access going */ + __asm__ __volatile__("eieio"); + +#endif /* !CONFIG_MOUSSE && !CONFIG_BMW */ +} + + +#ifdef CONFIG_MOUSSE +#ifdef INCLUDE_MPC107_REPORT +struct MPC107_s{ + unsigned int iobase; + char desc[120]; +} MPC107Regs[] ={ + {BMC_BASE+0x0, "MPC107 Vendor/Device ID"}, + {BMC_BASE+0x4, "MPC107 PCI Command/Status Register"}, + {BMC_BASE+0x8, "MPC107 Revision"}, + {BMC_BASE+0xC, "MPC107 Cache Line Size"}, + {BMC_BASE+0x10, "MPC107 LMBAR"}, + {BMC_BASE+0x14, "MPC824x PCSR"}, + {BMC_BASE+0xA8, "MPC824x PICR1"}, + {BMC_BASE+0xAC, "MPC824x PICR2"}, + {BMC_BASE+0x46, "MPC824x PACR"}, + {BMC_BASE+0x310, "MPC824x ITWR"}, + {BMC_BASE+0x300, "MPC824x OMBAR"}, + {BMC_BASE+0x308, "MPC824x OTWR"}, + {BMC_BASE+0x14, "MPC107 Peripheral Control and Status Register"}, + {BMC_BASE+0x78, "MPC107 EUMBAR"}, + {BMC_BASE+0xC0, "MPC107 Processor Bus Error Status"}, + {BMC_BASE+0xC4, "MPC107 PCI Bus Error Status"}, + {BMC_BASE+0xC8, "MPC107 Processor/PCI Error Address"}, + {BMC_BASE+0xE0, "MPC107 AMBOR Register"}, + {BMC_BASE+0xF0, "MPC107 MCCR1 Register"}, + {BMC_BASE+0xF4, "MPC107 MCCR2 Register"}, + {BMC_BASE+0xF8, "MPC107 MCCR3 Register"}, + {BMC_BASE+0xFC, "MPC107 MCCR4 Register"} +}; +#define N_MPC107_Regs (sizeof(MPC107Regs)/sizeof(MPC107Regs[0])) +#endif /* INCLUDE_MPC107_REPORT */ +#endif /* CONFIG_MOUSSE */ + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ +#ifdef CONFIG_MOUSSE +#ifdef INCLUDE_MPC107_REPORT + unsigned int tmp = 0, i; +#endif + /* + * Initialize the EUMBBAR (Embedded Util Mem Block Base Addr Reg). + * This is necessary before the EPIC, DMA ctlr, I2C ctlr, etc. can + * be accessed. + */ + +#ifdef CONFIG_MPC8240 /* only on MPC8240 */ + mpc824x_mpc107_setreg (EUMBBAR, EUMBBAR_VAL); + /* MOT/SPS: Issue #10002, PCI (FD Alias enable) */ + mpc824x_mpc107_setreg (AMBOR, 0x000000C0); +#endif + + +#ifdef INCLUDE_MPC107_REPORT + /* Check MPC824x PCI Device and Vendor ID */ + while ((tmp = mpc824x_mpc107_getreg (BMC_BASE)) != 0x31057) { + printf (" MPC107: offset=0x%x, val = 0x%x\n", + BMC_BASE, + tmp); + } + + for (i = 0; i < N_MPC107_Regs; i++) { + printf (" 0x%x/%s = 0x%x\n", + MPC107Regs[i].iobase, + MPC107Regs[i].desc, + mpc824x_mpc107_getreg (MPC107Regs[i].iobase)); + } + + printf ("IBAT0L = 0x%08X\n", mfspr (IBAT0L)); + printf ("IBAT0U = 0x%08X\n", mfspr (IBAT0U)); + printf ("IBAT1L = 0x%08X\n", mfspr (IBAT1L)); + printf ("IBAT1U = 0x%08X\n", mfspr (IBAT1U)); + printf ("IBAT2L = 0x%08X\n", mfspr (IBAT2L)); + printf ("IBAT2U = 0x%08X\n", mfspr (IBAT2U)); + printf ("IBAT3L = 0x%08X\n", mfspr (IBAT3L)); + printf ("IBAT3U = 0x%08X\n", mfspr (IBAT3U)); + printf ("DBAT0L = 0x%08X\n", mfspr (DBAT0L)); + printf ("DBAT0U = 0x%08X\n", mfspr (DBAT0U)); + printf ("DBAT1L = 0x%08X\n", mfspr (DBAT1L)); + printf ("DBAT1U = 0x%08X\n", mfspr (DBAT1U)); + printf ("DBAT2L = 0x%08X\n", mfspr (DBAT2L)); + printf ("DBAT2U = 0x%08X\n", mfspr (DBAT2U)); + printf ("DBAT3L = 0x%08X\n", mfspr (DBAT3L)); + printf ("DBAT3U = 0x%08X\n", mfspr (DBAT3U)); +#endif /* INCLUDE_MPC107_REPORT */ +#endif /* CONFIG_MOUSSE */ + return (0); +} diff --git a/cpu/mpc824x/drivers/i2c/i2c1.c b/cpu/mpc824x/drivers/i2c/i2c1.c new file mode 100644 index 00000000000..be6ec60a149 --- /dev/null +++ b/cpu/mpc824x/drivers/i2c/i2c1.c @@ -0,0 +1,1228 @@ +/************************************************************* + * + * Copyright @ Motorola, 1999 + * + ************************************************************/ +#include <common.h> + +#ifdef CONFIG_HARD_I2C +#include <i2c.h> +#include "i2c_export.h" +#include "i2c.h" + +#undef I2CDBG0 +#undef DEBUG + +/* Define a macro to use an optional application-layer print function, if + * one was passed to the I2C library during initialization. If there was + * no function pointer passed, this protects against calling it. Also define + * the global variable that holds the passed pointer. + */ +#define TIMEOUT (CFG_HZ/4) +#define PRINT if ( app_print ) app_print +static int (*app_print) (char *, ...); + +/******************* Internal to I2C Driver *****************/ +static unsigned int ByteToXmit = 0; +static unsigned int XmitByte = 0; +static unsigned char *XmitBuf = 0; +static unsigned int XmitBufEmptyStop = 0; +static unsigned int ByteToRcv = 0; +static unsigned int RcvByte = 0; +static unsigned char *RcvBuf = 0; +static unsigned int RcvBufFulStop = 0; +static unsigned int MasterRcvAddress = 0; + +/* Set by call to get_eumbbar during I2C_Initialize. + * This could be globally available to the I2C library, but there is + * an advantage to passing it as a parameter: it is already in a register + * and doesn't have to be loaded from memory. Also, that is the way the + * I2C library was already implemented and I don't want to change it without + * a more detailed analysis. + * It is being set as a global variable in I2C_Initialize to hide it from + * the DINK application layer, because it is Kahlua-specific. I think that + * get_eumbbar, load_runtime_reg, and store_runtime_reg should be defined in + * a Kahlua-specific library dealing with the embedded utilities memory block. + * Right now, get_eumbbar is defined in dink32/kahlua.s. The other two are + * defined in dink32/drivers/i2c/i2c2.s. + */ +static unsigned int Global_eumbbar = 0; + +extern unsigned int load_runtime_reg (unsigned int eumbbar, + unsigned int reg); + +extern unsigned int store_runtime_reg (unsigned int eumbbar, + unsigned int reg, unsigned int val); + +/************************** API *****************/ + +/* Application Program Interface (API) are the calls provided by the I2C + * library to upper layer applications (i.e., DINK) to access the Kahlua + * I2C bus interface. The functions and values that are part of this API + * are declared in i2c_export.h. + */ + +/* Initialize I2C unit with the following: + * driver's slave address + * interrupt enabled + * optional pointer to application layer print function + * + * These parameters may be added: + * desired clock rate + * digital filter frequency sampling rate + * + * This function must be called before I2C unit can be used. + */ +I2C_Status I2C_Initialize (unsigned char addr, + I2C_INTERRUPT_MODE en_int, + int (*p) (char *, ...)) +{ + I2CStatus status; + + /* establish the pointer, if there is one, to the application's "printf" */ + app_print = p; + + /* If this is the first call, get the embedded utilities memory block + * base address. I'm not sure what to do about error handling here: + * if a non-zero value is returned, accept it. + */ + if (Global_eumbbar == 0) + Global_eumbbar = get_eumbbar (); + if (Global_eumbbar == 0) { + PRINT ("I2C_Initialize: can't find EUMBBAR\n"); + return I2C_ERROR; + } + + /* validate the I2C address */ + if (addr & 0x80) { + PRINT ("I2C_Initialize, I2C address invalid: %d 0x%x\n", + (unsigned int) addr, (unsigned int) addr); + return I2C_ERROR; + } + + /* Call the internal I2C library function to perform work. + * Accept the default frequency sampling rate (no way to set it currently, + * via I2C_Init) and set the clock frequency to something reasonable. + */ + status = I2C_Init (Global_eumbbar, (unsigned char) 0x31, addr, en_int); + if (status != I2CSUCCESS) { + PRINT ("I2C_Initialize: error in initiation\n"); + return I2C_ERROR; + } + + /* all is well */ + return I2C_SUCCESS; +} + + +/* Perform the given I2C transaction, only MASTER_XMIT and MASTER_RCV + * are implemented. Both are only in polling mode. + * + * en_int controls interrupt/polling mode + * act is the type of transaction + * i2c_addr is the I2C address of the slave device + * data_addr is the address of the data on the slave device + * len is the length of data to send or receive + * buffer is the address of the data buffer + * stop = I2C_NO_STOP, don't signal STOP at end of transaction + * I2C_STOP, signal STOP at end of transaction + * retry is the timeout retry value, currently ignored + * rsta = I2C_NO_RESTART, this is not continuation of existing transaction + * I2C_RESTART, this is a continuation of existing transaction + */ +I2C_Status I2C_do_transaction ( I2C_INTERRUPT_MODE en_int, + I2C_TRANSACTION_MODE act, + unsigned char i2c_addr, + unsigned char data_addr, + int len, + char *buffer, + I2C_STOP_MODE stop, + int retry, I2C_RESTART_MODE rsta) +{ + I2C_Status status; + unsigned char data_addr_buffer[1]; + +#if 1 +/* This is a temporary work-around. The I2C library breaks the protocol + * if it attempts to handle a data transmission in more than one + * transaction, so the data address and the actual data bytes are put + * into a single buffer before sending it to the library internal functions. + * The problem is related to being able to restart a transaction without + * sending the I2C device address or repeating the data address. It may take + * a day or two to sort it all out, so I'll have to get back to it later. + * Look at I2C_Start to see about using some status flags (I'm not sure that + * "stop" and "rsta" are enough to reflect the states, maybe so; but the logic + * in the library is insufficient) to control correct handling of the protocol. + */ + unsigned char dummy_buffer[257]; + + if (act == I2C_MASTER_XMIT) { + int i; + + if (len > 256) + return I2C_ERROR; + for (i = 1; i <= len; i++) + dummy_buffer[i] = buffer[i - 1]; + dummy_buffer[0] = data_addr; + status = I2C_do_buffer (en_int, act, i2c_addr, 1 + len, + dummy_buffer, stop, retry, rsta); + if (status != I2C_SUCCESS) { + PRINT ("I2C_do_transaction: can't perform data transfer\n"); + return I2C_ERROR; + } + return I2C_SUCCESS; + } +#endif /* end of temp work-around */ + + /* validate requested transaction type */ + if ((act != I2C_MASTER_XMIT) && (act != I2C_MASTER_RCV)) { + PRINT ("I2C_do_transaction, invalid transaction request: %d\n", + act); + return I2C_ERROR; + } + + /* range check the I2C address */ + if (i2c_addr & 0x80) { + PRINT ("I2C_do_transaction, I2C address out of range: %d 0x%x\n", + (unsigned int) i2c_addr, (unsigned int) i2c_addr); + return I2C_ERROR; + } else { + data_addr_buffer[0] = data_addr; + } + + /* + * We first have to contact the slave device and transmit the + * data address. Be careful about the STOP and restart stuff. + * We don't want to signal STOP after sending the data + * address, but this could be a continuation if the + * application didn't release the bus after the previous + * transaction, by not sending a STOP after it. + */ + status = I2C_do_buffer (en_int, I2C_MASTER_XMIT, i2c_addr, 1, + data_addr_buffer, I2C_NO_STOP, retry, rsta); + if (status != I2C_SUCCESS) { + PRINT ("I2C_do_transaction: can't send data address for read\n"); + return I2C_ERROR; + } + + /* The data transfer will be a continuation. */ + rsta = I2C_RESTART; + + /* now handle the user data */ + status = I2C_do_buffer (en_int, act, i2c_addr, len, + buffer, stop, retry, rsta); + if (status != I2C_SUCCESS) { + PRINT ("I2C_do_transaction: can't perform data transfer\n"); + return I2C_ERROR; + } + + /* all is well */ + return I2C_SUCCESS; +} + +/* This function performs the work for I2C_do_transaction. The work is + * split into this function to enable I2C_do_transaction to first transmit + * the data address to the I2C slave device without putting the data address + * into the first byte of the buffer. + * + * en_int controls interrupt/polling mode + * act is the type of transaction + * i2c_addr is the I2C address of the slave device + * len is the length of data to send or receive + * buffer is the address of the data buffer + * stop = I2C_NO_STOP, don't signal STOP at end of transaction + * I2C_STOP, signal STOP at end of transaction + * retry is the timeout retry value, currently ignored + * rsta = I2C_NO_RESTART, this is not continuation of existing transaction + * I2C_RESTART, this is a continuation of existing transaction + */ +static I2C_Status I2C_do_buffer (I2C_INTERRUPT_MODE en_int, + I2C_TRANSACTION_MODE act, + unsigned char i2c_addr, + int len, + unsigned char *buffer, + I2C_STOP_MODE stop, + int retry, I2C_RESTART_MODE rsta) +{ + I2CStatus rval; + unsigned int dev_stat; + + if (act == I2C_MASTER_RCV) { + /* set up for master-receive transaction */ + rval = I2C_get (Global_eumbbar, i2c_addr, buffer, len, stop, rsta); + } else { + /* set up for master-transmit transaction */ + rval = I2C_put (Global_eumbbar, i2c_addr, buffer, len, stop, rsta); + } + + /* validate the setup */ + if (rval != I2CSUCCESS) { + dev_stat = load_runtime_reg (Global_eumbbar, I2CSR); + PRINT ("Error(I2C_do_buffer): control phase, code(0x%08x), status(0x%08x)\n", rval, dev_stat); + I2C_Stop (Global_eumbbar); + return I2C_ERROR; + } + + if (en_int == 1) { + /* this should not happen, no interrupt handling yet */ + return I2C_SUCCESS; + } + + /* this performs the polling action, when the transfer is completed, + * the status returned from I2C_Timer_Event will be I2CBUFFFULL or + * I2CBUFFEMPTY (rcv or xmit), I2CSUCCESS or I2CADDRESS indicates the + * transaction is not yet complete, anything else is an error. + */ + while (rval == I2CSUCCESS || rval == I2CADDRESS) { + int timeval = get_timer (0); + + /* poll the device until something happens */ + do { + rval = I2C_Timer_Event (Global_eumbbar, 0); + } + while (rval == I2CNOEVENT && get_timer (timeval) < TIMEOUT); + + /* check for error condition */ + if (rval == I2CSUCCESS || + rval == I2CBUFFFULL || + rval == I2CBUFFEMPTY || + rval == I2CADDRESS) { + ; /* do nothing */ + } else { + /* report the error condition */ + dev_stat = load_runtime_reg (Global_eumbbar, I2CSR); + PRINT ("Error(I2C_do_buffer): code(0x%08x), status(0x%08x)\n", + rval, dev_stat); + return I2C_ERROR; + } + } + + /* all is well */ + return I2C_SUCCESS; +} + +/** + * Note: + * + * In all following functions, + * the caller shall pass the configured embedded utility memory + * block base, EUMBBAR. + **/ + +/*********************************************************** + * function: I2C_put + * + * description: + Send a buffer of data to the intended rcv_addr. + * If stop_flag is set, after the whole buffer + * is sent, generate a STOP signal provided that the + * receiver doesn't signal the STOP in the middle. + * I2C is the master performing transmitting. If + * no STOP signal is generated at the end of current + * transaction, the master can generate a START signal + * to another slave addr. + * + * note: this is master xmit API + *********************************************************/ +static I2CStatus I2C_put (unsigned int eumbbar, unsigned char rcv_addr, /* receiver's address */ + unsigned char *buffer_ptr, /* pointer of data to be sent */ + unsigned int length, /* number of byte of in the buffer */ + unsigned int stop_flag, /* 1 - signal STOP when buffer is empty + * 0 - no STOP signal when buffer is empty + */ + unsigned int is_cnt) +{ /* 1 - this is a restart, don't check MBB + * 0 - this is a new start, check MBB + */ + if (buffer_ptr == 0 || length == 0) { + return I2CERROR; + } +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_put\n", __FILE__, __LINE__); +#endif + + XmitByte = 0; + ByteToXmit = length; + XmitBuf = buffer_ptr; + XmitBufEmptyStop = stop_flag; + + RcvByte = 0; + ByteToRcv = 0; + RcvBuf = 0; + + /* we are the master, start transaction */ + return I2C_Start (eumbbar, rcv_addr, XMIT, is_cnt); +} + +/*********************************************************** + * function: I2C_get + * + * description: + * Receive a buffer of data from the desired sender_addr + * If stop_flag is set, when the buffer is full and the + * sender does not signal STOP, generate a STOP signal. + * I2C is the master performing receiving. If no STOP signal + * is generated, the master can generate a START signal + * to another slave addr. + * + * note: this is master receive API + **********************************************************/ +static I2CStatus I2C_get (unsigned int eumbbar, unsigned char rcv_from, /* sender's address */ + unsigned char *buffer_ptr, /* pointer of receiving buffer */ + unsigned int length, /* length of the receiving buffer */ + unsigned int stop_flag, /* 1 - signal STOP when buffer is full + * 0 - no STOP signal when buffer is full + */ + unsigned int is_cnt) +{ /* 1 - this is a restart, don't check MBB + * 0 - this is a new start, check MBB + */ + if (buffer_ptr == 0 || length == 0) { + return I2CERROR; + } +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_get\n", __FILE__, __LINE__); +#endif + + RcvByte = 0; + ByteToRcv = length; + RcvBuf = buffer_ptr; + RcvBufFulStop = stop_flag; + + XmitByte = 0; + ByteToXmit = 0; + XmitBuf = 0; + + /* we are the master, start the transaction */ + return I2C_Start (eumbbar, rcv_from, RCV, is_cnt); + +} + +#if 0 /* turn off dead code */ +/********************************************************* + * function: I2C_write + * + * description: + * Send a buffer of data to the requiring master. + * If stop_flag is set, after the whole buffer is sent, + * generate a STOP signal provided that the requiring + * receiver doesn't signal the STOP in the middle. + * I2C is the slave performing transmitting. + * + * Note: this is slave xmit API. + * + * due to the current Kahlua design, slave transmitter + * shall not signal STOP since there is no way + * for master to detect it, causing I2C bus hung. + * + * For the above reason, the stop_flag is always + * set, i.e., 0. + * + * programmer shall use the timer on Kahlua to + * control the interval of data byte at the + * master side. + *******************************************************/ +static I2CStatus I2C_write (unsigned int eumbbar, unsigned char *buffer_ptr, /* pointer of data to be sent */ + unsigned int length, /* number of byte of in the buffer */ + unsigned int stop_flag) +{ /* 1 - signal STOP when buffer is empty + * 0 - no STOP signal when buffer is empty + */ + if (buffer_ptr == 0 || length == 0) { + return I2CERROR; + } + + XmitByte = 0; + ByteToXmit = length; + XmitBuf = buffer_ptr; + XmitBufEmptyStop = 0; /* in order to avoid bus hung, ignored the user's stop_flag */ + + RcvByte = 0; + ByteToRcv = 0; + RcvBuf = 0; + + /* we are the slave, just wait for being called, or pull */ + /* I2C_Timer_Event( eumbbar ); */ +} + +/****************************************************** + * function: I2C_read + * + * description: + * Receive a buffer of data from the sending master. + * If stop_flag is set, when the buffer is full and the + * sender does not signal STOP, generate a STOP signal. + * I2C is the slave performing receiving. + * + * note: this is slave receive API + ****************************************************/ +static I2CStatus I2C_read (unsigned int eumbbar, unsigned char *buffer_ptr, /* pointer of receiving buffer */ + unsigned int length, /* length of the receiving buffer */ + unsigned int stop_flag) +{ /* 1 - signal STOP when buffer is full + * 0 - no STOP signal when buffer is full + */ + if (buffer_ptr == 0 || length == 0) { + return I2CERROR; + } + + RcvByte = 0; + ByteToRcv = length; + RcvBuf = buffer_ptr; + RcvBufFulStop = stop_flag; + + XmitByte = 0; + ByteToXmit = 0; + XmitBuf = 0; + + /* wait for master to call us, or poll */ + /* I2C_Timer_Event( eumbbar ); */ +} +#endif /* turn off dead code */ + +/********************************************************* + * function: I2c_Timer_Event + * + * description: + * if interrupt is not used, this is the timer event handler. + * After each fixed time interval, this function can be called + * to check the I2C status and call appropriate function to + * handle the status event. + ********************************************************/ +static I2CStatus I2C_Timer_Event (unsigned int eumbbar, + I2CStatus (*handler) (unsigned int)) +{ + I2C_STAT stat; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Timer_Event\n", __FILE__, __LINE__); +#endif + + stat = I2C_Get_Stat (eumbbar); + + if (stat.mif == 1) { + if (handler == 0) { + return I2C_ISR (eumbbar); + } else { + return (*handler) (eumbbar); + } + } + + return I2CNOEVENT; +} + + +/****************** Device I/O function *****************/ + +/****************************************************** + * function: I2C_Start + * + * description: Generate a START signal in the desired mode. + * I2C is the master. + * + * Return I2CSUCCESS if no error. + * + * note: + ****************************************************/ +static I2CStatus I2C_Start (unsigned int eumbbar, unsigned char slave_addr, /* address of the receiver */ + I2C_MODE mode, /* XMIT(1) - put (write) + * RCV(0) - get (read) + */ + unsigned int is_cnt) +{ /* 1 - this is a restart, don't check MBB + * 0 - this is a new start + */ + unsigned int tmp = 0; + I2C_STAT stat; + I2C_CTRL ctrl; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Start addr 0x%x mode %d cnt %d\n", __FILE__, + __LINE__, slave_addr, mode, is_cnt); +#endif + + ctrl = I2C_Get_Ctrl (eumbbar); + + /* first make sure I2C has been initialized */ + if (ctrl.men == 0) { + return I2CERROR; + } + + /* next make sure bus is idle */ + stat = I2C_Get_Stat (eumbbar); + + if (is_cnt == 0 && stat.mbb == 1) { + /* sorry, we lost */ + return I2CBUSBUSY; + } else if (is_cnt == 1 && stat.mif == 1 && stat.mal == 0) { + /* sorry, we lost the bus */ + return I2CALOSS; + } + + + /* OK, I2C is enabled and we have the bus */ + + /* prepare to write the slave address */ + ctrl.msta = 1; + ctrl.mtx = 1; + ctrl.txak = 0; + ctrl.rsta = is_cnt; /* set the repeat start bit */ + I2C_Set_Ctrl (eumbbar, ctrl); + + /* write the slave address and xmit/rcv mode bit */ + tmp = load_runtime_reg (eumbbar, I2CDR); + tmp = (tmp & 0xffffff00) | + ((slave_addr & 0x007f) << 1) | + (mode == XMIT ? 0x0 : 0x1); + store_runtime_reg (eumbbar, I2CDR, tmp); + + if (mode == RCV) { + MasterRcvAddress = 1; + } else { + MasterRcvAddress = 0; + } + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Start exit\n", __FILE__, __LINE__); +#endif + + /* wait for the interrupt or poll */ + return I2CSUCCESS; +} + +/*********************************************************** + * function: I2c_Stop + * + * description: Generate a STOP signal to terminate the master + * transaction. + * return I2CSUCCESS + * + **********************************************************/ +static I2CStatus I2C_Stop (unsigned int eumbbar) +{ + I2C_CTRL ctrl; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Stop enter\n", __FILE__, __LINE__); +#endif + + ctrl = I2C_Get_Ctrl (eumbbar); + ctrl.msta = 0; + I2C_Set_Ctrl (eumbbar, ctrl); + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Stop exit\n", __FILE__, __LINE__); +#endif + + return I2CSUCCESS; +} + +/**************************************************** + * function: I2C_Master_Xmit + * + * description: Master sends one byte of data to + * slave target + * + * return I2CSUCCESS if the byte transmitted. + * Otherwise no-zero + * + * Note: condition must meet when this function is called: + * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && I2CSR(RXAK) == 0 + * I2CCR(MSTA) == 1 && I2CCR(MTX) == 1 + * + ***************************************************/ +static I2CStatus I2C_Master_Xmit (unsigned int eumbbar) +{ + unsigned int val; + + if (ByteToXmit > 0) { + + if (ByteToXmit == XmitByte) { + /* all xmitted */ + ByteToXmit = 0; + + if (XmitBufEmptyStop == 1) { + I2C_Stop (eumbbar); + } + + return I2CBUFFEMPTY; + + } +#ifdef I2CDBG0 + PRINT ("%s(%d): xmit 0x%02x\n", __FILE__, __LINE__, + *(XmitBuf + XmitByte)); +#endif + + val = *(XmitBuf + XmitByte); + val &= 0x000000ff; + store_runtime_reg (eumbbar, I2CDR, val); + XmitByte++; + + return I2CSUCCESS; + + } + + return I2CBUFFEMPTY; +} + +/*********************************************** + * function: I2C_Master_Rcv + * + * description: master reads one byte data + * from slave source + * + * return I2CSUCCESS if no error + * + * Note: condition must meet when this function is called: + * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && + * I2CCR(MSTA) == 1 && I2CCR(MTX) == 0 + * + ***********************************************/ +static I2CStatus I2C_Master_Rcv (unsigned int eumbbar) +{ + I2C_CTRL ctrl; + unsigned int val; + + if (ByteToRcv > 0) { + + if (ByteToRcv - RcvByte == 2 && RcvBufFulStop == 1) { + /* master requests more than or equal to 2 bytes + * we are reading 2nd to last byte + */ + + /* we need to set I2CCR(TXAK) to generate a STOP */ + ctrl = I2C_Get_Ctrl (eumbbar); + ctrl.txak = 1; + I2C_Set_Ctrl (eumbbar, ctrl); + + /* Kahlua will automatically generate a STOP + * next time a transaction happens + */ + + /* note: the case of master requesting one byte is + * handled in I2C_ISR + */ + } + + /* generat a STOP before reading the last byte */ + if (RcvByte + 1 == ByteToRcv && RcvBufFulStop == 1) { + I2C_Stop (eumbbar); + } + + val = load_runtime_reg (eumbbar, I2CDR); + *(RcvBuf + RcvByte) = val & 0xFF; + +#ifdef I2CDBG0 + PRINT ("%s(%d): rcv 0x%02x\n", __FILE__, __LINE__, + *(RcvBuf + RcvByte)); +#endif + + RcvByte++; + + if (ByteToRcv == RcvByte) { + ByteToRcv = 0; + + return I2CBUFFFULL; + } + + return I2CSUCCESS; + } + + return I2CBUFFFULL; + +} + +/**************************************************** + * function: I2C_Slave_Xmit + * + * description: Slave sends one byte of data to + * requesting destination + * + * return SUCCESS if the byte transmitted. Otherwise + * No-zero + * + * Note: condition must meet when this function is called: + * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && I2CSR(RXAK) = 0 + * I2CCR(MSTA) == 0 && I2CCR(MTX) == 1 + * + ***************************************************/ +static I2CStatus I2C_Slave_Xmit (unsigned int eumbbar) +{ + unsigned int val; + + if (ByteToXmit > 0) { + + if (ByteToXmit == XmitByte) { + /* no more data to send */ + ByteToXmit = 0; + + /* + * do not toggle I2CCR(MTX). Doing so will + * cause bus-hung since current Kahlua design + * does not give master a way to detect slave + * stop. It is always a good idea for master + * to use timer to prevent the long long + * delays + */ + + return I2CBUFFEMPTY; + } +#ifdef I2CDBG + PRINT ("%s(%d): xmit 0x%02x\n", __FILE__, __LINE__, + *(XmitBuf + XmitByte)); +#endif + + val = *(XmitBuf + XmitByte); + val &= 0x000000ff; + store_runtime_reg (eumbbar, I2CDR, val); + XmitByte++; + + return I2CSUCCESS; + } + + return I2CBUFFEMPTY; +} + +/*********************************************** + * function: I2C_Slave_Rcv + * + * description: slave reads one byte data + * from master source + * + * return I2CSUCCESS if no error otherwise non-zero + * + * Note: condition must meet when this function is called: + * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && + * I2CCR(MSTA) == 0 && I2CCR(MTX) = 0 + * + ***********************************************/ +static I2CStatus I2C_Slave_Rcv (unsigned int eumbbar) +{ + unsigned int val; + I2C_CTRL ctrl; + + if (ByteToRcv > 0) { + val = load_runtime_reg (eumbbar, I2CDR); + *(RcvBuf + RcvByte) = val & 0xff; +#ifdef I2CDBG + PRINT ("%s(%d): rcv 0x%02x\n", __FILE__, __LINE__, + *(RcvBuf + RcvByte)); +#endif + RcvByte++; + + if (ByteToRcv == RcvByte) { + if (RcvBufFulStop == 1) { + /* all done */ + ctrl = I2C_Get_Ctrl (eumbbar); + ctrl.txak = 1; + I2C_Set_Ctrl (eumbbar, ctrl); + } + + ByteToRcv = 0; + return I2CBUFFFULL; + } + + return I2CSUCCESS; + } + + return I2CBUFFFULL; +} + +/****************** Device Control Function *************/ + +/********************************************************* + * function: I2C_Init + * + * description: Initialize I2C unit with desired frequency divider, + * master's listening address, with interrupt enabled + * or disabled. + * + * note: + ********************************************************/ +static I2CStatus I2C_Init (unsigned int eumbbar, unsigned char fdr, /* frequency divider */ + unsigned char slave_addr, /* driver's address used for receiving */ + unsigned int en_int) +{ /* 1 - enable I2C interrupt + * 0 - disable I2C interrup + */ + I2C_CTRL ctrl; + unsigned int tmp; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Init enter\n", __FILE__, __LINE__); +#endif + + ctrl = I2C_Get_Ctrl (eumbbar); + /* disable the I2C module before we change everything */ + ctrl.men = 0; + I2C_Set_Ctrl (eumbbar, ctrl); + + /* set the frequency diver */ + tmp = load_runtime_reg (eumbbar, I2CFDR); + tmp = (tmp & 0xffffffc0) | (fdr & 0x3f); + store_runtime_reg (eumbbar, I2CFDR, tmp); + + /* Set our listening (slave) address */ + tmp = load_runtime_reg (eumbbar, I2CADR); + tmp = (tmp & 0xffffff01) | ((slave_addr & 0x7f) << 1); + store_runtime_reg (eumbbar, I2CADR, tmp); + + /* enable I2C with desired interrupt setting */ + ctrl.men = 1; + ctrl.mien = en_int & 0x1; + I2C_Set_Ctrl (eumbbar, ctrl); +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Init exit\n", __FILE__, __LINE__); +#endif + + return I2CSUCCESS; + +} + +/***************************************** + * function I2c_Get_Stat + * + * description: Query I2C Status, i.e., read I2CSR + * + ****************************************/ +static I2C_STAT I2C_Get_Stat (unsigned int eumbbar) +{ + unsigned int temp; + I2C_STAT stat; + + temp = load_runtime_reg (eumbbar, I2CSR); + +#ifdef I2CDBG0 + PRINT ("%s(%d): get stat = 0x%08x\n", __FILE__, __LINE__, temp); +#endif + + stat.rsrv0 = (temp & 0xffffff00) >> 8; + stat.mcf = (temp & 0x00000080) >> 7; + stat.maas = (temp & 0x00000040) >> 6; + stat.mbb = (temp & 0x00000020) >> 5; + stat.mal = (temp & 0x00000010) >> 4; + stat.rsrv1 = (temp & 0x00000008) >> 3; + stat.srw = (temp & 0x00000004) >> 2; + stat.mif = (temp & 0x00000002) >> 1; + stat.rxak = (temp & 0x00000001); + return stat; +} + +/********************************************* + * function: I2c_Set_Ctrl + * + * description: Change I2C Control bits, + * i.e., write to I2CCR + * + ********************************************/ +static void I2C_Set_Ctrl (unsigned int eumbbar, I2C_CTRL ctrl) +{ /* new control value */ + unsigned int temp = load_runtime_reg (eumbbar, I2CCR); + + temp &= 0xffffff03; + temp |= ((ctrl.men & 0x1) << 7); + temp |= ((ctrl.mien & 0x1) << 6); + temp |= ((ctrl.msta & 0x1) << 5); + temp |= ((ctrl.mtx & 0x1) << 4); + temp |= ((ctrl.txak & 0x1) << 3); + temp |= ((ctrl.rsta & 0x1) << 2); +#ifdef I2CDBG0 + PRINT ("%s(%d): set ctrl = 0x%08x\n", __FILE__, __LINE__, temp); +#endif + store_runtime_reg (eumbbar, I2CCR, temp); + +} + +/***************************************** + * function: I2C_Get_Ctrl + * + * description: Query I2C Control bits, + * i.e., read I2CCR + *****************************************/ +static I2C_CTRL I2C_Get_Ctrl (unsigned int eumbbar) +{ + union { + I2C_CTRL ctrl; + unsigned int temp; + } s; + + s.temp = load_runtime_reg (eumbbar, I2CCR); +#ifdef I2CDBG0 + PRINT ("%s(%d): get ctrl = 0x%08x\n", __FILE__, __LINE__, s.temp); +#endif + + return s.ctrl; +} + + +/**************************************** + * function: I2C_Slave_Addr + * + * description: Process slave address phase. + * return I2CSUCCESS if no error + * + * note: Precondition for calling this function: + * I2CSR(MIF) == 1 && + * I2CSR(MAAS) == 1 + ****************************************/ +static I2CStatus I2C_Slave_Addr (unsigned int eumbbar) +{ + I2C_STAT stat = I2C_Get_Stat (eumbbar); + I2C_CTRL ctrl = I2C_Get_Ctrl (eumbbar); + + if (stat.srw == 1) { + /* we are asked to xmit */ + ctrl.mtx = 1; + I2C_Set_Ctrl (eumbbar, ctrl); /* set MTX */ + return I2C_Slave_Xmit (eumbbar); + } + + /* we are asked to receive data */ + ctrl.mtx = 0; + I2C_Set_Ctrl (eumbbar, ctrl); + (void) load_runtime_reg (eumbbar, I2CDR); /* do a fake read to start */ + + return I2CADDRESS; +} + +/*********************************************** + * function: I2C_ISR + * + * description: I2C Interrupt service routine + * + * note: Precondition: + * I2CSR(MIF) == 1 + **********************************************/ +static I2CStatus I2C_ISR (unsigned int eumbbar) +{ + I2C_STAT stat; + I2C_CTRL ctrl; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_ISR\n", __FILE__, __LINE__); +#endif + + stat = I2C_Get_Stat (eumbbar); + ctrl = I2C_Get_Ctrl (eumbbar); + + /* clear MIF */ + stat.mif = 0; + + /* Now let see what kind of event this is */ + if (stat.mcf == 1) { + /* transfer compete */ + + /* clear the MIF bit */ + I2C_Set_Stat (eumbbar, stat); + + if (ctrl.msta == 1) { + /* master */ + if (ctrl.mtx == 1) { + /* check if this is the address phase for master receive */ + if (MasterRcvAddress == 1) { + /* Yes, it is the address phase of master receive */ + ctrl.mtx = 0; + /* now check how much we want to receive */ + if (ByteToRcv == 1 && RcvBufFulStop == 1) { + ctrl.txak = 1; + } + + I2C_Set_Ctrl (eumbbar, ctrl); + (void) load_runtime_reg (eumbbar, I2CDR); /* fake read first */ + + MasterRcvAddress = 0; + return I2CADDRESS; + + } + + /* master xmit */ + if (stat.rxak == 0) { + /* slave has acknowledged */ + return I2C_Master_Xmit (eumbbar); + } + + /* slave has not acknowledged yet, generate a STOP */ + if (XmitBufEmptyStop == 1) { + ctrl.msta = 0; + I2C_Set_Ctrl (eumbbar, ctrl); + } + + return I2CSUCCESS; + } + + /* master receive */ + return I2C_Master_Rcv (eumbbar); + } + + /* slave */ + if (ctrl.mtx == 1) { + /* slave xmit */ + if (stat.rxak == 0) { + /* master has acknowledged */ + return I2C_Slave_Xmit (eumbbar); + } + + /* master has not acknowledged, wait for STOP */ + /* do nothing for preventing bus from hung */ + return I2CSUCCESS; + } + + /* slave rcv */ + return I2C_Slave_Rcv (eumbbar); + + } else if (stat.maas == 1) { + /* received a call from master */ + + /* clear the MIF bit */ + I2C_Set_Stat (eumbbar, stat); + + /* master is calling us, process the address phase */ + return I2C_Slave_Addr (eumbbar); + } else { + /* has to be arbitration lost */ + stat.mal = 0; + I2C_Set_Stat (eumbbar, stat); + + ctrl.msta = 0; /* return to receive mode */ + I2C_Set_Ctrl (eumbbar, ctrl); + } + + return I2CSUCCESS; + +} + +/****************************************************** + * function: I2C_Set_Stat + * + * description: modify the I2CSR + * + *****************************************************/ +static void I2C_Set_Stat (unsigned int eumbbar, I2C_STAT stat) +{ + union { + unsigned int val; + I2C_STAT stat; + } s_tmp; + union { + unsigned int val; + I2C_STAT stat; + } s; + + s.val = load_runtime_reg (eumbbar, I2CSR); + s.val &= 0xffffff08; + s_tmp.stat = stat; + s.val |= (s_tmp.val & 0xf7); + +#ifdef I2CDBG0 + PRINT ("%s(%d): set stat = 0x%08x\n", __FILE__, __LINE__, s.val); +#endif + + store_runtime_reg (eumbbar, I2CSR, s.val); + +} + +/****************************************************** + * The following are routines to glue the rest of + * U-Boot to the Sandpoint I2C driver. + *****************************************************/ + +void i2c_init (int speed, int slaveadd) +{ +#ifdef DEBUG + I2C_Initialize (0x7f, 0, (void *) printf); +#else + I2C_Initialize (0x7f, 0, 0); +#endif +} + +int i2c_probe (uchar chip) +{ + int tmp; + + /* + * Try to read the first location of the chip. The underlying + * driver doesn't appear to support sending just the chip address + * and looking for an <ACK> back. + */ + udelay(10000); + return i2c_read (chip, 0, 1, (char *)&tmp, 1); +} + +int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + I2CStatus status; + uchar xaddr[4]; + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + status = I2C_do_buffer (0, I2C_MASTER_XMIT, chip, alen, + &xaddr[4 - alen], I2C_NO_STOP, 1, + I2C_NO_RESTART); + if (status != I2C_SUCCESS) { + PRINT ("i2c_read: can't send data address for read\n"); + return 1; + } + } + + /* The data transfer will be a continuation. */ + status = I2C_do_buffer (0, I2C_MASTER_RCV, chip, len, + buffer, I2C_STOP, 1, (alen > 0 ? I2C_RESTART : + I2C_NO_RESTART)); + + if (status != I2C_SUCCESS) { + PRINT ("i2c_read: can't perform data transfer\n"); + return 1; + } + + return 0; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + I2CStatus status; + unsigned char dummy_buffer[I2C_RXTX_LEN + 2]; + int i; + + dummy_buffer[0] = addr & 0xFF; + if (alen == 2) + dummy_buffer[1] = (addr >> 8) & 0xFF; + for (i = 0; i < len; i++) + dummy_buffer[i + alen] = buffer[i]; + + status = I2C_do_buffer (0, I2C_MASTER_XMIT, chip, alen + len, + dummy_buffer, I2C_STOP, 1, I2C_NO_RESTART); + +#ifdef CFG_EEPROM_PAGE_WRITE_DELAY_MS + udelay(CFG_EEPROM_PAGE_WRITE_DELAY_MS * 1000); +#endif + if (status != I2C_SUCCESS) { + PRINT ("i2c_write: can't perform data transfer\n"); + return 1; + } + + return 0; +} + +uchar i2c_reg_read (uchar i2c_addr, uchar reg) +{ + char buf[1]; + + i2c_init (0, 0); + + i2c_read (i2c_addr, reg, 1, buf, 1); + + return (buf[0]); +} + +void i2c_reg_write (uchar i2c_addr, uchar reg, uchar val) +{ + i2c_init (0, 0); + + i2c_write (i2c_addr, reg, 1, &val, 1); +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/cpu/mpc824x/pci.c b/cpu/mpc824x/pci.c new file mode 100644 index 00000000000..7e3c4c3b782 --- /dev/null +++ b/cpu/mpc824x/pci.c @@ -0,0 +1,78 @@ +/* + * arch/ppc/kernel/mpc10x_common.c + * + * Common routines for the Motorola SPS MPC106, MPC107 and MPC8240 Host bridge, + * Mem ctlr, EPIC, etc. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * 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. + */ + +#include <common.h> + +#ifdef CONFIG_PCI + +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> +#include <mpc824x.h> + +void pci_mpc824x_init (struct pci_controller *hose) +{ + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* System memory space */ + pci_set_region(hose->regions + 0, + CHRP_PCI_MEMORY_BUS, + CHRP_PCI_MEMORY_PHYS, + CHRP_PCI_MEMORY_SIZE, + PCI_REGION_MEM | PCI_REGION_MEMORY); + + /* PCI memory space */ + pci_set_region(hose->regions + 1, + CHRP_PCI_MEM_BUS, + CHRP_PCI_MEM_PHYS, + CHRP_PCI_MEM_SIZE, + PCI_REGION_MEM); + + /* ISA/PCI memory space */ + pci_set_region(hose->regions + 2, + CHRP_ISA_MEM_BUS, + CHRP_ISA_MEM_PHYS, + CHRP_ISA_MEM_SIZE, + PCI_REGION_MEM); + + /* PCI I/O space */ + pci_set_region(hose->regions + 3, + CHRP_PCI_IO_BUS, + CHRP_PCI_IO_PHYS, + CHRP_PCI_IO_SIZE, + PCI_REGION_IO); + + /* ISA/PCI I/O space */ + pci_set_region(hose->regions + 4, + CHRP_ISA_IO_BUS, + CHRP_ISA_IO_PHYS, + CHRP_ISA_IO_SIZE, + PCI_REGION_IO); + + hose->region_count = 5; + + pci_setup_indirect(hose, + CHRP_REG_ADDR, + CHRP_REG_DATA); + + pci_register_hose(hose); + + hose->last_busno = pci_hose_scan(hose); +} + +#endif diff --git a/cpu/mpc824x/start.S b/cpu/mpc824x/start.S new file mode 100644 index 00000000000..bd9706de7c6 --- /dev/null +++ b/cpu/mpc824x/start.S @@ -0,0 +1,835 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000,2001,2002 Wolfgang Denk <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 + */ + +/* U-Boot - Startup Code for PowerPC based Embedded Boards + * + * + * The processor starts at 0x00000100 and the code is executed + * from flash. The code is organized to be at an other address + * in memory, but as long we don't jump around before relocating. + * board_init lies at a quite high address and when the cpu has + * jumped there, everything is ok. + * This works because the cpu gives the FLASH (CS0) the whole + * address space at startup, and board_init lies as a echo of + * the flash somewhere up there in the memorymap. + * + * board_init will change CS0 to be positioned at the correct + * address and (s)dram will be positioned at address 0 + */ +#include <config.h> +#include <mpc824x.h> +#include <version.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +/* FP, Machine Check and Recoverable Interr. */ +#define MSR_KERNEL ( MSR_FP | MSR_ME | MSR_RI ) + +/* + * Set up GOT: Global Offset Table + * + * Use r14 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(_end) + GOT_ENTRY(.bss) +#if defined(CONFIG_FADS) + GOT_ENTRY(environment) +#endif + END_GOT + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ + .text + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", __DATE__, " - ", __TIME__, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + +boot_cold: +boot_warm: + + /* Initialize machine status; enable machine check interrupt */ + /*----------------------------------------------------------------------*/ + li r3, MSR_KERNEL /* Set FP, ME, RI flags */ + mtmsr r3 + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + addis r0,0,0x0000 /* lets make sure that r0 is really 0 */ + mtspr HID0, r0 /* disable I and D caches */ + + mfspr r3, ICR /* clear Interrupt Cause Register */ + + mfmsr r3 /* turn off address translation */ + addis r4,0,0xffff + ori r4,r4,0xffcf + and r3,r3,r4 + mtmsr r3 + isync + sync /* the MMU should be off... */ + + +in_flash: +#if defined(CONFIG_BMW) + bl early_init_f /* Must be ASM: no stack yet! */ +#endif + /* + * Setup BATs - cannot be done in C since we don't have a stack yet + */ + bl setup_bats + + /* Enable MMU. + */ + mfmsr r3 + ori r3, r3, (MSR_IR | MSR_DR) + mtmsr r3 +#if !defined(CONFIG_BMW) + /* Enable and invalidate data cache. + */ + mfspr r3, HID0 + mr r2, r3 + ori r3, r3, HID0_DCE | HID0_DCI + ori r2, r2, HID0_DCE + sync + mtspr HID0, r3 + mtspr HID0, r2 + sync + + /* Allocate Initial RAM in data cache. + */ + lis r3, CFG_INIT_RAM_ADDR@h + ori r3, r3, CFG_INIT_RAM_ADDR@l + li r2, 128 + mtctr r2 +1: + dcbz r0, r3 + addi r3, r3, 32 + bdnz 1b + + /* Lock way0 in data cache. + */ + mfspr r3, 1011 + lis r2, 0xffff + ori r2, r2, 0xff1f + and r3, r3, r2 + ori r3, r3, 0x0080 + sync + mtspr 1011, r3 +#endif /* !CONFIG_BMW */ + /* + * Thisk the stack pointer *somewhere* sensible. Doesnt + * matter much where as we'll move it when we relocate + */ + lis r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@h + ori r1, r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@l + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*----------------------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (from Flash) */ + + + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(EXC_OFF_MACH_CHCK, MachineCheck, MachineCheckException) + +/* Data Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(EXC_OFF_DATA_STOR, DataStorage, UnknownException) + +/* Instruction Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(EXC_OFF_INS_STOR, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(EXC_OFF_EXTERNAL, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = EXC_OFF_ALIGN +Alignment: + EXCEPTION_PROLOG + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + lwz r6,GOT(transfer_to_handler) + mtlr r6 + blrl +.L_Alignment: + .long AlignmentException - _start + EXC_OFF_SYS_RESET + .long int_return - _start + EXC_OFF_SYS_RESET + +/* Program check exception */ + . = EXC_OFF_PROGRAM +ProgramCheck: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + lwz r6,GOT(transfer_to_handler) + mtlr r6 + blrl +.L_ProgramCheck: + .long ProgramCheckException - _start + EXC_OFF_SYS_RESET + .long int_return - _start + EXC_OFF_SYS_RESET + + /* No FPU on MPC8xx. This exception is not supposed to happen. + */ + STD_EXCEPTION(EXC_OFF_FPUNAVAIL, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(EXC_OFF_DECR, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + + . = 0xc00 +/* + * r0 - SYSCALL number + * r3-... arguments + */ +SystemCall: + addis r11,r0,0 /* get functions table addr */ + ori r11,r11,0 /* Note: this code is patched in trap_init */ + addis r12,r0,0 /* get number of functions */ + ori r12,r12,0 + + cmplw 0, r0, r12 + bge 1f + + rlwinm r0,r0,2,0,31 /* fn_addr = fn_tbl[r0] */ + add r11,r11,r0 + lwz r11,0(r11) + + li r12,0xd00-4*3 /* save LR & SRRx */ + mflr r0 + stw r0,0(r12) + mfspr r0,SRR0 + stw r0,4(r12) + mfspr r0,SRR1 + stw r0,8(r12) + + li r12,0xc00+_back-SystemCall + mtlr r12 + mtspr SRR0,r11 + +1: SYNC + rfi + +_back: + + mfmsr r11 /* Disable interrupts */ + li r12,0 + ori r12,r12,MSR_EE + andc r11,r11,r12 + SYNC /* Some chip revs need this... */ + mtmsr r11 + SYNC + + li r12,0xd00-4*3 /* restore regs */ + lwz r11,0(r12) + mtlr r11 + lwz r11,4(r12) + mtspr SRR0,r11 + lwz r11,8(r12) + mtspr SRR1,r11 + + SYNC + rfi + + STD_EXCEPTION(EXC_OFF_TRACE, SingleStep, UnknownException) + + STD_EXCEPTION(EXC_OFF_FPUNASSIST, Trap_0e, UnknownException) + STD_EXCEPTION(EXC_OFF_PMI, Trap_0f, UnknownException) + + STD_EXCEPTION(EXC_OFF_ITME, InstructionTransMiss, UnknownException) + STD_EXCEPTION(EXC_OFF_DLTME, DataLoadTransMiss, UnknownException) + STD_EXCEPTION(EXC_OFF_DSTME, DataStoreTransMiss, UnknownException) + STD_EXCEPTION(EXC_OFF_IABE, InstructionBreakpoint, UnknownException) + STD_EXCEPTION(EXC_OFF_SMIE, SysManageInt, UnknownException) + STD_EXCEPTION(0x1500, Reserved5, UnknownException) + STD_EXCEPTION(0x1600, Reserved6, UnknownException) + STD_EXCEPTION(0x1700, Reserved7, UnknownException) + STD_EXCEPTION(0x1800, Reserved8, UnknownException) + STD_EXCEPTION(0x1900, Reserved9, UnknownException) + STD_EXCEPTION(0x1a00, ReservedA, UnknownException) + STD_EXCEPTION(0x1b00, ReservedB, UnknownException) + STD_EXCEPTION(0x1c00, ReservedC, UnknownException) + STD_EXCEPTION(0x1d00, ReservedD, UnknownException) + STD_EXCEPTION(0x1e00, ReservedE, UnknownException) + STD_EXCEPTION(0x1f00, ReservedF, UnknownException) + + STD_EXCEPTION(EXC_OFF_RMTE, RunModeTrace, UnknownException) + + .globl _end_of_vectors +_end_of_vectors: + + + . = 0x3000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) +#if 0 + andi. r23,r23,MSR_PR + mfspr r23,SPRG3 /* if from user, fix up tss.regs */ + beq 2f + addi r24,r1,STACK_FRAME_OVERHEAD + stw r24,PT_REGS(r23) +2: addi r2,r23,-TSS /* set r2 to current */ + tovirt(r2,r2,r23) +#endif + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ +#if 0 + addi r24,r2,TASK_STRUCT_SIZE /* check for kernel stack overflow */ + cmplw 0,r1,r2 + cmplw 1,r1,r24 + crand 1,1,4 + bgt stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ +#endif + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + ori r20,r20,0x30 /* enable IR, DR */ + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* Cache functions. +*/ + .globl icache_enable +icache_enable: + mfspr r5,HID0 /* turn on the I cache. */ + ori r5,r5,0x8800 /* Instruction cache only! */ + addis r6,0,0xFFFF + ori r6,r6,0xF7FF + and r6,r5,r6 /* clear the invalidate bit */ + sync + mtspr HID0,r5 + mtspr HID0,r6 + isync + sync + blr + + .globl icache_disable +icache_disable: + mfspr r5,HID0 + addis r6,0,0xFFFF + ori r6,r6,0x7FFF + and r5,r5,r6 + sync + mtspr HID0,r5 + isync + sync + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + srwi r3, r3, 15 /* >>15 & 1=> select bit 16 */ + andi. r3, r3, 1 + blr + + .globl dcache_enable +dcache_enable: + mfspr r5,HID0 /* turn on the D cache. */ + ori r5,r5,0x4400 /* Data cache only! */ + mfspr r4, PVR /* read PVR */ + srawi r3, r4, 16 /* shift off the least 16 bits */ + cmpi 0, 0, r3, 0xC /* Check for Max pvr */ + bne NotMax + ori r5,r5,0x0040 /* setting the DCFA bit, for Max rev 1 errata */ +NotMax: + addis r6,0,0xFFFF + ori r6,r6,0xFBFF + and r6,r5,r6 /* clear the invalidate bit */ + sync + mtspr HID0,r5 + mtspr HID0,r6 + isync + sync + blr + + .globl dcache_disable +dcache_disable: + mfspr r5,HID0 + addis r6,0,0xFFFF + ori r6,r6,0xBFFF + and r5,r5,r6 + sync + mtspr HID0,r5 + isync + sync + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + srwi r3, r3, 14 /* >>14 & 1=> select bit 17 */ + andi. r3, r3, 1 + blr + + .globl dc_read +dc_read: +/*TODO : who uses this, what should it do? +*/ + blr + + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + mr r3, r5 /* Destination Address */ +#ifdef DEBUG + lis r4, CFG_SDRAM_BASE@h /* Source Address */ + ori r4, r4, CFG_SDRAM_BASE@l +#else + lis r4, CFG_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CFG_MONITOR_BASE@l +#endif + lis r5, CFG_MONITOR_LEN@h /* Length in Bytes */ + ori r5, r5, CFG_MONITOR_LEN@l + li r6, CFG_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r14, r14, r15 + /* the the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r14 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + add r0,r0,r11 + stw r0,0(r3) + bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ +2: li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(.bss) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + blt 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* Problems accessing "end" in C, so do it here */ + .globl get_endaddr +get_endaddr: + lwz r3,GOT(_end) + blr + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + rlwinm r9, r7, 0, 18, 31 /* _start & 0x3FFF */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ + + mflr r4 /* save link register */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mtlr r4 /* restore link register */ + blr + + /* + * Function: relocate entries for one exception vector + */ +trap_reloc: + lwz r0, 0(r7) /* hdlr ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 0(r7) + + lwz r0, 4(r7) /* int_return ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 4(r7) + + blr + + /* Setup the BAT registers. + */ +setup_bats: + lis r4, CFG_IBAT0L@h + ori r4, r4, CFG_IBAT0L@l + lis r3, CFG_IBAT0U@h + ori r3, r3, CFG_IBAT0U@l + mtspr IBAT0L, r4 + mtspr IBAT0U, r3 + isync + + lis r4, CFG_DBAT0L@h + ori r4, r4, CFG_DBAT0L@l + lis r3, CFG_DBAT0U@h + ori r3, r3, CFG_DBAT0U@l + mtspr DBAT0L, r4 + mtspr DBAT0U, r3 + isync + + lis r4, CFG_IBAT1L@h + ori r4, r4, CFG_IBAT1L@l + lis r3, CFG_IBAT1U@h + ori r3, r3, CFG_IBAT1U@l + mtspr IBAT1L, r4 + mtspr IBAT1U, r3 + isync + + lis r4, CFG_DBAT1L@h + ori r4, r4, CFG_DBAT1L@l + lis r3, CFG_DBAT1U@h + ori r3, r3, CFG_DBAT1U@l + mtspr DBAT1L, r4 + mtspr DBAT1U, r3 + isync + + lis r4, CFG_IBAT2L@h + ori r4, r4, CFG_IBAT2L@l + lis r3, CFG_IBAT2U@h + ori r3, r3, CFG_IBAT2U@l + mtspr IBAT2L, r4 + mtspr IBAT2U, r3 + isync + + lis r4, CFG_DBAT2L@h + ori r4, r4, CFG_DBAT2L@l + lis r3, CFG_DBAT2U@h + ori r3, r3, CFG_DBAT2U@l + mtspr DBAT2L, r4 + mtspr DBAT2U, r3 + isync + + lis r4, CFG_IBAT3L@h + ori r4, r4, CFG_IBAT3L@l + lis r3, CFG_IBAT3U@h + ori r3, r3, CFG_IBAT3U@l + mtspr IBAT3L, r4 + mtspr IBAT3U, r3 + isync + + lis r4, CFG_DBAT3L@h + ori r4, r4, CFG_DBAT3L@l + lis r3, CFG_DBAT3U@h + ori r3, r3, CFG_DBAT3U@l + mtspr DBAT3L, r4 + mtspr DBAT3U, r3 + isync + + /* Invalidate TLBs. + * -> for (val = 0; val < 0x20000; val+=0x1000) + * -> tlbie(val); + */ + lis r3, 0 + lis r5, 2 + +1: + tlbie r3 + addi r3, r3, 0x1000 + cmp 0, 0, r3, r5 + blt 1b + + blr + + |