diff options
Diffstat (limited to 'arch/powerpc/cpu/mpc83xx')
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/Makefile | 62 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/config.mk | 29 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/cpu.c | 334 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/cpu_init.c | 548 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/ecc.c | 390 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/fdt.c | 143 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/interrupts.c | 97 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/nand_init.c | 112 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/pci.c | 241 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/pcie.c | 317 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/qe_io.c | 82 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/serdes.c | 145 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/spd_sdram.c | 918 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/speed.c | 549 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/start.S | 1207 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/traps.c | 266 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc83xx/u-boot.lds | 121 | 
17 files changed, 5561 insertions, 0 deletions
| diff --git a/arch/powerpc/cpu/mpc83xx/Makefile b/arch/powerpc/cpu/mpc83xx/Makefile new file mode 100644 index 000000000..15e2c18b1 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/Makefile @@ -0,0 +1,62 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# Copyright 2004 Freescale Semiconductor, Inc. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB	= $(obj)lib$(CPU).a + +START	= start.o + +COBJS-y += traps.o +COBJS-y += cpu.o +COBJS-y += cpu_init.o +COBJS-y += speed.o +COBJS-y += interrupts.o +COBJS-y += spd_sdram.o +COBJS-y += ecc.o +COBJS-$(CONFIG_QE) += qe_io.o +COBJS-$(CONFIG_FSL_SERDES) += serdes.o +COBJS-$(CONFIG_PCI) += pci.o +COBJS-$(CONFIG_PCIE) += pcie.o +COBJS-$(CONFIG_OF_LIBFDT) += fdt.o + +COBJS	:= $(COBJS-y) +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/mpc83xx/config.mk b/arch/powerpc/cpu/mpc83xx/config.mk new file mode 100644 index 000000000..8a3a8c1b9 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/config.mk @@ -0,0 +1,29 @@ +# +# Copyright 2004 Freescale Semiconductor, Inc. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +PLATFORM_RELFLAGS += -fPIC -meabi + +PLATFORM_CPPFLAGS += -DCONFIG_MPC83xx -DCONFIG_E300 \ +			-ffixed-r2 -msoft-float + +# Use default linker script.  Board port can override in board/*/config.mk +LDSCRIPT := $(SRCTREE)/arch/powerpc/cpu/mpc83xx/u-boot.lds diff --git a/arch/powerpc/cpu/mpc83xx/cpu.c b/arch/powerpc/cpu/mpc83xx/cpu.c new file mode 100644 index 000000000..51180d6da --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/cpu.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * CPU specific code for the MPC83xx family. + * + * Derived from the MPC8260 and MPC85xx. + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <mpc83xx.h> +#include <asm/processor.h> +#include <libfdt.h> +#include <tsec.h> +#include <netdev.h> +#include <fsl_esdhc.h> +#ifdef CONFIG_BOOTCOUNT_LIMIT +#include <asm/immap_qe.h> +#include <asm/io.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +int checkcpu(void) +{ +	volatile immap_t *immr; +	ulong clock = gd->cpu_clk; +	u32 pvr = get_pvr(); +	u32 spridr; +	char buf[32]; +	int i; + +	const struct cpu_type { +		char name[15]; +		u32 partid; +	} cpu_type_list [] = { +		CPU_TYPE_ENTRY(8311), +		CPU_TYPE_ENTRY(8313), +		CPU_TYPE_ENTRY(8314), +		CPU_TYPE_ENTRY(8315), +		CPU_TYPE_ENTRY(8321), +		CPU_TYPE_ENTRY(8323), +		CPU_TYPE_ENTRY(8343), +		CPU_TYPE_ENTRY(8347_TBGA_), +		CPU_TYPE_ENTRY(8347_PBGA_), +		CPU_TYPE_ENTRY(8349), +		CPU_TYPE_ENTRY(8358_TBGA_), +		CPU_TYPE_ENTRY(8358_PBGA_), +		CPU_TYPE_ENTRY(8360), +		CPU_TYPE_ENTRY(8377), +		CPU_TYPE_ENTRY(8378), +		CPU_TYPE_ENTRY(8379), +	}; + +	immr = (immap_t *)CONFIG_SYS_IMMR; + +	puts("CPU:   "); + +	switch (pvr & 0xffff0000) { +		case PVR_E300C1: +			printf("e300c1, "); +			break; + +		case PVR_E300C2: +			printf("e300c2, "); +			break; + +		case PVR_E300C3: +			printf("e300c3, "); +			break; + +		case PVR_E300C4: +			printf("e300c4, "); +			break; + +		default: +			printf("Unknown core, "); +	} + +	spridr = immr->sysconf.spridr; + +	for (i = 0; i < ARRAY_SIZE(cpu_type_list); i++) +		if (cpu_type_list[i].partid == PARTID_NO_E(spridr)) { +			puts("MPC"); +			puts(cpu_type_list[i].name); +			if (IS_E_PROCESSOR(spridr)) +				puts("E"); +			if (REVID_MAJOR(spridr) >= 2) +				puts("A"); +			printf(", Rev: %d.%d", REVID_MAJOR(spridr), +			       REVID_MINOR(spridr)); +			break; +		} + +	if (i == ARRAY_SIZE(cpu_type_list)) +		printf("(SPRIDR %08x unknown), ", spridr); + +	printf(" at %s MHz, ", strmhz(buf, clock)); + +	printf("CSB: %s MHz\n", strmhz(buf, gd->csb_clk)); + +	return 0; +} + + +/* + * Program a UPM with the code supplied in the table. + * + * The 'dummy' variable is used to increment the MAD. 'dummy' is + * supposed to be a pointer to the memory of the device being + * programmed by the UPM.  The data in the MDR is written into + * memory and the MAD is incremented every time there's a write + * to 'dummy'. Unfortunately, the current prototype for this + * function doesn't allow for passing the address of this + * device, and changing the prototype will break a number lots + * of other code, so we need to use a round-about way of finding + * the value for 'dummy'. + * + * The value can be extracted from the base address bits of the + * Base Register (BR) associated with the specific UPM.  To find + * that BR, we need to scan all 8 BRs until we find the one that + * has its MSEL bits matching the UPM we want.  Once we know the + * right BR, we can extract the base address bits from it. + * + * The MxMR and the BR and OR of the chosen bank should all be + * configured before calling this function. + * + * Parameters: + * upm: 0=UPMA, 1=UPMB, 2=UPMC + * table: Pointer to an array of values to program + * size: Number of elements in the array.  Must be 64 or less. + */ +void upmconfig (uint upm, uint *table, uint size) +{ +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	volatile fsl_lbus_t *lbus = &immap->lbus; +	volatile uchar *dummy = NULL; +	const u32 msel = (upm + 4) << BR_MSEL_SHIFT;	/* What the MSEL field in BRn should be */ +	volatile u32 *mxmr = &lbus->mamr + upm;	/* Pointer to mamr, mbmr, or mcmr */ +	uint i; + +	/* Scan all the banks to determine the base address of the device */ +	for (i = 0; i < 8; i++) { +		if ((lbus->bank[i].br & BR_MSEL) == msel) { +			dummy = (uchar *) (lbus->bank[i].br & BR_BA); +			break; +		} +	} + +	if (!dummy) { +		printf("Error: %s() could not find matching BR\n", __FUNCTION__); +		hang(); +	} + +	/* Set the OP field in the MxMR to "write" and the MAD field to 000000 */ +	*mxmr = (*mxmr & 0xCFFFFFC0) | 0x10000000; + +	for (i = 0; i < size; i++) { +		lbus->mdr = table[i]; +		__asm__ __volatile__ ("sync"); +		*dummy = 0;	/* Write the value to memory and increment MAD */ +		__asm__ __volatile__ ("sync"); +		while(((*mxmr & 0x3f) != ((i + 1) & 0x3f))); +	} + +	/* Set the OP field in the MxMR to "normal" and the MAD field to 000000 */ +	*mxmr &= 0xCFFFFFC0; +} + + +int +do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ +	ulong msr; +#ifndef MPC83xx_RESET +	ulong addr; +#endif + +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; + +	puts("Resetting the board.\n"); + +#ifdef MPC83xx_RESET + +	/* Interrupts and MMU off */ +	__asm__ __volatile__ ("mfmsr    %0":"=r" (msr):); + +	msr &= ~( MSR_EE | MSR_IR | MSR_DR); +	__asm__ __volatile__ ("mtmsr    %0"::"r" (msr)); + +	/* enable Reset Control Reg */ +	immap->reset.rpr = 0x52535445; +	__asm__ __volatile__ ("sync"); +	__asm__ __volatile__ ("isync"); + +	/* confirm Reset Control Reg is enabled */ +	while(!((immap->reset.rcer) & RCER_CRE)); + +	udelay(200); + +	/* perform reset, only one bit */ +	immap->reset.rcr = RCR_SWHR; + +#else	/* ! MPC83xx_RESET */ + +	immap->reset.rmr = RMR_CSRE;    /* Checkstop Reset enable */ + +	/* 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)); + +	/* +	 * Trying to execute the next instruction at a non-existing address +	 * should cause a machine check, resulting in reset +	 */ +	addr = CONFIG_SYS_RESET_ADDRESS; + +	((void (*)(void)) addr) (); +#endif	/* MPC83xx_RESET */ + +	return 1; +} + + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + */ + +unsigned long get_tbclk(void) +{ +	ulong tbclk; + +	tbclk = (gd->bus_clk + 3L) / 4L; + +	return tbclk; +} + + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset (void) +{ +	int re_enable = disable_interrupts(); + +	/* Reset the 83xx watchdog */ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; +	immr->wdt.swsrr = 0x556c; +	immr->wdt.swsrr = 0xaa39; + +	if (re_enable) +		enable_interrupts (); +} +#endif + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(CONFIG_UEC_ETH) +	uec_standard_init(bis); +#endif + +#if defined(CONFIG_TSEC_ENET) +	tsec_standard_init(bis); +#endif +	return 0; +} + +/* + * Initializes on-chip MMC controllers. + * to override, implement board_mmc_init() + */ +int cpu_mmc_init(bd_t *bis) +{ +#ifdef CONFIG_FSL_ESDHC +	return fsl_esdhc_mmc_init(bis); +#else +	return 0; +#endif +} + +#ifdef CONFIG_BOOTCOUNT_LIMIT + +#if !defined(CONFIG_MPC8360) +#error "CONFIG_BOOTCOUNT_LIMIT only for MPC8360 implemented" +#endif + +#if !defined(CONFIG_BOOTCOUNT_ADDR) +#define CONFIG_BOOTCOUNT_ADDR	(0x110000 + QE_MURAM_SIZE - 2 * sizeof(unsigned long)) +#endif + +#include <asm/io.h> + +void bootcount_store (ulong a) +{ +	void *reg = (void *)(CONFIG_SYS_IMMR + CONFIG_BOOTCOUNT_ADDR); +	out_be32 (reg, a); +	out_be32 (reg + 4, BOOTCOUNT_MAGIC); +} + +ulong bootcount_load (void) +{ +	void *reg = (void *)(CONFIG_SYS_IMMR + CONFIG_BOOTCOUNT_ADDR); + +	if (in_be32 (reg + 4) != BOOTCOUNT_MAGIC) +		return 0; +	else +		return in_be32 (reg); +} +#endif /* CONFIG_BOOTCOUNT_LIMIT */ diff --git a/arch/powerpc/cpu/mpc83xx/cpu_init.c b/arch/powerpc/cpu/mpc83xx/cpu_init.c new file mode 100644 index 000000000..75b45222b --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/cpu_init.c @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2004-2009 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <mpc83xx.h> +#include <ioports.h> +#include <asm/io.h> +#ifdef CONFIG_USB_EHCI_FSL +#include <usb/ehci-fsl.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_QE +extern qe_iop_conf_t qe_iop_conf_tab[]; +extern void qe_config_iopin(u8 port, u8 pin, int dir, +			 int open_drain, int assign); +extern void qe_init(uint qe_base); +extern void qe_reset(void); + +static void config_qe_ioports(void) +{ +	u8	port, pin; +	int	dir, open_drain, assign; +	int	i; + +	for (i = 0; qe_iop_conf_tab[i].assign != QE_IOP_TAB_END; i++) { +		port		= qe_iop_conf_tab[i].port; +		pin		= qe_iop_conf_tab[i].pin; +		dir		= qe_iop_conf_tab[i].dir; +		open_drain	= qe_iop_conf_tab[i].open_drain; +		assign		= qe_iop_conf_tab[i].assign; +		qe_config_iopin(port, pin, dir, open_drain, assign); +	} +} +#endif + +/* + * Breathe some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * im) +{ +	__be32 acr_mask = +#ifdef CONFIG_SYS_ACR_PIPE_DEP /* Arbiter pipeline depth */ +		(ACR_PIPE_DEP << ACR_PIPE_DEP_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_RPTCNT /* Arbiter repeat count */ +		(ACR_RPTCNT << ACR_RPTCNT_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_APARK	/* Arbiter address parking mode */ +		(ACR_APARK << ACR_APARK_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_PARKM	/* Arbiter parking master */ +		(ACR_PARKM << ACR_PARKM_SHIFT) | +#endif +		0; +	__be32 acr_val = +#ifdef CONFIG_SYS_ACR_PIPE_DEP /* Arbiter pipeline depth */ +		(CONFIG_SYS_ACR_PIPE_DEP << ACR_PIPE_DEP_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_RPTCNT /* Arbiter repeat count */ +		(CONFIG_SYS_ACR_RPTCNT << ACR_RPTCNT_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_APARK	/* Arbiter address parking mode */ +		(CONFIG_SYS_ACR_APARK << ACR_APARK_SHIFT) | +#endif +#ifdef CONFIG_SYS_ACR_PARKM	/* Arbiter parking master */ +		(CONFIG_SYS_ACR_PARKM << ACR_PARKM_SHIFT) | +#endif +		0; +	__be32 spcr_mask = +#ifdef CONFIG_SYS_SPCR_OPT /* Optimize transactions between CSB and other dev */ +		(SPCR_OPT << SPCR_OPT_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSECEP /* all eTSEC's Emergency priority */ +		(SPCR_TSECEP << SPCR_TSECEP_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSEC1EP /* TSEC1 Emergency priority */ +		(SPCR_TSEC1EP << SPCR_TSEC1EP_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSEC2EP /* TSEC2 Emergency priority */ +		(SPCR_TSEC2EP << SPCR_TSEC2EP_SHIFT) | +#endif +		0; +	__be32 spcr_val = +#ifdef CONFIG_SYS_SPCR_OPT +		(CONFIG_SYS_SPCR_OPT << SPCR_OPT_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSECEP /* all eTSEC's Emergency priority */ +		(CONFIG_SYS_SPCR_TSECEP << SPCR_TSECEP_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSEC1EP /* TSEC1 Emergency priority */ +		(CONFIG_SYS_SPCR_TSEC1EP << SPCR_TSEC1EP_SHIFT) | +#endif +#ifdef CONFIG_SYS_SPCR_TSEC2EP /* TSEC2 Emergency priority */ +		(CONFIG_SYS_SPCR_TSEC2EP << SPCR_TSEC2EP_SHIFT) | +#endif +		0; +	__be32 sccr_mask = +#ifdef CONFIG_SYS_SCCR_ENCCM /* Encryption clock mode */ +		(SCCR_ENCCM << SCCR_ENCCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_PCICM /* PCI & DMA clock mode */ +		(SCCR_PCICM << SCCR_PCICM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSECCM /* all TSEC's clock mode */ +		(SCCR_TSECCM << SCCR_TSECCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC1CM /* TSEC1 clock mode */ +		(SCCR_TSEC1CM << SCCR_TSEC1CM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC2CM /* TSEC2 clock mode */ +		(SCCR_TSEC2CM << SCCR_TSEC2CM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC1ON /* TSEC1 clock switch */ +		(SCCR_TSEC1ON << SCCR_TSEC1ON_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC2ON /* TSEC2 clock switch */ +		(SCCR_TSEC2ON << SCCR_TSEC2ON_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_USBMPHCM /* USB MPH clock mode */ +		(SCCR_USBMPHCM << SCCR_USBMPHCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_USBDRCM /* USB DR clock mode */ +		(SCCR_USBDRCM << SCCR_USBDRCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_SATACM /* SATA controller clock mode */ +		(SCCR_SATACM << SCCR_SATACM_SHIFT) | +#endif +		0; +	__be32 sccr_val = +#ifdef CONFIG_SYS_SCCR_ENCCM /* Encryption clock mode */ +		(CONFIG_SYS_SCCR_ENCCM << SCCR_ENCCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_PCICM /* PCI & DMA clock mode */ +		(CONFIG_SYS_SCCR_PCICM << SCCR_PCICM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSECCM /* all TSEC's clock mode */ +		(CONFIG_SYS_SCCR_TSECCM << SCCR_TSECCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC1CM /* TSEC1 clock mode */ +		(CONFIG_SYS_SCCR_TSEC1CM << SCCR_TSEC1CM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC2CM /* TSEC2 clock mode */ +		(CONFIG_SYS_SCCR_TSEC2CM << SCCR_TSEC2CM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC1ON /* TSEC1 clock switch */ +		(CONFIG_SYS_SCCR_TSEC1ON << SCCR_TSEC1ON_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_TSEC2ON /* TSEC2 clock switch */ +		(CONFIG_SYS_SCCR_TSEC2ON << SCCR_TSEC2ON_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_USBMPHCM /* USB MPH clock mode */ +		(CONFIG_SYS_SCCR_USBMPHCM << SCCR_USBMPHCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_USBDRCM /* USB DR clock mode */ +		(CONFIG_SYS_SCCR_USBDRCM << SCCR_USBDRCM_SHIFT) | +#endif +#ifdef CONFIG_SYS_SCCR_SATACM /* SATA controller clock mode */ +		(CONFIG_SYS_SCCR_SATACM << SCCR_SATACM_SHIFT) | +#endif +		0; +	__be32 lcrr_mask = +#ifdef CONFIG_SYS_LCRR_DBYP /* PLL bypass */ +		LCRR_DBYP | +#endif +#ifdef CONFIG_SYS_LCRR_EADC /* external address delay */ +		LCRR_EADC | +#endif +#ifdef CONFIG_SYS_LCRR_CLKDIV /* system clock divider */ +		LCRR_CLKDIV | +#endif +		0; +	__be32 lcrr_val = +#ifdef CONFIG_SYS_LCRR_DBYP /* PLL bypass */ +		CONFIG_SYS_LCRR_DBYP | +#endif +#ifdef CONFIG_SYS_LCRR_EADC +		CONFIG_SYS_LCRR_EADC | +#endif +#ifdef CONFIG_SYS_LCRR_CLKDIV /* system clock divider */ +		CONFIG_SYS_LCRR_CLKDIV | +#endif +		0; + +	/* 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)); + +	/* system performance tweaking */ +	clrsetbits_be32(&im->arbiter.acr, acr_mask, acr_val); + +	clrsetbits_be32(&im->sysconf.spcr, spcr_mask, spcr_val); + +	clrsetbits_be32(&im->clk.sccr, sccr_mask, sccr_val); + +	/* RSR - Reset Status Register - clear all status (4.6.1.3) */ +	gd->reset_status = __raw_readl(&im->reset.rsr); +	__raw_writel(~(RSR_RES), &im->reset.rsr); + +	/* AER - Arbiter Event Register - store status */ +	gd->arbiter_event_attributes = __raw_readl(&im->arbiter.aeatr); +	gd->arbiter_event_address = __raw_readl(&im->arbiter.aeadr); + +	/* +	 * RMR - Reset Mode Register +	 * contains checkstop reset enable (4.6.1.4) +	 */ +	__raw_writel(RMR_CSRE & (1<<RMR_CSRE_SHIFT), &im->reset.rmr); + +	/* LCRR - Clock Ratio Register (10.3.1.16) +	 * write, read, and isync per MPC8379ERM rev.1 CLKDEV field description +	 */ +	clrsetbits_be32(&im->lbus.lcrr, lcrr_mask, lcrr_val); +	__raw_readl(&im->lbus.lcrr); +	isync(); + +	/* Enable Time Base & Decrementer ( so we will have udelay() )*/ +	setbits_be32(&im->sysconf.spcr, SPCR_TBEN); + +	/* System General Purpose Register */ +#ifdef CONFIG_SYS_SICRH +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC8313) +	/* regarding to MPC34x manual rev.1 bits 28..29 must be preserved */ +	__raw_writel((im->sysconf.sicrh & 0x0000000C) | CONFIG_SYS_SICRH, +		     &im->sysconf.sicrh); +#else +	__raw_writel(CONFIG_SYS_SICRH, &im->sysconf.sicrh); +#endif +#endif +#ifdef CONFIG_SYS_SICRL +	__raw_writel(CONFIG_SYS_SICRL, &im->sysconf.sicrl); +#endif +#ifdef CONFIG_SYS_DDRCDR /* DDR control driver register */ +	__raw_writel(CONFIG_SYS_DDRCDR, &im->sysconf.ddrcdr); +#endif +#ifdef CONFIG_SYS_OBIR /* Output buffer impedance register */ +	__raw_writel(CONFIG_SYS_OBIR, &im->sysconf.obir); +#endif + +#ifdef CONFIG_QE +	/* Config QE ioports */ +	config_qe_ioports(); +#endif + +	/* +	 * Memory Controller: +	 */ + +	/* Map banks 0 and 1 to the FLASH banks 0 and 1 at preliminary +	 * addresses - these have to be modified later when FLASH size +	 * has been determined +	 */ + +#if defined(CONFIG_SYS_BR0_PRELIM)  \ +	&& defined(CONFIG_SYS_OR0_PRELIM) \ +	&& defined(CONFIG_SYS_LBLAWBAR0_PRELIM) \ +	&& defined(CONFIG_SYS_LBLAWAR0_PRELIM) +	im->lbus.bank[0].br = CONFIG_SYS_BR0_PRELIM; +	im->lbus.bank[0].or = CONFIG_SYS_OR0_PRELIM; +	im->sysconf.lblaw[0].bar = CONFIG_SYS_LBLAWBAR0_PRELIM; +	im->sysconf.lblaw[0].ar = CONFIG_SYS_LBLAWAR0_PRELIM; +#else +#error	CONFIG_SYS_BR0_PRELIM, CONFIG_SYS_OR0_PRELIM, CONFIG_SYS_LBLAWBAR0_PRELIM & CONFIG_SYS_LBLAWAR0_PRELIM must be defined +#endif + +#if defined(CONFIG_SYS_BR1_PRELIM) && defined(CONFIG_SYS_OR1_PRELIM) +	im->lbus.bank[1].br = CONFIG_SYS_BR1_PRELIM; +	im->lbus.bank[1].or = CONFIG_SYS_OR1_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR1_PRELIM) && defined(CONFIG_SYS_LBLAWAR1_PRELIM) +	im->sysconf.lblaw[1].bar = CONFIG_SYS_LBLAWBAR1_PRELIM; +	im->sysconf.lblaw[1].ar = CONFIG_SYS_LBLAWAR1_PRELIM; +#endif +#if defined(CONFIG_SYS_BR2_PRELIM) && defined(CONFIG_SYS_OR2_PRELIM) +	im->lbus.bank[2].br = CONFIG_SYS_BR2_PRELIM; +	im->lbus.bank[2].or = CONFIG_SYS_OR2_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR2_PRELIM) && defined(CONFIG_SYS_LBLAWAR2_PRELIM) +	im->sysconf.lblaw[2].bar = CONFIG_SYS_LBLAWBAR2_PRELIM; +	im->sysconf.lblaw[2].ar = CONFIG_SYS_LBLAWAR2_PRELIM; +#endif +#if defined(CONFIG_SYS_BR3_PRELIM) && defined(CONFIG_SYS_OR3_PRELIM) +	im->lbus.bank[3].br = CONFIG_SYS_BR3_PRELIM; +	im->lbus.bank[3].or = CONFIG_SYS_OR3_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR3_PRELIM) && defined(CONFIG_SYS_LBLAWAR3_PRELIM) +	im->sysconf.lblaw[3].bar = CONFIG_SYS_LBLAWBAR3_PRELIM; +	im->sysconf.lblaw[3].ar = CONFIG_SYS_LBLAWAR3_PRELIM; +#endif +#if defined(CONFIG_SYS_BR4_PRELIM) && defined(CONFIG_SYS_OR4_PRELIM) +	im->lbus.bank[4].br = CONFIG_SYS_BR4_PRELIM; +	im->lbus.bank[4].or = CONFIG_SYS_OR4_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR4_PRELIM) && defined(CONFIG_SYS_LBLAWAR4_PRELIM) +	im->sysconf.lblaw[4].bar = CONFIG_SYS_LBLAWBAR4_PRELIM; +	im->sysconf.lblaw[4].ar = CONFIG_SYS_LBLAWAR4_PRELIM; +#endif +#if defined(CONFIG_SYS_BR5_PRELIM) && defined(CONFIG_SYS_OR5_PRELIM) +	im->lbus.bank[5].br = CONFIG_SYS_BR5_PRELIM; +	im->lbus.bank[5].or = CONFIG_SYS_OR5_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR5_PRELIM) && defined(CONFIG_SYS_LBLAWAR5_PRELIM) +	im->sysconf.lblaw[5].bar = CONFIG_SYS_LBLAWBAR5_PRELIM; +	im->sysconf.lblaw[5].ar = CONFIG_SYS_LBLAWAR5_PRELIM; +#endif +#if defined(CONFIG_SYS_BR6_PRELIM) && defined(CONFIG_SYS_OR6_PRELIM) +	im->lbus.bank[6].br = CONFIG_SYS_BR6_PRELIM; +	im->lbus.bank[6].or = CONFIG_SYS_OR6_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR6_PRELIM) && defined(CONFIG_SYS_LBLAWAR6_PRELIM) +	im->sysconf.lblaw[6].bar = CONFIG_SYS_LBLAWBAR6_PRELIM; +	im->sysconf.lblaw[6].ar = CONFIG_SYS_LBLAWAR6_PRELIM; +#endif +#if defined(CONFIG_SYS_BR7_PRELIM) && defined(CONFIG_SYS_OR7_PRELIM) +	im->lbus.bank[7].br = CONFIG_SYS_BR7_PRELIM; +	im->lbus.bank[7].or = CONFIG_SYS_OR7_PRELIM; +#endif +#if defined(CONFIG_SYS_LBLAWBAR7_PRELIM) && defined(CONFIG_SYS_LBLAWAR7_PRELIM) +	im->sysconf.lblaw[7].bar = CONFIG_SYS_LBLAWBAR7_PRELIM; +	im->sysconf.lblaw[7].ar = CONFIG_SYS_LBLAWAR7_PRELIM; +#endif +#ifdef CONFIG_SYS_GPIO1_PRELIM +	im->gpio[0].dat = CONFIG_SYS_GPIO1_DAT; +	im->gpio[0].dir = CONFIG_SYS_GPIO1_DIR; +#endif +#ifdef CONFIG_SYS_GPIO2_PRELIM +	im->gpio[1].dat = CONFIG_SYS_GPIO2_DAT; +	im->gpio[1].dir = CONFIG_SYS_GPIO2_DIR; +#endif +#ifdef CONFIG_USB_EHCI_FSL +#ifndef CONFIG_MPC834x +	uint32_t temp; +	struct usb_ehci *ehci = (struct usb_ehci *)CONFIG_SYS_MPC8xxx_USB_ADDR; + +	/* Configure interface. */ +	setbits_be32(&ehci->control, REFSEL_16MHZ | UTMI_PHY_EN); + +	/* Wait for clock to stabilize */ +	do { +		temp = __raw_readl(&ehci->control); +		udelay(1000); +	} while (!(temp & PHY_CLK_VALID)); +#endif +#endif +} + +int cpu_init_r (void) +{ +#ifdef CONFIG_QE +	uint qe_base = CONFIG_SYS_IMMR + 0x00100000; /* QE immr base */ + +	qe_init(qe_base); +	qe_reset(); +#endif +	return 0; +} + +/* + * Print out the bus arbiter event + */ +#if defined(CONFIG_DISPLAY_AER_FULL) +static int print_83xx_arb_event(int force) +{ +	static char* event[] = { +		"Address Time Out", +		"Data Time Out", +		"Address Only Transfer Type", +		"External Control Word Transfer Type", +		"Reserved Transfer Type", +		"Transfer Error", +		"reserved", +		"reserved" +	}; +	static char* master[] = { +		"e300 Core Data Transaction", +		"reserved", +		"e300 Core Instruction Fetch", +		"reserved", +		"TSEC1", +		"TSEC2", +		"USB MPH", +		"USB DR", +		"Encryption Core", +		"I2C Boot Sequencer", +		"JTAG", +		"reserved", +		"eSDHC", +		"PCI1", +		"PCI2", +		"DMA", +		"QUICC Engine 00", +		"QUICC Engine 01", +		"QUICC Engine 10", +		"QUICC Engine 11", +		"reserved", +		"reserved", +		"reserved", +		"reserved", +		"SATA1", +		"SATA2", +		"SATA3", +		"SATA4", +		"reserved", +		"PCI Express 1", +		"PCI Express 2", +		"TDM-DMAC" +	}; +	static char *transfer[] = { +		"Address-only, Clean Block", +		"Address-only, lwarx reservation set", +		"Single-beat or Burst write", +		"reserved", +		"Address-only, Flush Block", +		"reserved", +		"Burst write", +		"reserved", +		"Address-only, sync", +		"Address-only, tlbsync", +		"Single-beat or Burst read", +		"Single-beat or Burst read", +		"Address-only, Kill Block", +		"Address-only, icbi", +		"Burst read", +		"reserved", +		"Address-only, eieio", +		"reserved", +		"Single-beat write", +		"reserved", +		"ecowx - Illegal single-beat write", +		"reserved", +		"reserved", +		"reserved", +		"Address-only, TLB Invalidate", +		"reserved", +		"Single-beat or Burst read", +		"reserved", +		"eciwx - Illegal single-beat read", +		"reserved", +		"Burst read", +		"reserved" +	}; + +	int etype = (gd->arbiter_event_attributes & AEATR_EVENT) +	            >> AEATR_EVENT_SHIFT; +	int mstr_id = (gd->arbiter_event_attributes & AEATR_MSTR_ID) +	              >> AEATR_MSTR_ID_SHIFT; +	int tbst = (gd->arbiter_event_attributes & AEATR_TBST) +	           >> AEATR_TBST_SHIFT; +	int tsize = (gd->arbiter_event_attributes & AEATR_TSIZE) +	            >> AEATR_TSIZE_SHIFT; +	int ttype = (gd->arbiter_event_attributes & AEATR_TTYPE) +	            >> AEATR_TTYPE_SHIFT; + +	if (!force && !gd->arbiter_event_address) +		return 0; + +	puts("Arbiter Event Status:\n"); +	printf("       Event Address: 0x%08lX\n", gd->arbiter_event_address); +	printf("       Event Type:    0x%1x  = %s\n", etype, event[etype]); +	printf("       Master ID:     0x%02x = %s\n", mstr_id, master[mstr_id]); +	printf("       Transfer Size: 0x%1x  = %d bytes\n", (tbst<<3) | tsize, +				tbst ? (tsize ? tsize : 8) : 16 + 8 * tsize); +	printf("       Transfer Type: 0x%02x = %s\n", ttype, transfer[ttype]); + +	return gd->arbiter_event_address; +} + +#elif defined(CONFIG_DISPLAY_AER_BRIEF) + +static int print_83xx_arb_event(int force) +{ +	if (!force && !gd->arbiter_event_address) +		return 0; + +	printf("Arbiter Event Status: AEATR=0x%08lX, AEADR=0x%08lX\n", +		gd->arbiter_event_attributes, gd->arbiter_event_address); + +	return gd->arbiter_event_address; +} +#endif /* CONFIG_DISPLAY_AER_xxxx */ + +/* + * Figure out the cause of the reset + */ +int prt_83xx_rsr(void) +{ +	static struct { +		ulong mask; +		char *desc; +	} bits[] = { +		{ +		RSR_SWSR, "Software Soft"}, { +		RSR_SWHR, "Software Hard"}, { +		RSR_JSRS, "JTAG Soft"}, { +		RSR_CSHR, "Check Stop"}, { +		RSR_SWRS, "Software Watchdog"}, { +		RSR_BMRS, "Bus Monitor"}, { +		RSR_SRS,  "External/Internal Soft"}, { +		RSR_HRS,  "External/Internal Hard"} +	}; +	static int n = sizeof bits / sizeof bits[0]; +	ulong rsr = gd->reset_status; +	int i; +	char *sep; + +	puts("Reset Status:"); + +	sep = " "; +	for (i = 0; i < n; i++) +		if (rsr & bits[i].mask) { +			printf("%s%s", sep, bits[i].desc); +			sep = ", "; +		} +	puts("\n"); + +#if defined(CONFIG_DISPLAY_AER_FULL) || defined(CONFIG_DISPLAY_AER_BRIEF) +	print_83xx_arb_event(rsr & RSR_BMRS); +#endif +	puts("\n"); + +	return 0; +} diff --git a/arch/powerpc/cpu/mpc83xx/ecc.c b/arch/powerpc/cpu/mpc83xx/ecc.c new file mode 100644 index 000000000..f3942b411 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/ecc.c @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2007 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on the contribution of Marian Balakowicz <m8@semihalf.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. + */ + +#include <common.h> +#include <mpc83xx.h> +#include <command.h> + +#if defined(CONFIG_DDR_ECC) && defined(CONFIG_DDR_ECC_CMD) +void ecc_print_status(void) +{ +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	volatile ddr83xx_t *ddr = &immap->ddr; + +	printf("\nECC mode: %s\n\n", +	       (ddr->sdram_cfg & SDRAM_CFG_ECC_EN) ? "ON" : "OFF"); + +	/* Interrupts */ +	printf("Memory Error Interrupt Enable:\n"); +	printf("  Multiple-Bit Error Interrupt Enable: %d\n", +	       (ddr->err_int_en & ECC_ERR_INT_EN_MBEE) ? 1 : 0); +	printf("  Single-Bit Error Interrupt Enable: %d\n", +	       (ddr->err_int_en & ECC_ERR_INT_EN_SBEE) ? 1 : 0); +	printf("  Memory Select Error Interrupt Enable: %d\n\n", +	       (ddr->err_int_en & ECC_ERR_INT_EN_MSEE) ? 1 : 0); + +	/* Error disable */ +	printf("Memory Error Disable:\n"); +	printf("  Multiple-Bit Error Disable: %d\n", +	       (ddr->err_disable & ECC_ERROR_DISABLE_MBED) ? 1 : 0); +	printf("  Sinle-Bit Error Disable: %d\n", +	       (ddr->err_disable & ECC_ERROR_DISABLE_SBED) ? 1 : 0); +	printf("  Memory Select Error Disable: %d\n\n", +	       (ddr->err_disable & ECC_ERROR_DISABLE_MSED) ? 1 : 0); + +	/* Error injection */ +	printf("Memory Data Path Error Injection Mask High/Low: %08x %08x\n", +	       ddr->data_err_inject_hi, ddr->data_err_inject_lo); + +	printf("Memory Data Path Error Injection Mask ECC:\n"); +	printf("  ECC Mirror Byte: %d\n", +	       (ddr->ecc_err_inject & ECC_ERR_INJECT_EMB) ? 1 : 0); +	printf("  ECC Injection Enable: %d\n", +	       (ddr->ecc_err_inject & ECC_ERR_INJECT_EIEN) ? 1 : 0); +	printf("  ECC Error Injection Mask: 0x%02x\n\n", +	       ddr->ecc_err_inject & ECC_ERR_INJECT_EEIM); + +	/* SBE counter/threshold */ +	printf("Memory Single-Bit Error Management (0..255):\n"); +	printf("  Single-Bit Error Threshold: %d\n", +	       (ddr->err_sbe & ECC_ERROR_MAN_SBET) >> ECC_ERROR_MAN_SBET_SHIFT); +	printf("  Single-Bit Error Counter: %d\n\n", +	       (ddr->err_sbe & ECC_ERROR_MAN_SBEC) >> ECC_ERROR_MAN_SBEC_SHIFT); + +	/* Error detect */ +	printf("Memory Error Detect:\n"); +	printf("  Multiple Memory Errors: %d\n", +	       (ddr->err_detect & ECC_ERROR_DETECT_MME) ? 1 : 0); +	printf("  Multiple-Bit Error: %d\n", +	       (ddr->err_detect & ECC_ERROR_DETECT_MBE) ? 1 : 0); +	printf("  Single-Bit Error: %d\n", +	       (ddr->err_detect & ECC_ERROR_DETECT_SBE) ? 1 : 0); +	printf("  Memory Select Error: %d\n\n", +	       (ddr->err_detect & ECC_ERROR_DETECT_MSE) ? 1 : 0); + +	/* Capture data */ +	printf("Memory Error Address Capture: 0x%08x\n", ddr->capture_address); +	printf("Memory Data Path Read Capture High/Low: %08x %08x\n", +	       ddr->capture_data_hi, ddr->capture_data_lo); +	printf("Memory Data Path Read Capture ECC: 0x%02x\n\n", +	       ddr->capture_ecc & CAPTURE_ECC_ECE); + +	printf("Memory Error Attributes Capture:\n"); +	printf(" Data Beat Number: %d\n", +	       (ddr->capture_attributes & ECC_CAPT_ATTR_BNUM) >> +	       ECC_CAPT_ATTR_BNUM_SHIFT); +	printf("  Transaction Size: %d\n", +	       (ddr->capture_attributes & ECC_CAPT_ATTR_TSIZ) >> +	       ECC_CAPT_ATTR_TSIZ_SHIFT); +	printf("  Transaction Source: %d\n", +	       (ddr->capture_attributes & ECC_CAPT_ATTR_TSRC) >> +	       ECC_CAPT_ATTR_TSRC_SHIFT); +	printf("  Transaction Type: %d\n", +	       (ddr->capture_attributes & ECC_CAPT_ATTR_TTYP) >> +	       ECC_CAPT_ATTR_TTYP_SHIFT); +	printf("  Error Information Valid: %d\n\n", +	       ddr->capture_attributes & ECC_CAPT_ATTR_VLD); +} + +int do_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	volatile ddr83xx_t *ddr = &immap->ddr; +	volatile u32 val; +	u64 *addr; +	u32 count; +	register u64 *i; +	u32 ret[2]; +	u32 pattern[2]; +	u32 writeback[2]; + +	/* The pattern is written into memory to generate error */ +	pattern[0] = 0xfedcba98UL; +	pattern[1] = 0x76543210UL; + +	/* After injecting error, re-initialize the memory with the value */ +	writeback[0] = 0x01234567UL; +	writeback[1] = 0x89abcdefUL; + +	if (argc > 4) { +		cmd_usage(cmdtp); +		return 1; +	} + +	if (argc == 2) { +		if (strcmp(argv[1], "status") == 0) { +			ecc_print_status(); +			return 0; +		} else if (strcmp(argv[1], "captureclear") == 0) { +			ddr->capture_address = 0; +			ddr->capture_data_hi = 0; +			ddr->capture_data_lo = 0; +			ddr->capture_ecc = 0; +			ddr->capture_attributes = 0; +			return 0; +		} +	} +	if (argc == 3) { +		if (strcmp(argv[1], "sbecnt") == 0) { +			val = simple_strtoul(argv[2], NULL, 10); +			if (val > 255) { +				printf("Incorrect Counter value, " +				       "should be 0..255\n"); +				return 1; +			} + +			val = (val << ECC_ERROR_MAN_SBEC_SHIFT); +			val |= (ddr->err_sbe & ECC_ERROR_MAN_SBET); + +			ddr->err_sbe = val; +			return 0; +		} else if (strcmp(argv[1], "sbethr") == 0) { +			val = simple_strtoul(argv[2], NULL, 10); +			if (val > 255) { +				printf("Incorrect Counter value, " +				       "should be 0..255\n"); +				return 1; +			} + +			val = (val << ECC_ERROR_MAN_SBET_SHIFT); +			val |= (ddr->err_sbe & ECC_ERROR_MAN_SBEC); + +			ddr->err_sbe = val; +			return 0; +		} else if (strcmp(argv[1], "errdisable") == 0) { +			val = ddr->err_disable; + +			if (strcmp(argv[2], "+sbe") == 0) { +				val |= ECC_ERROR_DISABLE_SBED; +			} else if (strcmp(argv[2], "+mbe") == 0) { +				val |= ECC_ERROR_DISABLE_MBED; +			} else if (strcmp(argv[2], "+mse") == 0) { +				val |= ECC_ERROR_DISABLE_MSED; +			} else if (strcmp(argv[2], "+all") == 0) { +				val |= (ECC_ERROR_DISABLE_SBED | +					ECC_ERROR_DISABLE_MBED | +					ECC_ERROR_DISABLE_MSED); +			} else if (strcmp(argv[2], "-sbe") == 0) { +				val &= ~ECC_ERROR_DISABLE_SBED; +			} else if (strcmp(argv[2], "-mbe") == 0) { +				val &= ~ECC_ERROR_DISABLE_MBED; +			} else if (strcmp(argv[2], "-mse") == 0) { +				val &= ~ECC_ERROR_DISABLE_MSED; +			} else if (strcmp(argv[2], "-all") == 0) { +				val &= ~(ECC_ERROR_DISABLE_SBED | +					 ECC_ERROR_DISABLE_MBED | +					 ECC_ERROR_DISABLE_MSED); +			} else { +				printf("Incorrect err_disable field\n"); +				return 1; +			} + +			ddr->err_disable = val; +			__asm__ __volatile__("sync"); +			__asm__ __volatile__("isync"); +			return 0; +		} else if (strcmp(argv[1], "errdetectclr") == 0) { +			val = ddr->err_detect; + +			if (strcmp(argv[2], "mme") == 0) { +				val |= ECC_ERROR_DETECT_MME; +			} else if (strcmp(argv[2], "sbe") == 0) { +				val |= ECC_ERROR_DETECT_SBE; +			} else if (strcmp(argv[2], "mbe") == 0) { +				val |= ECC_ERROR_DETECT_MBE; +			} else if (strcmp(argv[2], "mse") == 0) { +				val |= ECC_ERROR_DETECT_MSE; +			} else if (strcmp(argv[2], "all") == 0) { +				val |= (ECC_ERROR_DETECT_MME | +					ECC_ERROR_DETECT_MBE | +					ECC_ERROR_DETECT_SBE | +					ECC_ERROR_DETECT_MSE); +			} else { +				printf("Incorrect err_detect field\n"); +				return 1; +			} + +			ddr->err_detect = val; +			return 0; +		} else if (strcmp(argv[1], "injectdatahi") == 0) { +			val = simple_strtoul(argv[2], NULL, 16); + +			ddr->data_err_inject_hi = val; +			return 0; +		} else if (strcmp(argv[1], "injectdatalo") == 0) { +			val = simple_strtoul(argv[2], NULL, 16); + +			ddr->data_err_inject_lo = val; +			return 0; +		} else if (strcmp(argv[1], "injectecc") == 0) { +			val = simple_strtoul(argv[2], NULL, 16); +			if (val > 0xff) { +				printf("Incorrect ECC inject mask, " +				       "should be 0x00..0xff\n"); +				return 1; +			} +			val |= (ddr->ecc_err_inject & ~ECC_ERR_INJECT_EEIM); + +			ddr->ecc_err_inject = val; +			return 0; +		} else if (strcmp(argv[1], "inject") == 0) { +			val = ddr->ecc_err_inject; + +			if (strcmp(argv[2], "en") == 0) +				val |= ECC_ERR_INJECT_EIEN; +			else if (strcmp(argv[2], "dis") == 0) +				val &= ~ECC_ERR_INJECT_EIEN; +			else +				printf("Incorrect command\n"); + +			ddr->ecc_err_inject = val; +			__asm__ __volatile__("sync"); +			__asm__ __volatile__("isync"); +			return 0; +		} else if (strcmp(argv[1], "mirror") == 0) { +			val = ddr->ecc_err_inject; + +			if (strcmp(argv[2], "en") == 0) +				val |= ECC_ERR_INJECT_EMB; +			else if (strcmp(argv[2], "dis") == 0) +				val &= ~ECC_ERR_INJECT_EMB; +			else +				printf("Incorrect command\n"); + +			ddr->ecc_err_inject = val; +			return 0; +		} +	} +	if (argc == 4) { +		if (strcmp(argv[1], "testdw") == 0) { +			addr = (u64 *) simple_strtoul(argv[2], NULL, 16); +			count = simple_strtoul(argv[3], NULL, 16); + +			if ((u32) addr % 8) { +				printf("Address not alligned on " +				       "double word boundary\n"); +				return 1; +			} +			disable_interrupts(); + +			for (i = addr; i < addr + count; i++) { + +				/* enable injects */ +				ddr->ecc_err_inject |= ECC_ERR_INJECT_EIEN; +				__asm__ __volatile__("sync"); +				__asm__ __volatile__("isync"); + +				/* write memory location injecting errors */ +				ppcDWstore((u32 *) i, pattern); +				__asm__ __volatile__("sync"); + +				/* disable injects */ +				ddr->ecc_err_inject &= ~ECC_ERR_INJECT_EIEN; +				__asm__ __volatile__("sync"); +				__asm__ __volatile__("isync"); + +				/* read data, this generates ECC error */ +				ppcDWload((u32 *) i, ret); +				__asm__ __volatile__("sync"); + +				/* re-initialize memory, double word write the location again, +				 * generates new ECC code this time */ +				ppcDWstore((u32 *) i, writeback); +				__asm__ __volatile__("sync"); +			} +			enable_interrupts(); +			return 0; +		} +		if (strcmp(argv[1], "testword") == 0) { +			addr = (u64 *) simple_strtoul(argv[2], NULL, 16); +			count = simple_strtoul(argv[3], NULL, 16); + +			if ((u32) addr % 8) { +				printf("Address not alligned on " +				       "double word boundary\n"); +				return 1; +			} +			disable_interrupts(); + +			for (i = addr; i < addr + count; i++) { + +				/* enable injects */ +				ddr->ecc_err_inject |= ECC_ERR_INJECT_EIEN; +				__asm__ __volatile__("sync"); +				__asm__ __volatile__("isync"); + +				/* write memory location injecting errors */ +				*(u32 *) i = 0xfedcba98UL; +				__asm__ __volatile__("sync"); + +				/* sub double word write, +				 * bus will read-modify-write, +				 * generates ECC error */ +				*((u32 *) i + 1) = 0x76543210UL; +				__asm__ __volatile__("sync"); + +				/* disable injects */ +				ddr->ecc_err_inject &= ~ECC_ERR_INJECT_EIEN; +				__asm__ __volatile__("sync"); +				__asm__ __volatile__("isync"); + +				/* re-initialize memory, +				 * double word write the location again, +				 * generates new ECC code this time */ +				ppcDWstore((u32 *) i, writeback); +				__asm__ __volatile__("sync"); +			} +			enable_interrupts(); +			return 0; +		} +	} +	cmd_usage(cmdtp); +	return 1; +} + +U_BOOT_CMD(ecc, 4, 0, do_ecc, +	   "support for DDR ECC features", +	   "status              - print out status info\n" +	   "ecc captureclear        - clear capture regs data\n" +	   "ecc sbecnt <val>        - set Single-Bit Error counter\n" +	   "ecc sbethr <val>        - set Single-Bit Threshold\n" +	   "ecc errdisable <flag>   - clear/set disable Memory Error Disable, flag:\n" +	   "  [-|+]sbe - Single-Bit Error\n" +	   "  [-|+]mbe - Multiple-Bit Error\n" +	   "  [-|+]mse - Memory Select Error\n" +	   "  [-|+]all - all errors\n" +	   "ecc errdetectclr <flag> - clear Memory Error Detect, flag:\n" +	   "  mme - Multiple Memory Errors\n" +	   "  sbe - Single-Bit Error\n" +	   "  mbe - Multiple-Bit Error\n" +	   "  mse - Memory Select Error\n" +	   "  all - all errors\n" +	   "ecc injectdatahi <hi>  - set Memory Data Path Error Injection Mask High\n" +	   "ecc injectdatalo <lo>  - set Memory Data Path Error Injection Mask Low\n" +	   "ecc injectecc <ecc>    - set ECC Error Injection Mask\n" +	   "ecc inject <en|dis>    - enable/disable error injection\n" +	   "ecc mirror <en|dis>    - enable/disable mirror byte\n" +	   "ecc testdw <addr> <cnt>  - test mem region with double word access:\n" +	   "  - enables injects\n" +	   "  - writes pattern injecting errors with double word access\n" +	   "  - disables injects\n" +	   "  - reads pattern back with double word access, generates error\n" +	   "  - re-inits memory\n" +	   "ecc testword <addr> <cnt>  - test mem region with word access:\n" +	   "  - enables injects\n" +	   "  - writes pattern injecting errors with word access\n" +	   "  - writes pattern with word access, generates error\n" +	   "  - disables injects\n" "  - re-inits memory"); +#endif diff --git a/arch/powerpc/cpu/mpc83xx/fdt.c b/arch/powerpc/cpu/mpc83xx/fdt.c new file mode 100644 index 000000000..daf73a6e5 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/fdt.c @@ -0,0 +1,143 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <libfdt.h> +#include <fdt_support.h> +#include <asm/processor.h> + +extern void ft_qe_setup(void *blob); + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_BOOTCOUNT_LIMIT) && defined(CONFIG_MPC8360) +#include <asm/immap_qe.h> + +void fdt_fixup_muram (void *blob) +{ +	ulong data[2]; + +	data[0] = 0; +	data[1] = QE_MURAM_SIZE - 2 * sizeof(unsigned long); +	do_fixup_by_compat(blob, "fsl,qe-muram-data", "reg", +			data, sizeof (data), 0); +} +#endif + +void ft_cpu_setup(void *blob, bd_t *bd) +{ +	immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; +	int spridr = immr->sysconf.spridr; + +	/* +	 * delete crypto node if not on an E-processor +	 * initial revisions of the MPC834xE/6xE have the original SEC 2.0. +	 * EA revisions got the SEC uprevved to 2.4 but since the default device +	 * tree contains SEC 2.0 properties we uprev them here. +	 */ +	if (!IS_E_PROCESSOR(spridr)) +		fdt_fixup_crypto_node(blob, 0); +	else if (IS_E_PROCESSOR(spridr) && +		 (SPR_FAMILY(spridr) == SPR_834X_FAMILY || +		  SPR_FAMILY(spridr) == SPR_836X_FAMILY) && +		 REVID_MAJOR(spridr) >= 2) +		fdt_fixup_crypto_node(blob, 0x0204); + +#if defined(CONFIG_HAS_ETH0) || defined(CONFIG_HAS_ETH1) ||\ +    defined(CONFIG_HAS_ETH2) || defined(CONFIG_HAS_ETH3) ||\ +    defined(CONFIG_HAS_ETH4) || defined(CONFIG_HAS_ETH5) +	fdt_fixup_ethernet(blob); +#ifdef CONFIG_MPC8313 +	/* +	* mpc8313e erratum IPIC1 swapped TSEC interrupt ID numbers on rev. 1 +	* h/w (see AN3545).  The base device tree in use has rev. 1 ID numbers, +	* so if on Rev. 2 (and higher) h/w, we fix them up here +	*/ +	if (REVID_MAJOR(immr->sysconf.spridr) >= 2) { +		int nodeoffset, path; +		const char *prop; + +		nodeoffset = fdt_path_offset(blob, "/aliases"); +		if (nodeoffset >= 0) { +#if defined(CONFIG_HAS_ETH0) +			prop = fdt_getprop(blob, nodeoffset, "ethernet0", NULL); +			if (prop) { +				u32 tmp[] = { 32, 0x8, 33, 0x8, 34, 0x8 }; + +				path = fdt_path_offset(blob, prop); +				prop = fdt_getprop(blob, path, "interrupts", 0); +				if (prop) +					fdt_setprop(blob, path, "interrupts", +						    &tmp, sizeof(tmp)); +			} +#endif +#if defined(CONFIG_HAS_ETH1) +			prop = fdt_getprop(blob, nodeoffset, "ethernet1", NULL); +			if (prop) { +				u32 tmp[] = { 35, 0x8, 36, 0x8, 37, 0x8 }; + +				path = fdt_path_offset(blob, prop); +				prop = fdt_getprop(blob, path, "interrupts", 0); +				if (prop) +					fdt_setprop(blob, path, "interrupts", +						    &tmp, sizeof(tmp)); +			} +#endif +		} +	} +#endif +#endif + +	do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, +		"timebase-frequency", (bd->bi_busfreq / 4), 1); +	do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, +		"bus-frequency", bd->bi_busfreq, 1); +	do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, +		"clock-frequency", gd->core_clk, 1); +	do_fixup_by_prop_u32(blob, "device_type", "soc", 4, +		"bus-frequency", bd->bi_busfreq, 1); +	do_fixup_by_compat_u32(blob, "fsl,soc", +		"bus-frequency", bd->bi_busfreq, 1); +	do_fixup_by_compat_u32(blob, "fsl,soc", +		"clock-frequency", bd->bi_busfreq, 1); +	do_fixup_by_compat_u32(blob, "fsl,immr", +		"bus-frequency", bd->bi_busfreq, 1); +	do_fixup_by_compat_u32(blob, "fsl,immr", +		"clock-frequency", bd->bi_busfreq, 1); +#ifdef CONFIG_QE +	ft_qe_setup(blob); +#endif + +#ifdef CONFIG_SYS_NS16550 +	do_fixup_by_compat_u32(blob, "ns16550", +		"clock-frequency", CONFIG_SYS_NS16550_CLK, 1); +#endif + +	fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); + +#if defined(CONFIG_BOOTCOUNT_LIMIT) +	fdt_fixup_muram (blob); +#endif +} diff --git a/arch/powerpc/cpu/mpc83xx/interrupts.c b/arch/powerpc/cpu/mpc83xx/interrupts.c new file mode 100644 index 000000000..faffbaf83 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/interrupts.c @@ -0,0 +1,97 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright 2004 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <mpc83xx.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct irq_action { +	interrupt_handler_t *handler; +	void *arg; +	ulong count; +}; + +int interrupt_init_cpu (unsigned *decrementer_count) +{ +	volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; + +	*decrementer_count = (gd->bus_clk / 4) / CONFIG_SYS_HZ; + +	/* Enable e300 time base */ + +	immr->sysconf.spcr |= 0x00400000; + +	return 0; +} + + +/* + * Handle external interrupts + */ + +void external_interrupt (struct pt_regs *regs) +{ +} + + +/* + * Install and free an interrupt handler. + */ + +void +irq_install_handler (int irq, interrupt_handler_t * handler, void *arg) +{ +} + + +void irq_free_handler (int irq) +{ +} + + +void timer_interrupt_cpu (struct pt_regs *regs) +{ +	/* nothing to do here */ +	return; +} + + +#if defined(CONFIG_CMD_IRQ) + +/* ripped this out of ppc4xx/interrupts.c */ + +/* + * irqinfo - print information about PCI devices + */ + +void +do_irqinfo(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) +{ +} + +#endif diff --git a/arch/powerpc/cpu/mpc83xx/nand_init.c b/arch/powerpc/cpu/mpc83xx/nand_init.c new file mode 100644 index 000000000..38e141a82 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/nand_init.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2004-2008 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <mpc83xx.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Breathe some life into the CPU... + * + * Set up the memory map, + * initialize a bunch of registers, + * initialize the UPM's + */ +void cpu_init_f (volatile immap_t * im) +{ +	int i; + +	/* 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 */ +	for (i = 0; i < sizeof(gd_t); i++) +		((char *)gd)[i] = 0; + +	/* system performance tweaking */ + +#ifdef CONFIG_SYS_ACR_PIPE_DEP +	/* Arbiter pipeline depth */ +	im->arbiter.acr = (im->arbiter.acr & ~ACR_PIPE_DEP) | +			  (CONFIG_SYS_ACR_PIPE_DEP << ACR_PIPE_DEP_SHIFT); +#endif + +#ifdef CONFIG_SYS_ACR_RPTCNT +	/* Arbiter repeat count */ +	im->arbiter.acr = (im->arbiter.acr & ~(ACR_RPTCNT)) | +			  (CONFIG_SYS_ACR_RPTCNT << ACR_RPTCNT_SHIFT); +#endif + +#ifdef CONFIG_SYS_SPCR_OPT +	/* Optimize transactions between CSB and other devices */ +	im->sysconf.spcr = (im->sysconf.spcr & ~SPCR_OPT) | +			   (CONFIG_SYS_SPCR_OPT << SPCR_OPT_SHIFT); +#endif + +	/* Enable Time Base & Decrimenter (so we will have udelay()) */ +	im->sysconf.spcr |= SPCR_TBEN; + +	/* DDR control driver register */ +#ifdef CONFIG_SYS_DDRCDR +	im->sysconf.ddrcdr = CONFIG_SYS_DDRCDR; +#endif +	/* Output buffer impedance register */ +#ifdef CONFIG_SYS_OBIR +	im->sysconf.obir = CONFIG_SYS_OBIR; +#endif + +	/* +	 * Memory Controller: +	 */ + +	/* Map banks 0 and 1 to the FLASH banks 0 and 1 at preliminary +	 * addresses - these have to be modified later when FLASH size +	 * has been determined +	 */ + +#if defined(CONFIG_SYS_NAND_BR_PRELIM)  \ +	&& defined(CONFIG_SYS_NAND_OR_PRELIM) \ +	&& defined(CONFIG_SYS_NAND_LBLAWBAR_PRELIM) \ +	&& defined(CONFIG_SYS_NAND_LBLAWAR_PRELIM) +	im->lbus.bank[0].br = CONFIG_SYS_NAND_BR_PRELIM; +	im->lbus.bank[0].or = CONFIG_SYS_NAND_OR_PRELIM; +	im->sysconf.lblaw[0].bar = CONFIG_SYS_NAND_LBLAWBAR_PRELIM; +	im->sysconf.lblaw[0].ar = CONFIG_SYS_NAND_LBLAWAR_PRELIM; +#else +#error CONFIG_SYS_NAND_BR_PRELIM, CONFIG_SYS_NAND_OR_PRELIM, CONFIG_SYS_NAND_LBLAWBAR_PRELIM & CONFIG_SYS_NAND_LBLAWAR_PRELIM must be defined +#endif +} + +/* + * Get timebase clock frequency (like cpu_clk in Hz) + */ +unsigned long get_tbclk(void) +{ +	return (gd->bus_clk + 3L) / 4L; +} + +void puts(const char *str) +{ +	while (*str) +		putc(*str++); +} diff --git a/arch/powerpc/cpu/mpc83xx/pci.c b/arch/powerpc/cpu/mpc83xx/pci.c new file mode 100644 index 000000000..a42b230ff --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/pci.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) Freescale Semiconductor, Inc. 2007 + * + * Author: Scott Wood <scottwood@freescale.com>, + * with some bits from older board-specific PCI initialization. + * + * 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 <pci.h> + +#if defined(CONFIG_OF_LIBFDT) +#include <libfdt.h> +#include <fdt_support.h> +#endif + +#include <asm/mpc8349_pci.h> + +#define MAX_BUSES 2 + +DECLARE_GLOBAL_DATA_PTR; + +static struct pci_controller pci_hose[MAX_BUSES]; +static int pci_num_buses; + +static void pci_init_bus(int bus, struct pci_region *reg) +{ +	volatile immap_t *immr = (volatile immap_t *)CONFIG_SYS_IMMR; +	volatile pot83xx_t *pot = immr->ios.pot; +	volatile pcictrl83xx_t *pci_ctrl = &immr->pci_ctrl[bus]; +	struct pci_controller *hose = &pci_hose[bus]; +	u32 dev; +	u16 reg16; +	int i; + +	if (bus == 1) +		pot += 3; + +	/* Setup outbound translation windows */ +	for (i = 0; i < 3; i++, reg++, pot++) { +		if (reg->size == 0) +			break; + +		hose->regions[i] = *reg; +		hose->region_count++; + +		pot->potar = reg->bus_start >> 12; +		pot->pobar = reg->phys_start >> 12; +		pot->pocmr = ~(reg->size - 1) >> 12; + +		if (reg->flags & PCI_REGION_IO) +			pot->pocmr |= POCMR_IO; +#ifdef CONFIG_83XX_PCI_STREAMING +		else if (reg->flags & PCI_REGION_PREFETCH) +			pot->pocmr |= POCMR_SE; +#endif + +		if (bus == 1) +			pot->pocmr |= POCMR_DST; + +		pot->pocmr |= POCMR_EN; +	} + +	/* Point inbound translation at RAM */ +	pci_ctrl->pitar1 = 0; +	pci_ctrl->pibar1 = 0; +	pci_ctrl->piebar1 = 0; +	pci_ctrl->piwar1 = PIWAR_EN | PIWAR_PF | PIWAR_RTT_SNOOP | +	                   PIWAR_WTT_SNOOP | (__ilog2(gd->ram_size - 1)); + +	i = hose->region_count++; +	hose->regions[i].bus_start = 0; +	hose->regions[i].phys_start = 0; +	hose->regions[i].size = gd->ram_size; +	hose->regions[i].flags = PCI_REGION_MEM | PCI_REGION_SYS_MEMORY; + +	hose->first_busno = pci_last_busno() + 1; +	hose->last_busno = 0xff; + +	pci_setup_indirect(hose, CONFIG_SYS_IMMR + 0x8300 + bus * 0x80, +	                         CONFIG_SYS_IMMR + 0x8304 + bus * 0x80); + +	pci_register_hose(hose); + +	/* +	 * Write to Command register +	 */ +	reg16 = 0xff; +	dev = PCI_BDF(hose->first_busno, 0, 0); +	pci_hose_read_config_word(hose, dev, PCI_COMMAND, ®16); +	reg16 |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; +	pci_hose_write_config_word(hose, dev, PCI_COMMAND, reg16); + +	/* +	 * Clear non-reserved bits in status register. +	 */ +	pci_hose_write_config_word(hose, dev, PCI_STATUS, 0xffff); +	pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80); +	pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08); + +#ifdef CONFIG_PCI_SCAN_SHOW +	printf("PCI:   Bus Dev VenId DevId Class Int\n"); +#endif +#ifndef CONFIG_PCISLAVE +	/* +	 * Hose scan. +	 */ +	hose->last_busno = pci_hose_scan(hose); +#endif +} + +/* + * The caller must have already set OCCR, and the PCI_LAW BARs + * must have been set to cover all of the requested regions. + * + * If fewer than three regions are requested, then the region + * list is terminated with a region of size 0. + */ +void mpc83xx_pci_init(int num_buses, struct pci_region **reg, int warmboot) +{ +	volatile immap_t *immr = (volatile immap_t *)CONFIG_SYS_IMMR; +	int i; + +	if (num_buses > MAX_BUSES) { +		printf("%d PCI buses requsted, %d supported\n", +		       num_buses, MAX_BUSES); + +		num_buses = MAX_BUSES; +	} + +	pci_num_buses = num_buses; + +	/* +	 * Release PCI RST Output signal. +	 * Power on to RST high must be at least 100 ms as per PCI spec. +	 * On warm boots only 1 ms is required. +	 */ +	udelay(warmboot ? 1000 : 100000); + +	for (i = 0; i < num_buses; i++) +		immr->pci_ctrl[i].gcr = 1; + +	/* +	 * RST high to first config access must be at least 2^25 cycles +	 * as per PCI spec.  This could be cut in half if we know we're +	 * running at 66MHz.  This could be insufficiently long if we're +	 * running the PCI bus at significantly less than 33MHz. +	 */ +	udelay(1020000); + +	for (i = 0; i < num_buses; i++) +		pci_init_bus(i, reg[i]); +} + +#ifdef CONFIG_PCISLAVE + +#define PCI_FUNCTION_CONFIG	0x44 +#define PCI_FUNCTION_CFG_LOCK	0x20 + +/* + * Unlock the configuration bit so that the host system can begin booting + * + * This should be used after you have: + * 1) Called mpc83xx_pci_init() + * 2) Set up your inbound translation windows to the appropriate size + */ +void mpc83xx_pcislave_unlock(int bus) +{ +	struct pci_controller *hose = &pci_hose[bus]; +	u32 dev; +	u16 reg16; + +	/* Unlock configuration lock in PCI function configuration register */ +	dev = PCI_BDF(hose->first_busno, 0, 0); +	pci_hose_read_config_word (hose, dev, PCI_FUNCTION_CONFIG, ®16); +	reg16 &= ~(PCI_FUNCTION_CFG_LOCK); +	pci_hose_write_config_word (hose, dev, PCI_FUNCTION_CONFIG, reg16); + +	/* The configuration bit is now unlocked, so we can scan the bus */ +	hose->last_busno = pci_hose_scan(hose); +} +#endif + +#if defined(CONFIG_OF_LIBFDT) +void ft_pci_setup(void *blob, bd_t *bd) +{ +	int nodeoffset; +	int tmp[2]; +	const char *path; + +	if (pci_num_buses < 1) +		return; + +	nodeoffset = fdt_path_offset(blob, "/aliases"); +	if (nodeoffset >= 0) { +		path = fdt_getprop(blob, nodeoffset, "pci0", NULL); +		if (path) { +			tmp[0] = cpu_to_be32(pci_hose[0].first_busno); +			tmp[1] = cpu_to_be32(pci_hose[0].last_busno); +			do_fixup_by_path(blob, path, "bus-range", +				&tmp, sizeof(tmp), 1); + +			tmp[0] = cpu_to_be32(gd->pci_clk); +			do_fixup_by_path(blob, path, "clock-frequency", +				&tmp, sizeof(tmp[0]), 1); +		} + +		if (pci_num_buses < 2) +			return; + +		path = fdt_getprop(blob, nodeoffset, "pci1", NULL); +		if (path) { +			tmp[0] = cpu_to_be32(pci_hose[1].first_busno); +			tmp[1] = cpu_to_be32(pci_hose[1].last_busno); +			do_fixup_by_path(blob, path, "bus-range", +				&tmp, sizeof(tmp), 1); + +			tmp[0] = cpu_to_be32(gd->pci_clk); +			do_fixup_by_path(blob, path, "clock-frequency", +				&tmp, sizeof(tmp[0]), 1); +		} +	} +} +#endif /* CONFIG_OF_LIBFDT */ diff --git a/arch/powerpc/cpu/mpc83xx/pcie.c b/arch/powerpc/cpu/mpc83xx/pcie.c new file mode 100644 index 000000000..77f8906b9 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/pcie.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2007-2009  Freescale Semiconductor, Inc. + * Copyright (C) 2008-2009  MontaVista Software, Inc. + * + * Authors: Tony Li <tony.li@freescale.com> + *          Anton Vorontsov <avorontsov@ru.mvista.com> + * + * 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 <pci.h> +#include <mpc83xx.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define PCIE_MAX_BUSES 2 + +#ifdef CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES + +static int mpc83xx_pcie_remap_cfg(struct pci_controller *hose, pci_dev_t dev) +{ +	int bus = PCI_BUS(dev) - hose->first_busno; +	immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; +	pex83xx_t *pex = &immr->pciexp[bus]; +	struct pex_outbound_window *out_win = &pex->bridge.pex_outbound_win[0]; +	u8 devfn = PCI_DEV(dev) << 3 | PCI_FUNC(dev); +	u32 dev_base = bus << 24 | devfn << 16; + +	if (hose->indirect_type == INDIRECT_TYPE_NO_PCIE_LINK) +		return -1; +	/* +	 * Workaround for the HW bug: for Type 0 configure transactions the +	 * PCI-E controller does not check the device number bits and just +	 * assumes that the device number bits are 0. +	 */ +	if (devfn & 0xf8) +		return -1; + +	out_le32(&out_win->tarl, dev_base); +	return 0; +} + +#define cfg_read(val, addr, type, op) \ +	do { *val = op((type)(addr)); } while (0) +#define cfg_write(val, addr, type, op) \ +	do { op((type *)(addr), (val)); } while (0) + +#define cfg_read_err(val) do { *val = -1; } while (0) +#define cfg_write_err(val) do { } while (0) + +#define PCIE_OP(rw, size, type, op)					\ +static int pcie_##rw##_config_##size(struct pci_controller *hose,	\ +				     pci_dev_t dev, int offset,		\ +				     type val)				\ +{									\ +	int ret;							\ +									\ +	ret = mpc83xx_pcie_remap_cfg(hose, dev);			\ +	if (ret) {							\ +		cfg_##rw##_err(val); 					\ +		return ret; 						\ +	}								\ +	cfg_##rw(val, (void *)hose->cfg_addr + offset, type, op);	\ +	return 0;							\ +} + +PCIE_OP(read, byte, u8 *, in_8) +PCIE_OP(read, word, u16 *, in_le16) +PCIE_OP(read, dword, u32 *, in_le32) +PCIE_OP(write, byte, u8, out_8) +PCIE_OP(write, word, u16, out_le16) +PCIE_OP(write, dword, u32, out_le32) + +static void mpc83xx_pcie_register_hose(int bus, struct pci_region *reg, +				       u8 link) +{ +	extern void disable_addr_trans(void); /* start.S */ +	static struct pci_controller pcie_hose[PCIE_MAX_BUSES]; +	struct pci_controller *hose = &pcie_hose[bus]; +	int i; + +	/* +	 * There are no spare BATs to remap all PCI-E windows for U-Boot, so +	 * disable translations. In general, this is not great solution, and +	 * that's why we don't register PCI-E hoses by default. +	 */ +	disable_addr_trans(); + +	for (i = 0; i < 2; i++, reg++) { +		if (reg->size == 0) +			break; + +		hose->regions[i] = *reg; +		hose->region_count++; +	} + +	i = hose->region_count++; +	hose->regions[i].bus_start = 0; +	hose->regions[i].phys_start = 0; +	hose->regions[i].size = gd->ram_size; +	hose->regions[i].flags = PCI_REGION_MEM | PCI_REGION_SYS_MEMORY; + +	i = hose->region_count++; +	hose->regions[i].bus_start = CONFIG_SYS_IMMR; +	hose->regions[i].phys_start = CONFIG_SYS_IMMR; +	hose->regions[i].size = 0x100000; +	hose->regions[i].flags = PCI_REGION_MEM | PCI_REGION_SYS_MEMORY; + +	hose->first_busno = pci_last_busno() + 1; +	hose->last_busno = 0xff; + +	if (bus == 0) +		hose->cfg_addr = (unsigned int *)CONFIG_SYS_PCIE1_CFG_BASE; +	else +		hose->cfg_addr = (unsigned int *)CONFIG_SYS_PCIE2_CFG_BASE; + +	pci_set_ops(hose, +			pcie_read_config_byte, +			pcie_read_config_word, +			pcie_read_config_dword, +			pcie_write_config_byte, +			pcie_write_config_word, +			pcie_write_config_dword); + +	if (!link) +		hose->indirect_type = INDIRECT_TYPE_NO_PCIE_LINK; + +	pci_register_hose(hose); + +#ifdef CONFIG_PCI_SCAN_SHOW +	printf("PCI:   Bus Dev VenId DevId Class Int\n"); +#endif +	/* +	 * Hose scan. +	 */ +	hose->last_busno = pci_hose_scan(hose); +} + +#else + +static void mpc83xx_pcie_register_hose(int bus, struct pci_region *reg, +				       u8 link) {} + +#endif /* CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES */ + +static void mpc83xx_pcie_init_bus(int bus, struct pci_region *reg) +{ +	immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; +	pex83xx_t *pex = &immr->pciexp[bus]; +	struct pex_outbound_window *out_win; +	struct pex_inbound_window *in_win; +	void *hose_cfg_base; +	unsigned int ram_sz; +	unsigned int barl; +	unsigned int tar; +	u16 reg16; +	int i; + +	/* Enable pex csb bridge inbound & outbound transactions */ +	out_le32(&pex->bridge.pex_csb_ctrl, +		in_le32(&pex->bridge.pex_csb_ctrl) | PEX_CSB_CTRL_OBPIOE | +		PEX_CSB_CTRL_IBPIOE); + +	/* Enable bridge outbound */ +	out_le32(&pex->bridge.pex_csb_obctrl, PEX_CSB_OBCTRL_PIOE | +		PEX_CSB_OBCTRL_MEMWE | PEX_CSB_OBCTRL_IOWE | +		PEX_CSB_OBCTRL_CFGWE); + +	out_win = &pex->bridge.pex_outbound_win[0]; +	if (bus) { +		out_le32(&out_win->ar, PEX_OWAR_EN | PEX_OWAR_TYPE_CFG | +			CONFIG_SYS_PCIE2_CFG_SIZE); +		out_le32(&out_win->bar, CONFIG_SYS_PCIE2_CFG_BASE); +	} else { +		out_le32(&out_win->ar, PEX_OWAR_EN | PEX_OWAR_TYPE_CFG | +			CONFIG_SYS_PCIE1_CFG_SIZE); +		out_le32(&out_win->bar, CONFIG_SYS_PCIE1_CFG_BASE); +	} +	out_le32(&out_win->tarl, 0); +	out_le32(&out_win->tarh, 0); + +	for (i = 0; i < 2; i++, reg++) { +		u32 ar; + +		if (reg->size == 0) +			break; + +		out_win = &pex->bridge.pex_outbound_win[i + 1]; +		out_le32(&out_win->bar, reg->phys_start); +		out_le32(&out_win->tarl, reg->bus_start); +		out_le32(&out_win->tarh, 0); +		ar = PEX_OWAR_EN | (reg->size & PEX_OWAR_SIZE); +		if (reg->flags & PCI_REGION_IO) +			ar |= PEX_OWAR_TYPE_IO; +		else +			ar |= PEX_OWAR_TYPE_MEM; +		out_le32(&out_win->ar, ar); +	} + +	out_le32(&pex->bridge.pex_csb_ibctrl, PEX_CSB_IBCTRL_PIOE); + +	ram_sz = gd->ram_size; +	barl = 0; +	tar = 0; +	i = 0; +	while (ram_sz > 0) { +		in_win = &pex->bridge.pex_inbound_win[i]; +		out_le32(&in_win->barl, barl); +		out_le32(&in_win->barh, 0x0); +		out_le32(&in_win->tar, tar); +		if (ram_sz >= 0x10000000) { +			/* The maxium windows size is 256M */ +			out_le32(&in_win->ar, PEX_IWAR_EN | PEX_IWAR_NSOV | +				PEX_IWAR_TYPE_PF | 0x0FFFF000); +			barl += 0x10000000; +			tar += 0x10000000; +			ram_sz -= 0x10000000; +		} else { +			/* The UM  is not clear here. +			 * So, round up to even Mb boundary */ + +			ram_sz = ram_sz >> (20 + +					((ram_sz & 0xFFFFF) ? 1 : 0)); +			if (!(ram_sz % 2)) +				ram_sz -= 1; +			out_le32(&in_win->ar, PEX_IWAR_EN | PEX_IWAR_NSOV | +				PEX_IWAR_TYPE_PF | (ram_sz << 20) | 0xFF000); +			ram_sz = 0; +		} +		i++; +	} + +	in_win = &pex->bridge.pex_inbound_win[i]; +	out_le32(&in_win->barl, CONFIG_SYS_IMMR); +	out_le32(&in_win->barh, 0); +	out_le32(&in_win->tar, CONFIG_SYS_IMMR); +	out_le32(&in_win->ar, PEX_IWAR_EN | +		PEX_IWAR_TYPE_NO_PF | PEX_IWAR_SIZE_1M); + +	/* Enable the host virtual INTX interrupts */ +	out_le32(&pex->bridge.pex_int_axi_misc_enb, +		in_le32(&pex->bridge.pex_int_axi_misc_enb) | 0x1E0); + +	/* Hose configure header is memory-mapped */ +	hose_cfg_base = (void *)pex; + +	get_clocks(); +	/* Configure the PCIE controller core clock ratio */ +	out_le32(hose_cfg_base + PEX_GCLK_RATIO, +		(((bus ? gd->pciexp2_clk : gd->pciexp1_clk) / 1000000) * 16) +		/ 333); +	udelay(1000000); + +	/* Do Type 1 bridge configuration */ +	out_8(hose_cfg_base + PCI_PRIMARY_BUS, 0); +	out_8(hose_cfg_base + PCI_SECONDARY_BUS, 1); +	out_8(hose_cfg_base + PCI_SUBORDINATE_BUS, 255); + +	/* +	 * Write to Command register +	 */ +	reg16 = in_le16(hose_cfg_base + PCI_COMMAND); +	reg16 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO | +			PCI_COMMAND_SERR | PCI_COMMAND_PARITY; +	out_le16(hose_cfg_base + PCI_COMMAND, reg16); + +	/* +	 * Clear non-reserved bits in status register. +	 */ +	out_le16(hose_cfg_base + PCI_STATUS, 0xffff); +	out_8(hose_cfg_base + PCI_LATENCY_TIMER, 0x80); +	out_8(hose_cfg_base + PCI_CACHE_LINE_SIZE, 0x08); + +	printf("PCIE%d: ", bus); + +	reg16 = in_le16(hose_cfg_base + PCI_LTSSM); +	if (reg16 >= PCI_LTSSM_L0) +		printf("link\n"); +	else +		printf("No link\n"); + +	mpc83xx_pcie_register_hose(bus, reg, reg16 >= PCI_LTSSM_L0); +} + +/* + * The caller must have already set SCCR, SERDES and the PCIE_LAW BARs + * must have been set to cover all of the requested regions. + */ +void mpc83xx_pcie_init(int num_buses, struct pci_region **reg, int warmboot) +{ +	int i; + +	/* +	 * Release PCI RST Output signal. +	 * Power on to RST high must be at least 100 ms as per PCI spec. +	 * On warm boots only 1 ms is required. +	 */ +	udelay(warmboot ? 1000 : 100000); + +	for (i = 0; i < num_buses; i++) +		mpc83xx_pcie_init_bus(i, reg[i]); +} diff --git a/arch/powerpc/cpu/mpc83xx/qe_io.c b/arch/powerpc/cpu/mpc83xx/qe_io.c new file mode 100644 index 000000000..db94f0009 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/qe_io.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on source code of Shlomi Gridish + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "common.h" +#include "asm/errno.h" +#include "asm/io.h" +#include "asm/immap_83xx.h" + +#define	NUM_OF_PINS	32 +void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign) +{ +	u32			pin_2bit_mask; +	u32			pin_2bit_dir; +	u32			pin_2bit_assign; +	u32			pin_1bit_mask; +	u32			tmp_val; +	volatile immap_t	*im = (volatile immap_t *)CONFIG_SYS_IMMR; +	volatile qepio83xx_t	*par_io = (volatile qepio83xx_t *)&im->qepio; + +	/* Caculate pin location and 2bit mask and dir */ +	pin_2bit_mask = (u32)(0x3 << (NUM_OF_PINS-(pin%(NUM_OF_PINS/2)+1)*2)); +	pin_2bit_dir = (u32)(dir << (NUM_OF_PINS-(pin%(NUM_OF_PINS/2)+1)*2)); + +	/* Setup the direction */ +	tmp_val = (pin > (NUM_OF_PINS/2) - 1) ? \ +		in_be32(&par_io->ioport[port].dir2) : +		in_be32(&par_io->ioport[port].dir1); + +	if (pin > (NUM_OF_PINS/2) -1) { +		out_be32(&par_io->ioport[port].dir2, ~pin_2bit_mask & tmp_val); +		out_be32(&par_io->ioport[port].dir2, pin_2bit_dir | tmp_val); +	} else { +		out_be32(&par_io->ioport[port].dir1, ~pin_2bit_mask & tmp_val); +		out_be32(&par_io->ioport[port].dir1, pin_2bit_dir | tmp_val); +	} + +	/* Calculate pin location for 1bit mask */ +	pin_1bit_mask = (u32)(1 << (NUM_OF_PINS - (pin+1))); + +	/* Setup the open drain */ +	tmp_val = in_be32(&par_io->ioport[port].podr); +	if (open_drain) { +		out_be32(&par_io->ioport[port].podr, pin_1bit_mask | tmp_val); +	} else { +		out_be32(&par_io->ioport[port].podr, ~pin_1bit_mask & tmp_val); +	} + +	/* Setup the assignment */ +	tmp_val = (pin > (NUM_OF_PINS/2) - 1) ? +		in_be32(&par_io->ioport[port].ppar2): +		in_be32(&par_io->ioport[port].ppar1); +	pin_2bit_assign = (u32)(assign +				<< (NUM_OF_PINS - (pin%(NUM_OF_PINS/2)+1)*2)); + +	/* Clear and set 2 bits mask */ +	if (pin > (NUM_OF_PINS/2) - 1) { +		out_be32(&par_io->ioport[port].ppar2, ~pin_2bit_mask & tmp_val); +		out_be32(&par_io->ioport[port].ppar2, pin_2bit_assign | tmp_val); +	} else { +		out_be32(&par_io->ioport[port].ppar1, ~pin_2bit_mask & tmp_val); +		out_be32(&par_io->ioport[port].ppar1, pin_2bit_assign | tmp_val); +	} +} diff --git a/arch/powerpc/cpu/mpc83xx/serdes.c b/arch/powerpc/cpu/mpc83xx/serdes.c new file mode 100644 index 000000000..64033fe4c --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/serdes.c @@ -0,0 +1,145 @@ +/* + * Freescale SerDes initialization routine + * + * Copyright (C) 2007 Freescale Semicondutor, Inc. + * Copyright (C) 2008 MontaVista Software, Inc. + * + * Author: Li Yang <leoli@freescale.com> + * + * This program is free software; you can redistribute  it and/or modify it + * under  the terms of  the GNU General  Public License as published by the + * Free Software Foundation;  either version 2 of the  License, or (at your + * option) any later version. + */ + +#include <config.h> +#include <common.h> +#include <asm/io.h> +#include <asm/fsl_serdes.h> + +/* SerDes registers */ +#define FSL_SRDSCR0_OFFS		0x0 +#define FSL_SRDSCR0_DPP_1V2		0x00008800 +#define FSL_SRDSCR1_OFFS		0x4 +#define FSL_SRDSCR1_PLLBW		0x00000040 +#define FSL_SRDSCR2_OFFS		0x8 +#define FSL_SRDSCR2_VDD_1V2		0x00800000 +#define FSL_SRDSCR2_SEIC_MASK		0x00001c1c +#define FSL_SRDSCR2_SEIC_SATA		0x00001414 +#define FSL_SRDSCR2_SEIC_PEX		0x00001010 +#define FSL_SRDSCR2_SEIC_SGMII		0x00000101 +#define FSL_SRDSCR3_OFFS		0xc +#define FSL_SRDSCR3_KFR_SATA		0x10100000 +#define FSL_SRDSCR3_KPH_SATA		0x04040000 +#define FSL_SRDSCR3_SDFM_SATA_PEX	0x01010000 +#define FSL_SRDSCR3_SDTXL_SATA		0x00000505 +#define FSL_SRDSCR4_OFFS		0x10 +#define FSL_SRDSCR4_PROT_SATA		0x00000808 +#define FSL_SRDSCR4_PROT_PEX		0x00000101 +#define FSL_SRDSCR4_PROT_SGMII		0x00000505 +#define FSL_SRDSCR4_PLANE_X2		0x01000000 +#define FSL_SRDSRSTCTL_OFFS		0x20 +#define FSL_SRDSRSTCTL_RST		0x80000000 +#define FSL_SRDSRSTCTL_SATA_RESET	0xf + +void fsl_setup_serdes(u32 offset, char proto, u32 rfcks, char vdd) +{ +	void *regs = (void *)CONFIG_SYS_IMMR + offset; +	u32 tmp; + +	/* 1.0V corevdd */ +	if (vdd) { +		/* DPPE/DPPA = 0 */ +		tmp = in_be32(regs + FSL_SRDSCR0_OFFS); +		tmp &= ~FSL_SRDSCR0_DPP_1V2; +		out_be32(regs + FSL_SRDSCR0_OFFS, tmp); + +		/* VDD = 0 */ +		tmp = in_be32(regs + FSL_SRDSCR2_OFFS); +		tmp &= ~FSL_SRDSCR2_VDD_1V2; +		out_be32(regs + FSL_SRDSCR2_OFFS, tmp); +	} + +	/* protocol specific configuration */ +	switch (proto) { +	case FSL_SERDES_PROTO_SATA: +		/* Set and clear reset bits */ +		tmp = in_be32(regs + FSL_SRDSRSTCTL_OFFS); +		tmp |= FSL_SRDSRSTCTL_SATA_RESET; +		out_be32(regs + FSL_SRDSRSTCTL_OFFS, tmp); +		udelay(1000); +		tmp &= ~FSL_SRDSRSTCTL_SATA_RESET; +		out_be32(regs + FSL_SRDSRSTCTL_OFFS, tmp); + +		/* Configure SRDSCR1 */ +		tmp = in_be32(regs + FSL_SRDSCR1_OFFS); +		tmp &= ~FSL_SRDSCR1_PLLBW; +		out_be32(regs + FSL_SRDSCR1_OFFS, tmp); + +		/* Configure SRDSCR2 */ +		tmp = in_be32(regs + FSL_SRDSCR2_OFFS); +		tmp &= ~FSL_SRDSCR2_SEIC_MASK; +		tmp |= FSL_SRDSCR2_SEIC_SATA; +		out_be32(regs + FSL_SRDSCR2_OFFS, tmp); + +		/* Configure SRDSCR3 */ +		tmp = FSL_SRDSCR3_KFR_SATA | FSL_SRDSCR3_KPH_SATA | +			FSL_SRDSCR3_SDFM_SATA_PEX | +			FSL_SRDSCR3_SDTXL_SATA; +		out_be32(regs + FSL_SRDSCR3_OFFS, tmp); + +		/* Configure SRDSCR4 */ +		tmp = rfcks | FSL_SRDSCR4_PROT_SATA; +		out_be32(regs + FSL_SRDSCR4_OFFS, tmp); +		break; +	case FSL_SERDES_PROTO_PEX: +	case FSL_SERDES_PROTO_PEX_X2: +		/* Configure SRDSCR1 */ +		tmp = in_be32(regs + FSL_SRDSCR1_OFFS); +		tmp |= FSL_SRDSCR1_PLLBW; +		out_be32(regs + FSL_SRDSCR1_OFFS, tmp); + +		/* Configure SRDSCR2 */ +		tmp = in_be32(regs + FSL_SRDSCR2_OFFS); +		tmp &= ~FSL_SRDSCR2_SEIC_MASK; +		tmp |= FSL_SRDSCR2_SEIC_PEX; +		out_be32(regs + FSL_SRDSCR2_OFFS, tmp); + +		/* Configure SRDSCR3 */ +		tmp = FSL_SRDSCR3_SDFM_SATA_PEX; +		out_be32(regs + FSL_SRDSCR3_OFFS, tmp); + +		/* Configure SRDSCR4 */ +		tmp = rfcks | FSL_SRDSCR4_PROT_PEX; +		if (proto == FSL_SERDES_PROTO_PEX_X2) +			tmp |= FSL_SRDSCR4_PLANE_X2; +		out_be32(regs + FSL_SRDSCR4_OFFS, tmp); +		break; +	case FSL_SERDES_PROTO_SGMII: +		/* Configure SRDSCR1 */ +		tmp = in_be32(regs + FSL_SRDSCR1_OFFS); +		tmp &= ~FSL_SRDSCR1_PLLBW; +		out_be32(regs + FSL_SRDSCR1_OFFS, tmp); + +		/* Configure SRDSCR2 */ +		tmp = in_be32(regs + FSL_SRDSCR2_OFFS); +		tmp &= ~FSL_SRDSCR2_SEIC_MASK; +		tmp |= FSL_SRDSCR2_SEIC_SGMII; +		out_be32(regs + FSL_SRDSCR2_OFFS, tmp); + +		/* Configure SRDSCR3 */ +		out_be32(regs + FSL_SRDSCR3_OFFS, 0); + +		/* Configure SRDSCR4 */ +		tmp = rfcks | FSL_SRDSCR4_PROT_SGMII; +		out_be32(regs + FSL_SRDSCR4_OFFS, tmp); +		break; +	default: +		return; +	} + +	/* Do a software reset */ +	tmp = in_be32(regs + FSL_SRDSRSTCTL_OFFS); +	tmp |= FSL_SRDSRSTCTL_RST; +	out_be32(regs + FSL_SRDSRSTCTL_OFFS, tmp); +} diff --git a/arch/powerpc/cpu/mpc83xx/spd_sdram.c b/arch/powerpc/cpu/mpc83xx/spd_sdram.c new file mode 100644 index 000000000..44aaa9abc --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/spd_sdram.c @@ -0,0 +1,918 @@ +/* + * (C) Copyright 2006-2007 Freescale Semiconductor, Inc. + * + * (C) Copyright 2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 2004-2006 Freescale Semiconductor, Inc. + * (C) Copyright 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 + */ + +#include <common.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <i2c.h> +#include <spd.h> +#include <asm/mmu.h> +#include <spd_sdram.h> + +DECLARE_GLOBAL_DATA_PTR; + +void board_add_ram_info(int use_default) +{ +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	volatile ddr83xx_t *ddr = &immap->ddr; +	char buf[32]; + +	printf(" (DDR%d", ((ddr->sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) +			   >> SDRAM_CFG_SDRAM_TYPE_SHIFT) - 1); + +	if (ddr->sdram_cfg & SDRAM_CFG_32_BE) +		puts(", 32-bit"); +	else +		puts(", 64-bit"); + +	if (ddr->sdram_cfg & SDRAM_CFG_ECC_EN) +		puts(", ECC on"); +	else +		puts(", ECC off"); + +	printf(", %s MHz)", strmhz(buf, gd->mem_clk)); + +#if defined(CONFIG_SYS_LB_SDRAM) && defined(CONFIG_SYS_LBC_SDRAM_SIZE) +	puts("\nSDRAM: "); +	print_size (CONFIG_SYS_LBC_SDRAM_SIZE * 1024 * 1024, " (local bus)"); +#endif +} + +#ifdef CONFIG_SPD_EEPROM +#ifndef	CONFIG_SYS_READ_SPD +#define CONFIG_SYS_READ_SPD	i2c_read +#endif + +/* + * Convert picoseconds into clock cycles (rounding up if needed). + */ +int +picos_to_clk(int picos) +{ +	unsigned int mem_bus_clk; +	int clks; + +	mem_bus_clk = gd->mem_clk >> 1; +	clks = picos / (1000000000 / (mem_bus_clk / 1000)); +	if (picos % (1000000000 / (mem_bus_clk / 1000)) != 0) +		clks++; + +	return clks; +} + +unsigned int banksize(unsigned char row_dens) +{ +	return ((row_dens >> 2) | ((row_dens & 3) << 6)) << 24; +} + +int read_spd(uint addr) +{ +	return ((int) addr); +} + +#undef SPD_DEBUG +#ifdef SPD_DEBUG +static void spd_debug(spd_eeprom_t *spd) +{ +	printf ("\nDIMM type:       %-18.18s\n", spd->mpart); +	printf ("SPD size:        %d\n", spd->info_size); +	printf ("EEPROM size:     %d\n", 1 << spd->chip_size); +	printf ("Memory type:     %d\n", spd->mem_type); +	printf ("Row addr:        %d\n", spd->nrow_addr); +	printf ("Column addr:     %d\n", spd->ncol_addr); +	printf ("# of rows:       %d\n", spd->nrows); +	printf ("Row density:     %d\n", spd->row_dens); +	printf ("# of banks:      %d\n", spd->nbanks); +	printf ("Data width:      %d\n", +			256 * spd->dataw_msb + spd->dataw_lsb); +	printf ("Chip width:      %d\n", spd->primw); +	printf ("Refresh rate:    %02X\n", spd->refresh); +	printf ("CAS latencies:   %02X\n", spd->cas_lat); +	printf ("Write latencies: %02X\n", spd->write_lat); +	printf ("tRP:             %d\n", spd->trp); +	printf ("tRCD:            %d\n", spd->trcd); +	printf ("\n"); +} +#endif /* SPD_DEBUG */ + +long int spd_sdram() +{ +	volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; +	volatile ddr83xx_t *ddr = &immap->ddr; +	volatile law83xx_t *ecm = &immap->sysconf.ddrlaw[0]; +	spd_eeprom_t spd; +	unsigned int n_ranks; +	unsigned int odt_rd_cfg, odt_wr_cfg; +	unsigned char twr_clk, twtr_clk; +	unsigned int sdram_type; +	unsigned int memsize; +	unsigned int law_size; +	unsigned char caslat, caslat_ctrl; +	unsigned int trfc, trfc_clk, trfc_low, trfc_high; +	unsigned int trcd_clk, trtp_clk; +	unsigned char cke_min_clk; +	unsigned char add_lat, wr_lat; +	unsigned char wr_data_delay; +	unsigned char four_act; +	unsigned char cpo; +	unsigned char burstlen; +	unsigned char odt_cfg, mode_odt_enable; +	unsigned int max_bus_clk; +	unsigned int max_data_rate, effective_data_rate; +	unsigned int ddrc_clk; +	unsigned int refresh_clk; +	unsigned int sdram_cfg; +	unsigned int ddrc_ecc_enable; +	unsigned int pvr = get_pvr(); + +	/* +	 * First disable the memory controller (could be enabled +	 * by the debugger) +	 */ +	clrsetbits_be32(&ddr->sdram_cfg, SDRAM_CFG_MEM_EN, 0); +	sync(); +	isync(); + +	/* Read SPD parameters with I2C */ +	CONFIG_SYS_READ_SPD(SPD_EEPROM_ADDRESS, 0, 1, (uchar *) & spd, sizeof (spd)); +#ifdef SPD_DEBUG +	spd_debug(&spd); +#endif +	/* Check the memory type */ +	if (spd.mem_type != SPD_MEMTYPE_DDR && spd.mem_type != SPD_MEMTYPE_DDR2) { +		debug("DDR: Module mem type is %02X\n", spd.mem_type); +		return 0; +	} + +	/* Check the number of physical bank */ +	if (spd.mem_type == SPD_MEMTYPE_DDR) { +		n_ranks = spd.nrows; +	} else { +		n_ranks = (spd.nrows & 0x7) + 1; +	} + +	if (n_ranks > 2) { +		printf("DDR: The number of physical bank is %02X\n", n_ranks); +		return 0; +	} + +	/* Check if the number of row of the module is in the range of DDRC */ +	if (spd.nrow_addr < 12 || spd.nrow_addr > 15) { +		printf("DDR: Row number is out of range of DDRC, row=%02X\n", +							 spd.nrow_addr); +		return 0; +	} + +	/* Check if the number of col of the module is in the range of DDRC */ +	if (spd.ncol_addr < 8 || spd.ncol_addr > 11) { +		printf("DDR: Col number is out of range of DDRC, col=%02X\n", +							 spd.ncol_addr); +		return 0; +	} + +#ifdef CONFIG_SYS_DDRCDR_VALUE +	/* +	 * Adjust DDR II IO voltage biasing.  It just makes it work. +	 */ +	if(spd.mem_type == SPD_MEMTYPE_DDR2) { +		immap->sysconf.ddrcdr = CONFIG_SYS_DDRCDR_VALUE; +	} +	udelay(50000); +#endif + +	/* +	 * ODT configuration recommendation from DDR Controller Chapter. +	 */ +	odt_rd_cfg = 0;			/* Never assert ODT */ +	odt_wr_cfg = 0;			/* Never assert ODT */ +	if (spd.mem_type == SPD_MEMTYPE_DDR2) { +		odt_wr_cfg = 1;		/* Assert ODT on writes to CSn */ +	} + +	/* Setup DDR chip select register */ +#ifdef CONFIG_SYS_83XX_DDR_USES_CS0 +	ddr->csbnds[0].csbnds = (banksize(spd.row_dens) >> 24) - 1; +	ddr->cs_config[0] = ( 1 << 31 +			    | (odt_rd_cfg << 20) +			    | (odt_wr_cfg << 16) +			    | ((spd.nbanks == 8 ? 1 : 0) << 14) +			    | ((spd.nrow_addr - 12) << 8) +			    | (spd.ncol_addr - 8) ); +	debug("\n"); +	debug("cs0_bnds = 0x%08x\n",ddr->csbnds[0].csbnds); +	debug("cs0_config = 0x%08x\n",ddr->cs_config[0]); + +	if (n_ranks == 2) { +		ddr->csbnds[1].csbnds = ( (banksize(spd.row_dens) >> 8) +				  | ((banksize(spd.row_dens) >> 23) - 1) ); +		ddr->cs_config[1] = ( 1<<31 +				    | (odt_rd_cfg << 20) +				    | (odt_wr_cfg << 16) +				    | ((spd.nbanks == 8 ? 1 : 0) << 14) +				    | ((spd.nrow_addr - 12) << 8) +				    | (spd.ncol_addr - 8) ); +		debug("cs1_bnds = 0x%08x\n",ddr->csbnds[1].csbnds); +		debug("cs1_config = 0x%08x\n",ddr->cs_config[1]); +	} + +#else +	ddr->csbnds[2].csbnds = (banksize(spd.row_dens) >> 24) - 1; +	ddr->cs_config[2] = ( 1 << 31 +			    | (odt_rd_cfg << 20) +			    | (odt_wr_cfg << 16) +			    | ((spd.nbanks == 8 ? 1 : 0) << 14) +			    | ((spd.nrow_addr - 12) << 8) +			    | (spd.ncol_addr - 8) ); +	debug("\n"); +	debug("cs2_bnds = 0x%08x\n",ddr->csbnds[2].csbnds); +	debug("cs2_config = 0x%08x\n",ddr->cs_config[2]); + +	if (n_ranks == 2) { +		ddr->csbnds[3].csbnds = ( (banksize(spd.row_dens) >> 8) +				  | ((banksize(spd.row_dens) >> 23) - 1) ); +		ddr->cs_config[3] = ( 1<<31 +				    | (odt_rd_cfg << 20) +				    | (odt_wr_cfg << 16) +				    | ((spd.nbanks == 8 ? 1 : 0) << 14) +				    | ((spd.nrow_addr - 12) << 8) +				    | (spd.ncol_addr - 8) ); +		debug("cs3_bnds = 0x%08x\n",ddr->csbnds[3].csbnds); +		debug("cs3_config = 0x%08x\n",ddr->cs_config[3]); +	} +#endif + +	/* +	 * Figure out memory size in Megabytes. +	 */ +	memsize = n_ranks * banksize(spd.row_dens) / 0x100000; + +	/* +	 * First supported LAW size is 16M, at LAWAR_SIZE_16M == 23. +	 */ +	law_size = 19 + __ilog2(memsize); + +	/* +	 * Set up LAWBAR for all of DDR. +	 */ +	ecm->bar = CONFIG_SYS_DDR_SDRAM_BASE & 0xfffff000; +	ecm->ar  = (LAWAR_EN | LAWAR_TRGT_IF_DDR | (LAWAR_SIZE & law_size)); +	debug("DDR:bar=0x%08x\n", ecm->bar); +	debug("DDR:ar=0x%08x\n", ecm->ar); + +	/* +	 * Find the largest CAS by locating the highest 1 bit +	 * in the spd.cas_lat field.  Translate it to a DDR +	 * controller field value: +	 * +	 *	CAS Lat	DDR I	DDR II	Ctrl +	 *	Clocks	SPD Bit	SPD Bit	Value +	 *	-------	-------	-------	----- +	 *	1.0	0		0001 +	 *	1.5	1		0010 +	 *	2.0	2	2	0011 +	 *	2.5	3		0100 +	 *	3.0	4	3	0101 +	 *	3.5	5		0110 +	 *	4.0	6	4	0111 +	 *	4.5			1000 +	 *	5.0		5	1001 +	 */ +	caslat = __ilog2(spd.cas_lat); +	if ((spd.mem_type == SPD_MEMTYPE_DDR) +	    && (caslat > 6)) { +		printf("DDR I: Invalid SPD CAS Latency: 0x%x.\n", spd.cas_lat); +		return 0; +	} else if (spd.mem_type == SPD_MEMTYPE_DDR2 +		   && (caslat < 2 || caslat > 5)) { +		printf("DDR II: Invalid SPD CAS Latency: 0x%x.\n", +		       spd.cas_lat); +		return 0; +	} +	debug("DDR: caslat SPD bit is %d\n", caslat); + +	max_bus_clk = 1000 *10 / (((spd.clk_cycle & 0xF0) >> 4) * 10 +			+ (spd.clk_cycle & 0x0f)); +	max_data_rate = max_bus_clk * 2; + +	debug("DDR:Module maximum data rate is: %d MHz\n", max_data_rate); + +	ddrc_clk = gd->mem_clk / 1000000; +	effective_data_rate = 0; + +	if (max_data_rate >= 460) { /* it is DDR2-800, 667, 533 */ +		if (spd.cas_lat & 0x08) +			caslat = 3; +		else +			caslat = 4; +		if (ddrc_clk <= 460 && ddrc_clk > 350) +			effective_data_rate = 400; +		else if (ddrc_clk <=350 && ddrc_clk > 280) +			effective_data_rate = 333; +		else if (ddrc_clk <= 280 && ddrc_clk > 230) +			effective_data_rate = 266; +		else +			effective_data_rate = 200; +	} else if (max_data_rate >= 390 && max_data_rate < 460) { /* it is DDR 400 */ +		if (ddrc_clk <= 460 && ddrc_clk > 350) { +			/* DDR controller clk at 350~460 */ +			effective_data_rate = 400; /* 5ns */ +			caslat = caslat; +		} else if (ddrc_clk <= 350 && ddrc_clk > 280) { +			/* DDR controller clk at 280~350 */ +			effective_data_rate = 333; /* 6ns */ +			if (spd.clk_cycle2 == 0x60) +				caslat = caslat - 1; +			else +				caslat = caslat; +		} else if (ddrc_clk <= 280 && ddrc_clk > 230) { +			/* DDR controller clk at 230~280 */ +			effective_data_rate = 266; /* 7.5ns */ +			if (spd.clk_cycle3 == 0x75) +				caslat = caslat - 2; +			else if (spd.clk_cycle2 == 0x75) +				caslat = caslat - 1; +			else +				caslat = caslat; +		} else if (ddrc_clk <= 230 && ddrc_clk > 90) { +			/* DDR controller clk at 90~230 */ +			effective_data_rate = 200; /* 10ns */ +			if (spd.clk_cycle3 == 0xa0) +				caslat = caslat - 2; +			else if (spd.clk_cycle2 == 0xa0) +				caslat = caslat - 1; +			else +				caslat = caslat; +		} +	} else if (max_data_rate >= 323) { /* it is DDR 333 */ +		if (ddrc_clk <= 350 && ddrc_clk > 280) { +			/* DDR controller clk at 280~350 */ +			effective_data_rate = 333; /* 6ns */ +			caslat = caslat; +		} else if (ddrc_clk <= 280 && ddrc_clk > 230) { +			/* DDR controller clk at 230~280 */ +			effective_data_rate = 266; /* 7.5ns */ +			if (spd.clk_cycle2 == 0x75) +				caslat = caslat - 1; +			else +				caslat = caslat; +		} else if (ddrc_clk <= 230 && ddrc_clk > 90) { +			/* DDR controller clk at 90~230 */ +			effective_data_rate = 200; /* 10ns */ +			if (spd.clk_cycle3 == 0xa0) +				caslat = caslat - 2; +			else if (spd.clk_cycle2 == 0xa0) +				caslat = caslat - 1; +			else +				caslat = caslat; +		} +	} else if (max_data_rate >= 256) { /* it is DDR 266 */ +		if (ddrc_clk <= 350 && ddrc_clk > 280) { +			/* DDR controller clk at 280~350 */ +			printf("DDR: DDR controller freq is more than " +				"max data rate of the module\n"); +			return 0; +		} else if (ddrc_clk <= 280 && ddrc_clk > 230) { +			/* DDR controller clk at 230~280 */ +			effective_data_rate = 266; /* 7.5ns */ +			caslat = caslat; +		} else if (ddrc_clk <= 230 && ddrc_clk > 90) { +			/* DDR controller clk at 90~230 */ +			effective_data_rate = 200; /* 10ns */ +			if (spd.clk_cycle2 == 0xa0) +				caslat = caslat - 1; +		} +	} else if (max_data_rate >= 190) { /* it is DDR 200 */ +		if (ddrc_clk <= 350 && ddrc_clk > 230) { +			/* DDR controller clk at 230~350 */ +			printf("DDR: DDR controller freq is more than " +				"max data rate of the module\n"); +			return 0; +		} else if (ddrc_clk <= 230 && ddrc_clk > 90) { +			/* DDR controller clk at 90~230 */ +			effective_data_rate = 200; /* 10ns */ +			caslat = caslat; +		} +	} + +	debug("DDR:Effective data rate is: %dMHz\n", effective_data_rate); +	debug("DDR:The MSB 1 of CAS Latency is: %d\n", caslat); + +	/* +	 * Errata DDR6 work around: input enable 2 cycles earlier. +	 * including MPC834x Rev1.0/1.1 and MPC8360 Rev1.1/1.2. +	 */ +	if(PVR_MAJ(pvr) <= 1 && spd.mem_type == SPD_MEMTYPE_DDR){ +		if (caslat == 2) +			ddr->debug_reg = 0x201c0000; /* CL=2 */ +		else if (caslat == 3) +			ddr->debug_reg = 0x202c0000; /* CL=2.5 */ +		else if (caslat == 4) +			ddr->debug_reg = 0x202c0000; /* CL=3.0 */ + +		__asm__ __volatile__ ("sync"); + +		debug("Errata DDR6 (debug_reg=0x%08x)\n", ddr->debug_reg); +	} + +	/* +	 * Convert caslat clocks to DDR controller value. +	 * Force caslat_ctrl to be DDR Controller field-sized. +	 */ +	if (spd.mem_type == SPD_MEMTYPE_DDR) { +		caslat_ctrl = (caslat + 1) & 0x07; +	} else { +		caslat_ctrl =  (2 * caslat - 1) & 0x0f; +	} + +	debug("DDR: effective data rate is %d MHz\n", effective_data_rate); +	debug("DDR: caslat SPD bit is %d, controller field is 0x%x\n", +	      caslat, caslat_ctrl); + +	/* +	 * Timing Config 0. +	 * Avoid writing for DDR I. +	 */ +	if (spd.mem_type == SPD_MEMTYPE_DDR2) { +		unsigned char taxpd_clk = 8;		/* By the book. */ +		unsigned char tmrd_clk = 2;		/* By the book. */ +		unsigned char act_pd_exit = 2;		/* Empirical? */ +		unsigned char pre_pd_exit = 6;		/* Empirical? */ + +		ddr->timing_cfg_0 = (0 +			| ((act_pd_exit & 0x7) << 20)	/* ACT_PD_EXIT */ +			| ((pre_pd_exit & 0x7) << 16)	/* PRE_PD_EXIT */ +			| ((taxpd_clk & 0xf) << 8)	/* ODT_PD_EXIT */ +			| ((tmrd_clk & 0xf) << 0)	/* MRS_CYC */ +			); +		debug("DDR: timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0); +	} + +	/* +	 * For DDR I, WRREC(Twr) and WRTORD(Twtr) are not in SPD, +	 * use conservative value. +	 * For DDR II, they are bytes 36 and 37, in quarter nanos. +	 */ + +	if (spd.mem_type == SPD_MEMTYPE_DDR) { +		twr_clk = 3;	/* Clocks */ +		twtr_clk = 1;	/* Clocks */ +	} else { +		twr_clk = picos_to_clk(spd.twr * 250); +		twtr_clk = picos_to_clk(spd.twtr * 250); +		if (twtr_clk < 2) +			twtr_clk = 2; +	} + +	/* +	 * Calculate Trfc, in picos. +	 * DDR I:  Byte 42 straight up in ns. +	 * DDR II: Byte 40 and 42 swizzled some, in ns. +	 */ +	if (spd.mem_type == SPD_MEMTYPE_DDR) { +		trfc = spd.trfc * 1000;		/* up to ps */ +	} else { +		unsigned int byte40_table_ps[8] = { +			0, +			250, +			330, +			500, +			660, +			750, +			0, +			0 +		}; + +		trfc = (((spd.trctrfc_ext & 0x1) * 256) + spd.trfc) * 1000 +			+ byte40_table_ps[(spd.trctrfc_ext >> 1) & 0x7]; +	} +	trfc_clk = picos_to_clk(trfc); + +	/* +	 * Trcd, Byte 29, from quarter nanos to ps and clocks. +	 */ +	trcd_clk = picos_to_clk(spd.trcd * 250) & 0x7; + +	/* +	 * Convert trfc_clk to DDR controller fields.  DDR I should +	 * fit in the REFREC field (16-19) of TIMING_CFG_1, but the +	 * 83xx controller has an extended REFREC field of three bits. +	 * The controller automatically adds 8 clocks to this value, +	 * so preadjust it down 8 first before splitting it up. +	 */ +	trfc_low = (trfc_clk - 8) & 0xf; +	trfc_high = ((trfc_clk - 8) >> 4) & 0x3; + +	ddr->timing_cfg_1 = +	    (((picos_to_clk(spd.trp * 250) & 0x07) << 28 ) |	/* PRETOACT */ +	     ((picos_to_clk(spd.tras * 1000) & 0x0f ) << 24 ) | /* ACTTOPRE */ +	     (trcd_clk << 20 ) |				/* ACTTORW */ +	     (caslat_ctrl << 16 ) |				/* CASLAT */ +	     (trfc_low << 12 ) |				/* REFEC */ +	     ((twr_clk & 0x07) << 8) |				/* WRRREC */ +	     ((picos_to_clk(spd.trrd * 250) & 0x07) << 4) |	/* ACTTOACT */ +	     ((twtr_clk & 0x07) << 0)				/* WRTORD */ +	    ); + +	/* +	 * Additive Latency +	 * For DDR I, 0. +	 * For DDR II, with ODT enabled, use "a value" less than ACTTORW, +	 * which comes from Trcd, and also note that: +	 *	add_lat + caslat must be >= 4 +	 */ +	add_lat = 0; +	if (spd.mem_type == SPD_MEMTYPE_DDR2 +	    && (odt_wr_cfg || odt_rd_cfg) +	    && (caslat < 4)) { +		add_lat = 4 - caslat; +		if ((add_lat + caslat) < 4) { +			add_lat = 0; +		} +	} + +	/* +	 * Write Data Delay +	 * Historically 0x2 == 4/8 clock delay. +	 * Empirically, 0x3 == 6/8 clock delay is suggested for DDR I 266. +	 */ +	wr_data_delay = 2; + +	/* +	 * Write Latency +	 * Read to Precharge +	 * Minimum CKE Pulse Width. +	 * Four Activate Window +	 */ +	if (spd.mem_type == SPD_MEMTYPE_DDR) { +		/* +		 * This is a lie.  It should really be 1, but if it is +		 * set to 1, bits overlap into the old controller's +		 * otherwise unused ACSM field.  If we leave it 0, then +		 * the HW will magically treat it as 1 for DDR 1.  Oh Yea. +		 */ +		wr_lat = 0; + +		trtp_clk = 2;		/* By the book. */ +		cke_min_clk = 1;	/* By the book. */ +		four_act = 1;		/* By the book. */ + +	} else { +		wr_lat = caslat - 1; + +		/* Convert SPD value from quarter nanos to picos. */ +		trtp_clk = picos_to_clk(spd.trtp * 250); +		if (trtp_clk < 2) +			trtp_clk = 2; +		trtp_clk += add_lat; + +		cke_min_clk = 3;	/* By the book. */ +		four_act = picos_to_clk(37500);	/* By the book. 1k pages? */ +	} + +	/* +	 * Empirically set ~MCAS-to-preamble override for DDR 2. +	 * Your milage will vary. +	 */ +	cpo = 0; +	if (spd.mem_type == SPD_MEMTYPE_DDR2) { +		if (effective_data_rate == 266) { +			cpo = 0x4;		/* READ_LAT + 1/2 */ +		} else if (effective_data_rate == 333) { +			cpo = 0x6;		/* READ_LAT + 1 */ +		} else if (effective_data_rate == 400) { +			cpo = 0x7;		/* READ_LAT + 5/4 */ +		} else { +			/* Automatic calibration */ +			cpo = 0x1f; +		} +	} + +	ddr->timing_cfg_2 = (0 +		| ((add_lat & 0x7) << 28)		/* ADD_LAT */ +		| ((cpo & 0x1f) << 23)			/* CPO */ +		| ((wr_lat & 0x7) << 19)		/* WR_LAT */ +		| ((trtp_clk & 0x7) << 13)		/* RD_TO_PRE */ +		| ((wr_data_delay & 0x7) << 10)		/* WR_DATA_DELAY */ +		| ((cke_min_clk & 0x7) << 6)		/* CKE_PLS */ +		| ((four_act & 0x1f) << 0)		/* FOUR_ACT */ +		); + +	debug("DDR:timing_cfg_1=0x%08x\n", ddr->timing_cfg_1); +	debug("DDR:timing_cfg_2=0x%08x\n", ddr->timing_cfg_2); + +	/* Check DIMM data bus width */ +	if (spd.dataw_lsb < 64) { +		if (spd.mem_type == SPD_MEMTYPE_DDR) +			burstlen = 0x03; /* 32 bit data bus, burst len is 8 */ +		else +			burstlen = 0x02; /* 32 bit data bus, burst len is 4 */ +		debug("\n   DDR DIMM: data bus width is 32 bit"); +	} else { +		burstlen = 0x02; /* Others act as 64 bit bus, burst len is 4 */ +		debug("\n   DDR DIMM: data bus width is 64 bit"); +	} + +	/* Is this an ECC DDR chip? */ +	if (spd.config == 0x02) +		debug(" with ECC\n"); +	else +		debug(" without ECC\n"); + +	/* Burst length is always 4 for 64 bit data bus, 8 for 32 bit data bus, +	   Burst type is sequential +	 */ +	if (spd.mem_type == SPD_MEMTYPE_DDR) { +		switch (caslat) { +		case 1: +			ddr->sdram_mode = 0x50 | burstlen; /* CL=1.5 */ +			break; +		case 2: +			ddr->sdram_mode = 0x20 | burstlen; /* CL=2.0 */ +			break; +		case 3: +			ddr->sdram_mode = 0x60 | burstlen; /* CL=2.5 */ +			break; +		case 4: +			ddr->sdram_mode = 0x30 | burstlen; /* CL=3.0 */ +			break; +		default: +			printf("DDR:only CL 1.5, 2.0, 2.5, 3.0 is supported\n"); +			return 0; +		} +	} else { +		mode_odt_enable = 0x0;                  /* Default disabled */ +		if (odt_wr_cfg || odt_rd_cfg) { +			/* +			 * Bits 6 and 2 in Extended MRS(1) +			 * Bit 2 == 0x04 == 75 Ohm, with 2 DIMM modules. +			 * Bit 6 == 0x40 == 150 Ohm, with 1 DIMM module. +			 */ +			mode_odt_enable = 0x40;         /* 150 Ohm */ +		} + +		ddr->sdram_mode = +			(0 +			 | (1 << (16 + 10))             /* DQS Differential disable */ +			 | (add_lat << (16 + 3))        /* Additive Latency in EMRS1 */ +			 | (mode_odt_enable << 16)      /* ODT Enable in EMRS1 */ +			 | ((twr_clk - 1) << 9)         /* Write Recovery Autopre */ +			 | (caslat << 4)                /* caslat */ +			 | (burstlen << 0)              /* Burst length */ +			); +	} +	debug("DDR:sdram_mode=0x%08x\n", ddr->sdram_mode); + +	/* +	 * Clear EMRS2 and EMRS3. +	 */ +	ddr->sdram_mode2 = 0; +	debug("DDR: sdram_mode2 = 0x%08x\n", ddr->sdram_mode2); + +	switch (spd.refresh) { +		case 0x00: +		case 0x80: +			refresh_clk = picos_to_clk(15625000); +			break; +		case 0x01: +		case 0x81: +			refresh_clk = picos_to_clk(3900000); +			break; +		case 0x02: +		case 0x82: +			refresh_clk = picos_to_clk(7800000); +			break; +		case 0x03: +		case 0x83: +			refresh_clk = picos_to_clk(31300000); +			break; +		case 0x04: +		case 0x84: +			refresh_clk = picos_to_clk(62500000); +			break; +		case 0x05: +		case 0x85: +			refresh_clk = picos_to_clk(125000000); +			break; +		default: +			refresh_clk = 0x512; +			break; +	} + +	/* +	 * Set BSTOPRE to 0x100 for page mode +	 * If auto-charge is used, set BSTOPRE = 0 +	 */ +	ddr->sdram_interval = ((refresh_clk & 0x3fff) << 16) | 0x100; +	debug("DDR:sdram_interval=0x%08x\n", ddr->sdram_interval); + +	/* +	 * SDRAM Cfg 2 +	 */ +	odt_cfg = 0; +#ifndef CONFIG_NEVER_ASSERT_ODT_TO_CPU +	if (odt_rd_cfg | odt_wr_cfg) { +		odt_cfg = 0x2;		/* ODT to IOs during reads */ +	} +#endif +	if (spd.mem_type == SPD_MEMTYPE_DDR2) { +		ddr->sdram_cfg2 = (0 +			    | (0 << 26)	/* True DQS */ +			    | (odt_cfg << 21)	/* ODT only read */ +			    | (1 << 12)	/* 1 refresh at a time */ +			    ); + +		debug("DDR: sdram_cfg2  = 0x%08x\n", ddr->sdram_cfg2); +	} + +#ifdef CONFIG_SYS_DDR_SDRAM_CLK_CNTL	/* Optional platform specific value */ +	ddr->sdram_clk_cntl = CONFIG_SYS_DDR_SDRAM_CLK_CNTL; +#endif +	debug("DDR:sdram_clk_cntl=0x%08x\n", ddr->sdram_clk_cntl); + +	asm("sync;isync"); + +	udelay(600); + +	/* +	 * Figure out the settings for the sdram_cfg register. Build up +	 * the value in 'sdram_cfg' before writing since the write into +	 * the register will actually enable the memory controller, and all +	 * settings must be done before enabling. +	 * +	 * sdram_cfg[0]   = 1 (ddr sdram logic enable) +	 * sdram_cfg[1]   = 1 (self-refresh-enable) +	 * sdram_cfg[5:7] = (SDRAM type = DDR SDRAM) +	 *			010 DDR 1 SDRAM +	 *			011 DDR 2 SDRAM +	 * sdram_cfg[12] = 0 (32_BE =0 , 64 bit bus mode) +	 * sdram_cfg[13] = 0 (8_BE =0, 4-beat bursts) +	 */ +	if (spd.mem_type == SPD_MEMTYPE_DDR) +		sdram_type = SDRAM_CFG_SDRAM_TYPE_DDR1; +	else +		sdram_type = SDRAM_CFG_SDRAM_TYPE_DDR2; + +	sdram_cfg = (0 +		     | SDRAM_CFG_MEM_EN		/* DDR enable */ +		     | SDRAM_CFG_SREN		/* Self refresh */ +		     | sdram_type		/* SDRAM type */ +		     ); + +	/* sdram_cfg[3] = RD_EN - registered DIMM enable */ +	if (spd.mod_attr & 0x02) +		sdram_cfg |= SDRAM_CFG_RD_EN; + +	/* The DIMM is 32bit width */ +	if (spd.dataw_lsb < 64) { +		if (spd.mem_type == SPD_MEMTYPE_DDR) +			sdram_cfg |= SDRAM_CFG_32_BE | SDRAM_CFG_8_BE; +		if (spd.mem_type == SPD_MEMTYPE_DDR2) +			sdram_cfg |= SDRAM_CFG_32_BE; +	} + +	ddrc_ecc_enable = 0; + +#if defined(CONFIG_DDR_ECC) +	/* Enable ECC with sdram_cfg[2] */ +	if (spd.config == 0x02) { +		sdram_cfg |= 0x20000000; +		ddrc_ecc_enable = 1; +		/* disable error detection */ +		ddr->err_disable = ~ECC_ERROR_ENABLE; +		/* set single bit error threshold to maximum value, +		 * reset counter to zero */ +		ddr->err_sbe = (255 << ECC_ERROR_MAN_SBET_SHIFT) | +				(0 << ECC_ERROR_MAN_SBEC_SHIFT); +	} + +	debug("DDR:err_disable=0x%08x\n", ddr->err_disable); +	debug("DDR:err_sbe=0x%08x\n", ddr->err_sbe); +#endif +	debug("   DDRC ECC mode: %s\n", ddrc_ecc_enable ? "ON":"OFF"); + +#if defined(CONFIG_DDR_2T_TIMING) +	/* +	 * Enable 2T timing by setting sdram_cfg[16]. +	 */ +	sdram_cfg |= SDRAM_CFG_2T_EN; +#endif +	/* Enable controller, and GO! */ +	ddr->sdram_cfg = sdram_cfg; +	asm("sync;isync"); +	udelay(500); + +	debug("DDR:sdram_cfg=0x%08x\n", ddr->sdram_cfg); +	return memsize; /*in MBytes*/ +} +#endif /* CONFIG_SPD_EEPROM */ + +#if defined(CONFIG_DDR_ECC) && !defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) +/* + * Use timebase counter, get_timer() is not availabe + * at this point of initialization yet. + */ +static __inline__ unsigned long get_tbms (void) +{ +	unsigned long tbl; +	unsigned long tbu1, tbu2; +	unsigned long ms; +	unsigned long long tmp; + +	ulong tbclk = get_tbclk(); + +	/* get the timebase ticks */ +	do { +		asm volatile ("mftbu %0":"=r" (tbu1):); +		asm volatile ("mftb %0":"=r" (tbl):); +		asm volatile ("mftbu %0":"=r" (tbu2):); +	} while (tbu1 != tbu2); + +	/* convert ticks to ms */ +	tmp = (unsigned long long)(tbu1); +	tmp = (tmp << 32); +	tmp += (unsigned long long)(tbl); +	ms = tmp/(tbclk/1000); + +	return ms; +} + +/* + * Initialize all of memory for ECC, then enable errors. + */ +void ddr_enable_ecc(unsigned int dram_size) +{ +	volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; +	volatile ddr83xx_t *ddr= &immap->ddr; +	unsigned long t_start, t_end; +	register u64 *p; +	register uint size; +	unsigned int pattern[2]; + +	icache_enable(); +	t_start = get_tbms(); +	pattern[0] = 0xdeadbeef; +	pattern[1] = 0xdeadbeef; + +#if defined(CONFIG_DDR_ECC_INIT_VIA_DMA) +	dma_meminit(pattern[0], dram_size); +#else +	debug("ddr init: CPU FP write method\n"); +	size = dram_size; +	for (p = 0; p < (u64*)(size); p++) { +		ppcDWstore((u32*)p, pattern); +	} +	__asm__ __volatile__ ("sync"); +#endif + +	t_end = get_tbms(); +	icache_disable(); + +	debug("\nREADY!!\n"); +	debug("ddr init duration: %ld ms\n", t_end - t_start); + +	/* Clear All ECC Errors */ +	if ((ddr->err_detect & ECC_ERROR_DETECT_MME) == ECC_ERROR_DETECT_MME) +		ddr->err_detect |= ECC_ERROR_DETECT_MME; +	if ((ddr->err_detect & ECC_ERROR_DETECT_MBE) == ECC_ERROR_DETECT_MBE) +		ddr->err_detect |= ECC_ERROR_DETECT_MBE; +	if ((ddr->err_detect & ECC_ERROR_DETECT_SBE) == ECC_ERROR_DETECT_SBE) +		ddr->err_detect |= ECC_ERROR_DETECT_SBE; +	if ((ddr->err_detect & ECC_ERROR_DETECT_MSE) == ECC_ERROR_DETECT_MSE) +		ddr->err_detect |= ECC_ERROR_DETECT_MSE; + +	/* Disable ECC-Interrupts */ +	ddr->err_int_en &= ECC_ERR_INT_DISABLE; + +	/* Enable errors for ECC */ +	ddr->err_disable &= ECC_ERROR_ENABLE; + +	__asm__ __volatile__ ("sync"); +	__asm__ __volatile__ ("isync"); +} +#endif	/* CONFIG_DDR_ECC */ diff --git a/arch/powerpc/cpu/mpc83xx/speed.c b/arch/powerpc/cpu/mpc83xx/speed.c new file mode 100644 index 000000000..bde7e920a --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/speed.c @@ -0,0 +1,549 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <mpc83xx.h> +#include <command.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* ----------------------------------------------------------------- */ + +typedef enum { +	_unk, +	_off, +	_byp, +	_x8, +	_x4, +	_x2, +	_x1, +	_1x, +	_1_5x, +	_2x, +	_2_5x, +	_3x +} mult_t; + +typedef struct { +	mult_t core_csb_ratio; +	mult_t vco_divider; +} corecnf_t; + +corecnf_t corecnf_tab[] = { +	{_byp, _byp},		/* 0x00 */ +	{_byp, _byp},		/* 0x01 */ +	{_byp, _byp},		/* 0x02 */ +	{_byp, _byp},		/* 0x03 */ +	{_byp, _byp},		/* 0x04 */ +	{_byp, _byp},		/* 0x05 */ +	{_byp, _byp},		/* 0x06 */ +	{_byp, _byp},		/* 0x07 */ +	{_1x, _x2},		/* 0x08 */ +	{_1x, _x4},		/* 0x09 */ +	{_1x, _x8},		/* 0x0A */ +	{_1x, _x8},		/* 0x0B */ +	{_1_5x, _x2},		/* 0x0C */ +	{_1_5x, _x4},		/* 0x0D */ +	{_1_5x, _x8},		/* 0x0E */ +	{_1_5x, _x8},		/* 0x0F */ +	{_2x, _x2},		/* 0x10 */ +	{_2x, _x4},		/* 0x11 */ +	{_2x, _x8},		/* 0x12 */ +	{_2x, _x8},		/* 0x13 */ +	{_2_5x, _x2},		/* 0x14 */ +	{_2_5x, _x4},		/* 0x15 */ +	{_2_5x, _x8},		/* 0x16 */ +	{_2_5x, _x8},		/* 0x17 */ +	{_3x, _x2},		/* 0x18 */ +	{_3x, _x4},		/* 0x19 */ +	{_3x, _x8},		/* 0x1A */ +	{_3x, _x8},		/* 0x1B */ +}; + +/* ----------------------------------------------------------------- */ + +/* + * + */ +int get_clocks(void) +{ +	volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; +	u32 pci_sync_in; +	u8 spmf; +	u8 clkin_div; +	u32 sccr; +	u32 corecnf_tab_index; +	u8 corepll; +	u32 lcrr; + +	u32 csb_clk; +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC831x) || defined(CONFIG_MPC837x) +	u32 tsec1_clk; +	u32 tsec2_clk; +	u32 usbdr_clk; +#endif +#ifdef CONFIG_MPC834x +	u32 usbmph_clk; +#endif +	u32 core_clk; +	u32 i2c1_clk; +#if !defined(CONFIG_MPC832x) +	u32 i2c2_clk; +#endif +#if defined(CONFIG_MPC8315) +	u32 tdm_clk; +#endif +#if defined(CONFIG_MPC837x) +	u32 sdhc_clk; +#endif +	u32 enc_clk; +	u32 lbiu_clk; +	u32 lclk_clk; +	u32 mem_clk; +#if defined(CONFIG_MPC8360) +	u32 mem_sec_clk; +#endif +#if defined(CONFIG_MPC8360) || defined(CONFIG_MPC832x) +	u32 qepmf; +	u32 qepdf; +	u32 qe_clk; +	u32 brg_clk; +#endif +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC831x) +	u32 pciexp1_clk; +	u32 pciexp2_clk; +#endif +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) +	u32 sata_clk; +#endif + +	if ((im->sysconf.immrbar & IMMRBAR_BASE_ADDR) != (u32) im) +		return -1; + +	clkin_div = ((im->clk.spmr & SPMR_CKID) >> SPMR_CKID_SHIFT); + +	if (im->reset.rcwh & HRCWH_PCI_HOST) { +#if defined(CONFIG_83XX_CLKIN) +		pci_sync_in = CONFIG_83XX_CLKIN / (1 + clkin_div); +#else +		pci_sync_in = 0xDEADBEEF; +#endif +	} else { +#if defined(CONFIG_83XX_PCICLK) +		pci_sync_in = CONFIG_83XX_PCICLK; +#else +		pci_sync_in = 0xDEADBEEF; +#endif +	} + +	spmf = ((im->reset.rcwl & HRCWL_SPMF) >> HRCWL_SPMF_SHIFT); +	csb_clk = pci_sync_in * (1 + clkin_div) * spmf; + +	sccr = im->clk.sccr; + +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC831x) || defined(CONFIG_MPC837x) +	switch ((sccr & SCCR_TSEC1CM) >> SCCR_TSEC1CM_SHIFT) { +	case 0: +		tsec1_clk = 0; +		break; +	case 1: +		tsec1_clk = csb_clk; +		break; +	case 2: +		tsec1_clk = csb_clk / 2; +		break; +	case 3: +		tsec1_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_TSEC1CM value */ +		return -2; +	} + +	switch ((sccr & SCCR_USBDRCM) >> SCCR_USBDRCM_SHIFT) { +	case 0: +		usbdr_clk = 0; +		break; +	case 1: +		usbdr_clk = csb_clk; +		break; +	case 2: +		usbdr_clk = csb_clk / 2; +		break; +	case 3: +		usbdr_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_USBDRCM value */ +		return -3; +	} +#endif + +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) +	switch ((sccr & SCCR_TSEC2CM) >> SCCR_TSEC2CM_SHIFT) { +	case 0: +		tsec2_clk = 0; +		break; +	case 1: +		tsec2_clk = csb_clk; +		break; +	case 2: +		tsec2_clk = csb_clk / 2; +		break; +	case 3: +		tsec2_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_TSEC2CM value */ +		return -4; +	} +#elif defined(CONFIG_MPC8313) +	tsec2_clk = tsec1_clk; + +	if (!(sccr & SCCR_TSEC1ON)) +		tsec1_clk = 0; +	if (!(sccr & SCCR_TSEC2ON)) +		tsec2_clk = 0; +#endif + +#if defined(CONFIG_MPC834x) +	switch ((sccr & SCCR_USBMPHCM) >> SCCR_USBMPHCM_SHIFT) { +	case 0: +		usbmph_clk = 0; +		break; +	case 1: +		usbmph_clk = csb_clk; +		break; +	case 2: +		usbmph_clk = csb_clk / 2; +		break; +	case 3: +		usbmph_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_USBMPHCM value */ +		return -5; +	} + +	if (usbmph_clk != 0 && usbdr_clk != 0 && usbmph_clk != usbdr_clk) { +		/* if USB MPH clock is not disabled and +		 * USB DR clock is not disabled then +		 * USB MPH & USB DR must have the same rate +		 */ +		return -6; +	} +#endif +	switch ((sccr & SCCR_ENCCM) >> SCCR_ENCCM_SHIFT) { +	case 0: +		enc_clk = 0; +		break; +	case 1: +		enc_clk = csb_clk; +		break; +	case 2: +		enc_clk = csb_clk / 2; +		break; +	case 3: +		enc_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_ENCCM value */ +		return -7; +	} + +#if defined(CONFIG_MPC837x) +	switch ((sccr & SCCR_SDHCCM) >> SCCR_SDHCCM_SHIFT) { +	case 0: +		sdhc_clk = 0; +		break; +	case 1: +		sdhc_clk = csb_clk; +		break; +	case 2: +		sdhc_clk = csb_clk / 2; +		break; +	case 3: +		sdhc_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_SDHCCM value */ +		return -8; +	} +#endif +#if defined(CONFIG_MPC8315) +	switch ((sccr & SCCR_TDMCM) >> SCCR_TDMCM_SHIFT) { +	case 0: +		tdm_clk = 0; +		break; +	case 1: +		tdm_clk = csb_clk; +		break; +	case 2: +		tdm_clk = csb_clk / 2; +		break; +	case 3: +		tdm_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_TDMCM value */ +		return -8; +	} +#endif + +#if defined(CONFIG_MPC834x) +	i2c1_clk = tsec2_clk; +#elif defined(CONFIG_MPC8360) +	i2c1_clk = csb_clk; +#elif defined(CONFIG_MPC832x) +	i2c1_clk = enc_clk; +#elif defined(CONFIG_MPC831x) +	i2c1_clk = enc_clk; +#elif defined(CONFIG_MPC837x) +	i2c1_clk = sdhc_clk; +#endif +#if !defined(CONFIG_MPC832x) +	i2c2_clk = csb_clk; /* i2c-2 clk is equal to csb clk */ +#endif + +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC831x) +	switch ((sccr & SCCR_PCIEXP1CM) >> SCCR_PCIEXP1CM_SHIFT) { +	case 0: +		pciexp1_clk = 0; +		break; +	case 1: +		pciexp1_clk = csb_clk; +		break; +	case 2: +		pciexp1_clk = csb_clk / 2; +		break; +	case 3: +		pciexp1_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_PCIEXP1CM value */ +		return -9; +	} + +	switch ((sccr & SCCR_PCIEXP2CM) >> SCCR_PCIEXP2CM_SHIFT) { +	case 0: +		pciexp2_clk = 0; +		break; +	case 1: +		pciexp2_clk = csb_clk; +		break; +	case 2: +		pciexp2_clk = csb_clk / 2; +		break; +	case 3: +		pciexp2_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_PCIEXP2CM value */ +		return -10; +	} +#endif + +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) +	switch ((sccr & SCCR_SATA1CM) >> SCCR_SATA1CM_SHIFT) { +	case 0: +		sata_clk = 0; +		break; +	case 1: +		sata_clk = csb_clk; +		break; +	case 2: +		sata_clk = csb_clk / 2; +		break; +	case 3: +		sata_clk = csb_clk / 3; +		break; +	default: +		/* unkown SCCR_SATACM value */ +		return -11; +	} +#endif + +	lbiu_clk = csb_clk * +	           (1 + ((im->reset.rcwl & HRCWL_LBIUCM) >> HRCWL_LBIUCM_SHIFT)); +	lcrr = (im->lbus.lcrr & LCRR_CLKDIV) >> LCRR_CLKDIV_SHIFT; +	switch (lcrr) { +	case 2: +	case 4: +	case 8: +		lclk_clk = lbiu_clk / lcrr; +		break; +	default: +		/* unknown lcrr */ +		return -12; +	} + +	mem_clk = csb_clk * +		  (1 + ((im->reset.rcwl & HRCWL_DDRCM) >> HRCWL_DDRCM_SHIFT)); +	corepll = (im->reset.rcwl & HRCWL_COREPLL) >> HRCWL_COREPLL_SHIFT; +#if defined(CONFIG_MPC8360) +	mem_sec_clk = csb_clk * (1 + +		       ((im->reset.rcwl & HRCWL_LBIUCM) >> HRCWL_LBIUCM_SHIFT)); +#endif + +	corecnf_tab_index = ((corepll & 0x1F) << 2) | ((corepll & 0x60) >> 5); +	if (corecnf_tab_index > (sizeof(corecnf_tab) / sizeof(corecnf_t))) { +		/* corecnf_tab_index is too high, possibly worng value */ +		return -11; +	} +	switch (corecnf_tab[corecnf_tab_index].core_csb_ratio) { +	case _byp: +	case _x1: +	case _1x: +		core_clk = csb_clk; +		break; +	case _1_5x: +		core_clk = (3 * csb_clk) / 2; +		break; +	case _2x: +		core_clk = 2 * csb_clk; +		break; +	case _2_5x: +		core_clk = (5 * csb_clk) / 2; +		break; +	case _3x: +		core_clk = 3 * csb_clk; +		break; +	default: +		/* unkown core to csb ratio */ +		return -13; +	} + +#if defined(CONFIG_MPC8360) || defined(CONFIG_MPC832x) +	qepmf = (im->reset.rcwl & HRCWL_CEPMF) >> HRCWL_CEPMF_SHIFT; +	qepdf = (im->reset.rcwl & HRCWL_CEPDF) >> HRCWL_CEPDF_SHIFT; +	qe_clk = (pci_sync_in * qepmf) / (1 + qepdf); +	brg_clk = qe_clk / 2; +#endif + +	gd->csb_clk = csb_clk; +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC831x) || defined(CONFIG_MPC837x) +	gd->tsec1_clk = tsec1_clk; +	gd->tsec2_clk = tsec2_clk; +	gd->usbdr_clk = usbdr_clk; +#endif +#if defined(CONFIG_MPC834x) +	gd->usbmph_clk = usbmph_clk; +#endif +#if defined(CONFIG_MPC8315) +	gd->tdm_clk = tdm_clk; +#endif +#if defined(CONFIG_MPC837x) +	gd->sdhc_clk = sdhc_clk; +#endif +	gd->core_clk = core_clk; +	gd->i2c1_clk = i2c1_clk; +#if !defined(CONFIG_MPC832x) +	gd->i2c2_clk = i2c2_clk; +#endif +	gd->enc_clk = enc_clk; +	gd->lbiu_clk = lbiu_clk; +	gd->lclk_clk = lclk_clk; +	gd->mem_clk = mem_clk; +#if defined(CONFIG_MPC8360) +	gd->mem_sec_clk = mem_sec_clk; +#endif +#if defined(CONFIG_MPC8360) || defined(CONFIG_MPC832x) +	gd->qe_clk = qe_clk; +	gd->brg_clk = brg_clk; +#endif +#if defined(CONFIG_MPC837x) +	gd->pciexp1_clk = pciexp1_clk; +	gd->pciexp2_clk = pciexp2_clk; +#endif +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) +	gd->sata_clk = sata_clk; +#endif +	gd->pci_clk = pci_sync_in; +	gd->cpu_clk = gd->core_clk; +	gd->bus_clk = gd->csb_clk; +	return 0; + +} + +/******************************************** + * get_bus_freq + * return system bus freq in Hz + *********************************************/ +ulong get_bus_freq(ulong dummy) +{ +	return gd->csb_clk; +} + +int do_clocks (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ +	char buf[32]; + +	printf("Clock configuration:\n"); +	printf("  Core:                %-4s MHz\n", strmhz(buf, gd->core_clk)); +	printf("  Coherent System Bus: %-4s MHz\n", strmhz(buf, gd->csb_clk)); +#if defined(CONFIG_MPC8360) || defined(CONFIG_MPC832x) +	printf("  QE:                  %-4s MHz\n", strmhz(buf, gd->qe_clk)); +	printf("  BRG:                 %-4s MHz\n", strmhz(buf, gd->brg_clk)); +#endif +	printf("  Local Bus Controller:%-4s MHz\n", strmhz(buf, gd->lbiu_clk)); +	printf("  Local Bus:           %-4s MHz\n", strmhz(buf, gd->lclk_clk)); +	printf("  DDR:                 %-4s MHz\n", strmhz(buf, gd->mem_clk)); +#if defined(CONFIG_MPC8360) +	printf("  DDR Secondary:       %-4s MHz\n", strmhz(buf, gd->mem_sec_clk)); +#endif +	printf("  SEC:                 %-4s MHz\n", strmhz(buf, gd->enc_clk)); +	printf("  I2C1:                %-4s MHz\n", strmhz(buf, gd->i2c1_clk)); +#if !defined(CONFIG_MPC832x) +	printf("  I2C2:                %-4s MHz\n", strmhz(buf, gd->i2c2_clk)); +#endif +#if defined(CONFIG_MPC8315) +	printf("  TDM:                 %-4s MHz\n", strmhz(buf, gd->tdm_clk)); +#endif +#if defined(CONFIG_MPC837x) +	printf("  SDHC:                %-4s MHz\n", strmhz(buf, gd->sdhc_clk)); +#endif +#if defined(CONFIG_MPC834x) || defined(CONFIG_MPC831x) || defined(CONFIG_MPC837x) +	printf("  TSEC1:               %-4s MHz\n", strmhz(buf, gd->tsec1_clk)); +	printf("  TSEC2:               %-4s MHz\n", strmhz(buf, gd->tsec2_clk)); +	printf("  USB DR:              %-4s MHz\n", strmhz(buf, gd->usbdr_clk)); +#endif +#if defined(CONFIG_MPC834x) +	printf("  USB MPH:             %-4s MHz\n", strmhz(buf, gd->usbmph_clk)); +#endif +#if defined(CONFIG_MPC837x) +	printf("  PCIEXP1:             %-4s MHz\n", strmhz(buf, gd->pciexp1_clk)); +	printf("  PCIEXP2:             %-4s MHz\n", strmhz(buf, gd->pciexp2_clk)); +#endif +#if defined(CONFIG_MPC837x) || defined(CONFIG_MPC8315) +	printf("  SATA:                %-4s MHz\n", strmhz(buf, gd->sata_clk)); +#endif +	return 0; +} + +U_BOOT_CMD(clocks, 1, 0, do_clocks, +	"print clock configuration", +	"    clocks" +); diff --git a/arch/powerpc/cpu/mpc83xx/start.S b/arch/powerpc/cpu/mpc83xx/start.S new file mode 100644 index 000000000..a7c80792d --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/start.S @@ -0,0 +1,1207 @@ +/* + * Copyright (C) 1998  Dan Malek <dmalek@jlc.net> + * Copyright (C) 1999  Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * Copyright (C) 2000, 2001,2002 Wolfgang Denk <wd@denx.de> + * Copyright Freescale Semiconductor, Inc. 2004, 2006, 2008. + * + * 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 MPC83xx PowerPC based Embedded Boards + */ + +#include <config.h> +#include <mpc83xx.h> +#include <timestamp.h> +#include <version.h> + +#define CONFIG_83XX	1		/* needed for Linux kernel header files*/ +#define _LINUX_CONFIG_H 1	/* avoid reading Linux autoconf.h file */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#ifndef  CONFIG_IDENT_STRING +#define  CONFIG_IDENT_STRING "MPC83XX" +#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 + +#if !defined(CONFIG_NAND_SPL) && !defined(CONFIG_SYS_RAMBOOT) +#define CONFIG_SYS_FLASHBOOT +#endif + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ +	START_GOT +	GOT_ENTRY(_GOT2_TABLE_) +	GOT_ENTRY(__bss_start) +	GOT_ENTRY(_end) + +#ifndef CONFIG_NAND_SPL +	GOT_ENTRY(_FIXUP_TABLE_) +	GOT_ENTRY(_start) +	GOT_ENTRY(_start_of_vectors) +	GOT_ENTRY(_end_of_vectors) +	GOT_ENTRY(transfer_to_handler) +#endif +	END_GOT + +/* + * The Hard Reset Configuration Word (HRCW) table is in the first 64 + * (0x40) bytes of flash.  It has 8 bytes, but each byte is repeated 8 + * times so the processor can fetch it out of flash whether the flash + * is 8, 16, 32, or 64 bits wide (hardware trickery). + */ +	.text +#define _HRCW_TABLE_ENTRY(w)		\ +	.fill	8,1,(((w)>>24)&0xff);	\ +	.fill	8,1,(((w)>>16)&0xff);	\ +	.fill	8,1,(((w)>> 8)&0xff);	\ +	.fill	8,1,(((w)    )&0xff) + +	_HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_LOW) +	_HRCW_TABLE_ENTRY(CONFIG_SYS_HRCW_HIGH) + +/* + * Magic number and version string - put it after the HRCW since it + * cannot be first in flash like it is in many other processors. + */ +	.long	0x27051956		/* U-Boot Magic Number */ + +	.globl	version_string +version_string: +	.ascii U_BOOT_VERSION +	.ascii " (", U_BOOT_DATE, " - ", U_BOOT_TIME, ")" +	.ascii " ", CONFIG_IDENT_STRING, "\0" + +	.align 2 + +	.globl enable_addr_trans +enable_addr_trans: +	/* enable address translation */ +	mfmsr	r5 +	ori	r5, r5, (MSR_IR | MSR_DR) +	mtmsr	r5 +	isync +	blr + +	.globl disable_addr_trans +disable_addr_trans: +	/* disable address translation */ +	mflr	r4 +	mfmsr	r3 +	andi.	r0, r3, (MSR_IR | MSR_DR) +	beqlr +	andc	r3, r3, r0 +	mtspr	SRR0, r4 +	mtspr	SRR1, r3 +	rfi + +	.globl get_pvr +get_pvr: +	mfspr	r3, PVR +	blr + +	.globl	ppcDWstore +ppcDWstore: +	lfd	1, 0(r4) +	stfd	1, 0(r3) +	blr + +	.globl	ppcDWload +ppcDWload: +	lfd	1, 0(r3) +	stfd	1, 0(r4) +	blr + +#ifndef CONFIG_DEFAULT_IMMR +#error CONFIG_DEFAULT_IMMR must be defined +#endif /* CONFIG_SYS_DEFAULT_IMMR */ +#ifndef CONFIG_SYS_IMMR +#define CONFIG_SYS_IMMR CONFIG_DEFAULT_IMMR +#endif /* CONFIG_SYS_IMMR */ + +/* + * After configuration, a system reset exception is executed using the + * vector at offset 0x100 relative to the base set by MSR[IP]. If + * MSR[IP] is 0, the base address is 0x00000000. If MSR[IP] is 1, the + * base address is 0xfff00000. In the case of a Power On Reset or Hard + * Reset, the value of MSR[IP] is determined by the CIP field in the + * HRCW. + * + * Other bits in the HRCW set up the Base Address and Port Size in BR0. + * This determines the location of the boot ROM (flash or EPROM) in the + * processor's address space at boot time. As long as the HRCW is set up + * so that we eventually end up executing the code below when the + * processor executes the reset exception, the actual values used should + * not matter. + * + * Once we have got here, the address mask in OR0 is cleared so that the + * bottom 32K of the boot ROM is effectively repeated all throughout the + * processor's address space, after which we can jump to the absolute + * address at which the boot ROM was linked at compile time, and proceed + * to initialise the memory controller without worrying if the rug will + * be pulled out from under us, so to speak (it will be fine as long as + * we configure BR0 with the same boot ROM link address). + */ +	. = EXC_OFF_SYS_RESET + +	.globl	_start +_start: /* time t 0 */ +	li	r21, BOOTFLAG_COLD  /* Normal Power-On: Boot from FLASH*/ +	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: /* time t 3 */ +	lis	r4, CONFIG_DEFAULT_IMMR@h +	nop +boot_warm: /* time t 5 */ +	mfmsr	r5			/* save msr contents	*/ + +	/* 83xx manuals prescribe a specific sequence for updating IMMRBAR. */ +	bl	1f +1:	mflr	r7 + +	lis	r3, CONFIG_SYS_IMMR@h +	ori	r3, r3, CONFIG_SYS_IMMR@l + +	lwz	r6, IMMRBAR(r4) +	isync + +	stw	r3, IMMRBAR(r4) +	lwz	r6, 0(r7)		/* Arbitrary external load */ +	isync + +	lwz	r6, IMMRBAR(r3) +	isync + +	/* Initialise the E300 processor core		*/ +	/*------------------------------------------*/ + +#ifdef CONFIG_NAND_SPL +	/* The FCM begins execution after only the first page +	 * is loaded.  Wait for the rest before branching +	 * to another flash page. +	 */ +1:	lwz	r6, 0x50b0(r3) +	andi.	r6, r6, 1 +	beq	1b +#endif + +	bl	init_e300_core + +#ifdef CONFIG_SYS_FLASHBOOT + +	/* Inflate flash location so it appears everywhere, calculate */ +	/* the absolute address in final location of the FLASH, jump  */ +	/* there and deflate the flash size back to minimal size      */ +	/*------------------------------------------------------------*/ +	bl map_flash_by_law1 +	lis r4, (CONFIG_SYS_MONITOR_BASE)@h +	ori r4, r4, (CONFIG_SYS_MONITOR_BASE)@l +	addi r5, r4, in_flash - _start + EXC_OFF_SYS_RESET +	mtlr r5 +	blr +in_flash: +#if 1 /* Remapping flash with LAW0. */ +	bl remap_flash_by_law0 +#endif +#endif	/* CONFIG_SYS_FLASHBOOT */ + +	/* setup the bats */ +	bl	setup_bats +	sync + +	/* +	 * Cache must be enabled here for stack-in-cache trick. +	 * This means we need to enable the BATS. +	 * This means: +	 *   1) for the EVB, original gt regs need to be mapped +	 *   2) need to have an IBAT for the 0xf region, +	 *      we are running there! +	 * Cache should be turned on after BATs, since by default +	 * everything is write-through. +	 * The init-mem BAT can be reused after reloc. The old +	 * gt-regs BAT can be reused after board_init_f calls +	 * board_early_init_f (EVB only). +	 */ +	/* enable address translation */ +	bl	enable_addr_trans +	sync + +	/* enable the data cache */ +	bl	dcache_enable +	sync +#ifdef CONFIG_SYS_INIT_RAM_LOCK +	bl	lock_ram_in_cache +	sync +#endif + +	/* set up the stack pointer in our newly created +	 * cache-ram (r1) */ +	lis	r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@h +	ori	r1, r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_GBL_DATA_OFFSET)@l + +	li	r0, 0		/* Make room for stack frame header and	*/ +	stwu	r0, -4(r1)	/* clear final stack frame so that	*/ +	stwu	r0, -4(r1)	/* stack backtraces terminate cleanly	*/ + + +	/* let the C-code set up the rest	                    */ +	/*				                            */ +	/* Be careful to keep code relocatable & stack humble   */ +	/*------------------------------------------------------*/ + +	GET_GOT			/* initialize GOT access	*/ + +	/* r3: IMMR */ +	lis	r3, CONFIG_SYS_IMMR@h +	/* run low-level CPU init code (in Flash)*/ +	bl	cpu_init_f + +	/* r3: BOOTFLAG */ +	mr	r3, r21 +	/* run 1st part of board init code (in Flash)*/ +	bl	board_init_f + +#ifndef CONFIG_NAND_SPL +/* + * 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. */ +#ifndef FIXME +	STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) +#endif + +/* 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 +#endif /* !CONFIG_NAND_SPL */ + +/* + * This code initialises the E300 processor core + * (conforms to PowerPC 603e spec) + * Note: expects original MSR contents to be in r5. + */ +	.globl	init_e300_core +init_e300_core: /* time t 10 */ +	/* 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 */ + + +	lis	r3, CONFIG_SYS_IMMR@h +#if defined(CONFIG_WATCHDOG) +	/* Initialise the Wathcdog values and reset it (if req) */ +	/*------------------------------------------------------*/ +	lis r4, CONFIG_SYS_WATCHDOG_VALUE +	ori r4, r4, (SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR) +	stw r4, SWCRR(r3) + +	/* and reset it */ + +	li	r4, 0x556C +	sth	r4, SWSRR@l(r3) +	li	r4, -0x55C7 +	sth	r4, SWSRR@l(r3) +#else +	/* Disable Wathcdog  */ +	/*-------------------*/ +	lwz r4, SWCRR(r3) +	/* Check to see if its enabled for disabling +	   once disabled by SW you can't re-enable */ +	andi. r4, r4, 0x4 +	beq 1f +	xor r4, r4, r4 +	stw r4, SWCRR(r3) +1: +#endif /* CONFIG_WATCHDOG */ + +#if defined(CONFIG_MASK_AER_AO) +	/* Write the Arbiter Event Enable to mask Address Only traps. */ +	/* This prevents the dcbz instruction from being trapped when */ +	/* HID0_ABE Address Broadcast Enable is set and the MEMORY    */ +	/* COHERENCY bit is set in the WIMG bits, which is often      */ +	/* needed for PCI operation.                                  */ +	lwz	r4, 0x0808(r3) +	rlwinm	r0, r4, 0, ~AER_AO +	stw	r0, 0x0808(r3) +#endif /* CONFIG_MASK_AER_AO */ + +	/* Initialize the Hardware Implementation-dependent Registers */ +	/* HID0 also contains cache control			*/ +	/* - force invalidation of data and instruction caches  */ +	/*------------------------------------------------------*/ + +	lis	r3, CONFIG_SYS_HID0_INIT@h +	ori	r3, r3, (CONFIG_SYS_HID0_INIT | HID0_ICFI | HID0_DCFI)@l +	SYNC +	mtspr	HID0, r3 + +	lis	r3, CONFIG_SYS_HID0_FINAL@h +	ori	r3, r3, (CONFIG_SYS_HID0_FINAL & ~(HID0_ICFI | HID0_DCFI))@l +	SYNC +	mtspr	HID0, r3 + +	lis	r3, CONFIG_SYS_HID2@h +	ori	r3, r3, CONFIG_SYS_HID2@l +	SYNC +	mtspr	HID2, r3 + +	/* Done!						*/ +	/*------------------------------*/ +	blr + +	/* setup_bats - set them up to some initial state */ +	.globl	setup_bats +setup_bats: +	addis	r0, r0, 0x0000 + +	/* IBAT 0 */ +	addis	r4, r0, CONFIG_SYS_IBAT0L@h +	ori	r4, r4, CONFIG_SYS_IBAT0L@l +	addis	r3, r0, CONFIG_SYS_IBAT0U@h +	ori	r3, r3, CONFIG_SYS_IBAT0U@l +	mtspr	IBAT0L, r4 +	mtspr	IBAT0U, r3 + +	/* DBAT 0 */ +	addis	r4, r0, CONFIG_SYS_DBAT0L@h +	ori	r4, r4, CONFIG_SYS_DBAT0L@l +	addis	r3, r0, CONFIG_SYS_DBAT0U@h +	ori	r3, r3, CONFIG_SYS_DBAT0U@l +	mtspr	DBAT0L, r4 +	mtspr	DBAT0U, r3 + +	/* IBAT 1 */ +	addis	r4, r0, CONFIG_SYS_IBAT1L@h +	ori	r4, r4, CONFIG_SYS_IBAT1L@l +	addis	r3, r0, CONFIG_SYS_IBAT1U@h +	ori	r3, r3, CONFIG_SYS_IBAT1U@l +	mtspr	IBAT1L, r4 +	mtspr	IBAT1U, r3 + +	/* DBAT 1 */ +	addis	r4, r0, CONFIG_SYS_DBAT1L@h +	ori	r4, r4, CONFIG_SYS_DBAT1L@l +	addis	r3, r0, CONFIG_SYS_DBAT1U@h +	ori	r3, r3, CONFIG_SYS_DBAT1U@l +	mtspr	DBAT1L, r4 +	mtspr	DBAT1U, r3 + +	/* IBAT 2 */ +	addis	r4, r0, CONFIG_SYS_IBAT2L@h +	ori	r4, r4, CONFIG_SYS_IBAT2L@l +	addis	r3, r0, CONFIG_SYS_IBAT2U@h +	ori	r3, r3, CONFIG_SYS_IBAT2U@l +	mtspr	IBAT2L, r4 +	mtspr	IBAT2U, r3 + +	/* DBAT 2 */ +	addis	r4, r0, CONFIG_SYS_DBAT2L@h +	ori	r4, r4, CONFIG_SYS_DBAT2L@l +	addis	r3, r0, CONFIG_SYS_DBAT2U@h +	ori	r3, r3, CONFIG_SYS_DBAT2U@l +	mtspr	DBAT2L, r4 +	mtspr	DBAT2U, r3 + +	/* IBAT 3 */ +	addis	r4, r0, CONFIG_SYS_IBAT3L@h +	ori	r4, r4, CONFIG_SYS_IBAT3L@l +	addis	r3, r0, CONFIG_SYS_IBAT3U@h +	ori	r3, r3, CONFIG_SYS_IBAT3U@l +	mtspr	IBAT3L, r4 +	mtspr	IBAT3U, r3 + +	/* DBAT 3 */ +	addis	r4, r0, CONFIG_SYS_DBAT3L@h +	ori	r4, r4, CONFIG_SYS_DBAT3L@l +	addis	r3, r0, CONFIG_SYS_DBAT3U@h +	ori	r3, r3, CONFIG_SYS_DBAT3U@l +	mtspr	DBAT3L, r4 +	mtspr	DBAT3U, r3 + +#ifdef CONFIG_HIGH_BATS +	/* IBAT 4 */ +	addis   r4, r0, CONFIG_SYS_IBAT4L@h +	ori     r4, r4, CONFIG_SYS_IBAT4L@l +	addis   r3, r0, CONFIG_SYS_IBAT4U@h +	ori     r3, r3, CONFIG_SYS_IBAT4U@l +	mtspr   IBAT4L, r4 +	mtspr   IBAT4U, r3 + +	/* DBAT 4 */ +	addis   r4, r0, CONFIG_SYS_DBAT4L@h +	ori     r4, r4, CONFIG_SYS_DBAT4L@l +	addis   r3, r0, CONFIG_SYS_DBAT4U@h +	ori     r3, r3, CONFIG_SYS_DBAT4U@l +	mtspr   DBAT4L, r4 +	mtspr   DBAT4U, r3 + +	/* IBAT 5 */ +	addis   r4, r0, CONFIG_SYS_IBAT5L@h +	ori     r4, r4, CONFIG_SYS_IBAT5L@l +	addis   r3, r0, CONFIG_SYS_IBAT5U@h +	ori     r3, r3, CONFIG_SYS_IBAT5U@l +	mtspr   IBAT5L, r4 +	mtspr   IBAT5U, r3 + +	/* DBAT 5 */ +	addis   r4, r0, CONFIG_SYS_DBAT5L@h +	ori     r4, r4, CONFIG_SYS_DBAT5L@l +	addis   r3, r0, CONFIG_SYS_DBAT5U@h +	ori     r3, r3, CONFIG_SYS_DBAT5U@l +	mtspr   DBAT5L, r4 +	mtspr   DBAT5U, r3 + +	/* IBAT 6 */ +	addis   r4, r0, CONFIG_SYS_IBAT6L@h +	ori     r4, r4, CONFIG_SYS_IBAT6L@l +	addis   r3, r0, CONFIG_SYS_IBAT6U@h +	ori     r3, r3, CONFIG_SYS_IBAT6U@l +	mtspr   IBAT6L, r4 +	mtspr   IBAT6U, r3 + +	/* DBAT 6 */ +	addis   r4, r0, CONFIG_SYS_DBAT6L@h +	ori     r4, r4, CONFIG_SYS_DBAT6L@l +	addis   r3, r0, CONFIG_SYS_DBAT6U@h +	ori     r3, r3, CONFIG_SYS_DBAT6U@l +	mtspr   DBAT6L, r4 +	mtspr   DBAT6U, r3 + +	/* IBAT 7 */ +	addis   r4, r0, CONFIG_SYS_IBAT7L@h +	ori     r4, r4, CONFIG_SYS_IBAT7L@l +	addis   r3, r0, CONFIG_SYS_IBAT7U@h +	ori     r3, r3, CONFIG_SYS_IBAT7U@l +	mtspr   IBAT7L, r4 +	mtspr   IBAT7U, r3 + +	/* DBAT 7 */ +	addis   r4, r0, CONFIG_SYS_DBAT7L@h +	ori     r4, r4, CONFIG_SYS_DBAT7L@l +	addis   r3, r0, CONFIG_SYS_DBAT7U@h +	ori     r3, r3, CONFIG_SYS_DBAT7U@l +	mtspr   DBAT7L, r4 +	mtspr   DBAT7U, r3 +#endif + +	isync + +	/* 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 +	 * +	 */ +	lis	r3, 0 +	lis	r5, 2 + +1: +	tlbie	r3 +	addi	r3, r3, 0x1000 +	cmp	0, 0, r3, r5 +	blt	1b + +	blr + +/* Cache functions. + * + * Note: requires that all cache bits in + * HID0 are in the low half word. + */ +	.globl	icache_enable +icache_enable: +	mfspr	r3, HID0 +	ori	r3, r3, HID0_ICE +	li	r4, HID0_ICFI|HID0_ILOCK +	andc	r3, r3, r4 +	ori	r4, r3, HID0_ICFI +	isync +	mtspr	HID0, r4    /* sets enable and invalidate, clears lock */ +	isync +	mtspr	HID0, r3	/* clears invalidate */ +	blr + +	.globl	icache_disable +icache_disable: +	mfspr	r3, HID0 +	lis	r4, 0 +	ori	r4, r4, HID0_ICE|HID0_ICFI|HID0_ILOCK +	andc	r3, r3, r4 +	isync +	mtspr	HID0, r3	/* clears invalidate, enable and lock */ +	blr + +	.globl	icache_status +icache_status: +	mfspr	r3, HID0 +	rlwinm	r3, r3, (31 - HID0_ICE_SHIFT + 1), 31, 31 +	blr + +	.globl	dcache_enable +dcache_enable: +	mfspr	r3, HID0 +	li	r5, HID0_DCFI|HID0_DLOCK +	andc	r3, r3, r5 +	ori	r3, r3, HID0_DCE +	sync +	mtspr	HID0, r3		/* enable, no invalidate */ +	blr + +	.globl	dcache_disable +dcache_disable: +	mflr	r4 +	bl	flush_dcache		/* uses r3 and r5 */ +	mfspr	r3, HID0 +	li	r5, HID0_DCE|HID0_DLOCK +	andc	r3, r3, r5 +	ori	r5, r3, HID0_DCFI +	sync +	mtspr	HID0, r5	/* sets invalidate, clears enable and lock */ +	sync +	mtspr	HID0, r3	/* clears invalidate */ +	mtlr	r4 +	blr + +	.globl	dcache_status +dcache_status: +	mfspr	r3, HID0 +	rlwinm	r3, r3, (31 - HID0_DCE_SHIFT + 1), 31, 31 +	blr + +	.globl	flush_dcache +flush_dcache: +	lis	r3, 0 +	lis	r5, CONFIG_SYS_CACHELINE_SIZE +1:	cmp	0, 1, r3, r5 +	bge	2f +	lwz	r5, 0(r3) +	lis	r5, CONFIG_SYS_CACHELINE_SIZE +	addi	r3, r3, 0x4 +	b	1b +2:	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(__bss_start) +	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) + +	/* copy */ +1:	lwzu	r0,4(r8) +	stwu	r0,4(r7) +	bdnz	1b + +	addi	r0,r5,3 +	srwi.	r0,r0,2 +	mtctr	r0 +	la	r8,-4(r4) +	la	r7,-4(r3) + +	/* and compare */ +20:	lwzu	r20,4(r8) +	lwzu	r21,4(r7) +	xor. r22, r20, r21 +	bne  30f +	bdnz	20b +	b 4f + +	/* compare failed */ +30:	li r3, 0 +	blr + +2:	slwi	r0,r0,2 /* re copy in reverse order ... y do we needed it? */ +	add	r8,r4,r0 +	add	r7,r3,r0 +3:	lwzu	r0,-4(r8) +	stwu	r0,-4(r7) +	bdnz	3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4:	cmpwi	r6,0 +	add	r5,r3,r5 +	beq	7f		/* Always flush prefetch queue in any case */ +	subi	r0,r6,1 +	andc	r3,r3,r0 +	mr	r4,r3 +5:	dcbst	0,r4 +	add	r4,r4,r6 +	cmplw	r4,r5 +	blt	5b +	sync			/* Wait for all dcbst to complete on bus */ +	mr	r4,r3 +6:	icbi	0,r4 +	add	r4,r4,r6 +	cmplw	r4,r5 +	blt	6b +7:	sync			/* Wait for all icbi to complete on bus	*/ +	isync + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ +	addi	r0, r10, in_ram - _start + EXC_OFF_SYS_RESET +	mtlr	r0 +	blr + +in_ram: + +	/* +	 * Relocation Function, 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 + +#ifndef CONFIG_NAND_SPL +	/* +	 * 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: +#endif + +clear_bss: +	/* +	 * Now clear BSS segment +	 */ +	lwz	r3,GOT(__bss_start) +#if defined(CONFIG_HYMOD) +	/* +	 * For HYMOD - the environment is the very last item in flash. +	 * The real .bss stops just before environment starts, so only +	 * clear up to that point. +	 * +	 * taken from mods for FADS board +	 */ +	lwz	r4,GOT(environment) +#else +	lwz	r4,GOT(_end) +#endif + +	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 + +#ifndef CONFIG_NAND_SPL +	/* +	 * 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 + +#endif /* !CONFIG_NAND_SPL */ + +#ifdef CONFIG_SYS_INIT_RAM_LOCK +lock_ram_in_cache: +	/* Allocate Initial RAM in data cache. +	 */ +	lis	r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@h +	ori	r3, r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@l +	li	r4, ((CONFIG_SYS_INIT_RAM_END & ~31) + \ +		     (CONFIG_SYS_INIT_RAM_ADDR & 31) + 31) / 32 +	mtctr	r4 +1: +	dcbz	r0, r3 +	addi	r3, r3, 32 +	bdnz	1b + +	/* Lock the data cache */ +	mfspr	r0, HID0 +	ori	r0, r0, HID0_DLOCK +	sync +	mtspr	HID0, r0 +	sync +	blr + +#ifndef CONFIG_NAND_SPL +.globl unlock_ram_in_cache +unlock_ram_in_cache: +	/* invalidate the INIT_RAM section */ +	lis	r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@h +	ori	r3, r3, (CONFIG_SYS_INIT_RAM_ADDR & ~31)@l +	li	r4, ((CONFIG_SYS_INIT_RAM_END & ~31) + \ +		     (CONFIG_SYS_INIT_RAM_ADDR & 31) + 31) / 32 +	mtctr	r4 +1:	icbi	r0, r3 +	dcbi	r0, r3 +	addi	r3, r3, 32 +	bdnz	1b +	sync			/* Wait for all icbi to complete on bus	*/ +	isync + +	/* Unlock the data cache and invalidate it */ +	mfspr   r3, HID0 +	li	r5, HID0_DLOCK|HID0_DCFI +	andc	r3, r3, r5		/* no invalidate, unlock */ +	ori	r5, r3, HID0_DCFI	/* invalidate, unlock */ +	sync +	mtspr	HID0, r5		/* invalidate, unlock */ +	sync +	mtspr	HID0, r3		/* no invalidate, unlock */ +	blr +#endif /* !CONFIG_NAND_SPL */ +#endif /* CONFIG_SYS_INIT_RAM_LOCK */ + +#ifdef CONFIG_SYS_FLASHBOOT +map_flash_by_law1: +	/* When booting from ROM (Flash or EPROM), clear the  */ +	/* Address Mask in OR0 so ROM appears everywhere      */ +	/*----------------------------------------------------*/ +	lis	r3, (CONFIG_SYS_IMMR)@h  /* r3 <= CONFIG_SYS_IMMR    */ +	lwz	r4, OR0@l(r3) +	li	r5, 0x7fff        /* r5 <= 0x00007FFFF */ +	and	r4, r4, r5 +	stw	r4, OR0@l(r3)     /* OR0 <= OR0 & 0x00007FFFF */ + +	/* As MPC8349E User's Manual presented, when RCW[BMS] is set to 0, +	 * system will boot from 0x0000_0100, and the LBLAWBAR0[BASE_ADDR] +	 * reset value is 0x00000; when RCW[BMS] is set to 1, system will boot +	 * from 0xFFF0_0100, and the LBLAWBAR0[BASE_ADDR] reset value is +	 * 0xFF800.  From the hard resetting to here, the processor fetched and +	 * executed the instructions one by one.  There is not absolutely +	 * jumping happened.  Laterly, the u-boot code has to do an absolutely +	 * jumping to tell the CPU instruction fetching component what the +	 * u-boot TEXT base address is.  Because the TEXT base resides in the +	 * boot ROM memory space, to garantee the code can run smoothly after +	 * that jumping, we must map in the entire boot ROM by Local Access +	 * Window.  Sometimes, we desire an non-0x00000 or non-0xFF800 starting +	 * address for boot ROM, such as 0xFE000000.  In this case, the default +	 * LBIU Local Access Widow 0 will not cover this memory space.  So, we +	 * need another window to map in it. +	 */ +	lis r4, (CONFIG_SYS_FLASH_BASE)@h +	ori r4, r4, (CONFIG_SYS_FLASH_BASE)@l +	stw r4, LBLAWBAR1(r3) /* LBLAWBAR1 <= CONFIG_SYS_FLASH_BASE */ + +	/* Store 0x80000012 + log2(CONFIG_SYS_FLASH_SIZE) into LBLAWAR1 */ +	lis r4, (0x80000012)@h +	ori r4, r4, (0x80000012)@l +	li r5, CONFIG_SYS_FLASH_SIZE +1:	srawi. r5, r5, 1	/* r5 = r5 >> 1 */ +	addi r4, r4, 1 +	bne 1b + +	stw r4, LBLAWAR1(r3) /* LBLAWAR1 <= 8MB Flash Size */ +	blr + +	/* Though all the LBIU Local Access Windows and LBC Banks will be +	 * initialized in the C code, we'd better configure boot ROM's +	 * window 0 and bank 0 correctly at here. +	 */ +remap_flash_by_law0: +	/* Initialize the BR0 with the boot ROM starting address. */ +	lwz r4, BR0(r3) +	li  r5, 0x7FFF +	and r4, r4, r5 +	lis r5, (CONFIG_SYS_FLASH_BASE & 0xFFFF8000)@h +	ori r5, r5, (CONFIG_SYS_FLASH_BASE & 0xFFFF8000)@l +	or  r5, r5, r4 +	stw r5, BR0(r3) /* r5 <= (CONFIG_SYS_FLASH_BASE & 0xFFFF8000) | (BR0 & 0x00007FFF) */ + +	lwz r4, OR0(r3) +	lis r5, ~((CONFIG_SYS_FLASH_SIZE << 4) - 1) +	or r4, r4, r5 +	stw r4, OR0(r3) + +	lis r4, (CONFIG_SYS_FLASH_BASE)@h +	ori r4, r4, (CONFIG_SYS_FLASH_BASE)@l +	stw r4, LBLAWBAR0(r3) /* LBLAWBAR0 <= CONFIG_SYS_FLASH_BASE */ + +	/* Store 0x80000012 + log2(CONFIG_SYS_FLASH_SIZE) into LBLAWAR0 */ +	lis r4, (0x80000012)@h +	ori r4, r4, (0x80000012)@l +	li r5, CONFIG_SYS_FLASH_SIZE +1:	srawi. r5, r5, 1 /* r5 = r5 >> 1 */ +	addi r4, r4, 1 +	bne 1b +	stw r4, LBLAWAR0(r3) /* LBLAWAR0 <= Flash Size */ + + +	xor r4, r4, r4 +	stw r4, LBLAWBAR1(r3) +	stw r4, LBLAWAR1(r3) /* Off LBIU LAW1 */ +	blr +#endif /* CONFIG_SYS_FLASHBOOT */ diff --git a/arch/powerpc/cpu/mpc83xx/traps.c b/arch/powerpc/cpu/mpc83xx/traps.c new file mode 100644 index 000000000..9d71b8b73 --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/traps.c @@ -0,0 +1,266 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (C) 1995-1996  Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * This file handles the architecture-dependent parts of hardware + * exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> +#include <asm/mpc8349_pci.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Returns 0 if exception not found and fixup otherwise.  */ +extern unsigned long search_exception_table(unsigned long); + +#define END_OF_MEM	(gd->bd->bi_memstart + gd->bd->bi_memsize) + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ +	int cnt = 0; +	unsigned long i; + +	puts ("Call backtrace: "); +	while (sp) { +		if ((uint)sp > END_OF_MEM) +			break; + +		i = sp[1]; +		if (cnt++ % 7 == 0) +			putc ('\n'); +		printf("%08lX ", i); +		if (cnt > 32) break; +		sp = (unsigned long *)*sp; +	} +	putc ('\n'); +} + +void show_regs(struct pt_regs * regs) +{ +	int i; + +	printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", +	       regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); +	printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", +	       regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, +	       regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, +	       regs->msr&MSR_IR ? 1 : 0, +	       regs->msr&MSR_DR ? 1 : 0); + +	putc ('\n'); +	for (i = 0;  i < 32;  i++) { +		if ((i % 8) == 0) { +			printf("GPR%02d: ", i); +		} + +		printf("%08lX ", regs->gpr[i]); +		if ((i % 8) == 7) { +			putc ('\n'); +		} +	} +} + + +void +_exception(int signr, struct pt_regs *regs) +{ +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +#ifdef CONFIG_PCI +void dump_pci (void) +{ +/* +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	printf ("PCI: err status %x err mask %x err ctrl %x\n", +		le32_to_cpu (immap->im_pci.pci_esr), +		le32_to_cpu (immap->im_pci.pci_emr), +		le32_to_cpu (immap->im_pci.pci_ecr)); +	printf ("     error address %x error data %x ctrl %x\n", +		le32_to_cpu (immap->im_pci.pci_eacr), +		le32_to_cpu (immap->im_pci.pci_edcr), +		le32_to_cpu (immap->im_pci.pci_eccr)); +*/ +} +#endif + +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. +	 */ +#ifdef CONFIG_PCI +#if 0 +	volatile immap_t *immap  = (immap_t *)CONFIG_SYS_IMMR; +#ifdef DEBUG +	dump_pci(); +#endif +	/* clear the error in the error status register */ +	if(immap->im_pci.pci_esr & cpu_to_le32(PCI_ERROR_PCI_NO_RSP)) { +		immap->im_pci.pci_esr = cpu_to_le32(PCI_ERROR_PCI_NO_RSP); +		return; +	} +#endif +#endif /* CONFIG_PCI */ +	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 + +	puts ("Machine check in kernel mode.\n" +		"Caused by (from msr): "); +	printf("regs %p ",regs); +	switch( regs->msr & 0x000F0000) { +	case (0x80000000>>12): +		puts ("Machine check signal - probably due to mm fault\n" +			"with mmu off\n"); +		break; +	case (0x80000000>>13): +		puts ("Transfer error ack signal\n"); +		break; +	case (0x80000000>>14): +		puts ("Data parity signal\n"); +		break; +	case (0x80000000>>15): +		puts ("Address parity signal\n"); +		break; +	default: +		puts ("Unknown values in msr\n"); +	} +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +#ifdef CONFIG_PCI +	dump_pci(); +#endif +	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/mpc83xx/u-boot.lds b/arch/powerpc/cpu/mpc83xx/u-boot.lds new file mode 100644 index 000000000..0b74a13fb --- /dev/null +++ b/arch/powerpc/cpu/mpc83xx/u-boot.lds @@ -0,0 +1,121 @@ +/* + * (C) Copyright 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 + */ + +OUTPUT_ARCH(powerpc) +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/mpc83xx/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 = .); +} +ENTRY(_start) |