From c609719b8d1b2dca590e0ed499016d041203e403 Mon Sep 17 00:00:00 2001 From: wdenk Date: Sun, 3 Nov 2002 00:24:07 +0000 Subject: Initial revision --- cpu/arm720t/Makefile | 43 ++ cpu/arm720t/interrupts.c | 265 +++++++++ cpu/arm920t/Makefile | 43 ++ cpu/arm920t/interrupts.c | 304 ++++++++++ cpu/arm920t/serial.c | 200 +++++++ cpu/arm920t/speed.c | 96 ++++ cpu/mpc824x/cpu_init.c | 383 +++++++++++++ cpu/mpc824x/drivers/i2c/i2c1.c | 1228 ++++++++++++++++++++++++++++++++++++++++ cpu/mpc824x/pci.c | 78 +++ cpu/mpc824x/start.S | 835 +++++++++++++++++++++++++++ cpu/mpc8260/cpu_init.c | 264 +++++++++ cpu/mpc8260/i2c.c | 733 ++++++++++++++++++++++++ cpu/mpc8xx/cpu.c | 516 +++++++++++++++++ cpu/mpc8xx/fec.c | 710 +++++++++++++++++++++++ cpu/mpc8xx/spi.c | 571 +++++++++++++++++++ cpu/ppc4xx/405gp_enet.c | 867 ++++++++++++++++++++++++++++ cpu/ppc4xx/405gp_pci.c | 502 ++++++++++++++++ cpu/ppc4xx/cpu.c | 253 +++++++++ cpu/ppc4xx/i2c.c | 417 ++++++++++++++ cpu/ppc4xx/sdram.c | 191 +++++++ cpu/ppc4xx/speed.c | 308 ++++++++++ cpu/ppc4xx/vecnum.h | 100 ++++ cpu/sa1100/Makefile | 43 ++ cpu/xscale/serial.c | 143 +++++ cpu/xscale/start.S | 412 ++++++++++++++ 25 files changed, 9505 insertions(+) create mode 100644 cpu/arm720t/Makefile create mode 100644 cpu/arm720t/interrupts.c create mode 100644 cpu/arm920t/Makefile create mode 100644 cpu/arm920t/interrupts.c create mode 100644 cpu/arm920t/serial.c create mode 100644 cpu/arm920t/speed.c create mode 100644 cpu/mpc824x/cpu_init.c create mode 100644 cpu/mpc824x/drivers/i2c/i2c1.c create mode 100644 cpu/mpc824x/pci.c create mode 100644 cpu/mpc824x/start.S create mode 100644 cpu/mpc8260/cpu_init.c create mode 100644 cpu/mpc8260/i2c.c create mode 100644 cpu/mpc8xx/cpu.c create mode 100644 cpu/mpc8xx/fec.c create mode 100644 cpu/mpc8xx/spi.c create mode 100644 cpu/ppc4xx/405gp_enet.c create mode 100644 cpu/ppc4xx/405gp_pci.c create mode 100644 cpu/ppc4xx/cpu.c create mode 100644 cpu/ppc4xx/i2c.c create mode 100644 cpu/ppc4xx/sdram.c create mode 100644 cpu/ppc4xx/speed.c create mode 100644 cpu/ppc4xx/vecnum.h create mode 100644 cpu/sa1100/Makefile create mode 100644 cpu/xscale/serial.c create mode 100644 cpu/xscale/start.S (limited to 'cpu') diff --git a/cpu/arm720t/Makefile b/cpu/arm720t/Makefile new file mode 100644 index 0000000000..8c950daee8 --- /dev/null +++ b/cpu/arm720t/Makefile @@ -0,0 +1,43 @@ +# +# (C) Copyright 2000 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = lib$(CPU).a + +START = start.o +OBJS = serial.o interrupts.o cpu.o + +all: .depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) crv $@ $(OBJS) + +######################################################################### + +.depend: Makefile $(START:.o=.S) $(OBJS:.o=.c) + $(CC) -M $(CFLAGS) $(START:.o=.S) $(OBJS:.o=.c) > $@ + +sinclude .depend + +######################################################################### diff --git a/cpu/arm720t/interrupts.c b/cpu/arm720t/interrupts.c new file mode 100644 index 0000000000..52787fe835 --- /dev/null +++ b/cpu/arm720t/interrupts.c @@ -0,0 +1,265 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Alex Zuepke + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include + +#include + +extern void reset_cpu(ulong addr); + +/* we always count down the max. */ +#define TIMER_LOAD_VAL 0xffff + +/* macro to read the 16 bit timer */ +#define READ_TIMER (IO_TC1D & 0xffff) + +#ifdef CONFIG_USE_IRQ +/* enable IRQ/FIQ interrupts */ +void enable_interrupts (void) +{ + unsigned long temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "bic %0, %0, #0x80\n" + "msr cpsr_c, %0" + : "=r" (temp) + : + : "memory"); +} + + +/* + * disable IRQ/FIQ interrupts + * returns true if interrupts had been enabled before we disabled them + */ +int disable_interrupts (void) +{ + unsigned long old,temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "orr %1, %0, #0x80\n" + "msr cpsr_c, %1" + : "=r" (old), "=r" (temp) + : + : "memory"); + return (old & 0x80) == 0; +} +#else +void enable_interrupts (void) +{ + return; +} +int disable_interrupts (void) +{ + return 0; +} +#endif + + + +void bad_mode (void) +{ + panic ("Resetting CPU ...\n"); + reset_cpu (0); +} + +void show_regs (struct pt_regs *regs) +{ + unsigned long flags; + const char *processor_modes[] = + { "USER_26", "FIQ_26", "IRQ_26", "SVC_26", "UK4_26", "UK5_26", +"UK6_26", "UK7_26", + "UK8_26", "UK9_26", "UK10_26", "UK11_26", "UK12_26", "UK13_26", + "UK14_26", "UK15_26", + "USER_32", "FIQ_32", "IRQ_32", "SVC_32", "UK4_32", "UK5_32", + "UK6_32", "ABT_32", + "UK8_32", "UK9_32", "UK10_32", "UND_32", "UK12_32", "UK13_32", + "UK14_32", "SYS_32" + }; + + flags = condition_codes (regs); + + printf ("pc : [<%08lx>] lr : [<%08lx>]\n" + "sp : %08lx ip : %08lx fp : %08lx\n", + instruction_pointer (regs), + regs->ARM_lr, regs->ARM_sp, regs->ARM_ip, regs->ARM_fp); + printf ("r10: %08lx r9 : %08lx r8 : %08lx\n", + regs->ARM_r10, regs->ARM_r9, regs->ARM_r8); + printf ("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", + regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4); + printf ("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", + regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0); + printf ("Flags: %c%c%c%c", + flags & CC_N_BIT ? 'N' : 'n', + flags & CC_Z_BIT ? 'Z' : 'z', + flags & CC_C_BIT ? 'C' : 'c', flags & CC_V_BIT ? 'V' : 'v'); + printf (" IRQs %s FIQs %s Mode %s%s\n", + interrupts_enabled (regs) ? "on" : "off", + fast_interrupts_enabled (regs) ? "on" : "off", + processor_modes[processor_mode (regs)], + thumb_mode (regs) ? " (T)" : ""); +} + +void do_undefined_instruction (struct pt_regs *pt_regs) +{ + printf ("undefined instruction\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_software_interrupt (struct pt_regs *pt_regs) +{ + printf ("software interrupt\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_prefetch_abort (struct pt_regs *pt_regs) +{ + printf ("prefetch abort\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_data_abort (struct pt_regs *pt_regs) +{ + printf ("data abort\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_not_used (struct pt_regs *pt_regs) +{ + printf ("not used\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_fiq (struct pt_regs *pt_regs) +{ + printf ("fast interrupt request\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_irq (struct pt_regs *pt_regs) +{ + printf ("interrupt request\n"); + show_regs (pt_regs); + bad_mode (); +} + +static ulong timestamp; +static ulong lastdec; + +int interrupt_init (void) +{ + /* disable all interrupts */ + IO_INTMR1 = 0; + + /* operate timer 1 in prescale mode */ + IO_SYSCON1 |= SYSCON1_TC1M; + + /* select 2kHz clock source for timer 1 */ + IO_SYSCON1 &= ~SYSCON1_TC1S; + + /* set timer 1 counter */ + lastdec = IO_TC1D = TIMER_LOAD_VAL; + timestamp = 0; + + return (0); +} + +/* + * timer without interrupts + */ + +void reset_timer (void) +{ + reset_timer_masked (); +} + +ulong get_timer (ulong base) +{ + return get_timer_masked () - base; +} + +void set_timer (ulong t) +{ + timestamp = t; +} + +void udelay (unsigned long usec) +{ + ulong tmo; + + tmo = usec / 1000; + tmo *= CFG_HZ; + tmo /= 1000; + + tmo += get_timer (0); + + while (get_timer_masked () < tmo) + /*NOP*/; +} + +void reset_timer_masked (void) +{ + /* reset time */ + lastdec = READ_TIMER; + timestamp = 0; +} + +ulong get_timer_masked (void) +{ + ulong now = READ_TIMER; + + if (lastdec >= now) { + /* normal mode */ + timestamp += lastdec - now; + } else { + /* we have an overflow ... */ + timestamp += lastdec + TIMER_LOAD_VAL - now; + } + lastdec = now; + + return timestamp; +} + +void udelay_masked (unsigned long usec) +{ + ulong tmo; + + tmo = usec / 1000; + tmo *= CFG_HZ; + tmo /= 1000; + + reset_timer_masked (); + + while (get_timer_masked () < tmo) + /*NOP*/; +} diff --git a/cpu/arm920t/Makefile b/cpu/arm920t/Makefile new file mode 100644 index 0000000000..e5ec81d959 --- /dev/null +++ b/cpu/arm920t/Makefile @@ -0,0 +1,43 @@ +# +# (C) Copyright 2000, 2001, 2002 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = lib$(CPU).a + +START = start.o +OBJS = serial.o interrupts.o cpu.o speed.o + +all: .depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) crv $@ $(OBJS) + +######################################################################### + +.depend: Makefile $(START:.o=.S) $(OBJS:.o=.c) + $(CC) -M $(CFLAGS) $(START:.o=.S) $(OBJS:.o=.c) > $@ + +sinclude .depend + +######################################################################### diff --git a/cpu/arm920t/interrupts.c b/cpu/arm920t/interrupts.c new file mode 100644 index 0000000000..963ccbd2a3 --- /dev/null +++ b/cpu/arm920t/interrupts.c @@ -0,0 +1,304 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Alex Zuepke + * + * (C) Copyright 2002 + * Gary Jennejohn, 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 +#include +#if defined(CONFIG_S3C2400) +#include +#elif defined(CONFIG_S3C2410) +#include +#endif + +#include + +extern void reset_cpu(ulong addr); +int timer_load_val = 0; + +/* macro to read the 16 bit timer */ +#define READ_TIMER (rTCNTO4 & 0xffff) + +#ifdef CONFIG_USE_IRQ +/* enable IRQ interrupts */ +void enable_interrupts (void) +{ + unsigned long temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "bic %0, %0, #0x80\n" + "msr cpsr_c, %0" + : "=r" (temp) + : + : "memory"); +} + + +/* + * disable IRQ/FIQ interrupts + * returns true if interrupts had been enabled before we disabled them + */ +int disable_interrupts (void) +{ + unsigned long old,temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "orr %1, %0, #0xc0\n" + "msr cpsr_c, %1" + : "=r" (old), "=r" (temp) + : + : "memory"); + return (old & 0x80) == 0; +} +#else +void enable_interrupts (void) +{ + return; +} +int disable_interrupts (void) +{ + return 0; +} +#endif + + + +void bad_mode (void) +{ + panic ("Resetting CPU ...\n"); + reset_cpu (0); +} + +void show_regs (struct pt_regs *regs) +{ + unsigned long flags; + const char *processor_modes[] = { + "USER_26", "FIQ_26", "IRQ_26", "SVC_26", + "UK4_26", "UK5_26", "UK6_26", "UK7_26", + "UK8_26", "UK9_26", "UK10_26", "UK11_26", + "UK12_26", "UK13_26", "UK14_26", "UK15_26", + "USER_32", "FIQ_32", "IRQ_32", "SVC_32", + "UK4_32", "UK5_32", "UK6_32", "ABT_32", + "UK8_32", "UK9_32", "UK10_32", "UND_32", + "UK12_32", "UK13_32", "UK14_32", "SYS_32", + }; + + flags = condition_codes (regs); + + printf ("pc : [<%08lx>] lr : [<%08lx>]\n" + "sp : %08lx ip : %08lx fp : %08lx\n", + instruction_pointer (regs), + regs->ARM_lr, regs->ARM_sp, regs->ARM_ip, regs->ARM_fp); + printf ("r10: %08lx r9 : %08lx r8 : %08lx\n", + regs->ARM_r10, regs->ARM_r9, regs->ARM_r8); + printf ("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", + regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4); + printf ("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", + regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0); + printf ("Flags: %c%c%c%c", + flags & CC_N_BIT ? 'N' : 'n', + flags & CC_Z_BIT ? 'Z' : 'z', + flags & CC_C_BIT ? 'C' : 'c', flags & CC_V_BIT ? 'V' : 'v'); + printf (" IRQs %s FIQs %s Mode %s%s\n", + interrupts_enabled (regs) ? "on" : "off", + fast_interrupts_enabled (regs) ? "on" : "off", + processor_modes[processor_mode (regs)], + thumb_mode (regs) ? " (T)" : ""); +} + +void do_undefined_instruction (struct pt_regs *pt_regs) +{ + printf ("undefined instruction\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_software_interrupt (struct pt_regs *pt_regs) +{ + printf ("software interrupt\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_prefetch_abort (struct pt_regs *pt_regs) +{ + printf ("prefetch abort\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_data_abort (struct pt_regs *pt_regs) +{ + printf ("data abort\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_not_used (struct pt_regs *pt_regs) +{ + printf ("not used\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_fiq (struct pt_regs *pt_regs) +{ + printf ("fast interrupt request\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_irq (struct pt_regs *pt_regs) +{ + printf ("interrupt request\n"); + show_regs (pt_regs); + bad_mode (); +} + +static ulong timestamp; +static ulong lastdec; + +int interrupt_init (void) +{ + /* use PWM Timer 4 because it has no output */ + /* prescaler for Timer 4 is 16 */ + rTCFG0 = 0x0f00; + if (timer_load_val == 0) + { + /* + * for 10 ms clock period @ PCLK with 4 bit divider = 1/2 + * (default) and prescaler = 16. Should be 10390 + * @33.25MHz and 15625 @ 50 MHz + */ + timer_load_val = get_PCLK()/(2 * 16 * 100); + } + /* load value for 10 ms timeout */ + lastdec = rTCNTB4 = timer_load_val; + /* auto load, manual update of Timer 4 */ + rTCON = 0x600000; + /* auto load, start Timer 4 */ + rTCON = 0x500000; + timestamp = 0; + + return (0); +} + +/* + * timer without interrupts + */ + +void reset_timer (void) +{ + reset_timer_masked (); +} + +ulong get_timer (ulong base) +{ + return get_timer_masked () - base; +} + +void set_timer (ulong t) +{ + timestamp = t; +} + +void udelay (unsigned long usec) +{ + ulong tmo; + + tmo = usec / 1000; + tmo *= (timer_load_val * 100); + tmo /= 1000; + + tmo += get_timer (0); + + while (get_timer_masked () < tmo) + /*NOP*/; +} + +void reset_timer_masked (void) +{ + /* reset time */ + lastdec = READ_TIMER; + timestamp = 0; +} + +ulong get_timer_masked (void) +{ + ulong now = READ_TIMER; + + if (lastdec >= now) { + /* normal mode */ + timestamp += lastdec - now; + } else { + /* we have an overflow ... */ + timestamp += lastdec + timer_load_val - now; + } + lastdec = now; + + return timestamp; +} + +void udelay_masked (unsigned long usec) +{ + ulong tmo; + + tmo = usec / 1000; + tmo *= (timer_load_val * 100); + tmo /= 1000; + + reset_timer_masked (); + + while (get_timer_masked () < tmo) + /*NOP*/; +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +ulong get_tbclk (void) +{ + ulong tbclk; + +#if defined(CONFIG_SMDK2400) || defined(CONFIG_TRAB) + tbclk = timer_load_val * 100; +#elif defined(CONFIG_SMDK2410) + tbclk = CFG_HZ; +#endif + + return tbclk; +} diff --git a/cpu/arm920t/serial.c b/cpu/arm920t/serial.c new file mode 100644 index 0000000000..c32e73b714 --- /dev/null +++ b/cpu/arm920t/serial.c @@ -0,0 +1,200 @@ +/* + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, + * + * 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 +#if defined(CONFIG_S3C2400) || defined(CONFIG_TRAB) +#include +#elif defined(CONFIG_S3C2410) +#include +#endif + + +void serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + int i; + unsigned int reg = 0; + + /* value is calculated so : (int)(PCLK/16./baudrate) -1 */ + reg = get_PCLK() / (16 * gd->baudrate) - 1; + +#ifdef CONFIG_SERIAL1 + /* FIFO enable, Tx/Rx FIFO clear */ + rUFCON0 = 0x07; + rUMCON0 = 0x0; + /* Normal,No parity,1 stop,8 bit */ + rULCON0 = 0x3; + /* + * tx=level,rx=edge,disable timeout int.,enable rx error int., + * normal,interrupt or polling + */ + rUCON0 = 0x245; + rUBRDIV0 = reg; + +#ifdef CONFIG_HWFLOW + rUMCON0 = 0x1; /* RTS up */ +#endif + for (i = 0; i < 100; i++); +#elif CONFIG_SERIAL2 +# if defined(CONFIG_TRAB) +# #error "TRAB supports only CONFIG_SERIAL1" +# endif + /* FIFO enable, Tx/Rx FIFO clear */ + rUFCON1 = 0x06; + rUMCON1 = 0x0; + /* Normal,No parity,1 stop,8 bit */ + rULCON1 = 0x3; + /* + * tx=level,rx=edge,disable timeout int.,enable rx error int., + * normal,interrupt or polling + */ + rUCON1 = 0x245; + rUBRDIV1 = reg; + +#ifdef CONFIG_HWFLOW + rUMCON1 = 0x1; /* RTS up */ +#endif + for (i = 0; i < 100; i++); +#else +#error "Bad: you didn't configure serial ..." +#endif +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + * + */ +int serial_init (void) +{ + serial_setbrg (); + + return (0); +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int serial_getc (void) +{ +#ifdef CONFIG_SERIAL1 + while (!(rUTRSTAT0 & 0x1)); + + return rURXH0 & 0xff; +#elif CONFIG_SERIAL2 + while (!(rUTRSTAT1 & 0x1)); + + return rURXH1 & 0xff; +#endif +} + +#ifdef CONFIG_HWFLOW +static int hwflow = 0; /* turned off by default */ +int hwflow_onoff(int on) +{ + switch(on) { + case 0: + default: + break; /* return current */ + case 1: + hwflow = 1; /* turn on */ + break; + case -1: + hwflow = 0; /* turn off */ + break; + } + return hwflow; +} +#endif + +#ifdef CONFIG_MODEM_SUPPORT +static int be_quiet = 0; +void disable_putc(void) +{ + be_quiet = 1; +} + +void enable_putc(void) +{ + be_quiet = 0; +} +#endif + + +/* + * Output a single byte to the serial port. + */ +void serial_putc (const char c) +{ +#ifdef CONFIG_MODEM_SUPPORT + if (be_quiet) + return; +#endif + +#ifdef CONFIG_SERIAL1 + /* wait for room in the tx FIFO on SERIAL1 */ + while (!(rUTRSTAT0 & 0x2)); + +#ifdef CONFIG_HWFLOW + /* Wait for CTS up */ + while(hwflow && !(rUMSTAT0 & 0x1)) + ; +#endif + + rUTXH0 = c; +#elif CONFIG_SERIAL2 + /* wait for room in the tx FIFO on SERIAL2 */ + while (!(rUTRSTAT1 & 0x2)); + +#ifdef CONFIG_HWFLOW + /* Wait for CTS up */ + while(hwflow && !(rUMSTAT1 & 0x1)) + ; +#endif + rUTXH1 = c; +#endif + + /* If \n, also do \r */ + if (c == '\n') + serial_putc ('\r'); +} + +/* + * Test whether a character is in the RX buffer + */ +int serial_tstc (void) +{ +#ifdef CONFIG_SERIAL1 + return rUTRSTAT0 & 0x1; +#elif CONFIG_SERIAL2 + return rUTRSTAT1 & 0x1; +#endif +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} diff --git a/cpu/arm920t/speed.c b/cpu/arm920t/speed.c new file mode 100644 index 0000000000..1ee0c1a15a --- /dev/null +++ b/cpu/arm920t/speed.c @@ -0,0 +1,96 @@ +/* + * (C) Copyright 2001-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * David Mueller, ELSOFT AG, d.mueller@elsoft.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 + */ + +/* This code should work for both the S3C2400 and the S3C2410 + * as they seem to have the same PLL and clock machinery inside. + * The different address mapping is handled by the s3c24xx.h files below. + */ + +#include +#if defined(CONFIG_S3C2400) +#include +#elif defined(CONFIG_S3C2410) +#include +#endif + +#define MPLL 0 +#define UPLL 1 + +/* ------------------------------------------------------------------------- */ +/* NOTE: This describes the proper use of this file. + * + * CONFIG_PLL_INPUT_FREQ should be defined as the input frequency of the PLL. + * + * get_FCLK(), get_HCLK(), get_PCLK() and get_UCLK() return the clock of + * the specified bus in HZ. + */ +/* ------------------------------------------------------------------------- */ + +static ulong get_PLLCLK(int pllreg) +{ + ulong r, m, p, s; + + if (pllreg == MPLL) + r = rMPLLCON; + else if (pllreg == UPLL) + r = rUPLLCON; + else + hang(); + + m = ((r & 0xFF000) >> 12) + 8; + p = ((r & 0x003F0) >> 4) + 2; + s = r & 0x3; + + return((CONFIG_PLL_INPUT_FREQ * m) / (p << s)); +} + +/* return FCLK frequency */ +ulong get_FCLK(void) +{ + return(get_PLLCLK(MPLL)); +} + +/* return HCLK frequency */ +ulong get_HCLK(void) +{ + ulong clkdiv = rCLKDIVN; + + return((clkdiv & 0x2) ? get_FCLK()/2 : get_FCLK()); +} + +/* return PCLK frequency */ +ulong get_PCLK(void) +{ + ulong clkdiv = rCLKDIVN; + + return((clkdiv & 0x1) ? get_HCLK()/2 : get_HCLK()); +} + +/* return UCLK frequency */ +ulong get_UCLK(void) +{ + return(get_PLLCLK(UPLL)); +} diff --git a/cpu/mpc824x/cpu_init.c b/cpu/mpc824x/cpu_init.c new file mode 100644 index 0000000000..602f65d30b --- /dev/null +++ b/cpu/mpc824x/cpu_init.c @@ -0,0 +1,383 @@ +/* + * (C) Copyright 2000 + * Rob Taylor. Flying Pig Systems. robt@flyingpig.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +#ifndef CFG_BANK0_ROW +#define CFG_BANK0_ROW 0 +#endif +#ifndef CFG_BANK1_ROW +#define CFG_BANK1_ROW 0 +#endif +#ifndef CFG_BANK2_ROW +#define CFG_BANK2_ROW 0 +#endif +#ifndef CFG_BANK3_ROW +#define CFG_BANK3_ROW 0 +#endif +#ifndef CFG_BANK4_ROW +#define CFG_BANK4_ROW 0 +#endif +#ifndef CFG_BANK5_ROW +#define CFG_BANK5_ROW 0 +#endif +#ifndef CFG_BANK6_ROW +#define CFG_BANK6_ROW 0 +#endif +#ifndef CFG_BANK7_ROW +#define CFG_BANK7_ROW 0 +#endif +#ifndef CFG_DBUS_SIZE2 +#define CFG_DBUS_SIZE2 0 +#endif + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + */ +void +cpu_init_f (void) +{ +/* MOUSSE board is initialized in asm */ +#if !defined(CONFIG_MOUSSE) && !defined(CONFIG_BMW) + register unsigned long val; + CONFIG_WRITE_HALFWORD(PCICR, 0x06); /* Bus Master, respond to PCI memory space acesses*/ +/* CONFIG_WRITE_HALFWORD(PCISR, 0xffff); */ /*reset PCISR*/ + +#if defined(CONFIG_MUSENKI) || defined(CONFIG_PN62) +/* Why is this here, you ask? Try, just try setting 0x8000 + * in PCIACR with CONFIG_WRITE_HALFWORD() + * this one was a stumper, and we are annoyed + */ + +#define M_CONFIG_WRITE_HALFWORD( addr, data ) \ + __asm__ __volatile__( \ + " \ + stw %2,0(%0)\n \ + sync\n \ + sth %3,2(%1)\n \ + sync\n \ + " \ + : /* no output */ \ + : "r" (CONFIG_ADDR), "r" (CONFIG_DATA), \ + "r" (PCISWAP(addr & ~3)), "r" (PCISWAP(data << 16)) \ + ); + + M_CONFIG_WRITE_HALFWORD(PCIACR, 0x8000); +#endif + + CONFIG_WRITE_BYTE(PCLSR, 0x8); /* set PCI cache line size */ + + /* + * Note that although this bit is cleared after a hard reset, it + * must be explicitly set and then cleared by software during + * initialization in order to guarantee correct operation of the + * DLL and the SDRAM_CLK[0:3] signals (if they are used). + */ + CONFIG_READ_BYTE (AMBOR, val); + CONFIG_WRITE_BYTE(AMBOR, val & 0xDF); + CONFIG_WRITE_BYTE(AMBOR, val | 0x20); + CONFIG_WRITE_BYTE(AMBOR, val & 0xDF); + + CONFIG_READ_WORD(PICR1, val); +#if defined(CONFIG_MPC8240) + CONFIG_WRITE_WORD( PICR1, + (val & (PICR1_ADDRESS_MAP | PICR1_RCS0)) | + PIRC1_MSK | PICR1_PROC_TYPE_603E | + PICR1_FLASH_WR_EN | PICR1_MCP_EN | + PICR1_CF_DPARK | PICR1_EN_PCS | + PICR1_CF_APARK ); +#elif defined(CONFIG_MPC8245) + CONFIG_WRITE_WORD( PICR1, + (val & (PICR1_RCS0)) | + PICR1_PROC_TYPE_603E | + PICR1_FLASH_WR_EN | PICR1_MCP_EN | + PICR1_CF_DPARK | PICR1_NO_BUSW_CK | + PICR1_DEC| PICR1_CF_APARK | 0x10); /* 8245 UM says bit 4 must be set */ +#else +#error Specific type of MPC824x must be defined (i.e. CONFIG_MPC8240) +#endif + + CONFIG_READ_WORD(PICR2, val); + val= val & ~ (PICR2_CF_SNOOP_WS_MASK | PICR2_CF_APHASE_WS_MASK); /*mask off waitstate bits*/ +#ifndef CONFIG_PN62 + val |= PICR2_CF_SNOOP_WS_1WS | PICR2_CF_APHASE_WS_1WS; /*1 wait state*/ +#endif + CONFIG_WRITE_WORD(PICR2, val); + + CONFIG_WRITE_WORD(EUMBBAR, CFG_EUMB_ADDR); +#ifndef CFG_RAMBOOT + CONFIG_WRITE_WORD(MCCR1, (CFG_ROMNAL << MCCR1_ROMNAL_SHIFT) | + (CFG_BANK0_ROW) | + (CFG_BANK1_ROW << MCCR1_BANK1ROW_SHIFT) | + (CFG_BANK2_ROW << MCCR1_BANK2ROW_SHIFT) | + (CFG_BANK3_ROW << MCCR1_BANK3ROW_SHIFT) | + (CFG_BANK4_ROW << MCCR1_BANK4ROW_SHIFT) | + (CFG_BANK5_ROW << MCCR1_BANK5ROW_SHIFT) | + (CFG_BANK6_ROW << MCCR1_BANK6ROW_SHIFT) | + (CFG_BANK7_ROW << MCCR1_BANK7ROW_SHIFT) | + (CFG_ROMFAL << MCCR1_ROMFAL_SHIFT)); +#endif + +#if defined(CFG_ASRISE) && defined(CFG_ASFALL) + CONFIG_WRITE_WORD(MCCR2, CFG_REFINT << MCCR2_REFINT_SHIFT | + CFG_ASRISE << MCCR2_ASRISE_SHIFT | + CFG_ASFALL << MCCR2_ASFALL_SHIFT); +#else + CONFIG_WRITE_WORD(MCCR2, CFG_REFINT << MCCR2_REFINT_SHIFT); +#endif + +#if defined(CONFIG_MPC8240) + CONFIG_WRITE_WORD(MCCR3, + (((CFG_BSTOPRE & 0x003c) >> 2) << MCCR3_BSTOPRE2TO5_SHIFT) | + (CFG_REFREC << MCCR3_REFREC_SHIFT) | + (CFG_RDLAT << MCCR3_RDLAT_SHIFT)); +#elif defined(CONFIG_MPC8245) + CONFIG_WRITE_WORD(MCCR3, + (((CFG_BSTOPRE & 0x003c) >> 2) << MCCR3_BSTOPRE2TO5_SHIFT) | + (CFG_REFREC << MCCR3_REFREC_SHIFT)); +#else +#error Specific type of MPC824x must be defined (i.e. CONFIG_MPC8240) +#endif + +/* this is gross. We think these should all be the same, and various boards + * should define CFG_ACTORW to 0 if they don't want to set it, or even, if + * its not set, we define it to zero in this file + */ +#if defined(CONFIG_CU824) || defined(CONFIG_PN62) + CONFIG_WRITE_WORD(MCCR4, + (CFG_PRETOACT << MCCR4_PRETOACT_SHIFT) | + (CFG_ACTTOPRE << MCCR4_ACTTOPRE_SHIFT) | + MCCR4_BIT21 | + (CFG_REGISTERD_TYPE_BUFFER ? MCCR4_REGISTERED: 0) | + ((CFG_BSTOPRE & 0x0003) <> 6) << MCCR4_BSTOPRE6TO9_SHIFT)); +#elif defined(CONFIG_MPC8240) + CONFIG_WRITE_WORD(MCCR4, + (CFG_PRETOACT << MCCR4_PRETOACT_SHIFT) | + (CFG_ACTTOPRE << MCCR4_ACTTOPRE_SHIFT) | + MCCR4_BIT21 | + (CFG_REGISTERD_TYPE_BUFFER ? MCCR4_REGISTERED: 0) | + ((CFG_BSTOPRE & 0x0003) <> 6) <> 6) <> MICR_ADDR_SHIFT) | + (((CFG_BANK1_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CFG_BANK2_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CFG_BANK3_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMSAR1, + ( (CFG_BANK0_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CFG_BANK1_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CFG_BANK2_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CFG_BANK3_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(MSAR2, + ( (CFG_BANK4_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CFG_BANK5_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CFG_BANK6_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CFG_BANK7_START & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMSAR2, + ( (CFG_BANK4_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CFG_BANK5_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CFG_BANK6_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CFG_BANK7_START & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(MEAR1, + ( (CFG_BANK0_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CFG_BANK1_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CFG_BANK2_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CFG_BANK3_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMEAR1, + ( (CFG_BANK0_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CFG_BANK1_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CFG_BANK2_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CFG_BANK3_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(MEAR2, + ( (CFG_BANK4_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) | + (((CFG_BANK5_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 8) | + (((CFG_BANK6_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 16) | + (((CFG_BANK7_END & MICR_ADDR_MASK) >> MICR_ADDR_SHIFT) << 24)); + CONFIG_WRITE_WORD(EMEAR2, + ( (CFG_BANK4_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) | + (((CFG_BANK5_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 8) | + (((CFG_BANK6_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 16) | + (((CFG_BANK7_END & MICR_EADDR_MASK) >> MICR_EADDR_SHIFT) << 24)); + + CONFIG_WRITE_BYTE(ODCR, CFG_ODCR); +#ifdef CFG_DLL_MAX_DELAY + CONFIG_WRITE_BYTE(MIOCR1, CFG_DLL_MAX_DELAY); /* needed to make DLL lock */ +#endif +#if defined(CFG_DLL_EXTEND) && defined(CFG_PCI_HOLD_DEL) + CONFIG_WRITE_BYTE(PMCR2, CFG_DLL_EXTEND | CFG_PCI_HOLD_DEL); +#endif +#if defined(MIOCR2) && defined(CFG_SDRAM_DSCD) + CONFIG_WRITE_BYTE(MIOCR2, CFG_SDRAM_DSCD); /* change memory input */ +#endif /* setup & hold time */ + + CONFIG_WRITE_BYTE(MBER, + CFG_BANK0_ENABLE | + (CFG_BANK1_ENABLE << 1) | + (CFG_BANK2_ENABLE << 2) | + (CFG_BANK3_ENABLE << 3) | + (CFG_BANK4_ENABLE << 4) | + (CFG_BANK5_ENABLE << 5) | + (CFG_BANK6_ENABLE << 6) | + (CFG_BANK7_ENABLE << 7)); + +#ifdef CFG_PGMAX + CONFIG_WRITE_BYTE(MPMR, CFG_PGMAX); +#endif + + /* ! Wait 200us before initialize other registers */ + /*FIXME: write a decent udelay wait */ + __asm__ __volatile__( + " mtctr %0 \n \ + 0: bdnz 0b\n" + : + : "r" (0x10000)); + + CONFIG_READ_WORD(MCCR1, val); + CONFIG_WRITE_WORD(MCCR1, val | MCCR1_MEMGO); /* set memory access going */ + __asm__ __volatile__("eieio"); + +#endif /* !CONFIG_MOUSSE && !CONFIG_BMW */ +} + + +#ifdef CONFIG_MOUSSE +#ifdef INCLUDE_MPC107_REPORT +struct MPC107_s{ + unsigned int iobase; + char desc[120]; +} MPC107Regs[] ={ + {BMC_BASE+0x0, "MPC107 Vendor/Device ID"}, + {BMC_BASE+0x4, "MPC107 PCI Command/Status Register"}, + {BMC_BASE+0x8, "MPC107 Revision"}, + {BMC_BASE+0xC, "MPC107 Cache Line Size"}, + {BMC_BASE+0x10, "MPC107 LMBAR"}, + {BMC_BASE+0x14, "MPC824x PCSR"}, + {BMC_BASE+0xA8, "MPC824x PICR1"}, + {BMC_BASE+0xAC, "MPC824x PICR2"}, + {BMC_BASE+0x46, "MPC824x PACR"}, + {BMC_BASE+0x310, "MPC824x ITWR"}, + {BMC_BASE+0x300, "MPC824x OMBAR"}, + {BMC_BASE+0x308, "MPC824x OTWR"}, + {BMC_BASE+0x14, "MPC107 Peripheral Control and Status Register"}, + {BMC_BASE+0x78, "MPC107 EUMBAR"}, + {BMC_BASE+0xC0, "MPC107 Processor Bus Error Status"}, + {BMC_BASE+0xC4, "MPC107 PCI Bus Error Status"}, + {BMC_BASE+0xC8, "MPC107 Processor/PCI Error Address"}, + {BMC_BASE+0xE0, "MPC107 AMBOR Register"}, + {BMC_BASE+0xF0, "MPC107 MCCR1 Register"}, + {BMC_BASE+0xF4, "MPC107 MCCR2 Register"}, + {BMC_BASE+0xF8, "MPC107 MCCR3 Register"}, + {BMC_BASE+0xFC, "MPC107 MCCR4 Register"} +}; +#define N_MPC107_Regs (sizeof(MPC107Regs)/sizeof(MPC107Regs[0])) +#endif /* INCLUDE_MPC107_REPORT */ +#endif /* CONFIG_MOUSSE */ + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ +#ifdef CONFIG_MOUSSE +#ifdef INCLUDE_MPC107_REPORT + unsigned int tmp = 0, i; +#endif + /* + * Initialize the EUMBBAR (Embedded Util Mem Block Base Addr Reg). + * This is necessary before the EPIC, DMA ctlr, I2C ctlr, etc. can + * be accessed. + */ + +#ifdef CONFIG_MPC8240 /* only on MPC8240 */ + mpc824x_mpc107_setreg (EUMBBAR, EUMBBAR_VAL); + /* MOT/SPS: Issue #10002, PCI (FD Alias enable) */ + mpc824x_mpc107_setreg (AMBOR, 0x000000C0); +#endif + + +#ifdef INCLUDE_MPC107_REPORT + /* Check MPC824x PCI Device and Vendor ID */ + while ((tmp = mpc824x_mpc107_getreg (BMC_BASE)) != 0x31057) { + printf (" MPC107: offset=0x%x, val = 0x%x\n", + BMC_BASE, + tmp); + } + + for (i = 0; i < N_MPC107_Regs; i++) { + printf (" 0x%x/%s = 0x%x\n", + MPC107Regs[i].iobase, + MPC107Regs[i].desc, + mpc824x_mpc107_getreg (MPC107Regs[i].iobase)); + } + + printf ("IBAT0L = 0x%08X\n", mfspr (IBAT0L)); + printf ("IBAT0U = 0x%08X\n", mfspr (IBAT0U)); + printf ("IBAT1L = 0x%08X\n", mfspr (IBAT1L)); + printf ("IBAT1U = 0x%08X\n", mfspr (IBAT1U)); + printf ("IBAT2L = 0x%08X\n", mfspr (IBAT2L)); + printf ("IBAT2U = 0x%08X\n", mfspr (IBAT2U)); + printf ("IBAT3L = 0x%08X\n", mfspr (IBAT3L)); + printf ("IBAT3U = 0x%08X\n", mfspr (IBAT3U)); + printf ("DBAT0L = 0x%08X\n", mfspr (DBAT0L)); + printf ("DBAT0U = 0x%08X\n", mfspr (DBAT0U)); + printf ("DBAT1L = 0x%08X\n", mfspr (DBAT1L)); + printf ("DBAT1U = 0x%08X\n", mfspr (DBAT1U)); + printf ("DBAT2L = 0x%08X\n", mfspr (DBAT2L)); + printf ("DBAT2U = 0x%08X\n", mfspr (DBAT2U)); + printf ("DBAT3L = 0x%08X\n", mfspr (DBAT3L)); + printf ("DBAT3U = 0x%08X\n", mfspr (DBAT3U)); +#endif /* INCLUDE_MPC107_REPORT */ +#endif /* CONFIG_MOUSSE */ + return (0); +} diff --git a/cpu/mpc824x/drivers/i2c/i2c1.c b/cpu/mpc824x/drivers/i2c/i2c1.c new file mode 100644 index 0000000000..be6ec60a14 --- /dev/null +++ b/cpu/mpc824x/drivers/i2c/i2c1.c @@ -0,0 +1,1228 @@ +/************************************************************* + * + * Copyright @ Motorola, 1999 + * + ************************************************************/ +#include + +#ifdef CONFIG_HARD_I2C +#include +#include "i2c_export.h" +#include "i2c.h" + +#undef I2CDBG0 +#undef DEBUG + +/* Define a macro to use an optional application-layer print function, if + * one was passed to the I2C library during initialization. If there was + * no function pointer passed, this protects against calling it. Also define + * the global variable that holds the passed pointer. + */ +#define TIMEOUT (CFG_HZ/4) +#define PRINT if ( app_print ) app_print +static int (*app_print) (char *, ...); + +/******************* Internal to I2C Driver *****************/ +static unsigned int ByteToXmit = 0; +static unsigned int XmitByte = 0; +static unsigned char *XmitBuf = 0; +static unsigned int XmitBufEmptyStop = 0; +static unsigned int ByteToRcv = 0; +static unsigned int RcvByte = 0; +static unsigned char *RcvBuf = 0; +static unsigned int RcvBufFulStop = 0; +static unsigned int MasterRcvAddress = 0; + +/* Set by call to get_eumbbar during I2C_Initialize. + * This could be globally available to the I2C library, but there is + * an advantage to passing it as a parameter: it is already in a register + * and doesn't have to be loaded from memory. Also, that is the way the + * I2C library was already implemented and I don't want to change it without + * a more detailed analysis. + * It is being set as a global variable in I2C_Initialize to hide it from + * the DINK application layer, because it is Kahlua-specific. I think that + * get_eumbbar, load_runtime_reg, and store_runtime_reg should be defined in + * a Kahlua-specific library dealing with the embedded utilities memory block. + * Right now, get_eumbbar is defined in dink32/kahlua.s. The other two are + * defined in dink32/drivers/i2c/i2c2.s. + */ +static unsigned int Global_eumbbar = 0; + +extern unsigned int load_runtime_reg (unsigned int eumbbar, + unsigned int reg); + +extern unsigned int store_runtime_reg (unsigned int eumbbar, + unsigned int reg, unsigned int val); + +/************************** API *****************/ + +/* Application Program Interface (API) are the calls provided by the I2C + * library to upper layer applications (i.e., DINK) to access the Kahlua + * I2C bus interface. The functions and values that are part of this API + * are declared in i2c_export.h. + */ + +/* Initialize I2C unit with the following: + * driver's slave address + * interrupt enabled + * optional pointer to application layer print function + * + * These parameters may be added: + * desired clock rate + * digital filter frequency sampling rate + * + * This function must be called before I2C unit can be used. + */ +I2C_Status I2C_Initialize (unsigned char addr, + I2C_INTERRUPT_MODE en_int, + int (*p) (char *, ...)) +{ + I2CStatus status; + + /* establish the pointer, if there is one, to the application's "printf" */ + app_print = p; + + /* If this is the first call, get the embedded utilities memory block + * base address. I'm not sure what to do about error handling here: + * if a non-zero value is returned, accept it. + */ + if (Global_eumbbar == 0) + Global_eumbbar = get_eumbbar (); + if (Global_eumbbar == 0) { + PRINT ("I2C_Initialize: can't find EUMBBAR\n"); + return I2C_ERROR; + } + + /* validate the I2C address */ + if (addr & 0x80) { + PRINT ("I2C_Initialize, I2C address invalid: %d 0x%x\n", + (unsigned int) addr, (unsigned int) addr); + return I2C_ERROR; + } + + /* Call the internal I2C library function to perform work. + * Accept the default frequency sampling rate (no way to set it currently, + * via I2C_Init) and set the clock frequency to something reasonable. + */ + status = I2C_Init (Global_eumbbar, (unsigned char) 0x31, addr, en_int); + if (status != I2CSUCCESS) { + PRINT ("I2C_Initialize: error in initiation\n"); + return I2C_ERROR; + } + + /* all is well */ + return I2C_SUCCESS; +} + + +/* Perform the given I2C transaction, only MASTER_XMIT and MASTER_RCV + * are implemented. Both are only in polling mode. + * + * en_int controls interrupt/polling mode + * act is the type of transaction + * i2c_addr is the I2C address of the slave device + * data_addr is the address of the data on the slave device + * len is the length of data to send or receive + * buffer is the address of the data buffer + * stop = I2C_NO_STOP, don't signal STOP at end of transaction + * I2C_STOP, signal STOP at end of transaction + * retry is the timeout retry value, currently ignored + * rsta = I2C_NO_RESTART, this is not continuation of existing transaction + * I2C_RESTART, this is a continuation of existing transaction + */ +I2C_Status I2C_do_transaction ( I2C_INTERRUPT_MODE en_int, + I2C_TRANSACTION_MODE act, + unsigned char i2c_addr, + unsigned char data_addr, + int len, + char *buffer, + I2C_STOP_MODE stop, + int retry, I2C_RESTART_MODE rsta) +{ + I2C_Status status; + unsigned char data_addr_buffer[1]; + +#if 1 +/* This is a temporary work-around. The I2C library breaks the protocol + * if it attempts to handle a data transmission in more than one + * transaction, so the data address and the actual data bytes are put + * into a single buffer before sending it to the library internal functions. + * The problem is related to being able to restart a transaction without + * sending the I2C device address or repeating the data address. It may take + * a day or two to sort it all out, so I'll have to get back to it later. + * Look at I2C_Start to see about using some status flags (I'm not sure that + * "stop" and "rsta" are enough to reflect the states, maybe so; but the logic + * in the library is insufficient) to control correct handling of the protocol. + */ + unsigned char dummy_buffer[257]; + + if (act == I2C_MASTER_XMIT) { + int i; + + if (len > 256) + return I2C_ERROR; + for (i = 1; i <= len; i++) + dummy_buffer[i] = buffer[i - 1]; + dummy_buffer[0] = data_addr; + status = I2C_do_buffer (en_int, act, i2c_addr, 1 + len, + dummy_buffer, stop, retry, rsta); + if (status != I2C_SUCCESS) { + PRINT ("I2C_do_transaction: can't perform data transfer\n"); + return I2C_ERROR; + } + return I2C_SUCCESS; + } +#endif /* end of temp work-around */ + + /* validate requested transaction type */ + if ((act != I2C_MASTER_XMIT) && (act != I2C_MASTER_RCV)) { + PRINT ("I2C_do_transaction, invalid transaction request: %d\n", + act); + return I2C_ERROR; + } + + /* range check the I2C address */ + if (i2c_addr & 0x80) { + PRINT ("I2C_do_transaction, I2C address out of range: %d 0x%x\n", + (unsigned int) i2c_addr, (unsigned int) i2c_addr); + return I2C_ERROR; + } else { + data_addr_buffer[0] = data_addr; + } + + /* + * We first have to contact the slave device and transmit the + * data address. Be careful about the STOP and restart stuff. + * We don't want to signal STOP after sending the data + * address, but this could be a continuation if the + * application didn't release the bus after the previous + * transaction, by not sending a STOP after it. + */ + status = I2C_do_buffer (en_int, I2C_MASTER_XMIT, i2c_addr, 1, + data_addr_buffer, I2C_NO_STOP, retry, rsta); + if (status != I2C_SUCCESS) { + PRINT ("I2C_do_transaction: can't send data address for read\n"); + return I2C_ERROR; + } + + /* The data transfer will be a continuation. */ + rsta = I2C_RESTART; + + /* now handle the user data */ + status = I2C_do_buffer (en_int, act, i2c_addr, len, + buffer, stop, retry, rsta); + if (status != I2C_SUCCESS) { + PRINT ("I2C_do_transaction: can't perform data transfer\n"); + return I2C_ERROR; + } + + /* all is well */ + return I2C_SUCCESS; +} + +/* This function performs the work for I2C_do_transaction. The work is + * split into this function to enable I2C_do_transaction to first transmit + * the data address to the I2C slave device without putting the data address + * into the first byte of the buffer. + * + * en_int controls interrupt/polling mode + * act is the type of transaction + * i2c_addr is the I2C address of the slave device + * len is the length of data to send or receive + * buffer is the address of the data buffer + * stop = I2C_NO_STOP, don't signal STOP at end of transaction + * I2C_STOP, signal STOP at end of transaction + * retry is the timeout retry value, currently ignored + * rsta = I2C_NO_RESTART, this is not continuation of existing transaction + * I2C_RESTART, this is a continuation of existing transaction + */ +static I2C_Status I2C_do_buffer (I2C_INTERRUPT_MODE en_int, + I2C_TRANSACTION_MODE act, + unsigned char i2c_addr, + int len, + unsigned char *buffer, + I2C_STOP_MODE stop, + int retry, I2C_RESTART_MODE rsta) +{ + I2CStatus rval; + unsigned int dev_stat; + + if (act == I2C_MASTER_RCV) { + /* set up for master-receive transaction */ + rval = I2C_get (Global_eumbbar, i2c_addr, buffer, len, stop, rsta); + } else { + /* set up for master-transmit transaction */ + rval = I2C_put (Global_eumbbar, i2c_addr, buffer, len, stop, rsta); + } + + /* validate the setup */ + if (rval != I2CSUCCESS) { + dev_stat = load_runtime_reg (Global_eumbbar, I2CSR); + PRINT ("Error(I2C_do_buffer): control phase, code(0x%08x), status(0x%08x)\n", rval, dev_stat); + I2C_Stop (Global_eumbbar); + return I2C_ERROR; + } + + if (en_int == 1) { + /* this should not happen, no interrupt handling yet */ + return I2C_SUCCESS; + } + + /* this performs the polling action, when the transfer is completed, + * the status returned from I2C_Timer_Event will be I2CBUFFFULL or + * I2CBUFFEMPTY (rcv or xmit), I2CSUCCESS or I2CADDRESS indicates the + * transaction is not yet complete, anything else is an error. + */ + while (rval == I2CSUCCESS || rval == I2CADDRESS) { + int timeval = get_timer (0); + + /* poll the device until something happens */ + do { + rval = I2C_Timer_Event (Global_eumbbar, 0); + } + while (rval == I2CNOEVENT && get_timer (timeval) < TIMEOUT); + + /* check for error condition */ + if (rval == I2CSUCCESS || + rval == I2CBUFFFULL || + rval == I2CBUFFEMPTY || + rval == I2CADDRESS) { + ; /* do nothing */ + } else { + /* report the error condition */ + dev_stat = load_runtime_reg (Global_eumbbar, I2CSR); + PRINT ("Error(I2C_do_buffer): code(0x%08x), status(0x%08x)\n", + rval, dev_stat); + return I2C_ERROR; + } + } + + /* all is well */ + return I2C_SUCCESS; +} + +/** + * Note: + * + * In all following functions, + * the caller shall pass the configured embedded utility memory + * block base, EUMBBAR. + **/ + +/*********************************************************** + * function: I2C_put + * + * description: + Send a buffer of data to the intended rcv_addr. + * If stop_flag is set, after the whole buffer + * is sent, generate a STOP signal provided that the + * receiver doesn't signal the STOP in the middle. + * I2C is the master performing transmitting. If + * no STOP signal is generated at the end of current + * transaction, the master can generate a START signal + * to another slave addr. + * + * note: this is master xmit API + *********************************************************/ +static I2CStatus I2C_put (unsigned int eumbbar, unsigned char rcv_addr, /* receiver's address */ + unsigned char *buffer_ptr, /* pointer of data to be sent */ + unsigned int length, /* number of byte of in the buffer */ + unsigned int stop_flag, /* 1 - signal STOP when buffer is empty + * 0 - no STOP signal when buffer is empty + */ + unsigned int is_cnt) +{ /* 1 - this is a restart, don't check MBB + * 0 - this is a new start, check MBB + */ + if (buffer_ptr == 0 || length == 0) { + return I2CERROR; + } +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_put\n", __FILE__, __LINE__); +#endif + + XmitByte = 0; + ByteToXmit = length; + XmitBuf = buffer_ptr; + XmitBufEmptyStop = stop_flag; + + RcvByte = 0; + ByteToRcv = 0; + RcvBuf = 0; + + /* we are the master, start transaction */ + return I2C_Start (eumbbar, rcv_addr, XMIT, is_cnt); +} + +/*********************************************************** + * function: I2C_get + * + * description: + * Receive a buffer of data from the desired sender_addr + * If stop_flag is set, when the buffer is full and the + * sender does not signal STOP, generate a STOP signal. + * I2C is the master performing receiving. If no STOP signal + * is generated, the master can generate a START signal + * to another slave addr. + * + * note: this is master receive API + **********************************************************/ +static I2CStatus I2C_get (unsigned int eumbbar, unsigned char rcv_from, /* sender's address */ + unsigned char *buffer_ptr, /* pointer of receiving buffer */ + unsigned int length, /* length of the receiving buffer */ + unsigned int stop_flag, /* 1 - signal STOP when buffer is full + * 0 - no STOP signal when buffer is full + */ + unsigned int is_cnt) +{ /* 1 - this is a restart, don't check MBB + * 0 - this is a new start, check MBB + */ + if (buffer_ptr == 0 || length == 0) { + return I2CERROR; + } +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_get\n", __FILE__, __LINE__); +#endif + + RcvByte = 0; + ByteToRcv = length; + RcvBuf = buffer_ptr; + RcvBufFulStop = stop_flag; + + XmitByte = 0; + ByteToXmit = 0; + XmitBuf = 0; + + /* we are the master, start the transaction */ + return I2C_Start (eumbbar, rcv_from, RCV, is_cnt); + +} + +#if 0 /* turn off dead code */ +/********************************************************* + * function: I2C_write + * + * description: + * Send a buffer of data to the requiring master. + * If stop_flag is set, after the whole buffer is sent, + * generate a STOP signal provided that the requiring + * receiver doesn't signal the STOP in the middle. + * I2C is the slave performing transmitting. + * + * Note: this is slave xmit API. + * + * due to the current Kahlua design, slave transmitter + * shall not signal STOP since there is no way + * for master to detect it, causing I2C bus hung. + * + * For the above reason, the stop_flag is always + * set, i.e., 0. + * + * programmer shall use the timer on Kahlua to + * control the interval of data byte at the + * master side. + *******************************************************/ +static I2CStatus I2C_write (unsigned int eumbbar, unsigned char *buffer_ptr, /* pointer of data to be sent */ + unsigned int length, /* number of byte of in the buffer */ + unsigned int stop_flag) +{ /* 1 - signal STOP when buffer is empty + * 0 - no STOP signal when buffer is empty + */ + if (buffer_ptr == 0 || length == 0) { + return I2CERROR; + } + + XmitByte = 0; + ByteToXmit = length; + XmitBuf = buffer_ptr; + XmitBufEmptyStop = 0; /* in order to avoid bus hung, ignored the user's stop_flag */ + + RcvByte = 0; + ByteToRcv = 0; + RcvBuf = 0; + + /* we are the slave, just wait for being called, or pull */ + /* I2C_Timer_Event( eumbbar ); */ +} + +/****************************************************** + * function: I2C_read + * + * description: + * Receive a buffer of data from the sending master. + * If stop_flag is set, when the buffer is full and the + * sender does not signal STOP, generate a STOP signal. + * I2C is the slave performing receiving. + * + * note: this is slave receive API + ****************************************************/ +static I2CStatus I2C_read (unsigned int eumbbar, unsigned char *buffer_ptr, /* pointer of receiving buffer */ + unsigned int length, /* length of the receiving buffer */ + unsigned int stop_flag) +{ /* 1 - signal STOP when buffer is full + * 0 - no STOP signal when buffer is full + */ + if (buffer_ptr == 0 || length == 0) { + return I2CERROR; + } + + RcvByte = 0; + ByteToRcv = length; + RcvBuf = buffer_ptr; + RcvBufFulStop = stop_flag; + + XmitByte = 0; + ByteToXmit = 0; + XmitBuf = 0; + + /* wait for master to call us, or poll */ + /* I2C_Timer_Event( eumbbar ); */ +} +#endif /* turn off dead code */ + +/********************************************************* + * function: I2c_Timer_Event + * + * description: + * if interrupt is not used, this is the timer event handler. + * After each fixed time interval, this function can be called + * to check the I2C status and call appropriate function to + * handle the status event. + ********************************************************/ +static I2CStatus I2C_Timer_Event (unsigned int eumbbar, + I2CStatus (*handler) (unsigned int)) +{ + I2C_STAT stat; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Timer_Event\n", __FILE__, __LINE__); +#endif + + stat = I2C_Get_Stat (eumbbar); + + if (stat.mif == 1) { + if (handler == 0) { + return I2C_ISR (eumbbar); + } else { + return (*handler) (eumbbar); + } + } + + return I2CNOEVENT; +} + + +/****************** Device I/O function *****************/ + +/****************************************************** + * function: I2C_Start + * + * description: Generate a START signal in the desired mode. + * I2C is the master. + * + * Return I2CSUCCESS if no error. + * + * note: + ****************************************************/ +static I2CStatus I2C_Start (unsigned int eumbbar, unsigned char slave_addr, /* address of the receiver */ + I2C_MODE mode, /* XMIT(1) - put (write) + * RCV(0) - get (read) + */ + unsigned int is_cnt) +{ /* 1 - this is a restart, don't check MBB + * 0 - this is a new start + */ + unsigned int tmp = 0; + I2C_STAT stat; + I2C_CTRL ctrl; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Start addr 0x%x mode %d cnt %d\n", __FILE__, + __LINE__, slave_addr, mode, is_cnt); +#endif + + ctrl = I2C_Get_Ctrl (eumbbar); + + /* first make sure I2C has been initialized */ + if (ctrl.men == 0) { + return I2CERROR; + } + + /* next make sure bus is idle */ + stat = I2C_Get_Stat (eumbbar); + + if (is_cnt == 0 && stat.mbb == 1) { + /* sorry, we lost */ + return I2CBUSBUSY; + } else if (is_cnt == 1 && stat.mif == 1 && stat.mal == 0) { + /* sorry, we lost the bus */ + return I2CALOSS; + } + + + /* OK, I2C is enabled and we have the bus */ + + /* prepare to write the slave address */ + ctrl.msta = 1; + ctrl.mtx = 1; + ctrl.txak = 0; + ctrl.rsta = is_cnt; /* set the repeat start bit */ + I2C_Set_Ctrl (eumbbar, ctrl); + + /* write the slave address and xmit/rcv mode bit */ + tmp = load_runtime_reg (eumbbar, I2CDR); + tmp = (tmp & 0xffffff00) | + ((slave_addr & 0x007f) << 1) | + (mode == XMIT ? 0x0 : 0x1); + store_runtime_reg (eumbbar, I2CDR, tmp); + + if (mode == RCV) { + MasterRcvAddress = 1; + } else { + MasterRcvAddress = 0; + } + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Start exit\n", __FILE__, __LINE__); +#endif + + /* wait for the interrupt or poll */ + return I2CSUCCESS; +} + +/*********************************************************** + * function: I2c_Stop + * + * description: Generate a STOP signal to terminate the master + * transaction. + * return I2CSUCCESS + * + **********************************************************/ +static I2CStatus I2C_Stop (unsigned int eumbbar) +{ + I2C_CTRL ctrl; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Stop enter\n", __FILE__, __LINE__); +#endif + + ctrl = I2C_Get_Ctrl (eumbbar); + ctrl.msta = 0; + I2C_Set_Ctrl (eumbbar, ctrl); + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Stop exit\n", __FILE__, __LINE__); +#endif + + return I2CSUCCESS; +} + +/**************************************************** + * function: I2C_Master_Xmit + * + * description: Master sends one byte of data to + * slave target + * + * return I2CSUCCESS if the byte transmitted. + * Otherwise no-zero + * + * Note: condition must meet when this function is called: + * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && I2CSR(RXAK) == 0 + * I2CCR(MSTA) == 1 && I2CCR(MTX) == 1 + * + ***************************************************/ +static I2CStatus I2C_Master_Xmit (unsigned int eumbbar) +{ + unsigned int val; + + if (ByteToXmit > 0) { + + if (ByteToXmit == XmitByte) { + /* all xmitted */ + ByteToXmit = 0; + + if (XmitBufEmptyStop == 1) { + I2C_Stop (eumbbar); + } + + return I2CBUFFEMPTY; + + } +#ifdef I2CDBG0 + PRINT ("%s(%d): xmit 0x%02x\n", __FILE__, __LINE__, + *(XmitBuf + XmitByte)); +#endif + + val = *(XmitBuf + XmitByte); + val &= 0x000000ff; + store_runtime_reg (eumbbar, I2CDR, val); + XmitByte++; + + return I2CSUCCESS; + + } + + return I2CBUFFEMPTY; +} + +/*********************************************** + * function: I2C_Master_Rcv + * + * description: master reads one byte data + * from slave source + * + * return I2CSUCCESS if no error + * + * Note: condition must meet when this function is called: + * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && + * I2CCR(MSTA) == 1 && I2CCR(MTX) == 0 + * + ***********************************************/ +static I2CStatus I2C_Master_Rcv (unsigned int eumbbar) +{ + I2C_CTRL ctrl; + unsigned int val; + + if (ByteToRcv > 0) { + + if (ByteToRcv - RcvByte == 2 && RcvBufFulStop == 1) { + /* master requests more than or equal to 2 bytes + * we are reading 2nd to last byte + */ + + /* we need to set I2CCR(TXAK) to generate a STOP */ + ctrl = I2C_Get_Ctrl (eumbbar); + ctrl.txak = 1; + I2C_Set_Ctrl (eumbbar, ctrl); + + /* Kahlua will automatically generate a STOP + * next time a transaction happens + */ + + /* note: the case of master requesting one byte is + * handled in I2C_ISR + */ + } + + /* generat a STOP before reading the last byte */ + if (RcvByte + 1 == ByteToRcv && RcvBufFulStop == 1) { + I2C_Stop (eumbbar); + } + + val = load_runtime_reg (eumbbar, I2CDR); + *(RcvBuf + RcvByte) = val & 0xFF; + +#ifdef I2CDBG0 + PRINT ("%s(%d): rcv 0x%02x\n", __FILE__, __LINE__, + *(RcvBuf + RcvByte)); +#endif + + RcvByte++; + + if (ByteToRcv == RcvByte) { + ByteToRcv = 0; + + return I2CBUFFFULL; + } + + return I2CSUCCESS; + } + + return I2CBUFFFULL; + +} + +/**************************************************** + * function: I2C_Slave_Xmit + * + * description: Slave sends one byte of data to + * requesting destination + * + * return SUCCESS if the byte transmitted. Otherwise + * No-zero + * + * Note: condition must meet when this function is called: + * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && I2CSR(RXAK) = 0 + * I2CCR(MSTA) == 0 && I2CCR(MTX) == 1 + * + ***************************************************/ +static I2CStatus I2C_Slave_Xmit (unsigned int eumbbar) +{ + unsigned int val; + + if (ByteToXmit > 0) { + + if (ByteToXmit == XmitByte) { + /* no more data to send */ + ByteToXmit = 0; + + /* + * do not toggle I2CCR(MTX). Doing so will + * cause bus-hung since current Kahlua design + * does not give master a way to detect slave + * stop. It is always a good idea for master + * to use timer to prevent the long long + * delays + */ + + return I2CBUFFEMPTY; + } +#ifdef I2CDBG + PRINT ("%s(%d): xmit 0x%02x\n", __FILE__, __LINE__, + *(XmitBuf + XmitByte)); +#endif + + val = *(XmitBuf + XmitByte); + val &= 0x000000ff; + store_runtime_reg (eumbbar, I2CDR, val); + XmitByte++; + + return I2CSUCCESS; + } + + return I2CBUFFEMPTY; +} + +/*********************************************** + * function: I2C_Slave_Rcv + * + * description: slave reads one byte data + * from master source + * + * return I2CSUCCESS if no error otherwise non-zero + * + * Note: condition must meet when this function is called: + * I2CSR(MIF) == 1 && I2CSR(MCF) == 1 && + * I2CCR(MSTA) == 0 && I2CCR(MTX) = 0 + * + ***********************************************/ +static I2CStatus I2C_Slave_Rcv (unsigned int eumbbar) +{ + unsigned int val; + I2C_CTRL ctrl; + + if (ByteToRcv > 0) { + val = load_runtime_reg (eumbbar, I2CDR); + *(RcvBuf + RcvByte) = val & 0xff; +#ifdef I2CDBG + PRINT ("%s(%d): rcv 0x%02x\n", __FILE__, __LINE__, + *(RcvBuf + RcvByte)); +#endif + RcvByte++; + + if (ByteToRcv == RcvByte) { + if (RcvBufFulStop == 1) { + /* all done */ + ctrl = I2C_Get_Ctrl (eumbbar); + ctrl.txak = 1; + I2C_Set_Ctrl (eumbbar, ctrl); + } + + ByteToRcv = 0; + return I2CBUFFFULL; + } + + return I2CSUCCESS; + } + + return I2CBUFFFULL; +} + +/****************** Device Control Function *************/ + +/********************************************************* + * function: I2C_Init + * + * description: Initialize I2C unit with desired frequency divider, + * master's listening address, with interrupt enabled + * or disabled. + * + * note: + ********************************************************/ +static I2CStatus I2C_Init (unsigned int eumbbar, unsigned char fdr, /* frequency divider */ + unsigned char slave_addr, /* driver's address used for receiving */ + unsigned int en_int) +{ /* 1 - enable I2C interrupt + * 0 - disable I2C interrup + */ + I2C_CTRL ctrl; + unsigned int tmp; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Init enter\n", __FILE__, __LINE__); +#endif + + ctrl = I2C_Get_Ctrl (eumbbar); + /* disable the I2C module before we change everything */ + ctrl.men = 0; + I2C_Set_Ctrl (eumbbar, ctrl); + + /* set the frequency diver */ + tmp = load_runtime_reg (eumbbar, I2CFDR); + tmp = (tmp & 0xffffffc0) | (fdr & 0x3f); + store_runtime_reg (eumbbar, I2CFDR, tmp); + + /* Set our listening (slave) address */ + tmp = load_runtime_reg (eumbbar, I2CADR); + tmp = (tmp & 0xffffff01) | ((slave_addr & 0x7f) << 1); + store_runtime_reg (eumbbar, I2CADR, tmp); + + /* enable I2C with desired interrupt setting */ + ctrl.men = 1; + ctrl.mien = en_int & 0x1; + I2C_Set_Ctrl (eumbbar, ctrl); +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_Init exit\n", __FILE__, __LINE__); +#endif + + return I2CSUCCESS; + +} + +/***************************************** + * function I2c_Get_Stat + * + * description: Query I2C Status, i.e., read I2CSR + * + ****************************************/ +static I2C_STAT I2C_Get_Stat (unsigned int eumbbar) +{ + unsigned int temp; + I2C_STAT stat; + + temp = load_runtime_reg (eumbbar, I2CSR); + +#ifdef I2CDBG0 + PRINT ("%s(%d): get stat = 0x%08x\n", __FILE__, __LINE__, temp); +#endif + + stat.rsrv0 = (temp & 0xffffff00) >> 8; + stat.mcf = (temp & 0x00000080) >> 7; + stat.maas = (temp & 0x00000040) >> 6; + stat.mbb = (temp & 0x00000020) >> 5; + stat.mal = (temp & 0x00000010) >> 4; + stat.rsrv1 = (temp & 0x00000008) >> 3; + stat.srw = (temp & 0x00000004) >> 2; + stat.mif = (temp & 0x00000002) >> 1; + stat.rxak = (temp & 0x00000001); + return stat; +} + +/********************************************* + * function: I2c_Set_Ctrl + * + * description: Change I2C Control bits, + * i.e., write to I2CCR + * + ********************************************/ +static void I2C_Set_Ctrl (unsigned int eumbbar, I2C_CTRL ctrl) +{ /* new control value */ + unsigned int temp = load_runtime_reg (eumbbar, I2CCR); + + temp &= 0xffffff03; + temp |= ((ctrl.men & 0x1) << 7); + temp |= ((ctrl.mien & 0x1) << 6); + temp |= ((ctrl.msta & 0x1) << 5); + temp |= ((ctrl.mtx & 0x1) << 4); + temp |= ((ctrl.txak & 0x1) << 3); + temp |= ((ctrl.rsta & 0x1) << 2); +#ifdef I2CDBG0 + PRINT ("%s(%d): set ctrl = 0x%08x\n", __FILE__, __LINE__, temp); +#endif + store_runtime_reg (eumbbar, I2CCR, temp); + +} + +/***************************************** + * function: I2C_Get_Ctrl + * + * description: Query I2C Control bits, + * i.e., read I2CCR + *****************************************/ +static I2C_CTRL I2C_Get_Ctrl (unsigned int eumbbar) +{ + union { + I2C_CTRL ctrl; + unsigned int temp; + } s; + + s.temp = load_runtime_reg (eumbbar, I2CCR); +#ifdef I2CDBG0 + PRINT ("%s(%d): get ctrl = 0x%08x\n", __FILE__, __LINE__, s.temp); +#endif + + return s.ctrl; +} + + +/**************************************** + * function: I2C_Slave_Addr + * + * description: Process slave address phase. + * return I2CSUCCESS if no error + * + * note: Precondition for calling this function: + * I2CSR(MIF) == 1 && + * I2CSR(MAAS) == 1 + ****************************************/ +static I2CStatus I2C_Slave_Addr (unsigned int eumbbar) +{ + I2C_STAT stat = I2C_Get_Stat (eumbbar); + I2C_CTRL ctrl = I2C_Get_Ctrl (eumbbar); + + if (stat.srw == 1) { + /* we are asked to xmit */ + ctrl.mtx = 1; + I2C_Set_Ctrl (eumbbar, ctrl); /* set MTX */ + return I2C_Slave_Xmit (eumbbar); + } + + /* we are asked to receive data */ + ctrl.mtx = 0; + I2C_Set_Ctrl (eumbbar, ctrl); + (void) load_runtime_reg (eumbbar, I2CDR); /* do a fake read to start */ + + return I2CADDRESS; +} + +/*********************************************** + * function: I2C_ISR + * + * description: I2C Interrupt service routine + * + * note: Precondition: + * I2CSR(MIF) == 1 + **********************************************/ +static I2CStatus I2C_ISR (unsigned int eumbbar) +{ + I2C_STAT stat; + I2C_CTRL ctrl; + +#ifdef I2CDBG0 + PRINT ("%s(%d): I2C_ISR\n", __FILE__, __LINE__); +#endif + + stat = I2C_Get_Stat (eumbbar); + ctrl = I2C_Get_Ctrl (eumbbar); + + /* clear MIF */ + stat.mif = 0; + + /* Now let see what kind of event this is */ + if (stat.mcf == 1) { + /* transfer compete */ + + /* clear the MIF bit */ + I2C_Set_Stat (eumbbar, stat); + + if (ctrl.msta == 1) { + /* master */ + if (ctrl.mtx == 1) { + /* check if this is the address phase for master receive */ + if (MasterRcvAddress == 1) { + /* Yes, it is the address phase of master receive */ + ctrl.mtx = 0; + /* now check how much we want to receive */ + if (ByteToRcv == 1 && RcvBufFulStop == 1) { + ctrl.txak = 1; + } + + I2C_Set_Ctrl (eumbbar, ctrl); + (void) load_runtime_reg (eumbbar, I2CDR); /* fake read first */ + + MasterRcvAddress = 0; + return I2CADDRESS; + + } + + /* master xmit */ + if (stat.rxak == 0) { + /* slave has acknowledged */ + return I2C_Master_Xmit (eumbbar); + } + + /* slave has not acknowledged yet, generate a STOP */ + if (XmitBufEmptyStop == 1) { + ctrl.msta = 0; + I2C_Set_Ctrl (eumbbar, ctrl); + } + + return I2CSUCCESS; + } + + /* master receive */ + return I2C_Master_Rcv (eumbbar); + } + + /* slave */ + if (ctrl.mtx == 1) { + /* slave xmit */ + if (stat.rxak == 0) { + /* master has acknowledged */ + return I2C_Slave_Xmit (eumbbar); + } + + /* master has not acknowledged, wait for STOP */ + /* do nothing for preventing bus from hung */ + return I2CSUCCESS; + } + + /* slave rcv */ + return I2C_Slave_Rcv (eumbbar); + + } else if (stat.maas == 1) { + /* received a call from master */ + + /* clear the MIF bit */ + I2C_Set_Stat (eumbbar, stat); + + /* master is calling us, process the address phase */ + return I2C_Slave_Addr (eumbbar); + } else { + /* has to be arbitration lost */ + stat.mal = 0; + I2C_Set_Stat (eumbbar, stat); + + ctrl.msta = 0; /* return to receive mode */ + I2C_Set_Ctrl (eumbbar, ctrl); + } + + return I2CSUCCESS; + +} + +/****************************************************** + * function: I2C_Set_Stat + * + * description: modify the I2CSR + * + *****************************************************/ +static void I2C_Set_Stat (unsigned int eumbbar, I2C_STAT stat) +{ + union { + unsigned int val; + I2C_STAT stat; + } s_tmp; + union { + unsigned int val; + I2C_STAT stat; + } s; + + s.val = load_runtime_reg (eumbbar, I2CSR); + s.val &= 0xffffff08; + s_tmp.stat = stat; + s.val |= (s_tmp.val & 0xf7); + +#ifdef I2CDBG0 + PRINT ("%s(%d): set stat = 0x%08x\n", __FILE__, __LINE__, s.val); +#endif + + store_runtime_reg (eumbbar, I2CSR, s.val); + +} + +/****************************************************** + * The following are routines to glue the rest of + * U-Boot to the Sandpoint I2C driver. + *****************************************************/ + +void i2c_init (int speed, int slaveadd) +{ +#ifdef DEBUG + I2C_Initialize (0x7f, 0, (void *) printf); +#else + I2C_Initialize (0x7f, 0, 0); +#endif +} + +int i2c_probe (uchar chip) +{ + int tmp; + + /* + * Try to read the first location of the chip. The underlying + * driver doesn't appear to support sending just the chip address + * and looking for an back. + */ + udelay(10000); + return i2c_read (chip, 0, 1, (char *)&tmp, 1); +} + +int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + I2CStatus status; + uchar xaddr[4]; + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + status = I2C_do_buffer (0, I2C_MASTER_XMIT, chip, alen, + &xaddr[4 - alen], I2C_NO_STOP, 1, + I2C_NO_RESTART); + if (status != I2C_SUCCESS) { + PRINT ("i2c_read: can't send data address for read\n"); + return 1; + } + } + + /* The data transfer will be a continuation. */ + status = I2C_do_buffer (0, I2C_MASTER_RCV, chip, len, + buffer, I2C_STOP, 1, (alen > 0 ? I2C_RESTART : + I2C_NO_RESTART)); + + if (status != I2C_SUCCESS) { + PRINT ("i2c_read: can't perform data transfer\n"); + return 1; + } + + return 0; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + I2CStatus status; + unsigned char dummy_buffer[I2C_RXTX_LEN + 2]; + int i; + + dummy_buffer[0] = addr & 0xFF; + if (alen == 2) + dummy_buffer[1] = (addr >> 8) & 0xFF; + for (i = 0; i < len; i++) + dummy_buffer[i + alen] = buffer[i]; + + status = I2C_do_buffer (0, I2C_MASTER_XMIT, chip, alen + len, + dummy_buffer, I2C_STOP, 1, I2C_NO_RESTART); + +#ifdef CFG_EEPROM_PAGE_WRITE_DELAY_MS + udelay(CFG_EEPROM_PAGE_WRITE_DELAY_MS * 1000); +#endif + if (status != I2C_SUCCESS) { + PRINT ("i2c_write: can't perform data transfer\n"); + return 1; + } + + return 0; +} + +uchar i2c_reg_read (uchar i2c_addr, uchar reg) +{ + char buf[1]; + + i2c_init (0, 0); + + i2c_read (i2c_addr, reg, 1, buf, 1); + + return (buf[0]); +} + +void i2c_reg_write (uchar i2c_addr, uchar reg, uchar val) +{ + i2c_init (0, 0); + + i2c_write (i2c_addr, reg, 1, &val, 1); +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/cpu/mpc824x/pci.c b/cpu/mpc824x/pci.c new file mode 100644 index 0000000000..7e3c4c3b78 --- /dev/null +++ b/cpu/mpc824x/pci.c @@ -0,0 +1,78 @@ +/* + * arch/ppc/kernel/mpc10x_common.c + * + * Common routines for the Motorola SPS MPC106, MPC107 and MPC8240 Host bridge, + * Mem ctlr, EPIC, etc. + * + * Author: Mark A. Greer + * mgreer@mvista.com + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include + +#ifdef CONFIG_PCI + +#include +#include +#include +#include + +void pci_mpc824x_init (struct pci_controller *hose) +{ + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* System memory space */ + pci_set_region(hose->regions + 0, + CHRP_PCI_MEMORY_BUS, + CHRP_PCI_MEMORY_PHYS, + CHRP_PCI_MEMORY_SIZE, + PCI_REGION_MEM | PCI_REGION_MEMORY); + + /* PCI memory space */ + pci_set_region(hose->regions + 1, + CHRP_PCI_MEM_BUS, + CHRP_PCI_MEM_PHYS, + CHRP_PCI_MEM_SIZE, + PCI_REGION_MEM); + + /* ISA/PCI memory space */ + pci_set_region(hose->regions + 2, + CHRP_ISA_MEM_BUS, + CHRP_ISA_MEM_PHYS, + CHRP_ISA_MEM_SIZE, + PCI_REGION_MEM); + + /* PCI I/O space */ + pci_set_region(hose->regions + 3, + CHRP_PCI_IO_BUS, + CHRP_PCI_IO_PHYS, + CHRP_PCI_IO_SIZE, + PCI_REGION_IO); + + /* ISA/PCI I/O space */ + pci_set_region(hose->regions + 4, + CHRP_ISA_IO_BUS, + CHRP_ISA_IO_PHYS, + CHRP_ISA_IO_SIZE, + PCI_REGION_IO); + + hose->region_count = 5; + + pci_setup_indirect(hose, + CHRP_REG_ADDR, + CHRP_REG_DATA); + + pci_register_hose(hose); + + hose->last_busno = pci_hose_scan(hose); +} + +#endif diff --git a/cpu/mpc824x/start.S b/cpu/mpc824x/start.S new file mode 100644 index 0000000000..bd9706de7c --- /dev/null +++ b/cpu/mpc824x/start.S @@ -0,0 +1,835 @@ +/* + * Copyright (C) 1998 Dan Malek + * Copyright (C) 1999 Magnus Damm + * Copyright (C) 2000,2001,2002 Wolfgang Denk + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* U-Boot - Startup Code for PowerPC based Embedded Boards + * + * + * The processor starts at 0x00000100 and the code is executed + * from flash. The code is organized to be at an other address + * in memory, but as long we don't jump around before relocating. + * board_init lies at a quite high address and when the cpu has + * jumped there, everything is ok. + * This works because the cpu gives the FLASH (CS0) the whole + * address space at startup, and board_init lies as a echo of + * the flash somewhere up there in the memorymap. + * + * board_init will change CS0 to be positioned at the correct + * address and (s)dram will be positioned at address 0 + */ +#include +#include +#include + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include +#include + +#include +#include + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +/* FP, Machine Check and Recoverable Interr. */ +#define MSR_KERNEL ( MSR_FP | MSR_ME | MSR_RI ) + +/* + * Set up GOT: Global Offset Table + * + * Use r14 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(_end) + GOT_ENTRY(.bss) +#if defined(CONFIG_FADS) + GOT_ENTRY(environment) +#endif + END_GOT + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ + .text + .long 0x27051956 /* U-Boot Magic Number */ + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", __DATE__, " - ", __TIME__, ")" + .ascii CONFIG_IDENT_STRING, "\0" + + . = EXC_OFF_SYS_RESET + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH */ + b boot_cold + + . = EXC_OFF_SYS_RESET + 0x10 + + .globl _start_warm +_start_warm: + li r21, BOOTFLAG_WARM /* Software reboot */ + b boot_warm + +boot_cold: +boot_warm: + + /* Initialize machine status; enable machine check interrupt */ + /*----------------------------------------------------------------------*/ + li r3, MSR_KERNEL /* Set FP, ME, RI flags */ + mtmsr r3 + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + addis r0,0,0x0000 /* lets make sure that r0 is really 0 */ + mtspr HID0, r0 /* disable I and D caches */ + + mfspr r3, ICR /* clear Interrupt Cause Register */ + + mfmsr r3 /* turn off address translation */ + addis r4,0,0xffff + ori r4,r4,0xffcf + and r3,r3,r4 + mtmsr r3 + isync + sync /* the MMU should be off... */ + + +in_flash: +#if defined(CONFIG_BMW) + bl early_init_f /* Must be ASM: no stack yet! */ +#endif + /* + * Setup BATs - cannot be done in C since we don't have a stack yet + */ + bl setup_bats + + /* Enable MMU. + */ + mfmsr r3 + ori r3, r3, (MSR_IR | MSR_DR) + mtmsr r3 +#if !defined(CONFIG_BMW) + /* Enable and invalidate data cache. + */ + mfspr r3, HID0 + mr r2, r3 + ori r3, r3, HID0_DCE | HID0_DCI + ori r2, r2, HID0_DCE + sync + mtspr HID0, r3 + mtspr HID0, r2 + sync + + /* Allocate Initial RAM in data cache. + */ + lis r3, CFG_INIT_RAM_ADDR@h + ori r3, r3, CFG_INIT_RAM_ADDR@l + li r2, 128 + mtctr r2 +1: + dcbz r0, r3 + addi r3, r3, 32 + bdnz 1b + + /* Lock way0 in data cache. + */ + mfspr r3, 1011 + lis r2, 0xffff + ori r2, r2, 0xff1f + and r3, r3, r2 + ori r3, r3, 0x0080 + sync + mtspr 1011, r3 +#endif /* !CONFIG_BMW */ + /* + * Thisk the stack pointer *somewhere* sensible. Doesnt + * matter much where as we'll move it when we relocate + */ + lis r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@h + ori r1, r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@l + + li r0, 0 /* Make room for stack frame header and */ + stwu r0, -4(r1) /* clear final stack frame so that */ + stwu r0, -4(r1) /* stack backtraces terminate cleanly */ + + /* let the C-code set up the rest */ + /* */ + /* Be careful to keep code relocatable ! */ + /*----------------------------------------------------------------------*/ + + GET_GOT /* initialize GOT access */ + + /* r3: IMMR */ + bl cpu_init_f /* run low-level CPU init code (from Flash) */ + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (from Flash) */ + + + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(EXC_OFF_MACH_CHCK, MachineCheck, MachineCheckException) + +/* Data Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(EXC_OFF_DATA_STOR, DataStorage, UnknownException) + +/* Instruction Storage exception. "Never" generated on the 860. */ + STD_EXCEPTION(EXC_OFF_INS_STOR, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(EXC_OFF_EXTERNAL, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = EXC_OFF_ALIGN +Alignment: + EXCEPTION_PROLOG + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + lwz r6,GOT(transfer_to_handler) + mtlr r6 + blrl +.L_Alignment: + .long AlignmentException - _start + EXC_OFF_SYS_RESET + .long int_return - _start + EXC_OFF_SYS_RESET + +/* Program check exception */ + . = EXC_OFF_PROGRAM +ProgramCheck: + EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + lwz r6,GOT(transfer_to_handler) + mtlr r6 + blrl +.L_ProgramCheck: + .long ProgramCheckException - _start + EXC_OFF_SYS_RESET + .long int_return - _start + EXC_OFF_SYS_RESET + + /* No FPU on MPC8xx. This exception is not supposed to happen. + */ + STD_EXCEPTION(EXC_OFF_FPUNAVAIL, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(EXC_OFF_DECR, Decrementer, timer_interrupt) + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + + . = 0xc00 +/* + * r0 - SYSCALL number + * r3-... arguments + */ +SystemCall: + addis r11,r0,0 /* get functions table addr */ + ori r11,r11,0 /* Note: this code is patched in trap_init */ + addis r12,r0,0 /* get number of functions */ + ori r12,r12,0 + + cmplw 0, r0, r12 + bge 1f + + rlwinm r0,r0,2,0,31 /* fn_addr = fn_tbl[r0] */ + add r11,r11,r0 + lwz r11,0(r11) + + li r12,0xd00-4*3 /* save LR & SRRx */ + mflr r0 + stw r0,0(r12) + mfspr r0,SRR0 + stw r0,4(r12) + mfspr r0,SRR1 + stw r0,8(r12) + + li r12,0xc00+_back-SystemCall + mtlr r12 + mtspr SRR0,r11 + +1: SYNC + rfi + +_back: + + mfmsr r11 /* Disable interrupts */ + li r12,0 + ori r12,r12,MSR_EE + andc r11,r11,r12 + SYNC /* Some chip revs need this... */ + mtmsr r11 + SYNC + + li r12,0xd00-4*3 /* restore regs */ + lwz r11,0(r12) + mtlr r11 + lwz r11,4(r12) + mtspr SRR0,r11 + lwz r11,8(r12) + mtspr SRR1,r11 + + SYNC + rfi + + STD_EXCEPTION(EXC_OFF_TRACE, SingleStep, UnknownException) + + STD_EXCEPTION(EXC_OFF_FPUNASSIST, Trap_0e, UnknownException) + STD_EXCEPTION(EXC_OFF_PMI, Trap_0f, UnknownException) + + STD_EXCEPTION(EXC_OFF_ITME, InstructionTransMiss, UnknownException) + STD_EXCEPTION(EXC_OFF_DLTME, DataLoadTransMiss, UnknownException) + STD_EXCEPTION(EXC_OFF_DSTME, DataStoreTransMiss, UnknownException) + STD_EXCEPTION(EXC_OFF_IABE, InstructionBreakpoint, UnknownException) + STD_EXCEPTION(EXC_OFF_SMIE, SysManageInt, UnknownException) + STD_EXCEPTION(0x1500, Reserved5, UnknownException) + STD_EXCEPTION(0x1600, Reserved6, UnknownException) + STD_EXCEPTION(0x1700, Reserved7, UnknownException) + STD_EXCEPTION(0x1800, Reserved8, UnknownException) + STD_EXCEPTION(0x1900, Reserved9, UnknownException) + STD_EXCEPTION(0x1a00, ReservedA, UnknownException) + STD_EXCEPTION(0x1b00, ReservedB, UnknownException) + STD_EXCEPTION(0x1c00, ReservedC, UnknownException) + STD_EXCEPTION(0x1d00, ReservedD, UnknownException) + STD_EXCEPTION(0x1e00, ReservedE, UnknownException) + STD_EXCEPTION(0x1f00, ReservedF, UnknownException) + + STD_EXCEPTION(EXC_OFF_RMTE, RunModeTrace, UnknownException) + + .globl _end_of_vectors +_end_of_vectors: + + + . = 0x3000 + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ + .globl transfer_to_handler +transfer_to_handler: + stw r22,_NIP(r21) + lis r22,MSR_POW@h + andc r23,r23,r22 + stw r23,_MSR(r21) + SAVE_GPR(7, r21) + SAVE_4GPRS(8, r21) + SAVE_8GPRS(12, r21) + SAVE_8GPRS(24, r21) +#if 0 + andi. r23,r23,MSR_PR + mfspr r23,SPRG3 /* if from user, fix up tss.regs */ + beq 2f + addi r24,r1,STACK_FRAME_OVERHEAD + stw r24,PT_REGS(r23) +2: addi r2,r23,-TSS /* set r2 to current */ + tovirt(r2,r2,r23) +#endif + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ +#if 0 + addi r24,r2,TASK_STRUCT_SIZE /* check for kernel stack overflow */ + cmplw 0,r1,r2 + cmplw 1,r1,r24 + crand 1,1,4 + bgt stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ +#endif + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + ori r20,r20,0x30 /* enable IR, DR */ + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* Cache functions. +*/ + .globl icache_enable +icache_enable: + mfspr r5,HID0 /* turn on the I cache. */ + ori r5,r5,0x8800 /* Instruction cache only! */ + addis r6,0,0xFFFF + ori r6,r6,0xF7FF + and r6,r5,r6 /* clear the invalidate bit */ + sync + mtspr HID0,r5 + mtspr HID0,r6 + isync + sync + blr + + .globl icache_disable +icache_disable: + mfspr r5,HID0 + addis r6,0,0xFFFF + ori r6,r6,0x7FFF + and r5,r5,r6 + sync + mtspr HID0,r5 + isync + sync + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + srwi r3, r3, 15 /* >>15 & 1=> select bit 16 */ + andi. r3, r3, 1 + blr + + .globl dcache_enable +dcache_enable: + mfspr r5,HID0 /* turn on the D cache. */ + ori r5,r5,0x4400 /* Data cache only! */ + mfspr r4, PVR /* read PVR */ + srawi r3, r4, 16 /* shift off the least 16 bits */ + cmpi 0, 0, r3, 0xC /* Check for Max pvr */ + bne NotMax + ori r5,r5,0x0040 /* setting the DCFA bit, for Max rev 1 errata */ +NotMax: + addis r6,0,0xFFFF + ori r6,r6,0xFBFF + and r6,r5,r6 /* clear the invalidate bit */ + sync + mtspr HID0,r5 + mtspr HID0,r6 + isync + sync + blr + + .globl dcache_disable +dcache_disable: + mfspr r5,HID0 + addis r6,0,0xFFFF + ori r6,r6,0xBFFF + and r5,r5,r6 + sync + mtspr HID0,r5 + isync + sync + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + srwi r3, r3, 14 /* >>14 & 1=> select bit 17 */ + andi. r3, r3, 1 + blr + + .globl dc_read +dc_read: +/*TODO : who uses this, what should it do? +*/ + blr + + + .globl get_pvr +get_pvr: + mfspr r3, PVR + blr + + +/*------------------------------------------------------------------------------*/ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = dest + * r4 = src + * r5 = length in bytes + * r6 = cachelinesize + */ + .globl relocate_code +relocate_code: + + mr r1, r3 /* Set new stack pointer */ + mr r9, r4 /* Save copy of Global Data pointer */ + mr r10, r5 /* Save copy of Destination Address */ + + mr r3, r5 /* Destination Address */ +#ifdef DEBUG + lis r4, CFG_SDRAM_BASE@h /* Source Address */ + ori r4, r4, CFG_SDRAM_BASE@l +#else + lis r4, CFG_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CFG_MONITOR_BASE@l +#endif + lis r5, CFG_MONITOR_LEN@h /* Length in Bytes */ + ori r5, r5, CFG_MONITOR_LEN@l + li r6, CFG_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r14, r14, r15 + /* the the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +1: lwzu r0,4(r8) + stwu r0,4(r7) + bdnz 1b + b 4f + +2: slwi r0,r0,2 + add r8,r4,r0 + add r7,r3,r0 +3: lwzu r0,-4(r8) + stwu r0,-4(r7) + bdnz 3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4: cmpwi r6,0 + add r5,r3,r5 + beq 7f /* Always flush prefetch queue in any case */ + subi r0,r6,1 + andc r3,r3,r0 + mr r4,r3 +5: dcbst 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 5b + sync /* Wait for all dcbst to complete on bus */ + mr r4,r3 +6: icbi 0,r4 + add r4,r4,r6 + cmplw r4,r5 + blt 6b +7: sync /* Wait for all icbi to complete on bus */ + isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ + + addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET + mtlr r0 + blr + +in_ram: + + /* + * Relocation Function, r14 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + add r0,r0,r11 + stw r0,0(r3) + bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ +2: li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(.bss) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + blt 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* Problems accessing "end" in C, so do it here */ + .globl get_endaddr +get_endaddr: + lwz r3,GOT(_end) + blr + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + rlwinm r9, r7, 0, 18, 31 /* _start & 0x3FFF */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ + + mflr r4 /* save link register */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mtlr r4 /* restore link register */ + blr + + /* + * Function: relocate entries for one exception vector + */ +trap_reloc: + lwz r0, 0(r7) /* hdlr ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 0(r7) + + lwz r0, 4(r7) /* int_return ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 4(r7) + + blr + + /* Setup the BAT registers. + */ +setup_bats: + lis r4, CFG_IBAT0L@h + ori r4, r4, CFG_IBAT0L@l + lis r3, CFG_IBAT0U@h + ori r3, r3, CFG_IBAT0U@l + mtspr IBAT0L, r4 + mtspr IBAT0U, r3 + isync + + lis r4, CFG_DBAT0L@h + ori r4, r4, CFG_DBAT0L@l + lis r3, CFG_DBAT0U@h + ori r3, r3, CFG_DBAT0U@l + mtspr DBAT0L, r4 + mtspr DBAT0U, r3 + isync + + lis r4, CFG_IBAT1L@h + ori r4, r4, CFG_IBAT1L@l + lis r3, CFG_IBAT1U@h + ori r3, r3, CFG_IBAT1U@l + mtspr IBAT1L, r4 + mtspr IBAT1U, r3 + isync + + lis r4, CFG_DBAT1L@h + ori r4, r4, CFG_DBAT1L@l + lis r3, CFG_DBAT1U@h + ori r3, r3, CFG_DBAT1U@l + mtspr DBAT1L, r4 + mtspr DBAT1U, r3 + isync + + lis r4, CFG_IBAT2L@h + ori r4, r4, CFG_IBAT2L@l + lis r3, CFG_IBAT2U@h + ori r3, r3, CFG_IBAT2U@l + mtspr IBAT2L, r4 + mtspr IBAT2U, r3 + isync + + lis r4, CFG_DBAT2L@h + ori r4, r4, CFG_DBAT2L@l + lis r3, CFG_DBAT2U@h + ori r3, r3, CFG_DBAT2U@l + mtspr DBAT2L, r4 + mtspr DBAT2U, r3 + isync + + lis r4, CFG_IBAT3L@h + ori r4, r4, CFG_IBAT3L@l + lis r3, CFG_IBAT3U@h + ori r3, r3, CFG_IBAT3U@l + mtspr IBAT3L, r4 + mtspr IBAT3U, r3 + isync + + lis r4, CFG_DBAT3L@h + ori r4, r4, CFG_DBAT3L@l + lis r3, CFG_DBAT3U@h + ori r3, r3, CFG_DBAT3U@l + mtspr DBAT3L, r4 + mtspr DBAT3U, r3 + isync + + /* Invalidate TLBs. + * -> for (val = 0; val < 0x20000; val+=0x1000) + * -> tlbie(val); + */ + lis r3, 0 + lis r5, 2 + +1: + tlbie r3 + addi r3, r3, 0x1000 + cmp 0, 0, r3, r5 + blt 1b + + blr + + diff --git a/cpu/mpc8260/cpu_init.c b/cpu/mpc8260/cpu_init.c new file mode 100644 index 0000000000..9f9369ab3a --- /dev/null +++ b/cpu/mpc8260/cpu_init.c @@ -0,0 +1,264 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include + +static void config_8260_ioports (volatile immap_t * immr) +{ + int portnum; + + for (portnum = 0; portnum < 4; portnum++) { + uint pmsk = 0, + ppar = 0, + psor = 0, + pdir = 0, + podr = 0, + pdat = 0; + iop_conf_t *iopc = (iop_conf_t *) & iop_conf_tab[portnum][0]; + iop_conf_t *eiopc = iopc + 32; + uint msk = 1; + + /* + * NOTE: + * index 0 refers to pin 31, + * index 31 refers to pin 0 + */ + while (iopc < eiopc) { + if (iopc->conf) { + pmsk |= msk; + if (iopc->ppar) + ppar |= msk; + if (iopc->psor) + psor |= msk; + if (iopc->pdir) + pdir |= msk; + if (iopc->podr) + podr |= msk; + if (iopc->pdat) + pdat |= msk; + } + + msk <<= 1; + iopc++; + } + + if (pmsk != 0) { + volatile ioport_t *iop = ioport_addr (immr, portnum); + uint tpmsk = ~pmsk; + + /* + * the (somewhat confused) paragraph at the + * bottom of page 35-5 warns that there might + * be "unknown behaviour" when programming + * PSORx and PDIRx, if PPARx = 1, so I + * decided this meant I had to disable the + * dedicated function first, and enable it + * last. + */ + iop->ppar &= tpmsk; + iop->psor = (iop->psor & tpmsk) | psor; + iop->pdat = (iop->pdat & tpmsk) | pdat; + iop->pdir = (iop->pdir & tpmsk) | pdir; + iop->podr = (iop->podr & tpmsk) | podr; + iop->ppar |= ppar; + } + } +} + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * immr) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile memctl8260_t *memctl = &immr->im_memctl; + extern void m8260_cpm_reset (void); + + /* 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)); + + /* RSR - Reset Status Register - clear all status (5-4) */ + gd->reset_status = immr->im_clkrst.car_rsr; + immr->im_clkrst.car_rsr = RSR_ALLBITS; + + /* RMR - Reset Mode Register - contains checkstop reset enable (5-5) */ + immr->im_clkrst.car_rmr = CFG_RMR; + + /* BCR - Bus Configuration Register (4-25) */ + immr->im_siu_conf.sc_bcr = CFG_BCR; + + /* SIUMCR - contains debug pin configuration (4-31) */ + immr->im_siu_conf.sc_siumcr = CFG_SIUMCR; + + config_8260_ioports (immr); + + /* initialize time counter status and control register (4-40) */ + immr->im_sit.sit_tmcntsc = CFG_TMCNTSC; + + /* initialize the PIT (4-42) */ + immr->im_sit.sit_piscr = CFG_PISCR; + +#if !defined(CONFIG_COGENT) /* done in start.S for the cogent */ + /* System clock control register (9-8) */ + immr->im_clkrst.car_sccr = CFG_SCCR; +#endif /* !CONFIG_COGENT */ + + /* + * Memory Controller: + */ + + /* Map banks 0 and 1 to the FLASH banks 0 and 1 at preliminary + * addresses - these have to be modified later when FLASH size + * has been determined + */ + +#if defined(CFG_OR0_REMAP) + memctl->memc_or0 = CFG_OR0_REMAP; +#endif +#if defined(CFG_OR1_REMAP) + memctl->memc_or1 = CFG_OR1_REMAP; +#endif + + /* now restrict to preliminary range */ + memctl->memc_br0 = CFG_BR0_PRELIM; + memctl->memc_or0 = CFG_OR0_PRELIM; + +#if defined(CFG_BR1_PRELIM) && defined(CFG_OR1_PRELIM) + memctl->memc_or1 = CFG_OR1_PRELIM; + memctl->memc_br1 = CFG_BR1_PRELIM; +#endif + +#if defined(CFG_BR2_PRELIM) && defined(CFG_OR2_PRELIM) + memctl->memc_or2 = CFG_OR2_PRELIM; + memctl->memc_br2 = CFG_BR2_PRELIM; +#endif + +#if defined(CFG_BR3_PRELIM) && defined(CFG_OR3_PRELIM) + memctl->memc_or3 = CFG_OR3_PRELIM; + memctl->memc_br3 = CFG_BR3_PRELIM; +#endif + +#if defined(CFG_BR4_PRELIM) && defined(CFG_OR4_PRELIM) + memctl->memc_or4 = CFG_OR4_PRELIM; + memctl->memc_br4 = CFG_BR4_PRELIM; +#endif + +#if defined(CFG_BR5_PRELIM) && defined(CFG_OR5_PRELIM) + memctl->memc_or5 = CFG_OR5_PRELIM; + memctl->memc_br5 = CFG_BR5_PRELIM; +#endif + +#if defined(CFG_BR6_PRELIM) && defined(CFG_OR6_PRELIM) + memctl->memc_or6 = CFG_OR6_PRELIM; + memctl->memc_br6 = CFG_BR6_PRELIM; +#endif + +#if defined(CFG_BR7_PRELIM) && defined(CFG_OR7_PRELIM) + memctl->memc_or7 = CFG_OR7_PRELIM; + memctl->memc_br7 = CFG_BR7_PRELIM; +#endif + +#if defined(CFG_BR8_PRELIM) && defined(CFG_OR8_PRELIM) + memctl->memc_or8 = CFG_OR8_PRELIM; + memctl->memc_br8 = CFG_BR8_PRELIM; +#endif + +#if defined(CFG_BR9_PRELIM) && defined(CFG_OR9_PRELIM) + memctl->memc_or9 = CFG_OR9_PRELIM; + memctl->memc_br9 = CFG_BR9_PRELIM; +#endif + +#if defined(CFG_BR10_PRELIM) && defined(CFG_OR10_PRELIM) + memctl->memc_or10 = CFG_OR10_PRELIM; + memctl->memc_br10 = CFG_BR10_PRELIM; +#endif + +#if defined(CFG_BR11_PRELIM) && defined(CFG_OR11_PRELIM) + memctl->memc_or11 = CFG_OR11_PRELIM; + memctl->memc_br11 = CFG_BR11_PRELIM; +#endif + + m8260_cpm_reset (); +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immr = (immap_t *) gd->bd->bi_immr_base; + + immr->im_cpm.cp_rccr = CFG_RCCR; + + return (0); +} + +/* + * print out the reason for the reset + */ +int prt_8260_rsr (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + static struct { + ulong mask; + char *desc; + } bits[] = { + { + RSR_JTRS, "JTAG"}, { + RSR_CSRS, "Check Stop"}, { + RSR_SWRS, "Software Watchdog"}, { + RSR_BMRS, "Bus Monitor"}, { + RSR_ESRS, "External Soft"}, { + RSR_EHRS, "External Hard"} + }; + static int n = sizeof bits / sizeof bits[0]; + ulong rsr = gd->reset_status; + int i; + char *sep; + + puts ("MPC8260 Reset Status:"); + + sep = " "; + for (i = 0; i < n; i++) + if (rsr & bits[i].mask) { + printf ("%s%s", sep, bits[i].desc); + sep = ", "; + } + + puts ("\n\n"); + return (0); +} diff --git a/cpu/mpc8260/i2c.c b/cpu/mpc8260/i2c.c new file mode 100644 index 0000000000..8bfa2e8e74 --- /dev/null +++ b/cpu/mpc8260/i2c.c @@ -0,0 +1,733 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * 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 + +#if defined(CONFIG_HARD_I2C) + +#include +#include + +/* define to enable debug messages */ +#undef DEBUG_I2C + +/* uSec to wait between polls of the i2c */ +#define DELAY_US 100 +/* uSec to wait for the CPM to start processing the buffer */ +#define START_DELAY_US 1000 + +/* + * tx/rx per-byte timeout: we delay DELAY_US uSec between polls so the + * timeout will be (tx_length + rx_length) * DELAY_US * TOUT_LOOP + */ +#define TOUT_LOOP 5 + +/*----------------------------------------------------------------------- + * Set default values + */ +#ifndef CFG_I2C_SPEED +#define CFG_I2C_SPEED 50000 +#endif + +#ifndef CFG_I2C_SLAVE +#define CFG_I2C_SLAVE 0xFE +#endif +/*----------------------------------------------------------------------- + */ + +typedef void (*i2c_ecb_t)(int, int); /* error callback function */ + +/* This structure keeps track of the bd and buffer space usage. */ +typedef struct i2c_state { + int rx_idx; /* index to next free Rx BD */ + int tx_idx; /* index to next free Tx BD */ + void *rxbd; /* pointer to next free Rx BD */ + void *txbd; /* pointer to next free Tx BD */ + int tx_space; /* number of Tx bytes left */ + unsigned char *tx_buf; /* pointer to free Tx area */ + i2c_ecb_t err_cb; /* error callback function */ +} i2c_state_t; + +/* flags for i2c_send() and i2c_receive() */ +#define I2CF_ENABLE_SECONDARY 0x01 /* secondary_address is valid */ +#define I2CF_START_COND 0x02 /* tx: generate start condition */ +#define I2CF_STOP_COND 0x04 /* tx: generate stop condition */ + +/* return codes */ +#define I2CERR_NO_BUFFERS 0x01 /* no more BDs or buffer space */ +#define I2CERR_MSG_TOO_LONG 0x02 /* tried to send/receive to much data */ +#define I2CERR_TIMEOUT 0x03 /* timeout in i2c_doio() */ +#define I2CERR_QUEUE_EMPTY 0x04 /* i2c_doio called without send/receive */ + +/* error callback flags */ +#define I2CECB_RX_ERR 0x10 /* this is a receive error */ +#define I2CECB_RX_ERR_OV 0x02 /* receive overrun error */ +#define I2CECB_RX_MASK 0x0f /* mask for error bits */ +#define I2CECB_TX_ERR 0x20 /* this is a transmit error */ +#define I2CECB_TX_CL 0x01 /* transmit collision error */ +#define I2CECB_TX_UN 0x02 /* transmit underflow error */ +#define I2CECB_TX_NAK 0x04 /* transmit no ack error */ +#define I2CECB_TX_MASK 0x0f /* mask for error bits */ +#define I2CECB_TIMEOUT 0x40 /* this is a timeout error */ + +#define ERROR_I2C_NONE 0 +#define ERROR_I2C_LENGTH 1 + +#define I2C_WRITE_BIT 0x00 +#define I2C_READ_BIT 0x01 + +#define I2C_RXTX_LEN 128 /* maximum tx/rx buffer length */ + + +#define NUM_RX_BDS 4 +#define NUM_TX_BDS 4 +#define MAX_TX_SPACE 256 + +typedef struct I2C_BD +{ + unsigned short status; + unsigned short length; + unsigned char *addr; +} I2C_BD; +#define BD_I2C_TX_START 0x0400 /* special status for i2c: Start condition */ + +#define BD_I2C_TX_CL 0x0001 /* collision error */ +#define BD_I2C_TX_UN 0x0002 /* underflow error */ +#define BD_I2C_TX_NAK 0x0004 /* no acknowledge error */ +#define BD_I2C_TX_ERR (BD_I2C_TX_NAK|BD_I2C_TX_UN|BD_I2C_TX_CL) + +#define BD_I2C_RX_ERR BD_SC_OV + +#ifdef DEBUG_I2C +#define PRINTD(x) printf x +#else +#define PRINTD(x) +#endif + +/* + * Returns the best value of I2BRG to meet desired clock speed of I2C with + * input parameters (clock speed, filter, and predivider value). + * It returns computer speed value and the difference between it and desired + * speed. + */ +static inline int +i2c_roundrate(int hz, int speed, int filter, int modval, + int *brgval, int *totspeed) +{ + int moddiv = 1 << (5-(modval & 3)), brgdiv, div; + + PRINTD(("\t[I2C] trying hz=%d, speed=%d, filter=%d, modval=%d\n", + hz, speed, filter, modval)); + + div = moddiv * speed; + brgdiv = (hz + div - 1) / div; + + PRINTD(("\t\tmoddiv=%d, brgdiv=%d\n", moddiv, brgdiv)); + + *brgval = (brgdiv / 2) - 3 - (2*filter); + + if ((*brgval < 0) || (*brgval > 255)) { + PRINTD(("\t\trejected brgval=%d\n", *brgval)); + return -1; + } + + brgdiv = 2 * (*brgval + 3 + (2 * filter)); + div = moddiv * brgdiv ; + *totspeed = (hz + div - 1) / div; + + PRINTD(("\t\taccepted brgval=%d, totspeed=%d\n", *brgval, *totspeed)); + + return 0; +} + +/* + * Sets the I2C clock predivider and divider to meet required clock speed. + */ +static int i2c_setrate(int hz, int speed) +{ + immap_t *immap = (immap_t *)CFG_IMMR ; + volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c; + int brgval, + modval, /* 0-3 */ + bestspeed_diff = speed, + bestspeed_brgval=0, + bestspeed_modval=0, + bestspeed_filter=0, + totspeed, + filter = 0; /* Use this fixed value */ + + for (modval = 0; modval < 4; modval++) + { + if (i2c_roundrate (hz, speed, filter, modval, &brgval, &totspeed) == 0) + { + int diff = speed - totspeed ; + + if ((diff >= 0) && (diff < bestspeed_diff)) + { + bestspeed_diff = diff ; + bestspeed_modval = modval; + bestspeed_brgval = brgval; + bestspeed_filter = filter; + } + } + } + + PRINTD(("[I2C] Best is:\n")); + PRINTD(("[I2C] CPU=%dhz RATE=%d F=%d I2MOD=%08x I2BRG=%08x DIFF=%dhz\n", + hz, speed, + bestspeed_filter, bestspeed_modval, bestspeed_brgval, + bestspeed_diff)); + + i2c->i2c_i2mod |= ((bestspeed_modval & 3) << 1) | (bestspeed_filter << 3); + i2c->i2c_i2brg = bestspeed_brgval & 0xff; + + PRINTD(("[I2C] i2mod=%08x i2brg=%08x\n", i2c->i2c_i2mod, i2c->i2c_i2brg)); + + return 1 ; +} + +void i2c_init(int speed, int slaveadd) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immap = (immap_t *)CFG_IMMR ; + volatile cpm8260_t *cp = (cpm8260_t *)&immap->im_cpm; + volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c; + volatile iic_t *iip; + ulong rbase, tbase; + volatile I2C_BD *rxbd, *txbd; + uint dpaddr; + + dpaddr = *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])); + if (dpaddr == 0) { + /* need to allocate dual port ram */ + dpaddr = m8260_cpm_dpalloc(64 + + (NUM_RX_BDS * sizeof(I2C_BD)) + (NUM_TX_BDS * sizeof(I2C_BD)) + + MAX_TX_SPACE, 64); + *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])) = dpaddr; + } + + /* + * initialise data in dual port ram: + * + * dpaddr -> parameter ram (64 bytes) + * rbase -> rx BD (NUM_RX_BDS * sizeof(I2C_BD) bytes) + * tbase -> tx BD (NUM_TX_BDS * sizeof(I2C_BD) bytes) + * tx buffer (MAX_TX_SPACE bytes) + */ + + iip = (iic_t *)&immap->im_dprambase[dpaddr]; + memset((void*)iip, 0, sizeof(iic_t)); + + rbase = dpaddr + 64; + tbase = rbase + NUM_RX_BDS * sizeof(I2C_BD); + + /* Disable interrupts */ + i2c->i2c_i2mod = 0x00; + i2c->i2c_i2cmr = 0x00; + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2add = slaveadd; + + /* + * Set the I2C BRG Clock division factor from desired i2c rate + * and current CPU rate (we assume sccr dfbgr field is 0; + * divide BRGCLK by 1) + */ + PRINTD(("[I2C] Setting rate...\n")); + i2c_setrate (gd->brg_clk, CFG_I2C_SPEED) ; + + /* Set I2C controller in master mode */ + i2c->i2c_i2com = 0x01; + + /* Initialize Tx/Rx parameters */ + iip->iic_rbase = rbase; + iip->iic_tbase = tbase; + rxbd = (I2C_BD *)((unsigned char *)&immap->im_dprambase[iip->iic_rbase]); + txbd = (I2C_BD *)((unsigned char *)&immap->im_dprambase[iip->iic_tbase]); + + PRINTD(("[I2C] rbase = %04x\n", iip->iic_rbase)); + PRINTD(("[I2C] tbase = %04x\n", iip->iic_tbase)); + PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + /* Set big endian byte order */ + iip->iic_tfcr = 0x10; + iip->iic_rfcr = 0x10; + + /* Set maximum receive size. */ + iip->iic_mrblr = I2C_RXTX_LEN; + + cp->cp_cpcr = mk_cr_cmd(CPM_CR_I2C_PAGE, + CPM_CR_I2C_SBLOCK, + 0x00, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + + /* Clear events and interrupts */ + i2c->i2c_i2cer = 0xff; + i2c->i2c_i2cmr = 0x00; +} + +static +void i2c_newio(i2c_state_t *state) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR ; + volatile iic_t *iip; + uint dpaddr; + + PRINTD(("[I2C] i2c_newio\n")); + + dpaddr = *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])); + iip = (iic_t *)&immap->im_dprambase[dpaddr]; + state->rx_idx = 0; + state->tx_idx = 0; + state->rxbd = (void*)&immap->im_dprambase[iip->iic_rbase]; + state->txbd = (void*)&immap->im_dprambase[iip->iic_tbase]; + state->tx_space = MAX_TX_SPACE; + state->tx_buf = (uchar*)state->txbd + NUM_TX_BDS * sizeof(I2C_BD); + state->err_cb = NULL; + + PRINTD(("[I2C] rxbd = %08x\n", (int)state->rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)state->txbd)); + PRINTD(("[I2C] tx_buf = %08x\n", (int)state->tx_buf)); + + /* clear the buffer memory */ + memset((char *)state->tx_buf, 0, MAX_TX_SPACE); +} + +static +int i2c_send(i2c_state_t *state, + unsigned char address, + unsigned char secondary_address, + unsigned int flags, + unsigned short size, + unsigned char *dataout) +{ + volatile I2C_BD *txbd; + int i,j; + + PRINTD(("[I2C] i2c_send add=%02d sec=%02d flag=%02d size=%d\n", + address, secondary_address, flags, size)); + + /* trying to send message larger than BD */ + if (size > I2C_RXTX_LEN) + return I2CERR_MSG_TOO_LONG; + + /* no more free bds */ + if (state->tx_idx >= NUM_TX_BDS || state->tx_space < (2 + size)) + return I2CERR_NO_BUFFERS; + + txbd = (I2C_BD *)state->txbd; + txbd->addr = state->tx_buf; + + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + if (flags & I2CF_START_COND) + { + PRINTD(("[I2C] Formatting addresses...\n")); + if (flags & I2CF_ENABLE_SECONDARY) + { + txbd->length = size + 2; /* Length of message plus dest addresses */ + txbd->addr[0] = address << 1; + txbd->addr[1] = secondary_address; + i = 2; + } + else + { + txbd->length = size + 1; /* Length of message plus dest address */ + txbd->addr[0] = address << 1; /* Write destination address to BD */ + i = 1; + } + } + else + { + txbd->length = size; /* Length of message */ + i = 0; + } + + /* set up txbd */ + txbd->status = BD_SC_READY; + if (flags & I2CF_START_COND) + txbd->status |= BD_I2C_TX_START; + if (flags & I2CF_STOP_COND) + txbd->status |= BD_SC_LAST | BD_SC_WRAP; + + /* Copy data to send into buffer */ + PRINTD(("[I2C] copy data...\n")); + for(j = 0; j < size; i++, j++) + txbd->addr[i] = dataout[j]; + + PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + txbd->length, + txbd->status, + txbd->addr[0], + txbd->addr[1])); + + /* advance state */ + state->tx_buf += txbd->length; + state->tx_space -= txbd->length; + state->tx_idx++; + state->txbd = (void*)(txbd + 1); + + return 0; +} + +static +int i2c_receive(i2c_state_t *state, + unsigned char address, + unsigned char secondary_address, + unsigned int flags, + unsigned short size_to_expect, + unsigned char *datain) +{ + volatile I2C_BD *rxbd, *txbd; + + PRINTD(("[I2C] i2c_receive %02d %02d %02d\n", address, secondary_address, flags)); + + /* Expected to receive too much */ + if (size_to_expect > I2C_RXTX_LEN) + return I2CERR_MSG_TOO_LONG; + + /* no more free bds */ + if (state->tx_idx >= NUM_TX_BDS || state->rx_idx >= NUM_RX_BDS + || state->tx_space < 2) + return I2CERR_NO_BUFFERS; + + rxbd = (I2C_BD *)state->rxbd; + txbd = (I2C_BD *)state->txbd; + + PRINTD(("[I2C] rxbd = %08x\n", (int)rxbd)); + PRINTD(("[I2C] txbd = %08x\n", (int)txbd)); + + txbd->addr = state->tx_buf; + + /* set up TXBD for destination address */ + if (flags & I2CF_ENABLE_SECONDARY) + { + txbd->length = 2; + txbd->addr[0] = address << 1; /* Write data */ + txbd->addr[1] = secondary_address; /* Internal address */ + txbd->status = BD_SC_READY; + } + else + { + txbd->length = 1 + size_to_expect; + txbd->addr[0] = (address << 1) | 0x01; + txbd->status = BD_SC_READY; + memset(&txbd->addr[1], 0, txbd->length); + } + + /* set up rxbd for reception */ + rxbd->status = BD_SC_EMPTY; + rxbd->length = size_to_expect; + rxbd->addr = datain; + + txbd->status |= BD_I2C_TX_START; + if (flags & I2CF_STOP_COND) + { + txbd->status |= BD_SC_LAST | BD_SC_WRAP; + rxbd->status |= BD_SC_WRAP; + } + + PRINTD(("[I2C] txbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + txbd->length, + txbd->status, + txbd->addr[0], + txbd->addr[1])); + PRINTD(("[I2C] rxbd: length=0x%04x status=0x%04x addr[0]=0x%02x addr[1]=0x%02x\n", + rxbd->length, + rxbd->status, + rxbd->addr[0], + rxbd->addr[1])); + + /* advance state */ + state->tx_buf += txbd->length; + state->tx_space -= txbd->length; + state->tx_idx++; + state->txbd = (void*)(txbd + 1); + state->rx_idx++; + state->rxbd = (void*)(rxbd + 1); + + return 0; +} + + +static +int i2c_doio(i2c_state_t *state) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR ; + volatile iic_t *iip; + volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c; + volatile I2C_BD *txbd, *rxbd; + int j; + int timeout; + uint dpaddr; + + PRINTD(("[I2C] i2c_doio\n")); + + timeout = TOUT_LOOP * 256; /* arbitrarily long */ + + if (state->tx_idx <= 0 && state->rx_idx <= 0) { + PRINTD(("[I2C] No I/O is queued\n")); + return I2CERR_QUEUE_EMPTY; + } + + dpaddr = *((unsigned short*)(&immap->im_dprambase[PROFF_I2C_BASE])); + iip = (iic_t *)&immap->im_dprambase[dpaddr]; + iip->iic_rbptr = iip->iic_rbase; + iip->iic_tbptr = iip->iic_tbase; + + /* Enable I2C */ + PRINTD(("[I2C] Enabling I2C...\n")); + i2c->i2c_i2mod |= 0x01; + + /* Begin transmission */ + i2c->i2c_i2com |= 0x80; + + /* Loop until transmit & receive completed */ + + txbd = ((I2C_BD*)state->txbd) - 1; + j = 0; + if (state->tx_idx > 0) { + timeout = TOUT_LOOP * txbd->length; + + PRINTD(("[I2C] Transmitting...(txbd=0x%08lx)\n", (ulong)txbd)); + udelay(START_DELAY_US); /* give it time to start */ + while((txbd->status & BD_SC_READY) && (j++ < timeout)) { + udelay(DELAY_US); + if (ctrlc()) + return (-1); + __asm__ __volatile__ ("eieio"); + } + } + + rxbd = ((I2C_BD*)state->rxbd) - 1; + j = 0; + if ((state->rx_idx > 0) && (j < timeout)) { + timeout = TOUT_LOOP * rxbd->length; + PRINTD(("[I2C] Receiving...(rxbd=0x%08lx)\n", (ulong)rxbd)); + udelay(START_DELAY_US); /* give it time to start */ + while((rxbd->status & BD_SC_EMPTY) && (j++ < timeout)) { + udelay(DELAY_US); + if (ctrlc()) + return (-1); + __asm__ __volatile__ ("eieio"); + } + } + + /* Turn off I2C */ + i2c->i2c_i2mod &= ~0x01; + + if (state->err_cb != NULL) { + int n, i, b; + + /* + * if we have an error callback function, look at the + * error bits in the bd status and pass them back + */ + + if ((n = state->tx_idx) > 0) { + for (i = 0; i < n; i++) { + txbd = ((I2C_BD*)state->txbd) - (n - i); + if ((b = txbd->status & BD_I2C_TX_ERR) != 0) + (*state->err_cb)(I2CECB_TX_ERR|b, i); + } + } + + if ((n = state->rx_idx) > 0) { + for (i = 0; i < n; i++) { + rxbd = ((I2C_BD*)state->rxbd) - (n - i); + if ((b = rxbd->status & BD_I2C_RX_ERR) != 0) + (*state->err_cb)(I2CECB_RX_ERR|b, i); + } + } + + if (j >= timeout) + (*state->err_cb)(I2CECB_TIMEOUT, 0); + } + + /* sort out errors and return appropriate good/error status */ + if(j >= timeout) + return(I2CERR_TIMEOUT); + if((txbd->status & BD_I2C_TX_ERR) != 0) + return(I2CECB_TX_ERR | (txbd->status & I2CECB_TX_MASK)); + if((rxbd->status & BD_I2C_RX_ERR) != 0) + return(I2CECB_RX_ERR | (rxbd->status & I2CECB_RX_MASK)); + + return(0); +} + +static int had_tx_nak; + +static void +i2c_test_callback(int flags, int xnum) +{ + if ((flags & I2CECB_TX_ERR) && (flags & I2CECB_TX_NAK)) + had_tx_nak = 1; +} + +int i2c_probe(uchar chip) +{ + i2c_state_t state; + int rc; + uchar buf[1]; + + i2c_newio(&state); + + state.err_cb = i2c_test_callback; + had_tx_nak = 0; + + rc = i2c_receive(&state, chip, 0, I2CF_START_COND|I2CF_STOP_COND, 1, buf); + + if (rc != 0) + return (rc); + + rc = i2c_doio(&state); + + if ((rc != 0) && (rc != I2CERR_TIMEOUT)) + return (rc); + + return (had_tx_nak); +} + + +int +i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + i2c_state_t state; + uchar xaddr[4]; + int rc; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + +#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of address + * and the extra bits end up in the "chip address" bit slots. + * This makes a 24WC08 (1Kbyte) chip look like four 256 byte + * chips. + * + * Note that we consider the length of the address field to still + * be one byte because the extra address bits are hidden in the + * chip address. + */ + chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + i2c_newio(&state); + + rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); + if (rc != 0) { + printf("i2c_read: i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_receive(&state, chip, 0, I2CF_STOP_COND, len, buffer); + if (rc != 0) { + printf("i2c_read: i2c_receive failed (%d)\n", rc); + return 1; + } + + rc = i2c_doio(&state); + if (rc != 0) { + printf("i2c_read: i2c_doio failed (%d)\n", rc); + return 1; + } + return 0; +} + +int +i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + i2c_state_t state; + uchar xaddr[4]; + int rc; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + +#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of address + * and the extra bits end up in the "chip address" bit slots. + * This makes a 24WC08 (1Kbyte) chip look like four 256 byte + * chips. + * + * Note that we consider the length of the address field to still + * be one byte because the extra address bits are hidden in the + * chip address. + */ + chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + i2c_newio(&state); + + rc = i2c_send(&state, chip, 0, I2CF_START_COND, alen, &xaddr[4-alen]); + if (rc != 0) { + printf("i2c_write: first i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_send(&state, 0, 0, I2CF_STOP_COND, len, buffer); + if (rc != 0) { + printf("i2c_write: second i2c_send failed (%d)\n", rc); + return 1; + } + + rc = i2c_doio(&state); + if (rc != 0) { + printf("i2c_write: i2c_doio failed (%d)\n", rc); + return 1; + } + return 0; +} + +uchar +i2c_reg_read(uchar chip, uchar reg) +{ + char 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); +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/cpu/mpc8xx/cpu.c b/cpu/mpc8xx/cpu.c new file mode 100644 index 0000000000..b1b58b089b --- /dev/null +++ b/cpu/mpc8xx/cpu.c @@ -0,0 +1,516 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * m8xx.c + * + * CPU specific code + * + * written or collected and sometimes rewritten by + * Magnus Damm + * + * minor modifications by + * Wolfgang Denk + */ + +#include +#include +#include +#include +#include + +static char *cpu_warning = "\n " \ + "*** Warning: CPU Core has Silicon Bugs -- Check the Errata ***"; + +#if ((defined(CONFIG_MPC860) || defined(CONFIG_MPC855)) && \ + !defined(CONFIG_MPC862)) +# ifdef CONFIG_MPC855 +# define ID_STR "PC855" +# else +# define ID_STR "PC860" +# endif + +static int check_CPU (long clock, uint pvr, uint immr) +{ + volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); + uint k, m; + char buf[32]; + char pre = 'X'; + char *mid = "xx"; + char *suf; + + /* the highest 16 bits should be 0x0050 for a 860 */ + + if ((pvr >> 16) != 0x0050) + return -1; + + k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); + m = 0; + + switch (k) { + case 0x00020001: pre = 'p'; suf = ""; break; + case 0x00030001: suf = ""; break; + case 0x00120003: suf = "A"; break; + case 0x00130003: suf = "A3"; break; + + case 0x00200004: suf = "B"; break; + + case 0x00300004: suf = "C"; break; + case 0x00310004: suf = "C1"; m = 1; + break; + + case 0x00200064: mid = "SR"; suf = "B"; break; + case 0x00300065: mid = "SR"; suf = "C"; break; + case 0x00310065: mid = "SR"; suf = "C1"; m = 1; break; + case 0x05010000: suf = "D3"; m = 1; break; + case 0x05020000: suf = "D4"; m = 1; break; + + /* this value is not documented anywhere */ + case 0x40000000: pre = 'P'; suf = "D"; m = 1; break; + + default: suf = NULL; break; + } + + if (suf) + printf ("%c" ID_STR "%sZPnn%s", pre, mid, suf); + else + printf ("unknown M" ID_STR " (0x%08x)", k); + + printf (" at %s MHz:", strmhz (buf, clock)); + + printf (" %u kB I-Cache", checkicache () >> 10); + printf (" %u kB D-Cache", checkdcache () >> 10); + + /* lets check and see if we're running on a 860T (or P?) */ + + immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; + if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { + printf (" FEC present"); + } + + if (!m) { + puts (cpu_warning); + } + + putc ('\n'); + + return 0; +} + +#elif defined(CONFIG_MPC862) + +static int check_CPU (long clock, uint pvr, uint immr) +{ + volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); + uint k, m; + char buf[32]; + char pre = 'X'; + char *mid = "xx"; + char *suf; + + /* the highest 16 bits should be 0x0050 for a 8xx */ + + if ((pvr >> 16) != 0x0050) + return -1; + + k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); + m = 0; + + switch (k) { + + /* this value is not documented anywhere */ + case 0x06000000: mid = "P"; suf = "0"; break; + case 0x06010001: mid = "P"; suf = "A"; m = 1; break; + case 0x07000003: mid = "P"; suf = "B"; m = 1; break; + default: suf = NULL; break; + } + + if (suf) + printf ("%cPC862%sZPnn%s", pre, mid, suf); + else + printf ("unknown MPC862 (0x%08x)", k); + + printf (" at %s MHz:", strmhz (buf, clock)); + + printf (" %u kB I-Cache", checkicache () >> 10); + printf (" %u kB D-Cache", checkdcache () >> 10); + + /* lets check and see if we're running on a 862T (or P?) */ + + immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; + if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { + printf (" FEC present"); + } + + if (!m) { + puts (cpu_warning); + } + + putc ('\n'); + + return 0; +} + +#elif defined(CONFIG_MPC823) + +static int check_CPU (long clock, uint pvr, uint immr) +{ + volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); + uint k, m; + char buf[32]; + char *suf; + + /* the highest 16 bits should be 0x0050 for a 8xx */ + + if ((pvr >> 16) != 0x0050) + return -1; + + k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); + m = 0; + + switch (k) { + /* MPC823 */ + case 0x20000000: suf = "0"; break; + case 0x20010000: suf = "0.1"; break; + case 0x20020000: suf = "Z2/3"; break; + case 0x20020001: suf = "Z3"; break; + case 0x21000000: suf = "A"; break; + case 0x21010000: suf = "B"; m = 1; break; + case 0x21010001: suf = "B2"; m = 1; break; + /* MPC823E */ + case 0x24010000: suf = NULL; + puts ("PPC823EZTnnB2"); + m = 1; + break; + default: + suf = NULL; + printf ("unknown MPC823 (0x%08x)", k); + break; + } + if (suf) + printf ("PPC823ZTnn%s", suf); + + printf (" at %s MHz:", strmhz (buf, clock)); + + printf (" %u kB I-Cache", checkicache () >> 10); + printf (" %u kB D-Cache", checkdcache () >> 10); + + /* lets check and see if we're running on a 860T (or P?) */ + + immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; + if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { + puts (" FEC present"); + } + + if (!m) { + puts (cpu_warning); + } + + putc ('\n'); + + return 0; +} + +#elif defined(CONFIG_MPC850) + +static int check_CPU (long clock, uint pvr, uint immr) +{ + volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000); + uint k, m; + char buf[32]; + + /* the highest 16 bits should be 0x0050 for a 8xx */ + + if ((pvr >> 16) != 0x0050) + return -1; + + k = (immr << 16) | *((ushort *) & immap->im_cpm.cp_dparam[0xB0]); + m = 0; + + switch (k) { + case 0x20020001: + printf ("XPC850xxZT"); + break; + case 0x21000065: + printf ("XPC850xxZTA"); + break; + case 0x21010067: + printf ("XPC850xxZTB"); + m = 1; + break; + case 0x21020068: + printf ("XPC850xxZTC"); + m = 1; + break; + default: + printf ("unknown MPC850 (0x%08x)", k); + } + printf (" at %s MHz:", strmhz (buf, clock)); + + printf (" %u kB I-Cache", checkicache () >> 10); + printf (" %u kB D-Cache", checkdcache () >> 10); + + /* lets check and see if we're running on a 850T (or P?) */ + + immap->im_cpm.cp_fec.fec_addr_low = 0x12345678; + if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) { + printf (" FEC present"); + } + + if (!m) { + puts (cpu_warning); + } + + putc ('\n'); + + return 0; +} +#else +#error CPU undefined +#endif +/* ------------------------------------------------------------------------- */ + +int checkcpu (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + ulong clock = gd->cpu_clk; + uint immr = get_immr (0); /* Return full IMMR contents */ + uint pvr = get_pvr (); + + puts ("CPU: "); + + /* 850 has PARTNUM 20 */ + /* 801 has PARTNUM 10 */ + return check_CPU (clock, pvr, immr); +} + +/* ------------------------------------------------------------------------- */ +/* L1 i-cache */ +/* the standard 860 has 128 sets of 16 bytes in 2 ways (= 4 kB) */ +/* the 860 P (plus) has 256 sets of 16 bytes in 4 ways (= 16 kB) */ + +int checkicache (void) +{ + volatile immap_t *immap = (immap_t *) CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + u32 cacheon = rd_ic_cst () & IDC_ENABLED; + +#ifdef CONFIG_IP860 + u32 k = memctl->memc_br1 & ~0x00007fff; /* probe in flash memoryarea */ +#else + u32 k = memctl->memc_br0 & ~0x00007fff; /* probe in flash memoryarea */ +#endif + u32 m; + u32 lines = -1; + + wr_ic_cst (IDC_UNALL); + wr_ic_cst (IDC_INVALL); + wr_ic_cst (IDC_DISABLE); + __asm__ volatile ("isync"); + + while (!((m = rd_ic_cst ()) & IDC_CERR2)) { + wr_ic_adr (k); + wr_ic_cst (IDC_LDLCK); + __asm__ volatile ("isync"); + + lines++; + k += 0x10; /* the number of bytes in a cacheline */ + } + + wr_ic_cst (IDC_UNALL); + wr_ic_cst (IDC_INVALL); + + if (cacheon) + wr_ic_cst (IDC_ENABLE); + else + wr_ic_cst (IDC_DISABLE); + + __asm__ volatile ("isync"); + + return lines << 4; +}; + +/* ------------------------------------------------------------------------- */ +/* L1 d-cache */ +/* the standard 860 has 128 sets of 16 bytes in 2 ways (= 4 kB) */ +/* the 860 P (plus) has 256 sets of 16 bytes in 2 ways (= 8 kB) */ +/* call with cache disabled */ + +int checkdcache (void) +{ + volatile immap_t *immap = (immap_t *) CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + u32 cacheon = rd_dc_cst () & IDC_ENABLED; + +#ifdef CONFIG_IP860 + u32 k = memctl->memc_br1 & ~0x00007fff; /* probe in flash memoryarea */ +#else + u32 k = memctl->memc_br0 & ~0x00007fff; /* probe in flash memoryarea */ +#endif + u32 m; + u32 lines = -1; + + wr_dc_cst (IDC_UNALL); + wr_dc_cst (IDC_INVALL); + wr_dc_cst (IDC_DISABLE); + + while (!((m = rd_dc_cst ()) & IDC_CERR2)) { + wr_dc_adr (k); + wr_dc_cst (IDC_LDLCK); + lines++; + k += 0x10; /* the number of bytes in a cacheline */ + } + + wr_dc_cst (IDC_UNALL); + wr_dc_cst (IDC_INVALL); + + if (cacheon) + wr_dc_cst (IDC_ENABLE); + else + wr_dc_cst (IDC_DISABLE); + + return lines << 4; +}; + +/* ------------------------------------------------------------------------- */ + +void upmconfig (uint upm, uint * table, uint size) +{ + uint i; + uint addr = 0; + volatile immap_t *immap = (immap_t *) CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + + for (i = 0; i < size; i++) { + memctl->memc_mdr = table[i]; /* (16-15) */ + memctl->memc_mcr = addr | upm; /* (16-16) */ + addr++; + } +} + +/* ------------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, + char *argv[]) +{ + ulong msr, addr; + + volatile immap_t *immap = (immap_t *) CFG_IMMR; + + immap->im_clkrst.car_plprcr |= PLPRCR_CSR; /* Checkstop Reset enable */ + + /* Interrupts and MMU off */ + __asm__ volatile ("mtspr 81, 0"); + __asm__ volatile ("mfmsr %0":"=r" (msr)); + + msr &= ~0x1030; + __asm__ volatile ("mtmsr %0"::"r" (msr)); + + /* + * Trying to execute the next instruction at a non-existing address + * should cause a machine check, resulting in reset + */ +#ifdef CFG_RESET_ADDRESS + addr = CFG_RESET_ADDRESS; +#else + /* + * note: when CFG_MONITOR_BASE points to a RAM address, CFG_MONITOR_BASE + * - sizeof (ulong) is usually a valid address. Better pick an address + * known to be invalid on your system and assign it to CFG_RESET_ADDRESS. + * "(ulong)-1" used to be a good choice for many systems... + */ + addr = CFG_MONITOR_BASE - sizeof (ulong); +#endif + ((void (*)(void)) addr) (); + return 1; +} + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * + * See table 15-5 pp. 15-16, and SCCR[RTSEL] pp. 15-27. + */ +unsigned long get_tbclk (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile immap_t *immr = (volatile immap_t *) CFG_IMMR; + ulong oscclk, factor; + + if (immr->im_clkrst.car_sccr & SCCR_TBS) { + return (gd->cpu_clk / 16); + } + + factor = (((CFG_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); +} + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ + int re_enable = disable_interrupts (); + + reset_8xx_watchdog ((immap_t *) CFG_IMMR); + if (re_enable) + enable_interrupts (); +} + +void reset_8xx_watchdog (volatile immap_t * immr) +{ +# if defined(CONFIG_LWMON) + /* + * The LWMON board uses a MAX6301 Watchdog + * with the trigger pin connected to port PA.7 + * + * (The old board version used a MAX706TESA Watchdog, which + * had to be handled exactly the same.) + */ +# define WATCHDOG_BIT 0x0100 + immr->im_ioport.iop_papar &= ~(WATCHDOG_BIT); /* GPIO */ + immr->im_ioport.iop_padir |= WATCHDOG_BIT; /* Output */ + immr->im_ioport.iop_paodr &= ~(WATCHDOG_BIT); /* active output */ + + immr->im_ioport.iop_padat ^= WATCHDOG_BIT; /* Toggle WDI */ +# else + /* + * All other boards use the MPC8xx Internal Watchdog + */ + immr->im_siu_conf.sc_swsr = 0x556c; /* write magic1 */ + immr->im_siu_conf.sc_swsr = 0xaa39; /* write magic2 */ +# endif /* CONFIG_LWMON */ +} + +#endif /* CONFIG_WATCHDOG */ + +/* ------------------------------------------------------------------------- */ diff --git a/cpu/mpc8xx/fec.c b/cpu/mpc8xx/fec.c new file mode 100644 index 0000000000..d43dcaafdb --- /dev/null +++ b/cpu/mpc8xx/fec.c @@ -0,0 +1,710 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#undef ET_DEBUG + +#if (CONFIG_COMMANDS & CFG_CMD_NET) && defined(FEC_ENET) + +#ifdef CFG_DISCOVER_PHY +#include +static void mii_discover_phy(void); +#endif + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 + +#define TX_BUF_CNT 2 + +#define TOUT_LOOP 100 + +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 + + +static char txbuf[DBUF_LENGTH]; + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +/* + * FEC Ethernet Tx and Rx buffer descriptors allocated at the + * immr->udata_bd address on Dual-Port RAM + * Provide for Double Buffering + */ + +typedef volatile struct CommonBufferDescriptor { + cbd_t rxbd[PKTBUFSRX]; /* Rx BD */ + cbd_t txbd[TX_BUF_CNT]; /* Tx BD */ +} RTXBD; + +static RTXBD *rtx = NULL; + +static int fec_send(struct eth_device* dev, volatile void *packet, int length); +static int fec_recv(struct eth_device* dev); +static int fec_init(struct eth_device* dev, bd_t * bd); +static void fec_halt(struct eth_device* dev); + +int fec_initialize(bd_t *bis) +{ + struct eth_device* dev; + + dev = (struct eth_device*) malloc(sizeof *dev); + + sprintf(dev->name, "FEC ETHERNET"); + dev->iobase = 0; + dev->priv = 0; + dev->init = fec_init; + dev->halt = fec_halt; + dev->send = fec_send; + dev->recv = fec_recv; + + eth_register(dev); + + return 1; +} + +static int fec_send(struct eth_device* dev, volatile void *packet, int length) +{ + int j, rc; + volatile immap_t *immr = (immap_t *) CFG_IMMR; + volatile fec_t *fecp = &(immr->im_cpm.cp_fec); + + /* section 16.9.23.3 + * Wait for ready + */ + j = 0; + while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j=TOUT_LOOP) { + printf("TX not ready\n"); + } + + rtx->txbd[txIdx].cbd_bufaddr = (uint)packet; + rtx->txbd[txIdx].cbd_datlen = length; + rtx->txbd[txIdx].cbd_sc |= BD_ENET_TX_READY | BD_ENET_TX_LAST; + __asm__ ("eieio"); + + /* Activate transmit Buffer Descriptor polling */ + fecp->fec_x_des_active = 0x01000000; /* Descriptor polling active */ + + j = 0; + while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j=TOUT_LOOP) { + printf("TX timeout\n"); + } +#ifdef ET_DEBUG + printf("%s[%d] %s: cycles: %d status: %x retry cnt: %d\n", + __FILE__,__LINE__,__FUNCTION__,j,rtx->txbd[txIdx].cbd_sc, + (rtx->txbd[txIdx].cbd_sc & 0x003C)>>2); +#endif + /* return only status bits */; + rc = (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_STATS); + + txIdx = (txIdx + 1) % TX_BUF_CNT; + + return rc; +} + +static int fec_recv(struct eth_device* dev) +{ + int length; + volatile immap_t *immr = (immap_t *) CFG_IMMR; + volatile fec_t *fecp = &(immr->im_cpm.cp_fec); + + for (;;) { + /* section 16.9.23.2 */ + if (rtx->rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + length = rtx->rxbd[rxIdx].cbd_datlen; + + if (rtx->rxbd[rxIdx].cbd_sc & 0x003f) { +#ifdef ET_DEBUG + printf("%s[%d] err: %x\n", + __FUNCTION__,__LINE__,rtx->rxbd[rxIdx].cbd_sc); +#endif + } else { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[rxIdx], length - 4); + } + + /* Give the buffer back to the FEC. */ + rtx->rxbd[rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((rxIdx + 1) >= PKTBUFSRX) { + rtx->rxbd[PKTBUFSRX - 1].cbd_sc = (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); + rxIdx = 0; + } else { + rtx->rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + rxIdx++; + } + + __asm__ ("eieio"); + + /* Try to fill Buffer Descriptors */ + fecp->fec_r_des_active = 0x01000000; /* Descriptor polling active */ + } + + return length; +} + +/************************************************************** + * + * FEC Ethernet Initialization Routine + * + *************************************************************/ + +#define FEC_ECNTRL_PINMUX 0x00000004 +#define FEC_ECNTRL_ETHER_EN 0x00000002 +#define FEC_ECNTRL_RESET 0x00000001 + +#define FEC_RCNTRL_BC_REJ 0x00000010 +#define FEC_RCNTRL_PROM 0x00000008 +#define FEC_RCNTRL_MII_MODE 0x00000004 +#define FEC_RCNTRL_DRT 0x00000002 +#define FEC_RCNTRL_LOOP 0x00000001 + +#define FEC_TCNTRL_FDEN 0x00000004 +#define FEC_TCNTRL_HBC 0x00000002 +#define FEC_TCNTRL_GTS 0x00000001 + +#define FEC_RESET_DELAY 50 + +static int fec_init(struct eth_device* dev, bd_t * bd) +{ + + int i; + volatile immap_t *immr = (immap_t *) CFG_IMMR; + volatile fec_t *fecp = &(immr->im_cpm.cp_fec); + +#if defined(CONFIG_FADS) && defined(CONFIG_MPC860T) + /* configure FADS for fast (FEC) ethernet, half-duplex */ + /* The LXT970 needs about 50ms to recover from reset, so + * wait for it by discovering the PHY before leaving eth_init(). + */ + { + volatile uint *bcsr4 = (volatile uint *) BCSR4; + *bcsr4 = (*bcsr4 & ~(BCSR4_FETH_EN | BCSR4_FETHCFG1)) + | (BCSR4_FETHCFG0 | BCSR4_FETHFDE | BCSR4_FETHRST); + + /* reset the LXT970 PHY */ + *bcsr4 &= ~BCSR4_FETHRST; + udelay (10); + *bcsr4 |= BCSR4_FETHRST; + udelay (10); + } +#endif + /* Whack a reset. + * A delay is required between a reset of the FEC block and + * initialization of other FEC registers because the reset takes + * some time to complete. If you don't delay, subsequent writes + * to FEC registers might get killed by the reset routine which is + * still in progress. + */ + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; + for (i = 0; + (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); + ++i) { + udelay (1); + } + if (i == FEC_RESET_DELAY) { + printf ("FEC_RESET_DELAY timeout\n"); + return 0; + } + + /* We use strictly polling mode only + */ + fecp->fec_imask = 0; + + /* Clear any pending interrupt + */ + fecp->fec_ievent = 0xffc0; + + /* No need to set the IVEC register */ + + /* Set station address + */ +#define ea eth_get_dev()->enetaddr + fecp->fec_addr_low = (ea[0] << 24) | (ea[1] << 16) | + (ea[2] << 8) | (ea[3] ) ; + fecp->fec_addr_high = (ea[4] << 8) | (ea[5] ) ; +#undef ea + + /* Clear multicast address hash table + */ + fecp->fec_hash_table_high = 0; + fecp->fec_hash_table_low = 0; + + /* Set maximum receive buffer size. + */ + fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; + + /* Set maximum frame length + */ + fecp->fec_r_hash = PKT_MAXBUF_SIZE; + + /* + * Setup Buffers and Buffer Desriptors + */ + rxIdx = 0; + txIdx = 0; + + if (!rtx) { +#ifdef CFG_ALLOC_DPRAM + rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + dpram_alloc_align(sizeof(RTXBD),8)); +#else + rtx = (RTXBD *) (immr->im_cpm.cp_dpmem + CPM_FEC_BASE); +#endif + } + /* + * Setup Receiver Buffer Descriptors (13.14.24.18) + * Settings: + * Empty, Wrap + */ + for (i = 0; i < PKTBUFSRX; i++) { + rtx->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + rtx->rxbd[i].cbd_datlen = 0; /* Reset */ + rtx->rxbd[i].cbd_bufaddr = (uint) NetRxPackets[i]; + } + rtx->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* + * Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) + * Settings: + * Last, Tx CRC + */ + for (i = 0; i < TX_BUF_CNT; i++) { + rtx->txbd[i].cbd_sc = BD_ENET_TX_LAST | BD_ENET_TX_TC; + rtx->txbd[i].cbd_datlen = 0; /* Reset */ + rtx->txbd[i].cbd_bufaddr = (uint) (&txbuf[0]); + } + rtx->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* Set receive and transmit descriptor base + */ + fecp->fec_r_des_start = (unsigned int) (&rtx->rxbd[0]); + fecp->fec_x_des_start = (unsigned int) (&rtx->txbd[0]); + + /* Enable MII mode + */ +#if 0 /* Full duplex mode */ + fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE; + fecp->fec_x_cntrl = FEC_TCNTRL_FDEN; +#else /* Half duplex mode */ + fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE | FEC_RCNTRL_DRT; + fecp->fec_x_cntrl = 0; +#endif + + /* Enable big endian and don't care about SDMA FC. + */ + fecp->fec_fun_code = 0x78000000; + + /* Set MII speed to 2.5 MHz or slightly below. + * According to the MPC860T (Rev. D) Fast ethernet controller user + * manual (6.2.14), + * the MII management interface clock must be less than or equal + * to 2.5 MHz. + * This MDC frequency is equal to system clock / (2 * MII_SPEED). + * Then MII_SPEED = system_clock / 2 * 2,5 Mhz. + */ + fecp->fec_mii_speed = ((bd->bi_busfreq + 4999999) / 5000000) << 1; + +#if !defined(CONFIG_ICU862) && !defined(CONFIG_IAD210) + /* Configure all of port D for MII. + */ + immr->im_ioport.iop_pdpar = 0x1fff; + + /* Bits moved from Rev. D onward */ + if ((get_immr (0) & 0xffff) < 0x0501) { + immr->im_ioport.iop_pddir = 0x1c58; /* Pre rev. D */ + } else { + immr->im_ioport.iop_pddir = 0x1fff; /* Rev. D and later */ + } +#else + /* Configure port A for MII. + */ + +#if defined(CONFIG_ICU862) && defined(CFG_DISCOVER_PHY) + + /* On the ICU862 board the MII-MDC pin is routed to PD8 pin + * of CPU, so for this board we need to configure Utopia and + * enable PD8 to MII-MDC function */ + immr->im_ioport.iop_pdpar |= 0x4080; +#endif + + /* Has Utopia been configured? */ + if (immr->im_ioport.iop_pdpar & (0x8000 >> 1)) { + /* + * YES - Use MUXED mode for UTOPIA bus. + * This frees Port A for use by MII (see 862UM table 41-6). + */ + immr->im_ioport.utmode &= ~0x80; + } else { + /* + * NO - set SPLIT mode for UTOPIA bus. + * + * This doesn't really effect UTOPIA (which isn't + * enabled anyway) but just tells the 862 + * to use port A for MII (see 862UM table 41-6). + */ + immr->im_ioport.utmode |= 0x80; + } +#endif /* !defined(CONFIG_ICU862) */ + + rxIdx = 0; + txIdx = 0; + + /* Now enable the transmit and receive processing + */ + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN; + +#ifdef CFG_DISCOVER_PHY + /* wait for the PHY to wake up after reset + */ + mii_discover_phy(); +#endif + + /* And last, try to fill Rx Buffer Descriptors */ + fecp->fec_r_des_active = 0x01000000; /* Descriptor polling active */ + + return 1; +} + + + +static void fec_halt(struct eth_device* dev) +{ +#if 0 + volatile immap_t *immr = (immap_t *)CFG_IMMR; + immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#endif +} + +#if 0 +void restart(void) +{ + volatile immap_t *immr = (immap_t *)CFG_IMMR; + immr->im_cpm.cp_scc[SCC_ENET].scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); +} +#endif + +#if defined(CFG_DISCOVER_PHY) || (CONFIG_COMMANDS & CFG_CMD_MII) + +static int phyaddr = -1; /* didn't find a PHY yet */ +static uint phytype; + +/* Make MII read/write commands for the FEC. +*/ + +#define mk_mii_read(ADDR, REG) (0x60020000 | ((ADDR << 23) | \ + (REG & 0x1f) << 18)) + +#define mk_mii_write(ADDR, REG, VAL) (0x50020000 | ((ADDR << 23) | \ + (REG & 0x1f) << 18) | \ + (VAL & 0xffff)) + +/* Interrupt events/masks. +*/ +#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ +#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ +#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ +#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ +#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */ +#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ +#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */ +#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ +#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ +#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ + +/* PHY identification + */ +#define PHY_ID_LXT970 0x78100000 /* LXT970 */ +#define PHY_ID_LXT971 0x001378e0 /* LXT971 and 972 */ +#define PHY_ID_82555 0x02a80150 /* Intel 82555 */ +#define PHY_ID_QS6612 0x01814400 /* QS6612 */ +#define PHY_ID_AMD79C784 0x00225610 /* AMD 79C784 */ +#define PHY_ID_LSI80225 0x0016f870 /* LSI 80225 */ +#define PHY_ID_LSI80225B 0x0016f880 /* LSI 80225/B */ + + +/* send command to phy using mii, wait for result */ +static uint +mii_send(uint mii_cmd) +{ + uint mii_reply; + volatile fec_t *ep; + + ep = &(((immap_t *)CFG_IMMR)->im_cpm.cp_fec); + + ep->fec_mii_data = mii_cmd; /* command to phy */ + + /* wait for mii complete */ + while (!(ep->fec_ievent & FEC_ENET_MII)) + ; /* spin until done */ + mii_reply = ep->fec_mii_data; /* result from phy */ + ep->fec_ievent = FEC_ENET_MII; /* clear MII complete */ +#if 0 + printf("%s[%d] %s: sent=0x%8.8x, reply=0x%8.8x\n", + __FILE__,__LINE__,__FUNCTION__,mii_cmd,mii_reply); +#endif + return (mii_reply & 0xffff); /* data read from phy */ +} +#endif /* CFG_DISCOVER_PHY || (CONFIG_COMMANDS & CFG_CMD_MII) */ + +#if defined(CFG_DISCOVER_PHY) +static void +mii_discover_phy(void) +{ +#define MAX_PHY_PASSES 11 + uint phyno; + int pass; + + phyaddr = -1; /* didn't find a PHY yet */ + for (pass = 1; pass <= MAX_PHY_PASSES && phyaddr < 0; ++pass) { + if (pass > 1) { + /* PHY may need more time to recover from reset. + * The LXT970 needs 50ms typical, no maximum is + * specified, so wait 10ms before try again. + * With 11 passes this gives it 100ms to wake up. + */ + udelay(10000); /* wait 10ms */ + } + for (phyno = 0; phyno < 32 && phyaddr < 0; ++phyno) { + phytype = mii_send(mk_mii_read(phyno, PHY_PHYIDR1)); +#ifdef ET_DEBUG + printf("PHY type 0x%x pass %d type ", phytype, pass); +#endif + if (phytype != 0xffff) { + phyaddr = phyno; + phytype <<= 16; + phytype |= mii_send(mk_mii_read(phyno, + PHY_PHYIDR2)); + +#ifdef ET_DEBUG + printf("PHY @ 0x%x pass %d type ",phyno,pass); + switch (phytype & 0xfffffff0) { + case PHY_ID_LXT970: + printf("LXT970\n"); + break; + case PHY_ID_LXT971: + printf("LXT971\n"); + break; + case PHY_ID_82555: + printf("82555\n"); + break; + case PHY_ID_QS6612: + printf("QS6612\n"); + break; + case PHY_ID_AMD79C784: + printf("AMD79C784\n"); + break; + case PHY_ID_LSI80225B: + printf("LSI L80225/B\n"); + break; + default: + printf("0x%08x\n", phytype); + break; + } +#endif + } + } + } + if (phyaddr < 0) { + printf("No PHY device found.\n"); + } +} +#endif /* CFG_DISCOVER_PHY */ + +#if (CONFIG_COMMANDS & CFG_CMD_MII) && !defined(CONFIG_BITBANGMII) + +static int mii_init_done = 0; + +/**************************************************************************** + * mii_init -- Initialize the MII for MII command without ethernet + * This function is a subset of eth_init + **************************************************************************** + */ +void mii_init (void) +{ + DECLARE_GLOBAL_DATA_PTR; + bd_t *bd = gd->bd; + + volatile immap_t *immr = (immap_t *) CFG_IMMR; + volatile fec_t *fecp = &(immr->im_cpm.cp_fec); + int i; + + if (mii_init_done != 0) { + return; + } + + /* Whack a reset. + * A delay is required between a reset of the FEC block and + * initialization of other FEC registers because the reset takes + * some time to complete. If you don't delay, subsequent writes + * to FEC registers might get killed by the reset routine which is + * still in progress. + */ + + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET; + for (i = 0; + (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY); + ++i) { + udelay (1); + } + if (i == FEC_RESET_DELAY) { + printf ("FEC_RESET_DELAY timeout\n"); + return; + } + + /* We use strictly polling mode only + */ + fecp->fec_imask = 0; + + /* Clear any pending interrupt + */ + fecp->fec_ievent = 0xffc0; + + /* Set MII speed to 2.5 MHz or slightly below. + * According to the MPC860T (Rev. D) Fast ethernet controller user + * manual (6.2.14), + * the MII management interface clock must be less than or equal + * to 2.5 MHz. + * This MDC frequency is equal to system clock / (2 * MII_SPEED). + * Then MII_SPEED = system_clock / 2 * 2,5 Mhz. + */ + fecp->fec_mii_speed = ((bd->bi_busfreq + 4999999) / 5000000) << 1; + +#if !defined(CONFIG_ICU862) && !defined(CONFIG_IAD210) + /* Configure all of port D for MII. + */ + immr->im_ioport.iop_pdpar = 0x1fff; + + /* Bits moved from Rev. D onward */ + if ((get_immr (0) & 0xffff) < 0x0501) { + immr->im_ioport.iop_pddir = 0x1c58; /* Pre rev. D */ + } else { + immr->im_ioport.iop_pddir = 0x1fff; /* Rev. D and later */ + } +#else + /* Configure port A for MII. + */ + +#if defined(CONFIG_ICU862) + + /* On the ICU862 board the MII-MDC pin is routed to PD8 pin + * of CPU, so for this board we need to configure Utopia and + * enable PD8 to MII-MDC function */ + immr->im_ioport.iop_pdpar |= 0x4080; +#endif + + /* Has Utopia been configured? */ + if (immr->im_ioport.iop_pdpar & (0x8000 >> 1)) { + /* + * YES - Use MUXED mode for UTOPIA bus. + * This frees Port A for use by MII (see 862UM table 41-6). + */ + immr->im_ioport.utmode &= ~0x80; + } else { + /* + * NO - set SPLIT mode for UTOPIA bus. + * + * This doesn't really effect UTOPIA (which isn't + * enabled anyway) but just tells the 862 + * to use port A for MII (see 862UM table 41-6). + */ + immr->im_ioport.utmode |= 0x80; + } +#endif /* !defined(CONFIG_ICU862) */ + /* Now enable the transmit and receive processing + */ + fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN; + + mii_init_done = 1; +} +/***************************************************************************** + * Read and write a MII PHY register, routines used by MII Utilities + * + * FIXME: These routines are expected to return 0 on success, but mii_send + * does _not_ return an error code. Maybe 0xFFFF means error, i.e. + * no PHY connected... + * For now always return 0. + * FIXME: These routines only work after calling eth_init() at least once! + * Otherwise they hang in mii_send() !!! Sorry! + *****************************************************************************/ + +int miiphy_read(unsigned char addr, unsigned char reg, unsigned short *value) +{ + short rdreg; /* register working value */ + +#ifdef MII_DEBUG + printf ("miiphy_read(0x%x) @ 0x%x = ", reg, addr); +#endif + rdreg = mii_send(mk_mii_read(addr, reg)); + + *value = rdreg; + +#ifdef MII_DEBUG + printf ("0x%04x\n", *value); +#endif + + return 0; +} + +int miiphy_write(unsigned char addr, unsigned char reg, unsigned short value) +{ + short rdreg; /* register working value */ + +#ifdef MII_DEBUG + printf ("miiphy_write(0x%x) @ 0x%x = ", reg, addr); +#endif + + rdreg = mii_send(mk_mii_write(addr, reg, value)); + +#ifdef MII_DEBUG + printf ("0x%04x\n", value); +#endif + + return 0; +} +#endif /* (CONFIG_COMMANDS & CFG_CMD_MII) && !defined(CONFIG_BITBANGMII)*/ + +#endif /* CFG_CMD_NET, FEC_ENET */ diff --git a/cpu/mpc8xx/spi.c b/cpu/mpc8xx/spi.c new file mode 100644 index 0000000000..f04d88e34e --- /dev/null +++ b/cpu/mpc8xx/spi.c @@ -0,0 +1,571 @@ +/* + * Copyright (c) 2001 Navin Boppuri / Prashant Patel + * , + * + * Copyright (c) 2001 Gerd Mennchen + * Copyright (c) 2001 Wolfgang Denk, 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 + */ + +/* + * MPC8xx 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#if (defined(CONFIG_SPI)) || (CONFIG_POST & CFG_POST_SPI) + +/* Warning: + * You cannot enable DEBUG for early system initalization, i. e. when + * this driver is used to read environment parameters like "baudrate" + * from EEPROM which are used to initialize the serial port which is + * needed to print the debug messages... + */ +#undef DEBUG + +#define SPI_EEPROM_WREN 0x06 +#define SPI_EEPROM_RDSR 0x05 +#define SPI_EEPROM_READ 0x03 +#define SPI_EEPROM_WRITE 0x02 + +/* --------------------------------------------------------------- + * Offset for initial SPI buffers in DPRAM: + * We need a 520 byte scratch DPRAM area to use at an early stage. + * It is used between the two initialization calls (spi_init_f() + * and spi_init_r()). + * The value 0xb00 makes it far enough from the start of the data + * area (as well as from the stack pointer). + * --------------------------------------------------------------- */ +#ifndef CFG_SPI_INIT_OFFSET +#define CFG_SPI_INIT_OFFSET 0xB00 +#endif + +#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); + +/* ------------------- + * Variables + * ------------------- */ + +#define MAX_BUFFER 0x104 + +/* ---------------------------------------------------------------------- + * Initially we place the RX and TX buffers at a fixed location in DPRAM! + * ---------------------------------------------------------------------- */ +static uchar *rxbuf = + (uchar *)&((cpm8xx_t *)&((immap_t *)CFG_IMMR)->im_cpm)->cp_dpmem + [CFG_SPI_INIT_OFFSET]; +static uchar *txbuf = + (uchar *)&((cpm8xx_t *)&((immap_t *)CFG_IMMR)->im_cpm)->cp_dpmem + [CFG_SPI_INIT_OFFSET+MAX_BUFFER]; + +/* ************************************************************************** + * + * Function: spi_init_f + * + * Description: Init SPI-Controller (ROM part) + * + * return: --- + * + * *********************************************************************** */ +void spi_init_f (void) +{ + unsigned int dpaddr; + + volatile spi_t *spi; + volatile immap_t *immr; + volatile cpic8xx_t *cpi; + volatile cpm8xx_t *cp; + volatile iop8xx_t *iop; + volatile cbd_t *tbdf, *rbdf; + + immr = (immap_t *) CFG_IMMR; + cpi = (cpic8xx_t *)&immr->im_cpic; + iop = (iop8xx_t *) &immr->im_ioport; + cp = (cpm8xx_t *) &immr->im_cpm; + +#ifdef CFG_SPI_UCODE_PATCH + spi = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; +#else + spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; + /* Disable relocation */ + spi->spi_rpbase = 0; +#endif + +/* 1 */ + /* ------------------------------------------------ + * Initialize Port B SPI pins -> page 34-8 MPC860UM + * (we are only in Master Mode !) + * ------------------------------------------------ */ + + /* -------------------------------------------- + * GPIO or per. Function + * PBPAR[28] = 1 [0x00000008] -> PERI: (SPIMISO) + * PBPAR[29] = 1 [0x00000004] -> PERI: (SPIMOSI) + * PBPAR[30] = 1 [0x00000002] -> PERI: (SPICLK) + * PBPAR[31] = 0 [0x00000001] -> GPIO: (CS for PCUE/CCM-EEPROM) + * -------------------------------------------- */ + cp->cp_pbpar |= 0x0000000E; /* set bits */ + cp->cp_pbpar &= ~0x00000001; /* reset bit */ + + /* ---------------------------------------------- + * In/Out or per. Function 0/1 + * PBDIR[28] = 1 [0x00000008] -> PERI1: SPIMISO + * PBDIR[29] = 1 [0x00000004] -> PERI1: SPIMOSI + * PBDIR[30] = 1 [0x00000002] -> PERI1: SPICLK + * PBDIR[31] = 1 [0x00000001] -> GPIO OUT: CS for PCUE/CCM-EEPROM + * ---------------------------------------------- */ + cp->cp_pbdir |= 0x0000000F; + + /* ---------------------------------------------- + * open drain or active output + * PBODR[28] = 1 [0x00000008] -> open drain: SPIMISO + * PBODR[29] = 0 [0x00000004] -> active output SPIMOSI + * PBODR[30] = 0 [0x00000002] -> active output: SPICLK + * PBODR[31] = 0 [0x00000001] -> active output: GPIO OUT: CS for PCUE/CCM + * ---------------------------------------------- */ + + cp->cp_pbodr |= 0x00000008; + cp->cp_pbodr &= ~0x00000007; + + /* Initialize the parameter ram. + * We need to make sure many things are initialized to zero + */ + spi->spi_rstate = 0; + spi->spi_rdp = 0; + spi->spi_rbptr = 0; + spi->spi_rbc = 0; + spi->spi_rxtmp = 0; + spi->spi_tstate = 0; + spi->spi_tdp = 0; + spi->spi_tbptr = 0; + spi->spi_tbc = 0; + spi->spi_txtmp = 0; + + /* Allocate space for one transmit and one receive buffer + * descriptor in the DP ram + */ +#ifdef CFG_ALLOC_DPRAM + dpaddr = dpram_alloc_align (sizeof(cbd_t)*2, 8); +#else + dpaddr = CPM_SPI_BASE; +#endif + +/* 3 */ + /* Set up the SPI parameters in the parameter ram */ + spi->spi_rbase = dpaddr; + spi->spi_tbase = dpaddr + sizeof (cbd_t); + + /***********IMPORTANT******************/ + + /* + * Setting transmit and receive buffer descriptor pointers + * initially to rbase and tbase. Only the microcode patches + * documentation talks about initializing this pointer. This + * is missing from the sample I2C driver. If you dont + * initialize these pointers, the kernel hangs. + */ + spi->spi_rbptr = spi->spi_rbase; + spi->spi_tbptr = spi->spi_tbase; + +/* 4 */ +#ifdef CFG_SPI_UCODE_PATCH + /* + * Initialize required parameters if using microcode patch. + */ + spi->spi_rstate = 0; + spi->spi_tstate = 0; +#else + /* Init SPI Tx + Rx Parameters */ + while (cp->cp_cpcr & CPM_CR_FLG) + ; + cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SPI, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cpcr & CPM_CR_FLG) + ; +#endif /* CFG_SPI_UCODE_PATCH */ + +/* 5 */ + /* Set SDMA configuration register */ + immr->im_siu_conf.sc_sdcr = 0x0001; + +/* 6 */ + /* Set to big endian. */ + spi->spi_tfcr = SMC_EB; + spi->spi_rfcr = SMC_EB; + +/* 7 */ + /* Set maximum receive size. */ + spi->spi_mrblr = MAX_BUFFER; + +/* 8 + 9 */ + /* tx and rx buffer descriptors */ + tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; + rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; + + tbdf->cbd_sc &= ~BD_SC_READY; + rbdf->cbd_sc &= ~BD_SC_EMPTY; + + /* Set the bd's rx and tx buffer address pointers */ + rbdf->cbd_bufaddr = (ulong) rxbuf; + tbdf->cbd_bufaddr = (ulong) txbuf; + +/* 10 + 11 */ + cp->cp_spim = 0; /* Mask all SPI events */ + cp->cp_spie = SPI_EMASK; /* Clear all SPI events */ + + return; +} + +/* ************************************************************************** + * + * Function: spi_init_r + * + * Description: Init SPI-Controller (RAM part) - + * The malloc engine is ready and we can move our buffers to + * normal RAM + * + * return: --- + * + * *********************************************************************** */ +void spi_init_r (void) +{ + volatile cpm8xx_t *cp; + volatile spi_t *spi; + volatile immap_t *immr; + volatile cbd_t *tbdf, *rbdf; + + immr = (immap_t *) CFG_IMMR; + cp = (cpm8xx_t *) &immr->im_cpm; + +#ifdef CFG_SPI_UCODE_PATCH + spi = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; +#else + spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; + /* Disable relocation */ + spi->spi_rpbase = 0; +#endif + + /* tx and rx buffer descriptors */ + tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; + rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; + + /* Allocate memory for RX and TX buffers */ + rxbuf = (uchar *) malloc (MAX_BUFFER); + txbuf = (uchar *) malloc (MAX_BUFFER); + + rbdf->cbd_bufaddr = (ulong) rxbuf; + tbdf->cbd_bufaddr = (ulong) txbuf; + + return; +} + +/**************************************************************************** + * Function: spi_write + **************************************************************************** */ +ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len) +{ + int i; + + memset(rxbuf, 0, MAX_BUFFER); + memset(txbuf, 0, MAX_BUFFER); + *txbuf = SPI_EEPROM_WREN; /* write enable */ + spi_xfer(1); + memcpy(txbuf, addr, alen); + *txbuf = SPI_EEPROM_WRITE; /* WRITE memory array */ + memcpy(alen + txbuf, buffer, len); + spi_xfer(alen + len); + /* ignore received data */ + for (i = 0; i < 1000; i++) { + *txbuf = SPI_EEPROM_RDSR; /* read status */ + txbuf[1] = 0; + spi_xfer(2); + if (!(rxbuf[1] & 1)) { + break; + } + udelay(1000); + } + if (i >= 1000) { + printf ("*** spi_write: Time out while writing!\n"); + } + + return len; +} + +/**************************************************************************** + * Function: spi_read + **************************************************************************** */ +ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len) +{ + memset(rxbuf, 0, MAX_BUFFER); + memset(txbuf, 0, MAX_BUFFER); + memcpy(txbuf, addr, alen); + *txbuf = SPI_EEPROM_READ; /* READ memory array */ + + /* + * There is a bug in 860T (?) that cuts the last byte of input + * if we're reading into DPRAM. The solution we choose here is + * to always read len+1 bytes (we have one extra byte at the + * end of the buffer). + */ + spi_xfer(alen + len + 1); + memcpy(buffer, alen + rxbuf, len); + + return len; +} + +/**************************************************************************** + * Function: spi_xfer + **************************************************************************** */ +ssize_t spi_xfer (size_t count) +{ + volatile immap_t *immr; + volatile cpm8xx_t *cp; + volatile spi_t *spi; + cbd_t *tbdf, *rbdf; + ushort loop; + int tm; + + DPRINT (("*** spi_xfer entered ***\n")); + + immr = (immap_t *) CFG_IMMR; + cp = (cpm8xx_t *) &immr->im_cpm; + +#ifdef CFG_SPI_UCODE_PATCH + spi = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; +#else + spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; + /* Disable relocation */ + spi->spi_rpbase = 0; +#endif + + tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; + rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; + + /* Set CS for device */ + cp->cp_pbdat &= ~0x0001; + + /* Setting tx bd status and data length */ + tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP; + tbdf->cbd_datlen = count; + + DPRINT (("*** spi_xfer: Bytes to be xferred: %d ***\n", + tbdf->cbd_datlen)); + + /* Setting rx bd status and data length */ + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; + rbdf->cbd_datlen = 0; /* rx length has no significance */ + + loop = cp->cp_spmode & SPMODE_LOOP; + cp->cp_spmode = /*SPMODE_DIV16 |*/ /* BRG/16 mode not used here */ + loop | + SPMODE_REV | + SPMODE_MSTR | + SPMODE_EN | + SPMODE_LEN(8) | /* 8 Bits per char */ + SPMODE_PM(0x8) ; /* medium speed */ + cp->cp_spim = 0; /* Mask all SPI events */ + cp->cp_spie = SPI_EMASK; /* Clear all SPI events */ + + /* start spi transfer */ + DPRINT (("*** spi_xfer: Performing transfer ...\n")); + cp->cp_spcom |= SPI_STR; /* Start transmit */ + + /* -------------------------------- + * Wait for SPI transmit to get out + * or time out (1 second = 1000 ms) + * -------------------------------- */ + for (tm=0; tm<1000; ++tm) { + if (cp->cp_spie & SPI_TXB) { /* Tx Buffer Empty */ + DPRINT (("*** spi_xfer: Tx buffer empty\n")); + break; + } + if ((tbdf->cbd_sc & BD_SC_READY) == 0) { + DPRINT (("*** spi_xfer: Tx BD done\n")); + break; + } + udelay (1000); + } + if (tm >= 1000) { + printf ("*** spi_xfer: Time out while xferring to/from SPI!\n"); + } + DPRINT (("*** spi_xfer: ... transfer ended\n")); + +#ifdef DEBUG + printf ("\nspi_xfer: txbuf after xfer\n"); + memdump ((void *) txbuf, 16); /* dump of txbuf before transmit */ + printf ("spi_xfer: rxbuf after xfer\n"); + memdump ((void *) rxbuf, 16); /* dump of rxbuf after transmit */ + printf ("\n"); +#endif + + /* Clear CS for device */ + cp->cp_pbdat |= 0x0001; + + return count; +} +#endif /* CONFIG_SPI || (CONFIG_POST & CFG_POST_SPI) */ + +/* + * SPI test + * + * The Serial Peripheral Interface (SPI) is tested in the local loopback mode. + * The interface is configured accordingly and several packets + * are transfered. The configurable test parameters are: + * TEST_MIN_LENGTH - minimum size of packet to transfer + * TEST_MAX_LENGTH - maximum size of packet to transfer + * TEST_NUM - number of tests + */ + +#if CONFIG_POST & CFG_POST_SPI + +#define TEST_MIN_LENGTH 1 +#define TEST_MAX_LENGTH MAX_BUFFER +#define TEST_NUM 1 + +static void packet_fill (char * packet, int length) +{ + char c = (char) length; + int i; + + for (i = 0; i < length; i++) + { + packet[i] = c++; + } +} + +static int packet_check (char * packet, int length) +{ + char c = (char) length; + int i; + + for (i = 0; i < length; i++) + { + if (packet[i] != c++) return -1; + } + + return 0; +} + +int spi_post_test (int flags) +{ + DECLARE_GLOBAL_DATA_PTR; + + int res = -1; + volatile immap_t *immr = (immap_t *) CFG_IMMR; + volatile cpm8xx_t *cp = (cpm8xx_t *) &immr->im_cpm; + int i; + int l; + + spi_init_f(); + spi_init_r(); + + cp->cp_spmode |= SPMODE_LOOP; + + for (i = 0; i < TEST_NUM; i++) + { + for (l = TEST_MIN_LENGTH; l <= TEST_MAX_LENGTH; l += 8) + { + packet_fill(txbuf, l); + + spi_xfer(l); + + if (packet_check(rxbuf, l) < 0) + { + goto Done; + } + } + } + + res = 0; + + Done: + + cp->cp_spmode &= ~SPMODE_LOOP; + + /* + * SCC2 Ethernet parameter RAM space overlaps + * the SPI parameter RAM space. So we need to restore + * the SCC2 configuration if it is used by UART or Ethernet. + */ + +#if defined(CONFIG_8xx_CONS_SCC2) + serial_init(); +#endif + +#if defined(SCC_ENET) && (SCC_ENET == 1) + eth_init(gd->bd); +#endif + + if (res != 0) + { + post_log("SPI test failed\n"); + } + + return res; +} +#endif /* CONFIG_POST & CFG_POST_SPI */ diff --git a/cpu/ppc4xx/405gp_enet.c b/cpu/ppc4xx/405gp_enet.c new file mode 100644 index 0000000000..49e9e424fd --- /dev/null +++ b/cpu/ppc4xx/405gp_enet.c @@ -0,0 +1,867 @@ +/*-----------------------------------------------------------------------------+ + * + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + *-----------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------+ + * + * File Name: enetemac.c + * + * Function: Device driver for the ethernet EMAC3 macro on the 405GP. + * + * Author: Mark Wisner + * + * Change Activity- + * + * Date Description of Change BY + * --------- --------------------- --- + * 05-May-99 Created MKW + * 27-Jun-99 Clean up JWB + * 16-Jul-99 Added MAL error recovery and better IP packet handling MKW + * 29-Jul-99 Added Full duplex support MKW + * 06-Aug-99 Changed names for Mal CR reg MKW + * 23-Aug-99 Turned off SYE when running at 10Mbs MKW + * 24-Aug-99 Marked descriptor empty after call_xlc MKW + * 07-Sep-99 Set MAL RX buffer size reg to ENET_MAX_MTU_ALIGNED / 16 MCG + * to avoid chaining maximum sized packets. Push starting + * RX descriptor address up to the next cache line boundary. + * 16-Jan-00 Added support for booting with IP of 0x0 MKW + * 15-Mar-00 Updated enetInit() to enable broadcast addresses in the + * EMAC_RXM register. JWB + * 12-Mar-01 anne-sophie.harnois@nextream.fr + * - Variables are compatible with those already defined in + * include/net.h + * - Receive buffer descriptor ring is used to send buffers + * to the user + * - Info print about send/received/handled packet number if + * INFO_405_ENET is set + * 17-Apr-01 stefan.roese@esd-electronics.com + * - MAL reset in "eth_halt" included + * - Enet speed and duplex output now in one line + * 08-May-01 stefan.roese@esd-electronics.com + * - MAL error handling added (eth_init called again) + * 13-Nov-01 stefan.roese@esd-electronics.com + * - Set IST bit in EMAC_M1 reg upon 100MBit or full duplex + * 04-Jan-02 stefan.roese@esd-electronics.com + * - Wait for PHY auto negotiation to complete added + * 06-Feb-02 stefan.roese@esd-electronics.com + * - Bug fixed in waiting for auto negotiation to complete + * 26-Feb-02 stefan.roese@esd-electronics.com + * - rx and tx buffer descriptors now allocated (no fixed address + * used anymore) + * 17-Jun-02 stefan.roese@esd-electronics.com + * - MAL error debug printf 'M' removed (rx de interrupt may + * occur upon many incoming packets with only 4 rx buffers). + *-----------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include <405gp_enet.h> +#include <405_mal.h> +#include +#include +#include +#include "vecnum.h" + +#if defined(CONFIG_405GP) || defined(CONFIG_440) + +#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */ +#define PHY_AUTONEGOTIATE_TIMEOUT 2000 /* 2000 ms autonegotiate timeout */ + +#define NUM_TX_BUFF 1 +/* AS.HARNOIS + * Use PKTBUFSRX (include/net.h) instead of setting NUM_RX_BUFF again + * These both variables are used to define the same thing! + * #define NUM_RX_BUFF 4 + */ +#define NUM_RX_BUFF PKTBUFSRX + +/* Ethernet Transmit and Receive Buffers */ +/* AS.HARNOIS + * In the same way ENET_MAX_MTU and ENET_MAX_MTU_ALIGNED are set from + * PKTSIZE and PKTSIZE_ALIGN (include/net.h) + */ +#define ENET_MAX_MTU PKTSIZE +#define ENET_MAX_MTU_ALIGNED PKTSIZE_ALIGN + +static char *txbuf_ptr; + +/* define the number of channels implemented */ +#define EMAC_RXCHL 1 +#define EMAC_TXCHL 1 + +/*-----------------------------------------------------------------------------+ + * Defines for MAL/EMAC interrupt conditions as reported in the UIC (Universal + * Interrupt Controller). + *-----------------------------------------------------------------------------*/ +#define MAL_UIC_ERR ( UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE) +#define MAL_UIC_DEF (UIC_MAL_RXEOB | MAL_UIC_ERR) +#define EMAC_UIC_DEF UIC_ENET + +/*-----------------------------------------------------------------------------+ + * Global variables. TX and RX descriptors and buffers. + *-----------------------------------------------------------------------------*/ +static volatile mal_desc_t *tx; +static volatile mal_desc_t *rx; +static mal_desc_t *alloc_tx_buf = NULL; +static mal_desc_t *alloc_rx_buf = NULL; + +/* IER globals */ +static unsigned long emac_ier; +static unsigned long mal_ier; + + +/* Statistic Areas */ +#define MAX_ERR_LOG 10 +struct emac_stats { + int data_len_err; + int rx_frames; + int rx; + int rx_prot_err; +}; + +static struct stats { /* Statistic Block */ + struct emac_stats emac; + int int_err; + short tx_err_log[MAX_ERR_LOG]; + short rx_err_log[MAX_ERR_LOG]; +} stats; + +static int first_init = 0; + +static int tx_err_index = 0; /* Transmit Error Index for tx_err_log */ +static int rx_err_index = 0; /* Receive Error Index for rx_err_log */ + +static int rx_slot = 0; /* MAL Receive Slot */ +static int rx_i_index = 0; /* Receive Interrupt Queue Index */ +static int rx_u_index = 0; /* Receive User Queue Index */ +static int rx_ready[NUM_RX_BUFF]; /* Receive Ready Queue */ + +static int tx_slot = 0; /* MAL Transmit Slot */ +static int tx_i_index = 0; /* Transmit Interrupt Queue Index */ +static int tx_u_index = 0; /* Transmit User Queue Index */ +static int tx_run[NUM_TX_BUFF]; /* Transmit Running Queue */ + +#undef INFO_405_ENET 1 +#ifdef INFO_405_ENET +static int packetSent = 0; +static int packetReceived = 0; +static int packetHandled = 0; +#endif + +static char emac_hwd_addr[ENET_ADDR_LENGTH]; + +static bd_t *bis_save = NULL; /* for eth_init upon mal error */ + +static int is_receiving = 0; /* sync with eth interrupt */ +static int print_speed = 1; /* print speed message upon start */ + +static void enet_rcv (unsigned long malisr); + +/*-----------------------------------------------------------------------------+ + * Prototypes and externals. + *-----------------------------------------------------------------------------*/ +void mal_err (unsigned long isr, unsigned long uic, unsigned long mal_def, + unsigned long mal_errr); +void emac_err (unsigned long isr); + + +void eth_halt (void) +{ + mtdcr (malier, 0x00000000); /* disable mal interrupts */ + out32 (EMAC_IER, 0x00000000); /* disable emac interrupts */ + + /* 1st reset MAL */ + mtdcr (malmcr, MAL_CR_MMSR); + + /* wait for reset */ + while (mfdcr (malmcr) & MAL_CR_MMSR) { + }; + + /* EMAC RESET */ + out32 (EMAC_M0, EMAC_M0_SRST); + + print_speed = 1; /* print speed message again next time */ +} + + +int eth_init (bd_t * bis) +{ + int i; + unsigned long reg; + unsigned long msr; + unsigned long speed; + unsigned long duplex; + unsigned mode_reg; + unsigned short reg_short; + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); /* disable interrupts */ + +#ifdef INFO_405_ENET + /* AS.HARNOIS + * We should have : + * packetHandled <= packetReceived <= packetHandled+PKTBUFSRX + * In the most cases packetHandled = packetReceived, but it + * is possible that new packets (without relationship with + * current transfer) have got the time to arrived before + * netloop calls eth_halt + */ + printf ("About preceeding transfer:\n" + "- Sent packet number %d\n" + "- Received packet number %d\n" + "- Handled packet number %d\n", + packetSent, packetReceived, packetHandled); + packetSent = 0; + packetReceived = 0; + packetHandled = 0; +#endif + + /* MAL RESET */ + mtdcr (malmcr, MAL_CR_MMSR); + /* wait for reset */ + while (mfdcr (malmcr) & MAL_CR_MMSR) { + }; + + tx_err_index = 0; /* Transmit Error Index for tx_err_log */ + rx_err_index = 0; /* Receive Error Index for rx_err_log */ + + rx_slot = 0; /* MAL Receive Slot */ + rx_i_index = 0; /* Receive Interrupt Queue Index */ + rx_u_index = 0; /* Receive User Queue Index */ + + tx_slot = 0; /* MAL Transmit Slot */ + tx_i_index = 0; /* Transmit Interrupt Queue Index */ + tx_u_index = 0; /* Transmit User Queue Index */ + +#if defined(CONFIG_440) + /* set RMII mode */ + out32 (ZMII_FER, ZMII_RMII | ZMII_MDI0); +#endif /* CONFIG_440 */ + + /* EMAC RESET */ + out32 (EMAC_M0, EMAC_M0_SRST); + + /* wait for PHY to complete auto negotiation */ + reg_short = 0; +#ifndef CONFIG_CS8952_PHY + miiphy_read (CONFIG_PHY_ADDR, PHY_BMSR, ®_short); + + /* + * Wait if PHY is able of autonegotiation and autonegotiation is not complete + */ + if ((reg_short & PHY_BMSR_AUTN_ABLE) + && !(reg_short & PHY_BMSR_AUTN_COMP)) { + puts ("Waiting for PHY auto negotiation to complete"); + i = 0; + while (!(reg_short & PHY_BMSR_AUTN_COMP)) { + if ((i++ % 100) == 0) + putc ('.'); + udelay (10000); /* 10 ms */ + miiphy_read (CONFIG_PHY_ADDR, PHY_BMSR, ®_short); + + /* + * Timeout reached ? + */ + if (i * 10 > PHY_AUTONEGOTIATE_TIMEOUT) { + puts (" TIMEOUT !\n"); + break; + } + } + puts (" done\n"); + udelay (500000); /* another 500 ms (results in faster booting) */ + } +#endif + speed = miiphy_speed (CONFIG_PHY_ADDR); + duplex = miiphy_duplex (CONFIG_PHY_ADDR); + if (print_speed) { + print_speed = 0; + printf ("ENET Speed is %d Mbps - %s duplex connection\n", + (int) speed, (duplex == HALF) ? "HALF" : "FULL"); + } + + /* set the Mal configuration reg */ +#if defined(CONFIG_440) + /* Errata 1.12: MAL_1 -- Disable MAL bursting */ + if( get_pvr() == PVR_440GP_RB ) + mtdcr (malmcr, MAL_CR_OPBBL | MAL_CR_LEA | MAL_CR_PLBLT_DEFAULT); + else +#else + mtdcr (malmcr, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | MAL_CR_PLBLT_DEFAULT); +#endif + + /* Free "old" buffers */ + if (alloc_tx_buf) free(alloc_tx_buf); + if (alloc_rx_buf) free(alloc_rx_buf); + + /* + * Malloc MAL buffer desciptors, make sure they are + * aligned on cache line boundary size + * (401/403/IOP480 = 16, 405 = 32) + * and doesn't cross cache block boundaries. + */ + alloc_tx_buf = (mal_desc_t *)malloc((sizeof(mal_desc_t) * NUM_TX_BUFF) + + ((2 * CFG_CACHELINE_SIZE) - 2)); + if (((int)alloc_tx_buf & CACHELINE_MASK) != 0) { + tx = (mal_desc_t *)((int)alloc_tx_buf + CFG_CACHELINE_SIZE - + ((int)alloc_tx_buf & CACHELINE_MASK)); + } else { + tx = alloc_tx_buf; + } + + alloc_rx_buf = (mal_desc_t *)malloc((sizeof(mal_desc_t) * NUM_RX_BUFF) + + ((2 * CFG_CACHELINE_SIZE) - 2)); + if (((int)alloc_rx_buf & CACHELINE_MASK) != 0) { + rx = (mal_desc_t *)((int)alloc_rx_buf + CFG_CACHELINE_SIZE - + ((int)alloc_rx_buf & CACHELINE_MASK)); + } else { + rx = alloc_rx_buf; + } + + for (i = 0; i < NUM_TX_BUFF; i++) { + tx[i].ctrl = 0; + tx[i].data_len = 0; + if (first_init == 0) + txbuf_ptr = (char *) malloc (ENET_MAX_MTU_ALIGNED); + tx[i].data_ptr = txbuf_ptr; + if ((NUM_TX_BUFF - 1) == i) + tx[i].ctrl |= MAL_TX_CTRL_WRAP; + tx_run[i] = -1; +#if 0 + printf ("TX_BUFF %d @ 0x%08lx\n", i, (ulong) tx[i].data_ptr); +#endif + } + + for (i = 0; i < NUM_RX_BUFF; i++) { + rx[i].ctrl = 0; + rx[i].data_len = 0; + /* rx[i].data_ptr = (char *) &rx_buff[i]; */ + rx[i].data_ptr = (char *) NetRxPackets[i]; + if ((NUM_RX_BUFF - 1) == i) + rx[i].ctrl |= MAL_RX_CTRL_WRAP; + rx[i].ctrl |= MAL_RX_CTRL_EMPTY | MAL_RX_CTRL_INTR; + rx_ready[i] = -1; +#if 0 + printf ("RX_BUFF %d @ 0x%08lx\n", i, (ulong) rx[i].data_ptr); +#endif + } + + memcpy (emac_hwd_addr, bis->bi_enetaddr, ENET_ADDR_LENGTH); + + reg = 0x00000000; + + reg |= emac_hwd_addr[0]; /* set high address */ + reg = reg << 8; + reg |= emac_hwd_addr[1]; + + out32 (EMAC_IAH, reg); + + reg = 0x00000000; + reg |= emac_hwd_addr[2]; /* set low address */ + reg = reg << 8; + reg |= emac_hwd_addr[3]; + reg = reg << 8; + reg |= emac_hwd_addr[4]; + reg = reg << 8; + reg |= emac_hwd_addr[5]; + + out32 (EMAC_IAL, reg); + + /* setup MAL tx & rx channel pointers */ + mtdcr (maltxctp0r, tx); + mtdcr (malrxctp0r, rx); + + /* Reset transmit and receive channels */ + mtdcr (malrxcarr, 0x80000000); /* 2 channels */ + mtdcr (maltxcarr, 0x80000000); /* 2 channels */ + + /* Enable MAL transmit and receive channels */ + mtdcr (maltxcasr, 0x80000000); /* 1 channel */ + mtdcr (malrxcasr, 0x80000000); /* 1 channel */ + + /* set RX buffer size */ + mtdcr (malrcbs0, ENET_MAX_MTU_ALIGNED / 16); + + /* set transmit enable & receive enable */ + out32 (EMAC_M0, EMAC_M0_TXE | EMAC_M0_RXE); + + /* set receive fifo to 4k and tx fifo to 2k */ + mode_reg = EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K; + + /* set speed */ + if (speed == _100BASET) + mode_reg = mode_reg | EMAC_M1_MF_100MBPS | EMAC_M1_IST; + else + mode_reg = mode_reg & ~0x00C00000; /* 10 MBPS */ + if (duplex == FULL) + mode_reg = mode_reg | 0x80000000 | EMAC_M1_IST; + + out32 (EMAC_M1, mode_reg); + + /* Enable broadcast and indvidual address */ + out32 (EMAC_RXM, EMAC_RMR_BAE | EMAC_RMR_IAE + /*| EMAC_RMR_ARRP| EMAC_RMR_SFCS | EMAC_RMR_SP */ ); + + /* we probably need to set the tx mode1 reg? maybe at tx time */ + + /* set transmit request threshold register */ + out32 (EMAC_TRTR, 0x18000000); /* 256 byte threshold */ + + /* set receive low/high water mark register */ +#if defined(CONFIG_440) + /* 440GP has a 64 byte burst length */ + out32 (EMAC_RX_HI_LO_WMARK, 0x80009000); + out32 (EMAC_TXM1, 0xf8640000); +#else /* CONFIG_440 */ + /* 405s have a 16 byte burst length */ + out32 (EMAC_RX_HI_LO_WMARK, 0x0f002000); +#endif /* CONFIG_440 */ + + /* Frame gap set */ + out32 (EMAC_I_FRAME_GAP_REG, 0x00000008); + + if (first_init == 0) { + /* + * Connect interrupt service routines + */ + irq_install_handler (VECNUM_EWU0, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_MS, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_MTE, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_MRE, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_TXDE, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_RXDE, (interrupt_handler_t *) enetInt, NULL); + irq_install_handler (VECNUM_ETH0, (interrupt_handler_t *) enetInt, NULL); + } + + /* set up interrupt handler */ + /* setup interrupt controler to take interrupts from the MAL & + EMAC */ + mtdcr (uicsr, 0xffffffff); /* clear pending interrupts */ + mtdcr (uicer, mfdcr (uicer) | MAL_UIC_DEF | EMAC_UIC_DEF); + + /* set the MAL IER ??? names may change with new spec ??? */ + mal_ier = MAL_IER_DE | MAL_IER_NE | MAL_IER_TE | MAL_IER_OPBE | + MAL_IER_PLBE; + mtdcr (malesr, 0xffffffff); /* clear pending interrupts */ + mtdcr (maltxdeir, 0xffffffff); /* clear pending interrupts */ + mtdcr (malrxdeir, 0xffffffff); /* clear pending interrupts */ + mtdcr (malier, mal_ier); + + /* Set EMAC IER */ + emac_ier = EMAC_ISR_PTLE | EMAC_ISR_BFCS | + EMAC_ISR_PTLE | EMAC_ISR_ORE | EMAC_ISR_IRE; + if (speed == _100BASET) + emac_ier = emac_ier | EMAC_ISR_SYE; + + out32 (EMAC_ISR, 0xffffffff); /* clear pending interrupts */ + out32 (EMAC_IER, emac_ier); + + mtmsr (msr); /* enable interrupts again */ + + bis_save = bis; + first_init = 1; + + return (0); +} + + +int eth_send (volatile void *ptr, int len) +{ + struct enet_frame *ef_ptr; + ulong time_start, time_now; + unsigned long temp_txm0; + + ef_ptr = (struct enet_frame *) ptr; + + /*-----------------------------------------------------------------------+ + * Copy in our address into the frame. + *-----------------------------------------------------------------------*/ + (void) memcpy (ef_ptr->source_addr, emac_hwd_addr, ENET_ADDR_LENGTH); + + /*-----------------------------------------------------------------------+ + * If frame is too long or too short, modify length. + *-----------------------------------------------------------------------*/ + if (len > ENET_MAX_MTU) + len = ENET_MAX_MTU; + + /* memcpy ((void *) &tx_buff[tx_slot], (const void *) ptr, len); */ + memcpy ((void *) txbuf_ptr, (const void *) ptr, len); + + /*-----------------------------------------------------------------------+ + * set TX Buffer busy, and send it + *-----------------------------------------------------------------------*/ + tx[tx_slot].ctrl = (MAL_TX_CTRL_LAST | + EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP) & + ~(EMAC_TX_CTRL_ISA | EMAC_TX_CTRL_RSA); + if ((NUM_TX_BUFF - 1) == tx_slot) + tx[tx_slot].ctrl |= MAL_TX_CTRL_WRAP; + + tx[tx_slot].data_len = (short) len; + tx[tx_slot].ctrl |= MAL_TX_CTRL_READY; + + __asm__ volatile ("eieio"); + out32 (EMAC_TXM0, in32 (EMAC_TXM0) | EMAC_TXM0_GNP0); +#ifdef INFO_405_ENET + packetSent++; +#endif + + /*-----------------------------------------------------------------------+ + * poll unitl the packet is sent and then make sure it is OK + *-----------------------------------------------------------------------*/ + time_start = get_timer (0); + while (1) { + temp_txm0 = in32 (EMAC_TXM0); + /* loop until either TINT turns on or 3 seconds elapse */ + if ((temp_txm0 & EMAC_TXM0_GNP0) != 0) { + /* transmit is done, so now check for errors + * If there is an error, an interrupt should + * happen when we return + */ + time_now = get_timer (0); + if ((time_now - time_start) > 3000) { + return (-1); + } + } else { + return (0); + } + } +} + + +#if defined(CONFIG_440) +/*-----------------------------------------------------------------------------+ +| EnetInt. +| EnetInt is the interrupt handler. It will determine the +| cause of the interrupt and call the apporpriate servive +| routine. ++-----------------------------------------------------------------------------*/ +int enetInt () +{ + int serviced; + int rc = -1; /* default to not us */ + unsigned long mal_isr; + unsigned long emac_isr = 0; + unsigned long mal_rx_eob; + unsigned long my_uic0msr, my_uic1msr; + + /* enter loop that stays in interrupt code until nothing to service */ + do { + serviced = 0; + + my_uic0msr = mfdcr (uic0msr); + my_uic1msr = mfdcr (uic1msr); + + if (!(my_uic0msr & UIC_MRE) + && !(my_uic1msr & (UIC_ETH0 | UIC_MS | UIC_MTDE | UIC_MRDE))) { + /* not for us */ + return (rc); + } + + /* get and clear controller status interrupts */ + /* look at Mal and EMAC interrupts */ + if ((my_uic0msr & UIC_MRE) + || (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) { + /* we have a MAL interrupt */ + mal_isr = mfdcr (malesr); + /* look for mal error */ + if (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE)) { + mal_err (mal_isr, my_uic0msr, MAL_UIC_DEF, MAL_UIC_ERR); + serviced = 1; + rc = 0; + } + } + if (UIC_ETH0 & my_uic1msr) { /* look for EMAC errors */ + emac_isr = in32 (EMAC_ISR); + if ((emac_ier & emac_isr) != 0) { + emac_err (emac_isr); + serviced = 1; + rc = 0; + } + } + if ((emac_ier & emac_isr) + || (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) { + mtdcr (uic0sr, UIC_MRE); /* Clear */ + mtdcr (uic1sr, UIC_ETH0 | UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */ + return (rc); /* we had errors so get out */ + } + + /* handle MAL RX EOB interupt from a receive */ + /* check for EOB on valid channels */ + if (my_uic0msr & UIC_MRE) { + mal_rx_eob = mfdcr (malrxeobisr); + if ((mal_rx_eob & 0x80000000) != 0) { /* call emac routine for channel 0 */ + /* clear EOB + mtdcr(malrxeobisr, mal_rx_eob); */ + enet_rcv (emac_isr); + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + } + mtdcr (uic0sr, UIC_MRE); /* Clear */ + mtdcr (uic1sr, UIC_ETH0 | UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */ + } while (serviced); + + return (rc); +} +#else /* CONFIG_440 */ +/*-----------------------------------------------------------------------------+ + * EnetInt. + * EnetInt is the interrupt handler. It will determine the + * cause of the interrupt and call the apporpriate servive + * routine. + *-----------------------------------------------------------------------------*/ +int enetInt () +{ + int serviced; + int rc = -1; /* default to not us */ + unsigned long mal_isr; + unsigned long emac_isr = 0; + unsigned long mal_rx_eob; + unsigned long my_uicmsr; + + /* enter loop that stays in interrupt code until nothing to service */ + do { + serviced = 0; + + my_uicmsr = mfdcr (uicmsr); + if ((my_uicmsr & (MAL_UIC_DEF | EMAC_UIC_DEF)) == 0) { /* not for us */ + return (rc); + } + + + /* get and clear controller status interrupts */ + /* look at Mal and EMAC interrupts */ + if ((MAL_UIC_DEF & my_uicmsr) != 0) { /* we have a MAL interrupt */ + mal_isr = mfdcr (malesr); + /* look for mal error */ + if ((my_uicmsr & MAL_UIC_ERR) != 0) { + mal_err (mal_isr, my_uicmsr, MAL_UIC_DEF, MAL_UIC_ERR); + serviced = 1; + rc = 0; + } + } + if ((EMAC_UIC_DEF & my_uicmsr) != 0) { /* look for EMAC errors */ + emac_isr = in32 (EMAC_ISR); + if ((emac_ier & emac_isr) != 0) { + emac_err (emac_isr); + serviced = 1; + rc = 0; + } + } + if (((emac_ier & emac_isr) != 0) | ((MAL_UIC_ERR & my_uicmsr) != 0)) { + mtdcr (uicsr, MAL_UIC_DEF | EMAC_UIC_DEF); /* Clear */ + return (rc); /* we had errors so get out */ + } + + + /* handle MAL RX EOB interupt from a receive */ + /* check for EOB on valid channels */ + if ((my_uicmsr & UIC_MAL_RXEOB) != 0) { + mal_rx_eob = mfdcr (malrxeobisr); + if ((mal_rx_eob & 0x80000000) != 0) { /* call emac routine for channel 0 */ + /* clear EOB + mtdcr(malrxeobisr, mal_rx_eob); */ + enet_rcv (emac_isr); + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + } + mtdcr (uicsr, MAL_UIC_DEF | EMAC_UIC_DEF); /* Clear */ + } + while (serviced); + + return (rc); +} +#endif /* CONFIG_440 */ + +/*-----------------------------------------------------------------------------+ + * MAL Error Routine + *-----------------------------------------------------------------------------*/ +void mal_err (unsigned long isr, unsigned long uic, unsigned long maldef, + unsigned long mal_errr) +{ + mtdcr (malesr, isr); /* clear interrupt */ + + /* clear DE interrupt */ + mtdcr (maltxdeir, 0xC0000000); + mtdcr (malrxdeir, 0x80000000); + +#if 1 /*sr */ + printf ("\nMAL error occured.... ISR = %lx UIC = = %lx MAL_DEF = %lx MAL_ERR= %lx \n", + isr, uic, maldef, mal_errr); +#else +#if 0 + /* + * MAL error is RX DE error (out of rx buffers)! This is OK here, upon + * many incoming packets with only 4 rx buffers. + */ + printf ("M"); /* just to see something upon mal error */ +#endif +#endif /*sr */ + + eth_init (bis_save); /* start again... */ +} + +/*-----------------------------------------------------------------------------+ + * EMAC Error Routine + *-----------------------------------------------------------------------------*/ +void emac_err (unsigned long isr) +{ + printf ("EMAC error occured.... ISR = %lx\n", isr); + out32 (EMAC_ISR, isr); +} + +/*-----------------------------------------------------------------------------+ + * enet_rcv() handles the ethernet receive data + *-----------------------------------------------------------------------------*/ +static void enet_rcv (unsigned long malisr) +{ + struct enet_frame *ef_ptr; + unsigned long data_len; + unsigned long rx_eob_isr; + + int handled = 0; + int i; + int loop_count = 0; + + rx_eob_isr = mfdcr (malrxeobisr); + if ((0x80000000 >> (EMAC_RXCHL - 1)) & rx_eob_isr) { + /* clear EOB */ + mtdcr (malrxeobisr, rx_eob_isr); + + /* EMAC RX done */ + while (1) { /* do all */ + i = rx_slot; + + if ((MAL_RX_CTRL_EMPTY & rx[i].ctrl) + || (loop_count >= NUM_RX_BUFF)) + break; + loop_count++; + rx_slot++; + if (NUM_RX_BUFF == rx_slot) + rx_slot = 0; + handled++; + data_len = (unsigned long) rx[i].data_len; /* Get len */ + if (data_len) { + if (data_len > ENET_MAX_MTU) /* Check len */ + data_len = 0; + else { + if (EMAC_RX_ERRORS & rx[i].ctrl) { /* Check Errors */ + data_len = 0; + stats.rx_err_log[rx_err_index] = rx[i].ctrl; + rx_err_index++; + if (rx_err_index == MAX_ERR_LOG) + rx_err_index = 0; + } /* emac_erros */ + } /* data_len < max mtu */ + } /* if data_len */ + if (!data_len) { /* no data */ + rx[i].ctrl |= MAL_RX_CTRL_EMPTY; /* Free Recv Buffer */ + + stats.emac.data_len_err++; /* Error at Rx */ + } + + /* !data_len */ + /* AS.HARNOIS */ + /* Check if user has already eaten buffer */ + /* if not => ERROR */ + else if (rx_ready[rx_i_index] != -1) { + if (is_receiving) + printf ("ERROR : Receive buffers are full!\n"); + break; + } else { + stats.emac.rx_frames++; + stats.emac.rx += data_len; + ef_ptr = (struct enet_frame *) rx[i].data_ptr; +#ifdef INFO_405_ENET + packetReceived++; +#endif + /* AS.HARNOIS + * use ring buffer + */ + rx_ready[rx_i_index] = i; + rx_i_index++; + if (NUM_RX_BUFF == rx_i_index) + rx_i_index = 0; + + /* printf("X"); /|* test-only *|/ */ + + /* AS.HARNOIS + * free receive buffer only when + * buffer has been handled (eth_rx) + rx[i].ctrl |= MAL_RX_CTRL_EMPTY; + */ + } /* if data_len */ + } /* while */ + } /* if EMACK_RXCHL */ +} + + +int eth_rx (void) +{ + int length; + int user_index; + unsigned long msr; + + is_receiving = 1; /* tell driver */ + + for (;;) { + /* AS.HARNOIS + * use ring buffer and + * get index from rx buffer desciptor queue + */ + user_index = rx_ready[rx_u_index]; + if (user_index == -1) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); + + length = rx[user_index].data_len; + + /* Pass the packet up to the protocol layers. */ + /* NetReceive(NetRxPackets[rxIdx], length - 4); */ + /* NetReceive(NetRxPackets[i], length); */ + NetReceive (NetRxPackets[user_index], length - 4); + /* Free Recv Buffer */ + rx[user_index].ctrl |= MAL_RX_CTRL_EMPTY; + /* Free rx buffer descriptor queue */ + rx_ready[rx_u_index] = -1; + rx_u_index++; + if (NUM_RX_BUFF == rx_u_index) + rx_u_index = 0; + +#ifdef INFO_405_ENET + packetHandled++; +#endif + + mtmsr (msr); /* Enable IRQ's */ + } + + is_receiving = 0; /* tell driver */ + + return length; +} + +#endif /* CONFIG_405GP */ diff --git a/cpu/ppc4xx/405gp_pci.c b/cpu/ppc4xx/405gp_pci.c new file mode 100644 index 0000000000..1c2c59b9bd --- /dev/null +++ b/cpu/ppc4xx/405gp_pci.c @@ -0,0 +1,502 @@ +/*-----------------------------------------------------------------------------+ + * + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + *-----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------+ + * + * File Name: 405gp_pci.c + * + * Function: Initialization code for the 405GP PCI Configuration regs. + * + * Author: Mark Game + * + * Change Activity- + * + * Date Description of Change BY + * --------- --------------------- --- + * 09-Sep-98 Created MCG + * 02-Nov-98 Removed External arbiter selected message JWB + * 27-Nov-98 Zero out PTMBAR2 and disable in PTM2MS JWB + * 04-Jan-99 Zero out other unused PMM and PTM regs. Change bus scan MCG + * from (0 to n) to (1 to n). + * 17-May-99 Port to Walnut JWB + * 17-Jun-99 Updated for VGA support JWB + * 21-Jun-99 Updated to allow SRAM region to be a target from PCI bus JWB + * 19-Jul-99 Updated for 405GP pass 1 errata #26 (Low PCI subsequent MCG + * target latency timer values are not supported). + * Should be fixed in pass 2. + * 09-Sep-99 Removed use of PTM2 since the SRAM region no longer needs JWB + * to be a PCI target. Zero out PTMBAR2 and disable in PTM2MS. + * 10-Dec-99 Updated PCI_Write_CFG_Reg for pass2 errata #6 JWB + * 11-Jan-00 Ensure PMMxMAs disabled before setting PMMxLAs. This is not + * really required after a reset since PMMxMAs are already + * disabled but is a good practice nonetheless. JWB + * 12-Jun-01 stefan.roese@esd-electronics.com + * - PCI host/adapter handling reworked + * 09-Jul-01 stefan.roese@esd-electronics.com + * - PCI host now configures from device 0 (not 1) to max_dev, + * (host configures itself) + * - On CPCI-405 pci base address and size is generated from + * SDRAM and FLASH size (CFG regs not used anymore) + * - Some minor changes for CPCI-405-A (adapter version) + * 14-Sep-01 stefan.roese@esd-electronics.com + * - CONFIG_PCI_SCAN_SHOW added to print pci devices upon startup + * 28-Sep-01 stefan.roese@esd-electronics.com + * - Changed pci master configuration for linux compatibility + * (no need for bios_fixup() anymore) + * 26-Feb-02 stefan.roese@esd-electronics.com + * - Bug fixed in pci configuration (Andrew May) + * - Removed pci class code init for CPCI405 board + * 15-May-02 stefan.roese@esd-electronics.com + * - New vga device handling + * 29-May-02 stefan.roese@esd-electronics.com + * - PCI class code init added (if defined) + *----------------------------------------------------------------------------*/ + +#include +#include +#include +#if !defined(CONFIG_440) +#include <405gp_pci.h> +#endif +#include +#include + +#if defined(CONFIG_405GP) + +#ifdef CONFIG_PCI + +/*#define DEBUG*/ + +/*-----------------------------------------------------------------------------+ + * pci_init. Initializes the 405GP PCI Configuration regs. + *-----------------------------------------------------------------------------*/ +void pci_405gp_init(struct pci_controller *hose) +{ + DECLARE_GLOBAL_DATA_PTR; + + int i, reg_num = 0; + bd_t *bd = gd->bd; + + unsigned short temp_short; + unsigned long ptmpcila[2] = {CFG_PCI_PTM1PCI, CFG_PCI_PTM2PCI}; +#if defined(CONFIG_CPCI405) + unsigned long ptmla[2] = {bd->bi_memstart, bd->bi_flashstart}; + unsigned long ptmms[2] = {~(bd->bi_memsize - 1) | 1, ~(bd->bi_flashsize - 1) | 1}; +#else + unsigned long ptmla[2] = {CFG_PCI_PTM1LA, CFG_PCI_PTM2LA}; + unsigned long ptmms[2] = {CFG_PCI_PTM1MS, CFG_PCI_PTM2MS}; +#endif +#if defined(CONFIG_PIP405) || defined (CONFIG_MIP405) + unsigned long pmmla[3] = {0x80000000, 0xA0000000, 0}; + unsigned long pmmma[3] = {0xE0000001, 0xE0000001, 0}; + unsigned long pmmpcila[3] = {0x80000000, 0x00000000, 0}; + unsigned long pmmpciha[3] = {0x00000000, 0x00000000, 0}; +#else + unsigned long pmmla[3] = {0x80000000, 0,0}; + unsigned long pmmma[3] = {0xC0000001, 0,0}; + unsigned long pmmpcila[3] = {0x80000000, 0,0}; + unsigned long pmmpciha[3] = {0x00000000, 0,0}; +#endif + + /* + * Register the hose + */ + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* ISA/PCI I/O space */ + pci_set_region(hose->regions + reg_num++, + MIN_PCI_PCI_IOADDR, + MIN_PLB_PCI_IOADDR, + 0x10000, + PCI_REGION_IO); + + /* PCI I/O space */ + pci_set_region(hose->regions + reg_num++, + 0x00800000, + 0xe8800000, + 0x03800000, + PCI_REGION_IO); + + reg_num = 2; + + /* Memory spaces */ + for (i=0; i<2; i++) + if (ptmms[i] & 1) + { + if (!i) hose->pci_fb = hose->regions + reg_num; + + pci_set_region(hose->regions + reg_num++, + ptmpcila[i], ptmla[i], + ~(ptmms[i] & 0xfffff000) + 1, + PCI_REGION_MEM | + PCI_REGION_MEMORY); + } + + /* PCI memory spaces */ + for (i=0; i<3; i++) + if (pmmma[i] & 1) + { + pci_set_region(hose->regions + reg_num++, + pmmpcila[i], pmmla[i], + ~(pmmma[i] & 0xfffff000) + 1, + PCI_REGION_MEM); + } + + hose->region_count = reg_num; + + pci_setup_indirect(hose, + PCICFGADR, + PCICFGDATA); + + if (hose->pci_fb) + pciauto_region_init(hose->pci_fb); + + pci_register_hose(hose); + + /*--------------------------------------------------------------------------+ + * 405GP PCI Master configuration. + * Map one 512 MB range of PLB/processor addresses to PCI memory space. + * PLB address 0x80000000-0xBFFFFFFF ==> PCI address 0x80000000-0xBFFFFFFF + * Use byte reversed out routines to handle endianess. + *--------------------------------------------------------------------------*/ + out32r(PMM0MA, pmmma[0]); /* ensure disabled b4 setting PMM0LA */ + out32r(PMM0LA, pmmla[0]); + out32r(PMM0PCILA, pmmpcila[0]); + out32r(PMM0PCIHA, pmmpciha[0]); + out32r(PMM0MA, pmmma[0]); + + /*--------------------------------------------------------------------------+ + * PMM1 is not used. Initialize them to zero. + *--------------------------------------------------------------------------*/ + out32r(PMM1MA, pmmma[1]); /* ensure disabled b4 setting PMM2LA */ + out32r(PMM1LA, pmmla[1]); + out32r(PMM1PCILA, pmmpcila[1]); + out32r(PMM1PCIHA, pmmpciha[1]); + out32r(PMM1MA, pmmma[1]); + + /*--------------------------------------------------------------------------+ + * PMM2 is not used. Initialize them to zero. + *--------------------------------------------------------------------------*/ + out32r(PMM2MA, pmmma[2]); /* ensure disabled b4 setting PMM2LA */ + out32r(PMM2LA, pmmla[2]); + out32r(PMM2PCILA, pmmpcila[2]); + out32r(PMM2PCIHA, pmmpciha[2]); + out32r(PMM2MA, pmmma[2]); + + /*--------------------------------------------------------------------------+ + * 405GP PCI Target configuration. (PTM1) + * Note: PTM1MS is hardwire enabled but we set the enable bit anyway. + *--------------------------------------------------------------------------*/ + out32r(PTM1LA, ptmla[0]); /* insert address */ + out32r(PTM1MS, ptmms[0]); /* insert size, enable bit is 1 */ + + /*--------------------------------------------------------------------------+ + * 405GP PCI Target configuration. (PTM2) + *--------------------------------------------------------------------------*/ + out32r(PTM2LA, ptmla[1]); /* insert address */ + if (ptmms[1] == 0) + { + out32r(PTM2MS, 0x00000001); /* set enable bit */ + pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_2, 0x00000000); + out32r(PTM2MS, 0x00000000); /* disable */ + } + else + { + out32r(PTM2MS, ptmms[1]); /* insert size, enable bit is 1 */ + } + + /* + * Insert Subsystem Vendor and Device ID + */ + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_VENDOR_ID, CFG_PCI_SUBSYS_VENDORID); +#ifdef CONFIG_CPCI405 + if (mfdcr(strap) & PSR_PCI_ARBIT_EN) + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CFG_PCI_SUBSYS_DEVICEID); + else + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CFG_PCI_SUBSYS_DEVICEID2); +#else + pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CFG_PCI_SUBSYS_DEVICEID); +#endif + + /* + * Insert Class-code + */ +#ifdef CFG_PCI_CLASSCODE + pci_write_config_word(PCIDEVID_405GP, PCI_CLASS_SUB_CODE, CFG_PCI_CLASSCODE); +#endif /* CFG_PCI_CLASSCODE */ + + /*--------------------------------------------------------------------------+ + * If PCI speed = 66Mhz, set 66Mhz capable bit. + *--------------------------------------------------------------------------*/ + if (bd->bi_pci_busfreq >= 66000000) { + pci_read_config_word(PCIDEVID_405GP, PCI_STATUS, &temp_short); + pci_write_config_word(PCIDEVID_405GP,PCI_STATUS,(temp_short|PCI_STATUS_66MHZ)); + } + +#if (CONFIG_PCI_HOST != PCI_HOST_ADAPTER) +#if (CONFIG_PCI_HOSE == PCI_HOST_AUTO) + if (mfdcr(strap) & PSR_PCI_ARBIT_EN) +#endif + { + /*--------------------------------------------------------------------------+ + * Write the 405GP PCI Configuration regs. + * Enable 405GP to be a master on the PCI bus (PMM). + * Enable 405GP to act as a PCI memory target (PTM). + *--------------------------------------------------------------------------*/ + pci_read_config_word(PCIDEVID_405GP, PCI_COMMAND, &temp_short); + pci_write_config_word(PCIDEVID_405GP, PCI_COMMAND, temp_short | + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + } +#endif + + /* + * Set HCE bit (Host Configuration Enabled) + */ + pci_read_config_word(PCIDEVID_405GP, PCIBRDGOPT2, &temp_short); + pci_write_config_word(PCIDEVID_405GP, PCIBRDGOPT2, (temp_short | 0x0001)); + +#ifdef CONFIG_PCI_PNP + /*--------------------------------------------------------------------------+ + * Scan the PCI bus and configure devices found. + *--------------------------------------------------------------------------*/ +#if (CONFIG_PCI_HOST == PCI_HOST_AUTO) + if (mfdcr(strap) & PSR_PCI_ARBIT_EN) +#endif + { +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + + hose->last_busno = pci_hose_scan(hose); + } +#endif /* CONFIG_PCI_PNP */ + +} + +/* + * drivers/pci.c skips every host bridge but the 405GP since it could + * be set as an Adapter. + * + * I (Andrew May) don't know what we should do here, but I don't want + * the auto setup of a PCI device disabling what is done pci_405gp_init + * as has happened before. + */ +void pci_405gp_setup_bridge(struct pci_controller *hose, pci_dev_t dev, + struct pci_config_table *entry) +{ +#ifdef DEBUG + printf("405gp_setup_bridge\n"); +#endif +} + +/* + * + */ + +void pci_405gp_fixup_irq(struct pci_controller *hose, pci_dev_t dev) +{ + unsigned char int_line = 0xff; + + /* + * Write pci interrupt line register (cpci405 specific) + */ + switch (PCI_DEV(dev) & 0x03) + { + case 0: + int_line = 27 + 2; + break; + case 1: + int_line = 27 + 3; + break; + case 2: + int_line = 27 + 0; + break; + case 3: + int_line = 27 + 1; + break; + } + + pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE, int_line); +} + +void pci_405gp_setup_vga(struct pci_controller *hose, pci_dev_t dev, + struct pci_config_table *entry) +{ + unsigned int cmdstat = 0; + + pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_io); + + /* always enable io space on vga boards */ + pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat); + cmdstat |= PCI_COMMAND_IO; + pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat); +} + +#if !(defined(CONFIG_PIP405) || defined (CONFIG_MIP405)) + +/* + *As is these functs get called out of flash Not a horrible + *thing, but something to keep in mind. (no statics?) + */ +static struct pci_config_table pci_405gp_config_table[] = { +/*if VendID is 0 it terminates the table search (ie Walnut)*/ +#if CFG_PCI_SUBSYS_VENDORID + {CFG_PCI_SUBSYS_VENDORID, PCI_ANY_ID, PCI_CLASS_BRIDGE_HOST, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_bridge}, +#endif + {PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_vga}, + + {PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NOT_DEFINED_VGA, + PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_vga}, + + { } +}; + +static struct pci_controller hose = { + fixup_irq: pci_405gp_fixup_irq, + config_table: pci_405gp_config_table, +}; + +void pci_init(void) +{ + /*we want the ptrs to RAM not flash (ie don't use init list)*/ + hose.fixup_irq = pci_405gp_fixup_irq; + hose.config_table = pci_405gp_config_table; + pci_405gp_init(&hose); +} + +#endif + +#endif /* CONFIG_PCI */ + +#endif /* CONFIG_405GP */ + +/*-----------------------------------------------------------------------------+ + * CONFIG_440 + *-----------------------------------------------------------------------------*/ +#if defined(CONFIG_440) && defined(CONFIG_PCI) + +static struct pci_controller ppc440_hose = {0}; + + +void pci_440_init (struct pci_controller *hose) +{ + int reg_num = 0; + unsigned long strap; + + /*--------------------------------------------------------------------------+ + * The PCI initialization sequence enable bit must be set ... if not abort + * pci setup since updating the bit requires chip reset. + *--------------------------------------------------------------------------*/ + strap = mfdcr(cpc0_strp1); + if( (strap & 0x00040000) == 0 ){ + printf("PCI: CPC0_STRP1[PISE] not set.\n"); + printf("PCI: Configuration aborted.\n"); + return; + } + + /*--------------------------------------------------------------------------+ + * PCI controller init + *--------------------------------------------------------------------------*/ + hose->first_busno = 0; + hose->last_busno = 0xff; + + pci_set_region(hose->regions + reg_num++, + 0x00000000, + PCIX0_IOBASE, + 0x10000, + PCI_REGION_IO); + + pci_set_region(hose->regions + reg_num++, + CFG_PCI_TARGBASE, + CFG_PCI_MEMBASE, + 0x10000000, + PCI_REGION_MEM ); + hose->region_count = reg_num; + + pci_setup_indirect(hose, PCIX0_CFGADR, PCIX0_CFGDATA); + +#if defined(CFG_PCI_PRE_INIT) + /* Let board change/modify hose & do initial checks */ + if( pci_pre_init (hose) == 0 ){ + printf("PCI: Board-specific initialization failed.\n"); + printf("PCI: Configuration aborted.\n"); + return; + } +#endif + + pci_register_hose( hose ); + + /*--------------------------------------------------------------------------+ + * PCI target init + *--------------------------------------------------------------------------*/ +#if defined(CFG_PCI_TARGET_INIT) + pci_target_init(hose); /* Let board setup pci target */ +#else + out16r( PCIX0_SBSYSVID, CFG_PCI_SUBSYS_VENDORID ); + out16r( PCIX0_SBSYSID, CFG_PCI_SUBSYS_ID ); + out16r( PCIX0_CLS, 0x00060000 ); /* Bridge, host bridge */ +#endif + + out32r( PCIX0_BRDGOPT1, 0x10000060 ); /* PLB Rq pri highest */ + out32r( PCIX0_BRDGOPT2, in32(PCIX0_BRDGOPT2) | 1 ); /* Enable host config */ + + /*--------------------------------------------------------------------------+ + * PCI master init: default is one 256MB region for PCI memory: + * 0x3_00000000 - 0x3_0FFFFFFF ==> CFG_PCI_MEMBASE + *--------------------------------------------------------------------------*/ +#if defined(CFG_PCI_MASTER_INIT) + pci_master_init(hose); /* Let board setup pci master */ +#else + out32r( PCIX0_POM0SA, 0 ); /* disable */ + out32r( PCIX0_POM1SA, 0 ); /* disable */ + out32r( PCIX0_POM2SA, 0 ); /* disable */ + out32r( PCIX0_POM0LAL, 0x00000000 ); + out32r( PCIX0_POM0LAH, 0x00000003 ); + out32r( PCIX0_POM0PCIAL, CFG_PCI_MEMBASE ); + out32r( PCIX0_POM0PCIAH, 0x00000000 ); + out32r( PCIX0_POM0SA, 0xf0000001 ); /* 256MB, enabled */ + out32r( PCIX0_STS, in32r( PCIX0_STS ) & ~0x0000fff8 ); +#endif + + /*--------------------------------------------------------------------------+ + * PCI host configuration -- we don't make any assumptions here ... the + * _board_must_indicate_ what to do -- there's just too many runtime + * scenarios in environments like cPCI, PPMC, etc. to make a determination + * based on hard-coded values or state of arbiter enable. + *--------------------------------------------------------------------------*/ + if( is_pci_host(hose) ){ +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Bus Dev VenId DevId Class Int\n"); +#endif + out16r( PCIX0_CMD, in16r( PCIX0_CMD ) | PCI_COMMAND_MASTER); + hose->last_busno = pci_hose_scan(hose); + } +} + + +void pci_init(void) +{ + pci_440_init (&ppc440_hose); +} + +#endif /* CONFIG_440 & CONFIG_PCI */ diff --git a/cpu/ppc4xx/cpu.c b/cpu/ppc4xx/cpu.c new file mode 100644 index 0000000000..8aaffb1c19 --- /dev/null +++ b/cpu/ppc4xx/cpu.c @@ -0,0 +1,253 @@ +/* + * (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 + */ + +/* + * m8xx.c + * + * CPU specific code + * + * written or collected and sometimes rewritten by + * Magnus Damm + * + * minor modifications by + * Wolfgang Denk + */ + +#include +#include +#include +#include +#include + + +#if defined(CONFIG_440) +static int do_chip_reset( unsigned long sys0, unsigned long sys1 ); +#endif + +/* ------------------------------------------------------------------------- */ + +int checkcpu (void) +{ +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_IOP480) || defined(CONFIG_440) + uint pvr = get_pvr(); +#endif +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_IOP480) + DECLARE_GLOBAL_DATA_PTR; + + ulong clock = gd->cpu_clk; + char buf[32]; +#endif + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) + PPC405_SYS_INFO sys_info; + + puts ("CPU: "); + + get_sys_info(&sys_info); + +#if CONFIG_405GP + puts("IBM PowerPC 405GP"); + if (pvr == PVR_405GPR_RA) { + putc('r'); + } + puts(" Rev. "); +#endif +#if CONFIG_405CR + puts("IBM PowerPC 405CR Rev. "); +#endif + switch (pvr) { + case PVR_405GP_RB: + putc('B'); + break; + case PVR_405GP_RC: +#if CONFIG_405CR + case PVR_405CR_RC: +#endif + putc('C'); + break; + case PVR_405GP_RD: + putc('D'); + break; +#if CONFIG_405GP + case PVR_405GP_RE: + putc('E'); + break; +#endif + case PVR_405CR_RA: + case PVR_405GPR_RA: + putc('A'); + break; + case PVR_405CR_RB: + putc('B'); + break; + default: + printf("? (PVR=%08x)", pvr); + break; + } + + printf(" at %s MHz (PLB=%lu, OPB=%lu, EBC=%lu MHz)\n", strmhz(buf, clock), + sys_info.freqPLB / 1000000, + sys_info.freqPLB / sys_info.pllOpbDiv / 1000000, + sys_info.freqPLB / sys_info.pllExtBusDiv / 1000000); + +#if CONFIG_405GP + if (mfdcr(strap) & PSR_PCI_ASYNC_EN) + printf(" PCI async ext clock used, "); + else + printf(" PCI sync clock at %lu MHz, ", + sys_info.freqPLB / sys_info.pllPciDiv / 1000000); + if (mfdcr(strap) & PSR_PCI_ARBIT_EN) + printf("internal PCI arbiter enabled\n"); + else + printf("external PCI arbiter enabled\n"); +#endif + + if ((pvr | 0x00000001) == PVR_405GPR_RA) { + printf(" 16 kB I-Cache 16 kB D-Cache"); + } else { + printf(" 16 kB I-Cache 8 kB D-Cache"); + } + + +#endif /* defined(CONFIG_405GP) || defined(CONFIG_405CR) */ + +#ifdef CONFIG_IOP480 + printf("PLX IOP480 (PVR=%08x)", pvr); + printf(" at %s MHz:", strmhz(buf, clock)); + printf(" %u kB I-Cache", 4); + printf(" %u kB D-Cache", 2); +#endif + +#if defined(CONFIG_440) + puts("IBM PowerPC 440 Rev. "); + switch(pvr) + { + case PVR_440GP_RB: + putc('B'); + /* See errata 1.12: CHIP_4 */ + if( ( mfdcr(cpc0_sys0) != mfdcr(cpc0_strp0) ) + ||( mfdcr(cpc0_sys1) != mfdcr(cpc0_strp1) ) ){ + puts("\n\t CPC0_SYSx DCRs corrupted. Resetting chip ...\n"); + udelay( 1000 * 1000 ); /* Give time for serial buf to clear */ + do_chip_reset( mfdcr(cpc0_strp0), mfdcr(cpc0_strp1) ); + } + break; + case PVR_440GP_RC: + putc('C'); + break; + default: + printf("UNKNOWN (PVR=%08x)", pvr); + break; + } +#endif + + printf("\n"); + + return 0; +} + + +/* ------------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) +{ + /* + * Initiate system reset in debug control register DBCR + */ + __asm__ __volatile__("lis 3, 0x3000" ::: "r3"); +#if defined(CONFIG_440) + __asm__ __volatile__("mtspr 0x134, 3"); +#else + __asm__ __volatile__("mtspr 0x3f2, 3"); +#endif + return 1; +} + +#if defined(CONFIG_440) +static +int do_chip_reset( unsigned long sys0, unsigned long sys1 ) +{ + /* Changes to cpc0_sys0 and cpc0_sys1 require chip + * reset. + */ + mtdcr( cntrl0, mfdcr(cntrl0) | 0x80000000 ); /* Set SWE */ + mtdcr( cpc0_sys0, sys0 ); + mtdcr( cpc0_sys1, sys1 ); + mtdcr( cntrl0, mfdcr(cntrl0) & ~0x80000000 ); /* Clr SWE */ + mtspr( dbcr0, 0x20000000); /* Reset the chip */ + + return 1; +} +#endif + + +/* + * Get timebase clock frequency + */ +unsigned long get_tbclk (void) +{ +#if defined(CONFIG_440) + + sys_info_t sys_info; + + get_sys_info(&sys_info); + return (sys_info.freqProcessor); + +#elif defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_405) + + PPC405_SYS_INFO sys_info; + + get_sys_info(&sys_info); + return (sys_info.freqProcessor); + +#elif defined(CONFIG_IOP480) + + return (66000000); + +#else + +# error get_tbclk() not implemented + +#endif + +} + + +#if defined(CONFIG_WATCHDOG) +void +watchdog_reset(void) +{ + int re_enable = disable_interrupts(); + reset_4xx_watchdog(); + if (re_enable) enable_interrupts(); +} + +void +reset_4xx_watchdog(void) +{ + /* + * Clear TSR(WIS) bit + */ + mtspr(tsr, 0x40000000); +} +#endif /* CONFIG_WATCHDOG */ diff --git a/cpu/ppc4xx/i2c.c b/cpu/ppc4xx/i2c.c new file mode 100644 index 0000000000..68af057d1e --- /dev/null +++ b/cpu/ppc4xx/i2c.c @@ -0,0 +1,417 @@ +/*****************************************************************************/ +/* I2C Bus interface initialisation and I2C Commands */ +/* for PPC405GP */ +/* Author : AS HARNOIS */ +/* Date : 13.Dec.00 */ +/*****************************************************************************/ + +#include +#include +#if defined(CONFIG_440) +# include <440_i2c.h> +#else +# include <405gp_i2c.h> +#endif +#include + +#ifdef CONFIG_HARD_I2C + +#define IIC_OK 0 +#define IIC_NOK 1 +#define IIC_NOK_LA 2 /* Lost arbitration */ +#define IIC_NOK_ICT 3 /* Incomplete transfer */ +#define IIC_NOK_XFRA 4 /* Transfer aborted */ +#define IIC_NOK_DATA 5 /* No data in buffer */ +#define IIC_NOK_TOUT 6 /* Transfer timeout */ + +#define IIC_TIMEOUT 1 /* 1 seconde */ + + +static void _i2c_bus_reset (void) +{ + int i, status; + + /* Reset status register */ + /* write 1 in SCMP and IRQA to clear these fields */ + out8 (IIC_STS, 0x0A); + + /* write 1 in IRQP IRQD LA ICT XFRA to clear these fields */ + out8 (IIC_EXTSTS, 0x8F); + __asm__ volatile ("eieio"); + + /* + * Get current state, reset bus + * only if no transfers are pending. + */ + i = 10; + do { + /* Get status */ + status = in8 (IIC_STS); + udelay (500); /* 500us */ + i--; + } while ((status & IIC_STS_PT) && (i > 0)); + /* Soft reset controller */ + status = in8 (IIC_XTCNTLSS); + out8 (IIC_XTCNTLSS, (status | IIC_XTCNTLSS_SRST)); + __asm__ volatile ("eieio"); + + /* make sure where in initial state, data hi, clock hi */ + out8 (IIC_DIRECTCNTL, 0xC); + for (i = 0; i < 10; i++) { + if ((in8 (IIC_DIRECTCNTL) & 0x3) != 0x3) { + /* clock until we get to known state */ + out8 (IIC_DIRECTCNTL, 0x8); /* clock lo */ + udelay (100); /* 100us */ + out8 (IIC_DIRECTCNTL, 0xC); /* clock hi */ + udelay (100); /* 100us */ + } else { + break; + } + } + /* send start condition */ + out8 (IIC_DIRECTCNTL, 0x4); + udelay (1000); /* 1ms */ + /* send stop condition */ + out8 (IIC_DIRECTCNTL, 0xC); + udelay (1000); /* 1ms */ + /* Unreset controller */ + out8 (IIC_XTCNTLSS, (status & ~IIC_XTCNTLSS_SRST)); + udelay (1000); /* 1ms */ +} + +void i2c_init (int speed, int slaveadd) +{ + sys_info_t sysInfo; + unsigned long freqOPB; + int val, divisor; + + /* Handle possible failed I2C state */ + _i2c_bus_reset (); + + /* clear lo master address */ + out8 (IIC_LMADR, 0); + + /* clear hi master address */ + out8 (IIC_HMADR, 0); + + /* clear lo slave address */ + out8 (IIC_LSADR, 0); + + /* clear hi slave address */ + out8 (IIC_HSADR, 0); + + /* Clock divide Register */ + /* get OPB frequency */ + get_sys_info (&sysInfo); + freqOPB = sysInfo.freqPLB / sysInfo.pllOpbDiv; + /* set divisor according to freqOPB */ + divisor = (freqOPB - 1) / 10000000; + if (divisor == 0) + divisor = 1; + out8 (IIC_CLKDIV, divisor); + + /* no interrupts */ + out8 (IIC_INTRMSK, 0); + + /* clear transfer count */ + out8 (IIC_XFRCNT, 0); + + /* clear extended control & stat */ + /* write 1 in SRC SRS SWC SWS to clear these fields */ + out8 (IIC_XTCNTLSS, 0xF0); + + /* Mode Control Register + Flush Slave/Master data buffer */ + out8 (IIC_MDCNTL, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB); + __asm__ volatile ("eieio"); + + + val = in8(IIC_MDCNTL); + __asm__ volatile ("eieio"); + + /* Ignore General Call, slave transfers are ignored, + disable interrupts, exit unknown bus state, enable hold + SCL + 100kHz normaly or FastMode for 400kHz and above + */ + + val |= IIC_MDCNTL_EUBS|IIC_MDCNTL_HSCL; + if( speed >= 400000 ){ + val |= IIC_MDCNTL_FSM; + } + out8 (IIC_MDCNTL, val); + + /* clear control reg */ + out8 (IIC_CNTL, 0x00); + __asm__ volatile ("eieio"); + +} + +/* + This code tries to use the features of the 405GP i2c + controller. It will transfer up to 4 bytes in one pass + on the loop. It only does out8(lbz) to the buffer when it + is possible to do out16(lhz) transfers. + + cmd_type is 0 for write 1 for read. + + addr_len can take any value from 0-255, it is only limited + by the char, we could make it larger if needed. If it is + 0 we skip the address write cycle. + + Typical case is a Write of an addr followd by a Read. The + IBM FAQ does not cover this. On the last byte of the write + we don't set the creg CHT bit, and on the first bytes of the + read we set the RPST bit. + + It does not support address only transfers, there must be + a data part. If you want to write the address yourself, put + it in the data pointer. + + It does not support transfer to/from address 0. + + It does not check XFRCNT. +*/ +static +int i2c_transfer(unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len ) +{ + unsigned char* ptr; + int reading; + int tran,cnt; + int result; + int status; + int i; + uchar creg; + + if( data == 0 || data_len == 0 ){ + /*Don't support data transfer of no length or to address 0*/ + printf( "i2c_transfer: bad call\n" ); + return IIC_NOK; + } + if( addr && addr_len ){ + ptr = addr; + cnt = addr_len; + reading = 0; + }else{ + ptr = data; + cnt = data_len; + reading = cmd_type; + } + + /*Clear Stop Complete Bit*/ + out8(IIC_STS,IIC_STS_SCMP); + /* Check init */ + i=10; + do { + /* Get status */ + status = in8(IIC_STS); + __asm__ volatile("eieio"); + i--; + } while ((status & IIC_STS_PT) && (i>0)); + + if (status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + return(result); + } + /*flush the Master/Slave Databuffers*/ + out8(IIC_MDCNTL, ((in8(IIC_MDCNTL))|IIC_MDCNTL_FMDB|IIC_MDCNTL_FSDB)); + /*need to wait 4 OPB clocks? code below should take that long*/ + + /* 7-bit adressing */ + out8(IIC_HMADR,0); + out8(IIC_LMADR, chip); + __asm__ volatile("eieio"); + + tran = 0; + result = IIC_OK; + creg = 0; + + while ( tran != cnt && (result == IIC_OK)) { + int bc,j; + + /* Control register = + Normal transfer, 7-bits adressing, Transfer up to bc bytes, Normal start, + Transfer is a sequence of transfers + */ + creg |= IIC_CNTL_PT; + + bc = (cnt - tran) > 4 ? 4 : + cnt - tran; + creg |= (bc-1)<<4; + /* if the real cmd type is write continue trans*/ + if ( (!cmd_type && (ptr == addr)) || ((tran+bc) != cnt) ) + creg |= IIC_CNTL_CHT; + + if (reading) + creg |= IIC_CNTL_READ; + else { + for(j=0; j0)); + + if (status & IIC_STS_ERR) { + result = IIC_NOK; + status = in8 (IIC_EXTSTS); + /* Lost arbitration? */ + if (status & IIC_EXTSTS_LA) + result = IIC_NOK_LA; + /* Incomplete transfer? */ + if (status & IIC_EXTSTS_ICT) + result = IIC_NOK_ICT; + /* Transfer aborted? */ + if (status & IIC_EXTSTS_XFRA) + result = IIC_NOK_XFRA; + } else if ( status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + } + /* Command is reading => get buffer */ + if ((reading) && (result == IIC_OK)) { + /* Are there data in buffer */ + if (status & IIC_STS_MDBS) { + /* + even if we have data we have to wait 4OPB clocks + for it to hit the front of the FIFO, after that + we can just read. We should check XFCNT here and + if the FIFO is full there is no need to wait. + */ + udelay (1); + for(j=0;jed (i.e. there was a chip at that address which + * drove the data line low). + */ + return(i2c_transfer (1, chip << 1, 0,0, buf, 1) != 0); +} + + + +int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + uchar xaddr[4]; + int ret; + + if ( alen > 4 ) { + printf ("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if ( alen > 0 ) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + +#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if( alen > 0 ) + chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif + if( (ret = i2c_transfer( 1, chip<<1, &xaddr[4-alen], alen, buffer, len )) != 0) { + printf( "I2c read: failed %d\n", ret); + return 1; + } + return 0; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + uchar xaddr[4]; + + if ( alen > 4 ) { + printf ("I2C write: addr len %d not supported\n", alen); + return 1; + + } + if ( alen > 0 ) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + +#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if( alen > 0 ) + chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif + + return (i2c_transfer( 0, chip<<1, &xaddr[4-alen], alen, buffer, len ) != 0); +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/cpu/ppc4xx/sdram.c b/cpu/ppc4xx/sdram.c new file mode 100644 index 0000000000..d64bf96f60 --- /dev/null +++ b/cpu/ppc4xx/sdram.c @@ -0,0 +1,191 @@ +/* + * (C) Copyright 2002 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include + + +#ifdef CONFIG_SDRAM_BANK0 + + +#define MAGIC0 0x00000000 +#define MAGIC1 0x11111111 +#define MAGIC2 0x22222222 +#define MAGIC3 0x33333333 +#define MAGIC4 0x44444444 + +#define ADDR_ZERO 0x00000000 +#define ADDR_400 0x00000400 +#define ADDR_08MB 0x00800000 +#define ADDR_16MB 0x01000000 +#define ADDR_32MB 0x02000000 +#define ADDR_64MB 0x04000000 + +#define mtsdram0(reg, data) mtdcr(memcfga,reg);mtdcr(memcfgd,data) + + +/*----------------------------------------------------------------------- + */ +void sdram_init(void) +{ + ulong speed; + ulong sdtr1; + ulong rtr; + + /* + * Determine SDRAM speed + */ + speed = get_bus_freq(0); /* parameter not used on ppc4xx */ + + /* + * Support for 100MHz and 133MHz SDRAM + */ + if (speed > 100000000) { + /* + * 133 MHz SDRAM + */ + sdtr1 = 0x01074015; + rtr = 0x07f00000; + } else { + /* + * default: 100 MHz SDRAM + */ + sdtr1 = 0x0086400d; + rtr = 0x05f00000; + } + + /* + * Set MB0CF for bank 0. (0-64MB) Address Mode 3 since 13x9(4) + */ + mtsdram0(mem_mb0cf, 0x00084001); + + mtsdram0(mem_sdtr1, sdtr1); + mtsdram0(mem_rtr, rtr); + + /* + * Wait for 200us + */ + udelay(200); + + /* + * Set memory controller options reg, MCOPT1. + * Set DC_EN to '1' and BRD_PRF to '01' for 16 byte PLB Burst + * read/prefetch. + */ + mtsdram0(mem_mcopt1, 0x80800000); + + /* + * Wait for 10ms + */ + udelay(10000); + + /* + * Test if 64 MByte are equipped (mirror test) + */ + *(volatile ulong *)ADDR_ZERO = MAGIC0; + *(volatile ulong *)ADDR_08MB = MAGIC1; + *(volatile ulong *)ADDR_16MB = MAGIC2; + *(volatile ulong *)ADDR_32MB = MAGIC3; + + if ((*(volatile ulong *)ADDR_ZERO == MAGIC0) && + (*(volatile ulong *)ADDR_08MB == MAGIC1) && + (*(volatile ulong *)ADDR_16MB == MAGIC2)) { + /* + * OK, 64MB detected -> all done + */ + return; + } + + /* + * Now test for 32 MByte... + */ + + /* + * Disable memory controller. + */ + mtsdram0(mem_mcopt1, 0x00000000); + + /* + * Set MB0CF for bank 0. (0-32MB) Address Mode 2 since 12x9(4) + */ + mtsdram0(mem_mb0cf, 0x00062001); + + /* + * Set memory controller options reg, MCOPT1. + * Set DC_EN to '1' and BRD_PRF to '01' for 16 byte PLB Burst + * read/prefetch. + */ + mtsdram0(mem_mcopt1, 0x80800000); + + /* + * Wait for 10ms + */ + udelay(10000); + + /* + * Test if 32 MByte are equipped (mirror test) + */ + *(volatile ulong *)ADDR_ZERO = MAGIC0; + *(volatile ulong *)ADDR_400 = MAGIC1; + *(volatile ulong *)ADDR_08MB = MAGIC2; + *(volatile ulong *)ADDR_16MB = MAGIC3; + + if ((*(volatile ulong *)ADDR_ZERO == MAGIC0) && + (*(volatile ulong *)ADDR_400 == MAGIC1) && + (*(volatile ulong *)ADDR_08MB == MAGIC2)) { + /* + * OK, 32MB detected -> all done + */ + return; + } + + /* + * Setup for 16 MByte... + */ + + /* + * Disable memory controller. + */ + mtsdram0(mem_mcopt1, 0x00000000); + + /* + * Set MB0CF for bank 0. (0-16MB) Address Mode 4 since 12x8(4) + */ + mtsdram0(mem_mb0cf, 0x00046001); + + /* + * Set memory controller options reg, MCOPT1. + * Set DC_EN to '1' and BRD_PRF to '01' for 16 byte PLB Burst + * read/prefetch. + */ + mtsdram0(mem_mcopt1, 0x80800000); + + /* + * Wait for 10ms + */ + udelay(10000); +} + +#endif /* CONFIG_SDRAM_BANK0 */ diff --git a/cpu/ppc4xx/speed.c b/cpu/ppc4xx/speed.c new file mode 100644 index 0000000000..45415292c3 --- /dev/null +++ b/cpu/ppc4xx/speed.c @@ -0,0 +1,308 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include + +/* ------------------------------------------------------------------------- */ + +#define ONE_BILLION 1000000000 + + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) + +void get_sys_info (PPC405_SYS_INFO * sysInfo) +{ + unsigned long pllmr; + unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ / 1000); + uint pvr = get_pvr(); + unsigned long psr; + unsigned long m; + + /* + * Read PLL Mode register + */ + pllmr = mfdcr (pllmd); + + /* + * Read Pin Strapping register + */ + psr = mfdcr (strap); + + /* + * Determine FWD_DIV. + */ + sysInfo->pllFwdDiv = 8 - ((pllmr & PLLMR_FWD_DIV_MASK) >> 29); + + /* + * Determine FBK_DIV. + */ + sysInfo->pllFbkDiv = ((pllmr & PLLMR_FB_DIV_MASK) >> 25); + if (sysInfo->pllFbkDiv == 0) { + sysInfo->pllFbkDiv = 16; + } + + /* + * Determine PLB_DIV. + */ + sysInfo->pllPlbDiv = ((pllmr & PLLMR_CPU_TO_PLB_MASK) >> 17) + 1; + + /* + * Determine PCI_DIV. + */ + sysInfo->pllPciDiv = ((pllmr & PLLMR_PCI_TO_PLB_MASK) >> 13) + 1; + + /* + * Determine EXTBUS_DIV. + */ + sysInfo->pllExtBusDiv = ((pllmr & PLLMR_EXB_TO_PLB_MASK) >> 11) + 2; + + /* + * Determine OPB_DIV. + */ + sysInfo->pllOpbDiv = ((pllmr & PLLMR_OPB_TO_PLB_MASK) >> 15) + 1; + + /* + * Check if PPC405GPr used (mask minor revision field) + */ + if ((pvr & 0xfffffff0) == (PVR_405GPR_RA & 0xfffffff0)) { + /* + * Determine FWD_DIV B (only PPC405GPr with new mode strapping). + */ + sysInfo->pllFwdDivB = 8 - (pllmr & PLLMR_FWDB_DIV_MASK); + + /* + * Determine factor m depending on PLL feedback clock source + */ + if (!(psr & PSR_PCI_ASYNC_EN)) { + if (psr & PSR_NEW_MODE_EN) { + /* + * sync pci clock used as feedback (new mode) + */ + m = 1 * sysInfo->pllFwdDivB * 2 * sysInfo->pllPciDiv; + } else { + /* + * sync pci clock used as feedback (legacy mode) + */ + m = 1 * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv * sysInfo->pllPciDiv; + } + } else if (psr & PSR_NEW_MODE_EN) { + if (psr & PSR_PERCLK_SYNC_MODE_EN) { + /* + * PerClk used as feedback (new mode) + */ + m = 1 * sysInfo->pllFwdDivB * 2 * sysInfo->pllExtBusDiv; + } else { + /* + * CPU clock used as feedback (new mode) + */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDiv; + } + } else if (sysInfo->pllExtBusDiv == sysInfo->pllFbkDiv) { + /* + * PerClk used as feedback (legacy mode) + */ + m = 1 * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv * sysInfo->pllExtBusDiv; + } else { + /* + * PLB clock used as feedback (legacy mode) + */ + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv; + } + + sysInfo->freqVCOMhz = (1000000 * m) / sysClkPeriodPs; + sysInfo->freqProcessor = (sysInfo->freqVCOMhz * 1000000) / sysInfo->pllFwdDiv; + sysInfo->freqPLB = (sysInfo->freqVCOMhz * 1000000) / + (sysInfo->pllFwdDivB * sysInfo->pllPlbDiv); + } else { + /* + * Check pllFwdDiv to see if running in bypass mode where the CPU speed + * is equal to the 405GP SYS_CLK_FREQ. If not in bypass mode, check VCO + * to make sure it is within the proper range. + * spec: VCO = SYS_CLOCK x FBKDIV x PLBDIV x FWDDIV + * Note freqVCO is calculated in Mhz to avoid errors introduced by rounding. + */ + if (sysInfo->pllFwdDiv == 1) { + sysInfo->freqProcessor = CONFIG_SYS_CLK_FREQ; + sysInfo->freqPLB = CONFIG_SYS_CLK_FREQ / sysInfo->pllPlbDiv; + } else { + sysInfo->freqVCOMhz = ( 1000000 * + sysInfo->pllFwdDiv * + sysInfo->pllFbkDiv * + sysInfo->pllPlbDiv + ) / sysClkPeriodPs; + if (sysInfo->freqVCOMhz >= VCO_MIN + && sysInfo->freqVCOMhz <= VCO_MAX) { + sysInfo->freqPLB = (ONE_BILLION / + ((sysClkPeriodPs * 10) / + sysInfo->pllFbkDiv)) * 10000; + sysInfo->freqProcessor = sysInfo->freqPLB * sysInfo->pllPlbDiv; + } else { + printf ("\nInvalid VCO frequency calculated : %ld MHz \a\n", + sysInfo->freqVCOMhz); + printf ("It must be between %d-%d MHz \a\n", + VCO_MIN, VCO_MAX); + printf ("PLL Mode reg : %8.8lx\a\n", + pllmr); + hang (); + } + } + } +} + + +/******************************************** + * get_OPB_freq + * return OPB bus freq in Hz + *********************************************/ +ulong get_OPB_freq (void) +{ + ulong val = 0; + + PPC405_SYS_INFO sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqPLB / sys_info.pllOpbDiv; + + return val; +} + + +/******************************************** + * get_PCI_freq + * return PCI bus freq in Hz + *********************************************/ +ulong get_PCI_freq (void) +{ + ulong val; + PPC405_SYS_INFO sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqPLB / sys_info.pllPciDiv; + return val; +} + + +#elif defined(CONFIG_440) +void get_sys_info (sys_info_t * sysInfo) +{ + unsigned long strp0; + unsigned long temp; + unsigned long m; + + /* Extract configured divisors */ + strp0 = mfdcr( cpc0_strp0 ); + sysInfo->pllFwdDivA = 8 - ((strp0 & PLLSYS0_FWD_DIV_A_MASK) >> 15); + sysInfo->pllFwdDivB = 8 - ((strp0 & PLLSYS0_FWD_DIV_B_MASK) >> 12); + temp = (strp0 & PLLSYS0_FB_DIV_MASK) >> 18; + sysInfo->pllFbkDiv = temp ? temp : 16; + sysInfo->pllOpbDiv = 1 + ((strp0 & PLLSYS0_OPB_DIV_MASK) >> 10); + sysInfo->pllExtBusDiv = 1 + ((strp0 & PLLSYS0_EPB_DIV_MASK) >> 8); + + /* Calculate 'M' based on feedback source */ + if( strp0 & PLLSYS0_EXTSL_MASK ) + m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; + else + m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; + + /* Now calculate the individual clocks */ + sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m>>1); + sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; + sysInfo->freqPLB = sysInfo->freqVCOMhz/sysInfo->pllFwdDivB; + if( get_pvr() == PVR_440GP_RB ) /* Rev B divs an extra 2 -- geez! */ + sysInfo->freqPLB >>= 1; + sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; + sysInfo->freqEPB = sysInfo->freqOPB/sysInfo->pllExtBusDiv; + +} + +ulong get_OPB_freq (void) +{ + + sys_info_t sys_info; + get_sys_info (&sys_info); + return sys_info.freqOPB; +} + +#elif defined(CONFIG_405) + +void get_sys_info (sys_info_t * sysInfo) { + + sysInfo->freqVCOMhz=3125000; + sysInfo->freqProcessor=12*1000*1000; + sysInfo->freqPLB=50*1000*1000; + sysInfo->freqPCI=66*1000*1000; + +} + +#endif + +int get_clocks (void) +{ +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_440) || defined(CONFIG_405) + DECLARE_GLOBAL_DATA_PTR; + + sys_info_t sys_info; + + get_sys_info (&sys_info); + gd->cpu_clk = sys_info.freqProcessor; + gd->bus_clk = sys_info.freqPLB; + +#endif /* defined(CONFIG_405GP) || defined(CONFIG_405CR) */ + +#ifdef CONFIG_IOP480 + DECLARE_GLOBAL_DATA_PTR; + + gd->cpu_clk = 66000000; + gd->bus_clk = 66000000; +#endif + return (0); +} + + +/******************************************** + * get_bus_freq + * return PLB bus freq in Hz + *********************************************/ +ulong get_bus_freq (ulong dummy) +{ + ulong val; + +#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_405) || defined(CONFIG_440) + sys_info_t sys_info; + + get_sys_info (&sys_info); + val = sys_info.freqPLB; + +#elif defined(CONFIG_IOP480) + + val = 66; + +#else +# error get_bus_freq() not implemented +#endif + + return val; +} diff --git a/cpu/ppc4xx/vecnum.h b/cpu/ppc4xx/vecnum.h new file mode 100644 index 0000000000..d493a5dd8f --- /dev/null +++ b/cpu/ppc4xx/vecnum.h @@ -0,0 +1,100 @@ +/* +* Copyright (C) 2002 Scott McNutt +* +* 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 +*/ + +/* + * Interrupt vector number definitions to ease the + * 405 -- 440 porting pain ;-) + * + * NOTE: They're not all here yet ... update as needed. + * + */ + +#ifndef _VECNUMS_H_ +#define _VECNUMS_H_ + +#if defined(CONFIG_440) + +/* UIC 0 */ +#define VECNUM_U0 0 /* UART0 */ +#define VECNUM_U1 1 /* UART1 */ +#define VECNUM_IIC0 2 /* IIC0 */ +#define VECNUM_IIC1 3 /* IIC1 */ +#define VECNUM_PIM 4 /* PCI inbound message */ +#define VECNUM_PCRW 5 /* PCI command reg write */ +#define VECNUM_PPM 6 /* PCI power management */ +#define VECNUM_MSI0 7 /* PCI MSI level 0 */ +#define VECNUM_MSI1 8 /* PCI MSI level 0 */ +#define VECNUM_MSI2 9 /* PCI MSI level 0 */ +#define VECNUM_MTE 10 /* MAL TXEOB */ +#define VECNUM_MRE 11 /* MAL RXEOB */ +#define VECNUM_D0 12 /* DMA channel 0 */ +#define VECNUM_D1 13 /* DMA channel 1 */ +#define VECNUM_D2 14 /* DMA channel 2 */ +#define VECNUM_D3 15 /* DMA channel 3 */ +#define VECNUM_CT0 18 /* GPT compare timer 0 */ +#define VECNUM_CT1 19 /* GPT compare timer 1 */ +#define VECNUM_CT2 20 /* GPT compare timer 2 */ +#define VECNUM_CT3 21 /* GPT compare timer 3 */ +#define VECNUM_CT4 22 /* GPT compare timer 4 */ +#define VECNUM_EIR0 23 /* External interrupt 0 */ +#define VECNUM_EIR1 24 /* External interrupt 1 */ +#define VECNUM_EIR2 25 /* External interrupt 2 */ +#define VECNUM_EIR3 26 /* External interrupt 3 */ +#define VECNUM_EIR4 27 /* External interrupt 4 */ +#define VECNUM_EIR5 28 /* External interrupt 5 */ +#define VECNUM_EIR6 29 /* External interrupt 6 */ +#define VECNUM_UIC1NC 30 /* UIC1 non-critical interrupt */ +#define VECNUM_UIC1C 31 /* UIC1 critical interrupt */ + +/* UIC 1 */ +#define VECNUM_MS (32 + 0 ) /* MAL SERR */ +#define VECNUM_TXDE (32 + 1 ) /* MAL TXDE */ +#define VECNUM_RXDE (32 + 2 ) /* MAL RXDE */ +#define VECNUM_ETH0 (32 + 28) /* Ethernet 0 interrupt status */ +#define VECNUM_EWU0 (32 + 29) /* Ethernet 0 wakeup */ + +#else /* !defined(CONFIG_440) */ + +#define VECNUM_U0 0 /* UART0 */ +#define VECNUM_U1 1 /* UART1 */ +#define VECNUM_D0 5 /* DMA channel 0 */ +#define VECNUM_D1 6 /* DMA channel 1 */ +#define VECNUM_D2 7 /* DMA channel 2 */ +#define VECNUM_D3 8 /* DMA channel 3 */ +#define VECNUM_EWU0 9 /* Ethernet wakeup */ +#define VECNUM_MS 10 /* MAL SERR */ +#define VECNUM_MTE 11 /* MAL TXEOB */ +#define VECNUM_MRE 12 /* MAL RXEOB */ +#define VECNUM_TXDE 13 /* MAL TXDE */ +#define VECNUM_RXDE 14 /* MAL RXDE */ +#define VECNUM_ETH0 15 /* Ethernet interrupt status */ +#define VECNUM_EIR0 25 /* External interrupt 0 */ +#define VECNUM_EIR1 26 /* External interrupt 1 */ +#define VECNUM_EIR2 27 /* External interrupt 2 */ +#define VECNUM_EIR3 28 /* External interrupt 3 */ +#define VECNUM_EIR4 29 /* External interrupt 4 */ +#define VECNUM_EIR5 30 /* External interrupt 5 */ +#define VECNUM_EIR6 31 /* External interrupt 6 */ + +#endif /* defined(CONFIG_440) */ + +#endif /* _VECNUMS_H_ */ diff --git a/cpu/sa1100/Makefile b/cpu/sa1100/Makefile new file mode 100644 index 0000000000..8c950daee8 --- /dev/null +++ b/cpu/sa1100/Makefile @@ -0,0 +1,43 @@ +# +# (C) Copyright 2000 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = lib$(CPU).a + +START = start.o +OBJS = serial.o interrupts.o cpu.o + +all: .depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) crv $@ $(OBJS) + +######################################################################### + +.depend: Makefile $(START:.o=.S) $(OBJS:.o=.c) + $(CC) -M $(CFLAGS) $(START:.o=.S) $(OBJS:.o=.c) > $@ + +sinclude .depend + +######################################################################### diff --git a/cpu/xscale/serial.c b/cpu/xscale/serial.c new file mode 100644 index 0000000000..35302a7761 --- /dev/null +++ b/cpu/xscale/serial.c @@ -0,0 +1,143 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Alex Zuepke + * + * Copyright (C) 1999 2000 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +void serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned int quot = 0; + + if (gd->baudrate == 1200) + quot = 192; + else if (gd->baudrate == 9600) + quot = 96; + else if (gd->baudrate == 19200) + quot = 48; + else if (gd->baudrate == 38400) + quot = 24; + else if (gd->baudrate == 57600) + quot = 16; + else if (gd->baudrate == 115200) + quot = 8; + else + hang (); + +#ifdef CONFIG_FFUART + + CKEN |= CKEN6_FFUART; + + FFIER = 0; /* Disable for now */ + FFFCR = 0; /* No fifos enabled */ + + /* set baud rate */ + FFLCR = LCR_WLS0 | LCR_WLS1 | LCR_DLAB; + FFDLL = quot & 0xff; + FFDLH = quot >> 8; + FFLCR = LCR_WLS0 | LCR_WLS1; + + FFIER = IER_UUE; /* Enable FFUART */ + +#elif CONFIG_STUART +#error "Bad: not implemented yet!" +#else +#error "Bad: you didn't configured serial ..." +#endif +} + + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + * + */ +int serial_init (void) +{ + serial_setbrg (); + + return (0); +} + + +/* + * Output a single byte to the serial port. + */ +void serial_putc (const char c) +{ +#ifdef CONFIG_FFUART + /* wait for room in the tx FIFO on FFUART */ + while ((FFLSR & LSR_TEMT) == 0); + + FFTHR = c; +#elif CONFIG_STUART +#endif + + /* If \n, also do \r */ + if (c == '\n') + serial_putc ('\r'); +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int serial_tstc (void) +{ +#ifdef CONFIG_FFUART + return FFLSR & LSR_DR; +#elif CONFIG_STUART +#endif +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int serial_getc (void) +{ +#ifdef CONFIG_FFUART + while (!(FFLSR & LSR_DR)); + + return (char) FFRBR & 0xff; +#elif CONFIG_STUART +#endif +} + +void +serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} diff --git a/cpu/xscale/start.S b/cpu/xscale/start.S new file mode 100644 index 0000000000..f1049a8f5b --- /dev/null +++ b/cpu/xscale/start.S @@ -0,0 +1,412 @@ +/* + * armboot - Startup Code for XScale + * + * Copyright (C) 1998 Dan Malek + * Copyright (C) 1999 Magnus Damm + * Copyright (C) 2000 Wolfgang Denk + * Copyright (c) 2001 Alex Züpke + * Copyright (c) 2002 Kyle Harris + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + + + +#include +#include + +.globl _start +_start: b reset + ldr pc, _undefined_instruction + ldr pc, _software_interrupt + ldr pc, _prefetch_abort + ldr pc, _data_abort + ldr pc, _not_used + ldr pc, _irq + ldr pc, _fiq + +_undefined_instruction: .word undefined_instruction +_software_interrupt: .word software_interrupt +_prefetch_abort: .word prefetch_abort +_data_abort: .word data_abort +_not_used: .word not_used +_irq: .word irq +_fiq: .word fiq + + .balignl 16,0xdeadbeef + + +/* + * Startup Code (reset vector) + * + * do important init only if we don't start from memory! + * - relocate armboot to ram + * - setup stack + * - jump to second stage + */ + +/* + * CFG_MEM_END is in the board dependent config-file (configs/config_BOARD.h) + */ +_TEXT_BASE: + .word TEXT_BASE + +.globl _armboot_start +_armboot_start: + .word _start + +/* + * Note: _armboot_end_data and _armboot_end are defined + * by the (board-dependent) linker script. + * _armboot_end_data is the first usable FLASH address after armboot + */ +.globl _armboot_end_data +_armboot_end_data: + .word armboot_end_data +.globl _armboot_end +_armboot_end: + .word armboot_end + +/* + * _armboot_real_end is the first usable RAM address behind armboot + * and the various stacks + */ +.globl _armboot_real_end +_armboot_real_end: + .word 0x0badc0de + +/* + * We relocate uboot to this address (end of RAM - 128 KiB) + */ +.globl _uboot_reloc +_uboot_reloc: + .word CFG_DRAM_BASE + CFG_DRAM_SIZE - CFG_MONITOR_LEN + +#ifdef CONFIG_USE_IRQ +/* IRQ stack memory (calculated at run-time) */ +.globl IRQ_STACK_START +IRQ_STACK_START: + .word 0x0badc0de + +/* IRQ stack memory (calculated at run-time) */ +.globl FIQ_STACK_START +FIQ_STACK_START: + .word 0x0badc0de +#endif + + +/****************************************************************************/ +/* */ +/* the actual reset code */ +/* */ +/****************************************************************************/ + +reset: + mrs r0,cpsr /* set the cpu to SVC32 mode */ + bic r0,r0,#0x1f /* (superviser mode, M=10011) */ + orr r0,r0,#0x13 + msr cpsr,r0 + + bl cpu_init_crit /* we do sys-critical inits */ + +relocate: /* relocate U-Boot to RAM */ + adr r0, _start /* r0 <- current position of code */ + ldr r2, _armboot_start + ldr r3, _armboot_end + sub r2, r3, r2 /* r2 <- size of armboot */ +/* ldr r1, _uboot_reloc / * r1 <- destination address */ + ldr r1, _TEXT_BASE + add r2, r0, r2 /* r2 <- source end address */ + +copy_loop: + ldmia r0!, {r3-r10} /* copy from source address [r0] */ + stmia r1!, {r3-r10} /* copy to target address [r1] */ + cmp r0, r2 /* until source end addreee [r2] */ + ble copy_loop + + /* Set up the stack */ + ldr r0, _uboot_reloc /* upper 128 KiB: relocated uboot */ + sub r0, r0, #CFG_MALLOC_LEN /* malloc area */ + /* FIXME: bdinfo should be here */ + sub sp, r0, #12 /* leave 3 words for abort-stack */ + + ldr pc, _start_armboot + +_start_armboot: .word start_armboot + + +/****************************************************************************/ +/* */ +/* CPU_init_critical registers */ +/* */ +/* - setup important registers */ +/* - setup memory timing */ +/* */ +/****************************************************************************/ + + /* Interrupt-Controller base address */ +IC_BASE: .word 0x40d00000 +#define ICMR 0x04 + +/* Reset-Controller */ +RST_BASE: .word 0x40f00030 +#define RCSR 0x00 + + + /* Clock Manager Registers */ +CC_BASE: .word 0x41300000 +#define CCCR 0x00 +cpuspeed: .word CFG_CPUSPEED + + /* RS: ??? */ + .macro CPWAIT + mrc p15,0,r0,c2,c0,0 + mov r0,r0 + sub pc,pc,#4 + .endm + + +cpu_init_crit: + + /* mask all IRQs */ + ldr r0, IC_BASE + mov r1, #0x00 + str r1, [r0, #ICMR] + + /* set clock speed */ + ldr r0, CC_BASE + ldr r1, cpuspeed + str r1, [r0, #CCCR] + + /* + * before relocating, we have to setup RAM timing + * because memory timing is board-dependend, you will + * find a memsetup.S in your board directory. + */ + mov ip, lr + bl memsetup + mov lr, ip + + /* Memory interfaces are working. Disable MMU and enable I-cache. */ + + ldr r0, =0x2001 /* enable access to all coproc. */ + mcr p15, 0, r0, c15, c1, 0 + CPWAIT + + mcr p15, 0, r0, c7, c10, 4 /* drain the write & fill buffers */ + CPWAIT + + mcr p15, 0, r0, c7, c7, 0 /* flush Icache, Dcache and BTB */ + CPWAIT + + mcr p15, 0, r0, c8, c7, 0 /* flush instuction and data TLBs */ + CPWAIT + + /* Enable the Icache */ +/* + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #0x1800 + mcr p15, 0, r0, c1, c0, 0 + CPWAIT +*/ + mov pc, lr + + +/****************************************************************************/ +/* */ +/* Interrupt handling */ +/* */ +/****************************************************************************/ + +/* IRQ stack frame */ + +#define S_FRAME_SIZE 72 + +#define S_OLD_R0 68 +#define S_PSR 64 +#define S_PC 60 +#define S_LR 56 +#define S_SP 52 + +#define S_IP 48 +#define S_FP 44 +#define S_R10 40 +#define S_R9 36 +#define S_R8 32 +#define S_R7 28 +#define S_R6 24 +#define S_R5 20 +#define S_R4 16 +#define S_R3 12 +#define S_R2 8 +#define S_R1 4 +#define S_R0 0 + +#define MODE_SVC 0x13 + + /* use bad_save_user_regs for abort/prefetch/undef/swi ... */ + + .macro bad_save_user_regs + sub sp, sp, #S_FRAME_SIZE + stmia sp, {r0 - r12} /* Calling r0-r12 */ + add r8, sp, #S_PC + + ldr r2, _armboot_end + add r2, r2, #CONFIG_STACKSIZE + sub r2, r2, #8 + ldmia r2, {r2 - r4} /* get pc, cpsr, old_r0 */ + add r0, sp, #S_FRAME_SIZE /* restore sp_SVC */ + + add r5, sp, #S_SP + mov r1, lr + stmia r5, {r0 - r4} /* save sp_SVC, lr_SVC, pc, cpsr, old_r */ + mov r0, sp + .endm + + + /* use irq_save_user_regs / irq_restore_user_regs for */ + /* IRQ/FIQ handling */ + + .macro irq_save_user_regs + sub sp, sp, #S_FRAME_SIZE + stmia sp, {r0 - r12} /* Calling r0-r12 */ + add r8, sp, #S_PC + stmdb r8, {sp, lr}^ /* Calling SP, LR */ + str lr, [r8, #0] /* Save calling PC */ + mrs r6, spsr + str r6, [r8, #4] /* Save CPSR */ + str r0, [r8, #8] /* Save OLD_R0 */ + mov r0, sp + .endm + + .macro irq_restore_user_regs + ldmia sp, {r0 - lr}^ @ Calling r0 - lr + mov r0, r0 + ldr lr, [sp, #S_PC] @ Get PC + add sp, sp, #S_FRAME_SIZE + subs pc, lr, #4 @ return & move spsr_svc into cpsr + .endm + + .macro get_bad_stack + ldr r13, _armboot_end @ setup our mode stack + add r13, r13, #CONFIG_STACKSIZE @ resides at top of normal stack + sub r13, r13, #8 + + str lr, [r13] @ save caller lr / spsr + mrs lr, spsr + str lr, [r13, #4] + + mov r13, #MODE_SVC @ prepare SVC-Mode + msr spsr_c, r13 + mov lr, pc + movs pc, lr + .endm + + .macro get_irq_stack @ setup IRQ stack + ldr sp, IRQ_STACK_START + .endm + + .macro get_fiq_stack @ setup FIQ stack + ldr sp, FIQ_STACK_START + .endm + + +/****************************************************************************/ +/* */ +/* exception handlers */ +/* */ +/****************************************************************************/ + + .align 5 +undefined_instruction: + get_bad_stack + bad_save_user_regs + bl do_undefined_instruction + + .align 5 +software_interrupt: + get_bad_stack + bad_save_user_regs + bl do_software_interrupt + + .align 5 +prefetch_abort: + get_bad_stack + bad_save_user_regs + bl do_prefetch_abort + + .align 5 +data_abort: + get_bad_stack + bad_save_user_regs + bl do_data_abort + + .align 5 +not_used: + get_bad_stack + bad_save_user_regs + bl do_not_used + +#ifdef CONFIG_USE_IRQ + + .align 5 +irq: + get_irq_stack + irq_save_user_regs + bl do_irq + irq_restore_user_regs + + .align 5 +fiq: + get_fiq_stack + irq_save_user_regs /* someone ought to write a more */ + bl do_fiq /* effiction fiq_save_user_regs */ + irq_restore_user_regs + +#else + + .align 5 +irq: + get_bad_stack + bad_save_user_regs + bl do_irq + + .align 5 +fiq: + get_bad_stack + bad_save_user_regs + bl do_fiq + +#endif + +/* + * FIXME How do we reset??? Watchdog timeout?? + */ + .align 5 +.globl reset_cpu +reset_cpu: + /* + ldr r0, RST_BASE + mov r1, #0x0 @ set bit 3-0 ... + str r1, [r0, #RCSR] @ ... to clear in RCSR + mov r1, #0x1 + str r1, [r0, #RCSR] @ and perform reset + */ + b reset_cpu @ silly, but repeat endlessly + -- cgit v1.2.1