diff options
author | Markus Klotzbuecher <mk@denx.de> | 2007-08-07 22:30:29 +0200 |
---|---|---|
committer | Markus Klotzbuecher <mk@pollux.denx.de> | 2007-08-07 22:30:29 +0200 |
commit | 78549bbf44bd2c8d1a0730fb068836071751afaa (patch) | |
tree | 92f002dc9772874bc3c884b1caa5607763c2c276 /cpu/mpc512x | |
parent | 9b7464a2c88614e1061f509c48930a3d240d1a35 (diff) | |
parent | b23b547597ff2375ad13a9ab04e5257a3ad76c99 (diff) | |
download | u-boot-imx-78549bbf44bd2c8d1a0730fb068836071751afaa.zip u-boot-imx-78549bbf44bd2c8d1a0730fb068836071751afaa.tar.gz u-boot-imx-78549bbf44bd2c8d1a0730fb068836071751afaa.tar.bz2 |
Merge with git://www.denx.de/git/u-boot.git
Diffstat (limited to 'cpu/mpc512x')
-rw-r--r-- | cpu/mpc512x/Makefile | 46 | ||||
-rw-r--r-- | cpu/mpc512x/config.mk | 25 | ||||
-rw-r--r-- | cpu/mpc512x/cpu.c | 127 | ||||
-rw-r--r-- | cpu/mpc512x/cpu_init.c | 87 | ||||
-rw-r--r-- | cpu/mpc512x/fec.c | 801 | ||||
-rw-r--r-- | cpu/mpc512x/fec.h | 224 | ||||
-rw-r--r-- | cpu/mpc512x/i2c.c | 431 | ||||
-rw-r--r-- | cpu/mpc512x/interrupts.c | 61 | ||||
-rw-r--r-- | cpu/mpc512x/serial.c | 197 | ||||
-rw-r--r-- | cpu/mpc512x/speed.c | 135 | ||||
-rw-r--r-- | cpu/mpc512x/start.S | 780 | ||||
-rw-r--r-- | cpu/mpc512x/traps.c | 205 |
12 files changed, 3119 insertions, 0 deletions
diff --git a/cpu/mpc512x/Makefile b/cpu/mpc512x/Makefile new file mode 100644 index 0000000..2be35b2 --- /dev/null +++ b/cpu/mpc512x/Makefile @@ -0,0 +1,46 @@ +# +# (C) Copyright 2007 DENX Software Engineering +# +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o +COBJS = traps.o cpu.o cpu_init.o speed.o interrupts.o serial.o fec.o i2c.o + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/cpu/mpc512x/config.mk b/cpu/mpc512x/config.mk new file mode 100644 index 0000000..8a07c5a --- /dev/null +++ b/cpu/mpc512x/config.mk @@ -0,0 +1,25 @@ +# +# (C) Copyright 2007 DENX Software Engineering +# +# 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 +# +PLATFORM_RELFLAGS += -fPIC -ffixed-r14 -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_MPC512X -DCONFIG_E300 \ + -ffixed-r2 -ffixed-r29 -msoft-float -mcpu=603e diff --git a/cpu/mpc512x/cpu.c b/cpu/mpc512x/cpu.c new file mode 100644 index 0000000..3be565a --- /dev/null +++ b/cpu/mpc512x/cpu.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2004-2006 Freescale Semiconductor, Inc. + * (C) Copyright 2007 DENX Software Engineering + * + * 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 MPC512x family. + * + * Derived from the MPC83xx code. + */ + +#include <common.h> +#include <command.h> +#include <mpc512x.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +int checkcpu (void) +{ + volatile immap_t *immr = (immap_t *) CFG_IMMR; + ulong clock = gd->cpu_clk; + u32 pvr = get_pvr (); + u32 spridr = immr->sysconf.spridr; + char buf[32]; + + puts("CPU: "); + + switch (spridr & 0xffff0000) { + case SPR_5121E: + puts ("MPC5121e "); + break; + default: + printf ("Unknown part ID %08x ", spridr & 0xffff0000); + } + printf ("rev. %d.%d, Core ", SVR_MJREV (spridr), SVR_MNREV (spridr)); + + switch (pvr & 0xffff0000) { + case PVR_E300C4: + puts ("e300c4 "); + break; + default: + puts ("unknown "); + } + printf ("at %s MHz, CSB at %3d MHz\n", strmhz(buf, clock), + gd->csb_clk / 1000000); + return 0; +} + + +int +do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + ulong msr; + volatile immap_t *immap = (immap_t *) CFG_IMMR; + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~( MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* + * Enable Reset Control Reg - "RSTE" is the magic word that let us go + */ + immap->reset.rpr = 0x52535445; + + /* Verify Reset Control Reg is enabled */ + while (!((immap->reset.rcer) & RCER_CRE)) + ; + + printf ("Resetting the board.\n"); + udelay(200); + + /* Perform reset */ + immap->reset.rcr = RCR_SWHR; + + /* Unreached... */ + return 1; +} + + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + */ +unsigned long get_tbclk (void) +{ + ulong tbclk; + + tbclk = (gd->bus_clk + 3L) / 4L; + + return tbclk; +} + + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts (); + + /* Reset watchdog */ + volatile immap_t *immr = (immap_t *) CFG_IMMR; + immr->wdt.swsrr = 0x556c; + immr->wdt.swsrr = 0xaa39; + + if (re_enable) + enable_interrupts (); +} +#endif diff --git a/cpu/mpc512x/cpu_init.c b/cpu/mpc512x/cpu_init.c new file mode 100644 index 0000000..d6949f6 --- /dev/null +++ b/cpu/mpc512x/cpu_init.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2004-2006 Freescale Semiconductor, Inc. + * (C) Copyright 2007 DENX Software Engineering + * + * 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 + * + * Derived from the MPC83xx code. + * + */ + +#include <common.h> +#include <mpc512x.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Set up the memory map, initialize registers, + */ +void cpu_init_f (volatile immap_t * im) +{ + u32 ips_div; + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + + /* system performance tweaking */ + +#ifdef CFG_ACR_PIPE_DEP + /* Arbiter pipeline depth */ + im->arbiter.acr = (im->arbiter.acr & ~ACR_PIPE_DEP) | + (CFG_ACR_PIPE_DEP << ACR_PIPE_DEP_SHIFT); +#endif + +#ifdef CFG_ACR_RPTCNT + /* Arbiter repeat count */ + im->arbiter.acr = ((im->arbiter.acr & ~(ACR_RPTCNT)) | + (CFG_ACR_RPTCNT << ACR_RPTCNT_SHIFT)); +#endif + + /* RSR - Reset Status Register - clear all status */ + gd->reset_status = im->reset.rsr; + im->reset.rsr = ~(RSR_RES); + + /* + * RMR - Reset Mode Register - enable checkstop reset + */ + im->reset.rmr = (RMR_CSRE & (1 << RMR_CSRE_SHIFT)); + + /* Set IPS-CSB divider: IPS = 1/2 CSB */ + ips_div = im->clk.scfr[0]; + ips_div &= ~(SCFR1_IPS_DIV_MASK); + ips_div |= SCFR1_IPS_DIV << SCFR1_IPS_DIV_SHIFT; + im->clk.scfr[0] = ips_div; + + /* + * Enable Time Base/Decrementer + * + * NOTICE: TB needs to be enabled as early as possible in order to + * have udelay() working; if not enabled, usually leads to a hang, like + * during FLASH chip identification etc. + */ + im->sysconf.spcr |= SPCR_TBEN; +} + +int cpu_init_r (void) +{ + return 0; +} diff --git a/cpu/mpc512x/fec.c b/cpu/mpc512x/fec.c new file mode 100644 index 0000000..1c87a53 --- /dev/null +++ b/cpu/mpc512x/fec.c @@ -0,0 +1,801 @@ +/* + * (C) Copyright 2003-2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Derived from the MPC8xx FEC driver. + * Adapted for MPC512x by Grzegorz Bernacki <gjb@semihalf.com> + */ + +#include <common.h> +#include <mpc512x.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> +#include "fec.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define DEBUG 0 + +#if (CONFIG_COMMANDS & CFG_CMD_NET) && defined(CONFIG_NET_MULTI) && \ + defined(CONFIG_MPC512x_FEC) + +#if !(defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#if (DEBUG & 0x40) +static uint32 local_crc32(char *string, unsigned int crc_value, int len); +#endif + +int fec512x_miiphy_read(char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal); +int fec512x_miiphy_write(char *devname, uint8 phyAddr, uint8 regAddr, uint16 data); +int mpc512x_fec_init_phy(struct eth_device *dev, bd_t * bis); + +/********************************************************************/ +#if (DEBUG & 0x2) +static void mpc512x_fec_phydump (char *devname) +{ + uint16 phyStatus, i; + uint8 phyAddr = CONFIG_PHY_ADDR; + uint8 reg_mask[] = { + /* regs to print: 0...8, 21,27,31 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, + }; + + for (i = 0; i < 32; i++) { + if (reg_mask[i]) { + miiphy_read (devname, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x\n", i, phyStatus); + } + } +} +#endif + +/********************************************************************/ +static int mpc512x_fec_bd_init (mpc512x_fec_priv *fec) +{ + int ix; + + /* + * Receive BDs init + */ + for (ix = 0; ix < FEC_RBD_NUM; ix++) { + fec->bdBase->rbd[ix].dataPointer = (uint32)&fec->bdBase->recv_frames[ix]; + fec->bdBase->rbd[ix].status = FEC_RBD_EMPTY; + fec->bdBase->rbd[ix].dataLength = 0; + } + + /* + * have the last RBD to close the ring + */ + fec->bdBase->rbd[ix - 1].status |= FEC_RBD_WRAP; + fec->rbdIndex = 0; + + /* + * Trasmit BDs init + */ + for (ix = 0; ix < FEC_TBD_NUM; ix++) { + fec->bdBase->tbd[ix].status = 0; + } + + /* + * Have the last TBD to close the ring + */ + fec->bdBase->tbd[ix - 1].status |= FEC_TBD_WRAP; + + /* + * Initialize some indices + */ + fec->tbdIndex = 0; + fec->usedTbdIndex = 0; + fec->cleanTbdNum = FEC_TBD_NUM; + + return 0; +} + +/********************************************************************/ +static void mpc512x_fec_rbd_clean (mpc512x_fec_priv *fec, volatile FEC_RBD * pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) + pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); + else + pRbd->status = FEC_RBD_EMPTY; + + pRbd->dataLength = 0; + + /* + * Increment BD count + */ + fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; + + /* + * Now, we have an empty RxBD, notify FEC + */ + fec->eth->r_des_active = 0x01000000; /* Descriptor polling active */ +} + +/********************************************************************/ +static void mpc512x_fec_tbd_scrub (mpc512x_fec_priv *fec) +{ + volatile FEC_TBD *pUsedTbd; + +#if (DEBUG & 0x1) + printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", + fec->cleanTbdNum, fec->usedTbdIndex); +#endif + + /* + * process all the consumed TBDs + */ + while (fec->cleanTbdNum < FEC_TBD_NUM) { + pUsedTbd = &fec->bdBase->tbd[fec->usedTbdIndex]; + if (pUsedTbd->status & FEC_TBD_READY) { +#if (DEBUG & 0x20) + printf ("Cannot clean TBD %d, in use\n", fec->usedTbdIndex); +#endif + return; + } + + /* + * clean this buffer descriptor + */ + if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) + pUsedTbd->status = FEC_TBD_WRAP; + else + pUsedTbd->status = 0; + + /* + * update some indeces for a correct handling of the TBD ring + */ + fec->cleanTbdNum++; + fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; + } +} + +/********************************************************************/ +static void mpc512x_fec_set_hwaddr (mpc512x_fec_priv *fec, char *mac) +{ + uint8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + uint32 crc = 0xffffffff; /* initial value */ + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if (crc >= 32) { + fec->eth->iaddr1 = (1 << (crc - 32)); + fec->eth->iaddr2 = 0; + } else { + fec->eth->iaddr1 = 0; + fec->eth->iaddr2 = (1 << crc); + } + + /* + * Set physical address + */ + fec->eth->paddr1 = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + fec->eth->paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; +} + +/********************************************************************/ +static int mpc512x_fec_init (struct eth_device *dev, bd_t * bis) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init... Begin\n"); +#endif + + /* Set interrupt mask register */ + fec->eth->imask = 0x00000000; + + /* Clear FEC-Lite interrupt event register(IEVENT) */ + fec->eth->ievent = 0xffffffff; + + /* Set transmit fifo watermark register(X_WMRK), default = 64 */ + fec->eth->x_wmrk = 0x0; + + /* Set Opcode/Pause Duration Register */ + fec->eth->op_pause = 0x00010020; + + /* Frame length=1518; MII mode */ + fec->eth->r_cntrl = 0x05ee000c; + + /* Half-duplex, heartbeat disabled */ + fec->eth->x_cntrl = 0x00000000; + + /* Enable MIB counters */ + fec->eth->mib_control = 0x0; + + /* Setup recv fifo start and buff size */ + fec->eth->r_fstart = 0x500; + fec->eth->r_buff_size = 0x5e0; + + /* Setup BD base addresses */ + fec->eth->r_des_start = (uint32)fec->bdBase->rbd; + fec->eth->x_des_start = (uint32)fec->bdBase->tbd; + + /* DMA Control */ + fec->eth->dma_control = 0xc0000000; + + /* Enable FEC */ + fec->eth->ecntrl |= 0x00000006; + + /* Initilize addresses and status words of BDs */ + mpc512x_fec_bd_init (fec); + + /* Descriptor polling active */ + fec->eth->r_des_active = 0x01000000; + +#if (DEBUG & 0x1) + printf("mpc512x_fec_init... Done \n"); +#endif + return 1; +} + +/********************************************************************/ +int mpc512x_fec_init_phy (struct eth_device *dev, bd_t * bis) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + const uint8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + int timeout = 1; + uint16 phyStatus; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init_phy... Begin\n"); +#endif + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + fec->eth->mii_speed = (((gd->ipb_clk / 1000000) / 5) + 1) << 1; + + /* + * Reset PHY, then delay 300ns + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x8000); + udelay (1000); + + if (fec->xcv_type == MII10) { + /* + * Force 10Base-T, FDX operation + */ +#if (DEBUG & 0x2) + printf ("Forcing 10 Mbps ethernet link... "); +#endif + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); + + miiphy_write (dev->name, phyAddr, 0x0, 0x0180); + + timeout = 20; + do { /* wait for link status to go down */ + udelay (10000); + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf ("hmmm, should not have waited..."); +#endif + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf ("="); +#endif + } while ((phyStatus & 0x0004)); /* !link up */ + + timeout = 1000; + do { /* wait for link status to come back up */ + udelay (10000); + if ((timeout--) == 0) { + printf ("failed. Link is down.\n"); + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf ("+"); +#endif + } while (!(phyStatus & 0x0004)); /* !link up */ + +#if (DEBUG & 0x2) + printf ("done.\n"); +#endif + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write (dev->name, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 50000; + do { + udelay (1000); + + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf ("PHY auto neg 0 failed...\n"); +#endif + return -1; + } + + if (miiphy_read (dev->name, phyAddr, 0x1, &phyStatus) != 0) { +#if (DEBUG & 0x2) + printf ("PHY auto neg 1 failed 0x%04x...\n", phyStatus); +#endif + return -1; + } + } while (!(phyStatus & 0x0004)); + +#if (DEBUG & 0x2) + printf ("PHY auto neg complete! \n"); +#endif + } + } + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc512x_fec_phydump (dev->name); +#endif + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init_phy... Done \n"); +#endif + return 1; +} + +/********************************************************************/ +static void mpc512x_fec_halt (struct eth_device *dev) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + int counter = 0xffff; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc512x_fec_phydump (dev->name); +#endif + + /* + * mask FEC chip interrupts + */ + fec->eth->imask = 0; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + fec->eth->x_cntrl |= 0x00000001; + + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(fec->eth->ievent & 0x10000000))) ; + + /* + * Disable the Ethernet Controller + */ + fec->eth->ecntrl &= 0xfffffffd; + + /* + * Issue a reset command to the FEC chip + */ + fec->eth->ecntrl |= 0x1; + + /* + * wait at least 16 clock cycles + */ + udelay (10); +#if (DEBUG & 0x3) + printf ("Ethernet task stopped\n"); +#endif +} + +/********************************************************************/ + +static int mpc512x_fec_send (struct eth_device *dev, volatile void *eth_data, + int data_length) +{ + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + volatile FEC_TBD *pTbd; + +#if (DEBUG & 0x20) + printf("tbd status: 0x%04x\n", fec->tbdBase[fec->tbdIndex].status); +#endif + + /* + * Clear Tx BD ring at first + */ + mpc512x_fec_tbd_scrub (fec); + + /* + * Check for valid length of data. + */ + if ((data_length > 1500) || (data_length <= 0)) { + return -1; + } + + /* + * Check the number of vacant TxBDs. + */ + if (fec->cleanTbdNum < 1) { +#if (DEBUG & 0x20) + printf ("No available TxBDs ...\n"); +#endif + return -1; + } + + /* + * Get the first TxBD to send the mac header + */ + pTbd = &fec->bdBase->tbd[fec->tbdIndex]; + pTbd->dataLength = data_length; + pTbd->dataPointer = (uint32)eth_data; + pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; + + /* Activate transmit Buffer Descriptor polling */ + fec->eth->x_des_active = 0x01000000; /* Descriptor polling active */ + +#if (DEBUG & 0x8) + printf ( "+" ); +#endif + + fec->cleanTbdNum -= 1; + + /* + * wait until frame is sent . + */ + while (pTbd->status & FEC_TBD_READY) { + udelay (10); +#if (DEBUG & 0x8) + printf ("TDB status = %04x\n", pTbd->status); +#endif + } + + return 0; +} + + +/********************************************************************/ +static int mpc512x_fec_recv (struct eth_device *dev) +{ + /* + * This command pulls one frame from the card + */ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + volatile FEC_RBD *pRbd = &fec->bdBase->rbd[fec->rbdIndex]; + unsigned long ievent; + int frame_length, len = 0; + uchar buff[FEC_MAX_PKT_SIZE]; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_recv %d Start...\n", fec->rbdIndex); +#endif +#if (DEBUG & 0x8) + printf( "-" ); +#endif + + /* + * Check if any critical events have happened + */ + ievent = fec->eth->ievent; + fec->eth->ievent = ievent; + if (ievent & 0x20060000) { + /* BABT, Rx/Tx FIFO errors */ + mpc512x_fec_halt (dev); + mpc512x_fec_init (dev, NULL); + return 0; + } + if (ievent & 0x80000000) { + /* Heartbeat error */ + fec->eth->x_cntrl |= 0x00000001; + } + if (ievent & 0x10000000) { + /* Graceful stop complete */ + if (fec->eth->x_cntrl & 0x00000001) { + mpc512x_fec_halt (dev); + fec->eth->x_cntrl &= ~0x00000001; + mpc512x_fec_init (dev, NULL); + } + } + + if (!(pRbd->status & FEC_RBD_EMPTY)) { + if ((pRbd->status & FEC_RBD_LAST) && + !(pRbd->status & FEC_RBD_ERR) && + ((pRbd->dataLength - 4) > 14)) { + + /* + * Get buffer size + */ + frame_length = pRbd->dataLength - 4; + +#if (DEBUG & 0x20) + { + int i; + printf ("recv data hdr:"); + for (i = 0; i < 14; i++) + printf ("%x ", *((uint8*)pRbd->dataPointer + i)); + printf("\n"); + } +#endif + + /* + * Fill the buffer and pass it to upper layers + */ + memcpy (buff, (void*)pRbd->dataPointer, frame_length); + NetReceive ((uchar*)buff, frame_length); + len = frame_length; + } + + /* + * Reset buffer descriptor as empty + */ + mpc512x_fec_rbd_clean (fec, pRbd); + } + + /* Try to fill Buffer Descriptors */ + fec->eth->r_des_active = 0x01000000; /* Descriptor polling active */ + return len; +} + +/********************************************************************/ +int mpc512x_fec_initialize (bd_t * bis) +{ + + immap_t *im = (immap_t*) CFG_IMMR; + mpc512x_fec_priv *fec; + struct eth_device *dev; + int i; + char *tmp, *end, env_enetaddr[6]; + uint32 *reg; + void * bd; + + fec = (mpc512x_fec_priv *) malloc (sizeof(*fec)); + dev = (struct eth_device *) malloc (sizeof(*dev)); + memset (dev, 0, sizeof *dev); + + fec->eth = (ethernet_regs *) MPC512X_FEC; + +# ifndef CONFIG_FEC_10MBIT + fec->xcv_type = MII100; +# else + fec->xcv_type = MII10; +# endif + dev->priv = (void *)fec; + dev->iobase = MPC512X_FEC; + dev->init = mpc512x_fec_init; + dev->halt = mpc512x_fec_halt; + dev->send = mpc512x_fec_send; + dev->recv = mpc512x_fec_recv; + + sprintf (dev->name, "FEC ETHERNET"); + eth_register (dev); + +#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) + miiphy_register (dev->name, + fec512x_miiphy_read, fec512x_miiphy_write); +#endif + + /* + * Initialize I\O pins + */ + reg = (uint32 *) &(im->io_ctrl.regs[PSC0_0_IDX]); + + for (i = 0; i < 15; i++) + reg[i] = IOCTRL_MUX_FEC | 0x00000001; + + im->io_ctrl.regs[SPDIF_TXCLOCK_IDX] = IOCTRL_MUX_FEC | 0x00000001; + im->io_ctrl.regs[SPDIF_TX_IDX] = IOCTRL_MUX_FEC | 0x00000001; + im->io_ctrl.regs[SPDIF_RX_IDX] = IOCTRL_MUX_FEC | 0x00000001; + + /* Clean up space FEC's MIB and FIFO RAM ...*/ + memset ((void *) MPC512X_FEC + 0x200, 0x00, 0x400); + + /* + * Malloc space for BDs (must be quad word-aligned) + * this pointer is lost, so cannot be freed + */ + bd = malloc (sizeof(mpc512x_buff_descs) + 0x1f); + fec->bdBase = (mpc512x_buff_descs*)((uint32)bd & 0xfffffff0); + memset ((void *) bd, 0x00, sizeof(mpc512x_buff_descs) + 0x1f); + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Try to set the mac address now. The fec mac address is + * a garbage after reset. When not using fec for booting + * the Linux fec driver will try to work with this garbage. + */ + tmp = getenv ("ethaddr"); + if (tmp) { + for (i=0; i<6; i++) { + env_enetaddr[i] = tmp ? simple_strtoul (tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end+1 : end; + } + mpc512x_fec_set_hwaddr (fec, env_enetaddr); + fec->eth->gaddr1 = 0x00000000; + fec->eth->gaddr2 = 0x00000000; + } + + mpc512x_fec_init_phy (dev, bis); + + return 1; +} + +/* MII-interface related functions */ +/********************************************************************/ +int fec512x_miiphy_read (char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal) +{ + ethernet_regs *eth = (ethernet_regs *) MPC512X_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | phy | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Read MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear mii interrupt bit + */ + eth->ievent = 0x00800000; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (uint16) eth->mii_data; + + return 0; +} + +/********************************************************************/ +int fec512x_miiphy_write (char *devname, uint8 phyAddr, uint8 regAddr, uint16 data) +{ + ethernet_regs *eth = (ethernet_regs *) MPC512X_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Write MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear MII interrupt bit + */ + eth->ievent = 0x00800000; + + return 0; +} + +#if (DEBUG & 0x40) +static uint32 local_crc32 (char *string, unsigned int crc_value, int len) +{ + int i; + char c; + unsigned int crc, count; + + /* + * crc32 algorithm + */ + /* + * crc = 0xffffffff; * The initialized value should be 0xffffffff + */ + crc = crc_value; + + for (i = len; --i >= 0;) { + c = *string++; + for (count = 0; count < 8; count++) { + if ((c & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + c >>= 1; + } + } + + /* + * In big endian system, do byte swaping for crc value + */ + /**/ return crc; +} +#endif /* DEBUG */ + +#endif /* CONFIG_MPC512x_FEC */ diff --git a/cpu/mpc512x/fec.h b/cpu/mpc512x/fec.h new file mode 100644 index 0000000..d2d877a --- /dev/null +++ b/cpu/mpc512x/fec.h @@ -0,0 +1,224 @@ +/* + * (C) Copyright 2003 - 2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Derived from the MPC8xx driver's header file. + */ + +#ifndef __MPC512X_FEC_H +#define __MPC512X_FEC_H + +#include <common.h> +#include <mpc512x.h> + +typedef unsigned long uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +typedef struct ethernet_register_set { + +/* [10:2]addr = 00 */ + +/* Control and status Registers (offset 000-1FF) */ + + volatile uint32 fec_id; /* MBAR_ETH + 0x000 */ + volatile uint32 ievent; /* MBAR_ETH + 0x004 */ + volatile uint32 imask; /* MBAR_ETH + 0x008 */ + + volatile uint32 RES0[1]; /* MBAR_ETH + 0x00C */ + volatile uint32 r_des_active; /* MBAR_ETH + 0x010 */ + volatile uint32 x_des_active; /* MBAR_ETH + 0x014 */ + + volatile uint32 RES1[3]; /* MBAR_ETH + 0x018-020 */ + volatile uint32 ecntrl; /* MBAR_ETH + 0x024 */ + + volatile uint32 RES2[6]; /* MBAR_ETH + 0x028-03C */ + volatile uint32 mii_data; /* MBAR_ETH + 0x040 */ + volatile uint32 mii_speed; /* MBAR_ETH + 0x044 */ + + volatile uint32 RES3[7]; /* MBAR_ETH + 0x048-060 */ + volatile uint32 mib_control; /* MBAR_ETH + 0x064 */ + + volatile uint32 RES4[7]; /* MBAR_ETH + 0x068-80 */ + volatile uint32 r_cntrl; /* MBAR_ETH + 0x084 */ + volatile uint32 r_hash; /* MBAR_ETH + 0x088 */ + + volatile uint32 RES5[14]; /* MBAR_ETH + 0x08c-0C0 */ + volatile uint32 x_cntrl; /* MBAR_ETH + 0x0C4 */ + + volatile uint32 RES6[7]; /* MBAR_ETH + 0x0C8-0E0 */ + volatile uint32 paddr1; /* MBAR_ETH + 0x0E4 */ + volatile uint32 paddr2; /* MBAR_ETH + 0x0E8 */ + volatile uint32 op_pause; /* MBAR_ETH + 0x0EC */ + + volatile uint32 RES7[10]; /* MBAR_ETH + 0x0F0-114 */ + volatile uint32 iaddr1; /* MBAR_ETH + 0x118 */ + volatile uint32 iaddr2; /* MBAR_ETH + 0x11C */ + volatile uint32 gaddr1; /* MBAR_ETH + 0x120 */ + volatile uint32 gaddr2; /* MBAR_ETH + 0x124 */ + + volatile uint32 RES8[6]; /* MBAR_ETH + 0x128-13C */ + volatile uint32 fifo_id; /* MBAR_ETH + 0x140 */ + volatile uint32 x_wmrk; /* MBAR_ETH + 0x144 */ + volatile uint32 RES9[1]; /* MBAR_ETH + 0x148 */ + volatile uint32 r_bound; /* MBAR_ETH + 0x14C */ + volatile uint32 r_fstart; /* MBAR_ETH + 0x150 */ + + volatile uint32 RES10[11]; /* MBAR_ETH + 0x154-17C */ + volatile uint32 r_des_start; /* MBAR_ETH + 0x180 */ + volatile uint32 x_des_start; /* MBAR_ETH + 0x184 */ + volatile uint32 r_buff_size; /* MBAR_ETH + 0x188 */ + volatile uint32 RES11[26]; /* MBAR_ETH + 0x18C-1F0 */ + volatile uint32 dma_control; /* MBAR_ETH + 0x1F4 */ + volatile uint32 RES12[2]; /* MBAR_ETH + 0x1F8-1FC */ + +/* MIB COUNTERS (Offset 200-2FF) */ + + volatile uint32 rmon_t_drop; /* MBAR_ETH + 0x200 */ + volatile uint32 rmon_t_packets; /* MBAR_ETH + 0x204 */ + volatile uint32 rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ + volatile uint32 rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ + volatile uint32 rmon_t_crc_align; /* MBAR_ETH + 0x210 */ + volatile uint32 rmon_t_undersize; /* MBAR_ETH + 0x214 */ + volatile uint32 rmon_t_oversize; /* MBAR_ETH + 0x218 */ + volatile uint32 rmon_t_frag; /* MBAR_ETH + 0x21C */ + volatile uint32 rmon_t_jab; /* MBAR_ETH + 0x220 */ + volatile uint32 rmon_t_col; /* MBAR_ETH + 0x224 */ + volatile uint32 rmon_t_p64; /* MBAR_ETH + 0x228 */ + volatile uint32 rmon_t_p65to127; /* MBAR_ETH + 0x22C */ + volatile uint32 rmon_t_p128to255; /* MBAR_ETH + 0x230 */ + volatile uint32 rmon_t_p256to511; /* MBAR_ETH + 0x234 */ + volatile uint32 rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ + volatile uint32 rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */ + volatile uint32 rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ + volatile uint32 rmon_t_octets; /* MBAR_ETH + 0x244 */ + volatile uint32 ieee_t_drop; /* MBAR_ETH + 0x248 */ + volatile uint32 ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ + volatile uint32 ieee_t_1col; /* MBAR_ETH + 0x250 */ + volatile uint32 ieee_t_mcol; /* MBAR_ETH + 0x254 */ + volatile uint32 ieee_t_def; /* MBAR_ETH + 0x258 */ + volatile uint32 ieee_t_lcol; /* MBAR_ETH + 0x25C */ + volatile uint32 ieee_t_excol; /* MBAR_ETH + 0x260 */ + volatile uint32 ieee_t_macerr; /* MBAR_ETH + 0x264 */ + volatile uint32 ieee_t_cserr; /* MBAR_ETH + 0x268 */ + volatile uint32 ieee_t_sqe; /* MBAR_ETH + 0x26C */ + volatile uint32 t_fdxfc; /* MBAR_ETH + 0x270 */ + volatile uint32 ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ + + volatile uint32 RES13[2]; /* MBAR_ETH + 0x278-27C */ + volatile uint32 rmon_r_drop; /* MBAR_ETH + 0x280 */ + volatile uint32 rmon_r_packets; /* MBAR_ETH + 0x284 */ + volatile uint32 rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ + volatile uint32 rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ + volatile uint32 rmon_r_crc_align; /* MBAR_ETH + 0x290 */ + volatile uint32 rmon_r_undersize; /* MBAR_ETH + 0x294 */ + volatile uint32 rmon_r_oversize; /* MBAR_ETH + 0x298 */ + volatile uint32 rmon_r_frag; /* MBAR_ETH + 0x29C */ + volatile uint32 rmon_r_jab; /* MBAR_ETH + 0x2A0 */ + + volatile uint32 rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ + + volatile uint32 rmon_r_p64; /* MBAR_ETH + 0x2A8 */ + volatile uint32 rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ + volatile uint32 rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ + volatile uint32 rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ + volatile uint32 rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ + volatile uint32 rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */ + volatile uint32 rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ + volatile uint32 rmon_r_octets; /* MBAR_ETH + 0x2C4 */ + volatile uint32 ieee_r_drop; /* MBAR_ETH + 0x2C8 */ + volatile uint32 ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ + volatile uint32 ieee_r_crc; /* MBAR_ETH + 0x2D0 */ + volatile uint32 ieee_r_align; /* MBAR_ETH + 0x2D4 */ + volatile uint32 r_macerr; /* MBAR_ETH + 0x2D8 */ + volatile uint32 r_fdxfc; /* MBAR_ETH + 0x2DC */ + volatile uint32 ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ + + volatile uint32 RES14[6]; /* MBAR_ETH + 0x2E4-2FC */ + + volatile uint32 RES15[64]; /* MBAR_ETH + 0x300-3FF */ +} ethernet_regs; + +/* Receive & Transmit Buffer Descriptor definitions */ +typedef struct BufferDescriptor { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_RBD; + +typedef struct { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_TBD; + +/* private structure */ +typedef enum { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +} xceiver_type; + +/* BD Numer definitions */ +#define FEC_TBD_NUM 48 /* The user can adjust this value */ +#define FEC_RBD_NUM 32 /* The user can adjust this value */ + +/* packet size limit */ +#define FEC_MAX_PKT_SIZE 1536 + +typedef struct { + uint8 frame[FEC_MAX_PKT_SIZE]; +} mpc512x_frame; + +typedef struct { + FEC_RBD rbd[FEC_RBD_NUM]; /* RBD ring */ + FEC_TBD tbd[FEC_TBD_NUM]; /* TBD ring */ + mpc512x_frame recv_frames[FEC_RBD_NUM]; /* receive buff */ +} mpc512x_buff_descs; + +typedef struct { + ethernet_regs *eth; + xceiver_type xcv_type; /* transceiver type */ + mpc512x_buff_descs *bdBase; /* BD rings and recv buffer */ + uint16 rbdIndex; /* next receive BD to read */ + uint16 tbdIndex; /* next transmit BD to send */ + uint16 usedTbdIndex; /* next transmit BD to clean */ + uint16 cleanTbdNum; /* the number of available transmit BDs */ +} mpc512x_fec_priv; + +/* RBD bits definitions */ +#define FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define FEC_RBD_LG 0x0020 /* Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define FEC_RBD_SH 0x0008 /* Short frame */ +#define FEC_RBD_CR 0x0004 /* CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* TBD bits definitions */ +#define FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __MPC512X_FEC_H */ diff --git a/cpu/mpc512x/i2c.c b/cpu/mpc512x/i2c.c new file mode 100644 index 0000000..00e28d6 --- /dev/null +++ b/cpu/mpc512x/i2c.c @@ -0,0 +1,431 @@ +/* + * (C) Copyright 2003 - 2007 + * 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 + * + * Based on the MPC5xxx code. + */ + +#include <common.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_HARD_I2C + +#include <mpc512x.h> +#include <i2c.h> + +#define immr ((immap_t *)CFG_IMMR) + +/* by default set I2C bus 0 active */ +static unsigned int bus_num = 0; + +#define I2C_TIMEOUT 100 +#define I2C_RETRIES 3 + +struct mpc512x_i2c_tap { + int scl2tap; + int tap2tap; +}; + +static int mpc_reg_in(volatile u32 *reg); +static void mpc_reg_out(volatile u32 *reg, int val, int mask); +static int wait_for_bb(void); +static int wait_for_pin(int *status); +static int do_address(uchar chip, char rdwr_flag); +static int send_bytes(uchar chip, char *buf, int len); +static int receive_bytes(uchar chip, char *buf, int len); +static int mpc_get_fdr(int); + +static int mpc_reg_in (volatile u32 *reg) +{ + int ret = *reg >> 24; + __asm__ __volatile__ ("eieio"); + return ret; +} + +static void mpc_reg_out (volatile u32 *reg, int val, int mask) +{ + int tmp; + + if (!mask) { + *reg = val << 24; + } else { + tmp = mpc_reg_in (reg); + *reg = ((tmp & ~mask) | (val & mask)) << 24; + } + __asm__ __volatile__ ("eieio"); + + return; +} + +static int wait_for_bb (void) +{ + i2c512x_dev_t *regs = &immr->i2c.dev[bus_num]; + int timeout = I2C_TIMEOUT; + int status; + + status = mpc_reg_in (®s->msr); + + while (timeout-- && (status & I2C_BB)) { + volatile int temp; + mpc_reg_out (®s->mcr, I2C_STA, I2C_STA); + temp = mpc_reg_in (®s->mdr); + mpc_reg_out (®s->mcr, 0, I2C_STA); + mpc_reg_out (®s->mcr, 0, 0); + mpc_reg_out (®s->mcr, I2C_EN, 0); + + udelay (1000); + status = mpc_reg_in (®s->msr); + } + + return (status & I2C_BB); +} + +static int wait_for_pin (int *status) +{ + i2c512x_dev_t *regs = &immr->i2c.dev[bus_num]; + int timeout = I2C_TIMEOUT; + + *status = mpc_reg_in (®s->msr); + + while (timeout-- && !(*status & I2C_IF)) { + udelay (1000); + *status = mpc_reg_in (®s->msr); + } + + if (!(*status & I2C_IF)) { + return -1; + } + + mpc_reg_out (®s->msr, 0, I2C_IF); + + return 0; +} + +static int do_address (uchar chip, char rdwr_flag) +{ + i2c512x_dev_t *regs = &immr->i2c.dev[bus_num]; + int status; + + chip <<= 1; + + if (rdwr_flag) { + chip |= 1; + } + + mpc_reg_out (®s->mcr, I2C_TX, I2C_TX); + mpc_reg_out (®s->mdr, chip, 0); + + if (wait_for_pin (&status)) { + return -2; + } + + if (status & I2C_RXAK) { + return -3; + } + + return 0; +} + +static int send_bytes (uchar chip, char *buf, int len) +{ + i2c512x_dev_t *regs = &immr->i2c.dev[bus_num]; + int wrcount; + int status; + + for (wrcount = 0; wrcount < len; ++wrcount) { + + mpc_reg_out (®s->mdr, buf[wrcount], 0); + + if (wait_for_pin (&status)) { + break; + } + + if (status & I2C_RXAK) { + break; + } + + } + + return !(wrcount == len); +} + +static int receive_bytes (uchar chip, char *buf, int len) +{ + i2c512x_dev_t *regs = &immr->i2c.dev[bus_num]; + int dummy = 1; + int rdcount = 0; + int status; + int i; + + mpc_reg_out (®s->mcr, 0, I2C_TX); + + for (i = 0; i < len; ++i) { + buf[rdcount] = mpc_reg_in (®s->mdr); + + if (dummy) { + dummy = 0; + } else { + rdcount++; + } + + if (wait_for_pin (&status)) { + return -4; + } + } + + mpc_reg_out (®s->mcr, I2C_TXAK, I2C_TXAK); + buf[rdcount++] = mpc_reg_in (®s->mdr); + + if (wait_for_pin (&status)) { + return -5; + } + + mpc_reg_out (®s->mcr, 0, I2C_TXAK); + + return 0; +} + +/**************** I2C API ****************/ + +void i2c_init (int speed, int saddr) +{ + int i; + for(i = 0; i < I2C_BUS_CNT; i++){ + i2c512x_dev_t *regs = &immr->i2c.dev[i]; + mpc_reg_out (®s->mcr, 0, 0); + + /* Set clock */ + mpc_reg_out (®s->mfdr, mpc_get_fdr (speed), 0); + mpc_reg_out (®s->madr, saddr << 1, 0); + + /* Enable module */ + mpc_reg_out (®s->mcr, I2C_EN, I2C_INIT_MASK); + mpc_reg_out (®s->msr, 0, I2C_IF); + } + + /* Disable interrupts */ + immr->i2c.icr = 0; + /* Turn off filters */ + immr->i2c.mifr = 0; + return; +} + +static int mpc_get_fdr (int speed) +{ + static int fdr = -1; + + if (fdr == -1) { + ulong best_speed = 0; + ulong divider; + ulong ipb, scl; + ulong bestmatch = 0xffffffffUL; + int best_i = 0, best_j = 0, i, j; + int SCL_Tap[] = { 9, 10, 12, 15, 5, 6, 7, 8}; + struct mpc512x_i2c_tap scltap[] = { + {4, 1}, + {4, 2}, + {6, 4}, + {6, 8}, + {14, 16}, + {30, 32}, + {62, 64}, + {126, 128} + }; + + ipb = gd->ipb_clk; + for (i = 7; i >= 0; i--) { + for (j = 7; j >= 0; j--) { + scl = 2 * (scltap[j].scl2tap + + (SCL_Tap[i] - 1) * scltap[j].tap2tap + + 2); + if (ipb <= speed*scl) { + if ((speed*scl - ipb) < bestmatch) { + bestmatch = speed*scl - ipb; + best_i = i; + best_j = j; + best_speed = ipb/scl; + } + } + } + } + divider = (best_i & 3) | ((best_i & 4) << 3) | (best_j << 2); + if (gd->flags & GD_FLG_RELOC) { + fdr = divider; + } else { + debug("%ld kHz, \n", best_speed / 1000); + return divider; + } + } + + return fdr; +} + +int i2c_probe (uchar chip) +{ + i2c512x_dev_t *regs = &immr->i2c.dev[bus_num]; + int i; + + for (i = 0; i < I2C_RETRIES; i++) { + mpc_reg_out (®s->mcr, I2C_STA, I2C_STA); + + if (! do_address (chip, 0)) { + mpc_reg_out (®s->mcr, 0, I2C_STA); + udelay (500); + break; + } + + mpc_reg_out (®s->mcr, 0, I2C_STA); + udelay (500); + } + + return (i == I2C_RETRIES); +} + +int i2c_read (uchar chip, uint addr, int alen, uchar *buf, int len) +{ + char xaddr[4]; + i2c512x_dev_t *regs = &immr->i2c.dev[bus_num]; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb ()) { + printf ("i2c_read: bus is busy\n"); + goto Done; + } + + mpc_reg_out (®s->mcr, I2C_STA, I2C_STA); + if (do_address (chip, 0)) { + printf ("i2c_read: failed to address chip\n"); + goto Done; + } + + if (send_bytes (chip, &xaddr[4-alen], alen)) { + printf ("i2c_read: send_bytes failed\n"); + goto Done; + } + + mpc_reg_out (®s->mcr, I2C_RSTA, I2C_RSTA); + if (do_address (chip, 1)) { + printf ("i2c_read: failed to address chip\n"); + goto Done; + } + + if (receive_bytes (chip, (char *)buf, len)) { + printf ("i2c_read: receive_bytes failed\n"); + goto Done; + } + + ret = 0; +Done: + mpc_reg_out (®s->mcr, 0, I2C_STA); + return ret; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar *buf, int len) +{ + char xaddr[4]; + i2c512x_dev_t *regs = &immr->i2c.dev[bus_num]; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb ()) { + printf ("i2c_write: bus is busy\n"); + goto Done; + } + + mpc_reg_out (®s->mcr, I2C_STA, I2C_STA); + if (do_address (chip, 0)) { + printf ("i2c_write: failed to address chip\n"); + goto Done; + } + + if (send_bytes (chip, &xaddr[4-alen], alen)) { + printf ("i2c_write: send_bytes failed\n"); + goto Done; + } + + if (send_bytes (chip, (char *)buf, len)) { + printf ("i2c_write: send_bytes failed\n"); + goto Done; + } + + ret = 0; +Done: + mpc_reg_out (®s->mcr, 0, I2C_STA); + return ret; +} + +uchar i2c_reg_read (uchar chip, uchar reg) +{ + uchar buf; + + i2c_read (chip, reg, 1, &buf, 1); + + return buf; +} + +void i2c_reg_write (uchar chip, uchar reg, uchar val) +{ + i2c_write (chip, reg, 1, &val, 1); + + return; +} + + +int i2c_set_bus_num (unsigned int bus) +{ + if (bus >= I2C_BUS_CNT) { + return -1; + } + bus_num = bus; + + return 0; +} + +unsigned int i2c_get_bus_num (void) +{ + return bus_num; +} + +/* TODO */ +unsigned int i2c_get_bus_speed (void) +{ + return -1; +} + +int i2c_set_bus_speed (unsigned int speed) +{ + if (speed != CFG_I2C_SPEED) + return -1; + + return 0; +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/cpu/mpc512x/interrupts.c b/cpu/mpc512x/interrupts.c new file mode 100644 index 0000000..8cc241c --- /dev/null +++ b/cpu/mpc512x/interrupts.c @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2000-2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright 2004 Freescale Semiconductor, 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 + * + * Derived from the MPC83xx code. + */ + +#include <common.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct irq_action { + interrupt_handler_t *handler; + void *arg; + ulong count; +}; + +int interrupt_init_cpu (unsigned *decrementer_count) +{ + *decrementer_count = get_tbclk () / CFG_HZ; + + return 0; +} + +/* + * Install and free an interrupt handler. + */ +void +irq_install_handler (int irq, interrupt_handler_t * handler, void *arg) +{ +} + +void irq_free_handler (int irq) +{ +} + +void timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} diff --git a/cpu/mpc512x/serial.c b/cpu/mpc512x/serial.c new file mode 100644 index 0000000..200ff2c --- /dev/null +++ b/cpu/mpc512x/serial.c @@ -0,0 +1,197 @@ +/* + * (C) Copyright 2000 - 2007 + * 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 + * + * Based ont the MPC5200 PSC driver. + * Adapted for MPC512x by Jan Wrobel <wrr@semihalf.com> + */ + +/* + * Minimal serial functions needed to use one of the PSC ports + * as serial console interface. + */ + +#include <common.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_PSC_CONSOLE) + +static void fifo_init (volatile psc512x_t *psc) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + + /* reset Rx & Tx fifo slice */ + psc->rfcmd = PSC_FIFO_RESET_SLICE; + psc->tfcmd = PSC_FIFO_RESET_SLICE; + + /* disable Tx & Rx FIFO interrupts */ + psc->rfintmask = 0; + psc->tfintmask = 0; + + psc->tfsize = CONSOLE_FIFO_TX_SIZE | (CONSOLE_FIFO_TX_ADDR << 16); + psc->rfsize = CONSOLE_FIFO_RX_SIZE | (CONSOLE_FIFO_RX_ADDR << 16); + + /* enable Tx & Rx FIFO slice */ + psc->rfcmd = PSC_FIFO_ENABLE_SLICE; + psc->tfcmd = PSC_FIFO_ENABLE_SLICE; + + im->fifoc.fifoc_cmd = FIFOC_DISABLE_CLOCK_GATE; + __asm__ volatile ("sync"); +} + +int serial_init(void) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + unsigned long baseclk; + int div; + + fifo_init (psc); + + /* set MR register to point to MR1 */ + psc->command = PSC_SEL_MODE_REG_1; + + /* disable Tx/Rx */ + psc->command = PSC_TX_DISABLE | PSC_RX_DISABLE; + + /* choose the prescaler by 16 for the Tx/Rx clock generation */ + psc->psc_clock_select = 0xdd00; + + /* switch to UART mode */ + psc->sicr = 0; + + /* mode register points to mr1 */ + /* configure parity, bit length and so on in mode register 1*/ + psc->mode = PSC_MODE_8_BITS | PSC_MODE_PARNONE; + /* now, mode register points to mr2 */ + psc->mode = PSC_MODE_1_STOPBIT; + + /* calculate dividor for setting PSC CTUR and CTLR registers */ + baseclk = (gd->ipb_clk + 8) / 16; + div = (baseclk + (gd->baudrate / 2)) / gd->baudrate; + + psc->ctur = (div >> 8) & 0xff; + /* set baudrate */ + psc->ctlr = div & 0xff; + + /* disable all interrupts */ + psc->psc_imr = 0; + + /* reset and enable Rx/Tx */ + psc->command = PSC_RST_RX; + psc->command = PSC_RST_TX; + psc->command = PSC_RX_ENABLE | PSC_TX_ENABLE; + + return 0; +} + +void serial_putc (const char c) +{ + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + if (c == '\n') + serial_putc ('\r'); + + /* Wait for last character to go. */ + while (!(psc->psc_status & PSC_SR_TXEMP)) + ; + + psc->tfdata_8 = c; +} + +void serial_putc_raw (const char c) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + /* Wait for last character to go. */ + while (!(psc->psc_status & PSC_SR_TXEMP)) + ; + + psc->tfdata_8 = c; +} + + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int serial_getc (void) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + /* Wait for a character to arrive. */ + while (psc->rfstat & PSC_FIFO_EMPTY) + ; + + return psc->rfdata_8; +} + +int serial_tstc (void) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + return !(psc->rfstat & PSC_FIFO_EMPTY); +} + +void serial_setbrg (void) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + unsigned long baseclk, div; + + baseclk = (gd->csb_clk + 8) / 16; + div = (baseclk + (gd->baudrate / 2)) / gd->baudrate; + + psc->ctur = (div >> 8) & 0xFF; + psc->ctlr = div & 0xff; /* set baudrate */ +} + +void serial_setrts(int s) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + if (s) { + /* Assert RTS (become LOW) */ + psc->op1 = 0x1; + } + else { + /* Negate RTS (become HIGH) */ + psc->op0 = 0x1; + } +} + +int serial_getcts(void) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + volatile psc512x_t *psc = (psc512x_t *) &im->psc[CONFIG_PSC_CONSOLE]; + + return (psc->ip & 0x1) ? 0 : 1; +} +#endif /* CONFIG_PSC_CONSOLE */ diff --git a/cpu/mpc512x/speed.c b/cpu/mpc512x/speed.c new file mode 100644 index 0000000..a609827 --- /dev/null +++ b/cpu/mpc512x/speed.c @@ -0,0 +1,135 @@ +/* + * (C) Copyright 2000-2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 2004-2006 Freescale Semiconductor, 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 + * + * Based on the MPC83xx code. + */ + +#include <common.h> +#include <mpc512x.h> +#include <command.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int spmf_mult[] = { + 68, 1, 12, 16, + 20, 24, 28, 32, + 36, 40, 44, 48, + 52, 56, 60, 64 +}; + +static int cpmf_mult[][2] = { + {0, 1}, {0, 1}, /* 0 and 1 are not valid */ + {1, 1}, {3, 2}, + {2, 1}, {5, 2}, + {3, 1}, {7, 2}, + {0, 1}, {0, 1}, /* and all above 7 are not valid too */ + {0, 1}, {0, 1}, + {0, 1}, {0, 1}, + {0, 1}, {0, 1} +}; + +static int sys_dividors[][2] = { + {2, 1}, {5, 2}, {3, 1}, {7, 2}, {4, 1}, + {9, 2}, {5, 1}, {7, 1}, {6, 1}, {8, 1}, + {9, 1}, {11, 1}, {10, 1}, {12, 1}, {13, 1}, + {15, 1}, {14, 1}, {16, 1}, {17, 1}, {19, 1}, + {18, 1}, {20, 1}, {21, 1}, {23, 1}, {22, 1}, + {24, 1}, {25, 1}, {27, 1}, {26, 1}, {28, 1}, + {29, 1}, {31, 1}, {30, 1}, {32, 1}, {33, 1} +}; + +int get_clocks (void) +{ + volatile immap_t *im = (immap_t *) CFG_IMMR; + u8 spmf; + u8 cpmf; + u8 sys_div; + u8 ips_div; + u32 ref_clk = CFG_MPC512X_CLKIN; + u32 spll; + u32 sys_clk; + u32 core_clk; + u32 csb_clk; + u32 ips_clk; + + if ((im->sysconf.immrbar & IMMRBAR_BASE_ADDR) != (u32) im) + return -1; + + spmf = (im->clk.spmr & SPMR_SPMF) >> SPMR_SPMF_SHIFT; + spll = ref_clk * spmf_mult[spmf]; + + sys_div = (im->clk.scfr[1] & SCFR2_SYS_DIV) >> SCFR2_SYS_DIV_SHIFT; + sys_clk = (spll * sys_dividors[sys_div][1]) / sys_dividors[sys_div][0]; + + csb_clk = sys_clk / 2; + + cpmf = (im->clk.spmr & SPMR_CPMF) >> SPMR_CPMF_SHIFT; + core_clk = (csb_clk * cpmf_mult[cpmf][0]) / cpmf_mult[cpmf][1]; + + ips_div = (im->clk.scfr[0] & SCFR1_IPS_DIV_MASK) >> SCFR1_IPS_DIV_SHIFT; + if (ips_div != 0) { + ips_clk = csb_clk / ips_div; + } else { + /* in case we cannot get a sane IPS divisor, fail gracefully */ + ips_clk = 0; + } + + gd->ipb_clk = ips_clk; + gd->csb_clk = csb_clk; + gd->cpu_clk = core_clk; + gd->bus_clk = csb_clk; + return 0; + +} + +/******************************************** + * get_bus_freq + * return system bus freq in Hz + *********************************************/ +ulong get_bus_freq (ulong dummy) +{ + return gd->csb_clk; +} + +int do_clocks (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + printf ("Clock configuration:\n"); + printf (" CPU: %4d MHz\n", gd->cpu_clk / 1000000); + printf (" Coherent System Bus: %4d MHz\n", gd->csb_clk / 1000000); + printf (" IPS Bus: %4d MHz\n", gd->ipb_clk / 1000000); + printf (" DDR: %4d MHz\n", 2 * gd->csb_clk / 1000000); + return 0; +} + +U_BOOT_CMD(clocks, 1, 0, do_clocks, + "clocks - print clock configuration\n", + " clocks\n" +); + +int prt_mpc512x_clks (void) +{ + do_clocks (NULL, 0, 0, NULL); + return (0); +} diff --git a/cpu/mpc512x/start.S b/cpu/mpc512x/start.S new file mode 100644 index 0000000..244c69b --- /dev/null +++ b/cpu/mpc512x/start.S @@ -0,0 +1,780 @@ +/* + * 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, 2007 Wolfgang Denk <wd@denx.de> + * Copyright Freescale Semiconductor, Inc. 2004, 2006. All rights reserved. + * + * 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 + * + * Based on the MPC83xx code. + */ + +/* + * U-Boot - Startup Code for MPC512x based Embedded Boards + */ + +#include <config.h> +#include <mpc512x.h> +#include <version.h> + +#define CONFIG_521X 1 /* needed for Linux kernel header files*/ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "MPC512X" +#endif + +/* + * Floating Point enable, Machine Check and Recoverable Interr. + */ +#undef MSR_KERNEL +#ifdef DEBUG +#define MSR_KERNEL (MSR_FP|MSR_RI) +#else +#define MSR_KERNEL (MSR_FP|MSR_ME|MSR_RI) +#endif + +/* Macros for manipulating CSx_START/STOP */ +#define START_REG(start) ((start) >> 16) +#define STOP_REG(start, size) (((start) + (size) - 1) >> 16) + +/* + * 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(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * Magic number and version string + */ + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", __DATE__, " - ", __TIME__, ")" + .ascii " ", CONFIG_IDENT_STRING, "\0" + +/* + * Vector Table + */ + .text + . = EXC_OFF_SYS_RESET + + .globl _start + /* Start from here after reset/power on */ +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, UnknownException) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + 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 */ + rlwimi r20,r23,0,25,25 /* copy IP 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 */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + rlwimi r20,r23,0,25,25 /* copy IP 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 + +/* Floating Point Unit unavailable exception */ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + +/* Decrementer */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + +/* Critical interrupt */ + STD_EXCEPTION(0xa00, Critical, UnknownException) + +/* System Call */ + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + +/* Trace interrupt */ + STD_EXCEPTION(0xd00, Trace, UnknownException) + +/* Performance Monitor interrupt */ + STD_EXCEPTION(0xf00, PerfMon, UnknownException) + +/* Intruction Translation Miss */ + STD_EXCEPTION(0x1000, InstructionTLBMiss, UnknownException) + +/* Data Load Translation Miss */ + STD_EXCEPTION(0x1100, DataLoadTLBMiss, UnknownException) + +/* Data Store Translation Miss */ + STD_EXCEPTION(0x1200, DataStoreTLBMiss, UnknownException) + +/* Instruction Address Breakpoint */ + STD_EXCEPTION(0x1300, InstructionAddrBreakpoint, DebugException) + +/* System Management interrupt */ + STD_EXCEPTION(0x1400, SystemMgmtInterrupt, UnknownException) + + .globl _end_of_vectors +_end_of_vectors: + + . = 0x3000 +boot_cold: + /* Save msr contents */ + mfmsr r5 + + /* Set IMMR area to our preferred location */ + lis r4, CONFIG_DEFAULT_IMMR@h + lis r3, CFG_IMMR@h + ori r3, r3, CFG_IMMR@l + stw r3, IMMRBAR(r4) + mtspr MBAR, r3 /* IMMRBAR is mirrored into the MBAR SPR (311) */ + + /* Initialise the machine */ + bl cpu_early_init + + /* + * Set up Local Access Windows: + * + * 1) Boot/CS0 (boot FLASH) + * 2) On-chip SRAM (initial stack purposes) + */ + + /* Boot CS/CS0 window range */ + lis r3, CFG_IMMR@h + ori r3, r3, CFG_IMMR@l + + lis r4, START_REG(CFG_FLASH_BASE) + ori r4, r4, STOP_REG(CFG_FLASH_BASE, CFG_FLASH_SIZE) + stw r4, LPCS0AW(r3) + + /* + * The SRAM window has a fixed size (256K), so only the start address + * is necessary + */ + lis r4, START_REG(CFG_SRAM_BASE) & 0xff00 + stw r4, SRAMBAR(r3) + + /* + * According to MPC5121e RM, configuring local access windows should + * be followed by a dummy read of the config register that was + * modified last and an isync + */ + lwz r4, SRAMBAR(r3) + isync + + /* + * Set configuration of the Boot/CS0, the SRAM window does not have a + * config register so no params can be set for it + */ + lis r3, (CFG_IMMR + LPC_OFFSET)@h + ori r3, r3, (CFG_IMMR + LPC_OFFSET)@l + + lis r4, CFG_CS0_CFG@h + ori r4, r4, CFG_CS0_CFG@l + stw r4, CS0_CONFIG(r3) + + /* Master enable all CS's */ + lis r4, CS_CTRL_ME@h + ori r4, r4, CS_CTRL_ME@l + stw r4, CS_CTRL(r3) + + lis r4, (CFG_MONITOR_BASE)@h + ori r4, r4, (CFG_MONITOR_BASE)@l + addi r5, r4, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r5 + blr + +in_flash: + 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 & stack humble */ + /*------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + lis r3, CFG_IMMR@h + /* run low-level CPU init code (in Flash) */ + bl cpu_init_f + + /* r3: BOOTFLAG */ + mr r3, r21 + /* run 1st part of board init code (in Flash) */ + bl board_init_f + + /* NOTREACHED - board_init_f() does not return */ + +/* + * 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) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + 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 + +/* + * This code initialises the machine, it expects original MSR contents to be in r5. + */ +cpu_early_init: + /* Initialize machine status; enable machine check interrupt */ + /*-----------------------------------------------------------*/ + + li r3, MSR_KERNEL /* Set ME and RI flags */ + rlwimi r3, r5, 0, 25, 25 /* preserve IP bit */ +#ifdef DEBUG + rlwimi r3, r5, 0, 21, 22 /* debugger might set SE, BE bits */ +#endif + mtmsr r3 + SYNC + mtspr SRR1, r3 /* Mirror current MSR state in SRR1 */ + + lis r3, CFG_IMMR@h + +#if defined(CONFIG_WATCHDOG) + /* Initialise the watchdog and reset it */ + /*--------------------------------------*/ + lis r4, CFG_WATCHDOG_VALUE + ori r4, r4, (SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR) + stw r4, SWCRR(r3) + + /* reset */ + li r4, 0x556C + sth r4, SWSRR@l(r3) + li r4, 0x0 + ori r4, r4, 0xAA39 + sth r4, SWSRR@l(r3) +#else + /* Disable the watchdog */ + /*----------------------*/ + lwz r4, SWCRR(r3) + /* + * Check to see if it's enabled for disabling: once disabled by s/w + * it's not possible to re-enable it + */ + andi. r4, r4, 0x4 + beq 1f + xor r4, r4, r4 + stw r4, SWCRR(r3) +1: +#endif /* CONFIG_WATCHDOG */ + + /* Initialize the Hardware Implementation-dependent Registers */ + /* HID0 also contains cache control */ + /*------------------------------------------------------*/ + lis r3, CFG_HID0_INIT@h + ori r3, r3, CFG_HID0_INIT@l + SYNC + mtspr HID0, r3 + + lis r3, CFG_HID0_FINAL@h + ori r3, r3, CFG_HID0_FINAL@l + SYNC + mtspr HID0, r3 + + lis r3, CFG_HID2@h + ori r3, r3, CFG_HID2@l + SYNC + mtspr HID2, r3 + sync + blr + + +/* Cache functions. + * + * Note: requires that all cache bits in + * HID0 are in the low half word. + */ + .globl icache_enable +icache_enable: + mfspr r3, HID0 + ori r3, r3, HID0_ICE + lis r4, 0 + ori r4, r4, HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets enable and invalidate, clears lock */ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_disable +icache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_ICE|HID0_ILOCK + andc r3, r3, r4 + ori r4, r3, HID0_ICFI + isync + mtspr HID0, r4 /* sets invalidate, clears enable and lock*/ + isync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + rlwinm r3, r3, (31 - HID0_ICE_SHIFT + 1), 31, 31 + blr + + .globl dcache_enable +dcache_enable: + mfspr r3, HID0 + li r5, HID0_DCFI|HID0_DLOCK + andc r3, r3, r5 + mtspr HID0, r3 /* no invalidate, unlock */ + ori r3, r3, HID0_DCE + ori r5, r3, HID0_DCFI + mtspr HID0, r5 /* enable + invalidate */ + mtspr HID0, r3 /* enable */ + sync + blr + + .globl dcache_disable +dcache_disable: + mfspr r3, HID0 + lis r4, 0 + ori r4, r4, HID0_DCE|HID0_DLOCK + andc r3, r3, r4 + ori r4, r3, HID0_DCI + sync + mtspr HID0, r4 /* sets invalidate, clears enable and lock */ + sync + mtspr HID0, r3 /* clears invalidate */ + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + rlwinm r3, r3, (31 - HID0_DCE_SHIFT + 1), 31, 31 + blr + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + +/*------------------------------------------------------------------------------- */ +/* Function: ppcDcbf */ +/* Description: Data Cache block flush */ +/* Input: r3 = effective address */ +/* Output: none. */ +/*------------------------------------------------------------------------------- */ + .globl ppcDcbf +ppcDcbf: + dcbf r0,r3 + blr + +/*------------------------------------------------------------------------------- */ +/* Function: ppcDcbi */ +/* Description: Data Cache block Invalidate */ +/* Input: r3 = effective address */ +/* Output: none. */ +/*------------------------------------------------------------------------------- */ + .globl ppcDcbi +ppcDcbi: + dcbi r0,r3 + blr + +/*-------------------------------------------------------------------------- + * Function: ppcDcbz + * Description: Data Cache block zero. + * Input: r3 = effective address + * Output: none. + *-------------------------------------------------------------------------- */ + + .globl ppcDcbz +ppcDcbz: + dcbz r0,r3 + blr + + .globl ppcDWstore +ppcDWstore: + lfd 1, 0(r4) + stfd 1, 0(r3) + blr + + .globl ppcDWload +ppcDWload: + lfd 1, 0(r3) + stfd 1, 0(r4) + 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 */ + lis r4, CFG_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CFG_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + 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 + /* then 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) + + /* copy */ +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + + addi r0,r5,3 + srwi. r0,r0,2 + mtctr r0 + la r8,-4(r4) + la r7,-4(r3) + + /* and compare */ +20: lwzu r20,4(r8) + lwzu r21,4(r7) + xor. r22, r20, r21 + bne 30f + bdnz 20b + b 4f + + /* compare failed */ +30: li r3, 0 + blr + +2: slwi r0,r0,2 /* re copy in reverse order ... y do we needed it? */ + 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_start) + 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 + bne 5b +6: + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * 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) + + li r9, 0x100 /* reset vector at 0x100 */ + + 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_Trace - _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 + + mfmsr r3 /* now that the vectors have */ + lis r7, MSR_IP@h /* relocated into low memory */ + ori r7, r7, MSR_IP@l /* MSR[IP] can be turned off */ + andc r3, r3, r7 /* (if it was on) */ + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + + 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 diff --git a/cpu/mpc512x/traps.c b/cpu/mpc512x/traps.c new file mode 100644 index 0000000..40281a2 --- /dev/null +++ b/cpu/mpc512x/traps.c @@ -0,0 +1,205 @@ +/* + * (C) Copyright 2000 - 2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * 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 + * + * Derived from the MPC83xx code. + */ + +/* + * This file handles the architecture-dependent parts of hardware + * exceptions + */ + +#include <common.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +extern unsigned long search_exception_table(unsigned long); + +#define END_OF_MEM (gd->bd->bi_memstart + gd->bd->bi_memsize) + +/* + * Trap & Exception support + */ + +void +print_backtrace (unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + puts ("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + putc ('\n'); + printf ("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *) *sp; + } + putc ('\n'); +} + +void show_regs (struct pt_regs * regs) +{ + int i; + + printf ("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf ("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr & MSR_EE ? 1 : 0, regs->msr & MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr & MSR_ME ? 1 : 0, + regs->msr & MSR_IR ? 1 : 0, + regs->msr & MSR_DR ? 1 : 0); + + putc ('\n'); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) { + printf ("GPR%02d: ", i); + } + + printf ("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) { + putc ('\n'); + } + } +} + + +void +_exception (int signr, struct pt_regs *regs) +{ + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + panic ("Exception at pc %lx signal %d", regs->nip,signr); +} + + +void +MachineCheckException (struct pt_regs *regs) +{ + unsigned long fixup; + + if ((fixup = search_exception_table (regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + puts ("Machine check.\nCaused by (from msr): "); + printf ("regs %p ",regs); + switch (regs->msr & 0x00FF0000) { + case (0x80000000 >> 10): + puts ("Instruction cache parity signal\n"); + break; + case (0x80000000 >> 11): + puts ("Data cache parity signal\n"); + break; + case (0x80000000 >> 12): + puts ("Machine check signal\n"); + break; + case (0x80000000 >> 13): + puts ("Transfer error ack signal\n"); + break; + case (0x80000000 >> 14): + puts ("Data parity signal\n"); + break; + case (0x80000000 >> 15): + puts ("Address parity signal\n"); + break; + default: + puts ("Unknown values in msr\n"); + } + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + + panic ("machine check"); +} + +void +AlignmentException (struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + panic ("Alignment Exception"); +} + +void +ProgramCheckException (struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + panic ("Program Check Exception"); +} + +void +SoftEmuException (struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *)regs->gpr[1]); + panic ("Software Emulation Exception"); +} + + +void +UnknownException (struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf ("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception (0, regs); +} + +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) +extern void do_bedbug_breakpoint (struct pt_regs *); +#endif + +void +DebugException (struct pt_regs *regs) +{ + printf ("Debugger trap at @ %lx\n", regs->nip ); + show_regs (regs); +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) + do_bedbug_breakpoint (regs); +#endif +} |