diff options
Diffstat (limited to 'arch/ppc/cpu/mpc5xx')
-rw-r--r-- | arch/ppc/cpu/mpc5xx/Makefile | 59 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/config.mk | 36 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/cpu.c | 171 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/cpu_init.c | 123 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/interrupts.c | 207 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/serial.c | 170 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/speed.c | 67 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/spi.c | 412 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/start.S | 576 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/traps.c | 227 | ||||
-rw-r--r-- | arch/ppc/cpu/mpc5xx/u-boot.lds | 137 |
11 files changed, 2185 insertions, 0 deletions
diff --git a/arch/ppc/cpu/mpc5xx/Makefile b/arch/ppc/cpu/mpc5xx/Makefile new file mode 100644 index 0000000000..f2ebb9a123 --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/Makefile @@ -0,0 +1,59 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2003 +# Martin Winistoerfer, martinwinistoerfer@gmx.ch. +# +# 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 +# + +# +# File: arch/ppc/cpu/mpc5xx/Makefile +# +# Discription: Makefile to build mpc5xx cpu configuration. +# Will include top config.mk which itselfs +# uses the definitions made in arch/ppc/cpu/mpc5xx/config.mk +# + + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +START = start.o +COBJS = serial.o cpu.o cpu_init.o interrupts.o traps.o speed.o spi.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/arch/ppc/cpu/mpc5xx/config.mk b/arch/ppc/cpu/mpc5xx/config.mk new file mode 100644 index 0000000000..e0b0ce1fd5 --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/config.mk @@ -0,0 +1,36 @@ +# +# (C) Copyright 2003 +# Martin Winistoerfer, martinwinistoerfer@gmx.ch. +# +# 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 +# + +# +# File: config.mk +# +# Discription: compiler flags and make definitions +# + + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_5xx -ffixed-r2 -mpowerpc -msoft-float + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/ppc/cpu/mpc5xx/u-boot.lds diff --git a/arch/ppc/cpu/mpc5xx/cpu.c b/arch/ppc/cpu/mpc5xx/cpu.c new file mode 100644 index 0000000000..7fffebcc1e --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/cpu.c @@ -0,0 +1,171 @@ +/* + * (C) Copyright 2003 + * Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: cpu.c + * + * Discription: Some cpu specific function for watchdog, + * cpu version test, clock setting ... + * + */ + + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc5xx.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if (defined(CONFIG_MPC555)) +# define ID_STR "MPC555/556" + +/* + * Check version of cpu with Processor Version Register (PVR) + */ +static int check_cpu_version (long clock, uint pvr, uint immr) +{ + char buf[32]; + /* The highest 16 bits should be 0x0002 for a MPC555/556 */ + if ((pvr >> 16) == 0x0002) { + printf (" " ID_STR " Version %x", (pvr >> 16)); + printf (" at %s MHz:", strmhz (buf, clock)); + } else { + printf ("Not supported cpu version"); + return -1; + } + return 0; +} +#endif /* CONFIG_MPC555 */ + + +/* + * Check version of mpc5xx + */ +int checkcpu (void) +{ + ulong clock = gd->cpu_clk; + uint immr = get_immr (0); /* Return full IMMR contents */ + uint pvr = get_pvr (); /* Retrieve PVR register */ + + puts ("CPU: "); + + return check_cpu_version (clock, pvr, immr); +} + +/* + * Called by macro WATCHDOG_RESET + */ +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts (); + + reset_5xx_watchdog ((immap_t *) CONFIG_SYS_IMMR); + if (re_enable) + enable_interrupts (); +} + +/* + * Will clear software reset + */ +void reset_5xx_watchdog (volatile immap_t * immr) +{ + /* Use the MPC5xx Internal Watchdog */ + immr->im_siu_conf.sc_swsr = 0x556c; /* Prevent SW time-out */ + immr->im_siu_conf.sc_swsr = 0xaa39; +} + +#endif /* CONFIG_WATCHDOG */ + + +/* + * Get timebase clock frequency + */ +unsigned long get_tbclk (void) +{ + volatile immap_t *immr = (volatile immap_t *) CONFIG_SYS_IMMR; + ulong oscclk, factor; + + if (immr->im_clkrst.car_sccr & SCCR_TBS) { + return (gd->cpu_clk / 16); + } + + factor = (((CONFIG_SYS_PLPRCR) & PLPRCR_MF_MSK) >> PLPRCR_MF_SHIFT) + 1; + + oscclk = gd->cpu_clk / factor; + + if ((immr->im_clkrst.car_sccr & SCCR_RTSEL) == 0 || factor > 2) { + return (oscclk / 4); + } + return (oscclk / 16); +} + +void dcache_enable (void) +{ + return; +} + +void dcache_disable (void) +{ + return; +} + +int dcache_status (void) +{ + return 0; /* always off */ +} + +/* + * Reset board + */ +int do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ +#if defined(CONFIG_PATI) + volatile ulong *addr = (ulong *) CONFIG_SYS_RESET_ADDRESS; + *addr = 1; +#else + ulong addr; + + /* Interrupts off, enable reset */ + __asm__ volatile (" mtspr 81, %r0 \n\t" + " mfmsr %r3 \n\t" + " rlwinm %r31,%r3,0,25,23\n\t" + " mtmsr %r31 \n\t"); + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ +#ifdef CONFIG_SYS_RESET_ADDRESS + addr = CONFIG_SYS_RESET_ADDRESS; +#else + /* + * note: when CONFIG_SYS_MONITOR_BASE points to a RAM address, CONFIG_SYS_MONITOR_BASE * - sizeof (ulong) is usually a valid address. Better pick an address + * known to be invalid on your system and assign it to CONFIG_SYS_RESET_ADDRESS. + * "(ulong)-1" used to be a good choice for many systems... + */ + addr = CONFIG_SYS_MONITOR_BASE - sizeof (ulong); +#endif + ((void (*) (void)) addr) (); +#endif /* #if defined(CONFIG_PATI) */ + return 1; +} diff --git a/arch/ppc/cpu/mpc5xx/cpu_init.c b/arch/ppc/cpu/mpc5xx/cpu_init.c new file mode 100644 index 0000000000..cb4bf84737 --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/cpu_init.c @@ -0,0 +1,123 @@ +/* + * (C) Copyright 2003 Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: cpu_init.c + * + * Discription: Contains initialisation functions to setup + * the cpu properly + * + */ + +#include <common.h> +#include <mpc5xx.h> +#include <watchdog.h> + +/* + * Setup essential cpu registers to run + */ +void cpu_init_f (volatile immap_t * immr) +{ + volatile memctl5xx_t *memctl = &immr->im_memctl; + ulong reg; + + /* SYPCR - contains watchdog control. This will enable watchdog */ + /* if CONFIG_WATCHDOG is set */ + immr->im_siu_conf.sc_sypcr = CONFIG_SYS_SYPCR; + +#if defined(CONFIG_WATCHDOG) + reset_5xx_watchdog (immr); +#endif + + /* SIUMCR - contains debug pin configuration */ + immr->im_siu_conf.sc_siumcr |= CONFIG_SYS_SIUMCR; + + /* Initialize timebase. Unlock TBSCRK */ + immr->im_sitk.sitk_tbscrk = KAPWR_KEY; + immr->im_sit.sit_tbscr = CONFIG_SYS_TBSCR; + + /* Full IMB bus speed */ + immr->im_uimb.uimb_umcr = CONFIG_SYS_UMCR; + + /* Time base and decrementer will be enables (TBE) */ + /* in init_timebase() in time.c called from board_init_f(). */ + + /* Initialize the PIT. Unlock PISCRK */ + immr->im_sitk.sitk_piscrk = KAPWR_KEY; + immr->im_sit.sit_piscr = CONFIG_SYS_PISCR; + +#if !defined(CONFIG_PATI) + /* PATI sest PLL in start.S */ + /* PLL (CPU clock) settings */ + immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + + /* If CONFIG_SYS_PLPRCR (set in the various *_config.h files) tries to + * set the MF field, then just copy CONFIG_SYS_PLPRCR over car_plprcr, + * otherwise OR in CONFIG_SYS_PLPRCR so we do not change the currentMF + * field value. + */ +#if ((CONFIG_SYS_PLPRCR & PLPRCR_MF_MSK) != 0) + reg = CONFIG_SYS_PLPRCR; /* reset control bits */ +#else + reg = immr->im_clkrst.car_plprcr; + reg &= PLPRCR_MF_MSK; /* isolate MF field */ + reg |= CONFIG_SYS_PLPRCR; /* reset control bits */ +#endif + immr->im_clkrst.car_plprcr = reg; + +#endif /* !defined(CONFIG_PATI) */ + + /* System integration timers. CONFIG_SYS_MASK has EBDF configuration */ + immr->im_clkrstk.cark_sccrk = KAPWR_KEY; + reg = immr->im_clkrst.car_sccr; + reg &= SCCR_MASK; + reg |= CONFIG_SYS_SCCR; + immr->im_clkrst.car_sccr = reg; + + /* Memory Controller */ + memctl->memc_br0 = CONFIG_SYS_BR0_PRELIM; + memctl->memc_or0 = CONFIG_SYS_OR0_PRELIM; + +#if (defined(CONFIG_SYS_OR1_PRELIM) && defined(CONFIG_SYS_BR1_PRELIM)) + memctl->memc_or1 = CONFIG_SYS_OR1_PRELIM; + memctl->memc_br1 = CONFIG_SYS_BR1_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR2_PRELIM) && defined(CONFIG_SYS_BR2_PRELIM) + memctl->memc_or2 = CONFIG_SYS_OR2_PRELIM; + memctl->memc_br2 = CONFIG_SYS_BR2_PRELIM; +#endif + +#if defined(CONFIG_SYS_OR3_PRELIM) && defined(CONFIG_SYS_BR3_PRELIM) + memctl->memc_or3 = CONFIG_SYS_OR3_PRELIM; + memctl->memc_br3 = CONFIG_SYS_BR3_PRELIM; +#endif + +} + +/* + * Initialize higher level parts of cpu + */ +int cpu_init_r (void) +{ + /* Nothing to do at the moment */ + return (0); +} diff --git a/arch/ppc/cpu/mpc5xx/interrupts.c b/arch/ppc/cpu/mpc5xx/interrupts.c new file mode 100644 index 0000000000..167543fcf5 --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/interrupts.c @@ -0,0 +1,207 @@ +/* + * (C) Copyright 2000-2002 Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * (C) Copyright 2003 Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: interrupt.c + * + * Discription: Contains interrupt routines needed by U-Boot + * + */ + +#include <common.h> +#include <command.h> +#include <mpc5xx.h> +#include <asm/processor.h> + +#if defined(CONFIG_PATI) +/* PATI uses IRQs for PCI doorbell */ +#undef NR_IRQS +#define NR_IRQS 16 +#endif + +struct interrupt_action { + interrupt_handler_t *handler; + void *arg; + int count; +}; + +static struct interrupt_action irq_vecs[NR_IRQS]; + +/* + * Initialise interrupts + */ + +int interrupt_init_cpu (ulong *decrementer_count) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int vec; + + /* Decrementer used here for status led */ + *decrementer_count = get_tbclk () / CONFIG_SYS_HZ; + + /* Disable all interrupts */ + immr->im_siu_conf.sc_simask = 0; + for (vec=0; vec<NR_IRQS; vec++) { + irq_vecs[vec].handler = NULL; + irq_vecs[vec].arg = NULL; + irq_vecs[vec].count = 0; + } + + return (0); +} + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + int irq; + ulong simask, newmask; + ulong vec, v_bit; + + /* + * read the SIVEC register and shift the bits down + * to get the irq number + */ + vec = immr->im_siu_conf.sc_sivec; + irq = vec >> 26; + v_bit = 0x80000000UL >> irq; + + /* + * Read Interrupt Mask Register and Mask Interrupts + */ + simask = immr->im_siu_conf.sc_simask; + newmask = simask & (~(0xFFFF0000 >> irq)); + immr->im_siu_conf.sc_simask = newmask; + + if (!(irq & 0x1)) { /* External Interrupt ? */ + ulong siel; + + /* + * Read Interrupt Edge/Level Register + */ + siel = immr->im_siu_conf.sc_siel; + + if (siel & v_bit) { /* edge triggered interrupt ? */ + /* + * Rewrite SIPEND Register to clear interrupt + */ + immr->im_siu_conf.sc_sipend = v_bit; + } + } + + if (irq_vecs[irq].handler != NULL) { + irq_vecs[irq].handler (irq_vecs[irq].arg); + } else { + printf ("\nBogus External Interrupt IRQ %d Vector %ld\n", + irq, vec); + /* turn off the bogus interrupt to avoid it from now */ + simask &= ~v_bit; + } + /* + * Re-Enable old Interrupt Mask + */ + immr->im_siu_conf.sc_simask = simask; +} + +/* + * Install and free an interrupt handler + */ +void irq_install_handler (int vec, interrupt_handler_t * handler, + void *arg) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + /* SIU interrupt */ + if (irq_vecs[vec].handler != NULL) { + printf ("SIU interrupt %d 0x%x\n", + vec, + (uint) handler); + } + irq_vecs[vec].handler = handler; + irq_vecs[vec].arg = arg; + immr->im_siu_conf.sc_simask |= 1 << (31 - vec); +#if 0 + printf ("Install SIU interrupt for vector %d ==> %p\n", + vec, handler); +#endif +} + +void irq_free_handler (int vec) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + /* SIU interrupt */ +#if 0 + printf ("Free CPM interrupt for vector %d\n", + vec); +#endif + immr->im_siu_conf.sc_simask &= ~(1 << (31 - vec)); + irq_vecs[vec].handler = NULL; + irq_vecs[vec].arg = NULL; +} + +/* + * Timer interrupt - gets called when bit 0 of DEC changes from + * 0. Decrementer is enabled with bit TBE in TBSCR. + */ +void timer_interrupt_cpu (struct pt_regs *regs) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +#if 0 + printf ("*** Timer Interrupt *** "); +#endif + /* Reset Timer Status Bit and Timers Interrupt Status */ + immr->im_clkrstk.cark_plprcrk = KAPWR_KEY; + __asm__ ("nop"); + immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS | PLPRCR_TMIST; + + return; +} + +#if defined(CONFIG_CMD_IRQ) +/******************************************************************************* + * + * irqinfo - print information about IRQs + * + */ +int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int vec; + + printf ("\nInterrupt-Information:\n"); + printf ("Nr Routine Arg Count\n"); + + for (vec=0; vec<NR_IRQS; vec++) { + if (irq_vecs[vec].handler != NULL) { + printf ("%02d %08lx %08lx %d\n", + vec, + (ulong)irq_vecs[vec].handler, + (ulong)irq_vecs[vec].arg, + irq_vecs[vec].count); + } + } + return 0; +} + + +#endif diff --git a/arch/ppc/cpu/mpc5xx/serial.c b/arch/ppc/cpu/mpc5xx/serial.c new file mode 100644 index 0000000000..88c6db81cb --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/serial.c @@ -0,0 +1,170 @@ +/* + * (C) Copyright 2003 + * Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: serial.c + * + * Discription: Serial interface driver for SCI1 and SCI2. + * Since this code will be called from ROM use + * only non-static local variables. + * + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc5xx.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Local function prototypes + */ + +static int ready_to_send(void); + +/* + * Minimal global serial functions needed to use one of the SCI modules. + */ + +int serial_init (void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + + serial_setbrg(); + +#if defined(CONFIG_5xx_CONS_SCI1) + /* 10-Bit, 1 start bit, 8 data bit, no parity, 1 stop bit */ + immr->im_qsmcm.qsmcm_scc1r1 = SCI_M_10; + immr->im_qsmcm.qsmcm_scc1r1 = SCI_TE | SCI_RE; +#else + immr->im_qsmcm.qsmcm_scc2r1 = SCI_M_10; + immr->im_qsmcm.qsmcm_scc2r1 = SCI_TE | SCI_RE; +#endif + return 0; +} + +void serial_putc(const char c) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + + /* Test for completition */ + if(ready_to_send()) { +#if defined(CONFIG_5xx_CONS_SCI1) + immr->im_qsmcm.qsmcm_sc1dr = (short)c; +#else + immr->im_qsmcm.qsmcm_sc2dr = (short)c; +#endif + if(c == '\n') { + if(ready_to_send()); +#if defined(CONFIG_5xx_CONS_SCI1) + immr->im_qsmcm.qsmcm_sc1dr = (short)'\r'; +#else + immr->im_qsmcm.qsmcm_sc2dr = (short)'\r'; +#endif + } + } +} + +int serial_getc(void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile short status; + unsigned char tmp; + + /* New data ? */ + do { +#if defined(CONFIG_5xx_CONS_SCI1) + status = immr->im_qsmcm.qsmcm_sc1sr; +#else + status = immr->im_qsmcm.qsmcm_sc2sr; +#endif + +#if defined(CONFIG_WATCHDOG) + reset_5xx_watchdog (immr); +#endif + } while ((status & SCI_RDRF) == 0); + + /* Read data */ +#if defined(CONFIG_5xx_CONS_SCI1) + tmp = (unsigned char)(immr->im_qsmcm.qsmcm_sc1dr & SCI_SCXDR_MK); +#else + tmp = (unsigned char)( immr->im_qsmcm.qsmcm_sc2dr & SCI_SCXDR_MK); +#endif + return tmp; +} + +int serial_tstc() +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + short status; + + /* New data character ? */ +#if defined(CONFIG_5xx_CONS_SCI1) + status = immr->im_qsmcm.qsmcm_sc1sr; +#else + status = immr->im_qsmcm.qsmcm_sc2sr; +#endif + return (status & SCI_RDRF); +} + +void serial_setbrg (void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + short scxbr; + + /* Set baudrate */ + scxbr = (gd->cpu_clk / (32 * gd->baudrate)); +#if defined(CONFIG_5xx_CONS_SCI1) + immr->im_qsmcm.qsmcm_scc1r0 = (scxbr & SCI_SCXBR_MK); +#else + immr->im_qsmcm.qsmcm_scc2r0 = (scxbr & SCI_SCXBR_MK); +#endif +} + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc(*s); + ++s; + } +} + +int ready_to_send(void) +{ + volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + volatile short status; + + do { +#if defined(CONFIG_5xx_CONS_SCI1) + status = immr->im_qsmcm.qsmcm_sc1sr; +#else + status = immr->im_qsmcm.qsmcm_sc2sr; +#endif + +#if defined(CONFIG_WATCHDOG) + reset_5xx_watchdog (immr); +#endif + } while ((status & SCI_TDRE) == 0); + return 1; + +} diff --git a/arch/ppc/cpu/mpc5xx/speed.c b/arch/ppc/cpu/mpc5xx/speed.c new file mode 100644 index 0000000000..ea5c1dead5 --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/speed.c @@ -0,0 +1,67 @@ +/* + * (C) Copyright 2003 + * Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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, + */ + +/* + * File: speed.c + * + * Discription: Provides cpu speed calculation + * + */ + +#include <common.h> +#include <mpc5xx.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Get cpu and bus clock + */ +int get_clocks (void) +{ + volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +#ifndef CONFIG_5xx_GCLK_FREQ + uint divf = (immr->im_clkrst.car_plprcr & PLPRCR_DIVF_MSK); + uint mf = ((immr->im_clkrst.car_plprcr & PLPRCR_MF_MSK) >> PLPRCR_MF_SHIFT); + ulong vcoout; + + vcoout = (CONFIG_SYS_OSC_CLK / (divf + 1)) * (mf + 1) * 2; + if(immr->im_clkrst.car_plprcr & PLPRCR_CSRC_MSK) { + gd->cpu_clk = vcoout / (2^(((immr->im_clkrst.car_sccr & SCCR_DFNL_MSK) >> SCCR_DFNL_SHIFT) + 1)); + } else { + gd->cpu_clk = vcoout / (2^(immr->im_clkrst.car_sccr & SCCR_DFNH_MSK)); + } + +#else /* CONFIG_5xx_GCLK_FREQ */ + gd->bus_clk = CONFIG_5xx_GCLK_FREQ; +#endif /* CONFIG_5xx_GCLK_FREQ */ + + if ((immr->im_clkrst.car_sccr & SCCR_EBDF11) == 0) { + /* No Bus Divider active */ + gd->bus_clk = gd->cpu_clk; + } else { + /* CLKOUT is GCLK / 2 */ + gd->bus_clk = gd->cpu_clk / 2; + } + return (0); +} diff --git a/arch/ppc/cpu/mpc5xx/spi.c b/arch/ppc/cpu/mpc5xx/spi.c new file mode 100644 index 0000000000..3ca15ea838 --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/spi.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2001 Navin Boppuri / Prashant Patel + * <nboppuri@trinetcommunication.com>, + * <pmpatel@trinetcommunication.com> + * Copyright (c) 2001 Gerd Mennchen <Gerd.Mennchen@icn.siemens.de> + * Copyright (c) 2001 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 + */ + +/* + * MPC5xx CPM SPI interface. + * + * Parts of this code are probably not portable and/or specific to + * the board which I used for the tests. Please send fixes/complaints + * to wd@denx.de + * + * Ported to MPC5xx + * Copyright (c) 2003 Denis Peter, MPL AG Switzerland, d.petr@mpl.ch. + */ + +#include <common.h> +#include <mpc5xx.h> +#include <asm/5xx_immap.h> +#include <linux/ctype.h> +#include <malloc.h> +#include <post.h> +#include <net.h> + +#if defined(CONFIG_SPI) + +#undef DEBUG + +#define SPI_EEPROM_WREN 0x06 +#define SPI_EEPROM_RDSR 0x05 +#define SPI_EEPROM_READ 0x03 +#define SPI_EEPROM_WRITE 0x02 + + +#ifdef DEBUG + +#define DPRINT(a) printf a; +/* ----------------------------------------------- + * Helper functions to peek into tx and rx buffers + * ----------------------------------------------- */ +static const char * const hex_digit = "0123456789ABCDEF"; + +static char quickhex (int i) +{ + return hex_digit[i]; +} + +static void memdump (void *pv, int num) +{ + int i; + unsigned char *pc = (unsigned char *) pv; + + for (i = 0; i < num; i++) + printf ("%c%c ", quickhex (pc[i] >> 4), quickhex (pc[i] & 0x0f)); + printf ("\t"); + for (i = 0; i < num; i++) + printf ("%c", isprint (pc[i]) ? pc[i] : '.'); + printf ("\n"); +} +#else /* !DEBUG */ + +#define DPRINT(a) + +#endif /* DEBUG */ + +/* ------------------- + * Function prototypes + * ------------------- */ +void spi_init (void); + +ssize_t spi_read (uchar *, int, uchar *, int); +ssize_t spi_write (uchar *, int, uchar *, int); +ssize_t spi_xfer (size_t); + + +/* ************************************************************************** + * + * Function: spi_init_f + * + * Description: Init SPI-Controller (ROM part) + * + * return: --- + * + * *********************************************************************** */ + +void spi_init_f (void) +{ + int i; + + volatile immap_t *immr; + volatile qsmcm5xx_t *qsmcm; + + immr = (immap_t *) CONFIG_SYS_IMMR; + qsmcm = (qsmcm5xx_t *)&immr->im_qsmcm; + + qsmcm->qsmcm_qsmcr = 0; /* all accesses enabled */ + qsmcm->qsmcm_qspi_il = 0; /* lowest IRQ */ + + /* -------------------------------------------- + * GPIO or per. Function + * PQSPAR[00] = 0 reserved + * PQSPAR[01] = 1 [0x4000] -> PERI: (SPICS3) + * PQSPAR[02] = 0 [0x0000] -> GPIO + * PQSPAR[03] = 0 [0x0000] -> GPIO + * PQSPAR[04] = 1 [0x0800] -> PERI: (SPICS0) + * PQSPAR[05] = 0 reseved + * PQSPAR[06] = 1 [0x0200] -> PERI: (SPIMOSI) + * PQSPAR[07] = 1 [0x0100] -> PERI: (SPIMISO) + * -------------------------------------------- */ + qsmcm->qsmcm_pqspar = 0x3 | (CONFIG_SYS_SPI_CS_USED << 3); + + /* -------------------------------------------- + * DDRQS[00] = 0 reserved + * DDRQS[01] = 1 [0x0040] -> SPICS3 Output + * DDRQS[02] = 0 [0x0000] -> GPIO Output + * DDRQS[03] = 0 [0x0000] -> GPIO Output + * DDRQS[04] = 1 [0x0008] -> SPICS0 Output + * DDRQS[05] = 1 [0x0004] -> SPICLK Output + * DDRQS[06] = 1 [0x0002] -> SPIMOSI Output + * DDRQS[07] = 0 [0x0001] -> SPIMISO Input + * -------------------------------------------- */ + qsmcm->qsmcm_ddrqs = 0x7E; + /* -------------------------------------------- + * Base state for used SPI CS pins, if base = 0 active must be 1 + * PORTQS[00] = 0 reserved + * PORTQS[01] = 0 reserved + * PORTQS[02] = 0 reserved + * PORTQS[03] = 0 reserved + * PORTQS[04] = 0 [0x0000] RxD2 + * PORTQS[05] = 1 [0x0400] TxD2 + * PORTQS[06] = 0 [0x0000] RxD1 + * PORTQS[07] = 1 [0x0100] TxD1 + * PORTQS[08] = 0 reserved + * PORTQS[09] = 0 [0x0000] -> SPICS3 Base Output + * PORTQS[10] = 0 [0x0000] -> SPICS2 Base Output + * PORTQS[11] = 0 [0x0000] -> SPICS1 Base Output + * PORTQS[12] = 0 [0x0000] -> SPICS0 Base Output + * PORTQS[13] = 0 [0x0004] -> SPICLK Output + * PORTQS[14] = 0 [0x0002] -> SPIMOSI Output + * PORTQS[15] = 0 [0x0001] -> SPIMISO Input + * -------------------------------------------- */ + qsmcm->qsmcm_portqs |= (CONFIG_SYS_SPI_CS_BASE << 3); + /* -------------------------------------------- + * Controll Register 0 + * SPCR0[00] = 1 (0x8000) Master + * SPCR0[01] = 0 (0x0000) Wired-Or + * SPCR0[2..5] = (0x2000) Bits per transfer (default 8) + * SPCR0[06] = 0 (0x0000) Normal polarity + * SPCR0[07] = 0 (0x0000) Normal Clock Phase + * SPCR0[08..15] = 14 1.4MHz + */ + qsmcm->qsmcm_spcr0=0xA00E; + /* -------------------------------------------- + * Controll Register 1 + * SPCR1[00] = 0 (0x0000) QSPI enabled + * SPCR1[1..7] = (0x7F00) Delay before Transfer + * SPCR1[8..15] = (0x0000) Delay After transfer (204.8usec@40MHz) + */ + qsmcm->qsmcm_spcr1=0x7F00; + /* -------------------------------------------- + * Controll Register 2 + * SPCR2[00] = 0 (0x0000) SPI IRQs Disabeld + * SPCR2[01] = 0 (0x0000) No Wrap around + * SPCR2[02] = 0 (0x0000) Wrap to 0 + * SPCR2[3..7] = (0x0000) End Queue pointer = 0 + * SPCR2[8..10] = 0 (0x0000) reserved + * SPCR2[11..15] = 0 (0x0000) NewQueue Address = 0 + */ + qsmcm->qsmcm_spcr2=0x0000; + /* -------------------------------------------- + * Controll Register 3 + * SPCR3[00..04] = 0 (0x0000) reserved + * SPCR3[05] = 0 (0x0000) Feedback disabled + * SPCR3[06] = 0 (0x0000) IRQ on HALTA & MODF disabled + * SPCR3[07] = 0 (0x0000) Not halted + */ + qsmcm->qsmcm_spcr3=0x00; + /* -------------------------------------------- + * SPSR (Controll Register 3) Read only/ reset Flags 08,09,10 + * SPCR3[08] = 1 (0x80) QSPI finished + * SPCR3[09] = 1 (0x40) Mode Fault Flag + * SPCR3[10] = 1 (0x20) HALTA + * SPCR3[11..15] = 0 (0x0000) Last executed command + */ + qsmcm->qsmcm_spsr=0xE0; + /*------------------------------------------- + * Setup RAM + */ + for(i=0;i<32;i++) { + qsmcm->qsmcm_recram[i]=0x0000; + qsmcm->qsmcm_tranram[i]=0x0000; + qsmcm->qsmcm_comdram[i]=0x00; + } + return; +} + +/* ************************************************************************** + * + * Function: spi_init_r + * Dummy, all initializations have been done in spi_init_r + * *********************************************************************** */ +void spi_init_r (void) +{ + return; + +} + +/**************************************************************************** + * Function: spi_write + **************************************************************************** */ +ssize_t short_spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ + int i,dlen; + volatile immap_t *immr; + volatile qsmcm5xx_t *qsmcm; + + immr = (immap_t *) CONFIG_SYS_IMMR; + qsmcm = (qsmcm5xx_t *)&immr->im_qsmcm; + for(i=0;i<32;i++) { + qsmcm->qsmcm_recram[i]=0x0000; + qsmcm->qsmcm_tranram[i]=0x0000; + qsmcm->qsmcm_comdram[i]=0x00; + } + qsmcm->qsmcm_tranram[0] = SPI_EEPROM_WREN; /* write enable */ + spi_xfer(1); + i=0; + qsmcm->qsmcm_tranram[i++] = SPI_EEPROM_WRITE; /* WRITE memory array */ + qsmcm->qsmcm_tranram[i++] = addr[0]; + qsmcm->qsmcm_tranram[i++] = addr[1]; + + for(dlen=0;dlen<len;dlen++) { + qsmcm->qsmcm_tranram[i+dlen] = buffer[dlen]; /* WRITE memory array */ + } + /* transmit it */ + spi_xfer(i+dlen); + /* ignore received data */ + for (i = 0; i < 1000; i++) { + qsmcm->qsmcm_tranram[0] = SPI_EEPROM_RDSR; /* read status */ + qsmcm->qsmcm_tranram[1] = 0; + spi_xfer(2); + if (!(qsmcm->qsmcm_recram[1] & 1)) { + break; + } + udelay(1000); + } + if (i >= 1000) { + printf ("*** spi_write: Time out while writing!\n"); + } + return len; +} + +#define TRANSFER_LEN 16 + +ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ + int index,i,newlen; + uchar newaddr[2]; + int curraddr; + + curraddr=(addr[alen-2]<<8)+addr[alen-1]; + i=len; + index=0; + do { + newaddr[1]=(curraddr & 0xff); + newaddr[0]=((curraddr>>8) & 0xff); + if(i>TRANSFER_LEN) { + newlen=TRANSFER_LEN; + i-=TRANSFER_LEN; + } + else { + newlen=i; + i=0; + } + short_spi_write (newaddr, 2, &buffer[index], newlen); + index+=newlen; + curraddr+=newlen; + }while(i); + return (len); +} + +/**************************************************************************** + * Function: spi_read + **************************************************************************** */ +ssize_t short_spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ + int i; + volatile immap_t *immr; + volatile qsmcm5xx_t *qsmcm; + + immr = (immap_t *) CONFIG_SYS_IMMR; + qsmcm = (qsmcm5xx_t *)&immr->im_qsmcm; + + for(i=0;i<32;i++) { + qsmcm->qsmcm_recram[i]=0x0000; + qsmcm->qsmcm_tranram[i]=0x0000; + qsmcm->qsmcm_comdram[i]=0x00; + } + i=0; + qsmcm->qsmcm_tranram[i++] = (SPI_EEPROM_READ); /* READ memory array */ + qsmcm->qsmcm_tranram[i++] = addr[0] & 0xff; + qsmcm->qsmcm_tranram[i++] = addr[1] & 0xff; + spi_xfer(3 + len); + for(i=0;i<len;i++) { + *buffer++=(char)qsmcm->qsmcm_recram[i+3]; + } + return len; +} + +ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ + int index,i,newlen; + uchar newaddr[2]; + int curraddr; + + curraddr=(addr[alen-2]<<8)+addr[alen-1]; + i=len; + index=0; + do { + newaddr[1]=(curraddr & 0xff); + newaddr[0]=((curraddr>>8) & 0xff); + if(i>TRANSFER_LEN) { + newlen=TRANSFER_LEN; + i-=TRANSFER_LEN; + } + else { + newlen=i; + i=0; + } + short_spi_read (newaddr, 2, &buffer[index], newlen); + index+=newlen; + curraddr+=newlen; + }while(i); + return (len); +} + +/**************************************************************************** + * Function: spi_xfer + **************************************************************************** */ +ssize_t spi_xfer (size_t count) +{ + volatile immap_t *immr; + volatile qsmcm5xx_t *qsmcm; + int i; + int tm; + ushort status; + immr = (immap_t *) CONFIG_SYS_IMMR; + qsmcm = (qsmcm5xx_t *)&immr->im_qsmcm; + DPRINT (("*** spi_xfer entered count %d***\n",count)); + + /* Set CS for device */ + for(i=0;i<(count-1);i++) + qsmcm->qsmcm_comdram[i] = 0x80 | CONFIG_SYS_SPI_CS_ACT; /* CS3 is connected to the SPI EEPROM */ + + qsmcm->qsmcm_comdram[i] = CONFIG_SYS_SPI_CS_ACT; /* CS3 is connected to the SPI EEPROM */ + qsmcm->qsmcm_spcr2=((count-1)&0x1F)<<8; + + DPRINT (("*** spi_xfer: Bytes to be xferred: %d ***\n", count)); + + qsmcm->qsmcm_spsr=0xE0; /* clear all flags */ + + /* start spi transfer */ + DPRINT (("*** spi_xfer: Performing transfer ...\n")); + qsmcm->qsmcm_spcr1 |= 0x8000; /* Start transmit */ + + /* -------------------------------- + * Wait for SPI transmit to get out + * or time out (1 second = 1000 ms) + * -------------------------------- */ + for (tm=0; tm<1000; ++tm) { + status=qsmcm->qsmcm_spcr1; + if((status & 0x8000)==0) + break; + udelay (1000); + } + if (tm >= 1000) { + printf ("*** spi_xfer: Time out while xferring to/from SPI!\n"); + } +#ifdef DEBUG + printf ("\nspi_xfer: txbuf after xfer\n"); + memdump ((void *) qsmcm->qsmcm_tranram, 32); /* dump of txbuf before transmit */ + printf ("spi_xfer: rxbuf after xfer\n"); + memdump ((void *) qsmcm->qsmcm_recram, 32); /* dump of rxbuf after transmit */ + printf ("\nspi_xfer: commbuf after xfer\n"); + memdump ((void *) qsmcm->qsmcm_comdram, 32); /* dump of txbuf before transmit */ + printf ("\n"); +#endif + + return count; +} + +#endif /* CONFIG_SPI */ diff --git a/arch/ppc/cpu/mpc5xx/start.S b/arch/ppc/cpu/mpc5xx/start.S new file mode 100644 index 0000000000..0af879e391 --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/start.S @@ -0,0 +1,576 @@ +/* + * 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> + * Copyright (C) 2003 Martin Winistoerfer, martinwinistoerfer@gmx.ch. + * + * 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 + */ + +/* + * File: start.S + * + * Discription: startup code + * + */ + +#include <config.h> +#include <mpc5xx.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_5xx 1 /* needed for Linux kernel header files */ +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <linux/config.h> +#include <asm/processor.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't have a MMU. +*/ +#undef MSR_KERNEL +#define MSR_KERNEL ( MSR_ME | MSR_RI ) /* Machine Check and Recoverable Interr. */ + +/* + * Set up GOT: Global Offset Table + * + * Use r12 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 + +/* + * 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 " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start +_start: + mfspr r3, 638 + li r4, CONFIG_SYS_ISB /* Set ISB bit */ + or r3, r3, r4 + mtspr 638, r3 + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x20 + + .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 ME, RI flags */ + mtmsr r3 + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + /* Initialize debug port registers */ + /*----------------------------------------------------------------------*/ + xor r0, r0, r0 /* Clear R0 */ + mtspr LCTRL1, r0 /* Initialize debug port regs */ + mtspr LCTRL2, r0 + mtspr COUNTA, r0 + mtspr COUNTB, r0 + +#if defined(CONFIG_PATI) + /* the external flash access on PATI fails if programming the PLL to 40MHz. + * Copy the PLL programming code to the internal RAM and execute it + *----------------------------------------------------------------------*/ + lis r3, CONFIG_SYS_MONITOR_BASE@h + ori r3, r3, CONFIG_SYS_MONITOR_BASE@l + addi r3, r3, pll_prog_code_start - _start + EXC_OFF_SYS_RESET + + lis r4, CONFIG_SYS_INIT_RAM_ADDR@h + ori r4, r4, CONFIG_SYS_INIT_RAM_ADDR@l + mtlr r4 + addis r5,0,0x0 + ori r5,r5,((pll_prog_code_end - pll_prog_code_start) >>2) + mtctr r5 + addi r3, r3, -4 + addi r4, r4, -4 +0: + lwzu r0,4(r3) + stwu r0,4(r4) + bdnz 0b /* copy loop */ + blrl +#endif + + /* + * Calculate absolute address in FLASH and jump there + *----------------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_MONITOR_BASE@h + ori r3, r3, CONFIG_SYS_MONITOR_BASE@l + addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET + mtlr r3 + blr + +in_flash: + + /* Initialize some SPRs that are hard to access from C */ + /*----------------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_IMMR@h /* Pass IMMR as arg1 to C routine */ + lis r2, CONFIG_SYS_INIT_SP_ADDR@h + ori r1, r2, CONFIG_SYS_INIT_SP_ADDR@l /* Set up the stack in internal SRAM */ + /* Note: R0 is still 0 here */ + stwu r0, -4(r1) /* Clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* + * Disable serialized ifetch and show cycles + * (i.e. set processor to normal mode) for maximum + * performance. + */ + + li r2, 0x0007 + mtspr ICTRL, r2 + + /* Set up debug mode entry */ + + lis r2, CONFIG_SYS_DER@h + ori r2, r2, CONFIG_SYS_DER@l + mtspr DER, r2 + + /* 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(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* 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 + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + /* FPU on MPC5xx available. We will use it later. + */ + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + /* On the MPC8xx, this is a software emulation interrupt. It occurs + * for all unimplemented and illegal instructions. + */ + STD_EXCEPTION(0x1000, SoftEmu, SoftEmuException) + STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException) + STD_EXCEPTION(0x1300, InstructionTLBError, UnknownException) + STD_EXCEPTION(0x1400, DataTLBError, 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, DataBreakpoint, UnknownException) + STD_EXCEPTION(0x1d00, InstructionBreakpoint, DebugException) + STD_EXCEPTION(0x1e00, PeripheralBreakpoint, UnknownException) + STD_EXCEPTION(0x1f00, DevPortBreakpoint, UnknownException) + + + .globl _end_of_vectors +_end_of_vectors: + + + . = 0x2000 + +/* + * 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) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + 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 + + +/* + * unsigned int get_immr (unsigned int mask) + * + * return (mask ? (IMMR & mask) : IMMR); + */ + .globl get_immr +get_immr: + mr r4,r3 /* save mask */ + mfspr r3, IMMR /* IMMR */ + cmpwi 0,r4,0 /* mask != 0 ? */ + beq 4f + and r3,r3,r4 /* IMMR & mask */ +4: + 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 in SRAM */ + mr r9, r4 /* Save copy of global data pointer in SRAM */ + mr r10, r5 /* Save copy of monitor destination Address in SRAM */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, 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 4f /* 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 + +4: sync + 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, r12 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) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + 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: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +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 + +#if defined(CONFIG_PATI) +/* Program the PLL */ +pll_prog_code_start: + lis r4, (CONFIG_SYS_IMMR + 0x002fc384)@h + ori r4, r4, (CONFIG_SYS_IMMR + 0x002fc384)@l + lis r3, (0x55ccaa33)@h + ori r3, r3, (0x55ccaa33)@l + stw r3, 0(r4) + lis r4, (CONFIG_SYS_IMMR + 0x002fc284)@h + ori r4, r4, (CONFIG_SYS_IMMR + 0x002fc284)@l + lis r3, CONFIG_SYS_PLPRCR@h + ori r3, r3, CONFIG_SYS_PLPRCR@l + stw r3, 0(r4) + addis r3,0,0x0 + ori r3,r3,0xA000 + mtctr r3 +..spinlp: + bdnz ..spinlp /* spin loop */ + blr +pll_prog_code_end: + nop + blr +#endif diff --git a/arch/ppc/cpu/mpc5xx/traps.c b/arch/ppc/cpu/mpc5xx/traps.c new file mode 100644 index 0000000000..6882c21a41 --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/traps.c @@ -0,0 +1,227 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x0001000 + + +/* + * Print stack backtrace + */ +void print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +/* + * Print current registers + */ +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); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +/* + * General exception handler routine + */ +void _exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +/* + * Machine check exception handler routine + */ +void MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x000F0000) { + case (0x80000000>>12): + printf("Machine check signal\n"); + break; + case (0x80000000>>13): + printf("Transfer error ack signal\n"); + break; + case (0x80000000>>14): + printf("Data parity signal\n"); + break; + case (0x80000000>>15): + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +/* + * Alignment exception handler routine + */ +void AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_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"); +} + +/* + * Program check exception handler routine + */ +void ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_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"); +} + +/* + * Software emulation exception handler routine + */ +void SoftEmuException(struct pt_regs *regs) +{ +#if defined(CONFIG_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"); +} + + +/* + * Unknown exception handler routine + */ +void UnknownException(struct pt_regs *regs) +{ +#if defined(CONFIG_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); +} + +/* + * Debug exception handler routine + */ +void DebugException(struct pt_regs *regs) +{ + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} diff --git a/arch/ppc/cpu/mpc5xx/u-boot.lds b/arch/ppc/cpu/mpc5xx/u-boot.lds new file mode 100644 index 0000000000..55190c72d9 --- /dev/null +++ b/arch/ppc/cpu/mpc5xx/u-boot.lds @@ -0,0 +1,137 @@ +/* + * (C) Copyright 2001 Wolfgang Denk, DENX Software Engineering, wd@denx.de + * (C) Copyright 2003 Martin Winistoerfer, martinwinistoerfer@gmx.ch + * + * 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 + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + /* WARNING - the following is hand-optimized to fit within */ + /* the sector layout of our flash chips! XXX FIXME XXX */ + + arch/ppc/cpu/mpc5xx/start.o (.text) + + *(.text) + *(.got1) + } + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x00FF) & 0xFFFFFF00; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >>2; + __fixup_entries = (. - _FIXUP_TABLE_)>>2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(256); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(256); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + + _end = . ; + PROVIDE (end = .); +/* . = env_start; + .ppcenv : + { + common/env_embedded.o (.ppcenv) + } +*/ +} |