diff options
Diffstat (limited to 'arch/powerpc/cpu/mpc8220')
22 files changed, 5848 insertions, 0 deletions
diff --git a/arch/powerpc/cpu/mpc8220/Makefile b/arch/powerpc/cpu/mpc8220/Makefile new file mode 100644 index 00000000000..b4fad286dcb --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/Makefile @@ -0,0 +1,50 @@ +# +# (C) Copyright 2003-2006 +# 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 = $(obj)lib$(CPU).a + +START = start.o +SOBJS = io.o fec_dma_tasks.o +COBJS = cpu.o cpu_init.o dramSetup.o fec.o i2c.o \ + interrupts.o loadtask.o speed.o \ + traps.o uart.o pci.o + +SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +START := $(addprefix $(obj),$(START)) + +all: $(obj).depend $(START) $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/powerpc/cpu/mpc8220/config.mk b/arch/powerpc/cpu/mpc8220/config.mk new file mode 100644 index 00000000000..e706883938d --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/config.mk @@ -0,0 +1,30 @@ +# +# (C) Copyright 2003 +# 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 +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_MPC8220 -ffixed-r2 \ + -mstring -mcpu=603e -mmultiple + +# Use default linker script. Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc8220/u-boot.lds diff --git a/arch/powerpc/cpu/mpc8220/cpu.c b/arch/powerpc/cpu/mpc8220/cpu.c new file mode 100644 index 00000000000..563cfe0536b --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/cpu.c @@ -0,0 +1,104 @@ +/* + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * CPU specific code for the MPC8220 CPUs + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc8220.h> +#include <netdev.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +int checkcpu (void) +{ + ulong clock = gd->cpu_clk; + char buf[32]; + + puts ("CPU: "); + + printf (CPU_ID_STR); + + printf (" (JTAG ID %08lx)", *(vu_long *) (CONFIG_SYS_MBAR + 0x50)); + + printf (" at %s MHz\n", strmhz (buf, clock)); + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + volatile gptmr8220_t *gptmr = (volatile gptmr8220_t *) MMAP_GPTMR; + ulong msr; + + /* Interrupts and MMU off */ + __asm__ __volatile__ ("mfmsr %0":"=r" (msr):); + + msr &= ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR); + __asm__ __volatile__ ("mtmsr %0"::"r" (msr)); + + /* Charge the watchdog timer */ + gptmr->Prescl = 10; + gptmr->Count = 1; + + gptmr->Mode = GPT_TMS_SGPIO; + + gptmr->Control = GPT_CTRL_WDEN | GPT_CTRL_CE; + + return 1; +} + +/* ------------------------------------------------------------------------- */ + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + * + */ +unsigned long get_tbclk (void) +{ + ulong tbclk; + + tbclk = (gd->bus_clk + 3L) / 4L; + + return (tbclk); +} + +/* ------------------------------------------------------------------------- */ + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(CONFIG_MPC8220_FEC) + mpc8220_fec_initialize(bis); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8220/cpu_init.c b/arch/powerpc/cpu/mpc8220/cpu_init.c new file mode 100644 index 00000000000..8f52c7dd0eb --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/cpu_init.c @@ -0,0 +1,136 @@ +/* + * (C) Copyright 2000-2003 + * 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 <common.h> +#include <mpc8220.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Breath some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers. + */ +void cpu_init_f (void) +{ + volatile flexbus8220_t *flexbus = (volatile flexbus8220_t *) MMAP_FB; + volatile pcfg8220_t *portcfg = (volatile pcfg8220_t *) MMAP_PCFG; + volatile xlbarb8220_t *xlbarb = (volatile xlbarb8220_t *) MMAP_XLBARB; + + /* Pointer is writable since we allocated a register for it */ + gd = (gd_t *) (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET); + + /* Clear initial global data */ + memset ((void *) gd, 0, sizeof (gd_t)); + + /* Clear all port configuration */ + portcfg->pcfg0 = 0; + portcfg->pcfg1 = 0; + portcfg->pcfg2 = 0; + portcfg->pcfg3 = 0; + portcfg->pcfg2 = CONFIG_SYS_GP1_PORT2_CONFIG; + portcfg->pcfg3 = CONFIG_SYS_PCI_PORT3_CONFIG | CONFIG_SYS_GP2_PORT3_CONFIG; + + /* + * Flexbus Controller: configure chip selects and enable them + */ +#if defined (CONFIG_SYS_CS0_BASE) + flexbus->csar0 = CONFIG_SYS_CS0_BASE; + +/* Sorcery-C can hang-up after CTRL reg initialization */ +#if defined (CONFIG_SYS_CS0_CTRL) + flexbus->cscr0 = CONFIG_SYS_CS0_CTRL; +#endif + flexbus->csmr0 = ((CONFIG_SYS_CS0_MASK - 1) & 0xffff0000) | 1; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS1_BASE) + flexbus->csar1 = CONFIG_SYS_CS1_BASE; + flexbus->cscr1 = CONFIG_SYS_CS1_CTRL; + flexbus->csmr1 = ((CONFIG_SYS_CS1_MASK - 1) & 0xffff0000) | 1; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS2_BASE) + flexbus->csar2 = CONFIG_SYS_CS2_BASE; + flexbus->cscr2 = CONFIG_SYS_CS2_CTRL; + flexbus->csmr2 = ((CONFIG_SYS_CS2_MASK - 1) & 0xffff0000) | 1; + portcfg->pcfg3 |= CONFIG_SYS_CS2_PORT3_CONFIG; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS3_BASE) + flexbus->csar3 = CONFIG_SYS_CS3_BASE; + flexbus->cscr3 = CONFIG_SYS_CS3_CTRL; + flexbus->csmr3 = ((CONFIG_SYS_CS3_MASK - 1) & 0xffff0000) | 1; + portcfg->pcfg3 |= CONFIG_SYS_CS3_PORT3_CONFIG; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS4_BASE) + flexbus->csar4 = CONFIG_SYS_CS4_BASE; + flexbus->cscr4 = CONFIG_SYS_CS4_CTRL; + flexbus->csmr4 = ((CONFIG_SYS_CS4_MASK - 1) & 0xffff0000) | 1; + portcfg->pcfg3 |= CONFIG_SYS_CS4_PORT3_CONFIG; + __asm__ volatile ("sync"); +#endif +#if defined (CONFIG_SYS_CS5_BASE) + flexbus->csar5 = CONFIG_SYS_CS5_BASE; + flexbus->cscr5 = CONFIG_SYS_CS5_CTRL; + flexbus->csmr5 = ((CONFIG_SYS_CS5_MASK - 1) & 0xffff0000) | 1; + portcfg->pcfg3 |= CONFIG_SYS_CS5_PORT3_CONFIG; + __asm__ volatile ("sync"); +#endif + + /* This section of the code cannot place in cpu_init_r(), + it will cause the system to hang */ + /* enable timebase */ + xlbarb->addrTenTimeOut = 0x1000; + xlbarb->dataTenTimeOut = 0x1000; + xlbarb->busActTimeOut = 0x2000; + + xlbarb->config = 0x00002000; + + /* Master Priority Enable */ + xlbarb->mastPriority = 0; + xlbarb->mastPriEn = 0xff; +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ + /* this may belongs to disable interrupt section */ + /* mask all interrupts */ + *(vu_long *) 0xf0000700 = 0xfffffc00; + *(vu_long *) 0xf0000714 |= 0x0001ffff; + *(vu_long *) 0xf0000710 &= ~0x00000f00; + + /* route critical ints to normal ints */ + *(vu_long *) 0xf0000710 |= 0x00000001; + +#if defined(CONFIG_CMD_NET) && defined(CONFIG_MPC8220_FEC) + /* load FEC microcode */ + loadtask (0, 2); +#endif + return (0); +} diff --git a/arch/powerpc/cpu/mpc8220/dma.h b/arch/powerpc/cpu/mpc8220/dma.h new file mode 100644 index 00000000000..d06ee631394 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/dma.h @@ -0,0 +1,68 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on code + * (C) Copyright Motorola, Inc., 2000 + * + * MPC8220 dma header file + */ + +#ifndef __MPC8220_DMA_H +#define __MPC8220_DMA_H + +#include <common.h> +#include <mpc8220.h> + +/* Task number assignment */ +#define FEC_RECV_TASK_NO 0 +#define FEC_XMIT_TASK_NO 1 + +/*--------------------------------------------------------------------- + * Stuff for Ethernet Tx/Rx tasks + *--------------------------------------------------------------------- + */ + +/* Layout of Ethernet controller Parameter SRAM area: + * ---------------------------------------------------------------- + * 0x00: TBD_BASE, base address of TX BD ring + * 0x04: TBD_NEXT, address of next TX BD to be processed + * 0x08: RBD_BASE, base address of RX BD ring + * 0x0C: RBD_NEXT, address of next RX BD to be processed + * --------------------------------------------------------------- + * ALL PARAMETERS ARE ALL LONGWORDS (FOUR BYTES EACH). + */ + +/* base address of SRAM area to store parameters used by Ethernet tasks */ +#define FEC_PARAM_BASE (MMAP_SRAM + 0x5b00) + +/* base address of SRAM area for buffer descriptors */ +#define FEC_BD_BASE (MMAP_SRAM + 0x5b20) + +/*--------------------------------------------------------------------- + * common shortcuts used by driver C code + *--------------------------------------------------------------------- + */ + +/* Disable SmartDMA task */ +#define DMA_TASK_DISABLE(tasknum) \ +{ \ + volatile ushort *tcr = (ushort *)(MMAP_DMA + 0x0000001c + 2 * tasknum); \ + *tcr = (*tcr) & (~0x8000); \ +} + +/* Enable SmartDMA task */ +#define DMA_TASK_ENABLE(tasknum) \ +{ \ + volatile ushort *tcr = (ushort *) (MMAP_DMA + 0x0000001c + 2 * tasknum);\ + *tcr = (*tcr) | 0x8000; \ +} + +/* Clear interrupt pending bits */ +#define DMA_CLEAR_IEVENT(tasknum) \ +{ \ + struct mpc8220_dma *dma = (struct mpc8220_dma *)MMAP_DMA; \ + dma->IntPend = (1 << tasknum); \ +} + +#endif /* __MPC8220_DMA_H */ diff --git a/arch/powerpc/cpu/mpc8220/dramSetup.c b/arch/powerpc/cpu/mpc8220/dramSetup.c new file mode 100644 index 00000000000..52cf1333f79 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/dramSetup.c @@ -0,0 +1,752 @@ +/* + * (C) Copyright 2004, Freescale, Inc + * TsiChung Liew, Tsi-Chung.Liew@freescale.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 + */ + +/* +DESCRIPTION +Read Dram spd and base on its information to calculate the memory size, +characteristics to initialize the dram on MPC8220 +*/ + +#include <common.h> +#include <mpc8220.h> +#include "i2cCore.h" +#include "dramSetup.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define SPD_SIZE CONFIG_SYS_SDRAM_SPD_SIZE +#define DRAM_SPD (CONFIG_SYS_SDRAM_SPD_I2C_ADDR)<<1 /* on Board SPD eeprom */ +#define TOTAL_BANK CONFIG_SYS_SDRAM_TOTAL_BANKS + +int spd_status (volatile i2c8220_t * pi2c, u8 sta_bit, u8 truefalse) +{ + int i; + + for (i = 0; i < I2C_POLL_COUNT; i++) { + if ((pi2c->sr & sta_bit) == (truefalse ? sta_bit : 0)) + return (OK); + } + + return (ERROR); +} + +int spd_clear (volatile i2c8220_t * pi2c) +{ + pi2c->adr = 0; + pi2c->fdr = 0; + pi2c->cr = 0; + pi2c->sr = 0; + + return (OK); +} + +int spd_stop (volatile i2c8220_t * pi2c) +{ + pi2c->cr &= ~I2C_CTL_STA; /* Generate stop signal */ + if (spd_status (pi2c, I2C_STA_BB, 0) != OK) + return ERROR; + + return (OK); +} + +int spd_readbyte (volatile i2c8220_t * pi2c, u8 * readb, int *index) +{ + pi2c->sr &= ~I2C_STA_IF; /* Clear Interrupt Bit */ + *readb = pi2c->dr; /* Read a byte */ + + /* + Set I2C_CTRL_TXAK will cause Transfer pending and + set I2C_CTRL_STA will cause Interrupt pending + */ + if (*index != 2) { + if (spd_status (pi2c, I2C_STA_CF, 1) != OK) /* Transfer not complete? */ + return ERROR; + } + + if (*index != 1) { + if (spd_status (pi2c, I2C_STA_IF, 1) != OK) + return ERROR; + } + + return (OK); +} + +int readSpdData (u8 * spdData) +{ + volatile i2c8220_t *pi2cReg; + volatile pcfg8220_t *pcfg; + u8 slvAdr = DRAM_SPD; + u8 Tmp; + int Length = SPD_SIZE; + int i = 0; + + /* Enable Port Configuration for SDA and SDL signals */ + pcfg = (volatile pcfg8220_t *) (MMAP_PCFG); + __asm__ ("sync"); + pcfg->pcfg3 &= ~CONFIG_SYS_I2C_PORT3_CONFIG; + __asm__ ("sync"); + + /* Points the structure to I2c mbar memory offset */ + pi2cReg = (volatile i2c8220_t *) (MMAP_I2C); + + + /* Clear FDR, ADR, SR and CR reg */ + pi2cReg->adr = 0; + pi2cReg->fdr = 0; + pi2cReg->cr = 0; + pi2cReg->sr = 0; + + /* Set for fix XLB Bus Frequency */ + switch (gd->bus_clk) { + case 60000000: + pi2cReg->fdr = 0x15; + break; + case 70000000: + pi2cReg->fdr = 0x16; + break; + case 80000000: + pi2cReg->fdr = 0x3a; + break; + case 90000000: + pi2cReg->fdr = 0x17; + break; + case 100000000: + pi2cReg->fdr = 0x3b; + break; + case 110000000: + pi2cReg->fdr = 0x18; + break; + case 120000000: + pi2cReg->fdr = 0x19; + break; + case 130000000: + pi2cReg->fdr = 0x1a; + break; + } + + pi2cReg->adr = CONFIG_SYS_I2C_SLAVE<<1; + + pi2cReg->cr = I2C_CTL_EN; /* Set Enable */ + + /* + The I2C bus should be in Idle state. If the bus is busy, + clear the STA bit in control register + */ + if (spd_status (pi2cReg, I2C_STA_BB, 0) != OK) { + if ((pi2cReg->cr & I2C_CTL_STA) == I2C_CTL_STA) + pi2cReg->cr &= ~I2C_CTL_STA; + + /* Check again if it is still busy, return error if found */ + if (spd_status (pi2cReg, I2C_STA_BB, 1) == OK) + return ERROR; + } + + pi2cReg->cr |= I2C_CTL_TX; /* Enable the I2c for TX, Ack */ + pi2cReg->cr |= I2C_CTL_STA; /* Generate start signal */ + + if (spd_status (pi2cReg, I2C_STA_BB, 1) != OK) + return ERROR; + + + /* Write slave address */ + pi2cReg->sr &= ~I2C_STA_IF; /* Clear Interrupt */ + pi2cReg->dr = slvAdr; /* Write a byte */ + + if (spd_status (pi2cReg, I2C_STA_CF, 1) != OK) { /* Transfer not complete? */ + spd_stop (pi2cReg); + return ERROR; + } + + if (spd_status (pi2cReg, I2C_STA_IF, 1) != OK) { + spd_stop (pi2cReg); + return ERROR; + } + + + /* Issue the offset to start */ + pi2cReg->sr &= ~I2C_STA_IF; /* Clear Interrupt */ + pi2cReg->dr = 0; /* Write a byte */ + + if (spd_status (pi2cReg, I2C_STA_CF, 1) != OK) { /* Transfer not complete? */ + spd_stop (pi2cReg); + return ERROR; + } + + if (spd_status (pi2cReg, I2C_STA_IF, 1) != OK) { + spd_stop (pi2cReg); + return ERROR; + } + + + /* Set repeat start */ + pi2cReg->cr |= I2C_CTL_RSTA; /* Repeat Start */ + + pi2cReg->sr &= ~I2C_STA_IF; /* Clear Interrupt */ + pi2cReg->dr = slvAdr | 1; /* Write a byte */ + + if (spd_status (pi2cReg, I2C_STA_CF, 1) != OK) { /* Transfer not complete? */ + spd_stop (pi2cReg); + return ERROR; + } + + if (spd_status (pi2cReg, I2C_STA_IF, 1) != OK) { + spd_stop (pi2cReg); + return ERROR; + } + + if (((pi2cReg->sr & 0x07) == 0x07) || (pi2cReg->sr & 0x01)) + return ERROR; + + pi2cReg->cr &= ~I2C_CTL_TX; /* Set receive mode */ + + if (((pi2cReg->sr & 0x07) == 0x07) || (pi2cReg->sr & 0x01)) + return ERROR; + + /* Dummy Read */ + if (spd_readbyte (pi2cReg, &Tmp, &i) != OK) { + spd_stop (pi2cReg); + return ERROR; + } + + i = 0; + while (Length) { + if (Length == 2) + pi2cReg->cr |= I2C_CTL_TXAK; + + if (Length == 1) + pi2cReg->cr &= ~I2C_CTL_STA; + + if (spd_readbyte (pi2cReg, spdData, &Length) != OK) { + return spd_stop (pi2cReg); + } + i++; + Length--; + spdData++; + } + + /* Stop the service */ + spd_stop (pi2cReg); + + return OK; +} + +int getBankInfo (int bank, draminfo_t * pBank) +{ + int status; + int checksum; + int count; + u8 spdData[SPD_SIZE]; + + + if (bank > 2 || pBank == 0) { + /* illegal values */ + return (-42); + } + + status = readSpdData (&spdData[0]); + if (status < 0) + return (-1); + + /* check the checksum */ + for (count = 0, checksum = 0; count < LOC_CHECKSUM; count++) + checksum += spdData[count]; + + checksum = checksum - ((checksum / 256) * 256); + + if (checksum != spdData[LOC_CHECKSUM]) + return (-2); + + /* Get the memory type */ + if (! + ((spdData[LOC_TYPE] == TYPE_DDR) + || (spdData[LOC_TYPE] == TYPE_SDR))) + /* not one of the types we support */ + return (-3); + + pBank->type = spdData[LOC_TYPE]; + + /* Set logical banks */ + pBank->banks = spdData[LOC_LOGICAL_BANKS]; + + /* Check that we have enough physical banks to cover the bank we are + * figuring out. Odd-numbered banks correspond to the second bank + * on the device. + */ + if (bank & 1) { + /* Second bank of a "device" */ + if (spdData[LOC_PHYS_BANKS] < 2) + /* this bank doesn't exist on the "device" */ + return (-4); + + if (spdData[LOC_ROWS] & 0xf0) + /* Two asymmetric banks */ + pBank->rows = spdData[LOC_ROWS] >> 4; + else + pBank->rows = spdData[LOC_ROWS]; + + if (spdData[LOC_COLS] & 0xf0) + /* Two asymmetric banks */ + pBank->cols = spdData[LOC_COLS] >> 4; + else + pBank->cols = spdData[LOC_COLS]; + } else { + /* First bank of a "device" */ + pBank->rows = spdData[LOC_ROWS]; + pBank->cols = spdData[LOC_COLS]; + } + + pBank->width = spdData[LOC_WIDTH_HIGH] << 8 | spdData[LOC_WIDTH_LOW]; + pBank->bursts = spdData[LOC_BURSTS]; + pBank->CAS = spdData[LOC_CAS]; + pBank->CS = spdData[LOC_CS]; + pBank->WE = spdData[LOC_WE]; + pBank->Trp = spdData[LOC_Trp]; + pBank->Trcd = spdData[LOC_Trcd]; + pBank->buffered = spdData[LOC_Buffered] & 1; + pBank->refresh = spdData[LOC_REFRESH]; + + return (0); +} + + +/* checkMuxSetting -- given a row/column device geometry, return a mask + * of the valid DRAM controller addr_mux settings for + * that geometry. + * + * Arguments: u8 rows: number of row addresses in this device + * u8 columns: number of column addresses in this device + * + * Returns: a mask of the allowed addr_mux settings for this + * geometry. Each bit in the mask represents a + * possible addr_mux settings (for example, the + * (1<<2) bit in the mask represents the 0b10 setting)/ + * + */ +u8 checkMuxSetting (u8 rows, u8 columns) +{ + muxdesc_t *pIdx, *pMux; + u8 mask; + int lrows, lcolumns; + u32 mux[4] = { 0x00080c04, 0x01080d03, 0x02080e02, 0xffffffff }; + + /* Setup MuxDescriptor in SRAM space */ + /* MUXDESC AddressRuns [] = { + { 0, 8, 12, 4 }, / setting, columns, rows, extra columns / + { 1, 8, 13, 3 }, / setting, columns, rows, extra columns / + { 2, 8, 14, 2 }, / setting, columns, rows, extra columns / + { 0xff } / list terminator / + }; */ + + pIdx = (muxdesc_t *) & mux[0]; + + /* Check rows x columns against each possible address mux setting */ + for (pMux = pIdx, mask = 0;; pMux++) { + lrows = rows; + lcolumns = columns; + + if (pMux->MuxValue == 0xff) + break; /* end of list */ + + /* For a given mux setting, since we want all the memory in a + * device to be contiguous, we want the device "use up" the + * address lines such that there are no extra column or row + * address lines on the device. + */ + + lcolumns -= pMux->Columns; + if (lcolumns < 0) + /* Not enough columns to get to the rows */ + continue; + + lrows -= pMux->Rows; + if (lrows > 0) + /* we have extra rows left -- can't do that! */ + continue; + + /* At this point, we either have to have used up all the + * rows or we have to have no columns left. + */ + + if (lcolumns != 0 && lrows != 0) + /* rows AND columns are left. Bad! */ + continue; + + lcolumns -= pMux->MoreColumns; + + if (lcolumns <= 0) + mask |= (1 << pMux->MuxValue); + } + + return (mask); +} + + +u32 dramSetup (void) +{ + draminfo_t DramInfo[TOTAL_BANK]; + draminfo_t *pDramInfo; + u32 size, temp, cfg_value, mode_value, refresh; + u8 *ptr; + u8 bursts, Trp, Trcd, type, buffered; + u8 muxmask, rows, columns; + int count, banknum; + u32 *prefresh, *pIdx; + u32 refrate[8] = { 15625, 3900, 7800, 31300, + 62500, 125000, 0xffffffff, 0xffffffff + }; + volatile sysconf8220_t *sysconf; + volatile memctl8220_t *memctl; + + sysconf = (volatile sysconf8220_t *) MMAP_MBAR; + memctl = (volatile memctl8220_t *) MMAP_MEMCTL; + + /* Set everything in the descriptions to zero */ + ptr = (u8 *) & DramInfo[0]; + for (count = 0; count < sizeof (DramInfo); count++) + *ptr++ = 0; + + for (banknum = 0; banknum < TOTAL_BANK; banknum++) + sysconf->cscfg[banknum]; + + /* Descriptions of row/column address muxing for various + * addr_mux settings. + */ + + pIdx = prefresh = (u32 *) & refrate[0]; + + /* Get all the info for all three logical banks */ + bursts = 0xff; + Trp = 0; + Trcd = 0; + type = 0; + buffered = 0xff; + refresh = 0xffffffff; + muxmask = 0xff; + + /* Two bank, CS0 and CS1 */ + for (banknum = 0, pDramInfo = &DramInfo[0]; + banknum < TOTAL_BANK; banknum++, pDramInfo++) { + pDramInfo->ordinal = banknum; /* initial sorting */ + if (getBankInfo (banknum, pDramInfo) < 0) + continue; + + /* get cumulative parameters of all three banks */ + if (type && pDramInfo->type != type) + return 0; + + type = pDramInfo->type; + rows = pDramInfo->rows; + columns = pDramInfo->cols; + + /* This chip only supports 13 DRAM memory lines, but some devices + * have 14 rows. To deal with this, ignore the 14th address line + * by limiting the number of rows (and columns) to 13. This will + * mean that for 14-row devices we will only be able to use + * half of the memory, but it's better than nothing. + */ + if (rows > 13) + rows = 13; + if (columns > 13) + columns = 13; + + pDramInfo->size = + ((1 << (rows + columns)) * pDramInfo->width); + pDramInfo->size *= pDramInfo->banks; + pDramInfo->size >>= 3; + + /* figure out which addr_mux configurations will support this device */ + muxmask &= checkMuxSetting (rows, columns); + if (muxmask == 0) + return 0; + + buffered = pDramInfo->buffered; + bursts &= pDramInfo->bursts; /* union of all bursts */ + if (pDramInfo->Trp > Trp) /* worst case (longest) Trp */ + Trp = pDramInfo->Trp; + + if (pDramInfo->Trcd > Trcd) /* worst case (longest) Trcd */ + Trcd = pDramInfo->Trcd; + + prefresh = pIdx; + /* worst case (shortest) Refresh period */ + if (refresh > prefresh[pDramInfo->refresh & 7]) + refresh = prefresh[pDramInfo->refresh & 7]; + + } /* for loop */ + + + /* We only allow a burst length of 8! */ + if (!(bursts & 8)) + bursts = 8; + + /* Sort the devices. In order to get each chip select region + * aligned properly, put the biggest device at the lowest address. + * A simple bubble sort will do the trick. + */ + for (banknum = 0, pDramInfo = &DramInfo[0]; + banknum < TOTAL_BANK; banknum++, pDramInfo++) { + int i; + + for (i = 0; i < TOTAL_BANK; i++) { + if (pDramInfo->size < DramInfo[i].size && + pDramInfo->ordinal < DramInfo[i].ordinal) { + /* If the current bank is smaller, but if the ordinal is also + * smaller, swap the ordinals + */ + u8 temp8; + + temp8 = DramInfo[i].ordinal; + DramInfo[i].ordinal = pDramInfo->ordinal; + pDramInfo->ordinal = temp8; + } + } + } + + + /* Now figure out the base address for each bank. While + * we're at it, figure out how much memory there is. + * + */ + size = 0; + for (banknum = 0; banknum < TOTAL_BANK; banknum++) { + int i; + + for (i = 0; i < TOTAL_BANK; i++) { + if (DramInfo[i].ordinal == banknum + && DramInfo[i].size != 0) { + DramInfo[i].base = size; + size += DramInfo[i].size; + } + } + } + + /* Set up the Drive Strength register */ + sysconf->sdramds = CONFIG_SYS_SDRAM_DRIVE_STRENGTH; + + /* ********************** Cfg 1 ************************* */ + + /* Set the single read to read/write/precharge delay */ + cfg_value = CFG1_SRD2RWP ((type == TYPE_DDR) ? 7 : 0xb); + + /* Set the single write to read/write/precharge delay. + * This may or may not be correct. The controller spec + * says "tWR", but "tWR" does not appear in the SPD. It + * always seems to be 15nsec for the class of device we're + * using, which turns out to be 2 clock cycles at 133MHz, + * so that's what we're going to use. + * + * HOWEVER, because of a bug in the controller, for DDR + * we need to set this to be the same as the value + * calculated for bwt2rwp. + */ + cfg_value |= CFG1_SWT2RWP ((type == TYPE_DDR) ? 7 : 2); + + /* Set the Read CAS latency. We're going to use a CL of + * 2.5 for DDR and 2 SDR. + */ + cfg_value |= CFG1_RLATENCY ((type == TYPE_DDR) ? 7 : 2); + + + /* Set the Active to Read/Write delay. This depends + * on Trcd which is reported as nanoseconds times 4. + * We want to calculate Trcd (in nanoseconds) times XLB clock (in Hz) + * which gives us a dimensionless quantity. Play games with + * the divisions so we don't run out of dynamic ranges. + */ + /* account for megaherz and the times 4 */ + temp = (Trcd * (gd->bus_clk / 1000000)) / 4; + + /* account for nanoseconds and round up, with a minimum value of 2 */ + temp = ((temp + 999) / 1000) - 1; + if (temp < 2) + temp = 2; + + cfg_value |= CFG1_ACT2WR (temp); + + /* Set the precharge to active delay. This depends + * on Trp which is reported as nanoseconds times 4. + * We want to calculate Trp (in nanoseconds) times XLB clock (in Hz) + * which gives us a dimensionless quantity. Play games with + * the divisions so we don't run out of dynamic ranges. + */ + /* account for megaherz and the times 4 */ + temp = (Trp * (gd->bus_clk / 1000000)) / 4; + + /* account for nanoseconds and round up, then subtract 1, with a + * minumum value of 1 and a maximum value of 7. + */ + temp = (((temp + 999) / 1000) - 1) & 7; + if (temp < 1) + temp = 1; + + cfg_value |= CFG1_PRE2ACT (temp); + + /* Set refresh to active delay. This depends + * on Trfc which is not reported in the SPD. + * We'll use a nominal value of 75nsec which is + * what the controller spec uses. + */ + temp = (75 * (gd->bus_clk / 1000000)); + /* account for nanoseconds and round up, then subtract 1 */ + cfg_value |= CFG1_REF2ACT (((temp + 999) / 1000) - 1); + + /* Set the write latency, using the values given in the controller spec */ + cfg_value |= CFG1_WLATENCY ((type == TYPE_DDR) ? 3 : 0); + memctl->cfg1 = cfg_value; /* cfg 1 */ + asm volatile ("sync"); + + + /* ********************** Cfg 2 ************************* */ + + /* Set the burst read to read/precharge delay */ + cfg_value = CFG2_BRD2RP ((type == TYPE_DDR) ? 5 : 8); + + /* Set the burst write to read/precharge delay. Semi-magic numbers + * based on the controller spec recommendations, assuming tWR is + * two clock cycles. + */ + cfg_value |= CFG2_BWT2RWP ((type == TYPE_DDR) ? 7 : 10); + + /* Set the Burst read to write delay. Semi-magic numbers + * based on the DRAM controller documentation. + */ + cfg_value |= CFG2_BRD2WT ((type == TYPE_DDR) ? 7 : 0xb); + + /* Set the burst length -- must be 8!! Well, 7, actually, becuase + * it's burst lenght minus 1. + */ + cfg_value |= CFG2_BURSTLEN (7); + memctl->cfg2 = cfg_value; /* cfg 2 */ + asm volatile ("sync"); + + + /* ********************** mode ************************* */ + + /* Set enable bit, CKE high/low bits, and the DDR/SDR mode bit, + * disable automatic refresh. + */ + cfg_value = CTL_MODE_ENABLE | CTL_CKE_HIGH | + ((type == TYPE_DDR) ? CTL_DDR_MODE : 0); + + /* Set the address mux based on whichever setting(s) is/are common + * to all the devices we have. If there is more than one, choose + * one arbitrarily. + */ + if (muxmask & 0x4) + cfg_value |= CTL_ADDRMUX (2); + else if (muxmask & 0x2) + cfg_value |= CTL_ADDRMUX (1); + else + cfg_value |= CTL_ADDRMUX (0); + + /* Set the refresh interval. */ + temp = ((refresh * (gd->bus_clk / 1000000)) / (1000 * 64)) - 1; + cfg_value |= CTL_REFRESH_INTERVAL (temp); + + /* Set buffered/non-buffered memory */ + if (buffered) + cfg_value |= CTL_BUFFERED; + + memctl->ctrl = cfg_value; /* ctrl */ + asm volatile ("sync"); + + if (type == TYPE_DDR) { + /* issue precharge all */ + temp = cfg_value | CTL_PRECHARGE_CMD; + memctl->ctrl = temp; /* ctrl */ + asm volatile ("sync"); + } + + + /* Set up mode value for CAS latency */ +#if (CONFIG_SYS_SDRAM_CAS_LATENCY==5) /* CL=2.5 */ + mode_value = (MODE_MODE | MODE_BURSTLEN (MODE_BURSTLEN_8) | + MODE_BT_SEQUENTIAL | MODE_CL (MODE_CL_2p5) | MODE_CMD); +#else + mode_value = (MODE_MODE | MODE_BURSTLEN (MODE_BURSTLEN_8) | + MODE_BT_SEQUENTIAL | MODE_CL (MODE_CL_2) | MODE_CMD); +#endif + asm volatile ("sync"); + + /* Write Extended Mode - enable DLL */ + if (type == TYPE_DDR) { + temp = MODE_EXTENDED | MODE_X_DLL_ENABLE | + MODE_X_DS_NORMAL | MODE_CMD; + memctl->mode = (temp >> 16); /* mode */ + asm volatile ("sync"); + + /* Write Mode - reset DLL, set CAS latency */ + temp = mode_value | MODE_OPMODE (MODE_OPMODE_RESETDLL); + memctl->mode = (temp >> 16); /* mode */ + asm volatile ("sync"); + } + + /* Program the chip selects. */ + for (banknum = 0; banknum < TOTAL_BANK; banknum++) { + if (DramInfo[banknum].size != 0) { + u32 mask; + int i; + + for (i = 0, mask = 1; i < 32; mask <<= 1, i++) { + if (DramInfo[banknum].size & mask) + break; + } + temp = (DramInfo[banknum].base & 0xfff00000) | (i - + 1); + + sysconf->cscfg[banknum] = temp; + asm volatile ("sync"); + } + } + + /* Wait for DLL lock */ + udelay (200); + + temp = cfg_value | CTL_PRECHARGE_CMD; /* issue precharge all */ + memctl->ctrl = temp; /* ctrl */ + asm volatile ("sync"); + + temp = cfg_value | CTL_REFRESH_CMD; /* issue precharge all */ + memctl->ctrl = temp; /* ctrl */ + asm volatile ("sync"); + + memctl->ctrl = temp; /* ctrl */ + asm volatile ("sync"); + + /* Write Mode - DLL normal */ + temp = mode_value | MODE_OPMODE (MODE_OPMODE_NORMAL); + memctl->mode = (temp >> 16); /* mode */ + asm volatile ("sync"); + + /* Enable refresh, enable DQS's (if DDR), and lock the control register */ + cfg_value &= ~CTL_MODE_ENABLE; /* lock register */ + cfg_value |= CTL_REFRESH_ENABLE; /* enable refresh */ + + if (type == TYPE_DDR) + cfg_value |= CTL_DQSOEN (0xf); /* enable DQS's for DDR */ + + memctl->ctrl = cfg_value; /* ctrl */ + asm volatile ("sync"); + + return size; +} diff --git a/arch/powerpc/cpu/mpc8220/dramSetup.h b/arch/powerpc/cpu/mpc8220/dramSetup.h new file mode 100644 index 00000000000..3b64e088cd8 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/dramSetup.h @@ -0,0 +1,108 @@ +/* + * dramSetup.h + * + * Prototypes, etc. for the Motorola MPC8220 + * embedded cpu chips + * + * 2004 (c) Freescale, Inc. + * Author: TsiChung Liew <Tsi-Chung.Liew@freescale.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 + */ +#ifndef __INCdramsetuph +#define __INCdramsetuph +#ifndef __ASSEMBLY__ +/* Where various things are in the SPD */ +#define LOC_TYPE 2 +#define LOC_CHECKSUM 63 +#define LOC_PHYS_BANKS 5 +#define LOC_LOGICAL_BANKS 17 +#define LOC_ROWS 3 +#define LOC_COLS 4 +#define LOC_WIDTH_HIGH 7 +#define LOC_WIDTH_LOW 6 +#define LOC_REFRESH 12 +#define LOC_BURSTS 16 +#define LOC_CAS 18 +#define LOC_CS 19 +#define LOC_WE 20 +#define LOC_Tcyc 9 +#define LOC_Tac 10 +#define LOC_Trp 27 +#define LOC_Trrd 28 +#define LOC_Trcd 29 +#define LOC_Tras 30 +#define LOC_Buffered 21 +/* Types of memory the SPD can tell us about. + * We can actually only use SDRAM and DDR. + */ +#define TYPE_DRAM 1 /* plain old dram */ +#define TYPE_EDO 2 /* EDO dram */ +#define TYPE_Nibble 3 /* serial nibble memory */ +#define TYPE_SDR 4 /* SDRAM */ +#define TYPE_ROM 5 /* */ +#define TYPE_SGRRAM 6 /* graphics memory */ +#define TYPE_DDR 7 /* DDR sdram */ +#define SDRAMDS_MASK 0x3 /* each field is 2 bits wide */ +#define SDRAMDS_SBE_SHIFT 8 /* Clock enable drive strength */ +#define SDRAMDS_SBC_SHIFT 6 /* Clocks drive strength */ +#define SDRAMDS_SBA_SHIFT 4 /* Address drive strength */ +#define SDRAMDS_SBS_SHIFT 2 /* SDR DQS drive strength */ +#define SDRAMDS_SBD_SHIFT 0 /* Data and DQS drive strength */ +#define DRIVE_STRENGTH_HIGH 0 +#define DRIVE_STRENGTH_MED 1 +#define DRIVE_STRENGTH_LOW 2 +#define DRIVE_STRENGTH_OFF 3 + +#define OK 0 +#define ERROR -1 +/* Structure to hold information about address muxing. */ + typedef struct tagMuxDescriptor { + u8 MuxValue; + u8 Columns; + u8 Rows; + u8 MoreColumns; +} muxdesc_t; + +/* Structure to define one physical bank of + * memory. Note that dram size in bytes is + * (2^^(rows+columns)) * width * banks / 8 +*/ +typedef struct tagDramInfo { + u32 size; /* size in bytes */ + u32 base; /* base address */ + u8 ordinal; /* where in the memory map will we put this */ + u8 type; + u8 rows; + u8 cols; + u16 width; /* width of each chip in bits */ + u8 banks; /* number of chips, aka logical banks */ + u8 bursts; /* bit-encoded allowable burst length */ + u8 CAS; /* bit-encoded CAS latency values */ + u8 CS; /* bit-encoded CS latency values */ + u8 WE; /* bit-encoded WE latency values */ + u8 Trp; /* bit-encoded row precharge time */ + u8 Trcd; /* bit-encoded RAS to CAS delay */ + u8 buffered; /* buffered or not */ + u8 refresh; /* encoded refresh rate */ +} draminfo_t; + +#endif /* __ASSEMBLY__ */ + +#endif /* __INCdramsetuph */ diff --git a/arch/powerpc/cpu/mpc8220/fec.c b/arch/powerpc/cpu/mpc8220/fec.c new file mode 100644 index 00000000000..992e0ffbc46 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/fec.c @@ -0,0 +1,1000 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.c, + * (C) Copyright Motorola, Inc., 2000 + */ + +#include <common.h> +#include <mpc8220.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> +#include "dma.h" +#include "fec.h" + +#undef DEBUG +#if defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI) && \ + defined(CONFIG_MPC8220_FEC) + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#ifdef DEBUG +static void tfifo_print (char *devname, mpc8220_fec_priv * fec); +static void rfifo_print (char *devname, mpc8220_fec_priv * fec); +#endif /* DEBUG */ + +#ifdef DEBUG +static u32 local_crc32 (char *string, unsigned int crc_value, int len); +#endif + +typedef struct { + u8 data[1500]; /* actual data */ + int length; /* actual length */ + int used; /* buffer in use or not */ + u8 head[16]; /* MAC header(6 + 6 + 2) + 2(aligned) */ +} NBUF; + +int fec8220_miiphy_read (char *devname, u8 phyAddr, u8 regAddr, u16 * retVal); +int fec8220_miiphy_write (char *devname, u8 phyAddr, u8 regAddr, u16 data); + +/********************************************************************/ +#ifdef DEBUG +static void mpc8220_fec_phydump (char *devname) +{ + u16 phyStatus, i; + u8 phyAddr = CONFIG_PHY_ADDR; + u8 reg_mask[] = { +#if CONFIG_PHY_TYPE == 0x79c874 /* AMD Am79C874 */ + /* regs to print: 0...7, 16...19, 21, 23, 24 */ + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, +#else + /* regs to print: 0...8, 16...20 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif + }; + + for (i = 0; i < 32; i++) { + if (reg_mask[i]) { + miiphy_read (devname, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x\n", i, phyStatus); + } + } +} +#endif + +/********************************************************************/ +static int mpc8220_fec_rbd_init (mpc8220_fec_priv * fec) +{ + int ix; + char *data; + static int once = 0; + + for (ix = 0; ix < FEC_RBD_NUM; ix++) { + if (!once) { + data = (char *) malloc (FEC_MAX_PKT_SIZE); + if (data == NULL) { + printf ("RBD INIT FAILED\n"); + return -1; + } + fec->rbdBase[ix].dataPointer = (u32) data; + } + fec->rbdBase[ix].status = FEC_RBD_EMPTY; + fec->rbdBase[ix].dataLength = 0; + } + once++; + + /* + * have the last RBD to close the ring + */ + fec->rbdBase[ix - 1].status |= FEC_RBD_WRAP; + fec->rbdIndex = 0; + + return 0; +} + +/********************************************************************/ +static void mpc8220_fec_tbd_init (mpc8220_fec_priv * fec) +{ + int ix; + + for (ix = 0; ix < FEC_TBD_NUM; ix++) { + fec->tbdBase[ix].status = 0; + } + + /* + * Have the last TBD to close the ring + */ + fec->tbdBase[ix - 1].status |= FEC_TBD_WRAP; + + /* + * Initialize some indices + */ + fec->tbdIndex = 0; + fec->usedTbdIndex = 0; + fec->cleanTbdNum = FEC_TBD_NUM; +} + +/********************************************************************/ +static void mpc8220_fec_rbd_clean (mpc8220_fec_priv * fec, FEC_RBD * pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) + pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); + else + pRbd->status = FEC_RBD_EMPTY; + + pRbd->dataLength = 0; + + /* + * Now, we have an empty RxBD, restart the SmartDMA receive task + */ + DMA_TASK_ENABLE (FEC_RECV_TASK_NO); + + /* + * Increment BD count + */ + fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; +} + +/********************************************************************/ +static void mpc8220_fec_tbd_scrub (mpc8220_fec_priv * fec) +{ + FEC_TBD *pUsedTbd; + +#ifdef DEBUG + printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", + fec->cleanTbdNum, fec->usedTbdIndex); +#endif + + /* + * process all the consumed TBDs + */ + while (fec->cleanTbdNum < FEC_TBD_NUM) { + pUsedTbd = &fec->tbdBase[fec->usedTbdIndex]; + if (pUsedTbd->status & FEC_TBD_READY) { +#ifdef DEBUG + printf ("Cannot clean TBD %d, in use\n", + fec->cleanTbdNum); +#endif + return; + } + + /* + * clean this buffer descriptor + */ + if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) + pUsedTbd->status = FEC_TBD_WRAP; + else + pUsedTbd->status = 0; + + /* + * update some indeces for a correct handling of the TBD ring + */ + fec->cleanTbdNum++; + fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; + } +} + +/********************************************************************/ +static void mpc8220_fec_set_hwaddr (mpc8220_fec_priv * fec, char *mac) +{ + u8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + u32 crc = 0xffffffff; /* initial value */ + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if (crc >= 32) { + fec->eth->iaddr1 = (1 << (crc - 32)); + fec->eth->iaddr2 = 0; + } else { + fec->eth->iaddr1 = 0; + fec->eth->iaddr2 = (1 << crc); + } + + /* + * Set physical address + */ + fec->eth->paddr1 = + (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + fec->eth->paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; +} + +/********************************************************************/ +static int mpc8220_fec_init (struct eth_device *dev, bd_t * bis) +{ + mpc8220_fec_priv *fec = (mpc8220_fec_priv *) dev->priv; + struct mpc8220_dma *dma = (struct mpc8220_dma *) MMAP_DMA; + const u8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + +#ifdef DEBUG + printf ("mpc8220_fec_init... Begin\n"); +#endif + + /* + * Initialize RxBD/TxBD rings + */ + mpc8220_fec_rbd_init (fec); + mpc8220_fec_tbd_init (fec); + + /* + * Set up Pin Muxing for FEC 1 + */ + *(vu_long *) MMAP_PCFG = 0; + *(vu_long *) (MMAP_PCFG + 4) = 0; + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + + /* + * Set FEC-Lite receive control register(R_CNTRL): + */ + if (fec->xcv_type == SEVENWIRE) { + /* + * Frame length=1518; 7-wire mode + */ + fec->eth->r_cntrl = 0x05ee0020; /*0x05ee0000;FIXME */ + } else { + /* + * Frame length=1518; MII mode; + */ + fec->eth->r_cntrl = 0x05ee0024; /*0x05ee0004;FIXME */ + } + + fec->eth->x_cntrl = 0x00000000; /* half-duplex, heartbeat disabled */ + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + /* tbd - rtm */ + /*fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); */ + /* No MII for 7-wire mode */ + fec->eth->mii_speed = 0x00000030; + } + + /* + * Set Opcode/Pause Duration Register + */ + fec->eth->op_pause = 0x00010020; /*FIXME0xffff0020; */ + + /* + * Set Rx FIFO alarm and granularity value + */ + fec->eth->rfifo_cntrl = 0x0c000000; + fec->eth->rfifo_alarm = 0x0000030c; +#ifdef DEBUG + if (fec->eth->rfifo_status & 0x00700000) { + printf ("mpc8220_fec_init() RFIFO error\n"); + } +#endif + + /* + * Set Tx FIFO granularity value + */ + /*fec->eth->tfifo_cntrl = 0x0c000000; */ /*tbd - rtm */ + fec->eth->tfifo_cntrl = 0x0e000000; +#ifdef DEBUG + printf ("tfifo_status: 0x%08x\n", fec->eth->tfifo_status); + printf ("tfifo_alarm: 0x%08x\n", fec->eth->tfifo_alarm); +#endif + + /* + * Set transmit fifo watermark register(X_WMRK), default = 64 + */ + fec->eth->tfifo_alarm = 0x00000080; + fec->eth->x_wmrk = 0x2; + + /* + * Set individual address filter for unicast address + * and set physical address registers. + */ + mpc8220_fec_set_hwaddr (fec, (char *)(dev->enetaddr)); + + /* + * Set multicast address filter + */ + fec->eth->gaddr1 = 0x00000000; + fec->eth->gaddr2 = 0x00000000; + + /* + * Turn ON cheater FSM: ???? + */ + fec->eth->xmit_fsm = 0x03000000; + +#if 1 +/*#if defined(CONFIG_MPC5200)*/ + /* + * Turn off COMM bus prefetch in the MGT5200 BestComm. It doesn't + * work w/ the current receive task. + */ + dma->PtdCntrl |= 0x00000001; +#endif + + /* + * Set priority of different initiators + */ + dma->IPR0 = 7; /* always */ + dma->IPR3 = 6; /* Eth RX */ + dma->IPR4 = 5; /* Eth Tx */ + + /* + * Clear SmartDMA task interrupt pending bits + */ + DMA_CLEAR_IEVENT (FEC_RECV_TASK_NO); + + /* + * Initialize SmartDMA parameters stored in SRAM + */ + *(int *) FEC_TBD_BASE = (int) fec->tbdBase; + *(int *) FEC_RBD_BASE = (int) fec->rbdBase; + *(int *) FEC_TBD_NEXT = (int) fec->tbdBase; + *(int *) FEC_RBD_NEXT = (int) fec->rbdBase; + + if (fec->xcv_type != SEVENWIRE) { + /* + * Initialize PHY(LXT971A): + * + * Generally, on power up, the LXT971A reads its configuration + * pins to check for forced operation, If not cofigured for + * forced operation, it uses auto-negotiation/parallel detection + * to automatically determine line operating conditions. + * If the PHY device on the other side of the link supports + * auto-negotiation, the LXT971A auto-negotiates with it + * using Fast Link Pulse(FLP) Bursts. If the PHY partner does not + * support auto-negotiation, the LXT971A automatically detects + * the presence of either link pulses(10Mbps PHY) or Idle + * symbols(100Mbps) and sets its operating conditions accordingly. + * + * When auto-negotiation is controlled by software, the following + * steps are recommended. + * + * Note: + * The physical address is dependent on hardware configuration. + * + */ + int timeout = 1; + u16 phyStatus; + + /* + * Reset PHY, then delay 300ns + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x8000); + udelay (1000); + + if (fec->xcv_type == MII10) { + /* + * Force 10Base-T, FDX operation + */ +#ifdef DEBUG + printf ("Forcing 10 Mbps ethernet link... "); +#endif + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); + /* + miiphy_write(fec, phyAddr, 0x0, 0x0100); + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x0180); + + timeout = 20; + do { /* wait for link status to go down */ + udelay (10000); + if ((timeout--) == 0) { +#ifdef DEBUG + printf ("hmmm, should not have waited..."); +#endif + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#ifdef DEBUG + printf ("="); +#endif + } while ((phyStatus & 0x0004)); /* !link up */ + + timeout = 1000; + do { /* wait for link status to come back up */ + udelay (10000); + if ((timeout--) == 0) { + printf ("failed. Link is down.\n"); + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#ifdef DEBUG + printf ("+"); +#endif + } while (!(phyStatus & 0x0004)); /* !link up */ + +#ifdef DEBUG + printf ("done.\n"); +#endif + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write (dev->name, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 5000; + do { + udelay (1000); + + if ((timeout--) == 0) { +#ifdef DEBUG + printf ("PHY auto neg 0 failed...\n"); +#endif + return -1; + } + + if (miiphy_read (dev->name, phyAddr, 0x1, &phyStatus) != + 0) { +#ifdef DEBUG + printf ("PHY auto neg 1 failed 0x%04x...\n", phyStatus); +#endif + return -1; + } + } while (!(phyStatus & 0x0004)); + +#ifdef DEBUG + printf ("PHY auto neg complete! \n"); +#endif + } + + } + + /* + * Enable FEC-Lite controller + */ + fec->eth->ecntrl |= 0x00000006; + +#ifdef DEBUG + if (fec->xcv_type != SEVENWIRE) + mpc8220_fec_phydump (dev->name); +#endif + + /* + * Enable SmartDMA receive task + */ + DMA_TASK_ENABLE (FEC_RECV_TASK_NO); + +#ifdef DEBUG + printf ("mpc8220_fec_init... Done \n"); +#endif + + return 1; +} + +/********************************************************************/ +static void mpc8220_fec_halt (struct eth_device *dev) +{ + mpc8220_fec_priv *fec = (mpc8220_fec_priv *) dev->priv; + int counter = 0xffff; + +#ifdef DEBUG + if (fec->xcv_type != SEVENWIRE) + mpc8220_fec_phydump (dev->name); +#endif + + /* + * mask FEC chip interrupts + */ + fec->eth->imask = 0; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + fec->eth->x_cntrl |= 0x00000001; + + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(fec->eth->ievent & 0x10000000))); + + /* + * Disable SmartDMA tasks + */ + DMA_TASK_DISABLE (FEC_XMIT_TASK_NO); + DMA_TASK_DISABLE (FEC_RECV_TASK_NO); + + /* + * Disable the Ethernet Controller + */ + fec->eth->ecntrl &= 0xfffffffd; + + /* + * Clear FIFO status registers + */ + fec->eth->rfifo_status &= 0x00700000; + fec->eth->tfifo_status &= 0x00700000; + + fec->eth->reset_cntrl = 0x01000000; + + /* + * Issue a reset command to the FEC chip + */ + fec->eth->ecntrl |= 0x1; + + /* + * wait at least 16 clock cycles + */ + udelay (10); + +#ifdef DEBUG + printf ("Ethernet task stopped\n"); +#endif +} + +#ifdef DEBUG +/********************************************************************/ + +static void tfifo_print (char *devname, mpc8220_fec_priv * fec) +{ + u16 phyAddr = CONFIG_PHY_ADDR; + u16 phyStatus; + + if ((fec->eth->tfifo_lrf_ptr != fec->eth->tfifo_lwf_ptr) + || (fec->eth->tfifo_rdptr != fec->eth->tfifo_wrptr)) { + + miiphy_read (devname, phyAddr, 0x1, &phyStatus); + printf ("\nphyStatus: 0x%04x\n", phyStatus); + printf ("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf ("ievent: 0x%08x\n", fec->eth->ievent); + printf ("x_status: 0x%08x\n", fec->eth->x_status); + printf ("tfifo: status 0x%08x\n", fec->eth->tfifo_status); + + printf (" control 0x%08x\n", fec->eth->tfifo_cntrl); + printf (" lrfp 0x%08x\n", fec->eth->tfifo_lrf_ptr); + printf (" lwfp 0x%08x\n", fec->eth->tfifo_lwf_ptr); + printf (" alarm 0x%08x\n", fec->eth->tfifo_alarm); + printf (" readptr 0x%08x\n", fec->eth->tfifo_rdptr); + printf (" writptr 0x%08x\n", fec->eth->tfifo_wrptr); + } +} + +static void rfifo_print (char *devname, mpc8220_fec_priv * fec) +{ + u16 phyAddr = CONFIG_PHY_ADDR; + u16 phyStatus; + + if ((fec->eth->rfifo_lrf_ptr != fec->eth->rfifo_lwf_ptr) + || (fec->eth->rfifo_rdptr != fec->eth->rfifo_wrptr)) { + + miiphy_read (devname, phyAddr, 0x1, &phyStatus); + printf ("\nphyStatus: 0x%04x\n", phyStatus); + printf ("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf ("ievent: 0x%08x\n", fec->eth->ievent); + printf ("x_status: 0x%08x\n", fec->eth->x_status); + printf ("rfifo: status 0x%08x\n", fec->eth->rfifo_status); + + printf (" control 0x%08x\n", fec->eth->rfifo_cntrl); + printf (" lrfp 0x%08x\n", fec->eth->rfifo_lrf_ptr); + printf (" lwfp 0x%08x\n", fec->eth->rfifo_lwf_ptr); + printf (" alarm 0x%08x\n", fec->eth->rfifo_alarm); + printf (" readptr 0x%08x\n", fec->eth->rfifo_rdptr); + printf (" writptr 0x%08x\n", fec->eth->rfifo_wrptr); + } +} +#endif /* DEBUG */ + +/********************************************************************/ + +static int mpc8220_fec_send (struct eth_device *dev, volatile void *eth_data, + int data_length) +{ + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + mpc8220_fec_priv *fec = (mpc8220_fec_priv *) dev->priv; + FEC_TBD *pTbd; + +#ifdef DEBUG + printf ("tbd status: 0x%04x\n", fec->tbdBase[0].status); + tfifo_print (dev->name, fec); +#endif + + /* + * Clear Tx BD ring at first + */ + mpc8220_fec_tbd_scrub (fec); + + /* + * Check for valid length of data. + */ + if ((data_length > 1500) || (data_length <= 0)) { + return -1; + } + + /* + * Check the number of vacant TxBDs. + */ + if (fec->cleanTbdNum < 1) { +#ifdef DEBUG + printf ("No available TxBDs ...\n"); +#endif + return -1; + } + + /* + * Get the first TxBD to send the mac header + */ + pTbd = &fec->tbdBase[fec->tbdIndex]; + pTbd->dataLength = data_length; + pTbd->dataPointer = (u32) eth_data; + pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; + +#ifdef DEBUG + printf ("DMA_TASK_ENABLE, fec->tbdIndex = %d \n", fec->tbdIndex); +#endif + + /* + * Kick the MII i/f + */ + if (fec->xcv_type != SEVENWIRE) { + u16 phyStatus; + + miiphy_read (dev->name, 0, 0x1, &phyStatus); + } + + /* + * Enable SmartDMA transmit task + */ + +#ifdef DEBUG + tfifo_print (dev->name, fec); +#endif + + DMA_TASK_ENABLE (FEC_XMIT_TASK_NO); + +#ifdef DEBUG + tfifo_print (dev->name, fec); +#endif + +#ifdef DEBUG + printf ("+"); +#endif + + fec->cleanTbdNum -= 1; + +#ifdef DEBUG + printf ("smartDMA ethernet Tx task enabled\n"); +#endif + /* + * wait until frame is sent . + */ + while (pTbd->status & FEC_TBD_READY) { + udelay (10); +#ifdef DEBUG + printf ("TDB status = %04x\n", pTbd->status); +#endif + } + + return 0; +} + + +/********************************************************************/ +static int mpc8220_fec_recv (struct eth_device *dev) +{ + /* + * This command pulls one frame from the card + */ + mpc8220_fec_priv *fec = (mpc8220_fec_priv *) dev->priv; + FEC_RBD *pRbd = &fec->rbdBase[fec->rbdIndex]; + unsigned long ievent; + int frame_length, len = 0; + NBUF *frame; + +#ifdef DEBUG + printf ("mpc8220_fec_recv %d Start...\n", fec->rbdIndex); + printf ("-"); +#endif + + /* + * Check if any critical events have happened + */ + ievent = fec->eth->ievent; + fec->eth->ievent = ievent; + if (ievent & 0x20060000) { + /* BABT, Rx/Tx FIFO errors */ + mpc8220_fec_halt (dev); + mpc8220_fec_init (dev, NULL); + return 0; + } + if (ievent & 0x80000000) { + /* Heartbeat error */ + fec->eth->x_cntrl |= 0x00000001; + } + if (ievent & 0x10000000) { + /* Graceful stop complete */ + if (fec->eth->x_cntrl & 0x00000001) { + mpc8220_fec_halt (dev); + fec->eth->x_cntrl &= ~0x00000001; + mpc8220_fec_init (dev, NULL); + } + } + + if (!(pRbd->status & FEC_RBD_EMPTY)) { + if ((pRbd->status & FEC_RBD_LAST) + && !(pRbd->status & FEC_RBD_ERR) + && ((pRbd->dataLength - 4) > 14)) { + + /* + * Get buffer address and size + */ + frame = (NBUF *) pRbd->dataPointer; + frame_length = pRbd->dataLength - 4; + +#if (0) + { + int i; + + printf ("recv data hdr:"); + for (i = 0; i < 14; i++) + printf ("%x ", *(frame->head + i)); + printf ("\n"); + } +#endif + /* + * Fill the buffer and pass it to upper layers + */ +/* memcpy(buff, frame->head, 14); + memcpy(buff + 14, frame->data, frame_length);*/ + NetReceive ((volatile uchar *) pRbd->dataPointer, + frame_length); + len = frame_length; + } + /* + * Reset buffer descriptor as empty + */ + mpc8220_fec_rbd_clean (fec, pRbd); + } + DMA_CLEAR_IEVENT (FEC_RECV_TASK_NO); + return len; +} + + +/********************************************************************/ +int mpc8220_fec_initialize (bd_t * bis) +{ + mpc8220_fec_priv *fec; + +#ifdef CONFIG_HAS_ETH1 + mpc8220_fec_priv *fec2; +#endif + struct eth_device *dev; + char *tmp, *end; + char env_enetaddr[6]; + +#ifdef CONFIG_HAS_ETH1 + char env_enet1addr[6]; +#endif + int i; + + fec = (mpc8220_fec_priv *) malloc (sizeof (*fec)); + dev = (struct eth_device *) malloc (sizeof (*dev)); + memset (dev, 0, sizeof *dev); + + fec->eth = (ethernet_regs *) MMAP_FEC1; +#ifdef CONFIG_HAS_ETH1 + fec2 = (mpc8220_fec_priv *) malloc (sizeof (*fec)); + fec2->eth = (ethernet_regs *) MMAP_FEC2; +#endif + fec->tbdBase = (FEC_TBD *) FEC_BD_BASE; + fec->rbdBase = + (FEC_RBD *) (FEC_BD_BASE + FEC_TBD_NUM * sizeof (FEC_TBD)); + fec->xcv_type = MII100; + + dev->priv = (void *) fec; + dev->iobase = MMAP_FEC1; + dev->init = mpc8220_fec_init; + dev->halt = mpc8220_fec_halt; + dev->send = mpc8220_fec_send; + dev->recv = mpc8220_fec_recv; + + sprintf (dev->name, "FEC ETHERNET"); + eth_register (dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, + fec8220_miiphy_read, fec8220_miiphy_write); +#endif + + /* + * Try to set the mac address now. The fec mac address is + * a garbage after reset. When not using fec for booting + * the Linux fec driver will try to work with this garbage. + */ + tmp = getenv ("ethaddr"); + if (tmp) { + for (i = 0; i < 6; i++) { + env_enetaddr[i] = + tmp ? simple_strtoul (tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end + 1 : end; + } + mpc8220_fec_set_hwaddr (fec, env_enetaddr); + } +#ifdef CONFIG_HAS_ETH1 + tmp = getenv ("eth1addr"); + if (tmp) { + for (i = 0; i < 6; i++) { + env_enet1addr[i] = + tmp ? simple_strtoul (tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end + 1 : end; + } + mpc8220_fec_set_hwaddr (fec2, env_enet1addr); + } +#endif + + return 1; +} + +/* MII-interface related functions */ +/********************************************************************/ +int fec8220_miiphy_read (char *devname, u8 phyAddr, u8 regAddr, u16 * retVal) +{ + ethernet_regs *eth = (ethernet_regs *) MMAP_FEC1; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = + (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | phy + | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))); + + if (timeout == 0) { +#ifdef DEBUG + printf ("Read MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear mii interrupt bit + */ + eth->ievent = 0x00800000; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (u16) eth->mii_data; + + return 0; +} + +/********************************************************************/ +int fec8220_miiphy_write (char *devname, u8 phyAddr, u8 regAddr, u16 data) +{ + ethernet_regs *eth = (ethernet_regs *) MMAP_FEC1; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))); + + if (timeout == 0) { +#ifdef DEBUG + printf ("Write MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear MII interrupt bit + */ + eth->ievent = 0x00800000; + + return 0; +} + +#ifdef DEBUG +static u32 local_crc32 (char *string, unsigned int crc_value, int len) +{ + int i; + char c; + unsigned int crc, count; + + /* + * crc32 algorithm + */ + /* + * crc = 0xffffffff; * The initialized value should be 0xffffffff + */ + crc = crc_value; + + for (i = len; --i >= 0;) { + c = *string++; + for (count = 0; count < 8; count++) { + if ((c & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + c >>= 1; + } + } + + /* + * In big endian system, do byte swaping for crc value + */ + return crc; +} +#endif /* DEBUG */ + +#endif /* CONFIG_MPC8220_FEC */ diff --git a/arch/powerpc/cpu/mpc8220/fec.h b/arch/powerpc/cpu/mpc8220/fec.h new file mode 100644 index 00000000000..a8927fcdbb2 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/fec.h @@ -0,0 +1,283 @@ +/* + * (C) Copyright 2003-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.h + * (C) Copyright Motorola, Inc., 2000 + * + * odin ethernet header file + */ + +#ifndef __MPC8220_FEC_H +#define __MPC8220_FEC_H + +#include <common.h> +#include <mpc8220.h> +#include "dma.h" + +typedef struct ethernet_register_set { + +/* [10:2]addr = 00 */ + +/* Control and status Registers (offset 000-1FF) */ + + volatile u32 fec_id; /* MBAR_ETH + 0x000 */ + volatile u32 ievent; /* MBAR_ETH + 0x004 */ + volatile u32 imask; /* MBAR_ETH + 0x008 */ + + volatile u32 RES0[1]; /* MBAR_ETH + 0x00C */ + volatile u32 r_des_active; /* MBAR_ETH + 0x010 */ + volatile u32 x_des_active; /* MBAR_ETH + 0x014 */ + volatile u32 r_des_active_cl; /* MBAR_ETH + 0x018 */ + volatile u32 x_des_active_cl; /* MBAR_ETH + 0x01C */ + volatile u32 ivent_set; /* MBAR_ETH + 0x020 */ + volatile u32 ecntrl; /* MBAR_ETH + 0x024 */ + + volatile u32 RES1[6]; /* MBAR_ETH + 0x028-03C */ + volatile u32 mii_data; /* MBAR_ETH + 0x040 */ + volatile u32 mii_speed; /* MBAR_ETH + 0x044 */ + volatile u32 mii_status; /* MBAR_ETH + 0x048 */ + + volatile u32 RES2[5]; /* MBAR_ETH + 0x04C-05C */ + volatile u32 mib_data; /* MBAR_ETH + 0x060 */ + volatile u32 mib_control; /* MBAR_ETH + 0x064 */ + + volatile u32 RES3[6]; /* MBAR_ETH + 0x068-7C */ + volatile u32 r_activate; /* MBAR_ETH + 0x080 */ + volatile u32 r_cntrl; /* MBAR_ETH + 0x084 */ + volatile u32 r_hash; /* MBAR_ETH + 0x088 */ + volatile u32 r_data; /* MBAR_ETH + 0x08C */ + volatile u32 ar_done; /* MBAR_ETH + 0x090 */ + volatile u32 r_test; /* MBAR_ETH + 0x094 */ + volatile u32 r_mib; /* MBAR_ETH + 0x098 */ + volatile u32 r_da_low; /* MBAR_ETH + 0x09C */ + volatile u32 r_da_high; /* MBAR_ETH + 0x0A0 */ + + volatile u32 RES4[7]; /* MBAR_ETH + 0x0A4-0BC */ + volatile u32 x_activate; /* MBAR_ETH + 0x0C0 */ + volatile u32 x_cntrl; /* MBAR_ETH + 0x0C4 */ + volatile u32 backoff; /* MBAR_ETH + 0x0C8 */ + volatile u32 x_data; /* MBAR_ETH + 0x0CC */ + volatile u32 x_status; /* MBAR_ETH + 0x0D0 */ + volatile u32 x_mib; /* MBAR_ETH + 0x0D4 */ + volatile u32 x_test; /* MBAR_ETH + 0x0D8 */ + volatile u32 fdxfc_da1; /* MBAR_ETH + 0x0DC */ + volatile u32 fdxfc_da2; /* MBAR_ETH + 0x0E0 */ + volatile u32 paddr1; /* MBAR_ETH + 0x0E4 */ + volatile u32 paddr2; /* MBAR_ETH + 0x0E8 */ + volatile u32 op_pause; /* MBAR_ETH + 0x0EC */ + + volatile u32 RES5[4]; /* MBAR_ETH + 0x0F0-0FC */ + volatile u32 instr_reg; /* MBAR_ETH + 0x100 */ + volatile u32 context_reg; /* MBAR_ETH + 0x104 */ + volatile u32 test_cntrl; /* MBAR_ETH + 0x108 */ + volatile u32 acc_reg; /* MBAR_ETH + 0x10C */ + volatile u32 ones; /* MBAR_ETH + 0x110 */ + volatile u32 zeros; /* MBAR_ETH + 0x114 */ + volatile u32 iaddr1; /* MBAR_ETH + 0x118 */ + volatile u32 iaddr2; /* MBAR_ETH + 0x11C */ + volatile u32 gaddr1; /* MBAR_ETH + 0x120 */ + volatile u32 gaddr2; /* MBAR_ETH + 0x124 */ + volatile u32 random; /* MBAR_ETH + 0x128 */ + volatile u32 rand1; /* MBAR_ETH + 0x12C */ + volatile u32 tmp; /* MBAR_ETH + 0x130 */ + + volatile u32 RES6[3]; /* MBAR_ETH + 0x134-13C */ + volatile u32 fifo_id; /* MBAR_ETH + 0x140 */ + volatile u32 x_wmrk; /* MBAR_ETH + 0x144 */ + volatile u32 fcntrl; /* MBAR_ETH + 0x148 */ + volatile u32 r_bound; /* MBAR_ETH + 0x14C */ + volatile u32 r_fstart; /* MBAR_ETH + 0x150 */ + volatile u32 r_count; /* MBAR_ETH + 0x154 */ + volatile u32 r_lag; /* MBAR_ETH + 0x158 */ + volatile u32 r_read; /* MBAR_ETH + 0x15C */ + volatile u32 r_write; /* MBAR_ETH + 0x160 */ + volatile u32 x_count; /* MBAR_ETH + 0x164 */ + volatile u32 x_lag; /* MBAR_ETH + 0x168 */ + volatile u32 x_retry; /* MBAR_ETH + 0x16C */ + volatile u32 x_write; /* MBAR_ETH + 0x170 */ + volatile u32 x_read; /* MBAR_ETH + 0x174 */ + + volatile u32 RES7[2]; /* MBAR_ETH + 0x178-17C */ + volatile u32 fm_cntrl; /* MBAR_ETH + 0x180 */ + volatile u32 rfifo_data; /* MBAR_ETH + 0x184 */ + volatile u32 rfifo_status; /* MBAR_ETH + 0x188 */ + volatile u32 rfifo_cntrl; /* MBAR_ETH + 0x18C */ + volatile u32 rfifo_lrf_ptr; /* MBAR_ETH + 0x190 */ + volatile u32 rfifo_lwf_ptr; /* MBAR_ETH + 0x194 */ + volatile u32 rfifo_alarm; /* MBAR_ETH + 0x198 */ + volatile u32 rfifo_rdptr; /* MBAR_ETH + 0x19C */ + volatile u32 rfifo_wrptr; /* MBAR_ETH + 0x1A0 */ + volatile u32 tfifo_data; /* MBAR_ETH + 0x1A4 */ + volatile u32 tfifo_status; /* MBAR_ETH + 0x1A8 */ + volatile u32 tfifo_cntrl; /* MBAR_ETH + 0x1AC */ + volatile u32 tfifo_lrf_ptr; /* MBAR_ETH + 0x1B0 */ + volatile u32 tfifo_lwf_ptr; /* MBAR_ETH + 0x1B4 */ + volatile u32 tfifo_alarm; /* MBAR_ETH + 0x1B8 */ + volatile u32 tfifo_rdptr; /* MBAR_ETH + 0x1BC */ + volatile u32 tfifo_wrptr; /* MBAR_ETH + 0x1C0 */ + + volatile u32 reset_cntrl; /* MBAR_ETH + 0x1C4 */ + volatile u32 xmit_fsm; /* MBAR_ETH + 0x1C8 */ + + volatile u32 RES8[3]; /* MBAR_ETH + 0x1CC-1D4 */ + volatile u32 rdes_data0; /* MBAR_ETH + 0x1D8 */ + volatile u32 rdes_data1; /* MBAR_ETH + 0x1DC */ + volatile u32 r_length; /* MBAR_ETH + 0x1E0 */ + volatile u32 x_length; /* MBAR_ETH + 0x1E4 */ + volatile u32 x_addr; /* MBAR_ETH + 0x1E8 */ + volatile u32 cdes_data; /* MBAR_ETH + 0x1EC */ + volatile u32 status; /* MBAR_ETH + 0x1F0 */ + volatile u32 dma_control; /* MBAR_ETH + 0x1F4 */ + volatile u32 des_cmnd; /* MBAR_ETH + 0x1F8 */ + volatile u32 data; /* MBAR_ETH + 0x1FC */ + + /* MIB COUNTERS (Offset 200-2FF) */ + + volatile u32 rmon_t_drop; /* MBAR_ETH + 0x200 */ + volatile u32 rmon_t_packets; /* MBAR_ETH + 0x204 */ + volatile u32 rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ + volatile u32 rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ + volatile u32 rmon_t_crc_align; /* MBAR_ETH + 0x210 */ + volatile u32 rmon_t_undersize; /* MBAR_ETH + 0x214 */ + volatile u32 rmon_t_oversize; /* MBAR_ETH + 0x218 */ + volatile u32 rmon_t_frag; /* MBAR_ETH + 0x21C */ + volatile u32 rmon_t_jab; /* MBAR_ETH + 0x220 */ + volatile u32 rmon_t_col; /* MBAR_ETH + 0x224 */ + volatile u32 rmon_t_p64; /* MBAR_ETH + 0x228 */ + volatile u32 rmon_t_p65to127; /* MBAR_ETH + 0x22C */ + volatile u32 rmon_t_p128to255; /* MBAR_ETH + 0x230 */ + volatile u32 rmon_t_p256to511; /* MBAR_ETH + 0x234 */ + volatile u32 rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ + volatile u32 rmon_t_p1024to2047;/* MBAR_ETH + 0x23C */ + volatile u32 rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ + volatile u32 rmon_t_octets; /* MBAR_ETH + 0x244 */ + volatile u32 ieee_t_drop; /* MBAR_ETH + 0x248 */ + volatile u32 ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ + volatile u32 ieee_t_1col; /* MBAR_ETH + 0x250 */ + volatile u32 ieee_t_mcol; /* MBAR_ETH + 0x254 */ + volatile u32 ieee_t_def; /* MBAR_ETH + 0x258 */ + volatile u32 ieee_t_lcol; /* MBAR_ETH + 0x25C */ + volatile u32 ieee_t_excol; /* MBAR_ETH + 0x260 */ + volatile u32 ieee_t_macerr; /* MBAR_ETH + 0x264 */ + volatile u32 ieee_t_cserr; /* MBAR_ETH + 0x268 */ + volatile u32 ieee_t_sqe; /* MBAR_ETH + 0x26C */ + volatile u32 t_fdxfc; /* MBAR_ETH + 0x270 */ + volatile u32 ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ + + volatile u32 RES9[2]; /* MBAR_ETH + 0x278-27C */ + volatile u32 rmon_r_drop; /* MBAR_ETH + 0x280 */ + volatile u32 rmon_r_packets; /* MBAR_ETH + 0x284 */ + volatile u32 rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ + volatile u32 rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ + volatile u32 rmon_r_crc_align; /* MBAR_ETH + 0x290 */ + volatile u32 rmon_r_undersize; /* MBAR_ETH + 0x294 */ + volatile u32 rmon_r_oversize; /* MBAR_ETH + 0x298 */ + volatile u32 rmon_r_frag; /* MBAR_ETH + 0x29C */ + volatile u32 rmon_r_jab; /* MBAR_ETH + 0x2A0 */ + + volatile u32 rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ + + volatile u32 rmon_r_p64; /* MBAR_ETH + 0x2A8 */ + volatile u32 rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ + volatile u32 rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ + volatile u32 rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ + volatile u32 rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ + volatile u32 rmon_r_p1024to2047;/* MBAR_ETH + 0x2BC */ + volatile u32 rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ + volatile u32 rmon_r_octets; /* MBAR_ETH + 0x2C4 */ + volatile u32 ieee_r_drop; /* MBAR_ETH + 0x2C8 */ + volatile u32 ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ + volatile u32 ieee_r_crc; /* MBAR_ETH + 0x2D0 */ + volatile u32 ieee_r_align; /* MBAR_ETH + 0x2D4 */ + volatile u32 r_macerr; /* MBAR_ETH + 0x2D8 */ + volatile u32 r_fdxfc; /* MBAR_ETH + 0x2DC */ + volatile u32 ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ + + volatile u32 RES10[6]; /* MBAR_ETH + 0x2E4-2FC */ + + volatile u32 RES11[64]; /* MBAR_ETH + 0x300-3FF */ +} ethernet_regs; + +/* Receive & Transmit Buffer Descriptor definitions */ +typedef struct BufferDescriptor { + u16 status; + u16 dataLength; + u32 dataPointer; +} FEC_RBD; + +typedef struct { + u16 status; + u16 dataLength; + u32 dataPointer; +} FEC_TBD; + +/* private structure */ +typedef enum { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +} xceiver_type; + +typedef struct { + ethernet_regs *eth; + xceiver_type xcv_type; /* transceiver type */ + FEC_RBD *rbdBase; /* RBD ring */ + FEC_TBD *tbdBase; /* TBD ring */ + u16 rbdIndex; /* next receive BD to read */ + u16 tbdIndex; /* next transmit BD to send */ + u16 usedTbdIndex; /* next transmit BD to clean */ + u16 cleanTbdNum; /* the number of available transmit BDs */ +} mpc8220_fec_priv; + +/* Ethernet parameter area */ +#define FEC_TBD_BASE (FEC_PARAM_BASE + 0x00) +#define FEC_TBD_NEXT (FEC_PARAM_BASE + 0x04) +#define FEC_RBD_BASE (FEC_PARAM_BASE + 0x08) +#define FEC_RBD_NEXT (FEC_PARAM_BASE + 0x0c) + +/* BD Numer definitions */ +#define FEC_TBD_NUM 48 /* The user can adjust this value */ +#define FEC_RBD_NUM 32 /* The user can adjust this value */ + +/* packet size limit */ +#define FEC_MAX_PKT_SIZE 1536 + +/* RBD bits definitions */ +#define FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_RBD_INT 0x1000 /* Interrupt */ +#define FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define FEC_RBD_LG 0x0020 /* Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define FEC_RBD_SH 0x0008 /* Short frame */ +#define FEC_RBD_CR 0x0004 /* CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* TBD bits definitions */ +#define FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_TBD_INT 0x1000 /* Interrupt */ +#define FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __MPC8220_FEC_H */ diff --git a/arch/powerpc/cpu/mpc8220/fec_dma_tasks.S b/arch/powerpc/cpu/mpc8220/fec_dma_tasks.S new file mode 100644 index 00000000000..3f8a03bf157 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/fec_dma_tasks.S @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2004, Freescale Semiconductor, Inc. + * + * This file contains microcode for the FEC controller of the MPC8220. + */ + +#include <config.h> + +#if defined(CONFIG_MPC8220) + +/* sas/sccg, gas target */ +.section smartdmaInitData,"aw",@progbits /* Initialized data for task variables */ +.section smartdmaTaskTable,"aw",@progbits /* Task tables */ +.align 9 +.globl taskTable +taskTable: +.globl scEthernetRecv_Entry +scEthernetRecv_Entry: /* Task 0 */ +.long scEthernetRecv_TDT - taskTable /* Task 0 Descriptor Table */ +.long scEthernetRecv_TDT - taskTable + 0x00000094 +.long scEthernetRecv_VarTab - taskTable /* Task 0 Variable Table */ +.long scEthernetRecv_FDT - taskTable + 0x03 /* Task 0 Function Descriptor Table & Flags */ +.long 0x00000000 +.long 0x00000000 +.long scEthernetRecv_CSave - taskTable /* Task 0 context save space */ +.long 0xf0000000 +.globl scEthernetXmit_Entry +scEthernetXmit_Entry: /* Task 1 */ +.long scEthernetXmit_TDT - taskTable /* Task 1 Descriptor Table */ +.long scEthernetXmit_TDT - taskTable + 0x000000e0 +.long scEthernetXmit_VarTab - taskTable /* Task 1 Variable Table */ +.long scEthernetXmit_FDT - taskTable + 0x03 /* Task 1 Function Descriptor Table & Flags */ +.long 0x00000000 +.long 0x00000000 +.long scEthernetXmit_CSave - taskTable /* Task 1 context save space */ +.long 0xf0000000 + + +.globl scEthernetRecv_TDT +scEthernetRecv_TDT: /* Task 0 Descriptor Table */ +.long 0xc4c50000 /* 0000(153): LCDEXT: idx0 = var9 + var10; idx0 once var0; idx0 += inc0 */ +.long 0x84c5e000 /* 0004(153): LCD: idx1 = var9 + var11; ; idx1 += inc0 */ +.long 0x10001f08 /* 0008(156): DRD1A: var7 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x10000380 /* 000C(157): DRD1A: var0 = *idx0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000f88 /* 0010(158): DRD1A: var3 = *idx1; FN=0 init=0 WS=0 RS=0 */ +.long 0x81980000 /* 0014(162): LCD: idx0 = var3; idx0 once var0; idx0 += inc0 */ +.long 0x10000780 /* 0018(164): DRD1A: var1 = *idx0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 001C(165): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x010cf04c /* 0020(165): DRD2B1: var4 = EU3(); EU3(var1,var12) */ +.long 0x82180349 /* 0024(169): LCD: idx0 = var4; idx0 != var13; idx0 += inc1 */ +.long 0x81c68004 /* 0028(172): LCD: idx1 = var3 + var13 + 4; idx1 once var0; idx1 += inc0 */ +.long 0x70000000 /* 002C(174): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x018cf04e /* 0030(174): DRD2B1: var6 = EU3(); EU3(var1,var14) */ +.long 0x70000000 /* 0034(175): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x020cf04f /* 0038(175): DRD2B1: var8 = EU3(); EU3(var1,var15) */ +.long 0x00000b88 /* 003C(176): DRD1A: var2 = *idx1; FN=0 init=0 WS=0 RS=0 */ +.long 0x80025184 /* 0040(205): LCDEXT: idx1 = 0xf0009184; ; */ +.long 0x86810412 /* 0044(205): LCD: idx2 = var13, idx3 = var2; idx2 < var16; idx2 += inc2, idx3 += inc2 */ +.long 0x0200cf88 /* 0048(209): DRD1A: *idx3 = *idx1; FN=0 init=16 WS=0 RS=0 */ +.long 0x80025184 /* 004C(217): LCDEXT: idx1 = 0xf0009184; ; */ +.long 0x8681845b /* 0050(217): LCD: idx2 = var13, idx3 = var3; idx2 < var17; idx2 += inc3, idx3 += inc3 */ +.long 0x0000cf88 /* 0054(221): DRD1A: *idx3 = *idx1; FN=0 init=0 WS=0 RS=0 */ +.long 0xc31883a4 /* 0058(225): LCDEXT: idx1 = var6; idx1 == var14; idx1 += inc4 */ +.long 0x80190000 /* 005C(225): LCD: idx2 = var0; idx2 once var0; idx2 += inc0 */ +.long 0x04008468 /* 0060(227): DRD1A: idx1 = var13; FN=0 INT init=0 WS=0 RS=0 */ +.long 0xc4038360 /* 0064(232): LCDEXT: idx1 = var8, idx2 = var7; idx1 == var13; idx1 += inc4, idx2 += inc0 */ +.long 0x81c50000 /* 0068(233): LCD: idx3 = var3 + var10; idx3 once var0; idx3 += inc0 */ +.long 0x1000cb18 /* 006C(235): DRD1A: *idx2 = idx3; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000f18 /* 0070(236): DRD1A: var3 = idx3; FN=0 init=0 WS=0 RS=0 */ +.long 0xc418836d /* 0074(238): LCDEXT: idx1 = var8; idx1 > var13; idx1 += inc5 */ +.long 0x83990000 /* 0078(238): LCD: idx2 = var7; idx2 once var0; idx2 += inc0 */ +.long 0x10000c00 /* 007C(240): DRD1A: var3 = var0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x0000c800 /* 0080(241): DRD1A: *idx2 = var0; FN=0 init=0 WS=0 RS=0 */ +.long 0x81988000 /* 0084(245): LCD: idx1 = var3; idx1 once var0; idx1 += inc0 */ +.long 0x10000788 /* 0088(247): DRD1A: var1 = *idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 008C(248): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x080cf04c /* 0090(248): DRD2B1: idx0 = EU3(); EU3(var1,var12) */ +.long 0x000001f8 /* 0094(:0): NOP */ + + +.globl scEthernetXmit_TDT +scEthernetXmit_TDT: /* Task 1 Descriptor Table */ +.long 0x80095b00 /* 0000(280): LCDEXT: idx0 = 0xf0025b00; ; */ +.long 0x85c60004 /* 0004(280): LCD: idx1 = var11 + var12 + 4; idx1 once var0; idx1 += inc0 */ +.long 0x10002308 /* 0008(283): DRD1A: var8 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x10000f88 /* 000C(284): DRD1A: var3 = *idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000380 /* 0010(285): DRD1A: var0 = *idx0; FN=0 init=0 WS=0 RS=0 */ +.long 0x81980000 /* 0014(288): LCD: idx0 = var3; idx0 once var0; idx0 += inc0 */ +.long 0x10000780 /* 0018(290): DRD1A: var1 = *idx0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 001C(291): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x024cf04d /* 0020(291): DRD2B1: var9 = EU3(); EU3(var1,var13) */ +.long 0x84980309 /* 0024(294): LCD: idx0 = var9; idx0 != var12; idx0 += inc1 */ +.long 0xc0004003 /* 0028(297): LCDEXT: idx1 = 0x00000003; ; */ +.long 0x81c60004 /* 002C(297): LCD: idx2 = var3 + var12 + 4; idx2 once var0; idx2 += inc0 */ +.long 0x70000000 /* 0030(299): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x010cf04e /* 0034(299): DRD2B1: var4 = EU3(); EU3(var1,var14) */ +.long 0x70000000 /* 0038(300): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x014cf04f /* 003C(300): DRD2B1: var5 = EU3(); EU3(var1,var15) */ +.long 0x70000000 /* 0040(301): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x028cf050 /* 0044(301): DRD2B1: var10 = EU3(); EU3(var1,var16) */ +.long 0x70000000 /* 0048(302): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT MORE init=0 WS=0 RS=0 */ +.long 0x018cf051 /* 004C(302): DRD2B1: var6 = EU3(); EU3(var1,var17) */ +.long 0x10000b90 /* 0050(303): DRD1A: var2 = *idx2; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 0054(304): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x01ccf0a1 /* 0058(304): DRD2B1: var7 = EU3(); EU3(var2,idx1) */ +.long 0xc2988312 /* 005C(308): LCDEXT: idx1 = var5; idx1 > var12; idx1 += inc2 */ +.long 0x83490000 /* 0060(308): LCD: idx2 = var6 + var18; idx2 once var0; idx2 += inc0 */ +.long 0x00001b10 /* 0064(310): DRD1A: var6 = idx2; FN=0 init=0 WS=0 RS=0 */ +.long 0x800251a4 /* 0068(315): LCDEXT: idx1 = 0xf00091a4; ; */ +.long 0xc30104dc /* 006C(315): LCDEXT: idx2 = var6, idx3 = var2; idx2 >= var19; idx2 += inc3, idx3 += inc4 */ +.long 0x839a032d /* 0070(316): LCD: idx4 = var7; idx4 == var12; idx4 += inc5 */ +.long 0x0220c798 /* 0074(321): DRD1A: *idx1 = *idx3; FN=0 init=17 WS=0 RS=0 */ +.long 0x800251a4 /* 0078(329): LCDEXT: idx1 = 0xf00091a4; ; */ +.long 0x99198337 /* 007C(329): LCD: idx2 = idx2, idx3 = idx3; idx2 > var12; idx2 += inc6, idx3 += inc7 */ +.long 0x022ac798 /* 0080(333): DRD1A: *idx1 = *idx3; FN=0 init=17 WS=1 RS=1 */ +.long 0x800251a4 /* 0084(350): LCDEXT: idx1 = 0xf00091a4; ; */ +.long 0xc1430000 /* 0088(350): LCDEXT: idx2 = var2 + var6; idx2 once var0; idx2 += inc0 */ +.long 0x82998312 /* 008C(351): LCD: idx3 = var5; idx3 > var12; idx3 += inc2 */ +.long 0x0a2ac790 /* 0090(354): DRD1A: *idx1 = *idx2; FN=0 TFD init=17 WS=1 RS=1 */ +.long 0x81988000 /* 0094(359): LCD: idx1 = var3; idx1 once var0; idx1 += inc0 */ +.long 0x60000002 /* 0098(361): DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=0 RS=0 */ +.long 0x0c4cfc4d /* 009C(361): DRD2B1: *idx1 = EU3(); EU3(*idx1,var13) */ +.long 0xc21883ad /* 00A0(365): LCDEXT: idx1 = var4; idx1 == var14; idx1 += inc5 */ +.long 0x80190000 /* 00A4(365): LCD: idx2 = var0; idx2 once var0; idx2 += inc0 */ +.long 0x04008460 /* 00A8(367): DRD1A: idx1 = var12; FN=0 INT init=0 WS=0 RS=0 */ +.long 0xc4052305 /* 00AC(371): LCDEXT: idx1 = var8, idx2 = var10; idx2 == var12; idx1 += inc0, idx2 += inc5 */ +.long 0x81ca0000 /* 00B0(372): LCD: idx3 = var3 + var20; idx3 once var0; idx3 += inc0 */ +.long 0x1000c718 /* 00B4(374): DRD1A: *idx1 = idx3; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00000f18 /* 00B8(375): DRD1A: var3 = idx3; FN=0 init=0 WS=0 RS=0 */ +.long 0xc4188000 /* 00BC(378): LCDEXT: idx1 = var8; idx1 once var0; idx1 += inc0 */ +.long 0x85190312 /* 00C0(378): LCD: idx2 = var10; idx2 > var12; idx2 += inc2 */ +.long 0x10000c00 /* 00C4(380): DRD1A: var3 = var0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x1000c400 /* 00C8(381): DRD1A: *idx1 = var0; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x00008860 /* 00CC(382): DRD1A: idx2 = var12; FN=0 init=0 WS=0 RS=0 */ +.long 0x81988000 /* 00D0(386): LCD: idx1 = var3; idx1 once var0; idx1 += inc0 */ +.long 0x10000788 /* 00D4(388): DRD1A: var1 = *idx1; FN=0 MORE init=0 WS=0 RS=0 */ +.long 0x60000000 /* 00D8(389): DRD2A: EU0=0 EU1=0 EU2=0 EU3=0 EXT init=0 WS=0 RS=0 */ +.long 0x080cf04d /* 00DC(389): DRD2B1: idx0 = EU3(); EU3(var1,var13) */ +.long 0x000001f8 /* 00E0(:0): NOP */ + +.align 8 + +.globl scEthernetRecv_VarTab +scEthernetRecv_VarTab: /* Task 0 Variable Table */ +.long 0x00000000 /* var[0] */ +.long 0x00000000 /* var[1] */ +.long 0x00000000 /* var[2] */ +.long 0x00000000 /* var[3] */ +.long 0x00000000 /* var[4] */ +.long 0x00000000 /* var[5] */ +.long 0x00000000 /* var[6] */ +.long 0x00000000 /* var[7] */ +.long 0x00000000 /* var[8] */ +.long 0xf0025b00 /* var[9] */ +.long 0x00000008 /* var[10] */ +.long 0x0000000c /* var[11] */ +.long 0x80000000 /* var[12] */ +.long 0x00000000 /* var[13] */ +.long 0x10000000 /* var[14] */ +.long 0x20000000 /* var[15] */ +.long 0x00000800 /* var[16] */ +.long 0x00000001 /* var[17] */ +.long 0x00000000 /* var[18] */ +.long 0x00000000 /* var[19] */ +.long 0x00000000 /* var[20] */ +.long 0x00000000 /* var[21] */ +.long 0x00000000 /* var[22] */ +.long 0x00000000 /* var[23] */ +.long 0x00000000 /* inc[0] */ +.long 0x60000000 /* inc[1] */ +.long 0x20000004 /* inc[2] */ +.long 0x20000001 /* inc[3] */ +.long 0x80000000 /* inc[4] */ +.long 0x40000000 /* inc[5] */ +.long 0x00000000 /* inc[6] */ +.long 0x00000000 /* inc[7] */ + +.align 8 + +.globl scEthernetXmit_VarTab +scEthernetXmit_VarTab: /* Task 1 Variable Table */ +.long 0x00000000 /* var[0] */ +.long 0x00000000 /* var[1] */ +.long 0x00000000 /* var[2] */ +.long 0x00000000 /* var[3] */ +.long 0x00000000 /* var[4] */ +.long 0x00000000 /* var[5] */ +.long 0x00000000 /* var[6] */ +.long 0x00000000 /* var[7] */ +.long 0x00000000 /* var[8] */ +.long 0x00000000 /* var[9] */ +.long 0x00000000 /* var[10] */ +.long 0xf0025b00 /* var[11] */ +.long 0x00000000 /* var[12] */ +.long 0x80000000 /* var[13] */ +.long 0x10000000 /* var[14] */ +.long 0x08000000 /* var[15] */ +.long 0x20000000 /* var[16] */ +.long 0x0000ffff /* var[17] */ +.long 0xffffffff /* var[18] */ +.long 0x00000004 /* var[19] */ +.long 0x00000008 /* var[20] */ +.long 0x00000000 /* var[21] */ +.long 0x00000000 /* var[22] */ +.long 0x00000000 /* var[23] */ +.long 0x00000000 /* inc[0] */ +.long 0x60000000 /* inc[1] */ +.long 0x40000000 /* inc[2] */ +.long 0xc000fffc /* inc[3] */ +.long 0xe0000004 /* inc[4] */ +.long 0x80000000 /* inc[5] */ +.long 0x4000ffff /* inc[6] */ +.long 0xe0000001 /* inc[7] */ + +.align 8 + +.globl scEthernetRecv_FDT +scEthernetRecv_FDT: /* Task 0 Function Descriptor Table */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x21800000 /* and(), EU# 3 */ +.long 0x21e00000 /* or(), EU# 3 */ +.long 0x21400000 /* andn(), EU# 3 */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 + +.align 8 + +.globl scEthernetXmit_FDT +scEthernetXmit_FDT: /* Task 1 Function Descriptor Table */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x21800000 /* and(), EU# 3 */ +.long 0x21e00000 /* or(), EU# 3 */ +.long 0x21400000 /* andn(), EU# 3 */ +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 +.long 0x00000000 + + +.globl scEthernetRecv_CSave +scEthernetRecv_CSave: /* Task 0 context save space */ +.space 128, 0x0 + + +.globl scEthernetXmit_CSave +scEthernetXmit_CSave: /* Task 1 context save space */ +.space 128, 0x0 + +#endif diff --git a/arch/powerpc/cpu/mpc8220/i2c.c b/arch/powerpc/cpu/mpc8220/i2c.c new file mode 100644 index 00000000000..76ecdf11e21 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/i2c.c @@ -0,0 +1,390 @@ +/* + * (C) Copyright 2004, Freescale, Inc + * TsiChung Liew, Tsi-Chung.Liew@freescale.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_HARD_I2C + +#include <mpc8220.h> +#include <i2c.h> + +typedef struct mpc8220_i2c { + volatile u32 adr; /* I2Cn + 0x00 */ + volatile u32 fdr; /* I2Cn + 0x04 */ + volatile u32 cr; /* I2Cn + 0x08 */ + volatile u32 sr; /* I2Cn + 0x0C */ + volatile u32 dr; /* I2Cn + 0x10 */ +} i2c_t; + +/* I2Cn control register bits */ +#define I2C_EN 0x80 +#define I2C_IEN 0x40 +#define I2C_STA 0x20 +#define I2C_TX 0x10 +#define I2C_TXAK 0x08 +#define I2C_RSTA 0x04 +#define I2C_INIT_MASK (I2C_EN | I2C_STA | I2C_TX | I2C_RSTA) + +/* I2Cn status register bits */ +#define I2C_CF 0x80 +#define I2C_AAS 0x40 +#define I2C_BB 0x20 +#define I2C_AL 0x10 +#define I2C_SRW 0x04 +#define I2C_IF 0x02 +#define I2C_RXAK 0x01 + +#define I2C_TIMEOUT 100 +#define I2C_RETRIES 1 + +struct mpc8220_i2c_tap { + int scl2tap; + int tap2tap; +}; + +static int mpc_reg_in (volatile u32 * reg); +static void mpc_reg_out (volatile u32 * reg, int val, int mask); +static int wait_for_bb (void); +static int wait_for_pin (int *status); +static int do_address (uchar chip, char rdwr_flag); +static int send_bytes (uchar chip, char *buf, int len); +static int receive_bytes (uchar chip, char *buf, int len); +static int mpc_get_fdr (int); + +static int mpc_reg_in (volatile u32 * reg) +{ + int ret; + ret = *reg >> 24; + __asm__ __volatile__ ("eieio"); + return ret; +} + +static void mpc_reg_out (volatile u32 * reg, int val, int mask) +{ + int tmp; + + if (!mask) { + *reg = val << 24; + } else { + tmp = mpc_reg_in (reg); + *reg = ((tmp & ~mask) | (val & mask)) << 24; + } + __asm__ __volatile__ ("eieio"); + + return; +} + +static int wait_for_bb (void) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int timeout = I2C_TIMEOUT; + int status; + + status = mpc_reg_in (®s->sr); + + while (timeout-- && (status & I2C_BB)) { +#if 1 + volatile int temp; + + mpc_reg_out (®s->cr, I2C_STA, I2C_STA); + temp = mpc_reg_in (®s->dr); + mpc_reg_out (®s->cr, 0, I2C_STA); + mpc_reg_out (®s->cr, 0, 0); + mpc_reg_out (®s->cr, I2C_EN, 0); +#endif + udelay (1000); + status = mpc_reg_in (®s->sr); + } + + return (status & I2C_BB); +} + +static int wait_for_pin (int *status) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int timeout = I2C_TIMEOUT; + + *status = mpc_reg_in (®s->sr); + + while (timeout-- && !(*status & I2C_IF)) { + udelay (1000); + *status = mpc_reg_in (®s->sr); + } + + if (!(*status & I2C_IF)) { + return -1; + } + + mpc_reg_out (®s->sr, 0, I2C_IF); + return 0; +} + +static int do_address (uchar chip, char rdwr_flag) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int status; + + chip <<= 1; + + if (rdwr_flag) + chip |= 1; + + mpc_reg_out (®s->cr, I2C_TX, I2C_TX); + mpc_reg_out (®s->dr, chip, 0); + + if (wait_for_pin (&status)) + return -2; + if (status & I2C_RXAK) + return -3; + return 0; +} + +static int send_bytes (uchar chip, char *buf, int len) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int wrcount; + int status; + + for (wrcount = 0; wrcount < len; ++wrcount) { + + mpc_reg_out (®s->dr, buf[wrcount], 0); + + if (wait_for_pin (&status)) + break; + + if (status & I2C_RXAK) + break; + + } + + return !(wrcount == len); + return 0; +} + +static int receive_bytes (uchar chip, char *buf, int len) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int dummy = 1; + int rdcount = 0; + int status; + int i; + + mpc_reg_out (®s->cr, 0, I2C_TX); + + for (i = 0; i < len; ++i) { + buf[rdcount] = mpc_reg_in (®s->dr); + + if (dummy) + dummy = 0; + else + rdcount++; + + if (wait_for_pin (&status)) + return -4; + } + + mpc_reg_out (®s->cr, I2C_TXAK, I2C_TXAK); + buf[rdcount++] = mpc_reg_in (®s->dr); + + if (wait_for_pin (&status)) + return -5; + + mpc_reg_out (®s->cr, 0, I2C_TXAK); + return 0; +} + +/**************** I2C API ****************/ + +void i2c_init (int speed, int saddr) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + + mpc_reg_out (®s->cr, 0, 0); + mpc_reg_out (®s->adr, saddr << 1, 0); + + /* Set clock + */ + mpc_reg_out (®s->fdr, mpc_get_fdr (speed), 0); + + /* Enable module + */ + mpc_reg_out (®s->cr, I2C_EN, I2C_INIT_MASK); + mpc_reg_out (®s->sr, 0, I2C_IF); + return; +} + +static int mpc_get_fdr (int speed) +{ + static int fdr = -1; + + if (fdr == -1) { + ulong best_speed = 0; + ulong divider; + ulong ipb, scl; + ulong bestmatch = 0xffffffffUL; + int best_i = 0, best_j = 0, i, j; + int SCL_Tap[] = { 9, 10, 12, 15, 5, 6, 7, 8 }; + struct mpc8220_i2c_tap scltap[] = { + {4, 1}, + {4, 2}, + {6, 4}, + {6, 8}, + {14, 16}, + {30, 32}, + {62, 64}, + {126, 128} + }; + + ipb = gd->bus_clk; + for (i = 7; i >= 0; i--) { + for (j = 7; j >= 0; j--) { + scl = 2 * (scltap[j].scl2tap + + (SCL_Tap[i] - + 1) * scltap[j].tap2tap + 2); + if (ipb <= speed * scl) { + if ((speed * scl - ipb) < bestmatch) { + bestmatch = speed * scl - ipb; + best_i = i; + best_j = j; + best_speed = ipb / scl; + } + } + } + } + divider = (best_i & 3) | ((best_i & 4) << 3) | (best_j << 2); + if (gd->flags & GD_FLG_RELOC) { + fdr = divider; + } else { + printf ("%ld kHz, ", best_speed / 1000); + return divider; + } + } + + return fdr; +} + +int i2c_probe (uchar chip) +{ + i2c_t *regs = (i2c_t *) MMAP_I2C; + int i; + + for (i = 0; i < I2C_RETRIES; i++) { + mpc_reg_out (®s->cr, I2C_STA, I2C_STA); + + if (!do_address (chip, 0)) { + mpc_reg_out (®s->cr, 0, I2C_STA); + break; + } + + mpc_reg_out (®s->cr, 0, I2C_STA); + udelay (50); + } + + return (i == I2C_RETRIES); +} + +int i2c_read (uchar chip, uint addr, int alen, uchar * buf, int len) +{ + uchar xaddr[4]; + i2c_t *regs = (i2c_t *) MMAP_I2C; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb ()) { + printf ("i2c_read: bus is busy\n"); + goto Done; + } + + mpc_reg_out (®s->cr, I2C_STA, I2C_STA); + if (do_address (chip, 0)) { + printf ("i2c_read: failed to address chip\n"); + goto Done; + } + + if (send_bytes (chip, (char *)&xaddr[4 - alen], alen)) { + printf ("i2c_read: send_bytes failed\n"); + goto Done; + } + + mpc_reg_out (®s->cr, I2C_RSTA, I2C_RSTA); + if (do_address (chip, 1)) { + printf ("i2c_read: failed to address chip\n"); + goto Done; + } + + if (receive_bytes (chip, (char *)buf, len)) { + printf ("i2c_read: receive_bytes failed\n"); + goto Done; + } + + ret = 0; + Done: + mpc_reg_out (®s->cr, 0, I2C_STA); + return ret; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buf, int len) +{ + uchar xaddr[4]; + i2c_t *regs = (i2c_t *) MMAP_I2C; + int ret = -1; + + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + + if (wait_for_bb ()) { + printf ("i2c_write: bus is busy\n"); + goto Done; + } + + mpc_reg_out (®s->cr, I2C_STA, I2C_STA); + if (do_address (chip, 0)) { + printf ("i2c_write: failed to address chip\n"); + goto Done; + } + + if (send_bytes (chip, (char *)&xaddr[4 - alen], alen)) { + printf ("i2c_write: send_bytes failed\n"); + goto Done; + } + + if (send_bytes (chip, (char *)buf, len)) { + printf ("i2c_write: send_bytes failed\n"); + goto Done; + } + + ret = 0; + Done: + mpc_reg_out (®s->cr, 0, I2C_STA); + return ret; +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/arch/powerpc/cpu/mpc8220/i2cCore.c b/arch/powerpc/cpu/mpc8220/i2cCore.c new file mode 100644 index 00000000000..b89ad034f49 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/i2cCore.c @@ -0,0 +1,627 @@ +/* I2cCore.c - MPC8220 PPC I2C Library */ + +/* Copyright 2004 Freescale Semiconductor, Inc. */ + +/* +modification history +-------------------- +01c,29jun04,tcl 1.3 removed CR. Added two bytes offset support. +01b,19jan04,tcl 1.2 removed i2cMsDelay and sysDecGet. renamed i2cMsDelay + back to sysMsDelay +01a,19jan04,tcl 1.1 created and seperated from i2c.c +*/ + +/* +DESCRIPTION +This file contain I2C low level handling library functions +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <vxWorks.h> +#include <sysLib.h> +#include <iosLib.h> +#include <logLib.h> +#include <tickLib.h> + +/* BSP Includes */ +#include "config.h" +#include "mpc8220.h" +#include "i2cCore.h" + +#ifdef DEBUG_I2CCORE +int I2CCDbg = 0; +#endif + +#define ABS(x) ((x < 0)? -x : x) + +char *I2CERR[16] = { + "Transfer in Progress\n", /* 0 */ + "Transfer complete\n", + "Not Addressed\n", /* 2 */ + "Addressed as a slave\n", + "Bus is Idle\n", /* 4 */ + "Bus is busy\n", + "Arbitration Lost\n", /* 6 */ + "Arbitration on Track\n", + "Slave receive, master writing to slave\n", /* 8 */ + "Slave transmit, master reading from slave\n", + "Interrupt is pending\n", /* 10 */ + "Interrupt complete\n", + "Acknowledge received\n", /* 12 */ + "No acknowledge received\n", + "Unknown status\n", /* 14 */ + "\n" +}; + +/****************************************************************************** + * + * chk_status - Check I2C status bit + * + * RETURNS: OK, or ERROR if the bit encounter + * + */ + +STATUS chk_status (PSI2C pi2c, UINT8 sta_bit, UINT8 truefalse) +{ + int i, status = 0; + + for (i = 0; i < I2C_POLL_COUNT; i++) { + if ((pi2c->sr & sta_bit) == (truefalse ? sta_bit : 0)) + return (OK); + } + + I2CCDBG (L2, ("--- sr %x stabit %x truefalse %d\n", + pi2c->sr, sta_bit, truefalse, 0, 0, 0)); + + if (i == I2C_POLL_COUNT) { + switch (sta_bit) { + case I2C_STA_CF: + status = 0; + break; + case I2C_STA_AAS: + status = 2; + break; + case I2C_STA_BB: + status = 4; + break; + case I2C_STA_AL: + status = 6; + break; + case I2C_STA_SRW: + status = 8; + break; + case I2C_STA_IF: + status = 10; + break; + case I2C_STA_RXAK: + status = 12; + break; + default: + status = 14; + break; + } + + if (!truefalse) + status++; + + I2CCDBG (NO, ("--- status %d\n", status, 0, 0, 0, 0, 0)); + I2CCDBG (NO, (I2CERR[status], 0, 0, 0, 0, 0, 0)); + } + + return (ERROR); +} + +/****************************************************************************** + * + * I2C Enable - Enable the I2C Controller + * + */ +STATUS i2c_enable (SI2C * pi2c, PI2CSET pi2cSet) +{ + int fdr = pi2cSet->bit_rate; + UINT8 adr = pi2cSet->i2c_adr; + + I2CCDBG (L2, ("i2c_enable fdr %d adr %x\n", fdr, adr, 0, 0, 0, 0)); + + i2c_clear (pi2c); /* Clear FDR, ADR, SR and CR reg */ + + SetI2cFDR (pi2c, fdr); /* Frequency */ + pi2c->adr = adr; + + pi2c->cr = I2C_CTL_EN; /* Set Enable */ + + /* + The I2C bus should be in Idle state. If the bus is busy, + clear the STA bit in control register + */ + if (chk_status (pi2c, I2C_STA_BB, 0) != OK) { + if ((pi2c->cr & I2C_CTL_STA) == I2C_CTL_STA) + pi2c->cr &= ~I2C_CTL_STA; + + /* Check again if it is still busy, return error if found */ + if (chk_status (pi2c, I2C_STA_BB, 1) == OK) + return ERROR; + } + + return (OK); +} + +/****************************************************************************** + * + * I2C Disable - Disable the I2C Controller + * + */ +STATUS i2c_disable (PSI2C pi2c) +{ + i2c_clear (pi2c); + + pi2c->cr &= I2C_CTL_EN; /* Disable I2c */ + + if ((pi2c->cr & I2C_CTL_STA) == I2C_CTL_STA) + pi2c->cr &= ~I2C_CTL_STA; + + if (chk_status (pi2c, I2C_STA_BB, 0) != OK) + return ERROR; + + return (OK); +} + +/****************************************************************************** + * + * I2C Clear - Clear the I2C Controller + * + */ +STATUS i2c_clear (PSI2C pi2c) +{ + pi2c->adr = 0; + pi2c->fdr = 0; + pi2c->cr = 0; + pi2c->sr = 0; + + return (OK); +} + + +STATUS i2c_start (PSI2C pi2c, PI2CSET pi2cSet) +{ +#ifdef TWOBYTES + UINT16 ByteOffset = pi2cSet->str_adr; +#else + UINT8 ByteOffset = pi2cSet->str_adr; +#endif +#if 1 + UINT8 tmp = 0; +#endif + UINT8 Addr = pi2cSet->slv_adr; + + pi2c->cr |= I2C_CTL_STA; /* Generate start signal */ + + if (chk_status (pi2c, I2C_STA_BB, 1) != OK) + return ERROR; + + /* Write slave address */ + if (i2c_writebyte (pi2c, &Addr) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#ifdef TWOBYTES +# if 0 + /* Issue the offset to start */ + if (i2c_write2byte (pi2c, &ByteOffset) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#endif + tmp = (ByteOffset >> 8) & 0xff; + if (i2c_writebyte (pi2c, &tmp) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } + tmp = ByteOffset & 0xff; + if (i2c_writebyte (pi2c, &tmp) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#else + if (i2c_writebyte (pi2c, &ByteOffset) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } +#endif + + return (OK); +} + +STATUS i2c_stop (PSI2C pi2c) +{ + pi2c->cr &= ~I2C_CTL_STA; /* Generate stop signal */ + if (chk_status (pi2c, I2C_STA_BB, 0) != OK) + return ERROR; + + return (OK); +} + +/****************************************************************************** + * + * Read Len bytes to the location pointed to by *Data from the device + * with address Addr. + */ +int i2c_readblock (SI2C * pi2c, PI2CSET pi2cSet, UINT8 * Data) +{ + int i = 0; + UINT8 Tmp; + +/* UINT8 ByteOffset = pi2cSet->str_adr; not used? */ + UINT8 Addr = pi2cSet->slv_adr; + int Length = pi2cSet->xfer_size; + + I2CCDBG (L1, ("i2c_readblock addr %x data 0x%08x len %d offset %d\n", + Addr, (int) Data, Length, ByteOffset, 0, 0)); + + if (pi2c->sr & I2C_STA_AL) { /* Check if Arbitration lost */ + I2CCDBG (FN, ("Arbitration lost\n", 0, 0, 0, 0, 0, 0)); + pi2c->sr &= ~I2C_STA_AL; /* Clear Arbitration status bit */ + return ERROR; + } + + pi2c->cr |= I2C_CTL_TX; /* Enable the I2c for TX, Ack */ + + if (i2c_start (pi2c, pi2cSet) == ERROR) + return ERROR; + + pi2c->cr |= I2C_CTL_RSTA; /* Repeat Start */ + + Tmp = Addr | 1; + + if (i2c_writebyte (pi2c, &Tmp) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } + + if (((pi2c->sr & 0x07) == 0x07) || (pi2c->sr & 0x01)) + return ERROR; + + pi2c->cr &= ~I2C_CTL_TX; /* Set receive mode */ + + if (((pi2c->sr & 0x07) == 0x07) || (pi2c->sr & 0x01)) + return ERROR; + + /* Dummy Read */ + if (i2c_readbyte (pi2c, &Tmp, &i) != OK) { + i2c_stop (pi2c); /* Disable I2c */ + return ERROR; + } + + i = 0; + while (Length) { + if (Length == 2) + pi2c->cr |= I2C_CTL_TXAK; + + if (Length == 1) + pi2c->cr &= ~I2C_CTL_STA; + + if (i2c_readbyte (pi2c, Data, &Length) != OK) { + return i2c_stop (pi2c); + } + i++; + Length--; + Data++; + } + + if (i2c_stop (pi2c) == ERROR) + return ERROR; + + return i; +} + +STATUS i2c_writeblock (SI2C * pi2c, PI2CSET pi2cSet, UINT8 * Data) +{ + int Length = pi2cSet->xfer_size; + +#ifdef TWOBYTES + UINT16 ByteOffset = pi2cSet->str_adr; +#else + UINT8 ByteOffset = pi2cSet->str_adr; +#endif + int j, k; + + I2CCDBG (L2, ("i2c_writeblock\n", 0, 0, 0, 0, 0, 0)); + + if (pi2c->sr & I2C_STA_AL) { + /* Check if arbitration lost */ + I2CCDBG (L2, ("Arbitration lost\n", 0, 0, 0, 0, 0, 0)); + pi2c->sr &= ~I2C_STA_AL; /* Clear the condition */ + return ERROR; + } + + pi2c->cr |= I2C_CTL_TX; /* Enable the I2c for TX, Ack */ + + /* Do the not even offset first */ + if ((ByteOffset % 8) != 0) { + int remain; + + if (Length > 8) { + remain = 8 - (ByteOffset % 8); + Length -= remain; + + pi2cSet->str_adr = ByteOffset; + + if (i2c_start (pi2c, pi2cSet) == ERROR) + return ERROR; + + for (j = ByteOffset; j < remain; j++) { + if (i2c_writebyte (pi2c, Data++) != OK) + return ERROR; + } + + if (i2c_stop (pi2c) == ERROR) + return ERROR; + + sysMsDelay (32); + + /* Update the new ByteOffset */ + ByteOffset += remain; + } + } + + for (j = ByteOffset, k = 0; j < (Length + ByteOffset); j++) { + if ((j % 8) == 0) { + pi2cSet->str_adr = j; + if (i2c_start (pi2c, pi2cSet) == ERROR) + return ERROR; + } + + k++; + + if (i2c_writebyte (pi2c, Data++) != OK) + return ERROR; + + if ((j == (Length - 1)) || ((k % 8) == 0)) { + if (i2c_stop (pi2c) == ERROR) + return ERROR; + + sysMsDelay (50); + } + + } + + return k; +} + +STATUS i2c_readbyte (SI2C * pi2c, UINT8 * readb, int *index) +{ + pi2c->sr &= ~I2C_STA_IF; /* Clear Interrupt Bit */ + *readb = pi2c->dr; /* Read a byte */ + + /* + Set I2C_CTRL_TXAK will cause Transfer pending and + set I2C_CTRL_STA will cause Interrupt pending + */ + if (*index != 2) { + if (chk_status (pi2c, I2C_STA_CF, 1) != OK) /* Transfer not complete? */ + return ERROR; + } + + if (*index != 1) { + if (chk_status (pi2c, I2C_STA_IF, 1) != OK) + return ERROR; + } + + return (OK); +} + + +STATUS i2c_writebyte (SI2C * pi2c, UINT8 * writeb) +{ + pi2c->sr &= ~I2C_STA_IF; /* Clear Interrupt */ + pi2c->dr = *writeb; /* Write a byte */ + + if (chk_status (pi2c, I2C_STA_CF, 1) != OK) /* Transfer not complete? */ + return ERROR; + + if (chk_status (pi2c, I2C_STA_IF, 1) != OK) + return ERROR; + + return OK; +} + +STATUS i2c_write2byte (SI2C * pi2c, UINT16 * writeb) +{ + UINT8 data; + + data = (UINT8) ((*writeb >> 8) & 0xff); + if (i2c_writebyte (pi2c, &data) != OK) + return ERROR; + data = (UINT8) (*writeb & 0xff); + if (i2c_writebyte (pi2c, &data) != OK) + return ERROR; + return OK; +} + +/* FDR table base on 33MHz - more detail please refer to Odini2c_dividers.xls +FDR FDR scl sda scl2tap2 +510 432 tap tap tap tap scl_per sda_hold I2C Freq 0 1 2 3 4 5 +000 000 9 3 4 1 28 Clocks 9 Clocks 1190 KHz 0 0 0 0 0 0 +000 001 9 3 4 2 44 Clocks 11 Clocks 758 KHz 0 0 1 0 0 0 +000 010 9 3 6 4 80 Clocks 17 Clocks 417 KHz 0 0 0 1 0 0 +000 011 9 3 6 8 144 Clocks 25 Clocks 231 KHz 0 0 1 1 0 0 +000 100 9 3 14 16 288 Clocks 49 Clocks 116 KHz 0 0 0 0 1 0 +000 101 9 3 30 32 576 Clocks 97 Clocks 58 KHz 0 0 1 0 1 0 +000 110 9 3 62 64 1152 Clocks 193 Clocks 29 KHz 0 0 0 1 1 0 +000 111 9 3 126 128 2304 Clocks 385 Clocks 14 KHz 0 0 1 1 1 0 +001 000 10 3 4 1 30 Clocks 9 Clocks 1111 KHz1 0 0 0 0 0 +001 001 10 3 4 2 48 Clocks 11 Clocks 694 KHz 1 0 1 0 0 0 +001 010 10 3 6 4 88 Clocks 17 Clocks 379 KHz 1 0 0 1 0 0 +001 011 10 3 6 8 160 Clocks 25 Clocks 208 KHz 1 0 1 1 0 0 +001 100 10 3 14 16 320 Clocks 49 Clocks 104 KHz 1 0 0 0 1 0 +001 101 10 3 30 32 640 Clocks 97 Clocks 52 KHz 1 0 1 0 1 0 +001 110 10 3 62 64 1280 Clocks 193 Clocks 26 KHz 1 0 0 1 1 0 +001 111 10 3 126 128 2560 Clocks 385 Clocks 13 KHz 1 0 1 1 1 0 +010 000 12 4 4 1 34 Clocks 10 Clocks 980 KHz 0 1 0 0 0 0 +010 001 12 4 4 2 56 Clocks 13 Clocks 595 KHz 0 1 1 0 0 0 +010 010 12 4 6 4 104 Clocks 21 Clocks 321 KHz 0 1 0 1 0 0 +010 011 12 4 6 8 192 Clocks 33 Clocks 174 KHz 0 1 1 1 0 0 +010 100 12 4 14 16 384 Clocks 65 Clocks 87 KHz 0 1 0 0 1 0 +010 101 12 4 30 32 768 Clocks 129 Clocks 43 KHz 0 1 1 0 1 0 +010 110 12 4 62 64 1536 Clocks 257 Clocks 22 KHz 0 1 0 1 1 0 +010 111 12 4 126 128 3072 Clocks 513 Clocks 11 KHz 0 1 1 1 1 0 +011 000 15 4 4 1 40 Clocks 10 Clocks 833 KHz 1 1 0 0 0 0 +011 001 15 4 4 2 68 Clocks 13 Clocks 490 KHz 1 1 1 0 0 0 +011 010 15 4 6 4 128 Clocks 21 Clocks 260 KHz 1 1 0 1 0 0 +011 011 15 4 6 8 240 Clocks 33 Clocks 139 KHz 1 1 1 1 0 0 +011 100 15 4 14 16 480 Clocks 65 Clocks 69 KHz 1 1 0 0 1 0 +011 101 15 4 30 32 960 Clocks 129 Clocks 35 KHz 1 1 1 0 1 0 +011 110 15 4 62 64 1920 Clocks 257 Clocks 17 KHz 1 1 0 1 1 0 +011 111 15 4 126 128 3840 Clocks 513 Clocks 9 KHz 1 1 1 1 1 0 +100 000 5 1 4 1 20 Clocks 7 Clocks 1667 KHz 0 0 0 0 0 1 +100 001 5 1 4 2 28 Clocks 7 Clocks 1190 KHz 0 0 1 0 0 1 +100 010 5 1 6 4 48 Clocks 9 Clocks 694 KHz 0 0 0 1 0 1 +100 011 5 1 6 8 80 Clocks 9 Clocks 417 KHz 0 0 1 1 0 1 +100 100 5 1 14 16 160 Clocks 17 Clocks 208 KHz 0 0 0 0 1 1 +100 101 5 1 30 32 320 Clocks 33 Clocks 104 KHz 0 0 1 0 1 1 +100 110 5 1 62 64 640 Clocks 65 Clocks 52 KHz 0 0 0 1 1 1 +100 111 5 1 126 128 1280 Clocks 129 Clocks 26 KHz 0 0 1 1 1 1 +101 000 6 1 4 1 22 Clocks 7 Clocks 1515 KHz 1 0 0 0 0 1 +101 001 6 1 4 2 32 Clocks 7 Clocks 1042 KHz 1 0 1 0 0 1 +101 010 6 1 6 4 56 Clocks 9 Clocks 595 KHz 1 0 0 1 0 1 +101 011 6 1 6 8 96 Clocks 9 Clocks 347 KHz 1 0 1 1 0 1 +101 100 6 1 14 16 192 Clocks 17 Clocks 174 KHz 1 0 0 0 1 1 +101 101 6 1 30 32 384 Clocks 33 Clocks 87 KHz 1 0 1 0 1 1 +101 110 6 1 62 64 768 Clocks 65 Clocks 43 KHz 1 0 0 1 1 1 +101 111 6 1 126 128 1536 Clocks 129 Clocks 22 KHz 1 0 1 1 1 1 +110 000 7 2 4 1 24 Clocks 8 Clocks 1389 KHz 0 1 0 0 0 1 +110 001 7 2 4 2 36 Clocks 9 Clocks 926 KHz 0 1 1 0 0 1 +110 010 7 2 6 4 64 Clocks 13 Clocks 521 KHz 0 1 0 1 0 1 +110 011 7 2 6 8 112 Clocks 17 Clocks 298 KHz 0 1 1 1 0 1 +110 100 7 2 14 16 224 Clocks 33 Clocks 149 KHz 0 1 0 0 1 1 +110 101 7 2 30 32 448 Clocks 65 Clocks 74 KHz 0 1 1 0 1 1 +110 110 7 2 62 64 896 Clocks 129 Clocks 37 KHz 0 1 0 1 1 1 +110 111 7 2 126 128 1792 Clocks 257 Clocks 19 KHz 0 1 1 1 1 1 +111 000 8 2 4 1 26 Clocks 8 Clocks 1282 KHz 1 1 0 0 0 1 +111 001 8 2 4 2 40 Clocks 9 Clocks 833 KHz 1 1 1 0 0 1 +111 010 8 2 6 4 72 Clocks 13 Clocks 463 KHz 1 1 0 1 0 1 +111 011 8 2 6 8 128 Clocks 17 Clocks 260 KHz 1 1 1 1 0 1 +111 100 8 2 14 16 256 Clocks 33 Clocks 130 KHz 1 1 0 0 1 1 +111 101 8 2 30 32 512 Clocks 65 Clocks 65 KHz 1 1 1 0 1 1 +111 110 8 2 62 64 1024 Clocks 129 Clocks 33 KHz 1 1 0 1 1 1 +111 111 8 2 126 128 2048 Clocks 257 Clocks 16 KHz 1 1 1 1 1 1 +*/ +STATUS SetI2cFDR (PSI2C pi2cRegs, int bitrate) +{ +/* Constants */ + const UINT8 div_hold[8][3] = { {9, 3}, {10, 3}, + {12, 4}, {15, 4}, + {5, 1}, {6, 1}, + {7, 2}, {8, 2} + }; + + const UINT8 scl_tap[8][2] = { {4, 1}, {4, 2}, + {6, 4}, {6, 8}, + {14, 16}, {30, 32}, + {62, 64}, {126, 128} + }; + + UINT8 mfdr_bits; + + int i = 0; + int j = 0; + + int Diff, min; + int WhichFreq, iRec, jRec; + int SCL_Period; + int SCL_Hold; + int I2C_Freq; + + I2CCDBG (L2, ("Entering getBitRate: bitrate %d pi2cRegs 0x%08x\n", + bitrate, (int) pi2cRegs, 0, 0, 0, 0)); + + if (bitrate < 0) { + I2CCDBG (NO, ("Invalid bitrate\n", 0, 0, 0, 0, 0, 0)); + return ERROR; + } + + /* Initialize */ + mfdr_bits = 0; + min = 0x7fffffff; + WhichFreq = iRec = jRec = 0; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + /* SCL Period = 2 * (scl2tap + [(SCL_Tap - 1) * tap2tap] + 2) + * SCL Hold = scl2tap + ((SDA_Tap - 1) * tap2tap) + 3 + * Bit Rate (I2C Freq) = System Freq / SCL Period + */ + SCL_Period = + 2 * (scl_tap[i][0] + + ((div_hold[j][0] - 1) * scl_tap[i][1]) + + 2); + + /* Now get the I2C Freq */ + I2C_Freq = DEV_CLOCK_FREQ / SCL_Period; + + /* Take equal or slower */ + if (I2C_Freq > bitrate) + continue; + + /* Take the differences */ + Diff = I2C_Freq - bitrate; + + Diff = ABS (Diff); + + /* Find the closer value */ + if (Diff < min) { + min = Diff; + WhichFreq = I2C_Freq; + iRec = i; + jRec = j; + } + + I2CCDBG (L2, + ("--- (%d,%d) I2C_Freq %d minDiff %d min %d\n", + i, j, I2C_Freq, Diff, min, 0)); + } + } + + SCL_Period = + 2 * (scl_tap[iRec][0] + + ((div_hold[jRec][0] - 1) * scl_tap[iRec][1]) + 2); + + I2CCDBG (L2, ("\nmin %d WhichFreq %d iRec %d jRec %d\n", + min, WhichFreq, iRec, jRec, 0, 0)); + I2CCDBG (L2, ("--- scl2tap %d SCL_Tap %d tap2tap %d\n", + scl_tap[iRec][0], div_hold[jRec][0], scl_tap[iRec][1], + 0, 0, 0)); + + /* This may no require */ + SCL_Hold = + scl_tap[iRec][0] + + ((div_hold[jRec][1] - 1) * scl_tap[iRec][1]) + 3; + I2CCDBG (L2, + ("--- SCL_Period %d SCL_Hold %d\n", SCL_Period, SCL_Hold, 0, + 0, 0, 0)); + + I2CCDBG (L2, ("--- mfdr_bits %x\n", mfdr_bits, 0, 0, 0, 0, 0)); + + /* FDR 4,3,2 */ + if ((iRec & 1) == 1) + mfdr_bits |= 0x04; /* FDR 2 */ + if ((iRec & 2) == 2) + mfdr_bits |= 0x08; /* FDR 3 */ + if ((iRec & 4) == 4) + mfdr_bits |= 0x10; /* FDR 4 */ + /* FDR 5,1,0 */ + if ((jRec & 1) == 1) + mfdr_bits |= 0x01; /* FDR 0 */ + if ((jRec & 2) == 2) + mfdr_bits |= 0x02; /* FDR 1 */ + if ((jRec & 4) == 4) + mfdr_bits |= 0x20; /* FDR 5 */ + + I2CCDBG (L2, ("--- mfdr_bits %x\n", mfdr_bits, 0, 0, 0, 0, 0)); + + pi2cRegs->fdr = mfdr_bits; + + return OK; +} diff --git a/arch/powerpc/cpu/mpc8220/i2cCore.h b/arch/powerpc/cpu/mpc8220/i2cCore.h new file mode 100644 index 00000000000..72783fd48b1 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/i2cCore.h @@ -0,0 +1,103 @@ +/* + * i2cCore.h + * + * Prototypes, etc. for the Motorola MPC8220 + * embedded cpu chips + * + * 2004 (c) Freescale, Inc. + * Author: TsiChung Liew <Tsi-Chung.Liew@freescale.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 + */ +#ifndef __INCi2ccoreh +#define __INCi2ccoreh +#ifndef __ASSEMBLY__ +/* device types */ +#define I2C_DEVICE_TYPE_EEPROM 0 +#define I2C_EEPROM_ADRS 0xa0 +#define I2C_CTRL_ADRS I2C_EEPROM_ADRS +#define EEPROM_ADDR0 0xA2 /* on Dimm SPD eeprom */ +#define EEPROM_ADDR1 0xA4 /* on Board SPD eeprom */ +#define EEPROM_ADDR2 0xD2 /* non-standard eeprom - clock generator */ +/* Control Register */ +#define I2C_CTL_EN 0x80 /* I2C Enable */ +#define I2C_CTL_IEN 0x40 /* I2C Interrupt Enable */ +#define I2C_CTL_STA 0x20 /* Master/Slave Mode select */ +#define I2C_CTL_TX 0x10 /* Transmit/Receive Mode Select */ +#define I2C_CTL_TXAK 0x08 /* Transmit Acknowledge Enable */ +#define I2C_CTL_RSTA 0x04 /* Repeat Start */ +/* Status Register */ +#define I2C_STA_CF 0x80 /* Data Transfer */ +#define I2C_STA_AAS 0x40 /* Adressed As Slave */ +#define I2C_STA_BB 0x20 /* Bus Busy */ +#define I2C_STA_AL 0x10 /* Arbitration Lost */ +#define I2C_STA_SRW 0x04 /* Slave Read/Write */ +#define I2C_STA_IF 0x02 /* I2C Interrupt */ +#define I2C_STA_RXAK 0x01 /* Receive Acknowledge */ +/* Interrupt Contol Register */ +#define I2C_INT_BNBE2 0x80 /* Bus Not Busy Enable 2 */ +#define I2C_INT_TE2 0x40 /* Transmit Enable 2 */ +#define I2C_INT_RE2 0x20 /* Receive Enable 2 */ +#define I2C_INT_IE2 0x10 /* Interrupt Enable 2 */ +#define I2C_INT_BNBE1 0x08 /* Bus Not Busy Enable 1 */ +#define I2C_INT_TE1 0x04 /* Transmit Enable 1 */ +#define I2C_INT_RE1 0x02 /* Receive Enable 1 */ +#define I2C_INT_IE1 0x01 /* Interrupt Enable 1 */ +#define I2C_POLL_COUNT 0x100000 +#define I2C_ENABLE 0x00000001 +#define I2C_DISABLE 0x00000002 +#define I2C_START 0x00000004 +#define I2C_REPSTART 0x00000008 +#define I2C_STOP 0x00000010 +#define I2C_BITRATE 0x00000020 +#define I2C_SLAVEADR 0x00000040 +#define I2C_STARTADR 0x00000080 +#undef TWOBYTES +typedef struct i2c_settings { + /* Device settings */ + int bit_rate; /* Device bit rate */ + u8 i2c_adr; /* I2C address */ + u8 slv_adr; /* Slave address */ +#ifdef TWOBYTES + u16 str_adr; /* Start address */ +#else + u8 str_adr; /* Start address */ +#endif + int xfer_size; /* Transfer Size */ + + int bI2c_en; /* Enable or Disable */ + int cmdFlag; /* I2c Command Flags */ +} i2cset_t; + +/* +int check_status(PSI2C pi2c, u8 sta_bit, u8 truefalse); +int i2c_enable(PSI2C pi2c, PI2CSET pi2cSet); +int i2c_disable(PSI2C pi2c); +int i2c_start(PSI2C pi2c, PI2CSET pi2cSet); +int i2c_stop(PSI2C pi2c); +int i2c_clear(PSI2C pi2c); +int i2c_readblock (PSI2C pi2c, PI2CSET pi2cSet, u8 *Data); +int i2c_writeblock (PSI2C pi2c, PI2CSET pi2cSet, u8 *Data); +int i2c_readbyte(PSI2C pi2c, u8 *readb, int *index); +int i2c_writebyte(PSI2C pi2c, u8 *writeb); +int SetI2cFDR( PSI2C pi2cRegs, int bitrate ); +*/ +#endif /* __ASSEMBLY__ */ + +#endif /* __INCi2ccoreh */ diff --git a/arch/powerpc/cpu/mpc8220/interrupts.c b/arch/powerpc/cpu/mpc8220/interrupts.c new file mode 100644 index 00000000000..78e99179c94 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/interrupts.c @@ -0,0 +1,80 @@ +/* + * (C) Copyright -2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 + * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * interrupts.c - just enough support for the decrementer/timer + */ + +#include <common.h> +#include <asm/processor.h> +#include <command.h> + +int interrupt_init_cpu (ulong * decrementer_count) +{ + *decrementer_count = get_tbclk () / CONFIG_SYS_HZ; + + return (0); +} + +/****************************************************************************/ + +/* + * Handle external interrupts + */ +void external_interrupt (struct pt_regs *regs) +{ + puts ("external_interrupt (oops!)\n"); +} + +void timer_interrupt_cpu (struct pt_regs *regs) +{ + /* nothing to do here */ + return; +} + +/****************************************************************************/ + +/* + * Install and free a interrupt handler. + */ + +void irq_install_handler (int vec, interrupt_handler_t * handler, void *arg) +{ + +} + +void irq_free_handler (int vec) +{ + +} + +/****************************************************************************/ + +void +do_irqinfo (cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[]) +{ + puts ("IRQ related functions are unimplemented currently.\n"); +} diff --git a/arch/powerpc/cpu/mpc8220/io.S b/arch/powerpc/cpu/mpc8220/io.S new file mode 100644 index 00000000000..5ecdf550a17 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/io.S @@ -0,0 +1,128 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * Copyright (C) 2003 Wolfgang Denk <wd@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <ppc_asm.tmpl> + +/* ------------------------------------------------------------------------------- */ +/* Function: in8 */ +/* Description: Input 8 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in8 +in8: + lbz r3,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in16 */ +/* Description: Input 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in16 +in16: + lhz r3,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in16r */ +/* Description: Input 16 bits and byte reverse */ +/* ------------------------------------------------------------------------------- */ + .globl in16r +in16r: + lhbrx r3,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in32 */ +/* Description: Input 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl in32 +in32: + lwz 3,0(3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: in32r */ +/* Description: Input 32 bits and byte reverse */ +/* ------------------------------------------------------------------------------- */ + .globl in32r +in32r: + lwbrx r3,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out8 */ +/* Description: Output 8 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out8 +out8: + stb r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out16 */ +/* Description: Output 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out16 +out16: + sth r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out16r */ +/* Description: Byte reverse and output 16 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out16r +out16r: + sthbrx r4,0,r3 + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out32 */ +/* Description: Output 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out32 +out32: + stw r4,0(r3) + sync + blr + +/* ------------------------------------------------------------------------------- */ +/* Function: out32r */ +/* Description: Byte reverse and output 32 bits */ +/* ------------------------------------------------------------------------------- */ + .globl out32r +out32r: + stwbrx r4,0,r3 + sync + blr diff --git a/arch/powerpc/cpu/mpc8220/loadtask.c b/arch/powerpc/cpu/mpc8220/loadtask.c new file mode 100644 index 00000000000..6d8b627e8c6 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/loadtask.c @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on code + * (C) Copyright Motorola, Inc., 2000 + */ + +#include <common.h> +#include <mpc8220.h> + +/* Multichannel DMA microcode */ +extern int taskTable; + +void loadtask (int basetask, int tasks) +{ + int *sram = (int *) (MMAP_SRAM + 512); + int *task_org = &taskTable; + unsigned int start, offset, end; + int i; + +#ifdef DEBUG + printf ("basetask = %d, tasks = %d\n", basetask, tasks); + printf ("task_org = 0x%08x\n", (unsigned int) task_org); +#endif + + /* setup TaskBAR register */ + *(vu_long *) MMAP_DMA = (MMAP_SRAM + 512); + + /* relocate task table entries */ + offset = (unsigned int) sram; + for (i = basetask; i < basetask + tasks; i++) { + sram[i * 8 + 0] = task_org[i * 8 + 0] + offset; + sram[i * 8 + 1] = task_org[i * 8 + 1] + offset; + sram[i * 8 + 2] = task_org[i * 8 + 2] + offset; + sram[i * 8 + 3] = task_org[i * 8 + 3] + offset; + sram[i * 8 + 4] = task_org[i * 8 + 4]; + sram[i * 8 + 5] = task_org[i * 8 + 5]; + sram[i * 8 + 6] = task_org[i * 8 + 6] + offset; + sram[i * 8 + 7] = task_org[i * 8 + 7]; + } + + /* relocate task descriptors */ + start = (sram[basetask * 8] - (unsigned int) sram); + end = (sram[(basetask + tasks - 1) * 8 + 1] - (unsigned int) sram); + +#ifdef DEBUG + printf ("TDT start = 0x%08x, end = 0x%08x\n", start, end); +#endif + + start /= 4; + end /= 4; + for (i = start; i <= end; i++) { + sram[i] = task_org[i]; + } + + /* relocate variables */ + start = (sram[basetask * 8 + 2] - (unsigned int) sram); + end = (sram[(basetask + tasks - 1) * 8 + 2] + 256 - + (unsigned int) sram); + start /= 4; + end /= 4; + for (i = start; i < end; i++) { + sram[i] = task_org[i]; + } + + /* relocate function decriptors */ + start = ((sram[basetask * 8 + 3] & 0xfffffffc) - (unsigned int) sram); + end = ((sram[(basetask + tasks - 1) * 8 + 3] & 0xfffffffc) + 256 - + (unsigned int) sram); + start /= 4; + end /= 4; + for (i = start; i < end; i++) { + sram[i] = task_org[i]; + } + + asm volatile ("sync"); +} diff --git a/arch/powerpc/cpu/mpc8220/pci.c b/arch/powerpc/cpu/mpc8220/pci.c new file mode 100644 index 00000000000..7ef43b72cd7 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/pci.c @@ -0,0 +1,191 @@ +/* + * Copyright 2004 Freescale Semiconductor. + * Copyright (C) 2003 Motorola Inc. + * Xianghua Xiao (x.xiao@motorola.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 + */ + +/* + * PCI Configuration space access support for MPC8220 PCI Bridge + */ +#include <common.h> +#include <mpc8220.h> +#include <pci.h> +#include <asm/io.h> + +#if defined(CONFIG_PCI) + +/* System RAM mapped over PCI */ +#define CONFIG_PCI_SYS_MEM_BUS CONFIG_SYS_SDRAM_BASE +#define CONFIG_PCI_SYS_MEM_PHYS CONFIG_SYS_SDRAM_BASE +#define CONFIG_PCI_SYS_MEM_SIZE (1024 * 1024 * 1024) + +#define cfg_read(val, addr, type, op) *val = op((type)(addr)); +#define cfg_write(val, addr, type, op) op((type *)(addr), (val)); + +#define PCI_OP(rw, size, type, op, mask) \ +int mpc8220_pci_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + u32 addr = 0; \ + u16 cfg_type = 0; \ + addr = ((offset & 0xfc) | cfg_type | (dev) | 0x80000000); \ + out_be32(hose->cfg_addr, addr); \ + __asm__ __volatile__("sync"); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + out_be32(hose->cfg_addr, addr & 0x7fffffff); \ + __asm__ __volatile__("sync"); \ + return 0; \ +} + +PCI_OP(read, byte, u8 *, in_8, 3) +PCI_OP(read, word, u16 *, in_le16, 2) +PCI_OP(write, byte, u8, out_8, 3) +PCI_OP(write, word, u16, out_le16, 2) +PCI_OP(write, dword, u32, out_le32, 0) + +int mpc8220_pci_read_config_dword(struct pci_controller *hose, pci_dev_t dev, + int offset, u32 *val) +{ + u32 addr; + u32 tmpv; + u32 mask = 2; /* word access */ + /* Read lower 16 bits */ + addr = ((offset & 0xfc) | (dev) | 0x80000000); + out_be32(hose->cfg_addr, addr); + __asm__ __volatile__("sync"); + *val = (u32) in_le16((u16 *) (hose->cfg_data + (offset & mask))); + out_be32(hose->cfg_addr, addr & 0x7fffffff); + __asm__ __volatile__("sync"); + + /* Read upper 16 bits */ + offset += 2; + addr = ((offset & 0xfc) | 1 | (dev) | 0x80000000); + out_be32(hose->cfg_addr, addr); + __asm__ __volatile__("sync"); + tmpv = (u32) in_le16((u16 *) (hose->cfg_data + (offset & mask))); + out_be32(hose->cfg_addr, addr & 0x7fffffff); + __asm__ __volatile__("sync"); + + /* combine results into dword value */ + *val = (tmpv << 16) | *val; + + return 0; +} + +void +pci_mpc8220_init(struct pci_controller *hose) +{ + u32 win0, win1, win2; + volatile mpc8220_xcpci_t *xcpci = + (volatile mpc8220_xcpci_t *) MMAP_XCPCI; + + volatile pcfg8220_t *portcfg = (volatile pcfg8220_t *) MMAP_PCFG; + + win0 = (u32) CONFIG_PCI_MEM_PHYS; + win1 = (u32) CONFIG_PCI_IO_PHYS; + win2 = (u32) CONFIG_PCI_CFG_PHYS; + + /* Assert PCI reset */ + out_be32 (&xcpci->glb_stat_ctl, PCI_GLB_STAT_CTRL_PR); + + /* Disable prefetching but read-multiples will still prefetch */ + out_be32 (&xcpci->target_ctrl, 0x00000000); + + /* Initiator windows */ + out_be32 (&xcpci->init_win0, (win0 >> 16) | win0 | 0x003f0000); + out_be32 (&xcpci->init_win1, ((win1 >> 16) | win1 )); + out_be32 (&xcpci->init_win2, ((win2 >> 16) | win2 )); + + out_be32 (&xcpci->init_win_cfg, + PCI_INIT_WIN_CFG_WIN0_CTRL_EN | + PCI_INIT_WIN_CFG_WIN1_CTRL_EN | PCI_INIT_WIN_CFG_WIN1_CTRL_IO | + PCI_INIT_WIN_CFG_WIN2_CTRL_EN | PCI_INIT_WIN_CFG_WIN2_CTRL_IO); + + out_be32 (&xcpci->init_ctrl, 0x00000000); + + /* Enable bus master and mem access */ + out_be32 (&xcpci->stat_cmd_reg, PCI_STAT_CMD_B | PCI_STAT_CMD_M); + + /* Cache line size and master latency */ + out_be32 (&xcpci->bist_htyp_lat_cshl, (0xf8 << PCI_CFG1_LT_SHIFT)); + + out_be32 (&xcpci->base0, PCI_BASE_ADDR_REG0); /* 256MB - MBAR space */ + out_be32 (&xcpci->base1, PCI_BASE_ADDR_REG1); /* 1GB - SDRAM space */ + + out_be32 (&xcpci->target_bar0, + PCI_TARGET_BASE_ADDR_REG0 | PCI_TARGET_BASE_ADDR_EN); + out_be32 (&xcpci->target_bar1, + PCI_TARGET_BASE_ADDR_REG1 | PCI_TARGET_BASE_ADDR_EN); + + /* Deassert reset bit */ + out_be32 (&xcpci->glb_stat_ctl, 0x00000000); + + /* Enable PCI bus master support */ + /* Set PCIGNT1, PCIREQ1, PCIREQ0/PCIGNTIN, PCIGNT0/PCIREQOUT, + PCIREQ2, PCIGNT2 */ + out_be32((volatile u32 *)&portcfg->pcfg3, + (in_be32((volatile u32 *)&portcfg->pcfg3) & 0xFC3FCE7F)); + out_be32((volatile u32 *)&portcfg->pcfg3, + (in_be32((volatile u32 *)&portcfg->pcfg3) | 0x01400180)); + + hose->first_busno = 0; + hose->last_busno = 0xff; + + pci_set_region(hose->regions + 0, + CONFIG_PCI_MEM_BUS, + CONFIG_PCI_MEM_PHYS, + CONFIG_PCI_MEM_SIZE, + PCI_REGION_MEM); + + pci_set_region(hose->regions + 1, + CONFIG_PCI_IO_BUS, + CONFIG_PCI_IO_PHYS, + CONFIG_PCI_IO_SIZE, + PCI_REGION_IO); + + pci_set_region(hose->regions + 2, + CONFIG_PCI_SYS_MEM_BUS, + CONFIG_PCI_SYS_MEM_PHYS, + CONFIG_PCI_SYS_MEM_SIZE, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + hose->region_count = 3; + + hose->cfg_addr = &(xcpci->cfg_adr); + hose->cfg_data = (volatile unsigned char *)CONFIG_PCI_CFG_BUS; + + pci_set_ops(hose, + mpc8220_pci_read_config_byte, + mpc8220_pci_read_config_word, + mpc8220_pci_read_config_dword, + mpc8220_pci_write_config_byte, + mpc8220_pci_write_config_word, + mpc8220_pci_write_config_dword); + + /* Hose scan */ + pci_register_hose(hose); + hose->last_busno = pci_hose_scan(hose); + + out_be32 (&xcpci->base0, PCI_BASE_ADDR_REG0); /* 256MB - MBAR space */ + out_be32 (&xcpci->base1, PCI_BASE_ADDR_REG1); /* 1GB - SDRAM space */ +} + +#endif /* CONFIG_PCI */ diff --git a/arch/powerpc/cpu/mpc8220/speed.c b/arch/powerpc/cpu/mpc8220/speed.c new file mode 100644 index 00000000000..62ac845b7ae --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/speed.c @@ -0,0 +1,123 @@ +/* + * (C) Copyright 2004, Freescale, Inc + * TsiChung Liew, Tsi-Chung.Liew@freescale.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <mpc8220.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +typedef struct pllmultiplier { + u8 hid1; + int multi; + int vco_div; +} pllcfg_t; + +/* ------------------------------------------------------------------------- */ + +/* + * + */ + +int get_clocks (void) +{ + pllcfg_t bus2core[] = { + {0x02, 2, 8}, /* 1 */ + {0x01, 2, 4}, + {0x0C, 3, 8}, /* 1.5 */ + {0x00, 3, 4}, + {0x18, 3, 2}, + {0x05, 4, 4}, /* 2 */ + {0x04, 4, 2}, + {0x11, 5, 4}, /* 2.5 */ + {0x06, 5, 2}, + {0x10, 6, 4}, /* 3 */ + {0x08, 6, 2}, + {0x0E, 7, 2}, /* 3.5 */ + {0x0A, 8, 2}, /* 4 */ + {0x07, 9, 2}, /* 4.5 */ + {0x0B, 10, 2}, /* 5 */ + {0x09, 11, 2}, /* 5.5 */ + {0x0D, 12, 2}, /* 6 */ + {0x12, 13, 2}, /* 6.5 */ + {0x14, 14, 2}, /* 7 */ + {0x16, 15, 2}, /* 7.5 */ + {0x1C, 16, 2} /* 8 */ + }; + u32 hid1; + int i, size, pci2bus; + +#if !defined(CONFIG_SYS_MPC8220_CLKIN) +#error clock measuring not implemented yet - define CONFIG_SYS_MPC8220_CLKIN +#endif + + gd->inp_clk = CONFIG_SYS_MPC8220_CLKIN; + + /* Read XLB to PCI(INP) clock multiplier */ + pci2bus = (*((volatile u32 *)PCI_REG_PCIGSCR) & + PCI_REG_PCIGSCR_PCI2XLB_CLK_MASK)>>PCI_REG_PCIGSCR_PCI2XLB_CLK_BIT; + + /* XLB bus clock */ + gd->bus_clk = CONFIG_SYS_MPC8220_CLKIN * pci2bus; + + /* PCI clock is same as input clock */ + gd->pci_clk = CONFIG_SYS_MPC8220_CLKIN; + + /* FlexBus is temporary set as the same as input clock */ + /* will do dynamic in the future */ + gd->flb_clk = CONFIG_SYS_MPC8220_CLKIN; + + /* CPU Clock - Read HID1 */ + asm volatile ("mfspr %0, 1009":"=r" (hid1):); + + size = sizeof (bus2core) / sizeof (pllcfg_t); + + hid1 >>= 27; + + for (i = 0; i < size; i++) + if (hid1 == bus2core[i].hid1) { + gd->cpu_clk = (bus2core[i].multi * gd->bus_clk) >> 1; + gd->vco_clk = CONFIG_SYS_MPC8220_SYSPLL_VCO_MULTIPLIER * (gd->pci_clk * bus2core[i].vco_div)/2; + break; + } + + /* hardcoded 81MHz for now */ + gd->pev_clk = 81000000; + + return (0); +} + +int prt_mpc8220_clks (void) +{ + char buf1[32], buf2[32], buf3[32], buf4[32]; + + printf (" Bus %s MHz, CPU %s MHz, PCI %s MHz, VCO %s MHz\n", + strmhz(buf1, gd->bus_clk), + strmhz(buf2, gd->cpu_clk), + strmhz(buf3, gd->pci_clk), + strmhz(buf4, gd->vco_clk) + ); + return (0); +} + +/* ------------------------------------------------------------------------- */ diff --git a/arch/powerpc/cpu/mpc8220/start.S b/arch/powerpc/cpu/mpc8220/start.S new file mode 100644 index 00000000000..3d79d8ec0d2 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/start.S @@ -0,0 +1,750 @@ +/* + * Copyright (C) 1998 Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000 - 2003 Wolfgang Denk <wd@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * U-Boot - Startup Code for MPC8220 CPUs + */ +#include <config.h> +#include <mpc8220.h> +#include <timestamp.h> +#include <version.h> + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef CONFIG_IDENT_STRING +#define CONFIG_IDENT_STRING "" +#endif + +/* We don't want the MMU yet. +*/ +#undef MSR_KERNEL +/* Floating Point enable, Machine Check and Recoverable Interr. */ +#ifdef DEBUG +#define MSR_KERNEL (MSR_FP|MSR_RI) +#else +#define MSR_KERNEL (MSR_FP|MSR_ME|MSR_RI) +#endif + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ + START_GOT + GOT_ENTRY(_GOT2_TABLE_) + GOT_ENTRY(_FIXUP_TABLE_) + + GOT_ENTRY(_start) + GOT_ENTRY(_start_of_vectors) + GOT_ENTRY(_end_of_vectors) + GOT_ENTRY(transfer_to_handler) + + GOT_ENTRY(__init_end) + GOT_ENTRY(_end) + GOT_ENTRY(__bss_start) + END_GOT + +/* + * Version string + */ + .data + .globl version_string +version_string: + .ascii U_BOOT_VERSION + .ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" + .ascii CONFIG_IDENT_STRING, "\0" + +/* + * Exception vectors + */ + .text + . = EXC_OFF_SYS_RESET + .globl _start +_start: + li r21, BOOTFLAG_COLD /* Normal Power-On */ + nop + 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: + mfmsr r5 /* save msr contents */ + + /* replace default MBAR base address from 0x80000000 + to 0xf0000000 */ + +#if defined(CONFIG_SYS_DEFAULT_MBAR) && !defined(CONFIG_SYS_RAMBOOT) + lis r3, CONFIG_SYS_MBAR@h + ori r3, r3, CONFIG_SYS_MBAR@l + + /* MBAR is mirrored into the MBAR SPR */ + mtspr MBAR,r3 + mtspr SPRN_SPRG7W,r3 + lis r4, CONFIG_SYS_DEFAULT_MBAR@h + stw r3, 0(r4) +#endif /* CONFIG_SYS_DEFAULT_MBAR */ + + /* Initialise the MPC8220 processor core */ + /*--------------------------------------------------------------*/ + + bl init_8220_core + + /* initialize some things that are hard to access from C */ + /*--------------------------------------------------------------*/ + + /* set up stack in on-chip SRAM */ + lis r3, CONFIG_SYS_INIT_RAM_ADDR@h + ori r3, r3, CONFIG_SYS_INIT_RAM_ADDR@l + ori r1, r3, CONFIG_SYS_INIT_SP_OFFSET + + 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 (in Flash)*/ + + mr r3, r21 + /* r3: BOOTFLAG */ + bl board_init_f /* run 1st part of board init code (in Flash)*/ + +/* + * Vector Table + */ + + .globl _start_of_vectors +_start_of_vectors: + +/* Machine check */ + STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + +/* Data Storage exception. */ + STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ + STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ + STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ + . = 0x600 +Alignment: + EXCEPTION_PROLOG(SRR0, SRR1) + mfspr r4,DAR + stw r4,_DAR(r21) + mfspr r5,DSISR + stw r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ + . = 0x700 +ProgramCheck: + EXCEPTION_PROLOG(SRR0, SRR1) + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, + MSR_KERNEL, COPY_EE) + + STD_EXCEPTION(0x800, FPUnavailable, UnknownException) + + /* I guess we could implement decrementer, and may have + * to someday for timekeeping. + */ + STD_EXCEPTION(0x900, Decrementer, timer_interrupt) + + STD_EXCEPTION(0xa00, Trap_0a, UnknownException) + STD_EXCEPTION(0xb00, Trap_0b, UnknownException) + STD_EXCEPTION(0xc00, SystemCall, UnknownException) + STD_EXCEPTION(0xd00, SingleStep, UnknownException) + + STD_EXCEPTION(0xe00, Trap_0e, UnknownException) + STD_EXCEPTION(0xf00, Trap_0f, UnknownException) + + STD_EXCEPTION(0x1000, InstructionTLBMiss, UnknownException) + STD_EXCEPTION(0x1100, DataLoadTLBMiss, UnknownException) + STD_EXCEPTION(0x1200, DataStoreTLBMiss, UnknownException) +#ifdef DEBUG + . = 0x1300 + /* + * This exception occurs when the program counter matches the + * Instruction Address Breakpoint Register (IABR). + * + * I want the cpu to halt if this occurs so I can hunt around + * with the debugger and look at things. + * + * When DEBUG is defined, both machine check enable (in the MSR) + * and checkstop reset enable (in the reset mode register) are + * turned off and so a checkstop condition will result in the cpu + * halting. + * + * I force the cpu into a checkstop condition by putting an illegal + * instruction here (at least this is the theory). + * + * well - that didnt work, so just do an infinite loop! + */ +1: b 1b +#else + STD_EXCEPTION(0x1300, InstructionBreakpoint, DebugException) +#endif + STD_EXCEPTION(0x1400, SMI, UnknownException) + + STD_EXCEPTION(0x1500, Trap_15, UnknownException) + STD_EXCEPTION(0x1600, Trap_16, UnknownException) + STD_EXCEPTION(0x1700, Trap_17, UnknownException) + STD_EXCEPTION(0x1800, Trap_18, UnknownException) + STD_EXCEPTION(0x1900, Trap_19, UnknownException) + STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) + STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) + STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) + STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) + STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) + STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) + STD_EXCEPTION(0x2000, Trap_20, UnknownException) + STD_EXCEPTION(0x2100, Trap_21, UnknownException) + STD_EXCEPTION(0x2200, Trap_22, UnknownException) + STD_EXCEPTION(0x2300, Trap_23, UnknownException) + STD_EXCEPTION(0x2400, Trap_24, UnknownException) + STD_EXCEPTION(0x2500, Trap_25, UnknownException) + STD_EXCEPTION(0x2600, Trap_26, UnknownException) + STD_EXCEPTION(0x2700, Trap_27, UnknownException) + STD_EXCEPTION(0x2800, Trap_28, UnknownException) + STD_EXCEPTION(0x2900, Trap_29, UnknownException) + STD_EXCEPTION(0x2a00, Trap_2a, UnknownException) + STD_EXCEPTION(0x2b00, Trap_2b, UnknownException) + STD_EXCEPTION(0x2c00, Trap_2c, UnknownException) + STD_EXCEPTION(0x2d00, Trap_2d, UnknownException) + STD_EXCEPTION(0x2e00, Trap_2e, UnknownException) + STD_EXCEPTION(0x2f00, Trap_2f, 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) + mflr r23 + andi. r24,r23,0x3f00 /* get vector offset */ + stw r24,TRAP(r21) + li r22,0 + stw r22,RESULT(r21) + lwz r24,0(r23) /* virtual address of handler */ + lwz r23,4(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + SYNC + rfi /* jump to handler, enable MMU */ + +int_return: + mfmsr r28 /* Disable interrupts */ + li r4,0 + ori r4,r4,MSR_EE + andc r28,r28,r4 + SYNC /* Some chip revs need this... */ + mtmsr r28 + SYNC + lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + REST_10GPRS(3, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi + +/* + * This code initialises the MPC8220 processor core + * (conforms to PowerPC 603e spec) + * Note: expects original MSR contents to be in r5. + */ + + .globl init_8220_core +init_8220_core: + + /* Initialize machine status; enable machine check interrupt */ + /*--------------------------------------------------------------*/ + + li r3, MSR_KERNEL /* Set ME and RI flags */ + rlwimi r3, r5, 0, 25, 25 /* preserve IP bit set by HRCW */ +#ifdef DEBUG + rlwimi r3, r5, 0, 21, 22 /* debugger might set SE & BE bits */ +#endif + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + mtspr SRR1, r3 /* Make SRR1 match MSR */ + + /* Initialize the Hardware Implementation-dependent Registers */ + /* HID0 also contains cache control */ + /*--------------------------------------------------------------*/ + + lis r3, CONFIG_SYS_HID0_INIT@h + ori r3, r3, CONFIG_SYS_HID0_INIT@l + SYNC + mtspr HID0, r3 + + lis r3, CONFIG_SYS_HID0_FINAL@h + ori r3, r3, CONFIG_SYS_HID0_FINAL@l + SYNC + mtspr HID0, r3 + + /* Enable Extra BATs */ + mfspr r3, 1011 /* HID2 */ + lis r4, 0x0004 + ori r4, r4, 0x0000 + or r4, r4, r3 + mtspr 1011, r4 + sync + + /* clear all BAT's */ + /*--------------------------------------------------------------*/ + + li r0, 0 + mtspr DBAT0U, r0 + mtspr DBAT0L, r0 + mtspr DBAT1U, r0 + mtspr DBAT1L, r0 + mtspr DBAT2U, r0 + mtspr DBAT2L, r0 + mtspr DBAT3U, r0 + mtspr DBAT3L, r0 + mtspr DBAT4U, r0 + mtspr DBAT4L, r0 + mtspr DBAT5U, r0 + mtspr DBAT5L, r0 + mtspr DBAT6U, r0 + mtspr DBAT6L, r0 + mtspr DBAT7U, r0 + mtspr DBAT7L, r0 + mtspr IBAT0U, r0 + mtspr IBAT0L, r0 + mtspr IBAT1U, r0 + mtspr IBAT1L, r0 + mtspr IBAT2U, r0 + mtspr IBAT2L, r0 + mtspr IBAT3U, r0 + mtspr IBAT3L, r0 + mtspr IBAT4U, r0 + mtspr IBAT4L, r0 + mtspr IBAT5U, r0 + mtspr IBAT5L, r0 + mtspr IBAT6U, r0 + mtspr IBAT6L, r0 + mtspr IBAT7U, r0 + mtspr IBAT7L, r0 + SYNC + + /* invalidate all tlb's */ + /* */ + /* From the 603e User Manual: "The 603e provides the ability to */ + /* invalidate a TLB entry. The TLB Invalidate Entry (tlbie) */ + /* instruction invalidates the TLB entry indexed by the EA, and */ + /* operates on both the instruction and data TLBs simultaneously*/ + /* invalidating four TLB entries (both sets in each TLB). The */ + /* index corresponds to bits 15-19 of the EA. To invalidate all */ + /* entries within both TLBs, 32 tlbie instructions should be */ + /* issued, incrementing this field by one each time." */ + /* */ + /* "Note that the tlbia instruction is not implemented on the */ + /* 603e." */ + /* */ + /* bits 15-19 correspond to addresses 0x00000000 to 0x0001F000 */ + /* incrementing by 0x1000 each time. The code below is sort of */ + /* based on code in "flush_tlbs" from arch/powerpc/kernel/head.S */ + /* */ + /*--------------------------------------------------------------*/ + + li r3, 32 + mtctr r3 + li r3, 0 +1: tlbie r3 + addi r3, r3, 0x1000 + bdnz 1b + SYNC + + /* Done! */ + /*--------------------------------------------------------------*/ + + blr + +/* Cache functions. + * + * Note: requires that all cache bits in + * HID0 are in the low half word. + */ + .globl icache_enable +icache_enable: + lis r4, 0 + ori r4, r4, CONFIG_SYS_HID0_INIT /* set ICE & ICFI bit */ + rlwinm r3, r4, 0, 21, 19 /* clear the ICFI bit */ + + /* + * The setting of the instruction cache enable (ICE) bit must be + * preceded by an isync instruction to prevent the cache from being + * enabled or disabled while an instruction access is in progress. + */ + isync + mtspr HID0, r4 /* Enable Instr Cache & Inval cache */ + mtspr HID0, r3 /* using 2 consec instructions */ + isync + blr + + .globl icache_disable +icache_disable: + mfspr r3, HID0 + rlwinm r3, r3, 0, 17, 15 /* clear the ICE bit */ + mtspr HID0, r3 + isync + blr + + .globl icache_status +icache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_ICE_BITPOS + 1, 31, 31 + blr + + .globl dcache_enable +dcache_enable: + lis r4, 0 + ori r4, r4, HID0_DCE|HID0_DCFI /* set DCE & DCFI bit */ + rlwinm r3, r4, 0, 22, 20 /* clear the DCFI bit */ + + /* Enable address translation in MSR bit */ + mfmsr r5 + ori r5, r5, 0x + + + /* + * The setting of the instruction cache enable (ICE) bit must be + * preceded by an isync instruction to prevent the cache from being + * enabled or disabled while an instruction access is in progress. + */ + isync + mtspr HID0, r4 /* Enable Data Cache & Inval cache*/ + mtspr HID0, r3 /* using 2 consec instructions */ + isync + blr + + .globl dcache_disable +dcache_disable: + mfspr r3, HID0 + rlwinm r3, r3, 0, 18, 16 /* clear the DCE bit */ + mtspr HID0, r3 + isync + blr + + .globl dcache_status +dcache_status: + mfspr r3, HID0 + rlwinm r3, r3, HID0_DCE_BITPOS + 1, 31, 31 + 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 */ + + GET_GOT + mr r3, r5 /* Destination Address */ + lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */ + ori r4, r4, CONFIG_SYS_MONITOR_BASE@l + lwz r5, GOT(__init_end) + sub r5, r5, r4 + li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */ + + /* + * Fix GOT pointer: + * + * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address + * + * Offset: + */ + sub r15, r10, r4 + + /* First our own GOT */ + add r12, r12, r15 + /* then the one used by the C code */ + add r30, r30, r15 + + /* + * Now relocate code + */ + + cmplw cr1,r3,r4 + addi r0,r5,3 + srwi. r0,r0,2 + beq cr1,4f /* In place copy is not necessary */ + beq 7f /* Protect against 0 count */ + mtctr r0 + bge cr1,2f + + la r8,-4(r4) + la r7,-4(r3) +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 + mfspr r7,HID0 /* don't do dcbst if dcache is disabled */ + rlwinm r7,r7,HID0_DCE_BITPOS+1,31,31 + cmpwi r7,0 + beq 9f + 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 */ +9: mfspr r7,HID0 /* don't do icbi if icache is disabled */ + rlwinm r7,r7,HID0_ICE_BITPOS+1,31,31 + cmpwi r7,0 + beq 7f + 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, r12 point to got2+0x8000 + * + * Adjust got2 pointers, no need to check for 0, this code + * already puts a few entries in the table. + */ + li r0,__got2_entries@sectoff@l + la r3,GOT(_GOT2_TABLE_) + lwz r11,GOT(_GOT2_TABLE_) + mtctr r0 + sub r11,r3,r11 + addi r3,r3,-4 +1: lwzu r0,4(r3) + cmpwi r0,0 + beq- 2f + add r0,r0,r11 + stw r0,0(r3) +2: bdnz 1b + + /* + * Now adjust the fixups and the pointers to the fixups + * in case we need to move ourselves again. + */ + li r0,__fixup_entries@sectoff@l + lwz r3,GOT(_FIXUP_TABLE_) + cmpwi r0,0 + mtctr r0 + addi r3,r3,-4 + beq 4f +3: lwzu r4,4(r3) + lwzux r0,r4,r11 + add r0,r0,r11 + stw r10,0(r3) + stw r0,0(r4) + bdnz 3b +4: +clear_bss: + /* + * Now clear BSS segment + */ + lwz r3,GOT(__bss_start) + lwz r4,GOT(_end) + + cmplw 0, r3, r4 + beq 6f + + li r0, 0 +5: + stw r0, 0(r3) + addi r3, r3, 4 + cmplw 0, r3, r4 + bne 5b +6: + + mr r3, r9 /* Global Data pointer */ + mr r4, r10 /* Destination Address */ + bl board_init_r + + /* + * Copy exception vector code to low memory + * + * r3: dest_addr + * r7: source address, r8: end address, r9: target address + */ + .globl trap_init +trap_init: + mflr r4 /* save link register */ + GET_GOT + lwz r7, GOT(_start) + lwz r8, GOT(_end_of_vectors) + + li r9, 0x100 /* reset vector always at 0x100 */ + + cmplw 0, r7, r8 + bgelr /* return if r7>=r8 - just in case */ +1: + lwz r0, 0(r7) + stw r0, 0(r9) + addi r7, r7, 4 + addi r9, r9, 4 + cmplw 0, r7, r8 + bne 1b + + /* + * relocate `hdlr' and `int_return' entries + */ + li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET + li r8, Alignment - _start + EXC_OFF_SYS_RESET +2: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 2b + + li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET + bl trap_reloc + + li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET + li r8, SystemCall - _start + EXC_OFF_SYS_RESET +3: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 3b + + li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET + li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET +4: + bl trap_reloc + addi r7, r7, 0x100 /* next exception vector */ + cmplw 0, r7, r8 + blt 4b + + mfmsr r3 /* now that the vectors have */ + lis r7, MSR_IP@h /* relocated into low memory */ + ori r7, r7, MSR_IP@l /* MSR[IP] can be turned off */ + andc r3, r3, r7 /* (if it was on) */ + SYNC /* Some chip revs need this... */ + mtmsr r3 + SYNC + + mtlr r4 /* restore link register */ + blr diff --git a/arch/powerpc/cpu/mpc8220/traps.c b/arch/powerpc/cpu/mpc8220/traps.c new file mode 100644 index 00000000000..13894c90502 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/traps.c @@ -0,0 +1,236 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * fixed Machine Check Reasons by Reinhard Meyer (r.meyer@emk-elektronik.de) + * + * (C) Copyright 2000-2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table (unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x02000000 + +/* + * Trap & Exception support + */ + +void print_backtrace (unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf ("Call backtrace: "); + while (sp) { + if ((uint) sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf ("\n"); + printf ("%08lX ", i); + if (cnt > 32) + break; + sp = (unsigned long *) *sp; + } + printf ("\n"); +} + +void show_regs (struct pt_regs *regs) +{ + int i; + + printf ("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf ("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, + regs->msr & MSR_EE ? 1 : 0, regs->msr & MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0, regs->msr & MSR_ME ? 1 : 0, + regs->msr & MSR_IR ? 1 : 0, regs->msr & MSR_DR ? 1 : 0); + + printf ("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) { + printf ("GPR%02d: ", i); + } + + printf ("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) { + printf ("\n"); + } + } +} + + +void _exception (int signr, struct pt_regs *regs) +{ + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("Exception in kernel pc %lx signal %d", regs->nip, signr); +} + +void MachineCheckException (struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table (regs->nip)) != 0) { + regs->nip = fixup; + return; + } +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + + printf ("Machine check in kernel mode.\n"); + printf ("Caused by (from msr): "); + printf ("regs %p ", regs); + /* refer to 603e Manual (MPC603EUM/AD), chapter 4.5.2.1 */ + switch (regs->msr & 0x000F0000) { + case (0x80000000 >> 12): + printf ("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (0x80000000 >> 13): + printf ("Transfer error ack signal\n"); + break; + case (0x80000000 >> 14): + printf ("Data parity signal\n"); + break; + case (0x80000000 >> 15): + printf ("Address parity signal\n"); + break; + default: + printf ("Unknown values in msr\n"); + } + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("machine check"); +} + +void AlignmentException (struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("Alignment Exception"); +} + +void ProgramCheckException (struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("Program Check Exception"); +} + +void SoftEmuException (struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + show_regs (regs); + print_backtrace ((unsigned long *) regs->gpr[1]); + panic ("Software Emulation Exception"); +} + + +void UnknownException (struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) + if (debugger_exception_handler + && (*debugger_exception_handler) (regs)) + return; +#endif + printf ("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception (0, regs); +} + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint (struct pt_regs *); +#endif + +void DebugException (struct pt_regs *regs) +{ + + printf ("Debugger trap at @ %lx\n", regs->nip); + show_regs (regs); +#if defined(CONFIG_CMD_BEDBUG) + do_bedbug_breakpoint (regs); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int addr_probe (uint * addr) +{ +#if 0 + int retval; + + __asm__ __volatile__ ("1: lwz %0,0(%1)\n" + " eieio\n" + " li %0,0\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: li %0,-1\n" + " b 2b\n" + ".section __ex_table,\"a\"\n" + " .align 2\n" + " .long 1b,3b\n" + ".text":"=r" (retval):"r" (addr)); + + return (retval); +#endif + return 0; +} diff --git a/arch/powerpc/cpu/mpc8220/u-boot.lds b/arch/powerpc/cpu/mpc8220/u-boot.lds new file mode 100644 index 00000000000..31a7a0e4ef0 --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/u-boot.lds @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2003-2004 + * 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 + */ + +OUTPUT_ARCH(powerpc) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } + .plt : { *(.plt) } + .text : + { + arch/powerpc/cpu/mpc8220/start.o (.text) + *(.text) + *(.got1) + . = ALIGN(16); + *(.eh_frame) + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + _erotext = .; + PROVIDE (erotext = .); + .reloc : + { + *(.got) + _GOT2_TABLE_ = .; + *(.got2) + _FIXUP_TABLE_ = .; + *(.fixup) + } + __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >> 2; + __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + . = .; + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + + . = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss (NOLOAD) : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(4); + } + _end = . ; + PROVIDE (end = .); +} diff --git a/arch/powerpc/cpu/mpc8220/uart.c b/arch/powerpc/cpu/mpc8220/uart.c new file mode 100644 index 00000000000..0c4b536b48e --- /dev/null +++ b/arch/powerpc/cpu/mpc8220/uart.c @@ -0,0 +1,126 @@ +/* + * (C) Copyright 2004, Freescale, Inc + * TsiChung Liew, Tsi-Chung.Liew@freescale.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 + * + */ + +/* + * Minimal serial functions needed to use one of the PSC ports + * as serial console interface. + */ + +#include <common.h> +#include <mpc8220.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define PSC_BASE MMAP_PSC1 + +#if defined(CONFIG_PSC_CONSOLE) +int serial_init (void) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + u32 counter; + + /* write to SICR: SIM2 = uart mode,dcd does not affect rx */ + psc->cr = 0; + psc->ipcr_acr = 0; + psc->isr_imr = 0; + + /* write to CSR: RX/TX baud rate from timers */ + psc->sr_csr = 0xdd000000; + + psc->mr1_2 = PSC_MR1_BITS_CHAR_8 | PSC_MR1_NO_PARITY | PSC_MR2_STOP_BITS_1; + + /* Setting up BaudRate */ + counter = ((gd->bus_clk / gd->baudrate)) >> 5; + counter++; + + /* write to CTUR: divide counter upper byte */ + psc->ctur = ((counter & 0xff00) << 16); + /* write to CTLR: divide counter lower byte */ + psc->ctlr = ((counter & 0x00ff) << 24); + + psc->cr = PSC_CR_RST_RX_CMD; + psc->cr = PSC_CR_RST_TX_CMD; + psc->cr = PSC_CR_RST_ERR_STS_CMD; + psc->cr = PSC_CR_RST_BRK_INT_CMD; + psc->cr = PSC_CR_RST_MR_PTR_CMD; + + psc->cr = PSC_CR_RX_ENABLE | PSC_CR_TX_ENABLE; + return (0); +} + +void serial_putc (const char c) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + + if (c == '\n') + serial_putc ('\r'); + + /* Wait for last character to go. */ + while (!(psc->sr_csr & PSC_SR_TXRDY)); + + psc->xmitbuf[0] = c; +} + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int serial_getc (void) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + + /* Wait for a character to arrive. */ + while (!(psc->sr_csr & PSC_SR_RXRDY)); + return psc->xmitbuf[2]; +} + +int serial_tstc (void) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + + return (psc->sr_csr & PSC_SR_RXRDY); +} + +void serial_setbrg (void) +{ + volatile psc8220_t *psc = (psc8220_t *) PSC_BASE; + u32 counter; + + counter = ((gd->bus_clk / gd->baudrate)) >> 5; + counter++; + + /* write to CTUR: divide counter upper byte */ + psc->ctur = ((counter & 0xff00) << 16); + /* write to CTLR: divide counter lower byte */ + psc->ctlr = ((counter & 0x00ff) << 24); + + psc->cr = PSC_CR_RST_RX_CMD; + psc->cr = PSC_CR_RST_TX_CMD; + + psc->cr = PSC_CR_RX_ENABLE | PSC_CR_TX_ENABLE; +} +#endif /* CONFIG_PSC_CONSOLE */ |