diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /roms/u-boot/arch/powerpc/cpu/ppc4xx | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip  | |
Diffstat (limited to 'roms/u-boot/arch/powerpc/cpu/ppc4xx')
41 files changed, 20993 insertions, 0 deletions
diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/40x_spd_sdram.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/40x_spd_sdram.c new file mode 100644 index 00000000..717e7bad --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/40x_spd_sdram.c @@ -0,0 +1,444 @@ +/* + * arch/powerpc/cpu/ppc4xx/40x_spd_sdram.c + * This SPD SDRAM detection code supports IBM/AMCC PPC44x cpu with a + * SDRAM controller. Those are all current 405 PPC's. + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@attbi.com + * + * Based on code by: + * + * Kenneth Johansson ,Ericsson AB. + * kenneth.johansson@etx.ericsson.se + * + * hacked up by bill hunter. fixed so we could run before + * serial_init and console_init. previous version avoided this by + * running out of cache memory during serial/console init, then running + * this code later. + * + * (C) Copyright 2002 + * Jun Gu, Artesyn Technology, jung@artesyncp.com + * Support for AMCC 440 based on OpenBIOS draminit.c from IBM. + * + * (C) Copyright 2005 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/processor.h> +#include <i2c.h> +#include <asm/ppc4xx.h> + +#if defined(CONFIG_SPD_EEPROM) && !defined(CONFIG_440) + +/* + * Set default values + */ +#define ONE_BILLION	1000000000 + +#define	 SDRAM0_CFG_DCE		0x80000000 +#define	 SDRAM0_CFG_SRE		0x40000000 +#define	 SDRAM0_CFG_PME		0x20000000 +#define	 SDRAM0_CFG_MEMCHK	0x10000000 +#define	 SDRAM0_CFG_REGEN	0x08000000 +#define	 SDRAM0_CFG_ECCDD	0x00400000 +#define	 SDRAM0_CFG_EMDULR	0x00200000 +#define	 SDRAM0_CFG_DRW_SHIFT	(31-6) +#define	 SDRAM0_CFG_BRPF_SHIFT	(31-8) + +#define	 SDRAM0_TR_CASL_SHIFT	(31-8) +#define	 SDRAM0_TR_PTA_SHIFT	(31-13) +#define	 SDRAM0_TR_CTP_SHIFT	(31-15) +#define	 SDRAM0_TR_LDF_SHIFT	(31-17) +#define	 SDRAM0_TR_RFTA_SHIFT	(31-29) +#define	 SDRAM0_TR_RCD_SHIFT	(31-31) + +#define	 SDRAM0_RTR_SHIFT	(31-15) +#define	 SDRAM0_ECCCFG_SHIFT	(31-11) + +/* SDRAM0_CFG enable macro  */ +#define SDRAM0_CFG_BRPF(x) ( ( x & 0x3)<< SDRAM0_CFG_BRPF_SHIFT ) + +#define SDRAM0_BXCR_SZ_MASK	0x000e0000 +#define SDRAM0_BXCR_AM_MASK	0x0000e000 + +#define SDRAM0_BXCR_SZ_SHIFT	(31-14) +#define SDRAM0_BXCR_AM_SHIFT	(31-18) + +#define SDRAM0_BXCR_SZ(x)	( (( x << SDRAM0_BXCR_SZ_SHIFT) & SDRAM0_BXCR_SZ_MASK) ) +#define SDRAM0_BXCR_AM(x)	( (( x << SDRAM0_BXCR_AM_SHIFT) & SDRAM0_BXCR_AM_MASK) ) + +#ifdef CONFIG_SPDDRAM_SILENT +# define SPD_ERR(x) do { return 0; } while (0) +#else +# define SPD_ERR(x) do { printf(x); return(0); } while (0) +#endif + +#define sdram_HZ_to_ns(hertz) (1000000000/(hertz)) + +/* function prototypes */ +int spd_read(uint addr); + + +/* + * This function is reading data from the DIMM module EEPROM over the SPD bus + * and uses that to program the sdram controller. + * + * This works on boards that has the same schematics that the AMCC walnut has. + * + * Input: null for default I2C spd functions or a pointer to a custom function + * returning spd_data. + */ + +long int spd_sdram(int(read_spd)(uint addr)) +{ +	int tmp,row,col; +	int total_size,bank_size,bank_code; +	int mode; +	int bank_cnt; + +	int sdram0_pmit=0x07c00000; +	int sdram0_b0cr; +	int sdram0_b1cr = 0; +#ifndef CONFIG_405EP /* not on PPC405EP */ +	int sdram0_b2cr = 0; +	int sdram0_b3cr = 0; +	int sdram0_besr0 = -1; +	int sdram0_besr1 = -1; +	int sdram0_eccesr = -1; +	int sdram0_ecccfg; +	int ecc_on; +#endif + +	int sdram0_rtr=0; +	int sdram0_tr=0; + +	int sdram0_cfg=0; + +	int t_rp; +	int t_rcd; +	int t_ras; +	int t_rc; +	int min_cas; + +	PPC4xx_SYS_INFO sys_info; +	unsigned long bus_period_x_10; + +	/* +	 * get the board info +	 */ +	get_sys_info(&sys_info); +	bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10); + +	if (read_spd == 0){ +		read_spd=spd_read; +		/* +		 * Make sure I2C controller is initialized +		 * before continuing. +		 */ +		i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM); +	} + +	/* Make shure we are using SDRAM */ +	if (read_spd(2) != 0x04) { +		SPD_ERR("SDRAM - non SDRAM memory module found\n"); +	} + +	/* ------------------------------------------------------------------ +	 * configure memory timing register +	 * +	 * data from DIMM: +	 * 27	IN Row Precharge Time ( t RP) +	 * 29	MIN RAS to CAS Delay ( t RCD) +	 * 127	 Component and Clock Detail ,clk0-clk3, junction temp, CAS +	 * -------------------------------------------------------------------*/ + +	/* +	 * first figure out which cas latency mode to use +	 * use the min supported mode +	 */ + +	tmp = read_spd(127) & 0x6; +	if (tmp == 0x02) {		/* only cas = 2 supported */ +		min_cas = 2; +/*		t_ck = read_spd(9); */ +/*		t_ac = read_spd(10); */ +	} else if (tmp == 0x04) {	/* only cas = 3 supported */ +		min_cas = 3; +/*		t_ck = read_spd(9); */ +/*		t_ac = read_spd(10); */ +	} else if (tmp == 0x06) {	/* 2,3 supported, so use 2 */ +		min_cas = 2; +/*		t_ck = read_spd(23); */ +/*		t_ac = read_spd(24); */ +	} else { +		SPD_ERR("SDRAM - unsupported CAS latency \n"); +	} + +	/* get some timing values, t_rp,t_rcd,t_ras,t_rc +	 */ +	t_rp = read_spd(27); +	t_rcd = read_spd(29); +	t_ras = read_spd(30); +	t_rc = t_ras + t_rp; + +	/* The following timing calcs subtract 1 before deviding. +	 * this has effect of using ceiling instead of floor rounding, +	 * and also subtracting 1 to convert number to reg value +	 */ +	/* set up CASL */ +	sdram0_tr = (min_cas - 1) << SDRAM0_TR_CASL_SHIFT; +	/* set up PTA */ +	sdram0_tr |= ((((t_rp - 1) * 10)/bus_period_x_10) & 0x3) << SDRAM0_TR_PTA_SHIFT; +	/* set up CTP */ +	tmp = (((t_rc - t_rcd - t_rp -1) * 10) / bus_period_x_10) & 0x3; +	if (tmp < 1) +		tmp = 1; +	sdram0_tr |= tmp << SDRAM0_TR_CTP_SHIFT; +	/* set LDF	= 2 cycles, reg value = 1 */ +	sdram0_tr |= 1 << SDRAM0_TR_LDF_SHIFT; +	/* set RFTA = t_rfc/bus_period, use t_rfc = t_rc */ +	tmp = (((t_rc - 1) * 10) / bus_period_x_10) - 3; +	if (tmp < 0) +		tmp = 0; +	if (tmp > 6) +		tmp = 6; +	sdram0_tr |= tmp << SDRAM0_TR_RFTA_SHIFT; +	/* set RCD = t_rcd/bus_period*/ +	sdram0_tr |= ((((t_rcd - 1) * 10) / bus_period_x_10) &0x3) << SDRAM0_TR_RCD_SHIFT ; + + +	/*------------------------------------------------------------------ +	 * configure RTR register +	 * -------------------------------------------------------------------*/ +	row = read_spd(3); +	col = read_spd(4); +	tmp = read_spd(12) & 0x7f ; /* refresh type less self refresh bit */ +	switch (tmp) { +	case 0x00: +		tmp = 15625; +		break; +	case 0x01: +		tmp = 15625 / 4; +		break; +	case 0x02: +		tmp = 15625 / 2; +		break; +	case 0x03: +		tmp = 15625 * 2; +		break; +	case 0x04: +		tmp = 15625 * 4; +		break; +	case 0x05: +		tmp = 15625 * 8; +		break; +	default: +		SPD_ERR("SDRAM - Bad refresh period \n"); +	} +	/* convert from nsec to bus cycles */ +	tmp = (tmp * 10) / bus_period_x_10; +	sdram0_rtr = (tmp & 0x3ff8) <<	SDRAM0_RTR_SHIFT; + +	/*------------------------------------------------------------------ +	 * determine the number of banks used +	 * -------------------------------------------------------------------*/ +	/* byte 7:6 is module data width */ +	if (read_spd(7) != 0) +		SPD_ERR("SDRAM - unsupported module width\n"); +	tmp = read_spd(6); +	if (tmp < 32) +		SPD_ERR("SDRAM - unsupported module width\n"); +	else if (tmp < 64) +		bank_cnt = 1;		/* one bank per sdram side */ +	else if (tmp < 73) +		bank_cnt = 2;	/* need two banks per side */ +	else if (tmp < 161) +		bank_cnt = 4;	/* need four banks per side */ +	else +		SPD_ERR("SDRAM - unsupported module width\n"); + +	/* byte 5 is the module row count (refered to as dimm "sides") */ +	tmp = read_spd(5); +	if (tmp == 1) +		; +	else if (tmp==2) +		bank_cnt *= 2; +	else if (tmp==4) +		bank_cnt *= 4; +	else +		bank_cnt = 8;		/* 8 is an error code */ + +	if (bank_cnt > 4)	/* we only have 4 banks to work with */ +		SPD_ERR("SDRAM - unsupported module rows for this width\n"); + +#ifndef CONFIG_405EP /* not on PPC405EP */ +	/* now check for ECC ability of module. We only support ECC +	 *   on 32 bit wide devices with 8 bit ECC. +	 */ +	if ((read_spd(11)==2) && (read_spd(6)==40) && (read_spd(14)==8)) { +		sdram0_ecccfg = 0xf << SDRAM0_ECCCFG_SHIFT; +		ecc_on = 1; +	} else { +		sdram0_ecccfg = 0; +		ecc_on = 0; +	} +#endif + +	/*------------------------------------------------------------------ +	 * calculate total size +	 * -------------------------------------------------------------------*/ +	/* calculate total size and do sanity check */ +	tmp = read_spd(31); +	total_size = 1 << 22;	/* total_size = 4MB */ +	/* now multiply 4M by the smallest device row density */ +	/* note that we don't support asymetric rows */ +	while (((tmp & 0x0001) == 0) && (tmp != 0)) { +		total_size = total_size << 1; +		tmp = tmp >> 1; +	} +	total_size *= read_spd(5);	/* mult by module rows (dimm sides) */ + +	/*------------------------------------------------------------------ +	 * map	rows * cols * banks to a mode +	 * -------------------------------------------------------------------*/ + +	switch (row) { +	case 11: +		switch (col) { +		case 8: +			mode=4; /* mode 5 */ +			break; +		case 9: +		case 10: +			mode=0; /* mode 1 */ +			break; +		default: +			SPD_ERR("SDRAM - unsupported mode\n"); +		} +		break; +	case 12: +		switch (col) { +		case 8: +			mode=3; /* mode 4 */ +			break; +		case 9: +		case 10: +			mode=1; /* mode 2 */ +			break; +		default: +			SPD_ERR("SDRAM - unsupported mode\n"); +		} +		break; +	case 13: +		switch (col) { +		case 8: +			mode=5; /* mode 6 */ +			break; +		case 9: +		case 10: +			if (read_spd(17) == 2) +				mode = 6; /* mode 7 */ +			else +				mode = 2; /* mode 3 */ +			break; +		case 11: +			mode = 2; /* mode 3 */ +			break; +		default: +			SPD_ERR("SDRAM - unsupported mode\n"); +		} +		break; +	default: +		SPD_ERR("SDRAM - unsupported mode\n"); +	} + +	/*------------------------------------------------------------------ +	 * using the calculated values, compute the bank +	 * config register values. +	 * -------------------------------------------------------------------*/ + +	/* compute the size of each bank */ +	bank_size = total_size / bank_cnt; +	/* convert bank size to bank size code for ppc4xx +	   by takeing log2(bank_size) - 22 */ +	tmp = bank_size;		/* start with tmp = bank_size */ +	bank_code = 0;			/* and bank_code = 0 */ +	while (tmp > 1) {		/* this takes log2 of tmp */ +		bank_code++;		/* and stores result in bank_code */ +		tmp = tmp >> 1; +	}				/* bank_code is now log2(bank_size) */ +	bank_code -= 22;		/* subtract 22 to get the code */ + +	tmp = SDRAM0_BXCR_SZ(bank_code) | SDRAM0_BXCR_AM(mode) | 1; +	sdram0_b0cr = (bank_size * 0) | tmp; +#ifndef CONFIG_405EP /* not on PPC405EP */ +	if (bank_cnt > 1) +		sdram0_b2cr = (bank_size * 1) | tmp; +	if (bank_cnt > 2) +		sdram0_b1cr = (bank_size * 2) | tmp; +	if (bank_cnt > 3) +		sdram0_b3cr = (bank_size * 3) | tmp; +#else +	/* PPC405EP chip only supports two SDRAM banks */ +	if (bank_cnt > 1) +		sdram0_b1cr = (bank_size * 1) | tmp; +	if (bank_cnt > 2) +		total_size = 2 * bank_size; +#endif + +	/* +	 *   enable sdram controller DCE=1 +	 *  enable burst read prefetch to 32 bytes BRPF=2 +	 *  leave other functions off +	 */ + +	/*------------------------------------------------------------------ +	 * now that we've done our calculations, we are ready to +	 * program all the registers. +	 * -------------------------------------------------------------------*/ + +	/* disable memcontroller so updates work */ +	mtsdram(SDRAM0_CFG, 0); + +#ifndef CONFIG_405EP /* not on PPC405EP */ +	mtsdram(SDRAM0_BESR0, sdram0_besr0); +	mtsdram(SDRAM0_BESR1, sdram0_besr1); +	mtsdram(SDRAM0_ECCCFG, sdram0_ecccfg); +	mtsdram(SDRAM0_ECCESR, sdram0_eccesr); +#endif +	mtsdram(SDRAM0_RTR, sdram0_rtr); +	mtsdram(SDRAM0_PMIT, sdram0_pmit); +	mtsdram(SDRAM0_B0CR, sdram0_b0cr); +	mtsdram(SDRAM0_B1CR, sdram0_b1cr); +#ifndef CONFIG_405EP /* not on PPC405EP */ +	mtsdram(SDRAM0_B2CR, sdram0_b2cr); +	mtsdram(SDRAM0_B3CR, sdram0_b3cr); +#endif +	mtsdram(SDRAM0_TR, sdram0_tr); + +	/* SDRAM have a power on delay,	 500 micro should do */ +	udelay(500); +	sdram0_cfg = SDRAM0_CFG_DCE | SDRAM0_CFG_BRPF(1) | SDRAM0_CFG_ECCDD | SDRAM0_CFG_EMDULR; +#ifndef CONFIG_405EP /* not on PPC405EP */ +	if (ecc_on) +		sdram0_cfg |= SDRAM0_CFG_MEMCHK; +#endif +	mtsdram(SDRAM0_CFG, sdram0_cfg); + +	return (total_size); +} + +int spd_read(uint addr) +{ +	uchar data[2]; + +	if (i2c_read(SPD_EEPROM_ADDRESS, addr, 1, data, 1) == 0) +		return (int)data[0]; +	else +		return 0; +} + +#endif /* CONFIG_SPD_EEPROM */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/44x_spd_ddr.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/44x_spd_ddr.c new file mode 100644 index 00000000..36e7b01e --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/44x_spd_ddr.c @@ -0,0 +1,1224 @@ +/* + * arch/powerpc/cpu/ppc4xx/44x_spd_ddr.c + * This SPD DDR detection code supports IBM/AMCC PPC44x cpu with a + * DDR controller. Those are 440GP/GX/EP/GR. + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@attbi.com + * + * Based on code by: + * + * Kenneth Johansson ,Ericsson AB. + * kenneth.johansson@etx.ericsson.se + * + * hacked up by bill hunter. fixed so we could run before + * serial_init and console_init. previous version avoided this by + * running out of cache memory during serial/console init, then running + * this code later. + * + * (C) Copyright 2002 + * Jun Gu, Artesyn Technology, jung@artesyncp.com + * Support for AMCC 440 based on OpenBIOS draminit.c from IBM. + * + * (C) Copyright 2005-2007 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <asm/processor.h> +#include <i2c.h> +#include <asm/ppc4xx.h> +#include <asm/mmu.h> + +#include "ecc.h" + +#if defined(CONFIG_SPD_EEPROM) &&					\ +	(defined(CONFIG_440GP) || defined(CONFIG_440GX) ||		\ +	 defined(CONFIG_440EP) || defined(CONFIG_440GR)) + +/* + * Set default values + */ +#define ONE_BILLION	1000000000 + +/* + * Board-specific Platform code can reimplement spd_ddr_init_hang () if needed + */ +void __spd_ddr_init_hang (void) +{ +	hang (); +} +void spd_ddr_init_hang (void) __attribute__((weak, alias("__spd_ddr_init_hang"))); + +/*-----------------------------------------------------------------------------+ +  |  General Definition +  +-----------------------------------------------------------------------------*/ +#define DEFAULT_SPD_ADDR1	0x53 +#define DEFAULT_SPD_ADDR2	0x52 +#define MAXBANKS		4		/* at most 4 dimm banks */ +#define MAX_SPD_BYTES		256 +#define NUMHALFCYCLES		4 +#define NUMMEMTESTS		8 +#define NUMMEMWORDS		8 +#define MAXBXCR			4 + +/* + * This DDR2 setup code can dynamically setup the TLB entries for the DDR2 memory + * region. Right now the cache should still be disabled in U-Boot because of the + * EMAC driver, that need it's buffer descriptor to be located in non cached + * memory. + * + * If at some time this restriction doesn't apply anymore, just define + * CONFIG_4xx_DCACHE in the board config file and this code should setup + * everything correctly. + */ +#ifdef CONFIG_4xx_DCACHE +#define MY_TLB_WORD2_I_ENABLE	0			/* enable caching on SDRAM */ +#else +#define MY_TLB_WORD2_I_ENABLE	TLB_WORD2_I_ENABLE	/* disable caching on SDRAM */ +#endif + +/* bank_parms is used to sort the bank sizes by descending order */ +struct bank_param { +	unsigned long cr; +	unsigned long bank_size_bytes; +}; + +typedef struct bank_param BANKPARMS; + +#ifdef CONFIG_SYS_SIMULATE_SPD_EEPROM +extern const unsigned char cfg_simulate_spd_eeprom[128]; +#endif + +static unsigned char spd_read(uchar chip, uint addr); +static void get_spd_info(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks); +static void check_mem_type(unsigned long *dimm_populated, +			   unsigned char *iic0_dimm_addr, +			   unsigned long num_dimm_banks); +static void check_volt_type(unsigned long *dimm_populated, +			    unsigned char *iic0_dimm_addr, +			    unsigned long num_dimm_banks); +static void program_cfg0(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long  num_dimm_banks); +static void program_cfg1(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks); +static void program_rtr(unsigned long *dimm_populated, +			unsigned char *iic0_dimm_addr, +			unsigned long num_dimm_banks); +static void program_tr0(unsigned long *dimm_populated, +			unsigned char *iic0_dimm_addr, +			unsigned long num_dimm_banks); +static void program_tr1(void); + +static unsigned long program_bxcr(unsigned long *dimm_populated, +				  unsigned char *iic0_dimm_addr, +				  unsigned long num_dimm_banks); + +/* + * This function is reading data from the DIMM module EEPROM over the SPD bus + * and uses that to program the sdram controller. + * + * This works on boards that has the same schematics that the AMCC walnut has. + * + * BUG: Don't handle ECC memory + * BUG: A few values in the TR register is currently hardcoded + */ +long int spd_sdram(void) { +	unsigned char iic0_dimm_addr[] = SPD_EEPROM_ADDRESS; +	unsigned long dimm_populated[sizeof(iic0_dimm_addr)]; +	unsigned long total_size; +	unsigned long cfg0; +	unsigned long mcsts; +	unsigned long num_dimm_banks;		    /* on board dimm banks */ + +	num_dimm_banks = sizeof(iic0_dimm_addr); + +	/* +	 * Make sure I2C controller is initialized +	 * before continuing. +	 */ +	i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM); + +	/* +	 * Read the SPD information using I2C interface. Check to see if the +	 * DIMM slots are populated. +	 */ +	get_spd_info(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/* +	 * Check the memory type for the dimms plugged. +	 */ +	check_mem_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/* +	 * Check the voltage type for the dimms plugged. +	 */ +	check_volt_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || defined(CONFIG_440GR) +	/* +	 * Soft-reset SDRAM controller. +	 */ +	mtsdr(SDR0_SRST, SDR0_SRST_DMC); +	mtsdr(SDR0_SRST, 0x00000000); +#endif + +	/* +	 * program 440GP SDRAM controller options (SDRAM0_CFG0) +	 */ +	program_cfg0(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/* +	 * program 440GP SDRAM controller options (SDRAM0_CFG1) +	 */ +	program_cfg1(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/* +	 * program SDRAM refresh register (SDRAM0_RTR) +	 */ +	program_rtr(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/* +	 * program SDRAM Timing Register 0 (SDRAM0_TR0) +	 */ +	program_tr0(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/* +	 * program the BxCR registers to find out total sdram installed +	 */ +	total_size = program_bxcr(dimm_populated, iic0_dimm_addr, +				  num_dimm_banks); + +#ifdef CONFIG_PROG_SDRAM_TLB /* this define should eventually be removed */ +	/* and program tlb entries for this size (dynamic) */ +	program_tlb(0, 0, total_size, MY_TLB_WORD2_I_ENABLE); +#endif + +	/* +	 * program SDRAM Clock Timing Register (SDRAM0_CLKTR) +	 */ +	mtsdram(SDRAM0_CLKTR, 0x40000000); + +	/* +	 * delay to ensure 200 usec has elapsed +	 */ +	udelay(400); + +	/* +	 * enable the memory controller +	 */ +	mfsdram(SDRAM0_CFG0, cfg0); +	mtsdram(SDRAM0_CFG0, cfg0 | SDRAM_CFG0_DCEN); + +	/* +	 * wait for SDRAM_CFG0_DC_EN to complete +	 */ +	while (1) { +		mfsdram(SDRAM0_MCSTS, mcsts); +		if ((mcsts & SDRAM_MCSTS_MRSC) != 0) +			break; +	} + +	/* +	 * program SDRAM Timing Register 1, adding some delays +	 */ +	program_tr1(); + +#ifdef CONFIG_DDR_ECC +	/* +	 * If ecc is enabled, initialize the parity bits. +	 */ +	ecc_init(CONFIG_SYS_SDRAM_BASE, total_size); +#endif + +	return total_size; +} + +static unsigned char spd_read(uchar chip, uint addr) +{ +	unsigned char data[2]; + +#ifdef CONFIG_SYS_SIMULATE_SPD_EEPROM +	if (chip == CONFIG_SYS_SIMULATE_SPD_EEPROM) { +		/* +		 * Onboard spd eeprom requested -> simulate values +		 */ +		return cfg_simulate_spd_eeprom[addr]; +	} +#endif /* CONFIG_SYS_SIMULATE_SPD_EEPROM */ + +	if (i2c_probe(chip) == 0) { +		if (i2c_read(chip, addr, 1, data, 1) == 0) { +			return data[0]; +		} +	} + +	return 0; +} + +static void get_spd_info(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long dimm_found; +	unsigned char num_of_bytes; +	unsigned char total_size; + +	dimm_found = false; +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		num_of_bytes = 0; +		total_size = 0; + +		num_of_bytes = spd_read(iic0_dimm_addr[dimm_num], 0); +		total_size = spd_read(iic0_dimm_addr[dimm_num], 1); + +		if ((num_of_bytes != 0) && (total_size != 0)) { +			dimm_populated[dimm_num] = true; +			dimm_found = true; +			debug("DIMM slot %lu: populated\n", dimm_num); +		} else { +			dimm_populated[dimm_num] = false; +			debug("DIMM slot %lu: Not populated\n", dimm_num); +		} +	} + +	if (dimm_found == false) { +		printf("ERROR - No memory installed. Install a DDR-SDRAM DIMM.\n\n"); +		spd_ddr_init_hang (); +	} +} + +static void check_mem_type(unsigned long *dimm_populated, +			   unsigned char *iic0_dimm_addr, +			   unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned char dimm_type; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] == true) { +			dimm_type = spd_read(iic0_dimm_addr[dimm_num], 2); +			switch (dimm_type) { +			case 7: +				debug("DIMM slot %lu: DDR SDRAM detected\n", dimm_num); +				break; +			default: +				printf("ERROR: Unsupported DIMM detected in slot %lu.\n", +				       dimm_num); +				printf("Only DDR SDRAM DIMMs are supported.\n"); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +				break; +			} +		} +	} +} + +static void check_volt_type(unsigned long *dimm_populated, +			    unsigned char *iic0_dimm_addr, +			    unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long voltage_type; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] == true) { +			voltage_type = spd_read(iic0_dimm_addr[dimm_num], 8); +			if (voltage_type != 0x04) { +				printf("ERROR: DIMM %lu with unsupported voltage level.\n", +				       dimm_num); +				spd_ddr_init_hang (); +			} else { +				debug("DIMM %lu voltage level supported.\n", dimm_num); +			} +			break; +		} +	} +} + +static void program_cfg0(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long cfg0; +	unsigned long ecc_enabled; +	unsigned char ecc; +	unsigned char attributes; +	unsigned long data_width; + +	/* +	 * get Memory Controller Options 0 data +	 */ +	mfsdram(SDRAM0_CFG0, cfg0); + +	/* +	 * clear bits +	 */ +	cfg0 &= ~(SDRAM_CFG0_DCEN | SDRAM_CFG0_MCHK_MASK | +		  SDRAM_CFG0_RDEN | SDRAM_CFG0_PMUD | +		  SDRAM_CFG0_DMWD_MASK | +		  SDRAM_CFG0_UIOS_MASK | SDRAM_CFG0_PDP); + + +	/* +	 * FIXME: assume the DDR SDRAMs in both banks are the same +	 */ +	ecc_enabled = true; +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] == true) { +			ecc = spd_read(iic0_dimm_addr[dimm_num], 11); +			if (ecc != 0x02) { +				ecc_enabled = false; +			} + +			/* +			 * program Registered DIMM Enable +			 */ +			attributes = spd_read(iic0_dimm_addr[dimm_num], 21); +			if ((attributes & 0x02) != 0x00) { +				cfg0 |= SDRAM_CFG0_RDEN; +			} + +			/* +			 * program DDR SDRAM Data Width +			 */ +			data_width = +				(unsigned long)spd_read(iic0_dimm_addr[dimm_num],6) + +				(((unsigned long)spd_read(iic0_dimm_addr[dimm_num],7)) << 8); +			if (data_width == 64 || data_width == 72) { +				cfg0 |= SDRAM_CFG0_DMWD_64; +			} else if (data_width == 32 || data_width == 40) { +				cfg0 |= SDRAM_CFG0_DMWD_32; +			} else { +				printf("WARNING: DIMM with datawidth of %lu bits.\n", +				       data_width); +				printf("Only DIMMs with 32 or 64 bit datawidths supported.\n"); +				spd_ddr_init_hang (); +			} +			break; +		} +	} + +	/* +	 * program Memory Data Error Checking +	 */ +	if (ecc_enabled == true) { +		cfg0 |= SDRAM_CFG0_MCHK_GEN; +	} else { +		cfg0 |= SDRAM_CFG0_MCHK_NON; +	} + +	/* +	 * program Page Management Unit (0 == enabled) +	 */ +	cfg0 &= ~SDRAM_CFG0_PMUD; + +	/* +	 * program Memory Controller Options 0 +	 * Note: DCEN must be enabled after all DDR SDRAM controller +	 * configuration registers get initialized. +	 */ +	mtsdram(SDRAM0_CFG0, cfg0); +} + +static void program_cfg1(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks) +{ +	unsigned long cfg1; +	mfsdram(SDRAM0_CFG1, cfg1); + +	/* +	 * Self-refresh exit, disable PM +	 */ +	cfg1 &= ~(SDRAM_CFG1_SRE | SDRAM_CFG1_PMEN); + +	/* +	 * program Memory Controller Options 1 +	 */ +	mtsdram(SDRAM0_CFG1, cfg1); +} + +static void program_rtr(unsigned long *dimm_populated, +			unsigned char *iic0_dimm_addr, +			unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long bus_period_x_10; +	unsigned long refresh_rate = 0; +	unsigned char refresh_rate_type; +	unsigned long refresh_interval; +	unsigned long sdram_rtr; +	PPC4xx_SYS_INFO sys_info; + +	/* +	 * get the board info +	 */ +	get_sys_info(&sys_info); +	bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10); + +	for (dimm_num = 0;  dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] == true) { +			refresh_rate_type = 0x7F & spd_read(iic0_dimm_addr[dimm_num], 12); +			switch (refresh_rate_type) { +			case 0x00: +				refresh_rate = 15625; +				break; +			case 0x01: +				refresh_rate = 15625/4; +				break; +			case 0x02: +				refresh_rate = 15625/2; +				break; +			case 0x03: +				refresh_rate = 15626*2; +				break; +			case 0x04: +				refresh_rate = 15625*4; +				break; +			case 0x05: +				refresh_rate = 15625*8; +				break; +			default: +				printf("ERROR: DIMM %lu, unsupported refresh rate/type.\n", +				       dimm_num); +				printf("Replace the DIMM module with a supported DIMM.\n"); +				break; +			} + +			break; +		} +	} + +	refresh_interval = refresh_rate * 10 / bus_period_x_10; +	sdram_rtr = (refresh_interval & 0x3ff8) <<  16; + +	/* +	 * program Refresh Timer Register (SDRAM0_RTR) +	 */ +	mtsdram(SDRAM0_RTR, sdram_rtr); +} + +static void program_tr0(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long tr0; +	unsigned char wcsbc; +	unsigned char t_rp_ns; +	unsigned char t_rcd_ns; +	unsigned char t_ras_ns; +	unsigned long t_rp_clk; +	unsigned long t_ras_rcd_clk; +	unsigned long t_rcd_clk; +	unsigned long t_rfc_clk; +	unsigned long plb_check; +	unsigned char cas_bit; +	unsigned long cas_index; +	unsigned char cas_2_0_available; +	unsigned char cas_2_5_available; +	unsigned char cas_3_0_available; +	unsigned long cycle_time_ns_x_10[3]; +	unsigned long tcyc_3_0_ns_x_10; +	unsigned long tcyc_2_5_ns_x_10; +	unsigned long tcyc_2_0_ns_x_10; +	unsigned long tcyc_reg; +	unsigned long bus_period_x_10; +	PPC4xx_SYS_INFO sys_info; +	unsigned long residue; + +	/* +	 * get the board info +	 */ +	get_sys_info(&sys_info); +	bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10); + +	/* +	 * get SDRAM Timing Register 0 (SDRAM_TR0) and clear bits +	 */ +	mfsdram(SDRAM0_TR0, tr0); +	tr0 &= ~(SDRAM_TR0_SDWR_MASK | SDRAM_TR0_SDWD_MASK | +		 SDRAM_TR0_SDCL_MASK | SDRAM_TR0_SDPA_MASK | +		 SDRAM_TR0_SDCP_MASK | SDRAM_TR0_SDLD_MASK | +		 SDRAM_TR0_SDRA_MASK | SDRAM_TR0_SDRD_MASK); + +	/* +	 * initialization +	 */ +	wcsbc = 0; +	t_rp_ns = 0; +	t_rcd_ns = 0; +	t_ras_ns = 0; +	cas_2_0_available = true; +	cas_2_5_available = true; +	cas_3_0_available = true; +	tcyc_2_0_ns_x_10 = 0; +	tcyc_2_5_ns_x_10 = 0; +	tcyc_3_0_ns_x_10 = 0; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] == true) { +			wcsbc = spd_read(iic0_dimm_addr[dimm_num], 15); +			t_rp_ns	 = spd_read(iic0_dimm_addr[dimm_num], 27) >> 2; +			t_rcd_ns = spd_read(iic0_dimm_addr[dimm_num], 29) >> 2; +			t_ras_ns = spd_read(iic0_dimm_addr[dimm_num], 30); +			cas_bit = spd_read(iic0_dimm_addr[dimm_num], 18); + +			for (cas_index = 0; cas_index < 3; cas_index++) { +				switch (cas_index) { +				case 0: +					tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 9); +					break; +				case 1: +					tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 23); +					break; +				default: +					tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 25); +					break; +				} + +				if ((tcyc_reg & 0x0F) >= 10) { +					printf("ERROR: Tcyc incorrect for DIMM in slot %lu\n", +					       dimm_num); +					spd_ddr_init_hang (); +				} + +				cycle_time_ns_x_10[cas_index] = +					(((tcyc_reg & 0xF0) >> 4) * 10) + (tcyc_reg & 0x0F); +			} + +			cas_index = 0; + +			if ((cas_bit & 0x80) != 0) { +				cas_index += 3; +			} else if ((cas_bit & 0x40) != 0) { +				cas_index += 2; +			} else if ((cas_bit & 0x20) != 0) { +				cas_index += 1; +			} + +			if (((cas_bit & 0x10) != 0) && (cas_index < 3)) { +				tcyc_3_0_ns_x_10 = cycle_time_ns_x_10[cas_index]; +				cas_index++; +			} else { +				if (cas_index != 0) { +					cas_index++; +				} +				cas_3_0_available = false; +			} + +			if (((cas_bit & 0x08) != 0) || (cas_index < 3)) { +				tcyc_2_5_ns_x_10 = cycle_time_ns_x_10[cas_index]; +				cas_index++; +			} else { +				if (cas_index != 0) { +					cas_index++; +				} +				cas_2_5_available = false; +			} + +			if (((cas_bit & 0x04) != 0) || (cas_index < 3)) { +				tcyc_2_0_ns_x_10 = cycle_time_ns_x_10[cas_index]; +				cas_index++; +			} else { +				if (cas_index != 0) { +					cas_index++; +				} +				cas_2_0_available = false; +			} + +			break; +		} +	} + +	/* +	 * Program SD_WR and SD_WCSBC fields +	 */ +	tr0 |= SDRAM_TR0_SDWR_2_CLK;		    /* Write Recovery: 2 CLK */ +	switch (wcsbc) { +	case 0: +		tr0 |= SDRAM_TR0_SDWD_0_CLK; +		break; +	default: +		tr0 |= SDRAM_TR0_SDWD_1_CLK; +		break; +	} + +	/* +	 * Program SD_CASL field +	 */ +	if ((cas_2_0_available == true) && +	    (bus_period_x_10 >= tcyc_2_0_ns_x_10)) { +		tr0 |= SDRAM_TR0_SDCL_2_0_CLK; +	} else if ((cas_2_5_available == true) && +		 (bus_period_x_10 >= tcyc_2_5_ns_x_10)) { +		tr0 |= SDRAM_TR0_SDCL_2_5_CLK; +	} else if ((cas_3_0_available == true) && +		 (bus_period_x_10 >= tcyc_3_0_ns_x_10)) { +		tr0 |= SDRAM_TR0_SDCL_3_0_CLK; +	} else { +		printf("ERROR: No supported CAS latency with the installed DIMMs.\n"); +		printf("Only CAS latencies of 2.0, 2.5, and 3.0 are supported.\n"); +		printf("Make sure the PLB speed is within the supported range.\n"); +		spd_ddr_init_hang (); +	} + +	/* +	 * Calculate Trp in clock cycles and round up if necessary +	 * Program SD_PTA field +	 */ +	t_rp_clk = sys_info.freqPLB * t_rp_ns / ONE_BILLION; +	plb_check = ONE_BILLION * t_rp_clk / t_rp_ns; +	if (sys_info.freqPLB != plb_check) { +		t_rp_clk++; +	} +	switch ((unsigned long)t_rp_clk) { +	case 0: +	case 1: +	case 2: +		tr0 |= SDRAM_TR0_SDPA_2_CLK; +		break; +	case 3: +		tr0 |= SDRAM_TR0_SDPA_3_CLK; +		break; +	default: +		tr0 |= SDRAM_TR0_SDPA_4_CLK; +		break; +	} + +	/* +	 * Program SD_CTP field +	 */ +	t_ras_rcd_clk = sys_info.freqPLB * (t_ras_ns - t_rcd_ns) / ONE_BILLION; +	plb_check = ONE_BILLION * t_ras_rcd_clk / (t_ras_ns - t_rcd_ns); +	if (sys_info.freqPLB != plb_check) { +		t_ras_rcd_clk++; +	} +	switch (t_ras_rcd_clk) { +	case 0: +	case 1: +	case 2: +		tr0 |= SDRAM_TR0_SDCP_2_CLK; +		break; +	case 3: +		tr0 |= SDRAM_TR0_SDCP_3_CLK; +		break; +	case 4: +		tr0 |= SDRAM_TR0_SDCP_4_CLK; +		break; +	default: +		tr0 |= SDRAM_TR0_SDCP_5_CLK; +		break; +	} + +	/* +	 * Program SD_LDF field +	 */ +	tr0 |= SDRAM_TR0_SDLD_2_CLK; + +	/* +	 * Program SD_RFTA field +	 * FIXME tRFC hardcoded as 75 nanoseconds +	 */ +	t_rfc_clk = sys_info.freqPLB / (ONE_BILLION / 75); +	residue = sys_info.freqPLB % (ONE_BILLION / 75); +	if (residue >= (ONE_BILLION / 150)) { +		t_rfc_clk++; +	} +	switch (t_rfc_clk) { +	case 0: +	case 1: +	case 2: +	case 3: +	case 4: +	case 5: +	case 6: +		tr0 |= SDRAM_TR0_SDRA_6_CLK; +		break; +	case 7: +		tr0 |= SDRAM_TR0_SDRA_7_CLK; +		break; +	case 8: +		tr0 |= SDRAM_TR0_SDRA_8_CLK; +		break; +	case 9: +		tr0 |= SDRAM_TR0_SDRA_9_CLK; +		break; +	case 10: +		tr0 |= SDRAM_TR0_SDRA_10_CLK; +		break; +	case 11: +		tr0 |= SDRAM_TR0_SDRA_11_CLK; +		break; +	case 12: +		tr0 |= SDRAM_TR0_SDRA_12_CLK; +		break; +	default: +		tr0 |= SDRAM_TR0_SDRA_13_CLK; +		break; +	} + +	/* +	 * Program SD_RCD field +	 */ +	t_rcd_clk = sys_info.freqPLB * t_rcd_ns / ONE_BILLION; +	plb_check = ONE_BILLION * t_rcd_clk / t_rcd_ns; +	if (sys_info.freqPLB != plb_check) { +		t_rcd_clk++; +	} +	switch (t_rcd_clk) { +	case 0: +	case 1: +	case 2: +		tr0 |= SDRAM_TR0_SDRD_2_CLK; +		break; +	case 3: +		tr0 |= SDRAM_TR0_SDRD_3_CLK; +		break; +	default: +		tr0 |= SDRAM_TR0_SDRD_4_CLK; +		break; +	} + +	debug("tr0: %lx\n", tr0); +	mtsdram(SDRAM0_TR0, tr0); +} + +static int short_mem_test(void) +{ +	unsigned long i, j; +	unsigned long bxcr_num; +	unsigned long *membase; +	const unsigned long test[NUMMEMTESTS][NUMMEMWORDS] = { +		{0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}, +		{0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000}, +		{0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555}, +		{0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA}, +		{0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A}, +		{0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5}, +		{0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA}, +		{0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, +		 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55}}; + +	for (bxcr_num = 0; bxcr_num < MAXBXCR; bxcr_num++) { +		mtdcr(SDRAM0_CFGADDR, SDRAM0_B0CR + (bxcr_num << 2)); +		if ((mfdcr(SDRAM0_CFGDATA) & SDRAM_BXCR_SDBE) == SDRAM_BXCR_SDBE) { +			/* Bank is enabled */ +			membase = (unsigned long*) +				(mfdcr(SDRAM0_CFGDATA) & SDRAM_BXCR_SDBA_MASK); + +			/* +			 * Run the short memory test +			 */ +			for (i = 0; i < NUMMEMTESTS; i++) { +				for (j = 0; j < NUMMEMWORDS; j++) { +					/* printf("bank enabled base:%x\n", &membase[j]); */ +					membase[j] = test[i][j]; +					ppcDcbf((unsigned long)&(membase[j])); +				} + +				for (j = 0; j < NUMMEMWORDS; j++) { +					if (membase[j] != test[i][j]) { +						ppcDcbf((unsigned long)&(membase[j])); +						return 0; +					} +					ppcDcbf((unsigned long)&(membase[j])); +				} + +				if (j < NUMMEMWORDS) +					return 0; +			} + +			/* +			 * see if the rdclt value passed +			 */ +			if (i < NUMMEMTESTS) +				return 0; +		} +	} + +	return 1; +} + +static void program_tr1(void) +{ +	unsigned long tr0; +	unsigned long tr1; +	unsigned long cfg0; +	unsigned long ecc_temp; +	unsigned long dlycal; +	unsigned long dly_val; +	unsigned long k; +	unsigned long max_pass_length; +	unsigned long current_pass_length; +	unsigned long current_fail_length; +	unsigned long current_start; +	unsigned long rdclt; +	unsigned long rdclt_offset; +	long max_start; +	long max_end; +	long rdclt_average; +	unsigned char window_found; +	unsigned char fail_found; +	unsigned char pass_found; +	PPC4xx_SYS_INFO sys_info; + +	/* +	 * get the board info +	 */ +	get_sys_info(&sys_info); + +	/* +	 * get SDRAM Timing Register 0 (SDRAM_TR0) and clear bits +	 */ +	mfsdram(SDRAM0_TR1, tr1); +	tr1 &= ~(SDRAM_TR1_RDSS_MASK | SDRAM_TR1_RDSL_MASK | +		 SDRAM_TR1_RDCD_MASK | SDRAM_TR1_RDCT_MASK); + +	mfsdram(SDRAM0_TR0, tr0); +	if (((tr0 & SDRAM_TR0_SDCL_MASK) == SDRAM_TR0_SDCL_2_5_CLK) && +	    (sys_info.freqPLB > 100000000)) { +		tr1 |= SDRAM_TR1_RDSS_TR2; +		tr1 |= SDRAM_TR1_RDSL_STAGE3; +		tr1 |= SDRAM_TR1_RDCD_RCD_1_2; +	} else { +		tr1 |= SDRAM_TR1_RDSS_TR1; +		tr1 |= SDRAM_TR1_RDSL_STAGE2; +		tr1 |= SDRAM_TR1_RDCD_RCD_0_0; +	} + +	/* +	 * save CFG0 ECC setting to a temporary variable and turn ECC off +	 */ +	mfsdram(SDRAM0_CFG0, cfg0); +	ecc_temp = cfg0 & SDRAM_CFG0_MCHK_MASK; +	mtsdram(SDRAM0_CFG0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | SDRAM_CFG0_MCHK_NON); + +	/* +	 * get the delay line calibration register value +	 */ +	mfsdram(SDRAM0_DLYCAL, dlycal); +	dly_val = SDRAM_DLYCAL_DLCV_DECODE(dlycal) << 2; + +	max_pass_length = 0; +	max_start = 0; +	max_end = 0; +	current_pass_length = 0; +	current_fail_length = 0; +	current_start = 0; +	rdclt_offset = 0; +	window_found = false; +	fail_found = false; +	pass_found = false; +	debug("Starting memory test "); + +	for (k = 0; k < NUMHALFCYCLES; k++) { +		for (rdclt = 0; rdclt < dly_val; rdclt++) { +			/* +			 * Set the timing reg for the test. +			 */ +			mtsdram(SDRAM0_TR1, (tr1 | SDRAM_TR1_RDCT_ENCODE(rdclt))); + +			if (short_mem_test()) { +				if (fail_found == true) { +					pass_found = true; +					if (current_pass_length == 0) { +						current_start = rdclt_offset + rdclt; +					} + +					current_fail_length = 0; +					current_pass_length++; + +					if (current_pass_length > max_pass_length) { +						max_pass_length = current_pass_length; +						max_start = current_start; +						max_end = rdclt_offset + rdclt; +					} +				} +			} else { +				current_pass_length = 0; +				current_fail_length++; + +				if (current_fail_length >= (dly_val>>2)) { +					if (fail_found == false) { +						fail_found = true; +					} else if (pass_found == true) { +						window_found = true; +						break; +					} +				} +			} +		} +		debug("."); + +		if (window_found == true) +			break; + +		tr1 = tr1 ^ SDRAM_TR1_RDCD_MASK; +		rdclt_offset += dly_val; +	} +	debug("\n"); + +	/* +	 * make sure we find the window +	 */ +	if (window_found == false) { +		printf("ERROR: Cannot determine a common read delay.\n"); +		spd_ddr_init_hang (); +	} + +	/* +	 * restore the orignal ECC setting +	 */ +	mtsdram(SDRAM0_CFG0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | ecc_temp); + +	/* +	 * set the SDRAM TR1 RDCD value +	 */ +	tr1 &= ~SDRAM_TR1_RDCD_MASK; +	if ((tr0 & SDRAM_TR0_SDCL_MASK) == SDRAM_TR0_SDCL_2_5_CLK) { +		tr1 |= SDRAM_TR1_RDCD_RCD_1_2; +	} else { +		tr1 |= SDRAM_TR1_RDCD_RCD_0_0; +	} + +	/* +	 * set the SDRAM TR1 RDCLT value +	 */ +	tr1 &= ~SDRAM_TR1_RDCT_MASK; +	while (max_end >= (dly_val << 1)) { +		max_end -= (dly_val << 1); +		max_start -= (dly_val << 1); +	} + +	rdclt_average = ((max_start + max_end) >> 1); + +	if (rdclt_average < 0) { +		rdclt_average = 0; +	} + +	if (rdclt_average >= dly_val) { +		rdclt_average -= dly_val; +		tr1 = tr1 ^ SDRAM_TR1_RDCD_MASK; +	} +	tr1 |= SDRAM_TR1_RDCT_ENCODE(rdclt_average); + +	debug("tr1: %lx\n", tr1); + +	/* +	 * program SDRAM Timing Register 1 TR1 +	 */ +	mtsdram(SDRAM0_TR1, tr1); +} + +static unsigned long program_bxcr(unsigned long *dimm_populated, +				  unsigned char *iic0_dimm_addr, +				  unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long bank_base_addr; +	unsigned long cr; +	unsigned long i; +	unsigned long j; +	unsigned long temp; +	unsigned char num_row_addr; +	unsigned char num_col_addr; +	unsigned char num_banks; +	unsigned char bank_size_id; +	unsigned long ctrl_bank_num[MAXBANKS]; +	unsigned long bx_cr_num; +	unsigned long largest_size_index; +	unsigned long largest_size; +	unsigned long current_size_index; +	BANKPARMS bank_parms[MAXBXCR]; +	unsigned long sorted_bank_num[MAXBXCR]; /* DDR Controller bank number table (sorted by size) */ +	unsigned long sorted_bank_size[MAXBXCR]; /* DDR Controller bank size table (sorted by size)*/ + +	/* +	 * Set the BxCR regs.  First, wipe out the bank config registers. +	 */ +	for (bx_cr_num = 0; bx_cr_num < MAXBXCR; bx_cr_num++) { +		mtdcr(SDRAM0_CFGADDR, SDRAM0_B0CR + (bx_cr_num << 2)); +		mtdcr(SDRAM0_CFGDATA, 0x00000000); +		bank_parms[bx_cr_num].bank_size_bytes = 0; +	} + +#ifdef CONFIG_BAMBOO +	/* +	 * This next section is hardware dependent and must be programmed +	 * to match the hardware.  For bamboo, the following holds... +	 * 1. SDRAM0_B0CR: Bank 0 of dimm 0 ctrl_bank_num : 0 (soldered onboard) +	 * 2. SDRAM0_B1CR: Bank 0 of dimm 1 ctrl_bank_num : 1 +	 * 3. SDRAM0_B2CR: Bank 1 of dimm 1 ctrl_bank_num : 1 +	 * 4. SDRAM0_B3CR: Bank 0 of dimm 2 ctrl_bank_num : 3 +	 * ctrl_bank_num corresponds to the first usable DDR controller bank number by DIMM +	 */ +	ctrl_bank_num[0] = 0; +	ctrl_bank_num[1] = 1; +	ctrl_bank_num[2] = 3; +#else +	/* +	 * Ocotea, Ebony and the other IBM/AMCC eval boards have +	 * 2 DIMM slots with each max 2 banks +	 */ +	ctrl_bank_num[0] = 0; +	ctrl_bank_num[1] = 2; +#endif + +	/* +	 * reset the bank_base address +	 */ +	bank_base_addr = CONFIG_SYS_SDRAM_BASE; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] == true) { +			num_row_addr = spd_read(iic0_dimm_addr[dimm_num], 3); +			num_col_addr = spd_read(iic0_dimm_addr[dimm_num], 4); +			num_banks    = spd_read(iic0_dimm_addr[dimm_num], 5); +			bank_size_id = spd_read(iic0_dimm_addr[dimm_num], 31); +			debug("DIMM%ld: row=%d col=%d banks=%d\n", dimm_num, +			      num_row_addr, num_col_addr, num_banks); + +			/* +			 * Set the SDRAM0_BxCR regs +			 */ +			cr = 0; +			switch (bank_size_id) { +			case 0x02: +				cr |= SDRAM_BXCR_SDSZ_8; +				break; +			case 0x04: +				cr |= SDRAM_BXCR_SDSZ_16; +				break; +			case 0x08: +				cr |= SDRAM_BXCR_SDSZ_32; +				break; +			case 0x10: +				cr |= SDRAM_BXCR_SDSZ_64; +				break; +			case 0x20: +				cr |= SDRAM_BXCR_SDSZ_128; +				break; +			case 0x40: +				cr |= SDRAM_BXCR_SDSZ_256; +				break; +			case 0x80: +				cr |= SDRAM_BXCR_SDSZ_512; +				break; +			default: +				printf("DDR-SDRAM: DIMM %lu BxCR configuration.\n", +				       dimm_num); +				printf("ERROR: Unsupported value for the banksize: %d.\n", +				       bank_size_id); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +			} + +			switch (num_col_addr) { +			case 0x08: +				cr |= SDRAM_BXCR_SDAM_1; +				break; +			case 0x09: +				cr |= SDRAM_BXCR_SDAM_2; +				break; +			case 0x0A: +				cr |= SDRAM_BXCR_SDAM_3; +				break; +			case 0x0B: +				cr |= SDRAM_BXCR_SDAM_4; +				break; +			default: +				printf("DDR-SDRAM: DIMM %lu BxCR configuration.\n", +				       dimm_num); +				printf("ERROR: Unsupported value for number of " +				       "column addresses: %d.\n", num_col_addr); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +			} + +			/* +			 * enable the bank +			 */ +			cr |= SDRAM_BXCR_SDBE; + +			for (i = 0; i < num_banks; i++) { +				bank_parms[ctrl_bank_num[dimm_num]+i].bank_size_bytes = +					(4 << 20) * bank_size_id; +				bank_parms[ctrl_bank_num[dimm_num]+i].cr = cr; +				debug("DIMM%ld-bank %ld (SDRAM0_B%ldCR): " +					"bank_size_bytes=%ld\n", +					dimm_num, i, +					ctrl_bank_num[dimm_num] + i, +					bank_parms[ctrl_bank_num[dimm_num] + i].bank_size_bytes); +			} +		} +	} + +	/* Initialize sort tables */ +	for (i = 0; i < MAXBXCR; i++) { +		sorted_bank_num[i] = i; +		sorted_bank_size[i] = bank_parms[i].bank_size_bytes; +	} + +	for (i = 0; i < MAXBXCR-1; i++) { +		largest_size = sorted_bank_size[i]; +		largest_size_index = 255; + +		/* Find the largest remaining value */ +		for (j = i + 1; j < MAXBXCR; j++) { +			if (sorted_bank_size[j] > largest_size) { +				/* Save largest remaining value and its index */ +				largest_size = sorted_bank_size[j]; +				largest_size_index = j; +			} +		} + +		if (largest_size_index != 255) { +			/* Swap the current and largest values */ +			current_size_index = sorted_bank_num[largest_size_index]; +			sorted_bank_size[largest_size_index] = sorted_bank_size[i]; +			sorted_bank_size[i] = largest_size; +			sorted_bank_num[largest_size_index] = sorted_bank_num[i]; +			sorted_bank_num[i] = current_size_index; +		} +	} + +	/* Set the SDRAM0_BxCR regs thanks to sort tables */ +	for (bx_cr_num = 0, bank_base_addr = 0; bx_cr_num < MAXBXCR; bx_cr_num++) { +		if (bank_parms[sorted_bank_num[bx_cr_num]].bank_size_bytes) { +			mtdcr(SDRAM0_CFGADDR, SDRAM0_B0CR + (sorted_bank_num[bx_cr_num] << 2)); +			temp = mfdcr(SDRAM0_CFGDATA) & ~(SDRAM_BXCR_SDBA_MASK | SDRAM_BXCR_SDSZ_MASK | +						  SDRAM_BXCR_SDAM_MASK | SDRAM_BXCR_SDBE); +			temp = temp | (bank_base_addr & SDRAM_BXCR_SDBA_MASK) | +				bank_parms[sorted_bank_num[bx_cr_num]].cr; +			mtdcr(SDRAM0_CFGDATA, temp); +			bank_base_addr += bank_parms[sorted_bank_num[bx_cr_num]].bank_size_bytes; +			debug("SDRAM0_B%ldCR=0x%08lx\n", +				sorted_bank_num[bx_cr_num], temp); +		} +	} + +	return(bank_base_addr); +} +#endif /* CONFIG_SPD_EEPROM */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/44x_spd_ddr2.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/44x_spd_ddr2.c new file mode 100644 index 00000000..f8d03cba --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/44x_spd_ddr2.c @@ -0,0 +1,3129 @@ +/* + * arch/powerpc/cpu/ppc4xx/44x_spd_ddr2.c + * This SPD SDRAM detection code supports AMCC PPC44x cpu's with a + * DDR2 controller (non Denali Core). Those currently are: + * + * 405:		405EX(r) + * 440/460:	440SP/440SPe/460EX/460GT + * + * Copyright (c) 2008 Nuovation System Designs, LLC + *   Grant Erickson <gerickson@nuovations.com> + + * (C) Copyright 2007-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * COPYRIGHT   AMCC   CORPORATION 2004 + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <command.h> +#include <asm/ppc4xx.h> +#include <i2c.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/mmu.h> +#include <asm/cache.h> + +#include "ecc.h" + +#define PPC4xx_IBM_DDR2_DUMP_REGISTER(mnemonic)				\ +	do {								\ +		u32 data;						\ +		mfsdram(SDRAM_##mnemonic, data);			\ +		printf("%20s[%02x] = 0x%08X\n",				\ +		       "SDRAM_" #mnemonic, SDRAM_##mnemonic, data);	\ +	} while (0) + +#define PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(mnemonic)			\ +	do {								\ +		u32 data;						\ +		data = mfdcr(SDRAM_##mnemonic);				\ +		printf("%20s[%02x] = 0x%08X\n",				\ +		       "SDRAM_" #mnemonic, SDRAM_##mnemonic, data);	\ +	} while (0) + +static void update_rdcc(void) +{ +	u32 val; + +	/* +	 * Complete RDSS configuration as mentioned on page 7 of the AMCC +	 * PowerPC440SP/SPe DDR2 application note: +	 * "DDR1/DDR2 Initialization Sequence and Dynamic Tuning" +	 * +	 * Or item #10 "10. Complete RDSS configuration" in chapter +	 * "22.2.9 SDRAM Initialization" of AMCC PPC460EX/EXr/GT users +	 * manual. +	 */ +	mfsdram(SDRAM_RTSR, val); +	if ((val & SDRAM_RTSR_TRK1SM_MASK) == SDRAM_RTSR_TRK1SM_ATPLS1) { +		mfsdram(SDRAM_RDCC, val); +		if ((val & SDRAM_RDCC_RDSS_MASK) != SDRAM_RDCC_RDSS_T4) { +			val += 0x40000000; +			mtsdram(SDRAM_RDCC, val); +		} +	} +} + +#if defined(CONFIG_440) +/* + * This DDR2 setup code can dynamically setup the TLB entries for the DDR2 + * memory region. Right now the cache should still be disabled in U-Boot + * because of the EMAC driver, that need its buffer descriptor to be located + * in non cached memory. + * + * If at some time this restriction doesn't apply anymore, just define + * CONFIG_4xx_DCACHE in the board config file and this code should setup + * everything correctly. + */ +#ifdef CONFIG_4xx_DCACHE +/* enable caching on SDRAM */ +#define MY_TLB_WORD2_I_ENABLE		0 +#else +/* disable caching on SDRAM */ +#define MY_TLB_WORD2_I_ENABLE		TLB_WORD2_I_ENABLE +#endif /* CONFIG_4xx_DCACHE */ + +void dcbz_area(u32 start_address, u32 num_bytes); +#endif /* CONFIG_440 */ + +#define MAXRANKS	4 +#define MAXBXCF		4 + +#define MULDIV64(m1, m2, d)	(u32)(((u64)(m1) * (u64)(m2)) / (u64)(d)) + +/*-----------------------------------------------------------------------------+ + * sdram_memsize + *-----------------------------------------------------------------------------*/ +phys_size_t sdram_memsize(void) +{ +	phys_size_t mem_size; +	unsigned long mcopt2; +	unsigned long mcstat; +	unsigned long mb0cf; +	unsigned long sdsz; +	unsigned long i; + +	mem_size = 0; + +	mfsdram(SDRAM_MCOPT2, mcopt2); +	mfsdram(SDRAM_MCSTAT, mcstat); + +	/* DDR controller must be enabled and not in self-refresh. */ +	/* Otherwise memsize is zero. */ +	if (((mcopt2 & SDRAM_MCOPT2_DCEN_MASK) == SDRAM_MCOPT2_DCEN_ENABLE) +	    && ((mcopt2 & SDRAM_MCOPT2_SREN_MASK) == SDRAM_MCOPT2_SREN_EXIT) +	    && ((mcstat & (SDRAM_MCSTAT_MIC_MASK | SDRAM_MCSTAT_SRMS_MASK)) +		== (SDRAM_MCSTAT_MIC_COMP | SDRAM_MCSTAT_SRMS_NOT_SF))) { +		for (i = 0; i < MAXBXCF; i++) { +			mfsdram(SDRAM_MB0CF + (i << 2), mb0cf); +			/* Banks enabled */ +			if ((mb0cf & SDRAM_BXCF_M_BE_MASK) == SDRAM_BXCF_M_BE_ENABLE) { +#if defined(CONFIG_440) +				sdsz = mfdcr_any(SDRAM_R0BAS + i) & SDRAM_RXBAS_SDSZ_MASK; +#else +				sdsz = mb0cf & SDRAM_RXBAS_SDSZ_MASK; +#endif +				switch(sdsz) { +				case SDRAM_RXBAS_SDSZ_8: +					mem_size+=8; +					break; +				case SDRAM_RXBAS_SDSZ_16: +					mem_size+=16; +					break; +				case SDRAM_RXBAS_SDSZ_32: +					mem_size+=32; +					break; +				case SDRAM_RXBAS_SDSZ_64: +					mem_size+=64; +					break; +				case SDRAM_RXBAS_SDSZ_128: +					mem_size+=128; +					break; +				case SDRAM_RXBAS_SDSZ_256: +					mem_size+=256; +					break; +				case SDRAM_RXBAS_SDSZ_512: +					mem_size+=512; +					break; +				case SDRAM_RXBAS_SDSZ_1024: +					mem_size+=1024; +					break; +				case SDRAM_RXBAS_SDSZ_2048: +					mem_size+=2048; +					break; +				case SDRAM_RXBAS_SDSZ_4096: +					mem_size+=4096; +					break; +				default: +					printf("WARNING: Unsupported bank size (SDSZ=0x%lx)!\n" +					       , sdsz); +					mem_size=0; +					break; +				} +			} +		} +	} + +	return mem_size << 20; +} + +/*-----------------------------------------------------------------------------+ + * is_ecc_enabled + *-----------------------------------------------------------------------------*/ +static unsigned long is_ecc_enabled(void) +{ +	unsigned long val; + +	mfsdram(SDRAM_MCOPT1, val); + +	return SDRAM_MCOPT1_MCHK_CHK_DECODE(val); +} + +/*-----------------------------------------------------------------------------+ + * board_add_ram_info + *-----------------------------------------------------------------------------*/ +void board_add_ram_info(int use_default) +{ +	PPC4xx_SYS_INFO board_cfg; +	u32 val; + +	if (is_ecc_enabled()) +		puts(" (ECC"); +	else +		puts(" (ECC not"); + +	get_sys_info(&board_cfg); + +#if defined(CONFIG_405EX) +	val = board_cfg.freqPLB; +#else +	mfsdr(SDR0_DDR0, val); +	val = MULDIV64((board_cfg.freqPLB), SDR0_DDR0_DDRM_DECODE(val), 1); +#endif +	printf(" enabled, %d MHz", (val * 2) / 1000000); + +	mfsdram(SDRAM_MMODE, val); +	val = (val & SDRAM_MMODE_DCL_MASK) >> 4; +	printf(", CL%d)", val); +} + +#if defined(CONFIG_SPD_EEPROM) + +/*-----------------------------------------------------------------------------+ + * Defines + *-----------------------------------------------------------------------------*/ +#define SDRAM_DDR1	1 +#define SDRAM_DDR2	2 +#define SDRAM_NONE	0 + +#define MAXDIMMS	2 +#define MAX_SPD_BYTES	256   /* Max number of bytes on the DIMM's SPD EEPROM */ + +#define ONE_BILLION	1000000000 + +#define CMD_NOP		(7 << 19) +#define CMD_PRECHARGE	(2 << 19) +#define CMD_REFRESH	(1 << 19) +#define CMD_EMR		(0 << 19) +#define CMD_READ	(5 << 19) +#define CMD_WRITE	(4 << 19) + +#define SELECT_MR	(0 << 16) +#define SELECT_EMR	(1 << 16) +#define SELECT_EMR2	(2 << 16) +#define SELECT_EMR3	(3 << 16) + +/* MR */ +#define DLL_RESET	0x00000100 + +#define WRITE_RECOV_2	(1 << 9) +#define WRITE_RECOV_3	(2 << 9) +#define WRITE_RECOV_4	(3 << 9) +#define WRITE_RECOV_5	(4 << 9) +#define WRITE_RECOV_6	(5 << 9) + +#define BURST_LEN_4	0x00000002 + +/* EMR */ +#define ODT_0_OHM	0x00000000 +#define ODT_50_OHM	0x00000044 +#define ODT_75_OHM	0x00000004 +#define ODT_150_OHM	0x00000040 + +#define ODS_FULL	0x00000000 +#define ODS_REDUCED	0x00000002 +#define OCD_CALIB_DEF	0x00000380 + +/* defines for ODT (On Die Termination) of the 440SP(e) DDR2 controller */ +#define ODT_EB0R	(0x80000000 >> 8) +#define ODT_EB0W	(0x80000000 >> 7) +#define CALC_ODT_R(n)	(ODT_EB0R << (n << 1)) +#define CALC_ODT_W(n)	(ODT_EB0W << (n << 1)) +#define CALC_ODT_RW(n)	(CALC_ODT_R(n) | CALC_ODT_W(n)) + +/* Defines for the Read Cycle Delay test */ +#define NUMMEMTESTS	8 +#define NUMMEMWORDS	8 +#define NUMLOOPS	64		/* memory test loops */ + +/* + * Newer PPC's like 440SPe, 460EX/GT can be equipped with more than 2GB of SDRAM. + * To support such configurations, we "only" map the first 2GB via the TLB's. We + * need some free virtual address space for the remaining peripherals like, SoC + * devices, FLASH etc. + * + * Note that ECC is currently not supported on configurations with more than 2GB + * SDRAM. This is because we only map the first 2GB on such systems, and therefore + * the ECC parity byte of the remaining area can't be written. + */ + +/* + * Board-specific Platform code can reimplement spd_ddr_init_hang () if needed + */ +void __spd_ddr_init_hang (void) +{ +	hang (); +} +void spd_ddr_init_hang (void) __attribute__((weak, alias("__spd_ddr_init_hang"))); + +/* + * To provide an interface for board specific config values in this common + * DDR setup code, we implement he "weak" default functions here. They return + * the default value back to the caller. + * + * Please see include/configs/yucca.h for an example fora board specific + * implementation. + */ +u32 __ddr_wrdtr(u32 default_val) +{ +	return default_val; +} +u32 ddr_wrdtr(u32) __attribute__((weak, alias("__ddr_wrdtr"))); + +u32 __ddr_clktr(u32 default_val) +{ +	return default_val; +} +u32 ddr_clktr(u32) __attribute__((weak, alias("__ddr_clktr"))); + + +/* Private Structure Definitions */ + +/* enum only to ease code for cas latency setting */ +typedef enum ddr_cas_id { +	DDR_CAS_2      = 20, +	DDR_CAS_2_5    = 25, +	DDR_CAS_3      = 30, +	DDR_CAS_4      = 40, +	DDR_CAS_5      = 50 +} ddr_cas_id_t; + +/*-----------------------------------------------------------------------------+ + * Prototypes + *-----------------------------------------------------------------------------*/ +static void get_spd_info(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks); +static void check_mem_type(unsigned long *dimm_populated, +			   unsigned char *iic0_dimm_addr, +			   unsigned long num_dimm_banks); +static void check_frequency(unsigned long *dimm_populated, +			    unsigned char *iic0_dimm_addr, +			    unsigned long num_dimm_banks); +static void check_rank_number(unsigned long *dimm_populated, +			      unsigned char *iic0_dimm_addr, +			      unsigned long num_dimm_banks); +static void check_voltage_type(unsigned long *dimm_populated, +			       unsigned char *iic0_dimm_addr, +			       unsigned long num_dimm_banks); +static void program_memory_queue(unsigned long *dimm_populated, +				 unsigned char *iic0_dimm_addr, +				 unsigned long num_dimm_banks); +static void program_codt(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks); +static void program_mode(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks, +			 ddr_cas_id_t *selected_cas, +			 int *write_recovery); +static void program_tr(unsigned long *dimm_populated, +		       unsigned char *iic0_dimm_addr, +		       unsigned long num_dimm_banks); +static void program_rtr(unsigned long *dimm_populated, +			unsigned char *iic0_dimm_addr, +			unsigned long num_dimm_banks); +static void program_bxcf(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks); +static void program_copt1(unsigned long *dimm_populated, +			  unsigned char *iic0_dimm_addr, +			  unsigned long num_dimm_banks); +static void program_initplr(unsigned long *dimm_populated, +			    unsigned char *iic0_dimm_addr, +			    unsigned long num_dimm_banks, +			    ddr_cas_id_t selected_cas, +			    int write_recovery); +#ifdef CONFIG_DDR_ECC +static void program_ecc(unsigned long *dimm_populated, +			unsigned char *iic0_dimm_addr, +			unsigned long num_dimm_banks, +			unsigned long tlb_word2_i_value); +#endif +#if !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +static void program_DQS_calibration(unsigned long *dimm_populated, +				unsigned char *iic0_dimm_addr, +				unsigned long num_dimm_banks); +#ifdef HARD_CODED_DQS /* calibration test with hardvalues */ +static void	test(void); +#else +static void	DQS_calibration_process(void); +#endif +#endif + +static unsigned char spd_read(uchar chip, uint addr) +{ +	unsigned char data[2]; + +	if (i2c_probe(chip) == 0) +		if (i2c_read(chip, addr, 1, data, 1) == 0) +			return data[0]; + +	return 0; +} + +/*-----------------------------------------------------------------------------+ + * initdram.  Initializes the 440SP Memory Queue and DDR SDRAM controller. + * Note: This routine runs from flash with a stack set up in the chip's + * sram space.  It is important that the routine does not require .sbss, .bss or + * .data sections.  It also cannot call routines that require these sections. + *-----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------- + * Function:	 initdram + * Description:  Configures SDRAM memory banks for DDR operation. + *		 Auto Memory Configuration option reads the DDR SDRAM EEPROMs + *		 via the IIC bus and then configures the DDR SDRAM memory + *		 banks appropriately. If Auto Memory Configuration is + *		 not used, it is assumed that no DIMM is plugged + *-----------------------------------------------------------------------------*/ +phys_size_t initdram(int board_type) +{ +	unsigned char iic0_dimm_addr[] = SPD_EEPROM_ADDRESS; +	unsigned long dimm_populated[MAXDIMMS] = {SDRAM_NONE, SDRAM_NONE}; +	unsigned long num_dimm_banks;		/* on board dimm banks */ +	unsigned long val; +	ddr_cas_id_t selected_cas = DDR_CAS_5;	/* preset to silence compiler */ +	int write_recovery; +	phys_size_t dram_size = 0; + +	num_dimm_banks = sizeof(iic0_dimm_addr); + +	/*------------------------------------------------------------------ +	 * Reset the DDR-SDRAM controller. +	 *-----------------------------------------------------------------*/ +	mtsdr(SDR0_SRST, SDR0_SRST0_DMC); +	mtsdr(SDR0_SRST, 0x00000000); + +	/* +	 * Make sure I2C controller is initialized +	 * before continuing. +	 */ + +	/* switch to correct I2C bus */ +	i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM); + +	/*------------------------------------------------------------------ +	 * Clear out the serial presence detect buffers. +	 * Perform IIC reads from the dimm.  Fill in the spds. +	 * Check to see if the dimm slots are populated +	 *-----------------------------------------------------------------*/ +	get_spd_info(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Check the memory type for the dimms plugged. +	 *-----------------------------------------------------------------*/ +	check_mem_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Check the frequency supported for the dimms plugged. +	 *-----------------------------------------------------------------*/ +	check_frequency(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Check the total rank number. +	 *-----------------------------------------------------------------*/ +	check_rank_number(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Check the voltage type for the dimms plugged. +	 *-----------------------------------------------------------------*/ +	check_voltage_type(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Program SDRAM controller options 2 register +	 * Except Enabling of the memory controller. +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_MCOPT2, val); +	mtsdram(SDRAM_MCOPT2, +		(val & +		 ~(SDRAM_MCOPT2_SREN_MASK | SDRAM_MCOPT2_PMEN_MASK | +		   SDRAM_MCOPT2_IPTR_MASK | SDRAM_MCOPT2_XSRP_MASK | +		   SDRAM_MCOPT2_ISIE_MASK)) +		| (SDRAM_MCOPT2_SREN_ENTER | SDRAM_MCOPT2_PMEN_DISABLE | +		   SDRAM_MCOPT2_IPTR_IDLE | SDRAM_MCOPT2_XSRP_ALLOW | +		   SDRAM_MCOPT2_ISIE_ENABLE)); + +	/*------------------------------------------------------------------ +	 * Program SDRAM controller options 1 register +	 * Note: Does not enable the memory controller. +	 *-----------------------------------------------------------------*/ +	program_copt1(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Set the SDRAM Controller On Die Termination Register +	 *-----------------------------------------------------------------*/ +	program_codt(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Program SDRAM refresh register. +	 *-----------------------------------------------------------------*/ +	program_rtr(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Program SDRAM mode register. +	 *-----------------------------------------------------------------*/ +	program_mode(dimm_populated, iic0_dimm_addr, num_dimm_banks, +		     &selected_cas, &write_recovery); + +	/*------------------------------------------------------------------ +	 * Set the SDRAM Write Data/DM/DQS Clock Timing Reg +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_WRDTR, val); +	mtsdram(SDRAM_WRDTR, (val & ~(SDRAM_WRDTR_LLWP_MASK | SDRAM_WRDTR_WTR_MASK)) | +		ddr_wrdtr(SDRAM_WRDTR_LLWP_1_CYC | SDRAM_WRDTR_WTR_90_DEG_ADV)); + +	/*------------------------------------------------------------------ +	 * Set the SDRAM Clock Timing Register +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_CLKTR, val); +	mtsdram(SDRAM_CLKTR, (val & ~SDRAM_CLKTR_CLKP_MASK) | +		ddr_clktr(SDRAM_CLKTR_CLKP_0_DEG)); + +	/*------------------------------------------------------------------ +	 * Program the BxCF registers. +	 *-----------------------------------------------------------------*/ +	program_bxcf(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Program SDRAM timing registers. +	 *-----------------------------------------------------------------*/ +	program_tr(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Set the Extended Mode register +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_MEMODE, val); +	mtsdram(SDRAM_MEMODE, +		(val & ~(SDRAM_MEMODE_DIC_MASK  | SDRAM_MEMODE_DLL_MASK | +			 SDRAM_MEMODE_RTT_MASK | SDRAM_MEMODE_DQS_MASK)) | +		(SDRAM_MEMODE_DIC_NORMAL | SDRAM_MEMODE_DLL_ENABLE +		 | SDRAM_MEMODE_RTT_150OHM | SDRAM_MEMODE_DQS_ENABLE)); + +	/*------------------------------------------------------------------ +	 * Program Initialization preload registers. +	 *-----------------------------------------------------------------*/ +	program_initplr(dimm_populated, iic0_dimm_addr, num_dimm_banks, +			selected_cas, write_recovery); + +	/*------------------------------------------------------------------ +	 * Delay to ensure 200usec have elapsed since reset. +	 *-----------------------------------------------------------------*/ +	udelay(400); + +	/*------------------------------------------------------------------ +	 * Set the memory queue core base addr. +	 *-----------------------------------------------------------------*/ +	program_memory_queue(dimm_populated, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Program SDRAM controller options 2 register +	 * Enable the memory controller. +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_MCOPT2, val); +	mtsdram(SDRAM_MCOPT2, +		(val & ~(SDRAM_MCOPT2_SREN_MASK | SDRAM_MCOPT2_DCEN_MASK | +			 SDRAM_MCOPT2_IPTR_MASK | SDRAM_MCOPT2_ISIE_MASK)) | +			 SDRAM_MCOPT2_IPTR_EXECUTE); + +	/*------------------------------------------------------------------ +	 * Wait for IPTR_EXECUTE init sequence to complete. +	 *-----------------------------------------------------------------*/ +	do { +		mfsdram(SDRAM_MCSTAT, val); +	} while ((val & SDRAM_MCSTAT_MIC_MASK) == SDRAM_MCSTAT_MIC_NOTCOMP); + +	/* enable the controller only after init sequence completes */ +	mfsdram(SDRAM_MCOPT2, val); +	mtsdram(SDRAM_MCOPT2, (val | SDRAM_MCOPT2_DCEN_ENABLE)); + +	/* Make sure delay-line calibration is done before proceeding */ +	do { +		mfsdram(SDRAM_DLCR, val); +	} while (!(val & SDRAM_DLCR_DLCS_COMPLETE)); + +	/* get installed memory size */ +	dram_size = sdram_memsize(); + +	/* +	 * Limit size to 2GB +	 */ +	if (dram_size > CONFIG_MAX_MEM_MAPPED) +		dram_size = CONFIG_MAX_MEM_MAPPED; + +	/* and program tlb entries for this size (dynamic) */ + +	/* +	 * Program TLB entries with caches enabled, for best performace +	 * while auto-calibrating and ECC generation +	 */ +	program_tlb(0, 0, dram_size, 0); + +	/*------------------------------------------------------------------ +	 * DQS calibration. +	 *-----------------------------------------------------------------*/ +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +	DQS_autocalibration(); +#else +	program_DQS_calibration(dimm_populated, iic0_dimm_addr, num_dimm_banks); +#endif +	/* +	 * Now complete RDSS configuration as mentioned on page 7 of the AMCC +	 * PowerPC440SP/SPe DDR2 application note: +	 * "DDR1/DDR2 Initialization Sequence and Dynamic Tuning" +	 */ +	update_rdcc(); + +#ifdef CONFIG_DDR_ECC +	/*------------------------------------------------------------------ +	 * If ecc is enabled, initialize the parity bits. +	 *-----------------------------------------------------------------*/ +	program_ecc(dimm_populated, iic0_dimm_addr, num_dimm_banks, 0); +#endif + +	/* +	 * Flush the dcache before removing the TLB with caches +	 * enabled. Otherwise this might lead to problems later on, +	 * e.g. while booting Linux (as seen on ICON-440SPe). +	 */ +	flush_dcache(); + +	/* +	 * Now after initialization (auto-calibration and ECC generation) +	 * remove the TLB entries with caches enabled and program again with +	 * desired cache functionality +	 */ +	remove_tlb(0, dram_size); +	program_tlb(0, 0, dram_size, MY_TLB_WORD2_I_ENABLE); + +	ppc4xx_ibm_ddr2_register_dump(); + +	/* +	 * Clear potential errors resulting from auto-calibration. +	 * If not done, then we could get an interrupt later on when +	 * exceptions are enabled. +	 */ +	set_mcsr(get_mcsr()); + +	return sdram_memsize(); +} + +static void get_spd_info(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long dimm_found; +	unsigned char num_of_bytes; +	unsigned char total_size; + +	dimm_found = false; +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		num_of_bytes = 0; +		total_size = 0; + +		num_of_bytes = spd_read(iic0_dimm_addr[dimm_num], 0); +		debug("\nspd_read(0x%x) returned %d\n", +		      iic0_dimm_addr[dimm_num], num_of_bytes); +		total_size = spd_read(iic0_dimm_addr[dimm_num], 1); +		debug("spd_read(0x%x) returned %d\n", +		      iic0_dimm_addr[dimm_num], total_size); + +		if ((num_of_bytes != 0) && (total_size != 0)) { +			dimm_populated[dimm_num] = true; +			dimm_found = true; +			debug("DIMM slot %lu: populated\n", dimm_num); +		} else { +			dimm_populated[dimm_num] = false; +			debug("DIMM slot %lu: Not populated\n", dimm_num); +		} +	} + +	if (dimm_found == false) { +		printf("ERROR - No memory installed. Install a DDR-SDRAM DIMM.\n\n"); +		spd_ddr_init_hang (); +	} +} + + +/*------------------------------------------------------------------ + * For the memory DIMMs installed, this routine verifies that they + * really are DDR specific DIMMs. + *-----------------------------------------------------------------*/ +static void check_mem_type(unsigned long *dimm_populated, +			   unsigned char *iic0_dimm_addr, +			   unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long dimm_type; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] == true) { +			dimm_type = spd_read(iic0_dimm_addr[dimm_num], 2); +			switch (dimm_type) { +			case 1: +				printf("ERROR: Standard Fast Page Mode DRAM DIMM detected in " +				       "slot %d.\n", (unsigned int)dimm_num); +				printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +				break; +			case 2: +				printf("ERROR: EDO DIMM detected in slot %d.\n", +				       (unsigned int)dimm_num); +				printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +				break; +			case 3: +				printf("ERROR: Pipelined Nibble DIMM detected in slot %d.\n", +				       (unsigned int)dimm_num); +				printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +				break; +			case 4: +				printf("ERROR: SDRAM DIMM detected in slot %d.\n", +				       (unsigned int)dimm_num); +				printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +				break; +			case 5: +				printf("ERROR: Multiplexed ROM DIMM detected in slot %d.\n", +				       (unsigned int)dimm_num); +				printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +				break; +			case 6: +				printf("ERROR: SGRAM DIMM detected in slot %d.\n", +				       (unsigned int)dimm_num); +				printf("Only DDR and DDR2 SDRAM DIMMs are supported.\n"); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +				break; +			case 7: +				debug("DIMM slot %lu: DDR1 SDRAM detected\n", dimm_num); +				dimm_populated[dimm_num] = SDRAM_DDR1; +				break; +			case 8: +				debug("DIMM slot %lu: DDR2 SDRAM detected\n", dimm_num); +				dimm_populated[dimm_num] = SDRAM_DDR2; +				break; +			default: +				printf("ERROR: Unknown DIMM detected in slot %d.\n", +				       (unsigned int)dimm_num); +				printf("Only DDR1 and DDR2 SDRAM DIMMs are supported.\n"); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +				break; +			} +		} +	} +	for (dimm_num = 1; dimm_num < num_dimm_banks; dimm_num++) { +		if ((dimm_populated[dimm_num-1] != SDRAM_NONE) +		    && (dimm_populated[dimm_num]   != SDRAM_NONE) +		    && (dimm_populated[dimm_num-1] != dimm_populated[dimm_num])) { +			printf("ERROR: DIMM's DDR1 and DDR2 type can not be mixed.\n"); +			spd_ddr_init_hang (); +		} +	} +} + +/*------------------------------------------------------------------ + * For the memory DIMMs installed, this routine verifies that + * frequency previously calculated is supported. + *-----------------------------------------------------------------*/ +static void check_frequency(unsigned long *dimm_populated, +			    unsigned char *iic0_dimm_addr, +			    unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long tcyc_reg; +	unsigned long cycle_time; +	unsigned long calc_cycle_time; +	unsigned long sdram_freq; +	unsigned long sdr_ddrpll; +	PPC4xx_SYS_INFO board_cfg; + +	/*------------------------------------------------------------------ +	 * Get the board configuration info. +	 *-----------------------------------------------------------------*/ +	get_sys_info(&board_cfg); + +	mfsdr(SDR0_DDR0, sdr_ddrpll); +	sdram_freq = ((board_cfg.freqPLB) * SDR0_DDR0_DDRM_DECODE(sdr_ddrpll)); + +	/* +	 * calc_cycle_time is calculated from DDR frequency set by board/chip +	 * and is expressed in multiple of 10 picoseconds +	 * to match the way DIMM cycle time is calculated below. +	 */ +	calc_cycle_time = MULDIV64(ONE_BILLION, 100, sdram_freq); + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] != SDRAM_NONE) { +			tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 9); +			/* +			 * Byte 9, Cycle time for CAS Latency=X, is split into two nibbles: +			 * the higher order nibble (bits 4-7) designates the cycle time +			 * to a granularity of 1ns; +			 * the value presented by the lower order nibble (bits 0-3) +			 * has a granularity of .1ns and is added to the value designated +			 * by the higher nibble. In addition, four lines of the lower order +			 * nibble are assigned to support +.25,+.33, +.66 and +.75. +			 */ +			 /* Convert from hex to decimal */ +			if ((tcyc_reg & 0x0F) == 0x0D) +				cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + 75; +			else if ((tcyc_reg & 0x0F) == 0x0C) +				cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + 66; +			else if ((tcyc_reg & 0x0F) == 0x0B) +				cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + 33; +			else if ((tcyc_reg & 0x0F) == 0x0A) +				cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + 25; +			else +				cycle_time = (((tcyc_reg & 0xF0) >> 4) * 100) + +					((tcyc_reg & 0x0F)*10); +			debug("cycle_time=%lu [10 picoseconds]\n", cycle_time); + +			if  (cycle_time > (calc_cycle_time + 10)) { +				/* +				 * the provided sdram cycle_time is too small +				 * for the available DIMM cycle_time. +				 * The additionnal 100ps is here to accept a small incertainty. +				 */ +				printf("ERROR: DRAM DIMM detected with cycle_time %d ps in " +				       "slot %d \n while calculated cycle time is %d ps.\n", +				       (unsigned int)(cycle_time*10), +				       (unsigned int)dimm_num, +				       (unsigned int)(calc_cycle_time*10)); +				printf("Replace the DIMM, or change DDR frequency via " +				       "strapping bits.\n\n"); +				spd_ddr_init_hang (); +			} +		} +	} +} + +/*------------------------------------------------------------------ + * For the memory DIMMs installed, this routine verifies two + * ranks/banks maximum are availables. + *-----------------------------------------------------------------*/ +static void check_rank_number(unsigned long *dimm_populated, +			      unsigned char *iic0_dimm_addr, +			      unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long dimm_rank; +	unsigned long total_rank = 0; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] != SDRAM_NONE) { +			dimm_rank = spd_read(iic0_dimm_addr[dimm_num], 5); +			if (((unsigned long)spd_read(iic0_dimm_addr[dimm_num], 2)) == 0x08) +				dimm_rank = (dimm_rank & 0x0F) +1; +			else +				dimm_rank = dimm_rank & 0x0F; + + +			if (dimm_rank > MAXRANKS) { +				printf("ERROR: DRAM DIMM detected with %lu ranks in " +				       "slot %lu is not supported.\n", dimm_rank, dimm_num); +				printf("Only %d ranks are supported for all DIMM.\n", MAXRANKS); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +			} else +				total_rank += dimm_rank; +		} +		if (total_rank > MAXRANKS) { +			printf("ERROR: DRAM DIMM detected with a total of %d ranks " +			       "for all slots.\n", (unsigned int)total_rank); +			printf("Only %d ranks are supported for all DIMM.\n", MAXRANKS); +			printf("Remove one of the DIMM modules.\n\n"); +			spd_ddr_init_hang (); +		} +	} +} + +/*------------------------------------------------------------------ + * only support 2.5V modules. + * This routine verifies this. + *-----------------------------------------------------------------*/ +static void check_voltage_type(unsigned long *dimm_populated, +			       unsigned char *iic0_dimm_addr, +			       unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long voltage_type; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] != SDRAM_NONE) { +			voltage_type = spd_read(iic0_dimm_addr[dimm_num], 8); +			switch (voltage_type) { +			case 0x00: +				printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); +				printf("This DIMM is 5.0 Volt/TTL.\n"); +				printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", +				       (unsigned int)dimm_num); +				spd_ddr_init_hang (); +				break; +			case 0x01: +				printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); +				printf("This DIMM is LVTTL.\n"); +				printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", +				       (unsigned int)dimm_num); +				spd_ddr_init_hang (); +				break; +			case 0x02: +				printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); +				printf("This DIMM is 1.5 Volt.\n"); +				printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", +				       (unsigned int)dimm_num); +				spd_ddr_init_hang (); +				break; +			case 0x03: +				printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); +				printf("This DIMM is 3.3 Volt/TTL.\n"); +				printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", +				       (unsigned int)dimm_num); +				spd_ddr_init_hang (); +				break; +			case 0x04: +				/* 2.5 Voltage only for DDR1 */ +				break; +			case 0x05: +				/* 1.8 Voltage only for DDR2 */ +				break; +			default: +				printf("ERROR: Only DIMMs DDR 2.5V or DDR2 1.8V are supported.\n"); +				printf("Replace the DIMM module in slot %d with a supported DIMM.\n\n", +				       (unsigned int)dimm_num); +				spd_ddr_init_hang (); +				break; +			} +		} +	} +} + +/*-----------------------------------------------------------------------------+ + * program_copt1. + *-----------------------------------------------------------------------------*/ +static void program_copt1(unsigned long *dimm_populated, +			  unsigned char *iic0_dimm_addr, +			  unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long mcopt1; +	unsigned long ecc_enabled; +	unsigned long ecc = 0; +	unsigned long data_width = 0; +	unsigned long dimm_32bit; +	unsigned long dimm_64bit; +	unsigned long registered = 0; +	unsigned long attribute = 0; +	unsigned long buf0, buf1; /* TODO: code to be changed for IOP1.6 to support 4 DIMMs */ +	unsigned long bankcount; +	unsigned long val; + +#ifdef CONFIG_DDR_ECC +	ecc_enabled = true; +#else +	ecc_enabled = false; +#endif +	dimm_32bit = false; +	dimm_64bit = false; +	buf0 = false; +	buf1 = false; + +	/*------------------------------------------------------------------ +	 * Set memory controller options reg 1, SDRAM_MCOPT1. +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_MCOPT1, val); +	mcopt1 = val & ~(SDRAM_MCOPT1_MCHK_MASK | SDRAM_MCOPT1_RDEN_MASK | +			 SDRAM_MCOPT1_PMU_MASK  | SDRAM_MCOPT1_DMWD_MASK | +			 SDRAM_MCOPT1_UIOS_MASK | SDRAM_MCOPT1_BCNT_MASK | +			 SDRAM_MCOPT1_DDR_TYPE_MASK | SDRAM_MCOPT1_RWOO_MASK | +			 SDRAM_MCOPT1_WOOO_MASK | SDRAM_MCOPT1_DCOO_MASK | +			 SDRAM_MCOPT1_DREF_MASK); + +	mcopt1 |= SDRAM_MCOPT1_QDEP; +	mcopt1 |= SDRAM_MCOPT1_PMU_OPEN; +	mcopt1 |= SDRAM_MCOPT1_RWOO_DISABLED; +	mcopt1 |= SDRAM_MCOPT1_WOOO_DISABLED; +	mcopt1 |= SDRAM_MCOPT1_DCOO_DISABLED; +	mcopt1 |= SDRAM_MCOPT1_DREF_NORMAL; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] != SDRAM_NONE) { +			/* test ecc support */ +			ecc = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 11); +			if (ecc != 0x02) /* ecc not supported */ +				ecc_enabled = false; + +			/* test bank count */ +			bankcount = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 17); +			if (bankcount == 0x04) /* bank count = 4 */ +				mcopt1 |= SDRAM_MCOPT1_4_BANKS; +			else /* bank count = 8 */ +				mcopt1 |= SDRAM_MCOPT1_8_BANKS; + +			/* test for buffered/unbuffered, registered, differential clocks */ +			registered = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 20); +			attribute = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 21); + +			/* TODO: code to be changed for IOP1.6 to support 4 DIMMs */ +			if (dimm_num == 0) { +				if (dimm_populated[dimm_num] == SDRAM_DDR1) /* DDR1 type */ +					mcopt1 |= SDRAM_MCOPT1_DDR1_TYPE; +				if (dimm_populated[dimm_num] == SDRAM_DDR2) /* DDR2 type */ +					mcopt1 |= SDRAM_MCOPT1_DDR2_TYPE; +				if (registered == 1) { /* DDR2 always buffered */ +					/* TODO: what about above  comments ? */ +					mcopt1 |= SDRAM_MCOPT1_RDEN; +					buf0 = true; +				} else { +					/* TODO: the mask 0x02 doesn't match Samsung def for byte 21. */ +					if ((attribute & 0x02) == 0x00) { +						/* buffered not supported */ +						buf0 = false; +					} else { +						mcopt1 |= SDRAM_MCOPT1_RDEN; +						buf0 = true; +					} +				} +			} +			else if (dimm_num == 1) { +				if (dimm_populated[dimm_num] == SDRAM_DDR1) /* DDR1 type */ +					mcopt1 |= SDRAM_MCOPT1_DDR1_TYPE; +				if (dimm_populated[dimm_num] == SDRAM_DDR2) /* DDR2 type */ +					mcopt1 |= SDRAM_MCOPT1_DDR2_TYPE; +				if (registered == 1) { +					/* DDR2 always buffered */ +					mcopt1 |= SDRAM_MCOPT1_RDEN; +					buf1 = true; +				} else { +					if ((attribute & 0x02) == 0x00) { +						/* buffered not supported */ +						buf1 = false; +					} else { +						mcopt1 |= SDRAM_MCOPT1_RDEN; +						buf1 = true; +					} +				} +			} + +			/* Note that for DDR2 the byte 7 is reserved, but OK to keep code as is. */ +			data_width = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 6) + +				(((unsigned long)spd_read(iic0_dimm_addr[dimm_num], 7)) << 8); + +			switch (data_width) { +			case 72: +			case 64: +				dimm_64bit = true; +				break; +			case 40: +			case 32: +				dimm_32bit = true; +				break; +			default: +				printf("WARNING: Detected a DIMM with a data width of %lu bits.\n", +				       data_width); +				printf("Only DIMMs with 32 or 64 bit DDR-SDRAM widths are supported.\n"); +				break; +			} +		} +	} + +	/* verify matching properties */ +	if ((dimm_populated[0] != SDRAM_NONE) && (dimm_populated[1] != SDRAM_NONE)) { +		if (buf0 != buf1) { +			printf("ERROR: DIMM's buffered/unbuffered, registered, clocking don't match.\n"); +			spd_ddr_init_hang (); +		} +	} + +	if ((dimm_64bit == true) && (dimm_32bit == true)) { +		printf("ERROR: Cannot mix 32 bit and 64 bit DDR-SDRAM DIMMs together.\n"); +		spd_ddr_init_hang (); +	} else if ((dimm_64bit == true) && (dimm_32bit == false)) { +		mcopt1 |= SDRAM_MCOPT1_DMWD_64; +	} else if ((dimm_64bit == false) && (dimm_32bit == true)) { +		mcopt1 |= SDRAM_MCOPT1_DMWD_32; +	} else { +		printf("ERROR: Please install only 32 or 64 bit DDR-SDRAM DIMMs.\n\n"); +		spd_ddr_init_hang (); +	} + +	if (ecc_enabled == true) +		mcopt1 |= SDRAM_MCOPT1_MCHK_GEN; +	else +		mcopt1 |= SDRAM_MCOPT1_MCHK_NON; + +	mtsdram(SDRAM_MCOPT1, mcopt1); +} + +/*-----------------------------------------------------------------------------+ + * program_codt. + *-----------------------------------------------------------------------------*/ +static void program_codt(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks) +{ +	unsigned long codt; +	unsigned long modt0 = 0; +	unsigned long modt1 = 0; +	unsigned long modt2 = 0; +	unsigned long modt3 = 0; +	unsigned char dimm_num; +	unsigned char dimm_rank; +	unsigned char total_rank = 0; +	unsigned char total_dimm = 0; +	unsigned char dimm_type = 0; +	unsigned char firstSlot = 0; + +	/*------------------------------------------------------------------ +	 * Set the SDRAM Controller On Die Termination Register +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_CODT, codt); +	codt &= ~(SDRAM_CODT_DQS_SINGLE_END | SDRAM_CODT_CKSE_SINGLE_END); +	codt |= SDRAM_CODT_IO_NMODE; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] != SDRAM_NONE) { +			dimm_rank = (unsigned long)spd_read(iic0_dimm_addr[dimm_num], 5); +			if (((unsigned long)spd_read(iic0_dimm_addr[dimm_num], 2)) == 0x08) { +				dimm_rank = (dimm_rank & 0x0F) + 1; +				dimm_type = SDRAM_DDR2; +			} else { +				dimm_rank = dimm_rank & 0x0F; +				dimm_type = SDRAM_DDR1; +			} + +			total_rank += dimm_rank; +			total_dimm++; +			if ((dimm_num == 0) && (total_dimm == 1)) +				firstSlot = true; +			else +				firstSlot = false; +		} +	} +	if (dimm_type == SDRAM_DDR2) { +		codt |= SDRAM_CODT_DQS_1_8_V_DDR2; +		if ((total_dimm == 1) && (firstSlot == true)) { +			if (total_rank == 1) {	/* PUUU */ +				codt |= CALC_ODT_R(0); +				modt0 = CALC_ODT_W(0); +				modt1 = 0x00000000; +				modt2 = 0x00000000; +				modt3 = 0x00000000; +			} +			if (total_rank == 2) {	/* PPUU */ +				codt |= CALC_ODT_R(0) | CALC_ODT_R(1); +				modt0 = CALC_ODT_W(0) | CALC_ODT_W(1); +				modt1 = 0x00000000; +				modt2 = 0x00000000; +				modt3 = 0x00000000; +			} +		} else if ((total_dimm == 1) && (firstSlot != true)) { +			if (total_rank == 1) {	/* UUPU */ +				codt |= CALC_ODT_R(2); +				modt0 = 0x00000000; +				modt1 = 0x00000000; +				modt2 = CALC_ODT_W(2); +				modt3 = 0x00000000; +			} +			if (total_rank == 2) {	/* UUPP */ +				codt |= CALC_ODT_R(2) | CALC_ODT_R(3); +				modt0 = 0x00000000; +				modt1 = 0x00000000; +				modt2 = CALC_ODT_W(2) | CALC_ODT_W(3); +				modt3 = 0x00000000; +			} +		} +		if (total_dimm == 2) { +			if (total_rank == 2) {	/* PUPU */ +				codt |= CALC_ODT_R(0) | CALC_ODT_R(2); +				modt0 = CALC_ODT_RW(2); +				modt1 = 0x00000000; +				modt2 = CALC_ODT_RW(0); +				modt3 = 0x00000000; +			} +			if (total_rank == 4) {	/* PPPP */ +				codt |= CALC_ODT_R(0) | CALC_ODT_R(1) | +					CALC_ODT_R(2) | CALC_ODT_R(3); +				modt0 = CALC_ODT_RW(2) | CALC_ODT_RW(3); +				modt1 = 0x00000000; +				modt2 = CALC_ODT_RW(0) | CALC_ODT_RW(1); +				modt3 = 0x00000000; +			} +		} +	} else { +		codt |= SDRAM_CODT_DQS_2_5_V_DDR1; +		modt0 = 0x00000000; +		modt1 = 0x00000000; +		modt2 = 0x00000000; +		modt3 = 0x00000000; + +		if (total_dimm == 1) { +			if (total_rank == 1) +				codt |= 0x00800000; +			if (total_rank == 2) +				codt |= 0x02800000; +		} +		if (total_dimm == 2) { +			if (total_rank == 2) +				codt |= 0x08800000; +			if (total_rank == 4) +				codt |= 0x2a800000; +		} +	} + +	debug("nb of dimm %d\n", total_dimm); +	debug("nb of rank %d\n", total_rank); +	if (total_dimm == 1) +		debug("dimm in slot %d\n", firstSlot); + +	mtsdram(SDRAM_CODT, codt); +	mtsdram(SDRAM_MODT0, modt0); +	mtsdram(SDRAM_MODT1, modt1); +	mtsdram(SDRAM_MODT2, modt2); +	mtsdram(SDRAM_MODT3, modt3); +} + +/*-----------------------------------------------------------------------------+ + * program_initplr. + *-----------------------------------------------------------------------------*/ +static void program_initplr(unsigned long *dimm_populated, +			    unsigned char *iic0_dimm_addr, +			    unsigned long num_dimm_banks, +			    ddr_cas_id_t selected_cas, +			    int write_recovery) +{ +	u32 cas = 0; +	u32 odt = 0; +	u32 ods = 0; +	u32 mr; +	u32 wr; +	u32 emr; +	u32 emr2; +	u32 emr3; +	int dimm_num; +	int total_dimm = 0; + +	/****************************************************** +	 ** Assumption: if more than one DIMM, all DIMMs are the same +	 **		as already checked in check_memory_type +	 ******************************************************/ + +	if ((dimm_populated[0] == SDRAM_DDR1) || (dimm_populated[1] == SDRAM_DDR1)) { +		mtsdram(SDRAM_INITPLR0, 0x81B80000); +		mtsdram(SDRAM_INITPLR1, 0x81900400); +		mtsdram(SDRAM_INITPLR2, 0x81810000); +		mtsdram(SDRAM_INITPLR3, 0xff800162); +		mtsdram(SDRAM_INITPLR4, 0x81900400); +		mtsdram(SDRAM_INITPLR5, 0x86080000); +		mtsdram(SDRAM_INITPLR6, 0x86080000); +		mtsdram(SDRAM_INITPLR7, 0x81000062); +	} else if ((dimm_populated[0] == SDRAM_DDR2) || (dimm_populated[1] == SDRAM_DDR2)) { +		switch (selected_cas) { +		case DDR_CAS_3: +			cas = 3 << 4; +			break; +		case DDR_CAS_4: +			cas = 4 << 4; +			break; +		case DDR_CAS_5: +			cas = 5 << 4; +			break; +		default: +			printf("ERROR: ucode error on selected_cas value %d", selected_cas); +			spd_ddr_init_hang (); +			break; +		} + +#if 0 +		/* +		 * ToDo - Still a problem with the write recovery: +		 * On the Corsair CM2X512-5400C4 module, setting write recovery +		 * in the INITPLR reg to the value calculated in program_mode() +		 * results in not correctly working DDR2 memory (crash after +		 * relocation). +		 * +		 * So for now, set the write recovery to 3. This seems to work +		 * on the Corair module too. +		 * +		 * 2007-03-01, sr +		 */ +		switch (write_recovery) { +		case 3: +			wr = WRITE_RECOV_3; +			break; +		case 4: +			wr = WRITE_RECOV_4; +			break; +		case 5: +			wr = WRITE_RECOV_5; +			break; +		case 6: +			wr = WRITE_RECOV_6; +			break; +		default: +			printf("ERROR: write recovery not support (%d)", write_recovery); +			spd_ddr_init_hang (); +			break; +		} +#else +		wr = WRITE_RECOV_3; /* test-only, see description above */ +#endif + +		for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) +			if (dimm_populated[dimm_num] != SDRAM_NONE) +				total_dimm++; +		if (total_dimm == 1) { +			odt = ODT_150_OHM; +			ods = ODS_FULL; +		} else if (total_dimm == 2) { +			odt = ODT_75_OHM; +			ods = ODS_REDUCED; +		} else { +			printf("ERROR: Unsupported number of DIMM's (%d)", total_dimm); +			spd_ddr_init_hang (); +		} + +		mr = CMD_EMR | SELECT_MR | BURST_LEN_4 | wr | cas; +		emr = CMD_EMR | SELECT_EMR | odt | ods; +		emr2 = CMD_EMR | SELECT_EMR2; +		emr3 = CMD_EMR | SELECT_EMR3; +		/* NOP - Wait 106 MemClk cycles */ +		mtsdram(SDRAM_INITPLR0, SDRAM_INITPLR_ENABLE | CMD_NOP | +					SDRAM_INITPLR_IMWT_ENCODE(106)); +		udelay(1000); +		/* precharge 4 MemClk cycles */ +		mtsdram(SDRAM_INITPLR1, SDRAM_INITPLR_ENABLE | CMD_PRECHARGE | +					SDRAM_INITPLR_IMWT_ENCODE(4)); +		/* EMR2 - Wait tMRD (2 MemClk cycles) */ +		mtsdram(SDRAM_INITPLR2, SDRAM_INITPLR_ENABLE | emr2 | +					SDRAM_INITPLR_IMWT_ENCODE(2)); +		/* EMR3 - Wait tMRD (2 MemClk cycles) */ +		mtsdram(SDRAM_INITPLR3, SDRAM_INITPLR_ENABLE | emr3 | +					SDRAM_INITPLR_IMWT_ENCODE(2)); +		/* EMR DLL ENABLE - Wait tMRD (2 MemClk cycles) */ +		mtsdram(SDRAM_INITPLR4, SDRAM_INITPLR_ENABLE | emr | +					SDRAM_INITPLR_IMWT_ENCODE(2)); +		/* MR w/ DLL reset - 200 cycle wait for DLL reset */ +		mtsdram(SDRAM_INITPLR5, SDRAM_INITPLR_ENABLE | mr | DLL_RESET | +					SDRAM_INITPLR_IMWT_ENCODE(200)); +		udelay(1000); +		/* precharge 4 MemClk cycles */ +		mtsdram(SDRAM_INITPLR6, SDRAM_INITPLR_ENABLE | CMD_PRECHARGE | +					SDRAM_INITPLR_IMWT_ENCODE(4)); +		/* Refresh 25 MemClk cycles */ +		mtsdram(SDRAM_INITPLR7, SDRAM_INITPLR_ENABLE | CMD_REFRESH | +					SDRAM_INITPLR_IMWT_ENCODE(25)); +		/* Refresh 25 MemClk cycles */ +		mtsdram(SDRAM_INITPLR8, SDRAM_INITPLR_ENABLE | CMD_REFRESH | +					SDRAM_INITPLR_IMWT_ENCODE(25)); +		/* Refresh 25 MemClk cycles */ +		mtsdram(SDRAM_INITPLR9, SDRAM_INITPLR_ENABLE | CMD_REFRESH | +					SDRAM_INITPLR_IMWT_ENCODE(25)); +		/* Refresh 25 MemClk cycles */ +		mtsdram(SDRAM_INITPLR10, SDRAM_INITPLR_ENABLE | CMD_REFRESH | +					 SDRAM_INITPLR_IMWT_ENCODE(25)); +		/* MR w/o DLL reset - Wait tMRD (2 MemClk cycles) */ +		mtsdram(SDRAM_INITPLR11, SDRAM_INITPLR_ENABLE | mr | +					 SDRAM_INITPLR_IMWT_ENCODE(2)); +		/* EMR OCD Default - Wait tMRD (2 MemClk cycles) */ +		mtsdram(SDRAM_INITPLR12, SDRAM_INITPLR_ENABLE | OCD_CALIB_DEF | +					 SDRAM_INITPLR_IMWT_ENCODE(2) | emr); +		/* EMR OCD Exit */ +		mtsdram(SDRAM_INITPLR13, SDRAM_INITPLR_ENABLE | emr | +					 SDRAM_INITPLR_IMWT_ENCODE(2)); +	} else { +		printf("ERROR: ucode error as unknown DDR type in program_initplr"); +		spd_ddr_init_hang (); +	} +} + +/*------------------------------------------------------------------ + * This routine programs the SDRAM_MMODE register. + * the selected_cas is an output parameter, that will be passed + * by caller to call the above program_initplr( ) + *-----------------------------------------------------------------*/ +static void program_mode(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks, +			 ddr_cas_id_t *selected_cas, +			 int *write_recovery) +{ +	unsigned long dimm_num; +	unsigned long sdram_ddr1; +	unsigned long t_wr_ns; +	unsigned long t_wr_clk; +	unsigned long cas_bit; +	unsigned long cas_index; +	unsigned long sdram_freq; +	unsigned long ddr_check; +	unsigned long mmode; +	unsigned long tcyc_reg; +	unsigned long cycle_2_0_clk; +	unsigned long cycle_2_5_clk; +	unsigned long cycle_3_0_clk; +	unsigned long cycle_4_0_clk; +	unsigned long cycle_5_0_clk; +	unsigned long max_2_0_tcyc_ns_x_100; +	unsigned long max_2_5_tcyc_ns_x_100; +	unsigned long max_3_0_tcyc_ns_x_100; +	unsigned long max_4_0_tcyc_ns_x_100; +	unsigned long max_5_0_tcyc_ns_x_100; +	unsigned long cycle_time_ns_x_100[3]; +	PPC4xx_SYS_INFO board_cfg; +	unsigned char cas_2_0_available; +	unsigned char cas_2_5_available; +	unsigned char cas_3_0_available; +	unsigned char cas_4_0_available; +	unsigned char cas_5_0_available; +	unsigned long sdr_ddrpll; + +	/*------------------------------------------------------------------ +	 * Get the board configuration info. +	 *-----------------------------------------------------------------*/ +	get_sys_info(&board_cfg); + +	mfsdr(SDR0_DDR0, sdr_ddrpll); +	sdram_freq = MULDIV64((board_cfg.freqPLB), SDR0_DDR0_DDRM_DECODE(sdr_ddrpll), 1); +	debug("sdram_freq=%lu\n", sdram_freq); + +	/*------------------------------------------------------------------ +	 * Handle the timing.  We need to find the worst case timing of all +	 * the dimm modules installed. +	 *-----------------------------------------------------------------*/ +	t_wr_ns = 0; +	cas_2_0_available = true; +	cas_2_5_available = true; +	cas_3_0_available = true; +	cas_4_0_available = true; +	cas_5_0_available = true; +	max_2_0_tcyc_ns_x_100 = 10; +	max_2_5_tcyc_ns_x_100 = 10; +	max_3_0_tcyc_ns_x_100 = 10; +	max_4_0_tcyc_ns_x_100 = 10; +	max_5_0_tcyc_ns_x_100 = 10; +	sdram_ddr1 = true; + +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_populated[dimm_num] != SDRAM_NONE) { +			if (dimm_populated[dimm_num] == SDRAM_DDR1) +				sdram_ddr1 = true; +			else +				sdram_ddr1 = false; + +			cas_bit = spd_read(iic0_dimm_addr[dimm_num], 18); +			debug("cas_bit[SPD byte 18]=%02lx\n", cas_bit); + +			/* For a particular DIMM, grab the three CAS values it supports */ +			for (cas_index = 0; cas_index < 3; cas_index++) { +				switch (cas_index) { +				case 0: +					tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 9); +					break; +				case 1: +					tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 23); +					break; +				default: +					tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 25); +					break; +				} + +				if ((tcyc_reg & 0x0F) >= 10) { +					if ((tcyc_reg & 0x0F) == 0x0D) { +						/* Convert from hex to decimal */ +						cycle_time_ns_x_100[cas_index] = +							(((tcyc_reg & 0xF0) >> 4) * 100) + 75; +					} else { +						printf("ERROR: SPD reported Tcyc is incorrect for DIMM " +						       "in slot %d\n", (unsigned int)dimm_num); +						spd_ddr_init_hang (); +					} +				} else { +					/* Convert from hex to decimal */ +					cycle_time_ns_x_100[cas_index] = +						(((tcyc_reg & 0xF0) >> 4) * 100) + +						((tcyc_reg & 0x0F)*10); +				} +				debug("cas_index=%lu: cycle_time_ns_x_100=%lu\n", cas_index, +				      cycle_time_ns_x_100[cas_index]); +			} + +			/* The rest of this routine determines if CAS 2.0, 2.5, 3.0, 4.0 and 5.0 are */ +			/* supported for a particular DIMM. */ +			cas_index = 0; + +			if (sdram_ddr1) { +				/* +				 * DDR devices use the following bitmask for CAS latency: +				 *  Bit   7    6    5    4    3    2    1    0 +				 *       TBD  4.0  3.5  3.0  2.5  2.0  1.5  1.0 +				 */ +				if (((cas_bit & 0x40) == 0x40) && (cas_index < 3) && +				    (cycle_time_ns_x_100[cas_index] != 0)) { +					max_4_0_tcyc_ns_x_100 = max(max_4_0_tcyc_ns_x_100, +								    cycle_time_ns_x_100[cas_index]); +					cas_index++; +				} else { +					if (cas_index != 0) +						cas_index++; +					cas_4_0_available = false; +				} + +				if (((cas_bit & 0x10) == 0x10) && (cas_index < 3) && +				    (cycle_time_ns_x_100[cas_index] != 0)) { +					max_3_0_tcyc_ns_x_100 = max(max_3_0_tcyc_ns_x_100, +								    cycle_time_ns_x_100[cas_index]); +					cas_index++; +				} else { +					if (cas_index != 0) +						cas_index++; +					cas_3_0_available = false; +				} + +				if (((cas_bit & 0x08) == 0x08) && (cas_index < 3) && +				    (cycle_time_ns_x_100[cas_index] != 0)) { +					max_2_5_tcyc_ns_x_100 = max(max_2_5_tcyc_ns_x_100, +								    cycle_time_ns_x_100[cas_index]); +					cas_index++; +				} else { +					if (cas_index != 0) +						cas_index++; +					cas_2_5_available = false; +				} + +				if (((cas_bit & 0x04) == 0x04) && (cas_index < 3) && +				    (cycle_time_ns_x_100[cas_index] != 0)) { +					max_2_0_tcyc_ns_x_100 = max(max_2_0_tcyc_ns_x_100, +								    cycle_time_ns_x_100[cas_index]); +					cas_index++; +				} else { +					if (cas_index != 0) +						cas_index++; +					cas_2_0_available = false; +				} +			} else { +				/* +				 * DDR2 devices use the following bitmask for CAS latency: +				 *  Bit   7    6    5    4    3    2    1    0 +				 *       TBD  6.0  5.0  4.0  3.0  2.0  TBD  TBD +				 */ +				if (((cas_bit & 0x20) == 0x20) && (cas_index < 3) && +				    (cycle_time_ns_x_100[cas_index] != 0)) { +					max_5_0_tcyc_ns_x_100 = max(max_5_0_tcyc_ns_x_100, +								    cycle_time_ns_x_100[cas_index]); +					cas_index++; +				} else { +					if (cas_index != 0) +						cas_index++; +					cas_5_0_available = false; +				} + +				if (((cas_bit & 0x10) == 0x10) && (cas_index < 3) && +				    (cycle_time_ns_x_100[cas_index] != 0)) { +					max_4_0_tcyc_ns_x_100 = max(max_4_0_tcyc_ns_x_100, +								    cycle_time_ns_x_100[cas_index]); +					cas_index++; +				} else { +					if (cas_index != 0) +						cas_index++; +					cas_4_0_available = false; +				} + +				if (((cas_bit & 0x08) == 0x08) && (cas_index < 3) && +				    (cycle_time_ns_x_100[cas_index] != 0)) { +					max_3_0_tcyc_ns_x_100 = max(max_3_0_tcyc_ns_x_100, +								    cycle_time_ns_x_100[cas_index]); +					cas_index++; +				} else { +					if (cas_index != 0) +						cas_index++; +					cas_3_0_available = false; +				} +			} +		} +	} + +	/*------------------------------------------------------------------ +	 * Set the SDRAM mode, SDRAM_MMODE +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_MMODE, mmode); +	mmode = mmode & ~(SDRAM_MMODE_WR_MASK | SDRAM_MMODE_DCL_MASK); + +	/* add 10 here because of rounding problems */ +	cycle_2_0_clk = MULDIV64(ONE_BILLION, 100, max_2_0_tcyc_ns_x_100) + 10; +	cycle_2_5_clk = MULDIV64(ONE_BILLION, 100, max_2_5_tcyc_ns_x_100) + 10; +	cycle_3_0_clk = MULDIV64(ONE_BILLION, 100, max_3_0_tcyc_ns_x_100) + 10; +	cycle_4_0_clk = MULDIV64(ONE_BILLION, 100, max_4_0_tcyc_ns_x_100) + 10; +	cycle_5_0_clk = MULDIV64(ONE_BILLION, 100, max_5_0_tcyc_ns_x_100) + 10; +	debug("cycle_3_0_clk=%lu\n", cycle_3_0_clk); +	debug("cycle_4_0_clk=%lu\n", cycle_4_0_clk); +	debug("cycle_5_0_clk=%lu\n", cycle_5_0_clk); + +	if (sdram_ddr1 == true) { /* DDR1 */ +		if ((cas_2_0_available == true) && +			(sdram_freq <= cycle_2_0_clk)) { +			mmode |= SDRAM_MMODE_DCL_DDR1_2_0_CLK; +			*selected_cas = DDR_CAS_2; +		} else if ((cas_2_5_available == true) && +			(sdram_freq <= cycle_2_5_clk)) { +			mmode |= SDRAM_MMODE_DCL_DDR1_2_5_CLK; +			*selected_cas = DDR_CAS_2_5; +		} else if ((cas_3_0_available == true) && +			(sdram_freq <= cycle_3_0_clk)) { +			mmode |= SDRAM_MMODE_DCL_DDR1_3_0_CLK; +			*selected_cas = DDR_CAS_3; +		} else { +			printf("ERROR: Cannot find a supported CAS latency with the installed DIMMs.\n"); +			printf("Only DIMMs DDR1 with CAS latencies of 2.0, 2.5, and 3.0 are supported.\n"); +			printf("Make sure the PLB speed is within the supported range of the DIMMs.\n\n"); +			spd_ddr_init_hang (); +		} +	} else { /* DDR2 */ +		debug("cas_3_0_available=%d\n", cas_3_0_available); +		debug("cas_4_0_available=%d\n", cas_4_0_available); +		debug("cas_5_0_available=%d\n", cas_5_0_available); +		if ((cas_3_0_available == true) && +			(sdram_freq <= cycle_3_0_clk)) { +			mmode |= SDRAM_MMODE_DCL_DDR2_3_0_CLK; +			*selected_cas = DDR_CAS_3; +		} else if ((cas_4_0_available == true) && +			(sdram_freq <= cycle_4_0_clk)) { +			mmode |= SDRAM_MMODE_DCL_DDR2_4_0_CLK; +			*selected_cas = DDR_CAS_4; +		} else if ((cas_5_0_available == true) && +			(sdram_freq <= cycle_5_0_clk)) { +			mmode |= SDRAM_MMODE_DCL_DDR2_5_0_CLK; +			*selected_cas = DDR_CAS_5; +		} else { +			printf("ERROR: Cannot find a supported CAS latency with the installed DIMMs.\n"); +			printf("Only DIMMs DDR2 with CAS latencies of 3.0, 4.0, and 5.0 are supported.\n"); +			printf("Make sure the PLB speed is within the supported range of the DIMMs.\n"); +			printf("cas3=%d cas4=%d cas5=%d\n", +			       cas_3_0_available, cas_4_0_available, cas_5_0_available); +			printf("sdram_freq=%lu cycle3=%lu cycle4=%lu cycle5=%lu\n\n", +			       sdram_freq, cycle_3_0_clk, cycle_4_0_clk, cycle_5_0_clk); +			spd_ddr_init_hang (); +		} +	} + +	if (sdram_ddr1 == true) +		mmode |= SDRAM_MMODE_WR_DDR1; +	else { + +		/* loop through all the DIMM slots on the board */ +		for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +			/* If a dimm is installed in a particular slot ... */ +			if (dimm_populated[dimm_num] != SDRAM_NONE) +				t_wr_ns = max(t_wr_ns, +					      spd_read(iic0_dimm_addr[dimm_num], 36) >> 2); +		} + +		/* +		 * convert from nanoseconds to ddr clocks +		 * round up if necessary +		 */ +		t_wr_clk = MULDIV64(sdram_freq, t_wr_ns, ONE_BILLION); +		ddr_check = MULDIV64(ONE_BILLION, t_wr_clk, t_wr_ns); +		if (sdram_freq != ddr_check) +			t_wr_clk++; + +		switch (t_wr_clk) { +		case 0: +		case 1: +		case 2: +		case 3: +			mmode |= SDRAM_MMODE_WR_DDR2_3_CYC; +			break; +		case 4: +			mmode |= SDRAM_MMODE_WR_DDR2_4_CYC; +			break; +		case 5: +			mmode |= SDRAM_MMODE_WR_DDR2_5_CYC; +			break; +		default: +			mmode |= SDRAM_MMODE_WR_DDR2_6_CYC; +			break; +		} +		*write_recovery = t_wr_clk; +	} + +	debug("CAS latency = %d\n", *selected_cas); +	debug("Write recovery = %d\n", *write_recovery); + +	mtsdram(SDRAM_MMODE, mmode); +} + +/*-----------------------------------------------------------------------------+ + * program_rtr. + *-----------------------------------------------------------------------------*/ +static void program_rtr(unsigned long *dimm_populated, +			unsigned char *iic0_dimm_addr, +			unsigned long num_dimm_banks) +{ +	PPC4xx_SYS_INFO board_cfg; +	unsigned long max_refresh_rate; +	unsigned long dimm_num; +	unsigned long refresh_rate_type; +	unsigned long refresh_rate; +	unsigned long rint; +	unsigned long sdram_freq; +	unsigned long sdr_ddrpll; +	unsigned long val; + +	/*------------------------------------------------------------------ +	 * Get the board configuration info. +	 *-----------------------------------------------------------------*/ +	get_sys_info(&board_cfg); + +	/*------------------------------------------------------------------ +	 * Set the SDRAM Refresh Timing Register, SDRAM_RTR +	 *-----------------------------------------------------------------*/ +	mfsdr(SDR0_DDR0, sdr_ddrpll); +	sdram_freq = ((board_cfg.freqPLB) * SDR0_DDR0_DDRM_DECODE(sdr_ddrpll)); + +	max_refresh_rate = 0; +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] != SDRAM_NONE) { + +			refresh_rate_type = spd_read(iic0_dimm_addr[dimm_num], 12); +			refresh_rate_type &= 0x7F; +			switch (refresh_rate_type) { +			case 0: +				refresh_rate =  15625; +				break; +			case 1: +				refresh_rate =   3906; +				break; +			case 2: +				refresh_rate =   7812; +				break; +			case 3: +				refresh_rate =  31250; +				break; +			case 4: +				refresh_rate =  62500; +				break; +			case 5: +				refresh_rate = 125000; +				break; +			default: +				refresh_rate = 0; +				printf("ERROR: DIMM %d unsupported refresh rate/type.\n", +				       (unsigned int)dimm_num); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +				break; +			} + +			max_refresh_rate = max(max_refresh_rate, refresh_rate); +		} +	} + +	rint = MULDIV64(sdram_freq, max_refresh_rate, ONE_BILLION); +	mfsdram(SDRAM_RTR, val); +	mtsdram(SDRAM_RTR, (val & ~SDRAM_RTR_RINT_MASK) | +		(SDRAM_RTR_RINT_ENCODE(rint))); +} + +/*------------------------------------------------------------------ + * This routine programs the SDRAM_TRx registers. + *-----------------------------------------------------------------*/ +static void program_tr(unsigned long *dimm_populated, +		       unsigned char *iic0_dimm_addr, +		       unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long sdram_ddr1; +	unsigned long t_rp_ns; +	unsigned long t_rcd_ns; +	unsigned long t_rrd_ns; +	unsigned long t_ras_ns; +	unsigned long t_rc_ns; +	unsigned long t_rfc_ns; +	unsigned long t_wpc_ns; +	unsigned long t_wtr_ns; +	unsigned long t_rpc_ns; +	unsigned long t_rp_clk; +	unsigned long t_rcd_clk; +	unsigned long t_rrd_clk; +	unsigned long t_ras_clk; +	unsigned long t_rc_clk; +	unsigned long t_rfc_clk; +	unsigned long t_wpc_clk; +	unsigned long t_wtr_clk; +	unsigned long t_rpc_clk; +	unsigned long sdtr1, sdtr2, sdtr3; +	unsigned long ddr_check; +	unsigned long sdram_freq; +	unsigned long sdr_ddrpll; + +	PPC4xx_SYS_INFO board_cfg; + +	/*------------------------------------------------------------------ +	 * Get the board configuration info. +	 *-----------------------------------------------------------------*/ +	get_sys_info(&board_cfg); + +	mfsdr(SDR0_DDR0, sdr_ddrpll); +	sdram_freq = ((board_cfg.freqPLB) * SDR0_DDR0_DDRM_DECODE(sdr_ddrpll)); + +	/*------------------------------------------------------------------ +	 * Handle the timing.  We need to find the worst case timing of all +	 * the dimm modules installed. +	 *-----------------------------------------------------------------*/ +	t_rp_ns = 0; +	t_rrd_ns = 0; +	t_rcd_ns = 0; +	t_ras_ns = 0; +	t_rc_ns = 0; +	t_rfc_ns = 0; +	t_wpc_ns = 0; +	t_wtr_ns = 0; +	t_rpc_ns = 0; +	sdram_ddr1 = true; + +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_populated[dimm_num] != SDRAM_NONE) { +			if (dimm_populated[dimm_num] == SDRAM_DDR2) +				sdram_ddr1 = true; +			else +				sdram_ddr1 = false; + +			t_rcd_ns = max(t_rcd_ns, spd_read(iic0_dimm_addr[dimm_num], 29) >> 2); +			t_rrd_ns = max(t_rrd_ns, spd_read(iic0_dimm_addr[dimm_num], 28) >> 2); +			t_rp_ns  = max(t_rp_ns,  spd_read(iic0_dimm_addr[dimm_num], 27) >> 2); +			t_ras_ns = max(t_ras_ns, spd_read(iic0_dimm_addr[dimm_num], 30)); +			t_rc_ns  = max(t_rc_ns,  spd_read(iic0_dimm_addr[dimm_num], 41)); +			t_rfc_ns = max(t_rfc_ns, spd_read(iic0_dimm_addr[dimm_num], 42)); +		} +	} + +	/*------------------------------------------------------------------ +	 * Set the SDRAM Timing Reg 1, SDRAM_TR1 +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_SDTR1, sdtr1); +	sdtr1 &= ~(SDRAM_SDTR1_LDOF_MASK | SDRAM_SDTR1_RTW_MASK | +		   SDRAM_SDTR1_WTWO_MASK | SDRAM_SDTR1_RTRO_MASK); + +	/* default values */ +	sdtr1 |= SDRAM_SDTR1_LDOF_2_CLK; +	sdtr1 |= SDRAM_SDTR1_RTW_2_CLK; + +	/* normal operations */ +	sdtr1 |= SDRAM_SDTR1_WTWO_0_CLK; +	sdtr1 |= SDRAM_SDTR1_RTRO_1_CLK; + +	mtsdram(SDRAM_SDTR1, sdtr1); + +	/*------------------------------------------------------------------ +	 * Set the SDRAM Timing Reg 2, SDRAM_TR2 +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_SDTR2, sdtr2); +	sdtr2 &= ~(SDRAM_SDTR2_RCD_MASK  | SDRAM_SDTR2_WTR_MASK | +		   SDRAM_SDTR2_XSNR_MASK | SDRAM_SDTR2_WPC_MASK | +		   SDRAM_SDTR2_RPC_MASK  | SDRAM_SDTR2_RP_MASK  | +		   SDRAM_SDTR2_RRD_MASK); + +	/* +	 * convert t_rcd from nanoseconds to ddr clocks +	 * round up if necessary +	 */ +	t_rcd_clk = MULDIV64(sdram_freq, t_rcd_ns, ONE_BILLION); +	ddr_check = MULDIV64(ONE_BILLION, t_rcd_clk, t_rcd_ns); +	if (sdram_freq != ddr_check) +		t_rcd_clk++; + +	switch (t_rcd_clk) { +	case 0: +	case 1: +		sdtr2 |= SDRAM_SDTR2_RCD_1_CLK; +		break; +	case 2: +		sdtr2 |= SDRAM_SDTR2_RCD_2_CLK; +		break; +	case 3: +		sdtr2 |= SDRAM_SDTR2_RCD_3_CLK; +		break; +	case 4: +		sdtr2 |= SDRAM_SDTR2_RCD_4_CLK; +		break; +	default: +		sdtr2 |= SDRAM_SDTR2_RCD_5_CLK; +		break; +	} + +	if (sdram_ddr1 == true) { /* DDR1 */ +		if (sdram_freq < 200000000) { +			sdtr2 |= SDRAM_SDTR2_WTR_1_CLK; +			sdtr2 |= SDRAM_SDTR2_WPC_2_CLK; +			sdtr2 |= SDRAM_SDTR2_RPC_2_CLK; +		} else { +			sdtr2 |= SDRAM_SDTR2_WTR_2_CLK; +			sdtr2 |= SDRAM_SDTR2_WPC_3_CLK; +			sdtr2 |= SDRAM_SDTR2_RPC_2_CLK; +		} +	} else { /* DDR2 */ +		/* loop through all the DIMM slots on the board */ +		for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +			/* If a dimm is installed in a particular slot ... */ +			if (dimm_populated[dimm_num] != SDRAM_NONE) { +				t_wpc_ns = max(t_wtr_ns, spd_read(iic0_dimm_addr[dimm_num], 36) >> 2); +				t_wtr_ns = max(t_wtr_ns, spd_read(iic0_dimm_addr[dimm_num], 37) >> 2); +				t_rpc_ns = max(t_rpc_ns, spd_read(iic0_dimm_addr[dimm_num], 38) >> 2); +			} +		} + +		/* +		 * convert from nanoseconds to ddr clocks +		 * round up if necessary +		 */ +		t_wpc_clk = MULDIV64(sdram_freq, t_wpc_ns, ONE_BILLION); +		ddr_check = MULDIV64(ONE_BILLION, t_wpc_clk, t_wpc_ns); +		if (sdram_freq != ddr_check) +			t_wpc_clk++; + +		switch (t_wpc_clk) { +		case 0: +		case 1: +		case 2: +			sdtr2 |= SDRAM_SDTR2_WPC_2_CLK; +			break; +		case 3: +			sdtr2 |= SDRAM_SDTR2_WPC_3_CLK; +			break; +		case 4: +			sdtr2 |= SDRAM_SDTR2_WPC_4_CLK; +			break; +		case 5: +			sdtr2 |= SDRAM_SDTR2_WPC_5_CLK; +			break; +		default: +			sdtr2 |= SDRAM_SDTR2_WPC_6_CLK; +			break; +		} + +		/* +		 * convert from nanoseconds to ddr clocks +		 * round up if necessary +		 */ +		t_wtr_clk = MULDIV64(sdram_freq, t_wtr_ns, ONE_BILLION); +		ddr_check = MULDIV64(ONE_BILLION, t_wtr_clk, t_wtr_ns); +		if (sdram_freq != ddr_check) +			t_wtr_clk++; + +		switch (t_wtr_clk) { +		case 0: +		case 1: +			sdtr2 |= SDRAM_SDTR2_WTR_1_CLK; +			break; +		case 2: +			sdtr2 |= SDRAM_SDTR2_WTR_2_CLK; +			break; +		case 3: +			sdtr2 |= SDRAM_SDTR2_WTR_3_CLK; +			break; +		default: +			sdtr2 |= SDRAM_SDTR2_WTR_4_CLK; +			break; +		} + +		/* +		 * convert from nanoseconds to ddr clocks +		 * round up if necessary +		 */ +		t_rpc_clk = MULDIV64(sdram_freq, t_rpc_ns, ONE_BILLION); +		ddr_check = MULDIV64(ONE_BILLION, t_rpc_clk, t_rpc_ns); +		if (sdram_freq != ddr_check) +			t_rpc_clk++; + +		switch (t_rpc_clk) { +		case 0: +		case 1: +		case 2: +			sdtr2 |= SDRAM_SDTR2_RPC_2_CLK; +			break; +		case 3: +			sdtr2 |= SDRAM_SDTR2_RPC_3_CLK; +			break; +		default: +			sdtr2 |= SDRAM_SDTR2_RPC_4_CLK; +			break; +		} +	} + +	/* default value */ +	sdtr2 |= SDRAM_SDTR2_XSNR_16_CLK; + +	/* +	 * convert t_rrd from nanoseconds to ddr clocks +	 * round up if necessary +	 */ +	t_rrd_clk = MULDIV64(sdram_freq, t_rrd_ns, ONE_BILLION); +	ddr_check = MULDIV64(ONE_BILLION, t_rrd_clk, t_rrd_ns); +	if (sdram_freq != ddr_check) +		t_rrd_clk++; + +	if (t_rrd_clk == 3) +		sdtr2 |= SDRAM_SDTR2_RRD_3_CLK; +	else +		sdtr2 |= SDRAM_SDTR2_RRD_2_CLK; + +	/* +	 * convert t_rp from nanoseconds to ddr clocks +	 * round up if necessary +	 */ +	t_rp_clk = MULDIV64(sdram_freq, t_rp_ns, ONE_BILLION); +	ddr_check = MULDIV64(ONE_BILLION, t_rp_clk, t_rp_ns); +	if (sdram_freq != ddr_check) +		t_rp_clk++; + +	switch (t_rp_clk) { +	case 0: +	case 1: +	case 2: +	case 3: +		sdtr2 |= SDRAM_SDTR2_RP_3_CLK; +		break; +	case 4: +		sdtr2 |= SDRAM_SDTR2_RP_4_CLK; +		break; +	case 5: +		sdtr2 |= SDRAM_SDTR2_RP_5_CLK; +		break; +	case 6: +		sdtr2 |= SDRAM_SDTR2_RP_6_CLK; +		break; +	default: +		sdtr2 |= SDRAM_SDTR2_RP_7_CLK; +		break; +	} + +	mtsdram(SDRAM_SDTR2, sdtr2); + +	/*------------------------------------------------------------------ +	 * Set the SDRAM Timing Reg 3, SDRAM_TR3 +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_SDTR3, sdtr3); +	sdtr3 &= ~(SDRAM_SDTR3_RAS_MASK  | SDRAM_SDTR3_RC_MASK | +		   SDRAM_SDTR3_XCS_MASK | SDRAM_SDTR3_RFC_MASK); + +	/* +	 * convert t_ras from nanoseconds to ddr clocks +	 * round up if necessary +	 */ +	t_ras_clk = MULDIV64(sdram_freq, t_ras_ns, ONE_BILLION); +	ddr_check = MULDIV64(ONE_BILLION, t_ras_clk, t_ras_ns); +	if (sdram_freq != ddr_check) +		t_ras_clk++; + +	sdtr3 |= SDRAM_SDTR3_RAS_ENCODE(t_ras_clk); + +	/* +	 * convert t_rc from nanoseconds to ddr clocks +	 * round up if necessary +	 */ +	t_rc_clk = MULDIV64(sdram_freq, t_rc_ns, ONE_BILLION); +	ddr_check = MULDIV64(ONE_BILLION, t_rc_clk, t_rc_ns); +	if (sdram_freq != ddr_check) +		t_rc_clk++; + +	sdtr3 |= SDRAM_SDTR3_RC_ENCODE(t_rc_clk); + +	/* default xcs value */ +	sdtr3 |= SDRAM_SDTR3_XCS; + +	/* +	 * convert t_rfc from nanoseconds to ddr clocks +	 * round up if necessary +	 */ +	t_rfc_clk = MULDIV64(sdram_freq, t_rfc_ns, ONE_BILLION); +	ddr_check = MULDIV64(ONE_BILLION, t_rfc_clk, t_rfc_ns); +	if (sdram_freq != ddr_check) +		t_rfc_clk++; + +	sdtr3 |= SDRAM_SDTR3_RFC_ENCODE(t_rfc_clk); + +	mtsdram(SDRAM_SDTR3, sdtr3); +} + +/*-----------------------------------------------------------------------------+ + * program_bxcf. + *-----------------------------------------------------------------------------*/ +static void program_bxcf(unsigned long *dimm_populated, +			 unsigned char *iic0_dimm_addr, +			 unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long num_col_addr; +	unsigned long num_ranks; +	unsigned long num_banks; +	unsigned long mode; +	unsigned long ind_rank; +	unsigned long ind; +	unsigned long ind_bank; +	unsigned long bank_0_populated; + +	/*------------------------------------------------------------------ +	 * Set the BxCF regs.  First, wipe out the bank config registers. +	 *-----------------------------------------------------------------*/ +	mtsdram(SDRAM_MB0CF, 0x00000000); +	mtsdram(SDRAM_MB1CF, 0x00000000); +	mtsdram(SDRAM_MB2CF, 0x00000000); +	mtsdram(SDRAM_MB3CF, 0x00000000); + +	mode = SDRAM_BXCF_M_BE_ENABLE; + +	bank_0_populated = 0; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] != SDRAM_NONE) { +			num_col_addr = spd_read(iic0_dimm_addr[dimm_num], 4); +			num_ranks = spd_read(iic0_dimm_addr[dimm_num], 5); +			if ((spd_read(iic0_dimm_addr[dimm_num], 2)) == 0x08) +				num_ranks = (num_ranks & 0x0F) +1; +			else +				num_ranks = num_ranks & 0x0F; + +			num_banks = spd_read(iic0_dimm_addr[dimm_num], 17); + +			for (ind_bank = 0; ind_bank < 2; ind_bank++) { +				if (num_banks == 4) +					ind = 0; +				else +					ind = 5 << 8; +				switch (num_col_addr) { +				case 0x08: +					mode |= (SDRAM_BXCF_M_AM_0 + ind); +					break; +				case 0x09: +					mode |= (SDRAM_BXCF_M_AM_1 + ind); +					break; +				case 0x0A: +					mode |= (SDRAM_BXCF_M_AM_2 + ind); +					break; +				case 0x0B: +					mode |= (SDRAM_BXCF_M_AM_3 + ind); +					break; +				case 0x0C: +					mode |= (SDRAM_BXCF_M_AM_4 + ind); +					break; +				default: +					printf("DDR-SDRAM: DIMM %d BxCF configuration.\n", +					       (unsigned int)dimm_num); +					printf("ERROR: Unsupported value for number of " +					       "column addresses: %d.\n", (unsigned int)num_col_addr); +					printf("Replace the DIMM module with a supported DIMM.\n\n"); +					spd_ddr_init_hang (); +				} +			} + +			if ((dimm_populated[dimm_num] != SDRAM_NONE)&& (dimm_num ==1)) +				bank_0_populated = 1; + +			for (ind_rank = 0; ind_rank < num_ranks; ind_rank++) { +				mtsdram(SDRAM_MB0CF + +					((dimm_num + bank_0_populated + ind_rank) << 2), +					mode); +			} +		} +	} +} + +/*------------------------------------------------------------------ + * program memory queue. + *-----------------------------------------------------------------*/ +static void program_memory_queue(unsigned long *dimm_populated, +				 unsigned char *iic0_dimm_addr, +				 unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	phys_size_t rank_base_addr; +	unsigned long rank_reg; +	phys_size_t rank_size_bytes; +	unsigned long rank_size_id; +	unsigned long num_ranks; +	unsigned long baseadd_size; +	unsigned long i; +	unsigned long bank_0_populated = 0; +	phys_size_t total_size = 0; + +	/*------------------------------------------------------------------ +	 * Reset the rank_base_address. +	 *-----------------------------------------------------------------*/ +	rank_reg   = SDRAM_R0BAS; + +	rank_base_addr = 0x00000000; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_populated[dimm_num] != SDRAM_NONE) { +			num_ranks = spd_read(iic0_dimm_addr[dimm_num], 5); +			if ((spd_read(iic0_dimm_addr[dimm_num], 2)) == 0x08) +				num_ranks = (num_ranks & 0x0F) + 1; +			else +				num_ranks = num_ranks & 0x0F; + +			rank_size_id = spd_read(iic0_dimm_addr[dimm_num], 31); + +			/*------------------------------------------------------------------ +			 * Set the sizes +			 *-----------------------------------------------------------------*/ +			baseadd_size = 0; +			switch (rank_size_id) { +			case 0x01: +				baseadd_size |= SDRAM_RXBAS_SDSZ_1024; +				total_size = 1024; +				break; +			case 0x02: +				baseadd_size |= SDRAM_RXBAS_SDSZ_2048; +				total_size = 2048; +				break; +			case 0x04: +				baseadd_size |= SDRAM_RXBAS_SDSZ_4096; +				total_size = 4096; +				break; +			case 0x08: +				baseadd_size |= SDRAM_RXBAS_SDSZ_32; +				total_size = 32; +				break; +			case 0x10: +				baseadd_size |= SDRAM_RXBAS_SDSZ_64; +				total_size = 64; +				break; +			case 0x20: +				baseadd_size |= SDRAM_RXBAS_SDSZ_128; +				total_size = 128; +				break; +			case 0x40: +				baseadd_size |= SDRAM_RXBAS_SDSZ_256; +				total_size = 256; +				break; +			case 0x80: +				baseadd_size |= SDRAM_RXBAS_SDSZ_512; +				total_size = 512; +				break; +			default: +				printf("DDR-SDRAM: DIMM %d memory queue configuration.\n", +				       (unsigned int)dimm_num); +				printf("ERROR: Unsupported value for the banksize: %d.\n", +				       (unsigned int)rank_size_id); +				printf("Replace the DIMM module with a supported DIMM.\n\n"); +				spd_ddr_init_hang (); +			} +			rank_size_bytes = total_size << 20; + +			if ((dimm_populated[dimm_num] != SDRAM_NONE) && (dimm_num == 1)) +				bank_0_populated = 1; + +			for (i = 0; i < num_ranks; i++)	{ +				mtdcr_any(rank_reg+i+dimm_num+bank_0_populated, +					  (SDRAM_RXBAS_SDBA_ENCODE(rank_base_addr) | +					   baseadd_size)); +				rank_base_addr += rank_size_bytes; +			} +		} +	} + +#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ +    defined(CONFIG_460SX) +	/* +	 * Enable high bandwidth access +	 * This is currently not used, but with this setup +	 * it is possible to use it later on in e.g. the Linux +	 * EMAC driver for performance gain. +	 */ +	mtdcr(SDRAM_PLBADDULL, 0x00000000); /* MQ0_BAUL */ +	mtdcr(SDRAM_PLBADDUHB, 0x00000008); /* MQ0_BAUH */ + +	/* +	 * Set optimal value for Memory Queue HB/LL Configuration registers +	 */ +	mtdcr(SDRAM_CONF1HB, (mfdcr(SDRAM_CONF1HB) & ~SDRAM_CONF1HB_MASK) | +	      SDRAM_CONF1HB_AAFR | SDRAM_CONF1HB_RPEN | SDRAM_CONF1HB_RFTE | +	      SDRAM_CONF1HB_RPLM | SDRAM_CONF1HB_WRCL); +	mtdcr(SDRAM_CONF1LL, (mfdcr(SDRAM_CONF1LL) & ~SDRAM_CONF1LL_MASK) | +	      SDRAM_CONF1LL_AAFR | SDRAM_CONF1LL_RPEN | SDRAM_CONF1LL_RFTE | +	      SDRAM_CONF1LL_RPLM); +	mtdcr(SDRAM_CONFPATHB, mfdcr(SDRAM_CONFPATHB) | SDRAM_CONFPATHB_TPEN); +#endif +} + +#ifdef CONFIG_DDR_ECC +/*-----------------------------------------------------------------------------+ + * program_ecc. + *-----------------------------------------------------------------------------*/ +static void program_ecc(unsigned long *dimm_populated, +			unsigned char *iic0_dimm_addr, +			unsigned long num_dimm_banks, +			unsigned long tlb_word2_i_value) +{ +	unsigned long dimm_num; +	unsigned long ecc; + +	ecc = 0; +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < MAXDIMMS; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_populated[dimm_num] != SDRAM_NONE) +			ecc = max(ecc, spd_read(iic0_dimm_addr[dimm_num], 11)); +	} +	if (ecc == 0) +		return; + +	do_program_ecc(tlb_word2_i_value); +} +#endif + +#if !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +/*-----------------------------------------------------------------------------+ + * program_DQS_calibration. + *-----------------------------------------------------------------------------*/ +static void program_DQS_calibration(unsigned long *dimm_populated, +				    unsigned char *iic0_dimm_addr, +				    unsigned long num_dimm_banks) +{ +	unsigned long val; + +#ifdef HARD_CODED_DQS /* calibration test with hardvalues */ +	mtsdram(SDRAM_RQDC, 0x80000037); +	mtsdram(SDRAM_RDCC, 0x40000000); +	mtsdram(SDRAM_RFDC, 0x000001DF); + +	test(); +#else +	/*------------------------------------------------------------------ +	 * Program RDCC register +	 * Read sample cycle auto-update enable +	 *-----------------------------------------------------------------*/ + +	mfsdram(SDRAM_RDCC, val); +	mtsdram(SDRAM_RDCC, +		(val & ~(SDRAM_RDCC_RDSS_MASK | SDRAM_RDCC_RSAE_MASK)) +		| SDRAM_RDCC_RSAE_ENABLE); + +	/*------------------------------------------------------------------ +	 * Program RQDC register +	 * Internal DQS delay mechanism enable +	 *-----------------------------------------------------------------*/ +	mtsdram(SDRAM_RQDC, (SDRAM_RQDC_RQDE_ENABLE|SDRAM_RQDC_RQFD_ENCODE(0x38))); + +	/*------------------------------------------------------------------ +	 * Program RFDC register +	 * Set Feedback Fractional Oversample +	 * Auto-detect read sample cycle enable +	 * Set RFOS to 1/4 of memclk cycle (0x3f) +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_RFDC, val); +	mtsdram(SDRAM_RFDC, +		(val & ~(SDRAM_RFDC_ARSE_MASK | SDRAM_RFDC_RFOS_MASK | +			 SDRAM_RFDC_RFFD_MASK)) +		| (SDRAM_RFDC_ARSE_ENABLE | SDRAM_RFDC_RFOS_ENCODE(0x3f) | +		   SDRAM_RFDC_RFFD_ENCODE(0))); + +	DQS_calibration_process(); +#endif +} + +static int short_mem_test(void) +{ +	u32 *membase; +	u32 bxcr_num; +	u32 bxcf; +	int i; +	int j; +	phys_size_t base_addr; +	u32 test[NUMMEMTESTS][NUMMEMWORDS] = { +		{0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}, +		{0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000}, +		{0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555}, +		{0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA}, +		{0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A}, +		{0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5}, +		{0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA}, +		{0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, +		 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55} }; +	int l; + +	for (bxcr_num = 0; bxcr_num < MAXBXCF; bxcr_num++) { +		mfsdram(SDRAM_MB0CF + (bxcr_num << 2), bxcf); + +		/* Banks enabled */ +		if ((bxcf & SDRAM_BXCF_M_BE_MASK) == SDRAM_BXCF_M_BE_ENABLE) { +			/* Bank is enabled */ + +			/* +			 * Only run test on accessable memory (below 2GB) +			 */ +			base_addr = SDRAM_RXBAS_SDBA_DECODE(mfdcr_any(SDRAM_R0BAS+bxcr_num)); +			if (base_addr >= CONFIG_MAX_MEM_MAPPED) +				continue; + +			/*------------------------------------------------------------------ +			 * Run the short memory test. +			 *-----------------------------------------------------------------*/ +			membase = (u32 *)(u32)base_addr; + +			for (i = 0; i < NUMMEMTESTS; i++) { +				for (j = 0; j < NUMMEMWORDS; j++) { +					membase[j] = test[i][j]; +					ppcDcbf((u32)&(membase[j])); +				} +				sync(); +				for (l=0; l<NUMLOOPS; l++) { +					for (j = 0; j < NUMMEMWORDS; j++) { +						if (membase[j] != test[i][j]) { +							ppcDcbf((u32)&(membase[j])); +							return 0; +						} +						ppcDcbf((u32)&(membase[j])); +					} +					sync(); +				} +			} +		}	/* if bank enabled */ +	}		/* for bxcf_num */ + +	return 1; +} + +#ifndef HARD_CODED_DQS +/*-----------------------------------------------------------------------------+ + * DQS_calibration_process. + *-----------------------------------------------------------------------------*/ +static void DQS_calibration_process(void) +{ +	unsigned long rfdc_reg; +	unsigned long rffd; +	unsigned long val; +	long rffd_average; +	long max_start; +	unsigned long dlycal; +	unsigned long dly_val; +	unsigned long max_pass_length; +	unsigned long current_pass_length; +	unsigned long current_fail_length; +	unsigned long current_start; +	long max_end; +	unsigned char fail_found; +	unsigned char pass_found; +#if !defined(CONFIG_DDR_RQDC_FIXED) +	int window_found; +	u32 rqdc_reg; +	u32 rqfd; +	u32 rqfd_start; +	u32 rqfd_average; +	int loopi = 0; +	char str[] = "Auto calibration -"; +	char slash[] = "\\|/-\\|/-"; + +	/*------------------------------------------------------------------ +	 * Test to determine the best read clock delay tuning bits. +	 * +	 * Before the DDR controller can be used, the read clock delay needs to be +	 * set.  This is SDRAM_RQDC[RQFD] and SDRAM_RFDC[RFFD]. +	 * This value cannot be hardcoded into the program because it changes +	 * depending on the board's setup and environment. +	 * To do this, all delay values are tested to see if they +	 * work or not.  By doing this, you get groups of fails with groups of +	 * passing values.  The idea is to find the start and end of a passing +	 * window and take the center of it to use as the read clock delay. +	 * +	 * A failure has to be seen first so that when we hit a pass, we know +	 * that it is truely the start of the window.  If we get passing values +	 * to start off with, we don't know if we are at the start of the window. +	 * +	 * The code assumes that a failure will always be found. +	 * If a failure is not found, there is no easy way to get the middle +	 * of the passing window.  I guess we can pretty much pick any value +	 * but some values will be better than others.  Since the lowest speed +	 * we can clock the DDR interface at is 200 MHz (2x 100 MHz PLB speed), +	 * from experimentation it is safe to say you will always have a failure. +	 *-----------------------------------------------------------------*/ + +	/* first fix RQDC[RQFD] to an average of 80 degre phase shift to find RFDC[RFFD] */ +	rqfd_start = 64; /* test-only: don't know if this is the _best_ start value */ + +	puts(str); + +calibration_loop: +	mfsdram(SDRAM_RQDC, rqdc_reg); +	mtsdram(SDRAM_RQDC, (rqdc_reg & ~SDRAM_RQDC_RQFD_MASK) | +		SDRAM_RQDC_RQFD_ENCODE(rqfd_start)); +#else /* CONFIG_DDR_RQDC_FIXED */ +	/* +	 * On Katmai the complete auto-calibration somehow doesn't seem to +	 * produce the best results, meaning optimal values for RQFD/RFFD. +	 * This was discovered by GDA using a high bandwidth scope, +	 * analyzing the DDR2 signals. GDA provided a fixed value for RQFD, +	 * so now on Katmai "only" RFFD is auto-calibrated. +	 */ +	mtsdram(SDRAM_RQDC, CONFIG_DDR_RQDC_FIXED); +#endif /* CONFIG_DDR_RQDC_FIXED */ + +	max_start = 0; + +	max_pass_length = 0; +	max_start = 0; +	max_end = 0; +	current_pass_length = 0; +	current_fail_length = 0; +	current_start = 0; +	fail_found = false; +	pass_found = false; + +	/* +	 * get the delay line calibration register value +	 */ +	mfsdram(SDRAM_DLCR, dlycal); +	dly_val = SDRAM_DLYCAL_DLCV_DECODE(dlycal) << 2; + +	for (rffd = 0; rffd <= SDRAM_RFDC_RFFD_MAX; rffd++) { +		mfsdram(SDRAM_RFDC, rfdc_reg); +		rfdc_reg &= ~(SDRAM_RFDC_RFFD_MASK); + +		/*------------------------------------------------------------------ +		 * Set the timing reg for the test. +		 *-----------------------------------------------------------------*/ +		mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd)); + +		/*------------------------------------------------------------------ +		 * See if the rffd value passed. +		 *-----------------------------------------------------------------*/ +		if (short_mem_test()) { +			if (fail_found == true) { +				pass_found = true; +				if (current_pass_length == 0) +					current_start = rffd; + +				current_fail_length = 0; +				current_pass_length++; + +				if (current_pass_length > max_pass_length) { +					max_pass_length = current_pass_length; +					max_start = current_start; +					max_end = rffd; +				} +			} +		} else { +			current_pass_length = 0; +			current_fail_length++; + +			if (current_fail_length >= (dly_val >> 2)) { +				if (fail_found == false) +					fail_found = true; +				else if (pass_found == true) +					break; +			} +		} +	}		/* for rffd */ + +	/*------------------------------------------------------------------ +	 * Set the average RFFD value +	 *-----------------------------------------------------------------*/ +	rffd_average = ((max_start + max_end) >> 1); + +	if (rffd_average < 0) +		rffd_average = 0; + +	if (rffd_average > SDRAM_RFDC_RFFD_MAX) +		rffd_average = SDRAM_RFDC_RFFD_MAX; +	/* now fix RFDC[RFFD] found and find RQDC[RQFD] */ +	mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd_average)); + +#if !defined(CONFIG_DDR_RQDC_FIXED) +	max_pass_length = 0; +	max_start = 0; +	max_end = 0; +	current_pass_length = 0; +	current_fail_length = 0; +	current_start = 0; +	window_found = false; +	fail_found = false; +	pass_found = false; + +	for (rqfd = 0; rqfd <= SDRAM_RQDC_RQFD_MAX; rqfd++) { +		mfsdram(SDRAM_RQDC, rqdc_reg); +		rqdc_reg &= ~(SDRAM_RQDC_RQFD_MASK); + +		/*------------------------------------------------------------------ +		 * Set the timing reg for the test. +		 *-----------------------------------------------------------------*/ +		mtsdram(SDRAM_RQDC, rqdc_reg | SDRAM_RQDC_RQFD_ENCODE(rqfd)); + +		/*------------------------------------------------------------------ +		 * See if the rffd value passed. +		 *-----------------------------------------------------------------*/ +		if (short_mem_test()) { +			if (fail_found == true) { +				pass_found = true; +				if (current_pass_length == 0) +					current_start = rqfd; + +				current_fail_length = 0; +				current_pass_length++; + +				if (current_pass_length > max_pass_length) { +					max_pass_length = current_pass_length; +					max_start = current_start; +					max_end = rqfd; +				} +			} +		} else { +			current_pass_length = 0; +			current_fail_length++; + +			if (fail_found == false) { +				fail_found = true; +			} else if (pass_found == true) { +				window_found = true; +				break; +			} +		} +	} + +	rqfd_average = ((max_start + max_end) >> 1); + +	/*------------------------------------------------------------------ +	 * Make sure we found the valid read passing window.  Halt if not +	 *-----------------------------------------------------------------*/ +	if (window_found == false) { +		if (rqfd_start < SDRAM_RQDC_RQFD_MAX) { +			putc('\b'); +			putc(slash[loopi++ % 8]); + +			/* try again from with a different RQFD start value */ +			rqfd_start++; +			goto calibration_loop; +		} + +		printf("\nERROR: Cannot determine a common read delay for the " +		       "DIMM(s) installed.\n"); +		debug("%s[%d] ERROR : \n", __FUNCTION__,__LINE__); +		ppc4xx_ibm_ddr2_register_dump(); +		spd_ddr_init_hang (); +	} + +	if (rqfd_average < 0) +		rqfd_average = 0; + +	if (rqfd_average > SDRAM_RQDC_RQFD_MAX) +		rqfd_average = SDRAM_RQDC_RQFD_MAX; + +	mtsdram(SDRAM_RQDC, +		(rqdc_reg & ~SDRAM_RQDC_RQFD_MASK) | +		SDRAM_RQDC_RQFD_ENCODE(rqfd_average)); + +	blank_string(strlen(str)); +#endif /* CONFIG_DDR_RQDC_FIXED */ + +	mfsdram(SDRAM_DLCR, val); +	debug("%s[%d] DLCR: 0x%08lX\n", __FUNCTION__, __LINE__, val); +	mfsdram(SDRAM_RQDC, val); +	debug("%s[%d] RQDC: 0x%08lX\n", __FUNCTION__, __LINE__, val); +	mfsdram(SDRAM_RFDC, val); +	debug("%s[%d] RFDC: 0x%08lX\n", __FUNCTION__, __LINE__, val); +	mfsdram(SDRAM_RDCC, val); +	debug("%s[%d] RDCC: 0x%08lX\n", __FUNCTION__, __LINE__, val); +} +#else /* calibration test with hardvalues */ +/*-----------------------------------------------------------------------------+ + * DQS_calibration_process. + *-----------------------------------------------------------------------------*/ +static void test(void) +{ +	unsigned long dimm_num; +	unsigned long ecc_temp; +	unsigned long i, j; +	unsigned long *membase; +	unsigned long bxcf[MAXRANKS]; +	unsigned long val; +	char window_found; +	char begin_found[MAXDIMMS]; +	char end_found[MAXDIMMS]; +	char search_end[MAXDIMMS]; +	unsigned long test[NUMMEMTESTS][NUMMEMWORDS] = { +		{0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}, +		{0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000}, +		{0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555}, +		{0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA}, +		{0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A}, +		{0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5}, +		{0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA}, +		{0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, +		 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55} }; + +	/*------------------------------------------------------------------ +	 * Test to determine the best read clock delay tuning bits. +	 * +	 * Before the DDR controller can be used, the read clock delay needs to be +	 * set.  This is SDRAM_RQDC[RQFD] and SDRAM_RFDC[RFFD]. +	 * This value cannot be hardcoded into the program because it changes +	 * depending on the board's setup and environment. +	 * To do this, all delay values are tested to see if they +	 * work or not.  By doing this, you get groups of fails with groups of +	 * passing values.  The idea is to find the start and end of a passing +	 * window and take the center of it to use as the read clock delay. +	 * +	 * A failure has to be seen first so that when we hit a pass, we know +	 * that it is truely the start of the window.  If we get passing values +	 * to start off with, we don't know if we are at the start of the window. +	 * +	 * The code assumes that a failure will always be found. +	 * If a failure is not found, there is no easy way to get the middle +	 * of the passing window.  I guess we can pretty much pick any value +	 * but some values will be better than others.  Since the lowest speed +	 * we can clock the DDR interface at is 200 MHz (2x 100 MHz PLB speed), +	 * from experimentation it is safe to say you will always have a failure. +	 *-----------------------------------------------------------------*/ +	mfsdram(SDRAM_MCOPT1, ecc_temp); +	ecc_temp &= SDRAM_MCOPT1_MCHK_MASK; +	mfsdram(SDRAM_MCOPT1, val); +	mtsdram(SDRAM_MCOPT1, (val & ~SDRAM_MCOPT1_MCHK_MASK) | +		SDRAM_MCOPT1_MCHK_NON); + +	window_found = false; +	begin_found[0] = false; +	end_found[0] = false; +	search_end[0] = false; +	begin_found[1] = false; +	end_found[1] = false; +	search_end[1] = false; + +	for (dimm_num = 0; dimm_num < MAXDIMMS; dimm_num++) { +		mfsdram(SDRAM_MB0CF + (bxcr_num << 2), bxcf[bxcr_num]); + +		/* Banks enabled */ +		if ((bxcf[dimm_num] & SDRAM_BXCF_M_BE_MASK) == SDRAM_BXCF_M_BE_ENABLE) { + +			/* Bank is enabled */ +			membase = +				(unsigned long*)(SDRAM_RXBAS_SDBA_DECODE(mfdcr_any(SDRAM_R0BAS+dimm_num))); + +			/*------------------------------------------------------------------ +			 * Run the short memory test. +			 *-----------------------------------------------------------------*/ +			for (i = 0; i < NUMMEMTESTS; i++) { +				for (j = 0; j < NUMMEMWORDS; j++) { +					membase[j] = test[i][j]; +					ppcDcbf((u32)&(membase[j])); +				} +				sync(); +				for (j = 0; j < NUMMEMWORDS; j++) { +					if (membase[j] != test[i][j]) { +						ppcDcbf((u32)&(membase[j])); +						break; +					} +					ppcDcbf((u32)&(membase[j])); +				} +				sync(); +				if (j < NUMMEMWORDS) +					break; +			} + +			/*------------------------------------------------------------------ +			 * See if the rffd value passed. +			 *-----------------------------------------------------------------*/ +			if (i < NUMMEMTESTS) { +				if ((end_found[dimm_num] == false) && +				    (search_end[dimm_num] == true)) { +					end_found[dimm_num] = true; +				} +				if ((end_found[0] == true) && +				    (end_found[1] == true)) +					break; +			} else { +				if (begin_found[dimm_num] == false) { +					begin_found[dimm_num] = true; +					search_end[dimm_num] = true; +				} +			} +		} else { +			begin_found[dimm_num] = true; +			end_found[dimm_num] = true; +		} +	} + +	if ((begin_found[0] == true) && (begin_found[1] == true)) +		window_found = true; + +	/*------------------------------------------------------------------ +	 * Make sure we found the valid read passing window.  Halt if not +	 *-----------------------------------------------------------------*/ +	if (window_found == false) { +		printf("ERROR: Cannot determine a common read delay for the " +		       "DIMM(s) installed.\n"); +		spd_ddr_init_hang (); +	} + +	/*------------------------------------------------------------------ +	 * Restore the ECC variable to what it originally was +	 *-----------------------------------------------------------------*/ +	mtsdram(SDRAM_MCOPT1, +		(ppcMfdcr_sdram(SDRAM_MCOPT1) & ~SDRAM_MCOPT1_MCHK_MASK) +		| ecc_temp); +} +#endif /* !HARD_CODED_DQS */ +#endif /* !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) */ + +#else /* CONFIG_SPD_EEPROM */ + +/*----------------------------------------------------------------------------- + * Function:	initdram + * Description: Configures the PPC4xx IBM DDR1/DDR2 SDRAM memory controller. + * 		The configuration is performed using static, compile- + *		time parameters. + * 		Configures the PPC405EX(r) and PPC460EX/GT + *---------------------------------------------------------------------------*/ +phys_size_t initdram(int board_type) +{ +	unsigned long val; + +#if defined(CONFIG_440) +	mtdcr(SDRAM_R0BAS,	CONFIG_SYS_SDRAM_R0BAS); +	mtdcr(SDRAM_R1BAS,	CONFIG_SYS_SDRAM_R1BAS); +	mtdcr(SDRAM_R2BAS,	CONFIG_SYS_SDRAM_R2BAS); +	mtdcr(SDRAM_R3BAS,	CONFIG_SYS_SDRAM_R3BAS); +	mtdcr(SDRAM_PLBADDULL,	CONFIG_SYS_SDRAM_PLBADDULL);	/* MQ0_BAUL */ +	mtdcr(SDRAM_PLBADDUHB,	CONFIG_SYS_SDRAM_PLBADDUHB);	/* MQ0_BAUH */ +	mtdcr(SDRAM_CONF1LL,	CONFIG_SYS_SDRAM_CONF1LL); +	mtdcr(SDRAM_CONF1HB,	CONFIG_SYS_SDRAM_CONF1HB); +	mtdcr(SDRAM_CONFPATHB,	CONFIG_SYS_SDRAM_CONFPATHB); +#endif + +	/* Set Memory Bank Configuration Registers */ + +	mtsdram(SDRAM_MB0CF, CONFIG_SYS_SDRAM0_MB0CF); +	mtsdram(SDRAM_MB1CF, CONFIG_SYS_SDRAM0_MB1CF); +	mtsdram(SDRAM_MB2CF, CONFIG_SYS_SDRAM0_MB2CF); +	mtsdram(SDRAM_MB3CF, CONFIG_SYS_SDRAM0_MB3CF); + +	/* Set Memory Clock Timing Register */ + +	mtsdram(SDRAM_CLKTR, CONFIG_SYS_SDRAM0_CLKTR); + +	/* Set Refresh Time Register */ + +	mtsdram(SDRAM_RTR, CONFIG_SYS_SDRAM0_RTR); + +	/* Set SDRAM Timing Registers */ + +	mtsdram(SDRAM_SDTR1, CONFIG_SYS_SDRAM0_SDTR1); +	mtsdram(SDRAM_SDTR2, CONFIG_SYS_SDRAM0_SDTR2); +	mtsdram(SDRAM_SDTR3, CONFIG_SYS_SDRAM0_SDTR3); + +	/* Set Mode and Extended Mode Registers */ + +	mtsdram(SDRAM_MMODE, CONFIG_SYS_SDRAM0_MMODE); +	mtsdram(SDRAM_MEMODE, CONFIG_SYS_SDRAM0_MEMODE); + +	/* Set Memory Controller Options 1 Register */ + +	mtsdram(SDRAM_MCOPT1, CONFIG_SYS_SDRAM0_MCOPT1); + +	/* Set Manual Initialization Control Registers */ + +	mtsdram(SDRAM_INITPLR0, CONFIG_SYS_SDRAM0_INITPLR0); +	mtsdram(SDRAM_INITPLR1, CONFIG_SYS_SDRAM0_INITPLR1); +	mtsdram(SDRAM_INITPLR2, CONFIG_SYS_SDRAM0_INITPLR2); +	mtsdram(SDRAM_INITPLR3, CONFIG_SYS_SDRAM0_INITPLR3); +	mtsdram(SDRAM_INITPLR4, CONFIG_SYS_SDRAM0_INITPLR4); +	mtsdram(SDRAM_INITPLR5, CONFIG_SYS_SDRAM0_INITPLR5); +	mtsdram(SDRAM_INITPLR6, CONFIG_SYS_SDRAM0_INITPLR6); +	mtsdram(SDRAM_INITPLR7, CONFIG_SYS_SDRAM0_INITPLR7); +	mtsdram(SDRAM_INITPLR8, CONFIG_SYS_SDRAM0_INITPLR8); +	mtsdram(SDRAM_INITPLR9, CONFIG_SYS_SDRAM0_INITPLR9); +	mtsdram(SDRAM_INITPLR10, CONFIG_SYS_SDRAM0_INITPLR10); +	mtsdram(SDRAM_INITPLR11, CONFIG_SYS_SDRAM0_INITPLR11); +	mtsdram(SDRAM_INITPLR12, CONFIG_SYS_SDRAM0_INITPLR12); +	mtsdram(SDRAM_INITPLR13, CONFIG_SYS_SDRAM0_INITPLR13); +	mtsdram(SDRAM_INITPLR14, CONFIG_SYS_SDRAM0_INITPLR14); +	mtsdram(SDRAM_INITPLR15, CONFIG_SYS_SDRAM0_INITPLR15); + +	/* Set On-Die Termination Registers */ + +	mtsdram(SDRAM_CODT, CONFIG_SYS_SDRAM0_CODT); +	mtsdram(SDRAM_MODT0, CONFIG_SYS_SDRAM0_MODT0); +	mtsdram(SDRAM_MODT1, CONFIG_SYS_SDRAM0_MODT1); + +	/* Set Write Timing Register */ + +	mtsdram(SDRAM_WRDTR, CONFIG_SYS_SDRAM0_WRDTR); + +	/* +	 * Start Initialization by SDRAM0_MCOPT2[SREN] = 0 and +	 * SDRAM0_MCOPT2[IPTR] = 1 +	 */ + +	mtsdram(SDRAM_MCOPT2, (SDRAM_MCOPT2_SREN_EXIT | +			       SDRAM_MCOPT2_IPTR_EXECUTE)); + +	/* +	 * Poll SDRAM0_MCSTAT[MIC] for assertion to indicate the +	 * completion of initialization. +	 */ + +	do { +		mfsdram(SDRAM_MCSTAT, val); +	} while ((val & SDRAM_MCSTAT_MIC_MASK) != SDRAM_MCSTAT_MIC_COMP); + +	/* Set Delay Control Registers */ + +	mtsdram(SDRAM_DLCR, CONFIG_SYS_SDRAM0_DLCR); + +#if !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +	mtsdram(SDRAM_RDCC, CONFIG_SYS_SDRAM0_RDCC); +	mtsdram(SDRAM_RQDC, CONFIG_SYS_SDRAM0_RQDC); +	mtsdram(SDRAM_RFDC, CONFIG_SYS_SDRAM0_RFDC); +#endif /* !CONFIG_PPC4xx_DDR_AUTOCALIBRATION */ + +	/* +	 * Enable Controller by SDRAM0_MCOPT2[DCEN] = 1: +	 */ + +	mfsdram(SDRAM_MCOPT2, val); +	mtsdram(SDRAM_MCOPT2, val | SDRAM_MCOPT2_DCEN_ENABLE); + +#if defined(CONFIG_440) +	/* +	 * Program TLB entries with caches enabled, for best performace +	 * while auto-calibrating and ECC generation +	 */ +	program_tlb(0, 0, (CONFIG_SYS_MBYTES_SDRAM << 20), 0); +#endif + +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +	/*------------------------------------------------------------------ +	 | DQS calibration. +	 +-----------------------------------------------------------------*/ +	DQS_autocalibration(); +#endif /* CONFIG_PPC4xx_DDR_AUTOCALIBRATION */ + +	/* +	 * Now complete RDSS configuration as mentioned on page 7 of the AMCC +	 * PowerPC440SP/SPe DDR2 application note: +	 * "DDR1/DDR2 Initialization Sequence and Dynamic Tuning" +	 */ +	update_rdcc(); + +#if defined(CONFIG_DDR_ECC) +	do_program_ecc(0); +#endif /* defined(CONFIG_DDR_ECC) */ + +#if defined(CONFIG_440) +	/* +	 * Now after initialization (auto-calibration and ECC generation) +	 * remove the TLB entries with caches enabled and program again with +	 * desired cache functionality +	 */ +	remove_tlb(0, (CONFIG_SYS_MBYTES_SDRAM << 20)); +	program_tlb(0, 0, (CONFIG_SYS_MBYTES_SDRAM << 20), MY_TLB_WORD2_I_ENABLE); +#endif + +	ppc4xx_ibm_ddr2_register_dump(); + +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +	/* +	 * Clear potential errors resulting from auto-calibration. +	 * If not done, then we could get an interrupt later on when +	 * exceptions are enabled. +	 */ +	set_mcsr(get_mcsr()); +#endif /* CONFIG_PPC4xx_DDR_AUTOCALIBRATION */ + +	return (CONFIG_SYS_MBYTES_SDRAM << 20); +} +#endif /* CONFIG_SPD_EEPROM */ + +#if defined(CONFIG_440) +u32 mfdcr_any(u32 dcr) +{ +	u32 val; + +	switch (dcr) { +	case SDRAM_R0BAS + 0: +		val = mfdcr(SDRAM_R0BAS + 0); +		break; +	case SDRAM_R0BAS + 1: +		val = mfdcr(SDRAM_R0BAS + 1); +		break; +	case SDRAM_R0BAS + 2: +		val = mfdcr(SDRAM_R0BAS + 2); +		break; +	case SDRAM_R0BAS + 3: +		val = mfdcr(SDRAM_R0BAS + 3); +		break; +	default: +		printf("DCR %d not defined in case statement!!!\n", dcr); +		val = 0; /* just to satisfy the compiler */ +	} + +	return val; +} + +void mtdcr_any(u32 dcr, u32 val) +{ +	switch (dcr) { +	case SDRAM_R0BAS + 0: +		mtdcr(SDRAM_R0BAS + 0, val); +		break; +	case SDRAM_R0BAS + 1: +		mtdcr(SDRAM_R0BAS + 1, val); +		break; +	case SDRAM_R0BAS + 2: +		mtdcr(SDRAM_R0BAS + 2, val); +		break; +	case SDRAM_R0BAS + 3: +		mtdcr(SDRAM_R0BAS + 3, val); +		break; +	default: +		printf("DCR %d not defined in case statement!!!\n", dcr); +	} +} +#endif /* defined(CONFIG_440) */ + +inline void ppc4xx_ibm_ddr2_register_dump(void) +{ +#if defined(DEBUG) +	printf("\nPPC4xx IBM DDR2 Register Dump:\n"); + +#if (defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ +     defined(CONFIG_460EX) || defined(CONFIG_460GT)) +	PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(R0BAS); +	PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(R1BAS); +	PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(R2BAS); +	PPC4xx_IBM_DDR2_DUMP_MQ_REGISTER(R3BAS); +#endif /* (defined(CONFIG_440SP) || ... */ +#if defined(CONFIG_405EX) +	PPC4xx_IBM_DDR2_DUMP_REGISTER(BESR); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(BEARL); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(BEARH); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(WMIRQ); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(PLBOPT); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(PUABA); +#endif /* defined(CONFIG_405EX) */ +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MB0CF); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MB1CF); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MB2CF); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MB3CF); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MCSTAT); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MCOPT1); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MCOPT2); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MODT0); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MODT1); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MODT2); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MODT3); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(CODT); +#if (defined(CONFIG_440SP) || defined(CONFIG_440SPE) ||	\ +     defined(CONFIG_460EX) || defined(CONFIG_460GT)) +	PPC4xx_IBM_DDR2_DUMP_REGISTER(VVPR); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(OPARS); +	/* +	 * OPART is only used as a trigger register. +	 * +	 * No data is contained in this register, and reading or writing +	 * to is can cause bad things to happen (hangs). Just skip it and +	 * report "N/A". +	 */ +	printf("%20s = N/A\n", "SDRAM_OPART"); +#endif /* defined(CONFIG_440SP) || ... */ +	PPC4xx_IBM_DDR2_DUMP_REGISTER(RTR); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR0); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR1); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR2); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR3); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR4); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR5); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR6); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR7); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR8); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR9); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR10); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR11); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR12); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR13); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR14); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(INITPLR15); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(RQDC); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(RFDC); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(RDCC); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(DLCR); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(CLKTR); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(WRDTR); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(SDTR1); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(SDTR2); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(SDTR3); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MMODE); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(MEMODE); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(ECCES); +#if (defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ +     defined(CONFIG_460EX) || defined(CONFIG_460GT)) +	PPC4xx_IBM_DDR2_DUMP_REGISTER(CID); +#endif /* defined(CONFIG_440SP) || ... */ +	PPC4xx_IBM_DDR2_DUMP_REGISTER(RID); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(FCSR); +	PPC4xx_IBM_DDR2_DUMP_REGISTER(RTSR); +#endif /* defined(DEBUG) */ +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c new file mode 100644 index 00000000..67f149de --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c @@ -0,0 +1,1227 @@ +/* + * arch/powerpc/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c + * This SPD SDRAM detection code supports AMCC PPC44x cpu's with a + * DDR2 controller (non Denali Core). Those currently are: + * + * 405:		405EX + * 440/460:	440SP/440SPe/460EX/460GT/460SX + * + * (C) Copyright 2008 Applied Micro Circuits Corporation + * Adam Graham  <agraham@amcc.com> + * + * (C) Copyright 2007-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * COPYRIGHT   AMCC   CORPORATION 2004 + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#undef DEBUG + +#include <common.h> +#include <asm/ppc4xx.h> +#include <asm/io.h> +#include <asm/processor.h> + +#include "ecc.h" + +#define MAXBXCF			4 +#define SDRAM_RXBAS_SHIFT_1M	20 + +#if defined(CONFIG_SYS_DECREMENT_PATTERNS) +#define NUMMEMTESTS		24 +#else +#define NUMMEMTESTS		8 +#endif /* CONFIG_SYS_DECREMENT_PATTERNS */ +#define NUMLOOPS		1	/* configure as you deem approporiate */ +#define NUMMEMWORDS		16 + +#define SDRAM_RDCC_RDSS_VAL(n)	SDRAM_RDCC_RDSS_DECODE(ddr_rdss_opt(n)) + +/* Private Structure Definitions */ + +struct autocal_regs { +	u32 rffd; +	u32 rqfd; +}; + +struct ddrautocal { +	u32 rffd; +	u32 rffd_min; +	u32 rffd_max; +	u32 rffd_size; +	u32 rqfd; +	u32 rqfd_size; +	u32 rdcc; +	u32 flags; +}; + +struct sdram_timing_clks { +	u32 wrdtr; +	u32 clktr; +	u32 rdcc; +	u32 flags; +}; + +struct autocal_clks { +	struct sdram_timing_clks clocks; +	struct ddrautocal	 autocal; +}; + +/*--------------------------------------------------------------------------+ + * Prototypes + *--------------------------------------------------------------------------*/ +#if defined(CONFIG_PPC4xx_DDR_METHOD_A) +static u32 DQS_calibration_methodA(struct ddrautocal *); +static u32 program_DQS_calibration_methodA(struct ddrautocal *); +#else +static u32 DQS_calibration_methodB(struct ddrautocal *); +static u32 program_DQS_calibration_methodB(struct ddrautocal *); +#endif +static int short_mem_test(u32 *); + +/* + * To provide an interface for board specific config values in this common + * DDR setup code, we implement he "weak" default functions here. They return + * the default value back to the caller. + * + * Please see include/configs/yucca.h for an example fora board specific + * implementation. + */ + +#if !defined(CONFIG_SPD_EEPROM) +u32 __ddr_wrdtr(u32 default_val) +{ +	return default_val; +} +u32 ddr_wrdtr(u32) __attribute__((weak, alias("__ddr_wrdtr"))); + +u32 __ddr_clktr(u32 default_val) +{ +	return default_val; +} +u32 ddr_clktr(u32) __attribute__((weak, alias("__ddr_clktr"))); + +/* + * Board-specific Platform code can reimplement spd_ddr_init_hang () if needed + */ +void __spd_ddr_init_hang(void) +{ +	hang(); +} +void +spd_ddr_init_hang(void) __attribute__((weak, alias("__spd_ddr_init_hang"))); +#endif /* defined(CONFIG_SPD_EEPROM) */ + +struct sdram_timing *__ddr_scan_option(struct sdram_timing *default_val) +{ +	return default_val; +} +struct sdram_timing *ddr_scan_option(struct sdram_timing *) +	__attribute__((weak, alias("__ddr_scan_option"))); + +u32 __ddr_rdss_opt(u32 default_val) +{ +	return default_val; +} +u32 ddr_rdss_opt(ulong) __attribute__((weak, alias("__ddr_rdss_opt"))); + + +static u32 *get_membase(int bxcr_num) +{ +	u32 *membase; + +#if defined(SDRAM_R0BAS) +	/* BAS from Memory Queue rank reg. */ +	membase = +	    (u32 *)(SDRAM_RXBAS_SDBA_DECODE(mfdcr_any(SDRAM_R0BAS+bxcr_num))); +#else +	{ +		ulong bxcf; + +		/* BAS from SDRAM_MBxCF mem rank reg. */ +		mfsdram(SDRAM_MB0CF + (bxcr_num<<2), bxcf); +		membase = (u32 *)((bxcf & 0xfff80000) << 3); +	} +#endif + +	return membase; +} + +static inline void ecc_clear_status_reg(void) +{ +	mtsdram(SDRAM_ECCES, 0xffffffff); +#if defined(SDRAM_R0BAS) +	mtdcr(SDRAM_ERRSTATLL, 0xffffffff); +#endif +} + +/* + * Reset and relock memory DLL after SDRAM_CLKTR change + */ +static inline void relock_memory_DLL(void) +{ +	u32 reg; + +	mtsdram(SDRAM_MCOPT2, SDRAM_MCOPT2_IPTR_EXECUTE); + +	do { +		mfsdram(SDRAM_MCSTAT, reg); +	} while (!(reg & SDRAM_MCSTAT_MIC_COMP)); + +	mfsdram(SDRAM_MCOPT2, reg); +	mtsdram(SDRAM_MCOPT2, reg | SDRAM_MCOPT2_DCEN_ENABLE); +} + +static int ecc_check_status_reg(void) +{ +	u32 ecc_status; + +	/* +	 * Compare suceeded, now check +	 * if got ecc error. If got an +	 * ecc error, then don't count +	 * this as a passing value +	 */ +	mfsdram(SDRAM_ECCES, ecc_status); +	if (ecc_status != 0x00000000) { +		/* clear on error */ +		ecc_clear_status_reg(); +		/* ecc check failure */ +		return 0; +	} +	ecc_clear_status_reg(); +	sync(); + +	return 1; +} + +/* return 1 if passes, 0 if fail */ +static int short_mem_test(u32 *base_address) +{ +	int i, j, l; +	u32 ecc_mode = 0; + +	ulong test[NUMMEMTESTS][NUMMEMWORDS] = { +	/* 0 */	{0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}, +	/* 1 */	{0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000}, +	/* 2 */	{0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555}, +	/* 3 */	{0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA}, +	/* 4 */	{0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A}, +	/* 5 */	{0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5}, +	/* 6 */	{0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA}, +	/* 7 */	{0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, +		 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, +		 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, +		 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55}, + +#if defined(CONFIG_SYS_DECREMENT_PATTERNS) +	/* 8 */	{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +		 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +		 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, +		 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, +	/* 9 */	{0xfffefffe, 0xfffefffe, 0xfffefffe, 0xfffefffe, +		 0xfffefffe, 0xfffefffe, 0xfffefffe, 0xfffefffe, +		 0xfffefffe, 0xfffefffe, 0xfffefffe, 0xfffefffe, +		 0xfffefffe, 0xfffefffe, 0xfffefffe, 0xfffefffe}, +	/* 10 */{0xfffdfffd, 0xfffdfffd, 0xfffdffff, 0xfffdfffd, +		 0xfffdfffd, 0xfffdfffd, 0xfffdffff, 0xfffdfffd, +		 0xfffdfffd, 0xfffdfffd, 0xfffdffff, 0xfffdfffd, +		 0xfffdfffd, 0xfffdfffd, 0xfffdffff, 0xfffdfffd}, +	/* 11 */{0xfffcfffc, 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, +		 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, +		 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, +		 0xfffcfffc, 0xfffcfffc, 0xfffcfffc, 0xfffcfffc}, +	/* 12 */{0xfffbfffb, 0xfffffffb, 0xfffffffb, 0xfffffffb, +		 0xfffbfffb, 0xfffffffb, 0xfffffffb, 0xfffffffb, +		 0xfffbfffb, 0xfffffffb, 0xfffffffb, 0xfffffffb, +		 0xfffbfffb, 0xfffffffb, 0xfffffffb, 0xfffffffb}, +	/* 13 */{0xfffafffa, 0xfffafffa, 0xfffffffa, 0xfffafffa, +		 0xfffafffa, 0xfffafffa, 0xfffafffa, 0xfffafffa, +		 0xfffafffa, 0xfffafffa, 0xfffafffa, 0xfffafffa, +		 0xfffafffa, 0xfffafffa, 0xfffafffa, 0xfffafffa}, +	/* 14 */{0xfff9fff9, 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, +		 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, +		 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, +		 0xfff9fff9, 0xfff9fff9, 0xfff9fff9, 0xfff9fff9}, +	/* 15 */{0xfff8fff8, 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, +		 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, +		 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, +		 0xfff8fff8, 0xfff8fff8, 0xfff8fff8, 0xfff8fff8}, +	/* 16 */{0xfff7fff7, 0xfff7ffff, 0xfff7fff7, 0xfff7fff7, +		 0xfff7fff7, 0xfff7ffff, 0xfff7fff7, 0xfff7fff7, +		 0xfff7fff7, 0xfff7ffff, 0xfff7fff7, 0xfff7fff7, +		 0xfff7ffff, 0xfff7ffff, 0xfff7fff7, 0xfff7fff7}, +	/* 17 */{0xfff6fff5, 0xfff6ffff, 0xfff6fff6, 0xfff6fff7, +		 0xfff6fff5, 0xfff6ffff, 0xfff6fff6, 0xfff6fff7, +		 0xfff6fff5, 0xfff6ffff, 0xfff6fff6, 0xfff6fff7, +		 0xfff6fff5, 0xfff6ffff, 0xfff6fff6, 0xfff6fff7}, +	/* 18 */{0xfff5fff4, 0xfff5ffff, 0xfff5fff5, 0xfff5fff5, +		 0xfff5fff4, 0xfff5ffff, 0xfff5fff5, 0xfff5fff5, +		 0xfff5fff4, 0xfff5ffff, 0xfff5fff5, 0xfff5fff5, +		 0xfff5fff4, 0xfff5ffff, 0xfff5fff5, 0xfff5fff5}, +	/* 19 */{0xfff4fff3, 0xfff4ffff, 0xfff4fff4, 0xfff4fff4, +		 0xfff4fff3, 0xfff4ffff, 0xfff4fff4, 0xfff4fff4, +		 0xfff4fff3, 0xfff4ffff, 0xfff4fff4, 0xfff4fff4, +		 0xfff4fff3, 0xfff4ffff, 0xfff4fff4, 0xfff4fff4}, +	/* 20 */{0xfff3fff2, 0xfff3ffff, 0xfff3fff3, 0xfff3fff3, +		 0xfff3fff2, 0xfff3ffff, 0xfff3fff3, 0xfff3fff3, +		 0xfff3fff2, 0xfff3ffff, 0xfff3fff3, 0xfff3fff3, +		 0xfff3fff2, 0xfff3ffff, 0xfff3fff3, 0xfff3fff3}, +	/* 21 */{0xfff2ffff, 0xfff2ffff, 0xfff2fff2, 0xfff2fff2, +		 0xfff2ffff, 0xfff2ffff, 0xfff2fff2, 0xfff2fff2, +		 0xfff2ffff, 0xfff2ffff, 0xfff2fff2, 0xfff2fff2, +		 0xfff2ffff, 0xfff2ffff, 0xfff2fff2, 0xfff2fff2}, +	/* 22 */{0xfff1ffff, 0xfff1ffff, 0xfff1fff1, 0xfff1fff1, +		 0xfff1ffff, 0xfff1ffff, 0xfff1fff1, 0xfff1fff1, +		 0xfff1ffff, 0xfff1ffff, 0xfff1fff1, 0xfff1fff1, +		 0xfff1ffff, 0xfff1ffff, 0xfff1fff1, 0xfff1fff1}, +	/* 23 */{0xfff0fff0, 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, +		 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, +		 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, 0xfff0fff0, +		 0xfff0fff0, 0xfff0fffe, 0xfff0fff0, 0xfff0fff0}, +#endif /* CONFIG_SYS_DECREMENT_PATTERNS */ +								 }; + +	mfsdram(SDRAM_MCOPT1, ecc_mode); +	if ((ecc_mode & SDRAM_MCOPT1_MCHK_CHK_REP) == +						SDRAM_MCOPT1_MCHK_CHK_REP) { +		ecc_clear_status_reg(); +		sync(); +		ecc_mode = 1; +	} else { +		ecc_mode = 0; +	} + +	/* +	 * Run the short memory test. +	 */ +	for (i = 0; i < NUMMEMTESTS; i++) { +		for (j = 0; j < NUMMEMWORDS; j++) { +			base_address[j] = test[i][j]; +			ppcDcbf((ulong)&(base_address[j])); +		} +		sync(); +		iobarrier_rw(); +		for (l = 0; l < NUMLOOPS; l++) { +			for (j = 0; j < NUMMEMWORDS; j++) { +				if (base_address[j] != test[i][j]) { +					ppcDcbf((u32)&(base_address[j])); +					return 0; +				} else { +					if (ecc_mode) { +						if (!ecc_check_status_reg()) +							return 0; +					} +				} +				ppcDcbf((u32)&(base_address[j])); +			} /* for (j = 0; j < NUMMEMWORDS; j++) */ +			sync(); +			iobarrier_rw(); +		} /* for (l=0; l<NUMLOOPS; l++) */ +	} + +	return 1; +} + +#if defined(CONFIG_PPC4xx_DDR_METHOD_A) +/*-----------------------------------------------------------------------------+ +| program_DQS_calibration_methodA. ++-----------------------------------------------------------------------------*/ +static u32 program_DQS_calibration_methodA(struct ddrautocal *ddrcal) +{ +	u32 pass_result = 0; + +#ifdef DEBUG +	ulong temp; + +	mfsdram(SDRAM_RDCC, temp); +	debug("<%s>SDRAM_RDCC=0x%08x\n", __func__, temp); +#endif + +	pass_result = DQS_calibration_methodA(ddrcal); + +	return pass_result; +} + +/* + * DQS_calibration_methodA() + * + * Autocalibration Method A + * + *  ARRAY [Entire DQS Range] DQS_Valid_Window ;    initialized to all zeros + *  ARRAY [Entire FDBK Range] FDBK_Valid_Window;   initialized to all zeros + *  MEMWRITE(addr, expected_data); + *  for (i = 0; i < Entire DQS Range; i++) {       RQDC.RQFD + *      for (j = 0; j < Entire FDBK Range; j++) {  RFDC.RFFD + *         MEMREAD(addr, actual_data); + *         if (actual_data == expected_data) { + *             DQS_Valid_Window[i] = 1;            RQDC.RQFD + *             FDBK_Valid_Window[i][j] = 1;        RFDC.RFFD + *         } + *      } + *  } + */ +static u32 DQS_calibration_methodA(struct ddrautocal *cal) +{ +	ulong rfdc_reg; +	ulong rffd; + +	ulong rqdc_reg; +	ulong rqfd; + +	u32 *membase; +	ulong bxcf; +	int rqfd_average; +	int bxcr_num; +	int rffd_average; +	int pass; +	u32 passed = 0; + +	int in_window; +	struct autocal_regs curr_win_min; +	struct autocal_regs curr_win_max; +	struct autocal_regs best_win_min; +	struct autocal_regs best_win_max; +	struct autocal_regs loop_win_min; +	struct autocal_regs loop_win_max; + +#ifdef DEBUG +	ulong temp; +#endif +	ulong rdcc; + +	char slash[] = "\\|/-\\|/-"; +	int loopi = 0; + +	/* start */ +	in_window = 0; + +	memset(&curr_win_min, 0, sizeof(curr_win_min)); +	memset(&curr_win_max, 0, sizeof(curr_win_max)); +	memset(&best_win_min, 0, sizeof(best_win_min)); +	memset(&best_win_max, 0, sizeof(best_win_max)); +	memset(&loop_win_min, 0, sizeof(loop_win_min)); +	memset(&loop_win_max, 0, sizeof(loop_win_max)); + +	rdcc = 0; + +	/* +	 * Program RDCC register +	 * Read sample cycle auto-update enable +	 */ +	mtsdram(SDRAM_RDCC, +		ddr_rdss_opt(SDRAM_RDCC_RDSS_T2) | SDRAM_RDCC_RSAE_ENABLE); + +#ifdef DEBUG +	mfsdram(SDRAM_RDCC, temp); +	debug("<%s>SDRAM_RDCC=0x%x\n", __func__, temp); +	mfsdram(SDRAM_RTSR, temp); +	debug("<%s>SDRAM_RTSR=0x%x\n", __func__, temp); +	mfsdram(SDRAM_FCSR, temp); +	debug("<%s>SDRAM_FCSR=0x%x\n", __func__, temp); +#endif + +	/* +	 * Program RQDC register +	 * Internal DQS delay mechanism enable +	 */ +	mtsdram(SDRAM_RQDC, +		SDRAM_RQDC_RQDE_ENABLE | SDRAM_RQDC_RQFD_ENCODE(0x00)); + +#ifdef DEBUG +	mfsdram(SDRAM_RQDC, temp); +	debug("<%s>SDRAM_RQDC=0x%x\n", __func__, temp); +#endif + +	/* +	 * Program RFDC register +	 * Set Feedback Fractional Oversample +	 * Auto-detect read sample cycle enable +	 */ +	mtsdram(SDRAM_RFDC, SDRAM_RFDC_ARSE_ENABLE | +		SDRAM_RFDC_RFOS_ENCODE(0) | SDRAM_RFDC_RFFD_ENCODE(0)); + +#ifdef DEBUG +	mfsdram(SDRAM_RFDC, temp); +	debug("<%s>SDRAM_RFDC=0x%x\n", __func__, temp); +#endif + +	putc(' '); +	for (rqfd = 0; rqfd <= SDRAM_RQDC_RQFD_MAX; rqfd++) { + +		mfsdram(SDRAM_RQDC, rqdc_reg); +		rqdc_reg &= ~(SDRAM_RQDC_RQFD_MASK); +		mtsdram(SDRAM_RQDC, rqdc_reg | SDRAM_RQDC_RQFD_ENCODE(rqfd)); + +		putc('\b'); +		putc(slash[loopi++ % 8]); + +		curr_win_min.rffd = 0; +		curr_win_max.rffd = 0; +		in_window = 0; + +		for (rffd = 0, pass = 0; rffd <= SDRAM_RFDC_RFFD_MAX; rffd++) { +			mfsdram(SDRAM_RFDC, rfdc_reg); +			rfdc_reg &= ~(SDRAM_RFDC_RFFD_MASK); +			mtsdram(SDRAM_RFDC, +				    rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd)); + +			for (bxcr_num = 0; bxcr_num < MAXBXCF; bxcr_num++) { +				mfsdram(SDRAM_MB0CF + (bxcr_num<<2), bxcf); + +				/* Banks enabled */ +				if (bxcf & SDRAM_BXCF_M_BE_MASK) { +					/* Bank is enabled */ +					membase = get_membase(bxcr_num); +					pass = short_mem_test(membase); +				} /* if bank enabled */ +			} /* for bxcr_num */ + +			/* If this value passed update RFFD windows */ +			if (pass && !in_window) { /* at the start of window */ +				in_window = 1; +				curr_win_min.rffd = curr_win_max.rffd = rffd; +				curr_win_min.rqfd = curr_win_max.rqfd = rqfd; +				mfsdram(SDRAM_RDCC, rdcc); /*record this value*/ +			} else if (!pass && in_window) { /* at end of window */ +				in_window = 0; +			} else if (pass && in_window) { /* within the window */ +				curr_win_max.rffd = rffd; +				curr_win_max.rqfd = rqfd; +			} +			/* else if (!pass && !in_window) +				skip - no pass, not currently in a window */ + +			if (in_window) { +				if ((curr_win_max.rffd - curr_win_min.rffd) > +				    (best_win_max.rffd - best_win_min.rffd)) { +					best_win_min.rffd = curr_win_min.rffd; +					best_win_max.rffd = curr_win_max.rffd; + +					best_win_min.rqfd = curr_win_min.rqfd; +					best_win_max.rqfd = curr_win_max.rqfd; +					cal->rdcc	  = rdcc; +				} +				passed = 1; +			} +		} /* RFDC.RFFD */ + +		/* +		 * save-off the best window results of the RFDC.RFFD +		 * for this RQDC.RQFD setting +		 */ +		/* +		 * if (just ended RFDC.RFDC loop pass window) > +		 *	(prior RFDC.RFFD loop pass window) +		 */ +		if ((best_win_max.rffd - best_win_min.rffd) > +		    (loop_win_max.rffd - loop_win_min.rffd)) { +			loop_win_min.rffd = best_win_min.rffd; +			loop_win_max.rffd = best_win_max.rffd; +			loop_win_min.rqfd = rqfd; +			loop_win_max.rqfd = rqfd; +			debug("RQFD.min 0x%08x, RQFD.max 0x%08x, " +			      "RFFD.min 0x%08x, RFFD.max 0x%08x\n", +					loop_win_min.rqfd, loop_win_max.rqfd, +					loop_win_min.rffd, loop_win_max.rffd); +		} +	} /* RQDC.RQFD */ + +	putc('\b'); + +	debug("\n"); + +	if ((loop_win_min.rffd == 0) && (loop_win_max.rffd == 0) && +	    (best_win_min.rffd == 0) && (best_win_max.rffd == 0) && +	    (best_win_min.rqfd == 0) && (best_win_max.rqfd == 0)) { +		passed = 0; +	} + +	/* +	 * Need to program RQDC before RFDC. +	 */ +	debug("<%s> RQFD Min: 0x%x\n", __func__, loop_win_min.rqfd); +	debug("<%s> RQFD Max: 0x%x\n", __func__, loop_win_max.rqfd); +	rqfd_average = loop_win_max.rqfd; + +	if (rqfd_average < 0) +		rqfd_average = 0; + +	if (rqfd_average > SDRAM_RQDC_RQFD_MAX) +		rqfd_average = SDRAM_RQDC_RQFD_MAX; + +	debug("<%s> RFFD average: 0x%08x\n", __func__, rqfd_average); +	mtsdram(SDRAM_RQDC, (rqdc_reg & ~SDRAM_RQDC_RQFD_MASK) | +				SDRAM_RQDC_RQFD_ENCODE(rqfd_average)); + +	debug("<%s> RFFD Min: 0x%08x\n", __func__, loop_win_min.rffd); +	debug("<%s> RFFD Max: 0x%08x\n", __func__, loop_win_max.rffd); +	rffd_average = ((loop_win_min.rffd + loop_win_max.rffd) / 2); + +	if (rffd_average < 0) +		rffd_average = 0; + +	if (rffd_average > SDRAM_RFDC_RFFD_MAX) +		rffd_average = SDRAM_RFDC_RFFD_MAX; + +	debug("<%s> RFFD average: 0x%08x\n", __func__, rffd_average); +	mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd_average)); + +	/* if something passed, then return the size of the largest window */ +	if (passed != 0) { +		passed		= loop_win_max.rffd - loop_win_min.rffd; +		cal->rqfd	= rqfd_average; +		cal->rffd	= rffd_average; +		cal->rffd_min	= loop_win_min.rffd; +		cal->rffd_max	= loop_win_max.rffd; +	} + +	return (u32)passed; +} + +#else	/* !defined(CONFIG_PPC4xx_DDR_METHOD_A) */ + +/*-----------------------------------------------------------------------------+ +| program_DQS_calibration_methodB. ++-----------------------------------------------------------------------------*/ +static u32 program_DQS_calibration_methodB(struct ddrautocal *ddrcal) +{ +	u32 pass_result = 0; + +#ifdef DEBUG +	ulong temp; +#endif + +	/* +	 * Program RDCC register +	 * Read sample cycle auto-update enable +	 */ +	mtsdram(SDRAM_RDCC, +		ddr_rdss_opt(SDRAM_RDCC_RDSS_T2) | SDRAM_RDCC_RSAE_ENABLE); + +#ifdef DEBUG +	mfsdram(SDRAM_RDCC, temp); +	debug("<%s>SDRAM_RDCC=0x%08x\n", __func__, temp); +#endif + +	/* +	 * Program RQDC register +	 * Internal DQS delay mechanism enable +	 */ +	mtsdram(SDRAM_RQDC, +#if defined(CONFIG_DDR_RQDC_START_VAL) +			SDRAM_RQDC_RQDE_ENABLE | +			    SDRAM_RQDC_RQFD_ENCODE(CONFIG_DDR_RQDC_START_VAL)); +#else +			SDRAM_RQDC_RQDE_ENABLE | SDRAM_RQDC_RQFD_ENCODE(0x38)); +#endif + +#ifdef DEBUG +	mfsdram(SDRAM_RQDC, temp); +	debug("<%s>SDRAM_RQDC=0x%08x\n", __func__, temp); +#endif + +	/* +	 * Program RFDC register +	 * Set Feedback Fractional Oversample +	 * Auto-detect read sample cycle enable +	 */ +	mtsdram(SDRAM_RFDC,	SDRAM_RFDC_ARSE_ENABLE | +				SDRAM_RFDC_RFOS_ENCODE(0) | +				SDRAM_RFDC_RFFD_ENCODE(0)); + +#ifdef DEBUG +	mfsdram(SDRAM_RFDC, temp); +	debug("<%s>SDRAM_RFDC=0x%08x\n", __func__, temp); +#endif + +	pass_result = DQS_calibration_methodB(ddrcal); + +	return pass_result; +} + +/* + * DQS_calibration_methodB() + * + * Autocalibration Method B + * + * ARRAY [Entire DQS Range] DQS_Valid_Window ;       initialized to all zeros + * ARRAY [Entire Feedback Range] FDBK_Valid_Window;  initialized to all zeros + * MEMWRITE(addr, expected_data); + * Initialialize the DQS delay to 80 degrees (MCIF0_RRQDC[RQFD]=0x38). + * + *  for (j = 0; j < Entire Feedback Range; j++) { + *      MEMREAD(addr, actual_data); + *       if (actual_data == expected_data) { + *           FDBK_Valid_Window[j] = 1; + *       } + * } + * + * Set MCIF0_RFDC[RFFD] to the middle of the FDBK_Valid_Window. + * + * for (i = 0; i < Entire DQS Range; i++) { + *     MEMREAD(addr, actual_data); + *     if (actual_data == expected_data) { + *         DQS_Valid_Window[i] = 1; + *      } + * } + * + * Set MCIF0_RRQDC[RQFD] to the middle of the DQS_Valid_Window. + */ +/*-----------------------------------------------------------------------------+ +| DQS_calibration_methodB. ++-----------------------------------------------------------------------------*/ +static u32 DQS_calibration_methodB(struct ddrautocal *cal) +{ +	ulong rfdc_reg; +#ifndef CONFIG_DDR_RFDC_FIXED +	ulong rffd; +#endif + +	ulong rqdc_reg; +	ulong rqfd; + +	ulong rdcc; + +	u32 *membase; +	ulong bxcf; +	int rqfd_average; +	int bxcr_num; +	int rffd_average; +	int pass; +	uint passed = 0; + +	int in_window; +	u32 curr_win_min, curr_win_max; +	u32 best_win_min, best_win_max; +	u32 size = 0; + +	/*------------------------------------------------------------------ +	 | Test to determine the best read clock delay tuning bits. +	 | +	 | Before the DDR controller can be used, the read clock delay needs to +	 | be set.  This is SDRAM_RQDC[RQFD] and SDRAM_RFDC[RFFD]. +	 | This value cannot be hardcoded into the program because it changes +	 | depending on the board's setup and environment. +	 | To do this, all delay values are tested to see if they +	 | work or not.  By doing this, you get groups of fails with groups of +	 | passing values.  The idea is to find the start and end of a passing +	 | window and take the center of it to use as the read clock delay. +	 | +	 | A failure has to be seen first so that when we hit a pass, we know +	 | that it is truely the start of the window.  If we get passing values +	 | to start off with, we don't know if we are at the start of the window +	 | +	 | The code assumes that a failure will always be found. +	 | If a failure is not found, there is no easy way to get the middle +	 | of the passing window.  I guess we can pretty much pick any value +	 | but some values will be better than others.  Since the lowest speed +	 | we can clock the DDR interface at is 200 MHz (2x 100 MHz PLB speed), +	 | from experimentation it is safe to say you will always have a failure +	 +-----------------------------------------------------------------*/ + +	debug("\n\n"); + +#if defined(CONFIG_DDR_RFDC_FIXED) +	mtsdram(SDRAM_RFDC, CONFIG_DDR_RFDC_FIXED); +	size = 512; +	rffd_average = CONFIG_DDR_RFDC_FIXED & SDRAM_RFDC_RFFD_MASK; +	mfsdram(SDRAM_RDCC, rdcc);	/* record this value */ +	cal->rdcc = rdcc; +#else /* CONFIG_DDR_RFDC_FIXED */ +	in_window = 0; +	rdcc = 0; + +	curr_win_min = curr_win_max = 0; +	best_win_min = best_win_max = 0; +	for (rffd = 0; rffd <= SDRAM_RFDC_RFFD_MAX; rffd++) { +		mfsdram(SDRAM_RFDC, rfdc_reg); +		rfdc_reg &= ~(SDRAM_RFDC_RFFD_MASK); +		mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd)); + +		pass = 1; +		for (bxcr_num = 0; bxcr_num < MAXBXCF; bxcr_num++) { +			mfsdram(SDRAM_MB0CF + (bxcr_num<<2), bxcf); + +			/* Banks enabled */ +			if (bxcf & SDRAM_BXCF_M_BE_MASK) { +				/* Bank is enabled */ +				membase = get_membase(bxcr_num); +				pass &= short_mem_test(membase); +			} /* if bank enabled */ +		} /* for bxcf_num */ + +		/* If this value passed */ +		if (pass && !in_window) {	/* start of passing window */ +			in_window = 1; +			curr_win_min = curr_win_max = rffd; +			mfsdram(SDRAM_RDCC, rdcc);	/* record this value */ +		} else if (!pass && in_window) {	/* end passing window */ +			in_window = 0; +		} else if (pass && in_window) {	/* within the passing window */ +			curr_win_max = rffd; +		} + +		if (in_window) { +			if ((curr_win_max - curr_win_min) > +			    (best_win_max - best_win_min)) { +				best_win_min = curr_win_min; +				best_win_max = curr_win_max; +				cal->rdcc    = rdcc; +			} +			passed = 1; +		} +	} /* for rffd */ + +	if ((best_win_min == 0) && (best_win_max == 0)) +		passed = 0; +	else +		size = best_win_max - best_win_min; + +	debug("RFFD Min: 0x%x\n", best_win_min); +	debug("RFFD Max: 0x%x\n", best_win_max); +	rffd_average = ((best_win_min + best_win_max) / 2); + +	cal->rffd_min = best_win_min; +	cal->rffd_max = best_win_max; + +	if (rffd_average < 0) +		rffd_average = 0; + +	if (rffd_average > SDRAM_RFDC_RFFD_MAX) +		rffd_average = SDRAM_RFDC_RFFD_MAX; + +	mtsdram(SDRAM_RFDC, rfdc_reg | SDRAM_RFDC_RFFD_ENCODE(rffd_average)); +#endif /* CONFIG_DDR_RFDC_FIXED */ + +	in_window = 0; + +	curr_win_min = curr_win_max = 0; +	best_win_min = best_win_max = 0; +	for (rqfd = 0; rqfd <= SDRAM_RQDC_RQFD_MAX; rqfd++) { +		mfsdram(SDRAM_RQDC, rqdc_reg); +		rqdc_reg &= ~(SDRAM_RQDC_RQFD_MASK); +		mtsdram(SDRAM_RQDC, rqdc_reg | SDRAM_RQDC_RQFD_ENCODE(rqfd)); + +		pass = 1; +		for (bxcr_num = 0; bxcr_num < MAXBXCF; bxcr_num++) { + +			mfsdram(SDRAM_MB0CF + (bxcr_num<<2), bxcf); + +			/* Banks enabled */ +			if (bxcf & SDRAM_BXCF_M_BE_MASK) { +				/* Bank is enabled */ +				membase = get_membase(bxcr_num); +				pass &= short_mem_test(membase); +			} /* if bank enabled */ +		} /* for bxcf_num */ + +		/* If this value passed */ +		if (pass && !in_window) { +			in_window = 1; +			curr_win_min = curr_win_max = rqfd; +		} else if (!pass && in_window) { +			in_window = 0; +		} else if (pass && in_window) { +			curr_win_max = rqfd; +		} + +		if (in_window) { +			if ((curr_win_max - curr_win_min) > +			    (best_win_max - best_win_min)) { +				best_win_min = curr_win_min; +				best_win_max = curr_win_max; +			} +			passed = 1; +		} +	} /* for rqfd */ + +	if ((best_win_min == 0) && (best_win_max == 0)) +		passed = 0; + +	debug("RQFD Min: 0x%x\n", best_win_min); +	debug("RQFD Max: 0x%x\n", best_win_max); +	rqfd_average = ((best_win_min + best_win_max) / 2); + +	if (rqfd_average < 0) +		rqfd_average = 0; + +	if (rqfd_average > SDRAM_RQDC_RQFD_MAX) +		rqfd_average = SDRAM_RQDC_RQFD_MAX; + +	mtsdram(SDRAM_RQDC, (rqdc_reg & ~SDRAM_RQDC_RQFD_MASK) | +					SDRAM_RQDC_RQFD_ENCODE(rqfd_average)); + +	mfsdram(SDRAM_RQDC, rqdc_reg); +	mfsdram(SDRAM_RFDC, rfdc_reg); + +	/* +	 * Need to program RQDC before RFDC. The value is read above. +	 * That is the reason why auto cal not work. +	 * See, comments below. +	 */ +	mtsdram(SDRAM_RQDC, rqdc_reg); +	mtsdram(SDRAM_RFDC, rfdc_reg); + +	debug("RQDC: 0x%08lX\n", rqdc_reg); +	debug("RFDC: 0x%08lX\n", rfdc_reg); + +	/* if something passed, then return the size of the largest window */ +	if (passed != 0) { +		passed		= size; +		cal->rqfd	= rqfd_average; +		cal->rffd	= rffd_average; +	} + +	return (uint)passed; +} +#endif /* defined(CONFIG_PPC4xx_DDR_METHOD_A) */ + +/* + * Default table for DDR auto-calibration of all + * possible WRDTR and CLKTR values. + * Table format is: + *	 {SDRAM_WRDTR.[WDTR], SDRAM_CLKTR.[CKTR]} + * + * Table is terminated with {-1, -1} value pair. + * + * Board vendors can specify their own board specific subset of + * known working {SDRAM_WRDTR.[WDTR], SDRAM_CLKTR.[CKTR]} value + * pairs via a board defined ddr_scan_option() function. + */ +static struct sdram_timing full_scan_options[] = { +	{0, 0}, {0, 1}, {0, 2}, {0, 3}, +	{1, 0}, {1, 1}, {1, 2}, {1, 3}, +	{2, 0}, {2, 1}, {2, 2}, {2, 3}, +	{3, 0}, {3, 1}, {3, 2}, {3, 3}, +	{4, 0}, {4, 1}, {4, 2}, {4, 3}, +	{5, 0}, {5, 1}, {5, 2}, {5, 3}, +	{6, 0}, {6, 1}, {6, 2}, {6, 3}, +	{-1, -1} +}; + +/*---------------------------------------------------------------------------+ +| DQS_calibration. ++----------------------------------------------------------------------------*/ +u32 DQS_autocalibration(void) +{ +	u32 wdtr; +	u32 clkp; +	u32 result = 0; +	u32 best_result = 0; +	u32 best_rdcc; +	struct ddrautocal ddrcal; +	struct autocal_clks tcal; +	ulong rfdc_reg; +	ulong rqdc_reg; +	u32 val; +	int verbose_lvl = 0; +	char *str; +	char slash[] = "\\|/-\\|/-"; +	int loopi = 0; +	struct sdram_timing *scan_list; + +#if defined(DEBUG_PPC4xx_DDR_AUTOCALIBRATION) +	int i; +	char tmp[64];	/* long enough for environment variables */ +#endif + +	memset(&tcal, 0, sizeof(tcal)); + +	scan_list = ddr_scan_option(full_scan_options); + +	mfsdram(SDRAM_MCOPT1, val); +	if ((val & SDRAM_MCOPT1_MCHK_CHK_REP) == SDRAM_MCOPT1_MCHK_CHK_REP) +		str = "ECC Auto calibration -"; +	else +		str = "Auto calibration -"; + +	puts(str); + +#if defined(DEBUG_PPC4xx_DDR_AUTOCALIBRATION) +	i = getenv_f("autocalib", tmp, sizeof(tmp)); +	if (i < 0) +		strcpy(tmp, CONFIG_AUTOCALIB); + +	if (strcmp(tmp, "final") == 0) { +		/* display the final autocalibration results only */ +		verbose_lvl = 1; +	} else if (strcmp(tmp, "loop") == 0) { +		/* display summary autocalibration info per iteration */ +		verbose_lvl = 2; +	} else if (strcmp(tmp, "display") == 0) { +		/* display full debug autocalibration window info. */ +		verbose_lvl = 3; +	} +#endif /* (DEBUG_PPC4xx_DDR_AUTOCALIBRATION) */ + +	best_rdcc = (SDRAM_RDCC_RDSS_T4 >> 30); + +	while ((scan_list->wrdtr != -1) && (scan_list->clktr != -1)) { +		wdtr = scan_list->wrdtr; +		clkp = scan_list->clktr; + +		mfsdram(SDRAM_WRDTR, val); +		val &= ~(SDRAM_WRDTR_LLWP_MASK | SDRAM_WRDTR_WTR_MASK); +		mtsdram(SDRAM_WRDTR, (val | +			ddr_wrdtr(SDRAM_WRDTR_LLWP_1_CYC | (wdtr << 25)))); + +		mtsdram(SDRAM_CLKTR, clkp << 30); + +		relock_memory_DLL(); + +		putc('\b'); +		putc(slash[loopi++ % 8]); + +#ifdef DEBUG +		debug("\n"); +		debug("*** --------------\n"); +		mfsdram(SDRAM_WRDTR, val); +		debug("*** SDRAM_WRDTR set to 0x%08x\n", val); +		mfsdram(SDRAM_CLKTR, val); +		debug("*** SDRAM_CLKTR set to 0x%08x\n", val); +#endif + +		debug("\n"); +		if (verbose_lvl > 2) { +			printf("*** SDRAM_WRDTR (wdtr) set to %d\n", wdtr); +			printf("*** SDRAM_CLKTR (clkp) set to %d\n", clkp); +		} + +		memset(&ddrcal, 0, sizeof(ddrcal)); + +		/* +		 * DQS calibration. +		 */ +		/* +		 * program_DQS_calibration_method[A|B]() returns 0 if no +		 * passing RFDC.[RFFD] window is found or returns the size +		 * of the best passing window; in the case of a found passing +		 * window, the ddrcal will contain the values of the best +		 * window RQDC.[RQFD] and RFDC.[RFFD]. +		 */ + +		/* +		 * Call PPC4xx SDRAM DDR autocalibration methodA or methodB. +		 * Default is methodB. +		 * Defined the autocalibration method in the board specific +		 * header file. +		 * Please see include/configs/kilauea.h for an example for +		 * a board specific implementation. +		 */ +#if defined(CONFIG_PPC4xx_DDR_METHOD_A) +		result = program_DQS_calibration_methodA(&ddrcal); +#else +		result = program_DQS_calibration_methodB(&ddrcal); +#endif + +		sync(); + +		/* +		 * Clear potential errors resulting from auto-calibration. +		 * If not done, then we could get an interrupt later on when +		 * exceptions are enabled. +		 */ +		set_mcsr(get_mcsr()); + +		val = ddrcal.rdcc;	/* RDCC from the best passing window */ + +		udelay(100); + +		if (verbose_lvl > 1) { +			char *tstr; +			switch ((val >> 30)) { +			case 0: +				if (result != 0) +					tstr = "T1"; +				else +					tstr = "N/A"; +				break; +			case 1: +				tstr = "T2"; +				break; +			case 2: +				tstr = "T3"; +				break; +			case 3: +				tstr = "T4"; +				break; +			default: +				tstr = "unknown"; +				break; +			} +			printf("** WRDTR(%d) CLKTR(%d), Wind (%d), best (%d), " +			       "max-min(0x%04x)(0x%04x), RDCC: %s\n", +				wdtr, clkp, result, best_result, +				ddrcal.rffd_min, ddrcal.rffd_max, tstr); +		} + +		/* +		 * The DQS calibration "result" is either "0" +		 * if no passing window was found, or is the +		 * size of the RFFD passing window. +		 */ +		/* +		 * want the lowest Read Sample Cycle Select +		 */ +		val = SDRAM_RDCC_RDSS_DECODE(val); +		debug("*** (%d) (%d) current_rdcc, best_rdcc\n", +			val, best_rdcc); + +		if ((result != 0) && +		    (val >= SDRAM_RDCC_RDSS_VAL(SDRAM_RDCC_RDSS_T2))) { +			if (((result == best_result) && (val < best_rdcc)) || +			    ((result > best_result) && (val <= best_rdcc))) { +				tcal.autocal.flags = 1; +				debug("*** (%d)(%d) result passed window " +					"size: 0x%08x, rqfd = 0x%08x, " +					"rffd = 0x%08x, rdcc = 0x%08x\n", +					wdtr, clkp, result, ddrcal.rqfd, +					ddrcal.rffd, ddrcal.rdcc); + +				/* +				 * Save the SDRAM_WRDTR and SDRAM_CLKTR +				 * settings for the largest returned +				 * RFFD passing window size. +				 */ +				best_rdcc = val; +				tcal.clocks.wrdtr = wdtr; +				tcal.clocks.clktr = clkp; +				tcal.clocks.rdcc = SDRAM_RDCC_RDSS_ENCODE(val); +				tcal.autocal.rqfd = ddrcal.rqfd; +				tcal.autocal.rffd = ddrcal.rffd; +				best_result = result; + +					if (verbose_lvl > 2) { +						printf("** (%d)(%d)  " +						       "best result: 0x%04x\n", +							wdtr, clkp, +							best_result); +						printf("** (%d)(%d)  " +						       "best WRDTR: 0x%04x\n", +							wdtr, clkp, +							tcal.clocks.wrdtr); +						printf("** (%d)(%d)  " +						       "best CLKTR: 0x%04x\n", +							wdtr, clkp, +							tcal.clocks.clktr); +						printf("** (%d)(%d)  " +						       "best RQDC: 0x%04x\n", +							wdtr, clkp, +							tcal.autocal.rqfd); +						printf("** (%d)(%d)  " +						       "best RFDC: 0x%04x\n", +							wdtr, clkp, +							tcal.autocal.rffd); +						printf("** (%d)(%d)  " +						       "best RDCC: 0x%08x\n", +							wdtr, clkp, +							(u32)tcal.clocks.rdcc); +						mfsdram(SDRAM_RTSR, val); +						printf("** (%d)(%d)  best " +						       "loop RTSR: 0x%08x\n", +							wdtr, clkp, val); +						mfsdram(SDRAM_FCSR, val); +						printf("** (%d)(%d)  best " +						       "loop FCSR: 0x%08x\n", +							wdtr, clkp, val); +					} +			} +		} /* if ((result != 0) && (val >= (ddr_rdss_opt()))) */ +		scan_list++; +	} /* while ((scan_list->wrdtr != -1) && (scan_list->clktr != -1)) */ + +	if (tcal.autocal.flags == 1) { +		if (verbose_lvl > 0) { +			printf("*** --------------\n"); +			printf("*** best_result window size: %d\n", +							best_result); +			printf("*** best_result WRDTR: 0x%04x\n", +							tcal.clocks.wrdtr); +			printf("*** best_result CLKTR: 0x%04x\n", +							tcal.clocks.clktr); +			printf("*** best_result RQFD: 0x%04x\n", +							tcal.autocal.rqfd); +			printf("*** best_result RFFD: 0x%04x\n", +							tcal.autocal.rffd); +			printf("*** best_result RDCC: 0x%04x\n", +							tcal.clocks.rdcc); +			printf("*** --------------\n"); +			printf("\n"); +		} + +		/* +		 * if got best passing result window, then lock in the +		 * best CLKTR, WRDTR, RQFD, and RFFD values +		 */ +		mfsdram(SDRAM_WRDTR, val); +		mtsdram(SDRAM_WRDTR, (val & +		    ~(SDRAM_WRDTR_LLWP_MASK | SDRAM_WRDTR_WTR_MASK)) | +		    ddr_wrdtr(SDRAM_WRDTR_LLWP_1_CYC | +					(tcal.clocks.wrdtr << 25))); + +		mtsdram(SDRAM_CLKTR, tcal.clocks.clktr << 30); + +		relock_memory_DLL(); + +		mfsdram(SDRAM_RQDC, rqdc_reg); +		rqdc_reg &= ~(SDRAM_RQDC_RQFD_MASK); +		mtsdram(SDRAM_RQDC, rqdc_reg | +				SDRAM_RQDC_RQFD_ENCODE(tcal.autocal.rqfd)); + +		mfsdram(SDRAM_RQDC, rqdc_reg); +		debug("*** best_result: read value SDRAM_RQDC 0x%08lx\n", +				rqdc_reg); + +#if defined(CONFIG_DDR_RFDC_FIXED) +		mtsdram(SDRAM_RFDC, CONFIG_DDR_RFDC_FIXED); +#else /* CONFIG_DDR_RFDC_FIXED */ +		mfsdram(SDRAM_RFDC, rfdc_reg); +		rfdc_reg &= ~(SDRAM_RFDC_RFFD_MASK); +		mtsdram(SDRAM_RFDC, rfdc_reg | +				SDRAM_RFDC_RFFD_ENCODE(tcal.autocal.rffd)); +#endif /* CONFIG_DDR_RFDC_FIXED */ + +		mfsdram(SDRAM_RFDC, rfdc_reg); +		debug("*** best_result: read value SDRAM_RFDC 0x%08lx\n", +				rfdc_reg); +		mfsdram(SDRAM_RDCC, val); +		debug("***  SDRAM_RDCC 0x%08x\n", val); +	} else { +		/* +		 * no valid windows were found +		 */ +		printf("DQS memory calibration window can not be determined, " +		       "terminating u-boot.\n"); +		ppc4xx_ibm_ddr2_register_dump(); +		spd_ddr_init_hang(); +	} + +	blank_string(strlen(str)); + +	return 0; +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_pci.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_pci.c new file mode 100644 index 00000000..33dc7258 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_pci.c @@ -0,0 +1,864 @@ +/* + * SPDX-License-Identifier:	GPL-2.0	IBM-pibs + * + *  File Name:   405gp_pci.c + * + *  Function:    Initialization code for the 405GP PCI Configuration regs. + * + *  Author:      Mark Game + * + *  Change Activity- + * + *  Date        Description of Change                                       BY + *  ---------   ---------------------                                       --- + *  09-Sep-98   Created                                                     MCG + *  02-Nov-98   Removed External arbiter selected message                   JWB + *  27-Nov-98   Zero out PTMBAR2 and disable in PTM2MS                      JWB + *  04-Jan-99   Zero out other unused PMM and PTM regs. Change bus scan     MCG + *              from (0 to n) to (1 to n). + *  17-May-99   Port to Walnut                                              JWB + *  17-Jun-99   Updated for VGA support                                     JWB + *  21-Jun-99   Updated to allow SRAM region to be a target from PCI bus    JWB + *  19-Jul-99   Updated for 405GP pass 1 errata #26 (Low PCI subsequent     MCG + *              target latency timer values are not supported). + *              Should be fixed in pass 2. + *  09-Sep-99   Removed use of PTM2 since the SRAM region no longer needs   JWB + *              to be a PCI target. Zero out PTMBAR2 and disable in PTM2MS. + *  10-Dec-99   Updated PCI_Write_CFG_Reg for pass2 errata #6               JWB + *  11-Jan-00   Ensure PMMxMAs disabled before setting PMMxLAs. This is not + *              really required after a reset since PMMxMAs are already + *	        disabled but is a good practice nonetheless.                JWB + *  12-Jun-01   stefan.roese@esd-electronics.com + *              - PCI host/adapter handling reworked + *  09-Jul-01   stefan.roese@esd-electronics.com + *              - PCI host now configures from device 0 (not 1) to max_dev, + *                (host configures itself) + *              - On CPCI-405 pci base address and size is generated from + *                SDRAM and FLASH size (CFG regs not used anymore) + *              - Some minor changes for CPCI-405-A (adapter version) + *  14-Sep-01   stefan.roese@esd-electronics.com + *              - CONFIG_PCI_SCAN_SHOW added to print pci devices upon startup + *  28-Sep-01   stefan.roese@esd-electronics.com + *              - Changed pci master configuration for linux compatibility + *                (no need for bios_fixup() anymore) + *  26-Feb-02   stefan.roese@esd-electronics.com + *              - Bug fixed in pci configuration (Andrew May) + *              - Removed pci class code init for CPCI405 board + *  15-May-02   stefan.roese@esd-electronics.com + *              - New vga device handling + *  29-May-02   stefan.roese@esd-electronics.com + *              - PCI class code init added (if defined) + *----------------------------------------------------------------------------*/ + +#include <common.h> +#include <command.h> +#include <asm/4xx_pci.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> + +#ifdef CONFIG_PCI + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_405GP) || defined(CONFIG_405EP) + +#if defined(CONFIG_PMC405) +ushort pmc405_pci_subsys_deviceid(void); +#endif + +/*#define DEBUG*/ + +/* + * Board-specific pci initialization + * Platform code can reimplement pci_pre_init() if needed + */ +int __pci_pre_init(struct pci_controller *hose) +{ +#if defined(CONFIG_405EP) +	/* +	 * Enable the internal PCI arbiter by default. +	 * +	 * On 405EP CPUs the internal arbiter can be controlled +	 * by the I2C strapping EEPROM. If you want to do so +	 * or if you want to disable the arbiter pci_pre_init() +	 * must be reimplemented without enabling the arbiter. +	 * The arbiter is enabled in this place because of +	 * compatibility reasons. +	 */ +	mtdcr(CPC0_PCI, mfdcr(CPC0_PCI) | CPC0_PCI_ARBIT_EN); +#endif /* CONFIG_405EP */ + +	return 1; +} +int pci_pre_init(struct pci_controller *hose) +	__attribute__((weak, alias("__pci_pre_init"))); + +int __is_pci_host(struct pci_controller *hose) +{ +#if defined(CONFIG_405GP) +	if (mfdcr(CPC0_PSR) & PSR_PCI_ARBIT_EN) +		return 1; +#elif defined (CONFIG_405EP) +	if (mfdcr(CPC0_PCI) & CPC0_PCI_ARBIT_EN) +		return 1; +#endif +	return 0; +} +int is_pci_host(struct pci_controller *hose) __attribute__((weak, alias("__is_pci_host"))); + +/*-----------------------------------------------------------------------------+ + * pci_init.  Initializes the 405GP PCI Configuration regs. + *-----------------------------------------------------------------------------*/ +void pci_405gp_init(struct pci_controller *hose) +{ +	int i, reg_num = 0; +	bd_t *bd = gd->bd; + +	unsigned short temp_short; +	unsigned long ptmpcila[2] = {CONFIG_SYS_PCI_PTM1PCI, CONFIG_SYS_PCI_PTM2PCI}; +#if defined(CONFIG_PCI_4xx_PTM_OVERWRITE) +	char *ptmla_str, *ptmms_str; +#endif +	unsigned long ptmla[2]    = {CONFIG_SYS_PCI_PTM1LA, CONFIG_SYS_PCI_PTM2LA}; +	unsigned long ptmms[2]    = {CONFIG_SYS_PCI_PTM1MS, CONFIG_SYS_PCI_PTM2MS}; +#if defined(CONFIG_PIP405) || defined (CONFIG_MIP405) +	unsigned long pmmla[3]    = {0x80000000, 0xA0000000, 0}; +	unsigned long pmmma[3]    = {0xE0000001, 0xE0000001, 0}; +	unsigned long pmmpcila[3] = {0x80000000, 0x00000000, 0}; +	unsigned long pmmpciha[3] = {0x00000000, 0x00000000, 0}; +#else +	unsigned long pmmla[3]    = {0x80000000, 0,0}; +	unsigned long pmmma[3]    = {0xC0000001, 0,0}; +	unsigned long pmmpcila[3] = {0x80000000, 0,0}; +	unsigned long pmmpciha[3] = {0x00000000, 0,0}; +#endif +#ifdef CONFIG_PCI_PNP +#if (CONFIG_PCI_HOST == PCI_HOST_AUTO) +	char *s; +#endif +#endif + +#if defined(CONFIG_PCI_4xx_PTM_OVERWRITE) +	ptmla_str = getenv("ptm1la"); +	ptmms_str = getenv("ptm1ms"); +	if(NULL != ptmla_str && NULL != ptmms_str ) { +		ptmla[0] = simple_strtoul (ptmla_str, NULL, 16); +		ptmms[0] = simple_strtoul (ptmms_str, NULL, 16); +	} + +	ptmla_str = getenv("ptm2la"); +	ptmms_str = getenv("ptm2ms"); +	if(NULL != ptmla_str && NULL != ptmms_str ) { +		ptmla[1] = simple_strtoul (ptmla_str, NULL, 16); +		ptmms[1] = simple_strtoul (ptmms_str, NULL, 16); +	} +#endif + +	/* +	 * Register the hose +	 */ +	hose->first_busno = 0; +	hose->last_busno = 0xff; + +	/* ISA/PCI I/O space */ +	pci_set_region(hose->regions + reg_num++, +		       MIN_PCI_PCI_IOADDR, +		       MIN_PLB_PCI_IOADDR, +		       0x10000, +		       PCI_REGION_IO); + +	/* PCI I/O space */ +	pci_set_region(hose->regions + reg_num++, +		       0x00800000, +		       0xe8800000, +		       0x03800000, +		       PCI_REGION_IO); + +	reg_num = 2; + +	/* Memory spaces */ +	for (i=0; i<2; i++) +		if (ptmms[i] & 1) +		{ +			if (!i) hose->pci_fb = hose->regions + reg_num; + +			pci_set_region(hose->regions + reg_num++, +				       ptmpcila[i], ptmla[i], +				       ~(ptmms[i] & 0xfffff000) + 1, +				       PCI_REGION_MEM | +				       PCI_REGION_SYS_MEMORY); +		} + +	/* PCI memory spaces */ +	for (i=0; i<3; i++) +		if (pmmma[i] & 1) +		{ +			pci_set_region(hose->regions + reg_num++, +				       pmmpcila[i], pmmla[i], +				       ~(pmmma[i] & 0xfffff000) + 1, +				       PCI_REGION_MEM); +		} + +	hose->region_count = reg_num; + +	pci_setup_indirect(hose, +			   PCICFGADR, +			   PCICFGDATA); + +	if (hose->pci_fb) +		pciauto_region_init(hose->pci_fb); + +	/* Let board change/modify hose & do initial checks */ +	if (pci_pre_init(hose) == 0) { +		printf("PCI: Board-specific initialization failed.\n"); +		printf("PCI: Configuration aborted.\n"); +		return; +	} + +	pci_register_hose(hose); + +	/*--------------------------------------------------------------------------+ +	 * 405GP PCI Master configuration. +	 * Map one 512 MB range of PLB/processor addresses to PCI memory space. +	 * PLB address 0x80000000-0xBFFFFFFF ==> PCI address 0x80000000-0xBFFFFFFF +	 * Use byte reversed out routines to handle endianess. +	 *--------------------------------------------------------------------------*/ +	out32r(PMM0MA,    (pmmma[0]&~0x1)); /* disable, configure PMMxLA, PMMxPCILA first */ +	out32r(PMM0LA,    pmmla[0]); +	out32r(PMM0PCILA, pmmpcila[0]); +	out32r(PMM0PCIHA, pmmpciha[0]); +	out32r(PMM0MA,    pmmma[0]); + +	/*--------------------------------------------------------------------------+ +	 * PMM1 is not used.  Initialize them to zero. +	 *--------------------------------------------------------------------------*/ +	out32r(PMM1MA,    (pmmma[1]&~0x1)); +	out32r(PMM1LA,    pmmla[1]); +	out32r(PMM1PCILA, pmmpcila[1]); +	out32r(PMM1PCIHA, pmmpciha[1]); +	out32r(PMM1MA,    pmmma[1]); + +	/*--------------------------------------------------------------------------+ +	 * PMM2 is not used.  Initialize them to zero. +	 *--------------------------------------------------------------------------*/ +	out32r(PMM2MA,    (pmmma[2]&~0x1)); +	out32r(PMM2LA,    pmmla[2]); +	out32r(PMM2PCILA, pmmpcila[2]); +	out32r(PMM2PCIHA, pmmpciha[2]); +	out32r(PMM2MA,    pmmma[2]); + +	/*--------------------------------------------------------------------------+ +	 * 405GP PCI Target configuration.  (PTM1) +	 * Note: PTM1MS is hardwire enabled but we set the enable bit anyway. +	 *--------------------------------------------------------------------------*/ +	out32r(PTM1LA,    ptmla[0]);         /* insert address                     */ +	out32r(PTM1MS,    ptmms[0]);         /* insert size, enable bit is 1       */ +	pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_1, ptmpcila[0]); + +	/*--------------------------------------------------------------------------+ +	 * 405GP PCI Target configuration.  (PTM2) +	 *--------------------------------------------------------------------------*/ +	out32r(PTM2LA, ptmla[1]);            /* insert address                     */ +	pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_2, ptmpcila[1]); + +	if (ptmms[1] == 0) +	{ +		out32r(PTM2MS,    0x00000001);   /* set enable bit                     */ +		pci_write_config_dword(PCIDEVID_405GP, PCI_BASE_ADDRESS_2, 0x00000000); +		out32r(PTM2MS,    0x00000000);   /* disable                            */ +	} +	else +	{ +		out32r(PTM2MS, ptmms[1]);        /* insert size, enable bit is 1       */ +	} + +	/* +	 * Insert Subsystem Vendor and Device ID +	 */ +	pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_VENDOR_ID, CONFIG_SYS_PCI_SUBSYS_VENDORID); +#ifdef CONFIG_CPCI405 +	if (is_pci_host(hose)) +		pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CONFIG_SYS_PCI_SUBSYS_DEVICEID); +	else +		pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CONFIG_SYS_PCI_SUBSYS_DEVICEID2); +#else +	pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CONFIG_SYS_PCI_SUBSYS_DEVICEID); +#endif + +	/* +	 * Insert Class-code +	 */ +#ifdef CONFIG_SYS_PCI_CLASSCODE +	pci_write_config_word(PCIDEVID_405GP, PCI_CLASS_SUB_CODE, CONFIG_SYS_PCI_CLASSCODE); +#endif /* CONFIG_SYS_PCI_CLASSCODE */ + +	/*--------------------------------------------------------------------------+ +	 * If PCI speed = 66MHz, set 66MHz capable bit. +	 *--------------------------------------------------------------------------*/ +	if (bd->bi_pci_busfreq >= 66000000) { +		pci_read_config_word(PCIDEVID_405GP, PCI_STATUS, &temp_short); +		pci_write_config_word(PCIDEVID_405GP,PCI_STATUS,(temp_short|PCI_STATUS_66MHZ)); +	} + +#if (CONFIG_PCI_HOST != PCI_HOST_ADAPTER) +#if (CONFIG_PCI_HOST == PCI_HOST_AUTO) +	if (is_pci_host(hose) || +	    (((s = getenv("pciscan")) != NULL) && (strcmp(s, "yes") == 0))) +#endif +	{ +		/*--------------------------------------------------------------------------+ +		 * Write the 405GP PCI Configuration regs. +		 * Enable 405GP to be a master on the PCI bus (PMM). +		 * Enable 405GP to act as a PCI memory target (PTM). +		 *--------------------------------------------------------------------------*/ +		pci_read_config_word(PCIDEVID_405GP, PCI_COMMAND, &temp_short); +		pci_write_config_word(PCIDEVID_405GP, PCI_COMMAND, temp_short | +				      PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); +	} +#endif + +#if defined(CONFIG_405EP) +	/* +	 * on ppc405ep vendor/device id is not set +	 * The user manual says 0x1014 (IBM) / 0x0156 (405GP!) +	 * are the correct values. +	 */ +	pci_write_config_word(PCIDEVID_405GP, PCI_VENDOR_ID, PCI_VENDOR_ID_IBM); +	pci_write_config_word(PCIDEVID_405GP, +			      PCI_DEVICE_ID, PCI_DEVICE_ID_IBM_405GP); +#endif + +	/* +	 * Set HCE bit (Host Configuration Enabled) +	 */ +	pci_read_config_word(PCIDEVID_405GP, PCIBRDGOPT2, &temp_short); +	pci_write_config_word(PCIDEVID_405GP, PCIBRDGOPT2, (temp_short | 0x0001)); + +#ifdef CONFIG_PCI_PNP +	/*--------------------------------------------------------------------------+ +	 * Scan the PCI bus and configure devices found. +	 *--------------------------------------------------------------------------*/ +#if (CONFIG_PCI_HOST == PCI_HOST_AUTO) +	if (is_pci_host(hose) || +	    (((s = getenv("pciscan")) != NULL) && (strcmp(s, "yes") == 0))) +#endif +	{ +#ifdef CONFIG_PCI_SCAN_SHOW +		printf("PCI:   Bus Dev VenId DevId Class Int\n"); +#endif +		hose->last_busno = pci_hose_scan(hose); +	} +#endif  /* CONFIG_PCI_PNP */ + +} + +/* + * drivers/pci/pci.c skips every host bridge but the 405GP since it could + * be set as an Adapter. + * + * I (Andrew May) don't know what we should do here, but I don't want + * the auto setup of a PCI device disabling what is done pci_405gp_init + * as has happened before. + */ +void pci_405gp_setup_bridge(struct pci_controller *hose, pci_dev_t dev, +			    struct pci_config_table *entry) +{ +#ifdef DEBUG +	printf("405gp_setup_bridge\n"); +#endif +} + +/* + * + */ + +void pci_405gp_fixup_irq(struct pci_controller *hose, pci_dev_t dev) +{ +	unsigned char int_line = 0xff; + +	/* +	 * Write pci interrupt line register (cpci405 specific) +	 */ +	switch (PCI_DEV(dev) & 0x03) +	{ +	case 0: +		int_line = 27 + 2; +		break; +	case 1: +		int_line = 27 + 3; +		break; +	case 2: +		int_line = 27 + 0; +		break; +	case 3: +		int_line = 27 + 1; +		break; +	} + +	pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE, int_line); +} + +void pci_405gp_setup_vga(struct pci_controller *hose, pci_dev_t dev, +			 struct pci_config_table *entry) +{ +	unsigned int cmdstat = 0; + +	pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_prefetch, hose->pci_io); + +	/* always enable io space on vga boards */ +	pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat); +	cmdstat |= PCI_COMMAND_IO; +	pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat); +} + +#if !(defined(CONFIG_PIP405) || defined (CONFIG_MIP405)) && !(defined (CONFIG_SC3)) + +/* + *As is these functs get called out of flash Not a horrible + *thing, but something to keep in mind. (no statics?) + */ +static struct pci_config_table pci_405gp_config_table[] = { +/*if VendID is 0 it terminates the table search (ie Walnut)*/ +#ifdef CONFIG_SYS_PCI_SUBSYS_VENDORID +	{CONFIG_SYS_PCI_SUBSYS_VENDORID, PCI_ANY_ID, PCI_CLASS_BRIDGE_HOST, +	 PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_bridge}, +#endif +	{PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA, +	 PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_vga}, + +	{PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NOT_DEFINED_VGA, +	 PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pci_405gp_setup_vga}, + +	{ } +}; + +static struct pci_controller hose = { +	fixup_irq: pci_405gp_fixup_irq, +	config_table: pci_405gp_config_table, +}; + +void pci_init_board(void) +{ +	/*we want the ptrs to RAM not flash (ie don't use init list)*/ +	hose.fixup_irq    = pci_405gp_fixup_irq; +	hose.config_table = pci_405gp_config_table; +	pci_405gp_init(&hose); +} + +#endif + +#endif /* CONFIG_405GP */ + +/*-----------------------------------------------------------------------------+ + * CONFIG_440 + *-----------------------------------------------------------------------------*/ +#if defined(CONFIG_440) + +#if defined(CONFIG_SYS_PCI_MASTER_INIT) || defined(CONFIG_SYS_PCI_TARGET_INIT) +static struct pci_controller ppc440_hose = {0}; +#endif + +/* + * This routine is called to determine if a pci scan should be + * performed. With various hardware environments (especially cPCI and + * PPMC) it's insufficient to depend on the state of the arbiter enable + * bit in the strap register, or generic host/adapter assumptions. + * + * Rather than hard-code a bad assumption in the general 440 code, the + * 440 pci code requires the board to decide at runtime. + * + * Return 0 for adapter mode, non-zero for host (monarch) mode. + * + * Weak default implementation: "Normal" boards implement the PCI + * host functionality. This can be overridden for PCI adapter boards. + */ +int __is_pci_host(struct pci_controller *hose) +{ +	return 1; +} +int is_pci_host(struct pci_controller *hose) +	__attribute__((weak, alias("__is_pci_host"))); + +#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ +    defined(CONFIG_440GR) || defined(CONFIG_440GRX) + +#if defined(CONFIG_SYS_PCI_TARGET_INIT) +/* + * pci_target_init + * + * The bootstrap configuration provides default settings for the pci + * inbound map (PIM). But the bootstrap config choices are limited and + * may not be sufficient for a given board. + */ +void __pci_target_init(struct pci_controller *hose) +{ +	/* +	 * Set up Direct MMIO registers +	 */ + +	/* +	 * PowerPC440 EP PCI Master configuration. +	 * Map one 1Gig range of PLB/processor addresses to PCI memory space. +	 * PLB address 0xA0000000-0xDFFFFFFF ==> PCI address 0xA0000000-0xDFFFFFFF +	 * Use byte reversed out routines to handle endianess. +	 * Make this region non-prefetchable. +	 */ +	/* PMM0 Mask/Attribute - disabled b4 setting */ +	out_le32((void *)PCIL0_PMM0MA, 0x00000000); +	/* PMM0 Local Address */ +	out_le32((void *)PCIL0_PMM0LA, CONFIG_SYS_PCI_MEMBASE); +	/* PMM0 PCI Low Address */ +	out_le32((void *)PCIL0_PMM0PCILA, CONFIG_SYS_PCI_MEMBASE); +	/* PMM0 PCI High Address */ +	out_le32((void *)PCIL0_PMM0PCIHA, 0x00000000); +	/* 512M + No prefetching, and enable region */ +	out_le32((void *)PCIL0_PMM0MA, 0xE0000001); + +	/* PMM1 Mask/Attribute - disabled b4 setting */ +	out_le32((void *)PCIL0_PMM1MA, 0x00000000); +	/* PMM1 Local Address */ +	out_le32((void *)PCIL0_PMM1LA, CONFIG_SYS_PCI_MEMBASE2); +	/* PMM1 PCI Low Address */ +	out_le32((void *)PCIL0_PMM1PCILA, CONFIG_SYS_PCI_MEMBASE2); +	/* PMM1 PCI High Address */ +	out_le32((void *)PCIL0_PMM1PCIHA, 0x00000000); +	/* 512M + No prefetching, and enable region */ +	out_le32((void *)PCIL0_PMM1MA, 0xE0000001); + +	out_le32((void *)PCIL0_PTM1MS, 0x00000001); /* Memory Size/Attribute */ +	out_le32((void *)PCIL0_PTM1LA, 0);	/* Local Addr. Reg */ +	out_le32((void *)PCIL0_PTM2MS, 0);	/* Memory Size/Attribute */ +	out_le32((void *)PCIL0_PTM2LA, 0);	/* Local Addr. Reg */ + +	/* +	 * Set up Configuration registers +	 */ + +	/* Program the board's subsystem id/vendor id */ +	pci_write_config_word(0, PCI_SUBSYSTEM_VENDOR_ID, +			      CONFIG_SYS_PCI_SUBSYS_VENDORID); +	pci_write_config_word(0, PCI_SUBSYSTEM_ID, CONFIG_SYS_PCI_SUBSYS_ID); + +	/* Configure command register as bus master */ +	pci_write_config_word(0, PCI_COMMAND, PCI_COMMAND_MASTER); + +	/* 240nS PCI clock */ +	pci_write_config_word(0, PCI_LATENCY_TIMER, 1); + +	/* No error reporting */ +	pci_write_config_word(0, PCI_ERREN, 0); + +	pci_write_config_dword(0, PCI_BRDGOPT2, 0x00000101); +} +#endif /* CONFIG_SYS_PCI_TARGET_INIT */ + +/* + * pci_pre_init + * + * This routine is called just prior to registering the hose and gives + * the board the opportunity to check things. Returning a value of zero + * indicates that things are bad & PCI initialization should be aborted. + * + * Different boards may wish to customize the pci controller structure + * (add regions, override default access routines, etc) or perform + * certain pre-initialization actions. + * + */ +int __pci_pre_init(struct pci_controller *hose) +{ +	u32 reg; + +	/* +	 * Set priority for all PLB3 devices to 0. +	 * Set PLB3 arbiter to fair mode. +	 */ +	mfsdr(SDR0_AMP1, reg); +	mtsdr(SDR0_AMP1, (reg & 0x000000FF) | 0x0000FF00); +	reg = mfdcr(PLB3A0_ACR); +	mtdcr(PLB3A0_ACR, reg | 0x80000000); + +	/* +	 * Set priority for all PLB4 devices to 0. +	 */ +	mfsdr(SDR0_AMP0, reg); +	mtsdr(SDR0_AMP0, (reg & 0x000000FF) | 0x0000FF00); +	reg = mfdcr(PLB4A0_ACR) | 0xa0000000; +	mtdcr(PLB4A0_ACR, reg); + +	/* +	 * Set Nebula PLB4 arbiter to fair mode. +	 */ +	/* Segment0 */ +	reg = (mfdcr(PLB4A0_ACR) & ~PLB4Ax_ACR_PPM_MASK) | PLB4Ax_ACR_PPM_FAIR; +	reg = (reg & ~PLB4Ax_ACR_HBU_MASK) | PLB4Ax_ACR_HBU_ENABLED; +	reg = (reg & ~PLB4Ax_ACR_RDP_MASK) | PLB4Ax_ACR_RDP_4DEEP; +	reg = (reg & ~PLB4Ax_ACR_WRP_MASK) | PLB4Ax_ACR_WRP_2DEEP; +	mtdcr(PLB4A0_ACR, reg); + +	/* Segment1 */ +	reg = (mfdcr(PLB4A1_ACR) & ~PLB4Ax_ACR_PPM_MASK) | PLB4Ax_ACR_PPM_FAIR; +	reg = (reg & ~PLB4Ax_ACR_HBU_MASK) | PLB4Ax_ACR_HBU_ENABLED; +	reg = (reg & ~PLB4Ax_ACR_RDP_MASK) | PLB4Ax_ACR_RDP_4DEEP; +	reg = (reg & ~PLB4Ax_ACR_WRP_MASK) | PLB4Ax_ACR_WRP_2DEEP; +	mtdcr(PLB4A1_ACR, reg); + +#if defined(CONFIG_SYS_PCI_BOARD_FIXUP_IRQ) +	hose->fixup_irq = board_pci_fixup_irq; +#endif + +	return 1; +} + +#else /* defined(CONFIG_440EP) ... */ + +#if defined(CONFIG_SYS_PCI_TARGET_INIT) +void __pci_target_init(struct pci_controller * hose) +{ +	/* +	 * Disable everything +	 */ +	out_le32((void *)PCIL0_PIM0SA, 0); /* disable */ +	out_le32((void *)PCIL0_PIM1SA, 0); /* disable */ +	out_le32((void *)PCIL0_PIM2SA, 0); /* disable */ +	out_le32((void *)PCIL0_EROMBA, 0); /* disable expansion rom */ + +	/* +	 * Map all of SDRAM to PCI address 0x0000_0000. Note that the 440 +	 * strapping options do not support sizes such as 128/256 MB. +	 */ +	out_le32((void *)PCIL0_PIM0LAL, CONFIG_SYS_SDRAM_BASE); +	out_le32((void *)PCIL0_PIM0LAH, 0); +	out_le32((void *)PCIL0_PIM0SA, ~(gd->ram_size - 1) | 1); +	out_le32((void *)PCIL0_BAR0, 0); + +	/* +	 * Program the board's subsystem id/vendor id +	 */ +	out_le16((void *)PCIL0_SBSYSVID, CONFIG_SYS_PCI_SUBSYS_VENDORID); +	out_le16((void *)PCIL0_SBSYSID, CONFIG_SYS_PCI_SUBSYS_DEVICEID); + +	out_le16((void *)PCIL0_CMD, in_le16((void *)PCIL0_CMD) | +		 PCI_COMMAND_MEMORY); +} +#endif /* CONFIG_SYS_PCI_TARGET_INIT */ + +int __pci_pre_init(struct pci_controller *hose) +{ +	/* +	 * This board is always configured as the host & requires the +	 * PCI arbiter to be enabled. +	 */ +	if (!pci_arbiter_enabled()) { +		printf("PCI: PCI Arbiter disabled!\n"); +		return 0; +	} + +	return 1; +} + +#endif /* defined(CONFIG_440EP) ... */ + +#if defined(CONFIG_SYS_PCI_TARGET_INIT) +void pci_target_init(struct pci_controller * hose) +	__attribute__((weak, alias("__pci_target_init"))); +#endif /* CONFIG_SYS_PCI_TARGET_INIT */ + +int pci_pre_init(struct pci_controller *hose) +	__attribute__((weak, alias("__pci_pre_init"))); + +#if defined(CONFIG_SYS_PCI_MASTER_INIT) +void __pci_master_init(struct pci_controller *hose) +{ +	u16 reg; + +	/* +	 * Write the PowerPC440 EP PCI Configuration regs. +	 * Enable PowerPC440 EP to be a master on the PCI bus (PMM). +	 * Enable PowerPC440 EP to act as a PCI memory target (PTM). +	 */ +	pci_read_config_word(0, PCI_COMMAND, ®); +	pci_write_config_word(0, PCI_COMMAND, reg | +			      PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); +} +void pci_master_init(struct pci_controller *hose) +	__attribute__((weak, alias("__pci_master_init"))); +#endif /* CONFIG_SYS_PCI_MASTER_INIT */ + +#if defined(CONFIG_SYS_PCI_MASTER_INIT) || defined(CONFIG_SYS_PCI_TARGET_INIT) +static int pci_440_init (struct pci_controller *hose) +{ +	int reg_num = 0; + +#ifndef CONFIG_DISABLE_PISE_TEST +	/*--------------------------------------------------------------------------+ +	 * The PCI initialization sequence enable bit must be set ... if not abort +	 * pci setup since updating the bit requires chip reset. +	 *--------------------------------------------------------------------------*/ +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) || defined(CONFIG_440SPE) +	unsigned long strap; + +	mfsdr(SDR0_SDSTP1,strap); +	if ((strap & SDR0_SDSTP1_PISE_MASK) == 0) { +		printf("PCI: SDR0_STRP1[PISE] not set.\n"); +		printf("PCI: Configuration aborted.\n"); +		return -1; +	} +#elif defined(CONFIG_440GP) +	unsigned long strap; + +	strap = mfdcr(CPC0_STRP1); +	if ((strap & CPC0_STRP1_PISE_MASK) == 0) { +		printf("PCI: CPC0_STRP1[PISE] not set.\n"); +		printf("PCI: Configuration aborted.\n"); +		return -1; +	} +#endif +#endif /* CONFIG_DISABLE_PISE_TEST */ + +	/*--------------------------------------------------------------------------+ +	 * PCI controller init +	 *--------------------------------------------------------------------------*/ +	hose->first_busno = 0; +	hose->last_busno = 0; + +	/* PCI I/O space */ +	pci_set_region(hose->regions + reg_num++, +		       0x00000000, +		       PCIL0_IOBASE, +		       0x10000, +		       PCI_REGION_IO); + +	/* PCI memory space */ +	pci_set_region(hose->regions + reg_num++, +		       CONFIG_SYS_PCI_TARGBASE, +		       CONFIG_SYS_PCI_MEMBASE, +#ifdef CONFIG_SYS_PCI_MEMSIZE +		       CONFIG_SYS_PCI_MEMSIZE, +#else +		       0x10000000, +#endif +		       PCI_REGION_MEM ); + +#if defined(CONFIG_PCI_SYS_MEM_BUS) && defined(CONFIG_PCI_SYS_MEM_PHYS) && \ +	defined(CONFIG_PCI_SYS_MEM_SIZE) +	/* System memory space */ +	pci_set_region(hose->regions + reg_num++, +		       CONFIG_PCI_SYS_MEM_BUS, +		       CONFIG_PCI_SYS_MEM_PHYS, +		       CONFIG_PCI_SYS_MEM_SIZE, +		       PCI_REGION_MEM | PCI_REGION_SYS_MEMORY ); +#endif + +	hose->region_count = reg_num; + +	pci_setup_indirect(hose, PCIL0_CFGADR, PCIL0_CFGDATA); + +	/* Let board change/modify hose & do initial checks */ +	if (pci_pre_init(hose) == 0) { +		printf("PCI: Board-specific initialization failed.\n"); +		printf("PCI: Configuration aborted.\n"); +		return -1; +	} + +	pci_register_hose( hose ); + +	/*--------------------------------------------------------------------------+ +	 * PCI target init +	 *--------------------------------------------------------------------------*/ +#if defined(CONFIG_SYS_PCI_TARGET_INIT) +	pci_target_init(hose);                /* Let board setup pci target */ +#else +	out16r( PCIL0_SBSYSVID, CONFIG_SYS_PCI_SUBSYS_VENDORID ); +	out16r( PCIL0_SBSYSID, CONFIG_SYS_PCI_SUBSYS_ID ); +	out16r( PCIL0_CLS, 0x00060000 ); /* Bridge, host bridge */ +#endif + +#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT) +	out32r( PCIL0_BRDGOPT1, 0x04000060 );               /* PLB Rq pri highest   */ +	out32r( PCIL0_BRDGOPT2, in32(PCIL0_BRDGOPT2) | 0x83 ); /* Enable host config, clear Timeout, ensure int src1  */ +#elif defined(PCIL0_BRDGOPT1) +	out32r( PCIL0_BRDGOPT1, 0x10000060 );               /* PLB Rq pri highest   */ +	out32r( PCIL0_BRDGOPT2, in32(PCIL0_BRDGOPT2) | 1 ); /* Enable host config   */ +#endif + +	/*--------------------------------------------------------------------------+ +	 * PCI master init: default is one 256MB region for PCI memory: +	 * 0x3_00000000 - 0x3_0FFFFFFF  ==> CONFIG_SYS_PCI_MEMBASE +	 *--------------------------------------------------------------------------*/ +#if defined(CONFIG_SYS_PCI_MASTER_INIT) +	pci_master_init(hose);          /* Let board setup pci master */ +#else +	out32r( PCIL0_POM0SA, 0 ); /* disable */ +	out32r( PCIL0_POM1SA, 0 ); /* disable */ +	out32r( PCIL0_POM2SA, 0 ); /* disable */ +#if defined(CONFIG_440SPE) +	out32r( PCIL0_POM0LAL, 0x10000000 ); +	out32r( PCIL0_POM0LAH, 0x0000000c ); +#elif defined(CONFIG_460EX) || defined(CONFIG_460GT) +	out32r( PCIL0_POM0LAL, 0x20000000 ); +	out32r( PCIL0_POM0LAH, 0x0000000c ); +#else +	out32r( PCIL0_POM0LAL, 0x00000000 ); +	out32r( PCIL0_POM0LAH, 0x00000003 ); +#endif +	out32r( PCIL0_POM0PCIAL, CONFIG_SYS_PCI_MEMBASE ); +	out32r( PCIL0_POM0PCIAH, 0x00000000 ); +	out32r( PCIL0_POM0SA, 0xf0000001 ); /* 256MB, enabled */ +	out32r( PCIL0_STS, in32r( PCIL0_STS ) & ~0x0000fff8 ); +#endif + +	/*--------------------------------------------------------------------------+ +	 * PCI host configuration -- we don't make any assumptions here ... the +	 * _board_must_indicate_ what to do -- there's just too many runtime +	 * scenarios in environments like cPCI, PPMC, etc. to make a determination +	 * based on hard-coded values or state of arbiter enable. +	 *--------------------------------------------------------------------------*/ +	if (is_pci_host(hose)) { +#ifdef CONFIG_PCI_SCAN_SHOW +		printf("PCI:   Bus Dev VenId DevId Class Int\n"); +#endif +#if !defined(CONFIG_440EP) && !defined(CONFIG_440GR) && \ +    !defined(CONFIG_440EPX) && !defined(CONFIG_440GRX) +		out16r( PCIL0_CMD, in16r( PCIL0_CMD ) | PCI_COMMAND_MASTER); +#endif +		hose->last_busno = pci_hose_scan(hose); +	} +	return hose->last_busno; +} +#endif + +void pci_init_board(void) +{ +	int busno = 0; + +	/* +	 * Only init PCI when either master or target functionality +	 * is selected. +	 */ +#if defined(CONFIG_SYS_PCI_MASTER_INIT) || defined(CONFIG_SYS_PCI_TARGET_INIT) +	busno = pci_440_init(&ppc440_hose); +	if (busno < 0) +		return; +#endif +#if (defined(CONFIG_440SPE) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT)) && \ +    !defined(CONFIG_PCI_DISABLE_PCIE) +	pcie_setup_hoses(busno + 1); +#endif +} + +#endif /* CONFIG_440 */ + +#if defined(CONFIG_405EX) +void pci_init_board(void) +{ +#ifdef CONFIG_PCI_SCAN_SHOW +	printf("PCI:   Bus Dev VenId DevId Class Int\n"); +#endif +	pcie_setup_hoses(0); +} +#endif /* CONFIG_405EX */ + +#endif /* CONFIG_PCI */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_pcie.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_pcie.c new file mode 100644 index 00000000..f0f3462e --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_pcie.c @@ -0,0 +1,1280 @@ +/* + * (C) Copyright 2006 - 2008 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (c) 2005 Cisco Systems.  All rights reserved. + * Roland Dreier <rolandd@cisco.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <pci.h> +#include <asm/ppc4xx.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/errno.h> + +#if (defined(CONFIG_440SPE) || defined(CONFIG_405EX) ||	\ +    defined(CONFIG_460EX) || defined(CONFIG_460GT)) && \ +    defined(CONFIG_PCI) && !defined(CONFIG_PCI_DISABLE_PCIE) + +#include <asm/4xx_pcie.h> + +enum { +	PTYPE_ENDPOINT		= 0x0, +	PTYPE_LEGACY_ENDPOINT	= 0x1, +	PTYPE_ROOT_PORT		= 0x4, + +	LNKW_X1			= 0x1, +	LNKW_X4			= 0x4, +	LNKW_X8			= 0x8 +}; + +static struct pci_controller pcie_hose[CONFIG_SYS_PCIE_NR_PORTS]; + +/* + * Per default, all cards are present, so we need to check if the + * link comes up. + */ +int __board_pcie_card_present(int port) +{ +	return 1; +} +int board_pcie_card_present(int port) +	__attribute__((weak, alias("__board_pcie_card_present"))); + +/* + * Some boards have runtime detection of the first and last PCIe + * slot used, so let's provide weak default functions for the + * common version. + */ +int __board_pcie_first(void) +{ +	return 0; +} +int board_pcie_first(void) +	__attribute__((weak, alias("__board_pcie_first"))); + +int __board_pcie_last(void) +{ +	return CONFIG_SYS_PCIE_NR_PORTS - 1; +} +int board_pcie_last(void) +	__attribute__((weak, alias("__board_pcie_last"))); + +void __board_pcie_setup_port(int port, int rootpoint) +{ +	/* noting in this weak default implementation */ +} +void board_pcie_setup_port(int port, int rootpoint) +	__attribute__((weak, alias("__board_pcie_setup_port"))); + +void pcie_setup_hoses(int busno) +{ +	struct pci_controller *hose; +	int i, bus; +	int ret = 0; +	char *env; +	unsigned int delay; +	int first = board_pcie_first(); +	int last = board_pcie_last(); + +	/* +	 * Assume we're called after the PCI(X) hose(s) are initialized, +	 * which takes bus ID 0... and therefore start numbering PCIe's +	 * from the next number. +	 */ +	bus = busno; + +	for (i = first; i <= last; i++) { +		/* +		 * Some boards (e.g. Katmai) can detects via hardware +		 * if a PCIe card is plugged, so let's check this. +		 */ +		if (!board_pcie_card_present(i)) +			continue; + +		if (is_end_point(i)) { +			board_pcie_setup_port(i, 0); +			ret = ppc4xx_init_pcie_endport(i); +		} else { +			board_pcie_setup_port(i, 1); +			ret = ppc4xx_init_pcie_rootport(i); +		} +		if (ret == -ENODEV) +			continue; +		if (ret) { +			printf("PCIE%d: initialization as %s failed\n", i, +			       is_end_point(i) ? "endpoint" : "root-complex"); +			continue; +		} + +		hose = &pcie_hose[i]; +		hose->first_busno = bus; +		hose->last_busno = bus; +		hose->current_busno = bus; + +		/* setup mem resource */ +		pci_set_region(hose->regions + 0, +			       CONFIG_SYS_PCIE_MEMBASE + i * CONFIG_SYS_PCIE_MEMSIZE, +			       CONFIG_SYS_PCIE_MEMBASE + i * CONFIG_SYS_PCIE_MEMSIZE, +			       CONFIG_SYS_PCIE_MEMSIZE, +			       PCI_REGION_MEM); +		hose->region_count = 1; +		pci_register_hose(hose); + +		if (is_end_point(i)) { +			ppc4xx_setup_pcie_endpoint(hose, i); +			/* +			 * Reson for no scanning is endpoint can not generate +			 * upstream configuration accesses. +			 */ +		} else { +			ppc4xx_setup_pcie_rootpoint(hose, i); +			env = getenv ("pciscandelay"); +			if (env != NULL) { +				delay = simple_strtoul(env, NULL, 10); +				if (delay > 5) +					printf("Warning, expect noticable delay before " +					       "PCIe scan due to 'pciscandelay' value!\n"); +				mdelay(delay * 1000); +			} + +			/* +			 * Config access can only go down stream +			 */ +			hose->last_busno = pci_hose_scan(hose); +			bus = hose->last_busno + 1; +		} +	} +} + +static int validate_endpoint(struct pci_controller *hose) +{ +	if (hose->cfg_data == (u8 *)CONFIG_SYS_PCIE0_CFGBASE) +		return (is_end_point(0)); +	else if (hose->cfg_data == (u8 *)CONFIG_SYS_PCIE1_CFGBASE) +		return (is_end_point(1)); +#if CONFIG_SYS_PCIE_NR_PORTS > 2 +	else if (hose->cfg_data == (u8 *)CONFIG_SYS_PCIE2_CFGBASE) +		return (is_end_point(2)); +#endif + +	return 0; +} + +static u8* pcie_get_base(struct pci_controller *hose, unsigned int devfn) +{ +	u8 *base = (u8*)hose->cfg_data; + +	/* use local configuration space for the first bus */ +	if (PCI_BUS(devfn) == 0) { +		if (hose->cfg_data == (u8*)CONFIG_SYS_PCIE0_CFGBASE) +			base = (u8*)CONFIG_SYS_PCIE0_XCFGBASE; +		if (hose->cfg_data == (u8*)CONFIG_SYS_PCIE1_CFGBASE) +			base = (u8*)CONFIG_SYS_PCIE1_XCFGBASE; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 +		if (hose->cfg_data == (u8*)CONFIG_SYS_PCIE2_CFGBASE) +			base = (u8*)CONFIG_SYS_PCIE2_XCFGBASE; +#endif +	} + +	return base; +} + +static void pcie_dmer_disable(void) +{ +	mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE), +		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE)) | GPL_DMER_MASK_DISA); +	mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE), +		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE)) | GPL_DMER_MASK_DISA); +#if CONFIG_SYS_PCIE_NR_PORTS > 2 +	mtdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE), +		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE)) | GPL_DMER_MASK_DISA); +#endif +} + +static void pcie_dmer_enable(void) +{ +	mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE0_BASE), +		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE0_BASE)) & ~GPL_DMER_MASK_DISA); +	mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE1_BASE), +		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE1_BASE)) & ~GPL_DMER_MASK_DISA); +#if CONFIG_SYS_PCIE_NR_PORTS > 2 +	mtdcr (DCRN_PEGPL_CFG (DCRN_PCIE2_BASE), +		mfdcr (DCRN_PEGPL_CFG(DCRN_PCIE2_BASE)) & ~GPL_DMER_MASK_DISA); +#endif +} + +static int pcie_read_config(struct pci_controller *hose, unsigned int devfn, +	int offset, int len, u32 *val) { + +	*val = 0; + +	if (validate_endpoint(hose)) +		return 0;		/* No upstream config access */ + +	/* +	 * Bus numbers are relative to hose->first_busno +	 */ +	devfn -= PCI_BDF(hose->first_busno, 0, 0); + +	/* +	 * NOTICE: configuration space ranges are currenlty mapped only for +	 * the first 16 buses, so such limit must be imposed. In case more +	 * buses are required the TLB settings in board/amcc/<board>/init.S +	 * need to be altered accordingly (one bus takes 1 MB of memory space). +	 */ +	if (PCI_BUS(devfn) >= 16) +		return 0; + +	/* +	 * Only single device/single function is supported for the primary and +	 * secondary buses of the 440SPe host bridge. +	 */ +	if ((!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 0))) && +		((PCI_BUS(devfn) == 0) || (PCI_BUS(devfn) == 1))) +		return 0; + +	pcie_get_base(hose, devfn); +	offset += devfn << 4; + +	/* +	 * Reading from configuration space of non-existing device can +	 * generate transaction errors. For the read duration we suppress +	 * assertion of machine check exceptions to avoid those. +	 */ +	pcie_dmer_disable (); + +	debug("%s: cfg_data=%p offset=%08x\n", __func__, +		hose->cfg_data, offset); +	switch (len) { +	case 1: +		*val = in_8(hose->cfg_data + offset); +		break; +	case 2: +		*val = in_le16((u16 *)(hose->cfg_data + offset)); +		break; +	default: +		*val = in_le32((u32*)(hose->cfg_data + offset)); +		break; +	} + +	pcie_dmer_enable (); + +	return 0; +} + +static int pcie_write_config(struct pci_controller *hose, unsigned int devfn, +	int offset, int len, u32 val) { + +	if (validate_endpoint(hose)) +		return 0;		/* No upstream config access */ + +	/* +	 * Bus numbers are relative to hose->first_busno +	 */ +	devfn -= PCI_BDF(hose->first_busno, 0, 0); + +	/* +	 * Same constraints as in pcie_read_config(). +	 */ +	if (PCI_BUS(devfn) >= 16) +		return 0; + +	if ((!((PCI_FUNC(devfn) == 0) && (PCI_DEV(devfn) == 0))) && +		((PCI_BUS(devfn) == 0) || (PCI_BUS(devfn) == 1))) +		return 0; + +	pcie_get_base(hose, devfn); +	offset += devfn << 4; + +	/* +	 * Suppress MCK exceptions, similar to pcie_read_config() +	 */ +	pcie_dmer_disable (); + +	switch (len) { +	case 1: +		out_8(hose->cfg_data + offset, val); +		break; +	case 2: +		out_le16((u16 *)(hose->cfg_data + offset), val); +		break; +	default: +		out_le32((u32 *)(hose->cfg_data + offset), val); +		break; +	} + +	pcie_dmer_enable (); + +	return 0; +} + +int pcie_read_config_byte(struct pci_controller *hose,pci_dev_t dev,int offset,u8 *val) +{ +	u32 v; +	int rv; + +	rv = pcie_read_config(hose, dev, offset, 1, &v); +	*val = (u8)v; +	return rv; +} + +int pcie_read_config_word(struct pci_controller *hose,pci_dev_t dev,int offset,u16 *val) +{ +	u32 v; +	int rv; + +	rv = pcie_read_config(hose, dev, offset, 2, &v); +	*val = (u16)v; +	return rv; +} + +int pcie_read_config_dword(struct pci_controller *hose,pci_dev_t dev,int offset,u32 *val) +{ +	u32 v; +	int rv; + +	rv = pcie_read_config(hose, dev, offset, 3, &v); +	*val = (u32)v; +	return rv; +} + +int pcie_write_config_byte(struct pci_controller *hose,pci_dev_t dev,int offset,u8 val) +{ +	return pcie_write_config(hose,(u32)dev,offset,1,val); +} + +int pcie_write_config_word(struct pci_controller *hose,pci_dev_t dev,int offset,u16 val) +{ +	return pcie_write_config(hose,(u32)dev,offset,2,(u32 )val); +} + +int pcie_write_config_dword(struct pci_controller *hose,pci_dev_t dev,int offset,u32 val) +{ +	return pcie_write_config(hose,(u32)dev,offset,3,(u32 )val); +} + +#if defined(CONFIG_440SPE) +static void ppc4xx_setup_utl(u32 port) { + +	volatile void *utl_base = NULL; + +	/* +	 * Map UTL registers +	 */ +	switch (port) { +	case 0: +		mtdcr(DCRN_PEGPL_REGBAH(PCIE0), 0x0000000c); +		mtdcr(DCRN_PEGPL_REGBAL(PCIE0), 0x20000000); +		mtdcr(DCRN_PEGPL_REGMSK(PCIE0), 0x00007001); +		mtdcr(DCRN_PEGPL_SPECIAL(PCIE0), 0x68782800); +		break; + +	case 1: +		mtdcr(DCRN_PEGPL_REGBAH(PCIE1), 0x0000000c); +		mtdcr(DCRN_PEGPL_REGBAL(PCIE1), 0x20001000); +		mtdcr(DCRN_PEGPL_REGMSK(PCIE1), 0x00007001); +		mtdcr(DCRN_PEGPL_SPECIAL(PCIE1), 0x68782800); +		break; + +	case 2: +		mtdcr(DCRN_PEGPL_REGBAH(PCIE2), 0x0000000c); +		mtdcr(DCRN_PEGPL_REGBAL(PCIE2), 0x20002000); +		mtdcr(DCRN_PEGPL_REGMSK(PCIE2), 0x00007001); +		mtdcr(DCRN_PEGPL_SPECIAL(PCIE2), 0x68782800); +		break; +	} +	utl_base = (unsigned int *)(CONFIG_SYS_PCIE_BASE + 0x1000 * port); + +	/* +	 * Set buffer allocations and then assert VRB and TXE. +	 */ +	out_be32(utl_base + PEUTL_OUTTR,   0x08000000); +	out_be32(utl_base + PEUTL_INTR,    0x02000000); +	out_be32(utl_base + PEUTL_OPDBSZ,  0x10000000); +	out_be32(utl_base + PEUTL_PBBSZ,   0x53000000); +	out_be32(utl_base + PEUTL_IPHBSZ,  0x08000000); +	out_be32(utl_base + PEUTL_IPDBSZ,  0x10000000); +	out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000); +	out_be32(utl_base + PEUTL_PCTL,    0x80800066); +} + +static int check_error(void) +{ +	u32 valPE0, valPE1, valPE2; +	int err = 0; + +	/* SDR0_PEGPLLLCT1 reset */ +	if (!(valPE0 = SDR_READ(PESDR0_PLLLCT1) & 0x01000000)) +		printf("PCIE: SDR0_PEGPLLLCT1 reset error 0x%x\n", valPE0); + +	valPE0 = SDR_READ(PESDR0_RCSSET); +	valPE1 = SDR_READ(PESDR1_RCSSET); +	valPE2 = SDR_READ(PESDR2_RCSSET); + +	/* SDR0_PExRCSSET rstgu */ +	if (!(valPE0 & 0x01000000) || +	    !(valPE1 & 0x01000000) || +	    !(valPE2 & 0x01000000)) { +		printf("PCIE:  SDR0_PExRCSSET rstgu error\n"); +		err = -1; +	} + +	/* SDR0_PExRCSSET rstdl */ +	if (!(valPE0 & 0x00010000) || +	    !(valPE1 & 0x00010000) || +	    !(valPE2 & 0x00010000)) { +		printf("PCIE:  SDR0_PExRCSSET rstdl error\n"); +		err = -1; +	} + +	/* SDR0_PExRCSSET rstpyn */ +	if ((valPE0 & 0x00001000) || +	    (valPE1 & 0x00001000) || +	    (valPE2 & 0x00001000)) { +		printf("PCIE:  SDR0_PExRCSSET rstpyn error\n"); +		err = -1; +	} + +	/* SDR0_PExRCSSET hldplb */ +	if ((valPE0 & 0x10000000) || +	    (valPE1 & 0x10000000) || +	    (valPE2 & 0x10000000)) { +		printf("PCIE:  SDR0_PExRCSSET hldplb error\n"); +		err = -1; +	} + +	/* SDR0_PExRCSSET rdy */ +	if ((valPE0 & 0x00100000) || +	    (valPE1 & 0x00100000) || +	    (valPE2 & 0x00100000)) { +		printf("PCIE:  SDR0_PExRCSSET rdy error\n"); +		err = -1; +	} + +	/* SDR0_PExRCSSET shutdown */ +	if ((valPE0 & 0x00000100) || +	    (valPE1 & 0x00000100) || +	    (valPE2 & 0x00000100)) { +		printf("PCIE:  SDR0_PExRCSSET shutdown error\n"); +		err = -1; +	} +	return err; +} + +/* + * Initialize PCI Express core + */ +int ppc4xx_init_pcie(void) +{ +	int time_out = 20; + +	/* Set PLL clock receiver to LVPECL */ +	SDR_WRITE(PESDR0_PLLLCT1, SDR_READ(PESDR0_PLLLCT1) | 1 << 28); + +	if (check_error()) { +		printf("ERROR: failed to set PCIe reference clock receiver --" +			"PESDR0_PLLLCT1 = 0x%08x\n", SDR_READ(PESDR0_PLLLCT1)); + +		return -1; +	} + +	/* Did resistance calibration work? */ +	if (!(SDR_READ(PESDR0_PLLLCT2) & 0x10000)) { +		printf("ERROR: PCIe resistance calibration failed --" +			"PESDR0_PLLLCT2 = 0x%08x\n", SDR_READ(PESDR0_PLLLCT2)); + +		return -1; +	} +	/* De-assert reset of PCIe PLL, wait for lock */ +	SDR_WRITE(PESDR0_PLLLCT1, SDR_READ(PESDR0_PLLLCT1) & ~(1 << 24)); +	udelay(300);	/* 300 uS is maximum time lock should take */ + +	while (time_out) { +		if (!(SDR_READ(PESDR0_PLLLCT3) & 0x10000000)) { +			time_out--; +			udelay(20);	/* Wait 20 uS more if needed */ +		} else +			break; +	} +	if (!time_out) { +		printf("ERROR: PCIe PLL VCO output not locked to ref clock --" +			"PESDR0_PLLLCTS=0x%08x\n", SDR_READ(PESDR0_PLLLCT3)); + +		return -1; +	} +	return 0; +} +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +static void ppc4xx_setup_utl(u32 port) +{ +	volatile void *utl_base = NULL; + +	/* +	 * Map UTL registers at 0x0801_n000 (4K 0xfff mask) PEGPLn_REGMSK +	 */ +	switch (port) { +	case 0: +		mtdcr(DCRN_PEGPL_REGBAH(PCIE0), U64_TO_U32_HIGH(CONFIG_SYS_PCIE0_UTLBASE)); +		mtdcr(DCRN_PEGPL_REGBAL(PCIE0), U64_TO_U32_LOW(CONFIG_SYS_PCIE0_UTLBASE)); +		mtdcr(DCRN_PEGPL_REGMSK(PCIE0), 0x00007001);	/* BAM 11100000=4KB */ +		mtdcr(DCRN_PEGPL_SPECIAL(PCIE0), 0); +		break; + +	case 1: +		mtdcr(DCRN_PEGPL_REGBAH(PCIE1), U64_TO_U32_HIGH(CONFIG_SYS_PCIE0_UTLBASE)); +		mtdcr(DCRN_PEGPL_REGBAL(PCIE1), U64_TO_U32_LOW(CONFIG_SYS_PCIE0_UTLBASE) +			+ 0x1000); +		mtdcr(DCRN_PEGPL_REGMSK(PCIE1), 0x00007001);	/* BAM 11100000=4KB */ +		mtdcr(DCRN_PEGPL_SPECIAL(PCIE1), 0); +		break; +	} +	utl_base = (unsigned int *)(CONFIG_SYS_PCIE_BASE + 0x1000 * port); + +	/* +	 * Set buffer allocations and then assert VRB and TXE. +	 */ +	out_be32(utl_base + PEUTL_PBCTL, 0x0800000c);	/* PLBME, CRRE */ +	out_be32(utl_base + PEUTL_OUTTR, 0x08000000); +	out_be32(utl_base + PEUTL_INTR, 0x02000000); +	out_be32(utl_base + PEUTL_OPDBSZ, 0x04000000);	/* OPD = 512 Bytes */ +	out_be32(utl_base + PEUTL_PBBSZ, 0x00000000);	/* Max 512 Bytes */ +	out_be32(utl_base + PEUTL_IPHBSZ, 0x02000000); +	out_be32(utl_base + PEUTL_IPDBSZ, 0x04000000);	/* IPD = 512 Bytes */ +	out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000); +	out_be32(utl_base + PEUTL_PCTL, 0x80800066);	/* VRB,TXE,timeout=default */ +} + +/* + * TODO: double check PCI express SDR based on the latest user manual + *		 Some registers specified here no longer exist.. has to be + *		 updated based on the final EAS spec. + */ +static int check_error(void) +{ +	u32 valPE0, valPE1; +	int err = 0; + +	valPE0 = SDR_READ(SDRN_PESDR_RCSSET(0)); +	valPE1 = SDR_READ(SDRN_PESDR_RCSSET(1)); + +	/* SDR0_PExRCSSET rstgu */ +	if (!(valPE0 & PESDRx_RCSSET_RSTGU) || !(valPE1 & PESDRx_RCSSET_RSTGU)) { +		printf("PCIE:  SDR0_PExRCSSET rstgu error\n"); +		err = -1; +	} + +	/* SDR0_PExRCSSET rstdl */ +	if (!(valPE0 & PESDRx_RCSSET_RSTDL) || !(valPE1 & PESDRx_RCSSET_RSTDL)) { +		printf("PCIE:  SDR0_PExRCSSET rstdl error\n"); +		err = -1; +	} + +	/* SDR0_PExRCSSET rstpyn */ +	if ((valPE0 & PESDRx_RCSSET_RSTPYN) || (valPE1 & PESDRx_RCSSET_RSTPYN)) { +		printf("PCIE:  SDR0_PExRCSSET rstpyn error\n"); +		err = -1; +	} + +	/* SDR0_PExRCSSET hldplb */ +	if ((valPE0 & PESDRx_RCSSET_HLDPLB) || (valPE1 & PESDRx_RCSSET_HLDPLB)) { +		printf("PCIE:  SDR0_PExRCSSET hldplb error\n"); +		err = -1; +	} + +	/* SDR0_PExRCSSET rdy */ +	if ((valPE0 & PESDRx_RCSSET_RDY) || (valPE1 & PESDRx_RCSSET_RDY)) { +		printf("PCIE:  SDR0_PExRCSSET rdy error\n"); +		err = -1; +	} + +	return err; +} + +/* + * Initialize PCI Express core as described in User Manual + * TODO: double check PE SDR PLL Register with the updated user manual. + */ +int ppc4xx_init_pcie(void) +{ +	if (check_error()) +		return -1; + +	return 0; +} +#endif /* CONFIG_460EX */ + +#if defined(CONFIG_405EX) +static void ppc4xx_setup_utl(u32 port) +{ +	u32 utl_base; + +	/* +	 * Map UTL registers at 0xef4f_n000 (4K 0xfff mask) PEGPLn_REGMSK +	 */ +	switch (port) { +	case 0: +		mtdcr(DCRN_PEGPL_REGBAH(PCIE0), 0x00000000); +		mtdcr(DCRN_PEGPL_REGBAL(PCIE0), CONFIG_SYS_PCIE0_UTLBASE); +		mtdcr(DCRN_PEGPL_REGMSK(PCIE0), 0x00007001); /* 4k region, valid */ +		mtdcr(DCRN_PEGPL_SPECIAL(PCIE0), 0); +		break; + +	case 1: +		mtdcr(DCRN_PEGPL_REGBAH(PCIE1), 0x00000000); +		mtdcr(DCRN_PEGPL_REGBAL(PCIE1), CONFIG_SYS_PCIE1_UTLBASE); +		mtdcr(DCRN_PEGPL_REGMSK(PCIE1), 0x00007001); /* 4k region, valid */ +		mtdcr(DCRN_PEGPL_SPECIAL(PCIE1), 0); + +		break; +	} +	utl_base = (port==0) ? CONFIG_SYS_PCIE0_UTLBASE : CONFIG_SYS_PCIE1_UTLBASE; + +	/* +	 * Set buffer allocations and then assert VRB and TXE. +	 */ +	out_be32((u32 *)(utl_base + PEUTL_OUTTR),   0x02000000); +	out_be32((u32 *)(utl_base + PEUTL_INTR),    0x02000000); +	out_be32((u32 *)(utl_base + PEUTL_OPDBSZ),  0x04000000); +	out_be32((u32 *)(utl_base + PEUTL_PBBSZ),   0x21000000); +	out_be32((u32 *)(utl_base + PEUTL_IPHBSZ),  0x02000000); +	out_be32((u32 *)(utl_base + PEUTL_IPDBSZ),  0x04000000); +	out_be32((u32 *)(utl_base + PEUTL_RCIRQEN), 0x00f00000); +	out_be32((u32 *)(utl_base + PEUTL_PCTL),    0x80800066); + +	out_be32((u32 *)(utl_base + PEUTL_PBCTL),   0x0800000c); +	out_be32((u32 *)(utl_base + PEUTL_RCSTA), +		 in_be32((u32 *)(utl_base + PEUTL_RCSTA)) | 0x000040000); +} + +int ppc4xx_init_pcie(void) +{ +	/* +	 * Nothing to do on 405EX +	 */ +	return 0; +} +#endif /* CONFIG_405EX */ + +/* + * Board-specific pcie initialization + * Platform code can reimplement ppc4xx_init_pcie_port_hw() if needed + */ + +/* + * Initialize various parts of the PCI Express core for our port: + * + * - Set as a root port and enable max width + *   (PXIE0 -> X8, PCIE1 and PCIE2 -> X4). + * - Set up UTL configuration. + * - Increase SERDES drive strength to levels suggested by AMCC. + * - De-assert RSTPYN, RSTDL and RSTGU. + * + * NOTICE for 440SPE revB chip: PESDRn_UTLSET2 is not set - we leave it + * with default setting 0x11310000. The register has new fields, + * PESDRn_UTLSET2[LKINE] in particular: clearing it leads to PCIE core + * hang. + */ +#if defined(CONFIG_440SPE) +int __ppc4xx_init_pcie_port_hw(int port, int rootport) +{ +	u32 val = 1 << 24; +	u32 utlset1; + +	if (rootport) { +		val = PTYPE_ROOT_PORT << 20; +		utlset1 = 0x21222222; +	} else { +		val = PTYPE_LEGACY_ENDPOINT << 20; +		utlset1 = 0x20222222; +	} + +	if (port == 0) +		val |= LNKW_X8 << 12; +	else +		val |= LNKW_X4 << 12; + +	SDR_WRITE(SDRN_PESDR_DLPSET(port), val); +	SDR_WRITE(SDRN_PESDR_UTLSET1(port), utlset1); +	if (!ppc440spe_revB()) +		SDR_WRITE(SDRN_PESDR_UTLSET2(port), 0x11000000); +	SDR_WRITE(SDRN_PESDR_HSSL0SET1(port), 0x35000000); +	SDR_WRITE(SDRN_PESDR_HSSL1SET1(port), 0x35000000); +	SDR_WRITE(SDRN_PESDR_HSSL2SET1(port), 0x35000000); +	SDR_WRITE(SDRN_PESDR_HSSL3SET1(port), 0x35000000); +	if (port == 0) { +		SDR_WRITE(PESDR0_HSSL4SET1, 0x35000000); +		SDR_WRITE(PESDR0_HSSL5SET1, 0x35000000); +		SDR_WRITE(PESDR0_HSSL6SET1, 0x35000000); +		SDR_WRITE(PESDR0_HSSL7SET1, 0x35000000); +	} +	SDR_WRITE(SDRN_PESDR_RCSSET(port), (SDR_READ(SDRN_PESDR_RCSSET(port)) & +					    ~(1 << 24 | 1 << 16)) | 1 << 12); + +	return 0; +} +#endif /* CONFIG_440SPE */ + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +int __ppc4xx_init_pcie_port_hw(int port, int rootport) +{ +	u32 val; +	u32 utlset1; + +	if (rootport) +		val = PTYPE_ROOT_PORT << 20; +	else +		val = PTYPE_LEGACY_ENDPOINT << 20; + +	if (port == 0) { +		val |= LNKW_X1 << 12; +		utlset1 = 0x20000000; +	} else { +		val |= LNKW_X4 << 12; +		utlset1 = 0x20101101; +	} + +	SDR_WRITE(SDRN_PESDR_DLPSET(port), val); +	SDR_WRITE(SDRN_PESDR_UTLSET1(port), utlset1); +	SDR_WRITE(SDRN_PESDR_UTLSET2(port), 0x01210000); + +	switch (port) { +	case 0: +		SDR_WRITE(PESDR0_L0CDRCTL, 0x00003230); +		SDR_WRITE(PESDR0_L0DRV, 0x00000130); +		SDR_WRITE(PESDR0_L0CLK, 0x00000006); + +		SDR_WRITE(PESDR0_PHY_CTL_RST,0x10000000); +		break; + +	case 1: +		SDR_WRITE(PESDR1_L0CDRCTL, 0x00003230); +		SDR_WRITE(PESDR1_L1CDRCTL, 0x00003230); +		SDR_WRITE(PESDR1_L2CDRCTL, 0x00003230); +		SDR_WRITE(PESDR1_L3CDRCTL, 0x00003230); +		SDR_WRITE(PESDR1_L0DRV, 0x00000130); +		SDR_WRITE(PESDR1_L1DRV, 0x00000130); +		SDR_WRITE(PESDR1_L2DRV, 0x00000130); +		SDR_WRITE(PESDR1_L3DRV, 0x00000130); +		SDR_WRITE(PESDR1_L0CLK, 0x00000006); +		SDR_WRITE(PESDR1_L1CLK, 0x00000006); +		SDR_WRITE(PESDR1_L2CLK, 0x00000006); +		SDR_WRITE(PESDR1_L3CLK, 0x00000006); + +		SDR_WRITE(PESDR1_PHY_CTL_RST,0x10000000); +		break; +	} + +	SDR_WRITE(SDRN_PESDR_RCSSET(port), SDR_READ(SDRN_PESDR_RCSSET(port)) | +		  (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTPYN)); + +	/* Poll for PHY reset */ +	switch (port) { +	case 0: +		while (!(SDR_READ(PESDR0_RSTSTA) & 0x1)) +			udelay(10); +		break; +	case 1: +		while (!(SDR_READ(PESDR1_RSTSTA) & 0x1)) +			udelay(10); +		break; +	} + +	SDR_WRITE(SDRN_PESDR_RCSSET(port), +		  (SDR_READ(SDRN_PESDR_RCSSET(port)) & +		   ~(PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL)) | +		  PESDRx_RCSSET_RSTPYN); + +	return 0; +} +#endif /* CONFIG_440SPE */ + +#if defined(CONFIG_405EX) +int __ppc4xx_init_pcie_port_hw(int port, int rootport) +{ +	u32 val; + +	if (rootport) +		val = 0x00401000; +	else +		val = 0x00101000; + +	SDR_WRITE(SDRN_PESDR_DLPSET(port), val); +	SDR_WRITE(SDRN_PESDR_UTLSET1(port), 0x00000000); +	SDR_WRITE(SDRN_PESDR_UTLSET2(port), 0x01010000); +	SDR_WRITE(SDRN_PESDR_PHYSET1(port), 0x720F0000); +	SDR_WRITE(SDRN_PESDR_PHYSET2(port), 0x70600003); + +	/* Assert the PE0_PHY reset */ +	SDR_WRITE(SDRN_PESDR_RCSSET(port), 0x01010000); +	udelay(1000); + +	/* deassert the PE0_hotreset */ +	if (is_end_point(port)) +		SDR_WRITE(SDRN_PESDR_RCSSET(port), 0x01111000); +	else +		SDR_WRITE(SDRN_PESDR_RCSSET(port), 0x01101000); + +	/* poll for phy !reset */ +	while (!(SDR_READ(SDRN_PESDR_PHYSTA(port)) & 0x00001000)) +		; + +	/* deassert the PE0_gpl_utl_reset */ +	SDR_WRITE(SDRN_PESDR_RCSSET(port), 0x00101000); + +	if (port == 0) +		mtdcr(DCRN_PEGPL_CFG(PCIE0), 0x10000000);  /* guarded on */ +	else +		mtdcr(DCRN_PEGPL_CFG(PCIE1), 0x10000000);  /* guarded on */ + +	return 0; +} +#endif /* CONFIG_405EX */ + +int ppc4xx_init_pcie_port_hw(int port, int rootport) +__attribute__((weak, alias("__ppc4xx_init_pcie_port_hw"))); + +/* + * We map PCI Express configuration access into the 512MB regions + * + * NOTICE: revB is very strict about PLB real addressess and ranges to + * be mapped for config space; it seems to only work with d_nnnn_nnnn + * range (hangs the core upon config transaction attempts when set + * otherwise) while revA uses c_nnnn_nnnn. + * + * For 440SPe revA: + *     PCIE0: 0xc_4000_0000 + *     PCIE1: 0xc_8000_0000 + *     PCIE2: 0xc_c000_0000 + * + * For 440SPe revB: + *     PCIE0: 0xd_0000_0000 + *     PCIE1: 0xd_2000_0000 + *     PCIE2: 0xd_4000_0000 + * + * For 405EX: + *     PCIE0: 0xa000_0000 + *     PCIE1: 0xc000_0000 + * + * For 460EX/GT: + *     PCIE0: 0xd_0000_0000 + *     PCIE1: 0xd_2000_0000 + */ +static inline u64 ppc4xx_get_cfgaddr(int port) +{ +#if defined(CONFIG_405EX) +	if (port == 0) +		return (u64)CONFIG_SYS_PCIE0_CFGBASE; +	else +		return (u64)CONFIG_SYS_PCIE1_CFGBASE; +#endif +#if defined(CONFIG_440SPE) +	if (ppc440spe_revB()) { +		switch (port) { +		default:	/* to satisfy compiler */ +		case 0: +			return 0x0000000d00000000ULL; +		case 1: +			return 0x0000000d20000000ULL; +		case 2: +			return 0x0000000d40000000ULL; +		} +	} else { +		switch (port) { +		default:	/* to satisfy compiler */ +		case 0: +			return 0x0000000c40000000ULL; +		case 1: +			return 0x0000000c80000000ULL; +		case 2: +			return 0x0000000cc0000000ULL; +		} +	} +#endif +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +	if (port == 0) +		return 0x0000000d00000000ULL; +	else +		return 0x0000000d20000000ULL; +#endif +} + +/* + *  4xx boards as endpoint and root point setup + *                    and + *    testing inbound and out bound windows + * + *  4xx boards can be plugged into another 4xx boards or you can get PCI-E + *  cable which can be used to setup loop back from one port to another port. + *  Please rememeber that unless there is a endpoint plugged in to root port it + *  will not initialize. It is the same in case of endpoint , unless there is + *  root port attached it will not initialize. + * + *  In this release of software all the PCI-E ports are configured as either + *  endpoint or rootpoint.In future we will have support for selective ports + *  setup as endpoint and root point in single board. + * + *  Once your board came up as root point , you can verify by reading + *  /proc/bus/pci/devices. Where you can see the configuration registers + *  of endpoint device attached to the port. + * + *  Enpoint cofiguration can be verified by connecting 4xx board to any + *  host or another 4xx board. Then try to scan the device. In case of + *  linux use "lspci" or appripriate os command. + * + *  How do I verify the inbound and out bound windows ? (4xx to 4xx) + *  in this configuration inbound and outbound windows are setup to access + *  sram memroy area. SRAM is at 0x4 0000 0000 , on PLB bus. This address + *  is mapped at 0x90000000. From u-boot prompt write data 0xb000 0000, + *  This is waere your POM(PLB out bound memory window) mapped. then + *  read the data from other 4xx board's u-boot prompt at address + *  0x9000 0000(SRAM). Data should match. + *  In case of inbound , write data to u-boot command prompt at 0xb000 0000 + *  which is mapped to 0x4 0000 0000. Now on rootpoint yucca u-boot prompt check + *  data at 0x9000 0000(SRAM).Data should match. + */ +int ppc4xx_init_pcie_port(int port, int rootport) +{ +	static int core_init; +	volatile u32 val = 0; +	int attempts; +	u64 addr; +	u32 low, high; + +	if (!core_init) { +		if (ppc4xx_init_pcie()) +			return -1; +		++core_init; +	} + +	/* +	 * Initialize various parts of the PCI Express core for our port +	 */ +	ppc4xx_init_pcie_port_hw(port, rootport); + +	/* +	 * Notice: the following delay has critical impact on device +	 * initialization - if too short (<50ms) the link doesn't get up. +	 */ +	mdelay(100); + +	val = SDR_READ(SDRN_PESDR_RCSSTS(port)); +	if (val & (1 << 20)) { +		printf("PCIE%d: PGRST failed %08x\n", port, val); +		return -1; +	} + +	/* +	 * Verify link is up +	 */ +	val = SDR_READ(SDRN_PESDR_LOOP(port)); +	if (!(val & 0x00001000)) { +		printf("PCIE%d: link is not up.\n", port); +		return -ENODEV; +	} + +	/* +	 * Setup UTL registers - but only on revA! +	 * We use default settings for revB chip. +	 */ +	if (!ppc440spe_revB()) +		ppc4xx_setup_utl(port); + +	/* +	 * We map PCI Express configuration access into the 512MB regions +	 */ +	addr = ppc4xx_get_cfgaddr(port); +	low = U64_TO_U32_LOW(addr); +	high = U64_TO_U32_HIGH(addr); + +	switch (port) { +	case 0: +		mtdcr(DCRN_PEGPL_CFGBAH(PCIE0), high); +		mtdcr(DCRN_PEGPL_CFGBAL(PCIE0), low); +		mtdcr(DCRN_PEGPL_CFGMSK(PCIE0), 0xe0000001); /* 512MB region, valid */ +		break; +	case 1: +		mtdcr(DCRN_PEGPL_CFGBAH(PCIE1), high); +		mtdcr(DCRN_PEGPL_CFGBAL(PCIE1), low); +		mtdcr(DCRN_PEGPL_CFGMSK(PCIE1), 0xe0000001); /* 512MB region, valid */ +		break; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 +	case 2: +		mtdcr(DCRN_PEGPL_CFGBAH(PCIE2), high); +		mtdcr(DCRN_PEGPL_CFGBAL(PCIE2), low); +		mtdcr(DCRN_PEGPL_CFGMSK(PCIE2), 0xe0000001); /* 512MB region, valid */ +		break; +#endif +	} + +	/* +	 * Check for VC0 active and assert RDY. +	 */ +	attempts = 10; +	while(!(SDR_READ(SDRN_PESDR_RCSSTS(port)) & (1 << 16))) { +		if (!(attempts--)) { +			printf("PCIE%d: VC0 not active\n", port); +			return -1; +		} +		mdelay(1000); +	} +	SDR_WRITE(SDRN_PESDR_RCSSET(port), +		  SDR_READ(SDRN_PESDR_RCSSET(port)) | 1 << 20); +	mdelay(100); + +	return 0; +} + +int ppc4xx_init_pcie_rootport(int port) +{ +	return ppc4xx_init_pcie_port(port, 1); +} + +int ppc4xx_init_pcie_endport(int port) +{ +	return ppc4xx_init_pcie_port(port, 0); +} + +void ppc4xx_setup_pcie_rootpoint(struct pci_controller *hose, int port) +{ +	volatile void *mbase = NULL; + +	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); + +	switch (port) { +	case 0: +		mbase = (u32 *)CONFIG_SYS_PCIE0_XCFGBASE; +		hose->cfg_data = (u8 *)CONFIG_SYS_PCIE0_CFGBASE; +		break; +	case 1: +		mbase = (u32 *)CONFIG_SYS_PCIE1_XCFGBASE; +		hose->cfg_data = (u8 *)CONFIG_SYS_PCIE1_CFGBASE; +		break; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 +	case 2: +		mbase = (u32 *)CONFIG_SYS_PCIE2_XCFGBASE; +		hose->cfg_data = (u8 *)CONFIG_SYS_PCIE2_CFGBASE; +		break; +#endif +	} + +	/* +	 * Set bus numbers on our root port +	 */ +	out_8((u8 *)mbase + PCI_PRIMARY_BUS, 0); +	out_8((u8 *)mbase + PCI_SECONDARY_BUS, 1); +	out_8((u8 *)mbase + PCI_SUBORDINATE_BUS, 1); + +	/* +	 * Set up outbound translation to hose->mem_space from PLB +	 * addresses at an offset of 0xd_0000_0000.  We set the low +	 * bits of the mask to 11 to turn off splitting into 8 +	 * subregions and to enable the outbound translation. +	 */ +	out_le32(mbase + PECFG_POM0LAH, 0x00000000); +	out_le32(mbase + PECFG_POM0LAL, CONFIG_SYS_PCIE_MEMBASE + +		 port * CONFIG_SYS_PCIE_MEMSIZE); +	debug("PECFG_POM0LA=%08x.%08x\n", in_le32(mbase + PECFG_POM0LAH), +	      in_le32(mbase + PECFG_POM0LAL)); + +	switch (port) { +	case 0: +		mtdcr(DCRN_PEGPL_OMR1BAH(PCIE0), CONFIG_SYS_PCIE_ADDR_HIGH); +		mtdcr(DCRN_PEGPL_OMR1BAL(PCIE0), CONFIG_SYS_PCIE_MEMBASE + +		      port * CONFIG_SYS_PCIE_MEMSIZE); +		mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE0), 0x7fffffff); +		mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE0), +		      ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); +		debug("0:PEGPL_OMR1BA=%08x.%08x MSK=%08x.%08x\n", +		      mfdcr(DCRN_PEGPL_OMR1BAH(PCIE0)), +		      mfdcr(DCRN_PEGPL_OMR1BAL(PCIE0)), +		      mfdcr(DCRN_PEGPL_OMR1MSKH(PCIE0)), +		      mfdcr(DCRN_PEGPL_OMR1MSKL(PCIE0))); +		break; +	case 1: +		mtdcr(DCRN_PEGPL_OMR1BAH(PCIE1), CONFIG_SYS_PCIE_ADDR_HIGH); +		mtdcr(DCRN_PEGPL_OMR1BAL(PCIE1), CONFIG_SYS_PCIE_MEMBASE + +		      port * CONFIG_SYS_PCIE_MEMSIZE); +		mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE1), 0x7fffffff); +		mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE1), +		      ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); +		debug("1:PEGPL_OMR1BA=%08x.%08x MSK=%08x.%08x\n", +		      mfdcr(DCRN_PEGPL_OMR1BAH(PCIE1)), +		      mfdcr(DCRN_PEGPL_OMR1BAL(PCIE1)), +		      mfdcr(DCRN_PEGPL_OMR1MSKH(PCIE1)), +		      mfdcr(DCRN_PEGPL_OMR1MSKL(PCIE1))); +		break; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 +	case 2: +		mtdcr(DCRN_PEGPL_OMR1BAH(PCIE2), CONFIG_SYS_PCIE_ADDR_HIGH); +		mtdcr(DCRN_PEGPL_OMR1BAL(PCIE2), CONFIG_SYS_PCIE_MEMBASE + +		      port * CONFIG_SYS_PCIE_MEMSIZE); +		mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE2), 0x7fffffff); +		mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE2), +		      ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); +		debug("2:PEGPL_OMR1BA=%08x.%08x MSK=%08x.%08x\n", +		      mfdcr(DCRN_PEGPL_OMR1BAH(PCIE2)), +		      mfdcr(DCRN_PEGPL_OMR1BAL(PCIE2)), +		      mfdcr(DCRN_PEGPL_OMR1MSKH(PCIE2)), +		      mfdcr(DCRN_PEGPL_OMR1MSKL(PCIE2))); +		break; +#endif +	} + +	/* Set up 4GB inbound memory window at 0 */ +	out_le32(mbase + PCI_BASE_ADDRESS_0, 0); +	out_le32(mbase + PCI_BASE_ADDRESS_1, 0); +	out_le32(mbase + PECFG_BAR0HMPA, 0x7ffffff); +	out_le32(mbase + PECFG_BAR0LMPA, 0); + +	out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); +	out_le32(mbase + PECFG_PIM01SAL, 0x00000000); +	out_le32(mbase + PECFG_PIM0LAL, 0); +	out_le32(mbase + PECFG_PIM0LAH, 0); +	out_le32(mbase + PECFG_PIM1LAL, 0x00000000); +	out_le32(mbase + PECFG_PIM1LAH, 0x00000004); +	out_le32(mbase + PECFG_PIMEN, 0x1); + +	/* Enable I/O, Mem, and Busmaster cycles */ +	out_le16((u16 *)(mbase + PCI_COMMAND), +		 in_le16((u16 *)(mbase + PCI_COMMAND)) | +		 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + +	/* Set Device and Vendor Id */ +	out_le16(mbase + 0x200, 0xaaa0 + port); +	out_le16(mbase + 0x202, 0xbed0 + port); + +	/* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ +	out_le32(mbase + 0x208, 0x06040001); + +	printf("PCIE%d: successfully set as root-complex\n", port); +} + +int ppc4xx_setup_pcie_endpoint(struct pci_controller *hose, int port) +{ +	volatile void *mbase = NULL; +	int attempts = 0; + +	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); + +	switch (port) { +	case 0: +		mbase = (u32 *)CONFIG_SYS_PCIE0_XCFGBASE; +		hose->cfg_data = (u8 *)CONFIG_SYS_PCIE0_CFGBASE; +		break; +	case 1: +		mbase = (u32 *)CONFIG_SYS_PCIE1_XCFGBASE; +		hose->cfg_data = (u8 *)CONFIG_SYS_PCIE1_CFGBASE; +		break; +#if defined(CONFIG_SYS_PCIE2_CFGBASE) +	case 2: +		mbase = (u32 *)CONFIG_SYS_PCIE2_XCFGBASE; +		hose->cfg_data = (u8 *)CONFIG_SYS_PCIE2_CFGBASE; +		break; +#endif +	} + +	/* +	 * Set up outbound translation to hose->mem_space from PLB +	 * addresses at an offset of 0xd_0000_0000.  We set the low +	 * bits of the mask to 11 to turn off splitting into 8 +	 * subregions and to enable the outbound translation. +	 */ +	out_le32(mbase + PECFG_POM0LAH, 0x00001ff8); +	out_le32(mbase + PECFG_POM0LAL, 0x00001000); + +	switch (port) { +	case 0: +		mtdcr(DCRN_PEGPL_OMR1BAH(PCIE0), CONFIG_SYS_PCIE_ADDR_HIGH); +		mtdcr(DCRN_PEGPL_OMR1BAL(PCIE0), CONFIG_SYS_PCIE_MEMBASE + +		      port * CONFIG_SYS_PCIE_MEMSIZE); +		mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE0), 0x7fffffff); +		mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE0), +		      ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); +		break; +	case 1: +		mtdcr(DCRN_PEGPL_OMR1BAH(PCIE1), CONFIG_SYS_PCIE_ADDR_HIGH); +		mtdcr(DCRN_PEGPL_OMR1BAL(PCIE1), CONFIG_SYS_PCIE_MEMBASE + +		      port * CONFIG_SYS_PCIE_MEMSIZE); +		mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE1), 0x7fffffff); +		mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE1), +		      ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); +		break; +#if CONFIG_SYS_PCIE_NR_PORTS > 2 +	case 2: +		mtdcr(DCRN_PEGPL_OMR1BAH(PCIE2), CONFIG_SYS_PCIE_ADDR_HIGH); +		mtdcr(DCRN_PEGPL_OMR1BAL(PCIE2), CONFIG_SYS_PCIE_MEMBASE + +		      port * CONFIG_SYS_PCIE_MEMSIZE); +		mtdcr(DCRN_PEGPL_OMR1MSKH(PCIE2), 0x7fffffff); +		mtdcr(DCRN_PEGPL_OMR1MSKL(PCIE2), +		      ~(CONFIG_SYS_PCIE_MEMSIZE - 1) | 3); +		break; +#endif +	} + +	/* Set up 64MB inbound memory window at 0 */ +	out_le32(mbase + PCI_BASE_ADDRESS_0, 0); +	out_le32(mbase + PCI_BASE_ADDRESS_1, 0); + +	out_le32(mbase + PECFG_PIM01SAH, 0xffffffff); +	out_le32(mbase + PECFG_PIM01SAL, 0xfc000000); + +	/* Setup BAR0 */ +	out_le32(mbase + PECFG_BAR0HMPA, 0x7fffffff); +	out_le32(mbase + PECFG_BAR0LMPA, 0xfc000000 | PCI_BASE_ADDRESS_MEM_TYPE_64); + +	/* Disable BAR1 & BAR2 */ +	out_le32(mbase + PECFG_BAR1MPA, 0); +	out_le32(mbase + PECFG_BAR2HMPA, 0); +	out_le32(mbase + PECFG_BAR2LMPA, 0); + +	out_le32(mbase + PECFG_PIM0LAL, U64_TO_U32_LOW(CONFIG_SYS_PCIE_INBOUND_BASE)); +	out_le32(mbase + PECFG_PIM0LAH, U64_TO_U32_HIGH(CONFIG_SYS_PCIE_INBOUND_BASE)); +	out_le32(mbase + PECFG_PIMEN, 0x1); + +	/* Enable I/O, Mem, and Busmaster cycles */ +	out_le16((u16 *)(mbase + PCI_COMMAND), +		 in_le16((u16 *)(mbase + PCI_COMMAND)) | +		 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); +	out_le16(mbase + 0x200, 0xcaad);		/* Setting vendor ID */ +	out_le16(mbase + 0x202, 0xfeed);		/* Setting device ID */ + +	/* Set Class Code to Processor/PPC */ +	out_le32(mbase + 0x208, 0x0b200001); + +	attempts = 10; +	while(!(SDR_READ(SDRN_PESDR_RCSSTS(port)) & (1 << 8))) { +		if (!(attempts--)) { +			printf("PCIE%d: BME not active\n", port); +			return -1; +		} +		mdelay(1000); +	} + +	printf("PCIE%d: successfully set as endpoint\n", port); + +	return 0; +} +#endif /* CONFIG_440SPE && CONFIG_PCI */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_uart.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_uart.c new file mode 100644 index 00000000..c02058f7 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/4xx_uart.c @@ -0,0 +1,268 @@ +/* + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2010 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0	IBM-pibs + */ + +#include <common.h> +#include <commproc.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <watchdog.h> +#include <asm/ppc4xx.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_405GP) || \ +    defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ +    defined(CONFIG_405EX) || defined(CONFIG_440) + +#if defined(CONFIG_440) + +#if defined(CONFIG_440GP) +#define CR0_MASK        0x3fff0000 +#define CR0_EXTCLK_ENA  0x00600000 +#define CR0_UDIV_POS    16 +#define UDIV_SUBTRACT	1 +#define UART0_SDR	CPC0_CR0 +#define MFREG(a, d)	d = mfdcr(a) +#define MTREG(a, d)	mtdcr(a, d) +#else /* #if defined(CONFIG_440GP) */ +/* all other 440 PPC's access clock divider via sdr register */ +#define CR0_MASK        0xdfffffff +#define CR0_EXTCLK_ENA  0x00800000 +#define CR0_UDIV_POS    0 +#define UDIV_SUBTRACT	0 +#define UART0_SDR	SDR0_UART0 +#define UART1_SDR	SDR0_UART1 +#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ +    defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ +    defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define UART2_SDR	SDR0_UART2 +#endif +#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ +    defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define UART3_SDR	SDR0_UART3 +#endif +#define MFREG(a, d)	mfsdr(a, d) +#define MTREG(a, d)	mtsdr(a, d) +#endif /* #if defined(CONFIG_440GP) */ +#elif defined(CONFIG_405EP) || defined(CONFIG_405EZ) +#define UCR0_MASK       0x0000007f +#define UCR1_MASK       0x00007f00 +#define UCR0_UDIV_POS   0 +#define UCR1_UDIV_POS   8 +#define UDIV_MAX        127 +#elif defined(CONFIG_405EX) +#define MFREG(a, d)	mfsdr(a, d) +#define MTREG(a, d)	mtsdr(a, d) +#define CR0_MASK	0x000000ff +#define CR0_EXTCLK_ENA	0x00800000 +#define CR0_UDIV_POS	0 +#define UDIV_SUBTRACT	0 +#define UART0_SDR	SDR0_UART0 +#define UART1_SDR	SDR0_UART1 +#else /* CONFIG_405GP */ +#define CR0_MASK        0x00001fff +#define CR0_EXTCLK_ENA  0x000000c0 +#define CR0_UDIV_POS    1 +#define UDIV_MAX        32 +#endif + +#if defined(CONFIG_405EP) && defined(CONFIG_SYS_EXT_SERIAL_CLOCK) +#error "External serial clock not supported on AMCC PPC405EP!" +#endif + +#if (defined(CONFIG_405EX) || defined(CONFIG_405EZ) ||	\ +     defined(CONFIG_440)) && !defined(CONFIG_SYS_EXT_SERIAL_CLOCK) +/* + * For some SoC's, the cpu clock is on divider chain A, UART on + * divider chain B ... so cpu clock is irrelevant. Get the + * "optimized" values that are subject to the 1/2 opb clock + * constraint. + */ +static u16 serial_bdiv(int baudrate, u32 *udiv) +{ +	sys_info_t sysinfo; +	u32 div;		/* total divisor udiv * bdiv */ +	u32 umin;		/* minimum udiv	*/ +	u16 diff;		/* smallest diff */ +	u16 idiff;		/* current diff */ +	u16 ibdiv;		/* current bdiv */ +	u32 i; +	u32 est;		/* current estimate */ +	u32 max; +#if defined(CONFIG_405EZ) +	u32 cpr_pllc; +	u32 plloutb; +	u32 reg; +#endif + +	get_sys_info(&sysinfo); + +#if defined(CONFIG_405EZ) +	/* check the pll feedback source */ +	mfcpr(CPR0_PLLC, cpr_pllc); +	plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ? +					   sysinfo.pllFwdDivB : sysinfo.pllFwdDiv) * +		    sysinfo.pllFbkDiv) / sysinfo.pllFwdDivB); +	div = plloutb / (16 * baudrate); /* total divisor */ +	umin = (plloutb / get_OPB_freq()) << 1;	/* 2 x OPB divisor */ +	max = 256;			/* highest possible */ +#else /* 405EZ */ +	div = sysinfo.freqPLB / (16 * baudrate); /* total divisor */ +	umin = sysinfo.pllOpbDiv << 1;	/* 2 x OPB divisor */ +	max = 32;			/* highest possible */ +#endif /* 405EZ */ + +	*udiv = diff = max; + +	/* +	 * i is the test udiv value -- start with the largest +	 * possible (max) to minimize serial clock and constrain +	 * search to umin. +	 */ +	for (i = max; i > umin; i--) { +		ibdiv = div / i; +		est = i * ibdiv; +		idiff = (est > div) ? (est - div) : (div - est); +		if (idiff == 0) { +			*udiv = i; +			break;		/* can't do better */ +		} else if (idiff < diff) { +			*udiv = i;	/* best so far */ +			diff = idiff;	/* update lowest diff*/ +		} +	} + +#if defined(CONFIG_405EZ) +	mfcpr(CPR0_PERD0, reg); +	reg &= ~0x0000ffff; +	reg |= ((*udiv - 0) << 8) | (*udiv - 0); +	mtcpr(CPR0_PERD0, reg); +#endif + +	return div / *udiv; +} +#endif /* #if (defined(CONFIG_405EP) ... */ + +/* + * This function returns the UART clock used by the common + * NS16550 driver. Additionally the SoC internal divisors for + * optimal UART baudrate are configured. + */ +int get_serial_clock(void) +{ +	u32 clk; +	u32 udiv; +#if !defined(CONFIG_405EZ) +	u32 reg; +#endif +#if !defined(CONFIG_SYS_EXT_SERIAL_CLOCK) +	PPC4xx_SYS_INFO sys_info; +#endif + +	/* +	 * Programming of the internal divisors is SoC specific. +	 * Let's handle this in some #ifdef's for the SoC's. +	 */ + +#if defined(CONFIG_405GP) +	reg = mfdcr(CPC0_CR0) & ~CR0_MASK; +#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK +	clk = CONFIG_SYS_EXT_SERIAL_CLOCK; +	udiv = 1; +	reg |= CR0_EXTCLK_ENA; +#else /* CONFIG_SYS_EXT_SERIAL_CLOCK */ +	clk = gd->cpu_clk; +#ifdef CONFIG_SYS_405_UART_ERRATA_59 +	udiv = 31;			/* Errata 59: stuck at 31 */ +#else /* CONFIG_SYS_405_UART_ERRATA_59 */ +	{ +		u32 tmp = CONFIG_SYS_BASE_BAUD * 16; + +		udiv = (clk + tmp / 2) / tmp; +	} +	if (udiv > UDIV_MAX)                    /* max. n bits for udiv */ +		udiv = UDIV_MAX; +#endif /* CONFIG_SYS_405_UART_ERRATA_59 */ +#endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */ +	reg |= (udiv - 1) << CR0_UDIV_POS;	/* set the UART divisor */ +	mtdcr (CPC0_CR0, reg); +#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK +	clk = CONFIG_SYS_EXT_SERIAL_CLOCK; +#else +	clk = CONFIG_SYS_BASE_BAUD * 16; +#endif +#endif + +#if defined(CONFIG_405EP) +	{ +		u32 tmp = CONFIG_SYS_BASE_BAUD * 16; + +		reg = mfdcr(CPC0_UCR) & ~(UCR0_MASK | UCR1_MASK); +		clk = gd->cpu_clk; +		udiv = (clk + tmp / 2) / tmp; +		if (udiv > UDIV_MAX)                    /* max. n bits for udiv */ +			udiv = UDIV_MAX; +	} +	reg |= udiv << UCR0_UDIV_POS;	        /* set the UART divisor */ +	reg |= udiv << UCR1_UDIV_POS;	        /* set the UART divisor */ +	mtdcr(CPC0_UCR, reg); +	clk = CONFIG_SYS_BASE_BAUD * 16; +#endif /* CONFIG_405EP */ + +#if defined(CONFIG_405EX) || defined(CONFIG_440) +	MFREG(UART0_SDR, reg); +	reg &= ~CR0_MASK; + +#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK +	reg |= CR0_EXTCLK_ENA; +	udiv = 1; +	clk = CONFIG_SYS_EXT_SERIAL_CLOCK; +#else /* CONFIG_SYS_EXT_SERIAL_CLOCK */ +	clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16; +#endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */ + +	reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS;	/* set the UART divisor */ + +	/* +	 * Configure input clock to baudrate generator for all +	 * available serial ports here +	 */ +	MTREG(UART0_SDR, reg); +#if defined(UART1_SDR) +	MTREG(UART1_SDR, reg); +#endif +#if defined(UART2_SDR) +	MTREG(UART2_SDR, reg); +#endif +#if defined(UART3_SDR) +	MTREG(UART3_SDR, reg); +#endif +#endif /* CONFIG_405EX ... */ + +#if defined(CONFIG_405EZ) +	clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16; +#endif /* CONFIG_405EZ */ + +	/* +	 * Correct UART frequency in bd-info struct now that +	 * the UART divisor is available +	 */ +#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK +	gd->arch.uart_clk = CONFIG_SYS_EXT_SERIAL_CLOCK; +#else +	get_sys_info(&sys_info); +	gd->arch.uart_clk = sys_info.freqUART / udiv; +#endif + +	return clk; +} +#endif	/* CONFIG_405GP */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/Makefile b/roms/u-boot/arch/powerpc/cpu/ppc4xx/Makefile new file mode 100644 index 00000000..4b792ae2 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/Makefile @@ -0,0 +1,49 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier:	GPL-2.0+ +# + +extra-y	:= resetvec.o +extra-y	+= start.o + +obj-y	:= cache.o +obj-y	+= dcr.o +obj-y	+= kgdb.o + +obj-y	+= 40x_spd_sdram.o + +obj-y	+= 44x_spd_ddr.o +obj-$(CONFIG_SDRAM_PPC4xx_IBM_DDR2) += 44x_spd_ddr2.o +obj-$(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) += 4xx_ibm_ddr2_autocalib.o +obj-y	+= 4xx_pci.o +obj-y	+= 4xx_pcie.o +obj-y	+= bedbug_405.o +obj-$(CONFIG_CMD_CHIP_CONFIG)	+= cmd_chip_config.o +obj-y	+= cpu.o +obj-y	+= cpu_init.o +obj-y	+= denali_data_eye.o +obj-y	+= denali_spd_ddr2.o +obj-y	+= ecc.o +obj-$(CONFIG_CMD_ECCTEST) += cmd_ecctest.o +obj-y	+= fdt.o +obj-y	+= interrupts.o +obj-$(CONFIG_CMD_REGINFO) += reginfo.o +obj-y	+= sdram.o +obj-y	+= speed.o +obj-y	+= tlb.o +obj-y	+= traps.o +obj-y	+= usb.o +obj-y	+= usb_ohci.o +obj-$(CONFIG_XILINX_440) += xilinx_irq.o +ifndef CONFIG_XILINX_440 +obj-y	+= 4xx_uart.o +obj-y	+= gpio.o +obj-y	+= miiphy.o +obj-y	+= uic.o +endif + +ifdef CONFIG_SPL_BUILD +obj-y += spl_boot.o +endif diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/bedbug_405.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/bedbug_405.c new file mode 100644 index 00000000..e1500cc0 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/bedbug_405.c @@ -0,0 +1,308 @@ +/* + * Bedbug Functions specific to the PPC405 chip + */ + +#include <common.h> +#include <command.h> +#include <linux/ctype.h> +#include <bedbug/type.h> +#include <bedbug/bedbug.h> +#include <bedbug/regs.h> +#include <bedbug/ppc.h> + +#if defined(CONFIG_CMD_BEDBUG) && defined(CONFIG_4xx) + +#define MAX_BREAK_POINTS 4 + +extern CPU_DEBUG_CTX bug_ctx; + +void bedbug405_init __P ((void)); +void bedbug405_do_break __P ((cmd_tbl_t *, int, int, char * const [])); +void bedbug405_break_isr __P ((struct pt_regs *)); +int bedbug405_find_empty __P ((void)); +int bedbug405_set __P ((int, unsigned long)); +int bedbug405_clear __P ((int)); + + +/* ====================================================================== + * Initialize the global bug_ctx structure for the AMCC PPC405.	Clear all + * of the breakpoints. + * ====================================================================== */ + +void bedbug405_init (void) +{ +	int i; + +	/* -------------------------------------------------- */ + +	bug_ctx.hw_debug_enabled = 0; +	bug_ctx.stopped = 0; +	bug_ctx.current_bp = 0; +	bug_ctx.regs = NULL; + +	bug_ctx.do_break = bedbug405_do_break; +	bug_ctx.break_isr = bedbug405_break_isr; +	bug_ctx.find_empty = bedbug405_find_empty; +	bug_ctx.set = bedbug405_set; +	bug_ctx.clear = bedbug405_clear; + +	for (i = 1; i <= MAX_BREAK_POINTS; ++i) +		(*bug_ctx.clear) (i); + +	puts ("BEDBUG:ready\n"); +	return; +}	/* bedbug_init_breakpoints */ + + + +/* ====================================================================== + * Set/clear/show one of the hardware breakpoints for the 405.	The "off" + * string will disable a specific breakpoint.  The "show" string will + * display the current breakpoints.  Otherwise an address will set a + * breakpoint at that address.	Setting a breakpoint uses the CPU-specific + * set routine which will assign a breakpoint number. + * ====================================================================== */ + +void bedbug405_do_break (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ +	long addr = 0;		/* Address to break at  */ +	int which_bp;		/* Breakpoint number    */ + +	/* -------------------------------------------------- */ + +	if (argc < 2) { +		cmd_usage(cmdtp); +		return; +	} + +	/* Turn off a breakpoint */ + +	if (strcmp (argv[1], "off") == 0) { +		if (bug_ctx.hw_debug_enabled == 0) { +			printf ("No breakpoints enabled\n"); +			return; +		} + +		which_bp = simple_strtoul (argv[2], NULL, 10); + +		if (bug_ctx.clear) +			(*bug_ctx.clear) (which_bp); + +		printf ("Breakpoint %d removed\n", which_bp); +		return; +	} + +	/* Show a list of breakpoints */ + +	if (strcmp (argv[1], "show") == 0) { +		for (which_bp = 1; which_bp <= MAX_BREAK_POINTS; ++which_bp) { + +			switch (which_bp) { +			case 1: +				addr = GET_IAC1 (); +				break; +			case 2: +				addr = GET_IAC2 (); +				break; +			case 3: +				addr = GET_IAC3 (); +				break; +			case 4: +				addr = GET_IAC4 (); +				break; +			} + +			printf ("Breakpoint [%d]: ", which_bp); +			if (addr == 0) +				printf ("NOT SET\n"); +			else +				disppc ((unsigned char *) addr, 0, 1, bedbug_puts, +						F_RADHEX); +		} +		return; +	} + +	/* Set a breakpoint at the address */ + +	if (!isdigit (argv[1][0])) { +		cmd_usage(cmdtp); +		return; +	} + +	addr = simple_strtoul (argv[1], NULL, 16) & 0xfffffffc; + +	if ((bug_ctx.set) && (which_bp = (*bug_ctx.set) (0, addr)) > 0) { +		printf ("Breakpoint [%d]: ", which_bp); +		disppc ((unsigned char *) addr, 0, 1, bedbug_puts, F_RADHEX); +	} + +	return; +}	/* bedbug405_do_break */ + + + +/* ====================================================================== + * Handle a breakpoint.	 First determine which breakpoint was hit by + * looking at the DeBug Status Register (DBSR), clear the breakpoint + * and enter a mini main loop.	Stay in the loop until the stopped flag + * in the debug context is cleared. + * ====================================================================== */ + +void bedbug405_break_isr (struct pt_regs *regs) +{ +	unsigned long dbsr_val;		/* Value of the DBSR    */ +	unsigned long addr = 0;		/* Address stopped at   */ + +	/* -------------------------------------------------- */ + +	dbsr_val = GET_DBSR (); + +	if (dbsr_val & DBSR_IA1) { +		bug_ctx.current_bp = 1; +		addr = GET_IAC1 (); +		SET_DBSR (DBSR_IA1);	/* Write a 1 to clear */ +	} else if (dbsr_val & DBSR_IA2) { +		bug_ctx.current_bp = 2; +		addr = GET_IAC2 (); +		SET_DBSR (DBSR_IA2);	/* Write a 1 to clear */ +	} else if (dbsr_val & DBSR_IA3) { +		bug_ctx.current_bp = 3; +		addr = GET_IAC3 (); +		SET_DBSR (DBSR_IA3);	/* Write a 1 to clear */ +	} else if (dbsr_val & DBSR_IA4) { +		bug_ctx.current_bp = 4; +		addr = GET_IAC4 (); +		SET_DBSR (DBSR_IA4);	/* Write a 1 to clear */ +	} + +	bedbug_main_loop (addr, regs); +	return; +}	/* bedbug405_break_isr */ + + + +/* ====================================================================== + * Look through all of the hardware breakpoints available to see if one + * is unused. + * ====================================================================== */ + +int bedbug405_find_empty (void) +{ +	/* -------------------------------------------------- */ + +	if (GET_IAC1 () == 0) +		return 1; + +	if (GET_IAC2 () == 0) +		return 2; + +	if (GET_IAC3 () == 0) +		return 3; + +	if (GET_IAC4 () == 0) +		return 4; + +	return 0; +}	/* bedbug405_find_empty */ + + + +/* ====================================================================== + * Set a breakpoint.  If 'which_bp' is zero then find an unused breakpoint + * number, otherwise reassign the given breakpoint.  If hardware debugging + * is not enabled, then turn it on via the MSR and DBCR0.  Set the break + * address in the appropriate IACx register and enable proper address + * beakpoint in DBCR0. + * ====================================================================== */ + +int bedbug405_set (int which_bp, unsigned long addr) +{ +	/* -------------------------------------------------- */ + +	/* Only look if which_bp == 0, else use which_bp */ +	if ((bug_ctx.find_empty) && (!which_bp) && +		(which_bp = (*bug_ctx.find_empty) ()) == 0) { +		printf ("All breakpoints in use\n"); +		return 0; +	} + +	if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { +		printf ("Invalid break point # %d\n", which_bp); +		return 0; +	} + +	if (!bug_ctx.hw_debug_enabled) { +		SET_MSR (GET_MSR () | 0x200);	/* set MSR[ DE ] */ +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IDM); +		bug_ctx.hw_debug_enabled = 1; +	} + +	switch (which_bp) { +	case 1: +		SET_IAC1 (addr); +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IA1); +		break; + +	case 2: +		SET_IAC2 (addr); +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IA2); +		break; + +	case 3: +		SET_IAC3 (addr); +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IA3); +		break; + +	case 4: +		SET_IAC4 (addr); +		SET_DBCR0 (GET_DBCR0 () | DBCR0_IA4); +		break; +	} + +	return which_bp; +}	/* bedbug405_set */ + + + +/* ====================================================================== + * Disable a specific breakoint by setting the appropriate IACx register + * to zero and claring the instruction address breakpoint in DBCR0. + * ====================================================================== */ + +int bedbug405_clear (int which_bp) +{ +	/* -------------------------------------------------- */ + +	if (which_bp < 1 || which_bp > MAX_BREAK_POINTS) { +		printf ("Invalid break point # (%d)\n", which_bp); +		return -1; +	} + +	switch (which_bp) { +	case 1: +		SET_IAC1 (0); +		SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA1); +		break; + +	case 2: +		SET_IAC2 (0); +		SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA2); +		break; + +	case 3: +		SET_IAC3 (0); +		SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA3); +		break; + +	case 4: +		SET_IAC4 (0); +		SET_DBCR0 (GET_DBCR0 () & ~DBCR0_IA4); +		break; +	} + +	return 0; +}	/* bedbug405_clear */ + + +/* ====================================================================== */ +#endif diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/cache.S b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cache.S new file mode 100644 index 00000000..2714c2f9 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cache.S @@ -0,0 +1,231 @@ +/* + * This file contains miscellaneous low-level functions. + *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <config.h> +#include <config.h> +#include <asm/ppc4xx.h> +#include <ppc_asm.tmpl> +#include <ppc_defs.h> +#include <asm/cache.h> +#include <asm/mmu.h> + +/* + * Flush instruction cache. + */ +_GLOBAL(invalidate_icache) +	iccci	r0,r0 +	isync +	blr + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * + * flush_icache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_icache_range) +	li	r5,L1_CACHE_BYTES-1 +	andc	r3,r3,r5 +	subf	r4,r3,r4 +	add	r4,r4,r5 +	srwi.	r4,r4,L1_CACHE_SHIFT +	beqlr +	mtctr	r4 +	mr	r6,r3 +1:	dcbst	0,r3 +	addi	r3,r3,L1_CACHE_BYTES +	bdnz	1b +	sync				/* wait for dcbst's to get to ram */ +	mtctr	r4 +2:	icbi	0,r6 +	addi	r6,r6,L1_CACHE_BYTES +	bdnz	2b +	sync				/* additional sync needed on g4 */ +	isync +	blr + +/* + * Write any modified data cache blocks out to memory. + * Does not invalidate the corresponding cache lines (especially for + * any corresponding instruction cache). + * + * clean_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(clean_dcache_range) +	li	r5,L1_CACHE_BYTES-1 +	andc	r3,r3,r5 +	subf	r4,r3,r4 +	add	r4,r4,r5 +	srwi.	r4,r4,L1_CACHE_SHIFT +	beqlr +	mtctr	r4 + +1:	dcbst	0,r3 +	addi	r3,r3,L1_CACHE_BYTES +	bdnz	1b +	sync				/* wait for dcbst's to get to ram */ +	blr + +/* + * Write any modified data cache blocks out to memory and invalidate them. + * Does not invalidate the corresponding instruction cache blocks. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) +	li	r5,L1_CACHE_BYTES-1 +	andc	r3,r3,r5 +	subf	r4,r3,r4 +	add	r4,r4,r5 +	srwi.	r4,r4,L1_CACHE_SHIFT +	beqlr +	mtctr	r4 + +1:	dcbf	0,r3 +	addi	r3,r3,L1_CACHE_BYTES +	bdnz	1b +	sync				/* wait for dcbst's to get to ram */ +	blr + +/* + * Like above, but invalidate the D-cache.  This is used by the 8xx + * to invalidate the cache so the PPC core doesn't get stale data + * from the CPM (no cache snooping here :-). + * + * invalidate_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(invalidate_dcache_range) +	li	r5,L1_CACHE_BYTES-1 +	andc	r3,r3,r5 +	subf	r4,r3,r4 +	add	r4,r4,r5 +	srwi.	r4,r4,L1_CACHE_SHIFT +	beqlr +	mtctr	r4 + +1:	dcbi	0,r3 +	addi	r3,r3,L1_CACHE_BYTES +	bdnz	1b +	sync				/* wait for dcbi's to get to ram */ +	blr + +/* + * 40x cores have 8K or 16K dcache and 32 byte line size. + * 44x has a 32K dcache and 32 byte line size. + * 8xx has 1, 2, 4, 8K variants. + * For now, cover the worst case of the 44x. + * Must be called with external interrupts disabled. + */ +#define CACHE_NWAYS     64 +#define CACHE_NLINES    32 + +_GLOBAL(flush_dcache) +	li	r4,(2 * CACHE_NWAYS * CACHE_NLINES) +	mtctr	r4 +	lis	r5,0 +1:	lwz	r3,0(r5)		/* Load one word from every line */ +	addi	r5,r5,L1_CACHE_BYTES +	bdnz	1b +	sync +	blr + +_GLOBAL(invalidate_dcache) +	addi	r6,0,0x0000		/* clear GPR 6 */ +	/* Do loop for # of dcache congruence classes. */ +	lis	r7,(CONFIG_SYS_DCACHE_SIZE / L1_CACHE_BYTES / 2)@ha	/* TBS for large sized cache */ +	ori	r7,r7,(CONFIG_SYS_DCACHE_SIZE / L1_CACHE_BYTES / 2)@l +					/* NOTE: dccci invalidates both */ +	mtctr	r7			/* ways in the D cache */ +..dcloop: +	dccci	0,r6			/* invalidate line */ +	addi	r6,r6,L1_CACHE_BYTES	/* bump to next line */ +	bdnz	..dcloop +	sync +	blr + +/* + * Cache functions. + * + * NOTE: currently the 440s run with dcache _disabled_ once relocated to DRAM, + * although for some cache-ralated calls stubs have to be provided to satisfy + * symbols resolution. + * Icache-related functions are used in POST framework. + * + */ +#ifdef CONFIG_440 + +       .globl  dcache_disable +       .globl  dcache_enable +       .globl  icache_disable +       .globl  icache_enable +dcache_disable: +dcache_enable: +icache_disable: +icache_enable: +	blr + +	.globl	dcache_status +	.globl	icache_status +dcache_status: +icache_status: +	mr	r3,  0 +	blr + +#else /* CONFIG_440 */ + +	.globl	icache_enable +icache_enable: +	mflr	r8 +	bl	invalidate_icache +	mtlr	r8 +	isync +	addis	r3,r0, 0xc000	      /* set bit 0 */ +	mticcr	r3 +	blr + +	.globl	icache_disable +icache_disable: +	addis	r3,r0, 0x0000	      /* clear bit 0 */ +	mticcr	r3 +	isync +	blr + +	.globl	icache_status +icache_status: +	mficcr	r3 +	srwi	r3, r3, 31	/* >>31 => select bit 0 */ +	blr + +	.globl	dcache_enable +dcache_enable: +	mflr	r8 +	bl	invalidate_dcache +	mtlr	r8 +	isync +	addis	r3,r0, 0x8000	      /* set bit 0 */ +	mtdccr	r3 +	blr + +	.globl	dcache_disable +dcache_disable: +	mflr	r8 +	bl	flush_dcache +	mtlr	r8 +	addis	r3,r0, 0x0000	      /* clear bit 0 */ +	mtdccr	r3 +	blr + +	.globl	dcache_status +dcache_status: +	mfdccr	r3 +	srwi	r3, r3, 31	/* >>31 => select bit 0 */ +	blr + +#endif /* CONFIG_440 */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/cmd_chip_config.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cmd_chip_config.c new file mode 100644 index 00000000..4e9a40d5 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cmd_chip_config.c @@ -0,0 +1,131 @@ +/* + * (C) Copyright 2008-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * (C) Copyright 2009 + * Dirk Eibach,  Guntermann & Drunck GmbH, eibach@gdsys.de + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <i2c.h> +#include <asm/ppc4xx_config.h> +#include <asm/io.h> + +static void print_configs(int cur_config_nr) +{ +	int i; + +	for (i = 0; i < ppc4xx_config_count; i++) { +		printf("%-16s - %s", ppc4xx_config_val[i].label, +		       ppc4xx_config_val[i].description); +		if (i == cur_config_nr) +			printf(" ***"); +		printf("\n"); +	} + +} + +static int do_chip_config(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	int i; +	int ret; +	int cur_config_nr = -1; +	u8 cur_config[CONFIG_4xx_CONFIG_BLOCKSIZE]; + +	/* +	 * First switch to correct I2C bus. This is I2C bus 0 +	 * for all currently available 4xx derivats. +	 */ +	i2c_set_bus_num(0); + +#ifdef CONFIG_CMD_EEPROM +	ret = eeprom_read(CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, +			  CONFIG_4xx_CONFIG_I2C_EEPROM_OFFSET, +			  cur_config, CONFIG_4xx_CONFIG_BLOCKSIZE); +#else +	ret = i2c_read(CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, +		       CONFIG_4xx_CONFIG_I2C_EEPROM_OFFSET, +		       1, cur_config, CONFIG_4xx_CONFIG_BLOCKSIZE); +#endif +	if (ret) { +		printf("Error reading EEPROM at addr 0x%x\n", +		       CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR); +		return -1; +	} + +	/* +	 * Search the current configuration +	 */ +	for (i = 0; i < ppc4xx_config_count; i++) { +		if (memcmp(cur_config, ppc4xx_config_val[i].val, +			   CONFIG_4xx_CONFIG_BLOCKSIZE) == 0) +			cur_config_nr = i; +	} + +	if (cur_config_nr == -1) { +		printf("Warning: The I2C bootstrap values don't match any" +		       " of the available options!\n"); +		printf("I2C bootstrap EEPROM values are (I2C address 0x%02x):\n", +			CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR); +		for (i = 0; i < CONFIG_4xx_CONFIG_BLOCKSIZE; i++) { +			printf("%02x ", cur_config[i]); +		} +		printf("\n"); +	} + +	if (argc < 2) { +		printf("Available configurations (I2C address 0x%02x):\n", +		       CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR); +		print_configs(cur_config_nr); +		return 0; +	} + +	for (i = 0; i < ppc4xx_config_count; i++) { +		/* +		 * Search for configuration name/label +		 */ +		if (strcmp(argv[1], ppc4xx_config_val[i].label) == 0) { +			printf("Using configuration:\n%-16s - %s\n", +			       ppc4xx_config_val[i].label, +			       ppc4xx_config_val[i].description); + +#ifdef CONFIG_CMD_EEPROM +			ret = eeprom_write(CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, +					   CONFIG_4xx_CONFIG_I2C_EEPROM_OFFSET, +					   ppc4xx_config_val[i].val, +					   CONFIG_4xx_CONFIG_BLOCKSIZE); +#else +			ret = i2c_write(CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, +					CONFIG_4xx_CONFIG_I2C_EEPROM_OFFSET, +					1, ppc4xx_config_val[i].val, +					CONFIG_4xx_CONFIG_BLOCKSIZE); +#endif +			udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000); +			if (ret) { +				printf("Error updating EEPROM at addr 0x%x\n", +				       CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR); +				return -1; +			} + +			printf("done (dump via 'i2c md %x 0.1 %x')\n", +			       CONFIG_4xx_CONFIG_I2C_EEPROM_ADDR, +			       CONFIG_4xx_CONFIG_BLOCKSIZE); +			printf("Reset the board for the changes to" +			       " take effect\n"); +			return 0; +		} +	} + +	printf("Configuration %s not found!\n", argv[1]); +	print_configs(cur_config_nr); +	return -1; +} + +U_BOOT_CMD( +	chip_config,	2,	0,	do_chip_config, +	"program the I2C bootstrap EEPROM", +	"[config-label]" +); diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/cmd_ecctest.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cmd_ecctest.c new file mode 100644 index 00000000..958ba6c9 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cmd_ecctest.c @@ -0,0 +1,262 @@ +/* + * (C) Copyright 2010 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/ppc4xx.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/cache.h> + +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) || \ +    defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2) +#if defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC) + +#if defined(CONFIG_405EX) +/* + * Currently only 405EX uses 16bit data bus width as an alternative + * option to 32bit data width (SDRAM0_MCOPT1_WDTH) + */ +#define SDRAM_DATA_ALT_WIDTH	2 +#else +#define SDRAM_DATA_ALT_WIDTH	8 +#endif + +#if defined(CONFIG_SYS_OCM_BASE) +#define CONFIG_FUNC_ISRAM_ADDR	CONFIG_SYS_OCM_BASE +#endif + +#if defined(CONFIG_SYS_ISRAM_BASE) +#define CONFIG_FUNC_ISRAM_ADDR	CONFIG_SYS_ISRAM_BASE +#endif + +#if !defined(CONFIG_FUNC_ISRAM_ADDR) +#error "No internal SRAM/OCM provided!" +#endif + +#define force_inline inline __attribute__ ((always_inline)) + +static inline void machine_check_disable(void) +{ +	mtmsr(mfmsr() & ~MSR_ME); +} + +static inline void machine_check_enable(void) +{ +	mtmsr(mfmsr() | MSR_ME); +} + +/* + * These helper functions need to be inlined, since they + * are called from the functions running from internal SRAM. + * SDRAM operation is forbidden at that time, so calling + * functions in SDRAM has to be avoided. + */ +static force_inline void wait_ddr_idle(void) +{ +	u32 val; + +	do { +		mfsdram(SDRAM_MCSTAT, val); +	} while ((val & SDRAM_MCSTAT_IDLE_MASK) == SDRAM_MCSTAT_IDLE_NOT); +} + +static force_inline void recalibrate_ddr(void) +{ +	u32 val; + +	/* +	 * Rewrite RQDC & RFDC to calibrate again. If this is not +	 * done, the SDRAM controller is working correctly after +	 * changing the MCOPT1_MCHK bits. +	 */ +	mfsdram(SDRAM_RQDC, val); +	mtsdram(SDRAM_RQDC, val); +	mfsdram(SDRAM_RFDC, val); +	mtsdram(SDRAM_RFDC, val); +} + +static force_inline void set_mcopt1_mchk(u32 bits) +{ +	u32 val; + +	wait_ddr_idle(); +	mfsdram(SDRAM_MCOPT1, val); +	mtsdram(SDRAM_MCOPT1, (val & ~SDRAM_MCOPT1_MCHK_MASK) | bits); +	recalibrate_ddr(); +} + +/* + * The next 2 functions are copied to internal SRAM/OCM and run + * there. No function calls allowed here. No SDRAM acitivity should + * be done here. + */ +static void inject_ecc_error(void *ptr, int par) +{ +	/* +	 * Taken from PPC460EX/EXr/GT users manual (Rev 1.21) +	 * 22.2.17.13 ECC Diagnostics +	 * +	 * Items 1 ... 5 are already done by now, running from RAM +	 * with ECC enabled +	 */ + +	out_be32(ptr, 0x00000000); +	in_be32(ptr); + +	/* 6. Set memory controller to no error checking */ +	set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_NON); + +	/* 7. Modify one or two bits for error simulation */ +	if (par == 1) +		out_be32(ptr, in_be32(ptr) ^ 0x00000001); +	else +		out_be32(ptr, in_be32(ptr) ^ 0x00000003); + +	/* 8. Wait for SDRAM idle */ +	in_be32(ptr); +	set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_CHK_REP); + +	/* Wait for SDRAM idle */ +	wait_ddr_idle(); + +	/* Continue with 9. in calling function... */ +} + +static void rewrite_ecc_parity(void *ptr, int par) +{ +	u32 current_address = (u32)ptr; +	u32 end_address; +	u32 address_increment; +	u32 mcopt1; + +	/* +	 * Fill ECC parity byte again. Otherwise further accesses to +	 * the failure address will result in exceptions. +	 */ + +	/* Wait for SDRAM idle */ +	in_be32(0x00000000); +	set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_GEN); + +	/* ECC bit set method for non-cached memory */ +	mfsdram(SDRAM_MCOPT1, mcopt1); +	if ((mcopt1 & SDRAM_MCOPT1_DMWD_MASK) == SDRAM_MCOPT1_DMWD_32) +		address_increment = 4; +	else +		address_increment = SDRAM_DATA_ALT_WIDTH; +	end_address = current_address + CONFIG_SYS_CACHELINE_SIZE; + +	while (current_address < end_address) { +		*((unsigned long *)current_address) = 0; +		current_address += address_increment; +	} + +	set_mcopt1_mchk(SDRAM_MCOPT1_MCHK_CHK_REP); + +	/* Wait for SDRAM idle */ +	wait_ddr_idle(); +} + +static int do_ecctest(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	u32 old_val; +	u32 val; +	u32 *ptr; +	void (*sram_func)(u32 *, int); +	int error; + +	if (argc < 3) { +		return cmd_usage(cmdtp); +	} + +	ptr = (u32 *)simple_strtoul(argv[1], NULL, 16); +	error = simple_strtoul(argv[2], NULL, 16); +	if ((error < 1) || (error > 2)) { +		return cmd_usage(cmdtp); +	} + +	printf("Using address %p for %d bit ECC error injection\n", +	       ptr, error); + +	/* +	 * Save value to restore it later on +	 */ +	old_val = in_be32(ptr); + +	/* +	 * Copy ECC injection function into internal SRAM/OCM +	 */ +	sram_func = (void *)CONFIG_FUNC_ISRAM_ADDR; +	memcpy((void *)CONFIG_FUNC_ISRAM_ADDR, inject_ecc_error, 0x10000); + +	/* +	 * Disable interrupts and exceptions before calling this +	 * function in internal SRAM/OCM +	 */ +	disable_interrupts(); +	machine_check_disable(); +	eieio(); + +	/* +	 * Jump to ECC simulation function in internal SRAM/OCM +	 */ +	(*sram_func)(ptr, error); + +	/* 10. Read the corresponding address */ +	val = in_be32(ptr); + +	/* +	 * Read and print ECC status register/info: +	 * The faulting address is only known upon uncorrectable ECC +	 * errors. +	 */ +	mfsdram(SDRAM_ECCES, val); +	if (val & SDRAM_ECCES_CE) +		printf("ECC: Correctable error\n"); +	if (val & SDRAM_ECCES_UE) { +		printf("ECC: Uncorrectable error at 0x%02x%08x\n", +		       mfdcr(SDRAM_ERRADDULL), mfdcr(SDRAM_ERRADDLLL)); +	} + +	/* +	 * Clear pending interrupts/exceptions +	 */ +	mtsdram(SDRAM_ECCES, 0xffffffff); +	mtdcr(SDRAM_ERRSTATLL, 0xff000000); +	set_mcsr(get_mcsr()); + +	/* Now enable interrupts and exceptions again */ +	eieio(); +	machine_check_enable(); +	enable_interrupts(); + +	/* +	 * The ECC parity byte need to be re-written for the +	 * corresponding address. Otherwise future accesses to it +	 * will result in exceptions. +	 * +	 * Jump to ECC parity generation function +	 */ +	memcpy((void *)CONFIG_FUNC_ISRAM_ADDR, rewrite_ecc_parity, 0x10000); +	(*sram_func)(ptr, 0); + +	/* +	 * Restore value in corresponding address +	 */ +	out_be32(ptr, old_val); + +	return 0; +} + +U_BOOT_CMD( +	ecctest,	3,	0,	do_ecctest, +	"Test ECC by single and double error bit injection", +	"address 1/2" +); + +#endif /* defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC) */ +#endif /* defined(CONFIG_SDRAM_PPC4xx_IBM_DDR)... */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/config.mk b/roms/u-boot/arch/powerpc/cpu/ppc4xx/config.mk new file mode 100644 index 00000000..102f069f --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/config.mk @@ -0,0 +1,17 @@ +# +# (C) Copyright 2000-2010 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier:	GPL-2.0+ +# + +PLATFORM_CPPFLAGS += -DCONFIG_4xx -mstring -msoft-float + +cfg=$(shell grep configs $(objtree)/include/config.h | sed 's/.*<\(configs.*\)>/\1/') +is440:=$(shell grep CONFIG_440 $(srctree)/include/$(cfg)) + +ifneq (,$(findstring CONFIG_440,$(is440))) +PLATFORM_CPPFLAGS += -Wa,-m440 -mcpu=440 +else +PLATFORM_CPPFLAGS += -Wa,-m405 -mcpu=405 +endif diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/cpu.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cpu.c new file mode 100644 index 00000000..d1fc7f3f --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cpu.c @@ -0,0 +1,719 @@ +/* + * (C) Copyright 2000-2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* + * CPU specific code + * + * written or collected and sometimes rewritten by + * Magnus Damm <damm@bitsmart.com> + * + * minor modifications by + * Wolfgang Denk <wd@denx.de> + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/cache.h> +#include <asm/ppc4xx.h> +#include <netdev.h> + +DECLARE_GLOBAL_DATA_PTR; + +void board_reset(void); + +/* + * To provide an interface to detect CPU number for boards that support + * more then one CPU, we implement the "weak" default functions here. + * + * Returns CPU number + */ +int __get_cpu_num(void) +{ +	return NA_OR_UNKNOWN_CPU; +} +int get_cpu_num(void) __attribute__((weak, alias("__get_cpu_num"))); + +#if defined(CONFIG_PCI) +#if defined(CONFIG_405GP) || \ +    defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ +    defined(CONFIG_440EPX) || defined(CONFIG_440GRX) + +#define PCI_ASYNC + +static int pci_async_enabled(void) +{ +#if defined(CONFIG_405GP) +	return (mfdcr(CPC0_PSR) & PSR_PCI_ASYNC_EN); +#endif + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ +    defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT) +	unsigned long val; + +	mfsdr(SDR0_SDSTP1, val); +	return (val & SDR0_SDSTP1_PAME_MASK); +#endif +} +#endif +#endif /* CONFIG_PCI */ + +#if defined(CONFIG_PCI) && \ +    !defined(CONFIG_405) && !defined(CONFIG_405EX) +int pci_arbiter_enabled(void) +{ +#if defined(CONFIG_405GP) +	return (mfdcr(CPC0_PSR) & PSR_PCI_ARBIT_EN); +#endif + +#if defined(CONFIG_405EP) +	return (mfdcr(CPC0_PCI) & CPC0_PCI_ARBIT_EN); +#endif + +#if defined(CONFIG_440GP) +	return (mfdcr(CPC0_STRP1) & CPC0_STRP1_PAE_MASK); +#endif + +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) || defined(CONFIG_440SPE) +	unsigned long val; + +	mfsdr(SDR0_XCR0, val); +	return (val & SDR0_XCR0_PAE_MASK); +#endif +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ +    defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT) +	unsigned long val; + +	mfsdr(SDR0_PCI0, val); +	return (val & SDR0_PCI0_PAE_MASK); +#endif +} +#endif + +#if defined(CONFIG_405EP) +#define I2C_BOOTROM + +static int i2c_bootrom_enabled(void) +{ +#if defined(CONFIG_405EP) +	return (mfdcr(CPC0_BOOT) & CPC0_BOOT_SEP); +#else +	unsigned long val; + +	mfsdr(SDR0_SDCS0, val); +	return (val & SDR0_SDCS_SDD); +#endif +} +#endif + +#if defined(CONFIG_440GX) +#define SDR0_PINSTP_SHIFT	29 +static char *bootstrap_str[] = { +	"EBC (16 bits)", +	"EBC (8 bits)", +	"EBC (32 bits)", +	"EBC (8 bits)", +	"PCI", +	"I2C (Addr 0x54)", +	"Reserved", +	"I2C (Addr 0x50)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'B', 'D', 'E', 'x', 'F' }; +#endif + +#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) +#define SDR0_PINSTP_SHIFT	30 +static char *bootstrap_str[] = { +	"EBC (8 bits)", +	"PCI", +	"I2C (Addr 0x54)", +	"I2C (Addr 0x50)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D'}; +#endif + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) +#define SDR0_PINSTP_SHIFT	29 +static char *bootstrap_str[] = { +	"EBC (8 bits)", +	"PCI", +	"NAND (8 bits)", +	"EBC (16 bits)", +	"EBC (16 bits)", +	"I2C (Addr 0x54)", +	"PCI", +	"I2C (Addr 0x52)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'G', 'F', 'H' }; +#endif + +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +#define SDR0_PINSTP_SHIFT	29 +static char *bootstrap_str[] = { +	"EBC (8 bits)", +	"EBC (16 bits)", +	"EBC (16 bits)", +	"NAND (8 bits)", +	"PCI", +	"I2C (Addr 0x54)", +	"PCI", +	"I2C (Addr 0x52)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'G', 'F', 'H' }; +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define SDR0_PINSTP_SHIFT	29 +static char *bootstrap_str[] = { +	"EBC (8 bits)", +	"EBC (16 bits)", +	"PCI", +	"PCI", +	"EBC (16 bits)", +	"NAND (8 bits)", +	"I2C (Addr 0x54)",	/* A8 */ +	"I2C (Addr 0x52)",	/* A4 */ +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; +#endif + +#if defined(CONFIG_460SX) +#define SDR0_PINSTP_SHIFT	29 +static char *bootstrap_str[] = { +	"EBC (8 bits)", +	"EBC (16 bits)", +	"EBC (32 bits)", +	"NAND (8 bits)", +	"I2C (Addr 0x54)",      /* A8 */ +	"I2C (Addr 0x52)",      /* A4 */ +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'G' }; +#endif + +#if defined(CONFIG_405EZ) +#define SDR0_PINSTP_SHIFT	28 +static char *bootstrap_str[] = { +	"EBC (8 bits)", +	"SPI (fast)", +	"NAND (512 page, 4 addr cycle)", +	"I2C (Addr 0x50)", +	"EBC (32 bits)", +	"I2C (Addr 0x50)", +	"NAND (2K page, 5 addr cycle)", +	"I2C (Addr 0x50)", +	"EBC (16 bits)", +	"Reserved", +	"NAND (2K page, 4 addr cycle)", +	"I2C (Addr 0x50)", +	"NAND (512 page, 3 addr cycle)", +	"I2C (Addr 0x50)", +	"SPI (slow)", +	"I2C (Addr 0x50)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', \ +				 'I', 'x', 'K', 'L', 'M', 'N', 'O', 'P' }; +#endif + +#if defined(CONFIG_405EX) +#define SDR0_PINSTP_SHIFT	29 +static char *bootstrap_str[] = { +	"EBC (8 bits)", +	"EBC (16 bits)", +	"EBC (16 bits)", +	"NAND (8 bits)", +	"NAND (8 bits)", +	"I2C (Addr 0x54)", +	"EBC (8 bits)", +	"I2C (Addr 0x52)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'G', 'F', 'H' }; +#endif +#if defined(CONFIG_APM821XX) +#define SDR0_PINSTP_SHIFT       29 +static char *bootstrap_str[] = { +	"RESERVED", +	"RESERVED", +	"RESERVED", +	"NAND (8 bits)", +	"NOR  (8 bits)", +	"NOR  (8 bits) w/PLL Bypassed", +	"I2C (Addr 0x54)", +	"I2C (Addr 0x52)", +}; +static char bootstrap_char[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; +#endif + +#if defined(SDR0_PINSTP_SHIFT) +static int bootstrap_option(void) +{ +	unsigned long val; + +	mfsdr(SDR0_PINSTP, val); +	return ((val & 0xf0000000) >> SDR0_PINSTP_SHIFT); +} +#endif /* SDR0_PINSTP_SHIFT */ + + +#if defined(CONFIG_440GP) +static int do_chip_reset (unsigned long sys0, unsigned long sys1) +{ +	/* Changes to CPC0_SYS0 and CPC0_SYS1 require chip +	 * reset. +	 */ +	mtdcr (CPC0_CR0, mfdcr (CPC0_CR0) | 0x80000000);	/* Set SWE */ +	mtdcr (CPC0_SYS0, sys0); +	mtdcr (CPC0_SYS1, sys1); +	mtdcr (CPC0_CR0, mfdcr (CPC0_CR0) & ~0x80000000);	/* Clr SWE */ +	mtspr (SPRN_DBCR0, 0x20000000);	/* Reset the chip */ + +	return 1; +} +#endif /* CONFIG_440GP */ + + +int checkcpu (void) +{ +#if !defined(CONFIG_405)	/* not used on Xilinx 405 FPGA implementations */ +	uint pvr = get_pvr(); +	ulong clock = gd->cpu_clk; +	char buf[32]; +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +	u32 reg; +#endif + +	char addstr[64] = ""; +	sys_info_t sys_info; +	int cpu_num; + +	cpu_num = get_cpu_num(); +	if (cpu_num >= 0) +		printf("CPU%d:  ", cpu_num); +	else +		puts("CPU:   "); + +	get_sys_info(&sys_info); + +#if defined(CONFIG_XILINX_440) +	puts("IBM PowerPC "); +#else +	puts("AMCC PowerPC "); +#endif + +	switch (pvr) { + +#if !defined(CONFIG_440) +	case PVR_405GP_RB: +		puts("405GP Rev. B"); +		break; + +	case PVR_405GP_RC: +		puts("405GP Rev. C"); +		break; + +	case PVR_405GP_RD: +		puts("405GP Rev. D"); +		break; + +	case PVR_405GP_RE: +		puts("405GP Rev. E"); +		break; + +	case PVR_405GPR_RB: +		puts("405GPr Rev. B"); +		break; + +	case PVR_405EP_RB: +		puts("405EP Rev. B"); +		break; + +	case PVR_405EZ_RA: +		puts("405EZ Rev. A"); +		break; + +	case PVR_405EX1_RA: +		puts("405EX Rev. A"); +		strcpy(addstr, "Security support"); +		break; + +	case PVR_405EXR2_RA: +		puts("405EXr Rev. A"); +		strcpy(addstr, "No Security support"); +		break; + +	case PVR_405EX1_RC: +		puts("405EX Rev. C"); +		strcpy(addstr, "Security support"); +		break; + +	case PVR_405EX2_RC: +		puts("405EX Rev. C"); +		strcpy(addstr, "No Security support"); +		break; + +	case PVR_405EXR1_RC: +		puts("405EXr Rev. C"); +		strcpy(addstr, "Security support"); +		break; + +	case PVR_405EXR2_RC: +		puts("405EXr Rev. C"); +		strcpy(addstr, "No Security support"); +		break; + +	case PVR_405EX1_RD: +		puts("405EX Rev. D"); +		strcpy(addstr, "Security support"); +		break; + +	case PVR_405EX2_RD: +		puts("405EX Rev. D"); +		strcpy(addstr, "No Security support"); +		break; + +	case PVR_405EXR1_RD: +		puts("405EXr Rev. D"); +		strcpy(addstr, "Security support"); +		break; + +	case PVR_405EXR2_RD: +		puts("405EXr Rev. D"); +		strcpy(addstr, "No Security support"); +		break; + +#else /* CONFIG_440 */ + +#if defined(CONFIG_440GP) +	case PVR_440GP_RB: +		puts("440GP Rev. B"); +		/* See errata 1.12: CHIP_4 */ +		if ((mfdcr(CPC0_SYS0) != mfdcr(CPC0_STRP0)) || +		    (mfdcr(CPC0_SYS1) != mfdcr(CPC0_STRP1)) ){ +			puts (  "\n\t CPC0_SYSx DCRs corrupted. " +				"Resetting chip ...\n"); +			udelay( 1000 * 1000 ); /* Give time for serial buf to clear */ +			do_chip_reset ( mfdcr(CPC0_STRP0), +					mfdcr(CPC0_STRP1) ); +		} +		break; + +	case PVR_440GP_RC: +		puts("440GP Rev. C"); +		break; +#endif /* CONFIG_440GP */ + +	case PVR_440GX_RA: +		puts("440GX Rev. A"); +		break; + +	case PVR_440GX_RB: +		puts("440GX Rev. B"); +		break; + +	case PVR_440GX_RC: +		puts("440GX Rev. C"); +		break; + +	case PVR_440GX_RF: +		puts("440GX Rev. F"); +		break; + +	case PVR_440EP_RA: +		puts("440EP Rev. A"); +		break; + +#ifdef CONFIG_440EP +	case PVR_440EP_RB: /* 440EP rev B and 440GR rev A have same PVR */ +		puts("440EP Rev. B"); +		break; + +	case PVR_440EP_RC: /* 440EP rev C and 440GR rev B have same PVR */ +		puts("440EP Rev. C"); +		break; +#endif /*  CONFIG_440EP */ + +#ifdef CONFIG_440GR +	case PVR_440GR_RA: /* 440EP rev B and 440GR rev A have same PVR */ +		puts("440GR Rev. A"); +		break; + +	case PVR_440GR_RB: /* 440EP rev C and 440GR rev B have same PVR */ +		puts("440GR Rev. B"); +		break; +#endif /* CONFIG_440GR */ + +#ifdef CONFIG_440EPX +	case PVR_440EPX1_RA: /* 440EPx rev A and 440GRx rev A have same PVR */ +		puts("440EPx Rev. A"); +		strcpy(addstr, "Security/Kasumi support"); +		break; + +	case PVR_440EPX2_RA: /* 440EPx rev A and 440GRx rev A have same PVR */ +		puts("440EPx Rev. A"); +		strcpy(addstr, "No Security/Kasumi support"); +		break; +#endif /* CONFIG_440EPX */ + +#ifdef CONFIG_440GRX +	case PVR_440GRX1_RA: /* 440EPx rev A and 440GRx rev A have same PVR */ +		puts("440GRx Rev. A"); +		strcpy(addstr, "Security/Kasumi support"); +		break; + +	case PVR_440GRX2_RA: /* 440EPx rev A and 440GRx rev A have same PVR */ +		puts("440GRx Rev. A"); +		strcpy(addstr, "No Security/Kasumi support"); +		break; +#endif /* CONFIG_440GRX */ + +	case PVR_440SP_6_RAB: +		puts("440SP Rev. A/B"); +		strcpy(addstr, "RAID 6 support"); +		break; + +	case PVR_440SP_RAB: +		puts("440SP Rev. A/B"); +		strcpy(addstr, "No RAID 6 support"); +		break; + +	case PVR_440SP_6_RC: +		puts("440SP Rev. C"); +		strcpy(addstr, "RAID 6 support"); +		break; + +	case PVR_440SP_RC: +		puts("440SP Rev. C"); +		strcpy(addstr, "No RAID 6 support"); +		break; + +	case PVR_440SPe_6_RA: +		puts("440SPe Rev. A"); +		strcpy(addstr, "RAID 6 support"); +		break; + +	case PVR_440SPe_RA: +		puts("440SPe Rev. A"); +		strcpy(addstr, "No RAID 6 support"); +		break; + +	case PVR_440SPe_6_RB: +		puts("440SPe Rev. B"); +		strcpy(addstr, "RAID 6 support"); +		break; + +	case PVR_440SPe_RB: +		puts("440SPe Rev. B"); +		strcpy(addstr, "No RAID 6 support"); +		break; + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +	case PVR_460EX_RA: +		puts("460EX Rev. A"); +		strcpy(addstr, "No Security/Kasumi support"); +		break; + +	case PVR_460EX_SE_RA: +		puts("460EX Rev. A"); +		strcpy(addstr, "Security/Kasumi support"); +		break; + +	case PVR_460EX_RB: +		puts("460EX Rev. B"); +		mfsdr(SDR0_ECID3, reg); +		if (reg & 0x00100000) +			strcpy(addstr, "No Security/Kasumi support"); +		else +			strcpy(addstr, "Security/Kasumi support"); +		break; + +	case PVR_460GT_RA: +		puts("460GT Rev. A"); +		strcpy(addstr, "No Security/Kasumi support"); +		break; + +	case PVR_460GT_SE_RA: +		puts("460GT Rev. A"); +		strcpy(addstr, "Security/Kasumi support"); +		break; + +	case PVR_460GT_RB: +		puts("460GT Rev. B"); +		mfsdr(SDR0_ECID3, reg); +		if (reg & 0x00100000) +			strcpy(addstr, "No Security/Kasumi support"); +		else +			strcpy(addstr, "Security/Kasumi support"); +		break; +#endif + +	case PVR_460SX_RA: +		puts("460SX Rev. A"); +		strcpy(addstr, "Security support"); +		break; + +	case PVR_460SX_RA_V1: +		puts("460SX Rev. A"); +		strcpy(addstr, "No Security support"); +		break; + +	case PVR_460GX_RA: +		puts("460GX Rev. A"); +		strcpy(addstr, "Security support"); +		break; + +	case PVR_460GX_RA_V1: +		puts("460GX Rev. A"); +		strcpy(addstr, "No Security support"); +		break; + +	case PVR_APM821XX_RA: +		puts("APM821XX Rev. A"); +		strcpy(addstr, "Security support"); +		break; + +	case PVR_VIRTEX5: +		puts("440x5 VIRTEX5"); +		break; +#endif /* CONFIG_440 */ + +	default: +		printf (" UNKNOWN (PVR=%08x)", pvr); +		break; +	} + +	printf (" at %s MHz (PLB=%lu OPB=%lu EBC=%lu", +		strmhz(buf, clock), +		sys_info.freqPLB / 1000000, +		get_OPB_freq() / 1000000, +		sys_info.freqEBC / 1000000); +#if defined(CONFIG_PCI) && \ +	(defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ +	 defined(CONFIG_440GR) || defined(CONFIG_440GRX)) +	printf(" PCI=%lu MHz", sys_info.freqPCI / 1000000); +#endif +	printf(")\n"); + +	if (addstr[0] != 0) +		printf("       %s\n", addstr); + +#if defined(I2C_BOOTROM) +	printf ("       I2C boot EEPROM %sabled\n", i2c_bootrom_enabled() ? "en" : "dis"); +#endif	/* I2C_BOOTROM */ +#if defined(SDR0_PINSTP_SHIFT) +	printf ("       Bootstrap Option %c - ", bootstrap_char[bootstrap_option()]); +	printf ("Boot ROM Location %s", bootstrap_str[bootstrap_option()]); +#ifdef CONFIG_NAND_U_BOOT +	puts(", booting from NAND"); +#endif /* CONFIG_NAND_U_BOOT */ +	putc('\n'); +#endif	/* SDR0_PINSTP_SHIFT */ + +#if defined(CONFIG_PCI) && !defined(CONFIG_405EX) +	printf ("       Internal PCI arbiter %sabled", pci_arbiter_enabled() ? "en" : "dis"); +#endif + +#if defined(CONFIG_PCI) && defined(PCI_ASYNC) +	if (pci_async_enabled()) { +		printf (", PCI async ext clock used"); +	} else { +		printf (", PCI sync clock at %lu MHz", +		       sys_info.freqPLB / sys_info.pllPciDiv / 1000000); +	} +#endif + +#if defined(CONFIG_PCI) && !defined(CONFIG_405EX) +	putc('\n'); +#endif + +#if defined(CONFIG_405EP) || defined(CONFIG_405EZ) || defined(CONFIG_405EX) +	printf("       16 KiB I-Cache 16 KiB D-Cache"); +#elif defined(CONFIG_440) +	printf("       32 KiB I-Cache 32 KiB D-Cache"); +#else +	printf("       16 KiB I-Cache %d KiB D-Cache", +	       ((pvr | 0x00000001) == PVR_405GPR_RB) ? 16 : 8); +#endif + +#endif /* !defined(CONFIG_405) */ + +	putc ('\n'); + +	return 0; +} + +int ppc440spe_revB() { +	unsigned int pvr; + +	pvr = get_pvr(); +	if ((pvr == PVR_440SPe_6_RB) || (pvr == PVR_440SPe_RB)) +		return 1; +	else +		return 0; +} + +/* ------------------------------------------------------------------------- */ + +int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +#if defined(CONFIG_BOARD_RESET) +	board_reset(); +#else +#if defined(CONFIG_SYS_4xx_RESET_TYPE) +	mtspr(SPRN_DBCR0, CONFIG_SYS_4xx_RESET_TYPE << 28); +#else +	/* +	 * Initiate system reset in debug control register DBCR +	 */ +	mtspr(SPRN_DBCR0, 0x30000000); +#endif /* defined(CONFIG_SYS_4xx_RESET_TYPE) */ +#endif /* defined(CONFIG_BOARD_RESET) */ + +	return 1; +} + + +/* + * Get timebase clock frequency + */ +unsigned long get_tbclk (void) +{ +	sys_info_t  sys_info; + +	get_sys_info(&sys_info); +	return (sys_info.freqProcessor); +} + + +#if defined(CONFIG_WATCHDOG) +void watchdog_reset(void) +{ +	int re_enable = disable_interrupts(); +	reset_4xx_watchdog(); +	if (re_enable) enable_interrupts(); +} + +void reset_4xx_watchdog(void) +{ +	/* +	 * Clear TSR(WIS) bit +	 */ +	mtspr(SPRN_TSR, 0x40000000); +} +#endif	/* CONFIG_WATCHDOG */ + +/* + * Initializes on-chip ethernet controllers. + * to override, implement board_eth_init() + */ +int cpu_eth_init(bd_t *bis) +{ +#if defined(CONFIG_PPC4xx_EMAC) +	ppc_4xx_eth_initialize(bis); +#endif +	return 0; +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/cpu_init.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cpu_init.c new file mode 100644 index 00000000..d465dcda --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/cpu_init.c @@ -0,0 +1,543 @@ +/* + * (C) Copyright 2000-2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <watchdog.h> +#include <asm/ppc4xx-emac.h> +#include <asm/processor.h> +#include <asm/ppc4xx-gpio.h> +#include <asm/ppc4xx.h> + +#if defined(CONFIG_405GP)  || defined(CONFIG_405EP) +DECLARE_GLOBAL_DATA_PTR; +#endif + +#ifndef CONFIG_SYS_PLL_RECONFIG +#define CONFIG_SYS_PLL_RECONFIG	0 +#endif + +#if defined(CONFIG_440EPX) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT) +static void reset_with_rli(void) +{ +	u32 reg; + +	/* +	 * Set reload inhibit so configuration will persist across +	 * processor resets +	 */ +	mfcpr(CPR0_ICFG, reg); +	reg |= CPR0_ICFG_RLI_MASK; +	mtcpr(CPR0_ICFG, reg); + +	/* Reset processor if configuration changed */ +	__asm__ __volatile__ ("sync; isync"); +	mtspr(SPRN_DBCR0, 0x20000000); +} +#endif + +void reconfigure_pll(u32 new_cpu_freq) +{ +#if defined(CONFIG_440EPX) +	int	reset_needed = 0; +	u32	reg, temp; +	u32	prbdv0, target_prbdv0,				/* CLK_PRIMBD */ +		fwdva, target_fwdva, fwdvb, target_fwdvb,	/* CLK_PLLD */ +		fbdv, target_fbdv, lfbdv, target_lfbdv, +		perdv0,	target_perdv0,				/* CLK_PERD */ +		spcid0,	target_spcid0;				/* CLK_SPCID */ + +	/* Reconfigure clocks if necessary. +	 * See PPC440EPx User's Manual, sections 8.2 and 14 */ +	if (new_cpu_freq == 667) { +		target_prbdv0 = 2; +		target_fwdva = 2; +		target_fwdvb = 4; +		target_fbdv = 20; +		target_lfbdv = 1; +		target_perdv0 = 4; +		target_spcid0 = 4; + +		mfcpr(CPR0_PRIMBD0, reg); +		temp = (reg & PRBDV_MASK) >> 24; +		prbdv0 = temp ? temp : 8; +		if (prbdv0 != target_prbdv0) { +			reg &= ~PRBDV_MASK; +			reg |= ((target_prbdv0 == 8 ? 0 : target_prbdv0) << 24); +			mtcpr(CPR0_PRIMBD0, reg); +			reset_needed = 1; +		} + +		mfcpr(CPR0_PLLD, reg); + +		temp = (reg & PLLD_FWDVA_MASK) >> 16; +		fwdva = temp ? temp : 16; + +		temp = (reg & PLLD_FWDVB_MASK) >> 8; +		fwdvb = temp ? temp : 8; + +		temp = (reg & PLLD_FBDV_MASK) >> 24; +		fbdv = temp ? temp : 32; + +		temp = (reg & PLLD_LFBDV_MASK); +		lfbdv = temp ? temp : 64; + +		if (fwdva != target_fwdva || fbdv != target_fbdv || lfbdv != target_lfbdv) { +			reg &= ~(PLLD_FWDVA_MASK | PLLD_FWDVB_MASK | +				 PLLD_FBDV_MASK | PLLD_LFBDV_MASK); +			reg |= ((target_fwdva == 16 ? 0 : target_fwdva) << 16) | +				((target_fwdvb == 8 ? 0 : target_fwdvb) << 8) | +				((target_fbdv == 32 ? 0 : target_fbdv) << 24) | +				(target_lfbdv == 64 ? 0 : target_lfbdv); +			mtcpr(CPR0_PLLD, reg); +			reset_needed = 1; +		} + +		mfcpr(CPR0_PERD, reg); +		perdv0 = (reg & CPR0_PERD_PERDV0_MASK) >> 24; +		if (perdv0 != target_perdv0) { +			reg &= ~CPR0_PERD_PERDV0_MASK; +			reg |= (target_perdv0 << 24); +			mtcpr(CPR0_PERD, reg); +			reset_needed = 1; +		} + +		mfcpr(CPR0_SPCID, reg); +		temp = (reg & CPR0_SPCID_SPCIDV0_MASK) >> 24; +		spcid0 = temp ? temp : 4; +		if (spcid0 != target_spcid0) { +			reg &= ~CPR0_SPCID_SPCIDV0_MASK; +			reg |= ((target_spcid0 == 4 ? 0 : target_spcid0) << 24); +			mtcpr(CPR0_SPCID, reg); +			reset_needed = 1; +		} +	} + +	/* Get current value of FWDVA.*/ +	mfcpr(CPR0_PLLD, reg); +	temp = (reg & PLLD_FWDVA_MASK) >> 16; + +	/* +	 * Check to see if FWDVA has been set to value of 1. if it has we must +	 * modify it. +	 */ +	if (temp == 1) { +		/* +		 * Load register that contains current boot strapping option. +		 */ +		mfcpr(CPR0_ICFG, reg); +		/* +		 * Strapping option bits (ICS) are already in correct position, +		 * only masking needed. +		 */ +		reg &= CPR0_ICFG_ICS_MASK; + +		if ((reg == BOOT_STRAP_OPTION_A) || (reg == BOOT_STRAP_OPTION_B) || +		    (reg == BOOT_STRAP_OPTION_D) || (reg == BOOT_STRAP_OPTION_E)) { +			mfcpr(CPR0_PLLD, reg); + +			/* Get current value of fbdv.  */ +			temp = (reg & PLLD_FBDV_MASK) >> 24; +			fbdv = temp ? temp : 32; + +			/* Get current value of lfbdv. */ +			temp = (reg & PLLD_LFBDV_MASK); +			lfbdv = temp ? temp : 64; + +			/* +			 * Get current value of FWDVA. Assign current FWDVA to +			 * new FWDVB. +			 */ +			mfcpr(CPR0_PLLD, reg); +			target_fwdvb = (reg & PLLD_FWDVA_MASK) >> 16; +			fwdvb = target_fwdvb ? target_fwdvb : 8; + +			/* +			 * Get current value of FWDVB. Assign current FWDVB to +			 * new FWDVA. +			 */ +			target_fwdva = (reg & PLLD_FWDVB_MASK) >> 8; +			fwdva = target_fwdva ? target_fwdva : 16; + +			/* +			 * Update CPR0_PLLD with switched FWDVA and FWDVB. +			 */ +			reg &= ~(PLLD_FWDVA_MASK | PLLD_FWDVB_MASK | +				PLLD_FBDV_MASK | PLLD_LFBDV_MASK); +			reg |= ((fwdva == 16 ? 0 : fwdva) << 16) | +				((fwdvb == 8 ? 0 : fwdvb) << 8) | +				((fbdv == 32 ? 0 : fbdv) << 24) | +				(lfbdv == 64 ? 0 : lfbdv); +			mtcpr(CPR0_PLLD, reg); + +			/* Acknowledge that a reset is required. */ +			reset_needed = 1; +		} +	} + +	/* Now reset the CPU if needed */ +	if (reset_needed) +		reset_with_rli(); +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +	u32 reg; + +	/* +	 * See "9.2.1.1 Booting with Option E" in the 460EX/GT +	 * users manual +	 */ +	mfcpr(CPR0_PLLC, reg); +	if ((reg & (CPR0_PLLC_RST | CPR0_PLLC_ENG)) == CPR0_PLLC_RST) { +		/* +		 * Set engage bit +		 */ +		reg = (reg & ~CPR0_PLLC_RST) | CPR0_PLLC_ENG; +		mtcpr(CPR0_PLLC, reg); + +		/* Now reset the CPU */ +		reset_with_rli(); +	} +#endif +} + +#ifdef CONFIG_SYS_4xx_CHIP_21_ERRATA +void +chip_21_errata(void) +{ +	/* +	 * See rev 1.09 of the 405EX/405EXr errata.  CHIP_21 says that +	 * sometimes reading the PVR and/or SDR0_ECID results in incorrect +	 * values.  Since the rev-D chip uses the SDR0_ECID bits to control +	 * internal features, that means the second PCIe or ethernet of an EX +	 * variant could fail to work.  Also, security features of both EX and +	 * EXr might be incorrectly disabled. +	 * +	 * The suggested workaround is as follows (covering rev-C and rev-D): +	 * +	 * 1.Read the PVR and SDR0_ECID3. +	 * +	 * 2.If the PVR matches an expected Revision C PVR value AND if +	 * SDR0_ECID3[12:15] is different from PVR[28:31], then processor is +	 * Revision C: continue executing the initialization code (no reset +	 * required).  else go to step 3. +	 * +	 * 3.If the PVR matches an expected Revision D PVR value AND if +	 * SDR0_ECID3[10:11] matches its expected value, then continue +	 * executing initialization code, no reset required.  else write +	 * DBCR0[RST] = 0b11 to generate a SysReset. +	 */ + +	u32 pvr; +	u32 pvr_28_31; +	u32 ecid3; +	u32 ecid3_10_11; +	u32 ecid3_12_15; + +	/* Step 1: */ +	pvr = get_pvr(); +	mfsdr(SDR0_ECID3, ecid3); + +	/* Step 2: */ +	pvr_28_31 = pvr & 0xf; +	ecid3_10_11 = (ecid3 >> 20) & 0x3; +	ecid3_12_15 = (ecid3 >> 16) & 0xf; +	if ((pvr == CONFIG_405EX_CHIP21_PVR_REV_C) && +			(pvr_28_31 != ecid3_12_15)) { +		/* No reset required. */ +		return; +	} + +	/* Step 3: */ +	if ((pvr == CONFIG_405EX_CHIP21_PVR_REV_D) && +			(ecid3_10_11 == CONFIG_405EX_CHIP21_ECID3_REV_D)) { +		/* No reset required. */ +		return; +	} + +	/* Reset required. */ +	__asm__ __volatile__ ("sync; isync"); +	mtspr(SPRN_DBCR0, 0x30000000); +} +#endif + +/* + * Breath some life into the CPU... + * + * Reconfigure PLL if necessary, + * set up the memory map, + * initialize a bunch of registers + */ +void +cpu_init_f (void) +{ +#if defined(CONFIG_WATCHDOG) || defined(CONFIG_440GX) || defined(CONFIG_460EX) +	u32 val; +#endif + +#ifdef CONFIG_SYS_4xx_CHIP_21_ERRATA +	chip_21_errata(); +#endif + +	reconfigure_pll(CONFIG_SYS_PLL_RECONFIG); + +#if (defined(CONFIG_405EP) || defined (CONFIG_405EX)) && \ +    !defined(CONFIG_APM821XX) &&!defined(CONFIG_SYS_4xx_GPIO_TABLE) +	/* +	 * GPIO0 setup (select GPIO or alternate function) +	 */ +#if defined(CONFIG_SYS_GPIO0_OR) +	out32(GPIO0_OR, CONFIG_SYS_GPIO0_OR);		/* set initial state of output pins	*/ +#endif +#if defined(CONFIG_SYS_GPIO0_ODR) +	out32(GPIO0_ODR, CONFIG_SYS_GPIO0_ODR);	/* open-drain select			*/ +#endif +	out32(GPIO0_OSRH, CONFIG_SYS_GPIO0_OSRH);	/* output select			*/ +	out32(GPIO0_OSRL, CONFIG_SYS_GPIO0_OSRL); +	out32(GPIO0_ISR1H, CONFIG_SYS_GPIO0_ISR1H);	/* input select				*/ +	out32(GPIO0_ISR1L, CONFIG_SYS_GPIO0_ISR1L); +	out32(GPIO0_TSRH, CONFIG_SYS_GPIO0_TSRH);	/* three-state select			*/ +	out32(GPIO0_TSRL, CONFIG_SYS_GPIO0_TSRL); +#if defined(CONFIG_SYS_GPIO0_ISR2H) +	out32(GPIO0_ISR2H, CONFIG_SYS_GPIO0_ISR2H); +	out32(GPIO0_ISR2L, CONFIG_SYS_GPIO0_ISR2L); +#endif +#if defined (CONFIG_SYS_GPIO0_TCR) +	out32(GPIO0_TCR, CONFIG_SYS_GPIO0_TCR);	/* enable output driver for outputs	*/ +#endif +#endif /* CONFIG_405EP ... && !CONFIG_SYS_4xx_GPIO_TABLE */ + +#if defined (CONFIG_405EP) +	/* +	 * Set EMAC noise filter bits +	 */ +	mtdcr(CPC0_EPCTL, CPC0_EPCTL_E0NFE | CPC0_EPCTL_E1NFE); +#endif /* CONFIG_405EP */ + +#if defined(CONFIG_SYS_4xx_GPIO_TABLE) +	gpio_set_chip_configuration(); +#endif /* CONFIG_SYS_4xx_GPIO_TABLE */ + +	/* +	 * External Bus Controller (EBC) Setup +	 */ +#if (defined(CONFIG_SYS_EBC_PB0AP) && defined(CONFIG_SYS_EBC_PB0CR)) +#if (defined(CONFIG_405GP) || \ +     defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ +     defined(CONFIG_405EX) || defined(CONFIG_405)) +	/* +	 * Move the next instructions into icache, since these modify the flash +	 * we are running from! +	 */ +	asm volatile("	bl	0f"		::: "lr"); +	asm volatile("0:	mflr	3"		::: "r3"); +	asm volatile("	addi	4, 0, 14"	::: "r4"); +	asm volatile("	mtctr	4"		::: "ctr"); +	asm volatile("1:	icbt	0, 3"); +	asm volatile("	addi	3, 3, 32"	::: "r3"); +	asm volatile("	bdnz	1b"		::: "ctr", "cr0"); +	asm volatile("	addis	3, 0, 0x0"	::: "r3"); +	asm volatile("	ori	3, 3, 0xA000"	::: "r3"); +	asm volatile("	mtctr	3"		::: "ctr"); +	asm volatile("2:	bdnz	2b"		::: "ctr", "cr0"); +#endif + +	mtebc(PB0AP, CONFIG_SYS_EBC_PB0AP); +	mtebc(PB0CR, CONFIG_SYS_EBC_PB0CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB1AP) && defined(CONFIG_SYS_EBC_PB1CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 1)) +	mtebc(PB1AP, CONFIG_SYS_EBC_PB1AP); +	mtebc(PB1CR, CONFIG_SYS_EBC_PB1CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB2AP) && defined(CONFIG_SYS_EBC_PB2CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 2)) +	mtebc(PB2AP, CONFIG_SYS_EBC_PB2AP); +	mtebc(PB2CR, CONFIG_SYS_EBC_PB2CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB3AP) && defined(CONFIG_SYS_EBC_PB3CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 3)) +	mtebc(PB3AP, CONFIG_SYS_EBC_PB3AP); +	mtebc(PB3CR, CONFIG_SYS_EBC_PB3CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB4AP) && defined(CONFIG_SYS_EBC_PB4CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 4)) +	mtebc(PB4AP, CONFIG_SYS_EBC_PB4AP); +	mtebc(PB4CR, CONFIG_SYS_EBC_PB4CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB5AP) && defined(CONFIG_SYS_EBC_PB5CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 5)) +	mtebc(PB5AP, CONFIG_SYS_EBC_PB5AP); +	mtebc(PB5CR, CONFIG_SYS_EBC_PB5CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB6AP) && defined(CONFIG_SYS_EBC_PB6CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 6)) +	mtebc(PB6AP, CONFIG_SYS_EBC_PB6AP); +	mtebc(PB6CR, CONFIG_SYS_EBC_PB6CR); +#endif + +#if (defined(CONFIG_SYS_EBC_PB7AP) && defined(CONFIG_SYS_EBC_PB7CR) && !(CONFIG_SYS_INIT_DCACHE_CS == 7)) +	mtebc(PB7AP, CONFIG_SYS_EBC_PB7AP); +	mtebc(PB7CR, CONFIG_SYS_EBC_PB7CR); +#endif + +#if defined (CONFIG_SYS_EBC_CFG) +	mtebc(EBC0_CFG, CONFIG_SYS_EBC_CFG); +#endif + +#if defined(CONFIG_WATCHDOG) +	val = mfspr(SPRN_TCR); +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) +	val |= 0xb8000000;      /* generate system reset after 1.34 seconds */ +#elif defined(CONFIG_440EPX) +	val |= 0xb0000000;      /* generate system reset after 1.34 seconds */ +#else +	val |= 0xf0000000;      /* generate system reset after 2.684 seconds */ +#endif +#if defined(CONFIG_SYS_4xx_RESET_TYPE) +	val &= ~0x30000000;			/* clear WRC bits */ +	val |= CONFIG_SYS_4xx_RESET_TYPE << 28;	/* set board specific WRC type */ +#endif +	mtspr(SPRN_TCR, val); + +	val = mfspr(SPRN_TSR); +	val |= 0x80000000;      /* enable watchdog timer */ +	mtspr(SPRN_TSR, val); + +	reset_4xx_watchdog(); +#endif /* CONFIG_WATCHDOG */ + +#if defined(CONFIG_440GX) +	/* Take the GX out of compatibility mode +	 * Travis Sawyer, 9 Mar 2004 +	 * NOTE: 440gx user manual inconsistency here +	 *       Compatibility mode and Ethernet Clock select are not +	 *       correct in the manual +	 */ +	mfsdr(SDR0_MFR, val); +	val &= ~0x10000000; +	mtsdr(SDR0_MFR,val); +#endif /* CONFIG_440GX */ + +#if defined(CONFIG_460EX) +	/* +	 * Set SDR0_AHB_CFG[A2P_INCR4] (bit 24) and +	 * clear SDR0_AHB_CFG[A2P_PROT2] (bit 25) for a new 460EX errata +	 * regarding concurrent use of AHB USB OTG, USB 2.0 host and SATA +	 */ +	mfsdr(SDR0_AHB_CFG, val); +	val |= 0x80; +	val &= ~0x40; +	mtsdr(SDR0_AHB_CFG, val); +	mfsdr(SDR0_USB2HOST_CFG, val); +	val &= ~0xf00; +	val |= 0x400; +	mtsdr(SDR0_USB2HOST_CFG, val); +#endif /* CONFIG_460EX */ + +#if defined(CONFIG_405EX) || \ +    defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT)  || \ +    defined(CONFIG_460SX) || defined(CONFIG_APM821XX) +	/* +	 * Set PLB4 arbiter (Segment 0 and 1) to 4 deep pipeline read +	 */ +	mtdcr(PLB4A0_ACR, (mfdcr(PLB4A0_ACR) & ~PLB4Ax_ACR_RDP_MASK) | +	      PLB4Ax_ACR_RDP_4DEEP); +	mtdcr(PLB4A1_ACR, (mfdcr(PLB4A1_ACR) & ~PLB4Ax_ACR_RDP_MASK) | +	      PLB4Ax_ACR_RDP_4DEEP); +#endif /* CONFIG_440SP/SPE || CONFIG_460EX/GT || CONFIG_405EX */ +} + +/* + * initialize higher level parts of CPU like time base and timers + */ +int cpu_init_r (void) +{ +#if defined(CONFIG_405GP) +	uint pvr = get_pvr(); + +	/* +	 * Set edge conditioning circuitry on PPC405GPr +	 * for compatibility to existing PPC405GP designs. +	 */ +	if ((pvr & 0xfffffff0) == (PVR_405GPR_RB & 0xfffffff0)) { +		mtdcr(CPC0_ECR, 0x60606000); +	} +#endif  /* defined(CONFIG_405GP) */ + +	return 0; +} + +#if defined(CONFIG_PCI) && \ +	(defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \ +	 defined(CONFIG_440GR) || defined(CONFIG_440GRX)) +/* + * 440EP(x)/GR(x) PCI async/sync clocking restriction: + * + * In asynchronous PCI mode, the synchronous PCI clock must meet + * certain requirements. The following equation describes the + * relationship that must be maintained between the asynchronous PCI + * clock and synchronous PCI clock. Select an appropriate PCI:PLB + * ratio to maintain the relationship: + * + * AsyncPCIClk - 1MHz <= SyncPCIclock <= (2 * AsyncPCIClk) - 1MHz + */ +static int ppc4xx_pci_sync_clock_ok(u32 sync, u32 async) +{ +	if (((async - 1000000) > sync) || (sync > ((2 * async) - 1000000))) +		return 0; +	else +		return 1; +} + +int ppc4xx_pci_sync_clock_config(u32 async) +{ +	sys_info_t sys_info; +	u32 sync; +	int div; +	u32 reg; +	u32 spcid_val[] = { +		CPR0_SPCID_SPCIDV0_DIV1, CPR0_SPCID_SPCIDV0_DIV2, +		CPR0_SPCID_SPCIDV0_DIV3, CPR0_SPCID_SPCIDV0_DIV4 }; + +	get_sys_info(&sys_info); +	sync = sys_info.freqPCI; + +	/* +	 * First check if the equation above is met +	 */ +	if (!ppc4xx_pci_sync_clock_ok(sync, async)) { +		/* +		 * Reconfigure PCI sync clock to meet the equation. +		 * Start with highest possible PCI sync frequency +		 * (divider 1). +		 */ +		for (div = 1; div <= 4; div++) { +			sync = sys_info.freqPLB / div; +			if (ppc4xx_pci_sync_clock_ok(sync, async)) +			    break; +		} + +		if (div <= 4) { +			mtcpr(CPR0_SPCID, spcid_val[div]); + +			mfcpr(CPR0_ICFG, reg); +			reg |= CPR0_ICFG_RLI_MASK; +			mtcpr(CPR0_ICFG, reg); + +			/* do chip reset */ +			mtspr(SPRN_DBCR0, 0x20000000); +		} else { +			/* Impossible to configure the PCI sync clock */ +			return -1; +		} +	} + +	return 0; +} +#endif diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/dcr.S b/roms/u-boot/arch/powerpc/cpu/ppc4xx/dcr.S new file mode 100644 index 00000000..6b13528c --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/dcr.S @@ -0,0 +1,180 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com + * + * SPDX-License-Identifier:	GPL-2.0+ + */ +#include <config.h> + +#if defined(CONFIG_4xx) && defined(CONFIG_CMD_SETGETDCR) + +#include <asm/ppc4xx.h> + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#define _ASMLANGUAGE + +/***************************************************************************** + * + *  XXX - DANGER + *        These routines make use of self modifying code.  DO NOT CALL THEM + *	  UNTIL THEY ARE RELOCATED TO RAM.  Additionally, I do not + *	  recommend them for use in anything other than an interactive + *        debugging environment.  This is mainly due to performance reasons. + * + ****************************************************************************/ + +/* + * static void _create_MFDCR(unsigned short dcrn) + * + * Builds a 'mfdcr' instruction for get_dcr + * function. + */ +		.section ".text" +		.align 2 +		.type	 _create_MFDCR,@function +_create_MFDCR: +		/* +		 * Build up a 'mfdcr' instruction formatted as follows: +		 * +		 *  OPCD |   RT   |    DCRF      |     XO       | CR | +		 * ---------------|--------------|--------------|----| +		 * 0   5 | 6   10 | 11        20 | 21        30 | 31 | +		 *       |        |    DCRN      |              |    | +		 *   31  |  %r3   | (5..9|0..4)  |      323     |  0 | +		 * +		 * Where: +		 *	OPCD = opcode - 31 +		 *	RT   = destination register - %r3 return register +		 *	DCRF = DCRN # with upper and lower halves swapped +		 *	XO   = extended opcode - 323 +		 *	CR   = CR[CR0] NOT undefined - 0 +		 */ +		rlwinm	r0, r3, 27, 27, 31	/* OPCD = 31 */ +		rlwinm	r3, r3, 5, 22, 26 +		or	r3, r3, r0 +		slwi	r3, r3, 10 +		oris	r3, r3, 0x3e30		/* RT = %r3 */ +		ori	r3, r3, 323		/* XO = 323 */ +		slwi	r3, r3, 1		/* CR = 0 */ + +		mflr	r4 +		stw	r3, 0(r4)		/* Store instr in get_dcr() */ +		dcbst	r0, r4			/* Make sure val is written out */ +		sync				/* Wait for write to complete */ +		icbi	r0, r4			/* Make sure old instr is dumped */ +		isync				/* Wait for icbi to complete */ + +		blr +.Lfe1:		.size	 _create_MFDCR,.Lfe1-_create_MFDCR +/* end _create_MFDCR() */ + +/* + * static void _create_MTDCR(unsigned short dcrn, unsigned long value) + * + * Builds a 'mtdcr' instruction for set_dcr + * function. + */ +		.section ".text" +		.align 2 +		.type	 _create_MTDCR,@function +_create_MTDCR: +		/* +		 * Build up a 'mtdcr' instruction formatted as follows: +		 * +		 *  OPCD |   RS   |    DCRF      |     XO       | CR | +		 * ---------------|--------------|--------------|----| +		 * 0   5 | 6   10 | 11        20 | 21        30 | 31 | +		 *       |        |    DCRN      |              |    | +		 *   31  |  %r3   | (5..9|0..4)  |      451     |  0 | +		 * +		 * Where: +		 *	OPCD = opcode - 31 +		 *	RS   = source register - %r4 +		 *	DCRF = dest. DCRN # with upper and lower halves swapped +		 *	XO   = extended opcode - 451 +		 *	CR   = CR[CR0] NOT undefined - 0 +		 */ +		rlwinm	r0, r3, 27, 27, 31	/* OPCD = 31 */ +		rlwinm	r3, r3, 5, 22, 26 +		or	r3, r3, r0 +		slwi	r3, r3, 10 +		oris	r3, r3, 0x3e40		/* RS = %r4 */ +		ori	r3, r3, 451		/* XO = 451 */ +		slwi	r3, r3, 1		/* CR = 0 */ + +		mflr	r5 +		stw	r3, 0(r5)		/* Store instr in set_dcr() */ +		dcbst	r0, r5			/* Make sure val is written out */ +		sync				/* Wait for write to complete */ +		icbi	r0, r5			/* Make sure old instr is dumped */ +		isync				/* Wait for icbi to complete */ + +		blr +.Lfe2:		.size	 _create_MTDCR,.Lfe2-_create_MTDCR +/* end _create_MTDCR() */ + + +/* + * unsigned long get_dcr(unsigned short dcrn) + * + * Return a given DCR's value. + */ +		/* */ +		/* XXX - This is self modifying code, hence */ +		/* it is in the data section. */ +		/* */ +		.section ".data" +		.align	2 +		.globl	get_dcr +		.type	get_dcr,@function +get_dcr: +		mflr	r0			/* Get link register */ +		stwu	r1, -32(r1)		/* Save back chain and move SP */ +		stw	r0, +36(r1)		/* Save link register */ + +		bl	_create_MFDCR		/* Build following instruction */ +		/* XXX - we build this instuction up on the fly. */ +		.long	0			/* Get DCR's value */ + +		lwz	r0, +36(r1)		/* Get saved link register */ +		mtlr	r0			/* Restore link register */ +		addi	r1, r1, +32		/* Remove frame from stack */ +		blr				/* Return to calling function */ +.Lfe3:		.size	get_dcr,.Lfe3-get_dcr +/* end get_dcr() */ + + +/* + * unsigned void set_dcr(unsigned short dcrn, unsigned long value) + * + * Return a given DCR's value. + */ +		/* +		 * XXX - This is self modifying code, hence +		 * it is in the data section. +		 */ +		.section ".data" +		.align	2 +		.globl	set_dcr +		.type	set_dcr,@function +set_dcr: +		mflr	r0			/* Get link register */ +		stwu	r1, -32(r1)		/* Save back chain and move SP */ +		stw	r0, +36(r1)		/* Save link register */ + +		bl	_create_MTDCR		/* Build following instruction */ +		/* XXX - we build this instuction up on the fly. */ +		.long	0			/* Set DCR's value */ + +		lwz	r0, +36(r1)		/* Get saved link register */ +		mtlr	r0			/* Restore link register */ +		addi	r1, r1, +32		/* Remove frame from stack */ +		blr				/* Return to calling function */ +.Lfe4:		.size	set_dcr,.Lfe4-set_dcr +/* end set_dcr() */ +#endif diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/denali_data_eye.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/denali_data_eye.c new file mode 100644 index 00000000..a955a5ba --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/denali_data_eye.c @@ -0,0 +1,376 @@ +/* + * arch/powerpc/cpu/ppc4xx/denali_data_eye.c + * Extracted from board/amcc/sequoia/sdram.c by Larry Johnson <lrj@acm.org>. + * + * (C) Copyright 2006 + * Sylvie Gohl,             AMCC/IBM, gohl.sylvie@fr.ibm.com + * Jacqueline Pira-Ferriol, AMCC/IBM, jpira-ferriol@fr.ibm.com + * Thierry Roman,           AMCC/IBM, thierry_roman@fr.ibm.com + * Alain Saurel,            AMCC/IBM, alain.saurel@fr.ibm.com + * Robert Snyder,           AMCC/IBM, rob.snyder@fr.ibm.com + * + * (C) Copyright 2006-2007 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/ppc4xx.h> + +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +/*-----------------------------------------------------------------------------+ + * denali_wait_for_dlllock. + +----------------------------------------------------------------------------*/ +int denali_wait_for_dlllock(void) +{ +	u32 val; +	int wait; + +	/* -----------------------------------------------------------+ +	 * Wait for the DCC master delay line to finish calibration +	 * ----------------------------------------------------------*/ +	for (wait = 0; wait != 0xffff; ++wait) { +		mfsdram(DDR0_17, val); +		if (DDR0_17_DLLLOCKREG_DECODE(val)) { +			/* dlllockreg bit on */ +			return 0; +		} +	} +	debug("0x%04x: DDR0_17 Value (dlllockreg bit): 0x%08x\n", wait, val); +	debug("Waiting for dlllockreg bit to raise\n"); +	return -1; +} + +#if defined(CONFIG_DDR_DATA_EYE) +#define DDR_DCR_BASE 0x10 +#define ddrcfga  (DDR_DCR_BASE+0x0)	/* DDR configuration address reg */ +#define ddrcfgd  (DDR_DCR_BASE+0x1)	/* DDR configuration data reg    */ + +/*-----------------------------------------------------------------------------+ + * wait_for_dram_init_complete. + +----------------------------------------------------------------------------*/ +static int wait_for_dram_init_complete(void) +{ +	unsigned long val; +	int wait = 0; + +	/* --------------------------------------------------------------+ +	 * Wait for 'DRAM initialization complete' bit in status register +	 * -------------------------------------------------------------*/ +	mtdcr(ddrcfga, DDR0_00); + +	while (wait != 0xffff) { +		val = mfdcr(ddrcfgd); +		if ((val & DDR0_00_INT_STATUS_BIT6) == DDR0_00_INT_STATUS_BIT6) +			/* 'DRAM initialization complete' bit */ +			return 0; +		else +			wait++; +	} +	debug("DRAM initialization complete bit in status register did not " +	      "rise\n"); +	return -1; +} + +#define NUM_TRIES 64 +#define NUM_READS 10 + +/*-----------------------------------------------------------------------------+ + * denali_core_search_data_eye. + +----------------------------------------------------------------------------*/ +void denali_core_search_data_eye(void) +{ +	int k, j; +	u32 val; +	u32 wr_dqs_shift, dqs_out_shift, dll_dqs_delay_X; +	u32 max_passing_cases = 0, wr_dqs_shift_with_max_passing_cases = 0; +	u32 passing_cases = 0, dll_dqs_delay_X_sw_val = 0; +	u32 dll_dqs_delay_X_start_window = 0, dll_dqs_delay_X_end_window = 0; +	volatile u32 *ram_pointer; +	u32 test[NUM_TRIES] = { +		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, +		0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55 +	}; + +	ram_pointer = (volatile u32 *)(CONFIG_SYS_SDRAM_BASE); + +	for (wr_dqs_shift = 64; wr_dqs_shift < 96; wr_dqs_shift++) { +		/* for (wr_dqs_shift=1; wr_dqs_shift<96; wr_dqs_shift++) { */ + +		/* -----------------------------------------------------------+ +		 * De-assert 'start' parameter. +		 * ----------------------------------------------------------*/ +		mtdcr(ddrcfga, DDR0_02); +		val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | +		    DDR0_02_START_OFF; +		mtdcr(ddrcfgd, val); + +		/* -----------------------------------------------------------+ +		 * Set 'wr_dqs_shift' +		 * ----------------------------------------------------------*/ +		mtdcr(ddrcfga, DDR0_09); +		val = (mfdcr(ddrcfgd) & ~DDR0_09_WR_DQS_SHIFT_MASK) | +		    DDR0_09_WR_DQS_SHIFT_ENCODE(wr_dqs_shift); +		mtdcr(ddrcfgd, val); + +		/* -----------------------------------------------------------+ +		 * Set 'dqs_out_shift' = wr_dqs_shift + 32 +		 * ----------------------------------------------------------*/ +		dqs_out_shift = wr_dqs_shift + 32; +		mtdcr(ddrcfga, DDR0_22); +		val = (mfdcr(ddrcfgd) & ~DDR0_22_DQS_OUT_SHIFT_MASK) | +		    DDR0_22_DQS_OUT_SHIFT_ENCODE(dqs_out_shift); +		mtdcr(ddrcfgd, val); + +		passing_cases = 0; + +		for (dll_dqs_delay_X = 1; dll_dqs_delay_X < 64; +		     dll_dqs_delay_X++) { +			/* for (dll_dqs_delay_X=1; dll_dqs_delay_X<128; +			   dll_dqs_delay_X++) { */ +			/* -----------------------------------------------------------+ +			 * Set 'dll_dqs_delay_X'. +			 * ----------------------------------------------------------*/ +			/* dll_dqs_delay_0 */ +			mtdcr(ddrcfga, DDR0_17); +			val = (mfdcr(ddrcfgd) & ~DDR0_17_DLL_DQS_DELAY_0_MASK) +			    | DDR0_17_DLL_DQS_DELAY_0_ENCODE(dll_dqs_delay_X); +			mtdcr(ddrcfgd, val); +			/* dll_dqs_delay_1 to dll_dqs_delay_4 */ +			mtdcr(ddrcfga, DDR0_18); +			val = (mfdcr(ddrcfgd) & ~DDR0_18_DLL_DQS_DELAY_X_MASK) +			    | DDR0_18_DLL_DQS_DELAY_4_ENCODE(dll_dqs_delay_X) +			    | DDR0_18_DLL_DQS_DELAY_3_ENCODE(dll_dqs_delay_X) +			    | DDR0_18_DLL_DQS_DELAY_2_ENCODE(dll_dqs_delay_X) +			    | DDR0_18_DLL_DQS_DELAY_1_ENCODE(dll_dqs_delay_X); +			mtdcr(ddrcfgd, val); +			/* dll_dqs_delay_5 to dll_dqs_delay_8 */ +			mtdcr(ddrcfga, DDR0_19); +			val = (mfdcr(ddrcfgd) & ~DDR0_19_DLL_DQS_DELAY_X_MASK) +			    | DDR0_19_DLL_DQS_DELAY_8_ENCODE(dll_dqs_delay_X) +			    | DDR0_19_DLL_DQS_DELAY_7_ENCODE(dll_dqs_delay_X) +			    | DDR0_19_DLL_DQS_DELAY_6_ENCODE(dll_dqs_delay_X) +			    | DDR0_19_DLL_DQS_DELAY_5_ENCODE(dll_dqs_delay_X); +			mtdcr(ddrcfgd, val); +			/* clear any ECC errors */ +			mtdcr(ddrcfga, DDR0_00); +			mtdcr(ddrcfgd, +			      mfdcr(ddrcfgd) | DDR0_00_INT_ACK_ENCODE(0x3C)); + +			sync(); +			eieio(); + +			/* -----------------------------------------------------------+ +			 * Assert 'start' parameter. +			 * ----------------------------------------------------------*/ +			mtdcr(ddrcfga, DDR0_02); +			val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | +			    DDR0_02_START_ON; +			mtdcr(ddrcfgd, val); + +			sync(); +			eieio(); + +			/* -----------------------------------------------------------+ +			 * Wait for the DCC master delay line to finish calibration +			 * ----------------------------------------------------------*/ +			if (denali_wait_for_dlllock() != 0) { +				printf("dll lock did not occur !!!\n"); +				printf("denali_core_search_data_eye!!!\n"); +				printf("wr_dqs_shift = %d - dll_dqs_delay_X = " +				       "%d\n", wr_dqs_shift, dll_dqs_delay_X); +				hang(); +			} +			sync(); +			eieio(); + +			if (wait_for_dram_init_complete() != 0) { +				printf("dram init complete did not occur!!!\n"); +				printf("denali_core_search_data_eye!!!\n"); +				printf("wr_dqs_shift = %d - dll_dqs_delay_X = " +				       "%d\n", wr_dqs_shift, dll_dqs_delay_X); +				hang(); +			} +			udelay(100); /* wait 100us to ensure init is really completed !!! */ + +			/* write values */ +			for (j = 0; j < NUM_TRIES; j++) { +				ram_pointer[j] = test[j]; + +				/* clear any cache at ram location */ +			      __asm__("dcbf 0,%0": :"r"(&ram_pointer[j])); +			} + +			/* read values back */ +			for (j = 0; j < NUM_TRIES; j++) { +				for (k = 0; k < NUM_READS; k++) { +					/* clear any cache at ram location */ +				      __asm__("dcbf 0,%0": :"r"(&ram_pointer +					    [j])); + +					if (ram_pointer[j] != test[j]) +						break; +				} + +				/* read error */ +				if (k != NUM_READS) +					break; +			} + +			/* See if the dll_dqs_delay_X value passed. */ +			mtdcr(ddrcfga, DDR0_00); +			if (j < NUM_TRIES +			    || (DDR0_00_INT_STATUS_DECODE(mfdcr(ddrcfgd)) & +				0x3F)) { +				/* Failed */ +				passing_cases = 0; +				/* break; */ +			} else { +				/* Passed */ +				if (passing_cases == 0) +					dll_dqs_delay_X_sw_val = +					    dll_dqs_delay_X; +				passing_cases++; +				if (passing_cases >= max_passing_cases) { +					max_passing_cases = passing_cases; +					wr_dqs_shift_with_max_passing_cases = +					    wr_dqs_shift; +					dll_dqs_delay_X_start_window = +					    dll_dqs_delay_X_sw_val; +					dll_dqs_delay_X_end_window = +					    dll_dqs_delay_X; +				} +			} + +			/* -----------------------------------------------------------+ +			 * De-assert 'start' parameter. +			 * ----------------------------------------------------------*/ +			mtdcr(ddrcfga, DDR0_02); +			val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | +			    DDR0_02_START_OFF; +			mtdcr(ddrcfgd, val); +		} /* for (dll_dqs_delay_X=0; dll_dqs_delay_X<128; dll_dqs_delay_X++) */ +	} /* for (wr_dqs_shift=0; wr_dqs_shift<96; wr_dqs_shift++) */ + +	/* -----------------------------------------------------------+ +	 * Largest passing window is now detected. +	 * ----------------------------------------------------------*/ + +	/* Compute dll_dqs_delay_X value */ +	dll_dqs_delay_X = (dll_dqs_delay_X_end_window + +			   dll_dqs_delay_X_start_window) / 2; +	wr_dqs_shift = wr_dqs_shift_with_max_passing_cases; + +	debug("DQS calibration - Window detected:\n"); +	debug("max_passing_cases = %d\n", max_passing_cases); +	debug("wr_dqs_shift      = %d\n", wr_dqs_shift); +	debug("dll_dqs_delay_X   = %d\n", dll_dqs_delay_X); +	debug("dll_dqs_delay_X window = %d - %d\n", +	      dll_dqs_delay_X_start_window, dll_dqs_delay_X_end_window); + +	/* -----------------------------------------------------------+ +	 * De-assert 'start' parameter. +	 * ----------------------------------------------------------*/ +	mtdcr(ddrcfga, DDR0_02); +	val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | DDR0_02_START_OFF; +	mtdcr(ddrcfgd, val); + +	/* -----------------------------------------------------------+ +	 * Set 'wr_dqs_shift' +	 * ----------------------------------------------------------*/ +	mtdcr(ddrcfga, DDR0_09); +	val = (mfdcr(ddrcfgd) & ~DDR0_09_WR_DQS_SHIFT_MASK) +	    | DDR0_09_WR_DQS_SHIFT_ENCODE(wr_dqs_shift); +	mtdcr(ddrcfgd, val); +	debug("DDR0_09=0x%08x\n", val); + +	/* -----------------------------------------------------------+ +	 * Set 'dqs_out_shift' = wr_dqs_shift + 32 +	 * ----------------------------------------------------------*/ +	dqs_out_shift = wr_dqs_shift + 32; +	mtdcr(ddrcfga, DDR0_22); +	val = (mfdcr(ddrcfgd) & ~DDR0_22_DQS_OUT_SHIFT_MASK) +	    | DDR0_22_DQS_OUT_SHIFT_ENCODE(dqs_out_shift); +	mtdcr(ddrcfgd, val); +	debug("DDR0_22=0x%08x\n", val); + +	/* -----------------------------------------------------------+ +	 * Set 'dll_dqs_delay_X'. +	 * ----------------------------------------------------------*/ +	/* dll_dqs_delay_0 */ +	mtdcr(ddrcfga, DDR0_17); +	val = (mfdcr(ddrcfgd) & ~DDR0_17_DLL_DQS_DELAY_0_MASK) +	    | DDR0_17_DLL_DQS_DELAY_0_ENCODE(dll_dqs_delay_X); +	mtdcr(ddrcfgd, val); +	debug("DDR0_17=0x%08x\n", val); + +	/* dll_dqs_delay_1 to dll_dqs_delay_4 */ +	mtdcr(ddrcfga, DDR0_18); +	val = (mfdcr(ddrcfgd) & ~DDR0_18_DLL_DQS_DELAY_X_MASK) +	    | DDR0_18_DLL_DQS_DELAY_4_ENCODE(dll_dqs_delay_X) +	    | DDR0_18_DLL_DQS_DELAY_3_ENCODE(dll_dqs_delay_X) +	    | DDR0_18_DLL_DQS_DELAY_2_ENCODE(dll_dqs_delay_X) +	    | DDR0_18_DLL_DQS_DELAY_1_ENCODE(dll_dqs_delay_X); +	mtdcr(ddrcfgd, val); +	debug("DDR0_18=0x%08x\n", val); + +	/* dll_dqs_delay_5 to dll_dqs_delay_8 */ +	mtdcr(ddrcfga, DDR0_19); +	val = (mfdcr(ddrcfgd) & ~DDR0_19_DLL_DQS_DELAY_X_MASK) +	    | DDR0_19_DLL_DQS_DELAY_8_ENCODE(dll_dqs_delay_X) +	    | DDR0_19_DLL_DQS_DELAY_7_ENCODE(dll_dqs_delay_X) +	    | DDR0_19_DLL_DQS_DELAY_6_ENCODE(dll_dqs_delay_X) +	    | DDR0_19_DLL_DQS_DELAY_5_ENCODE(dll_dqs_delay_X); +	mtdcr(ddrcfgd, val); +	debug("DDR0_19=0x%08x\n", val); + +	/* -----------------------------------------------------------+ +	 * Assert 'start' parameter. +	 * ----------------------------------------------------------*/ +	mtdcr(ddrcfga, DDR0_02); +	val = (mfdcr(ddrcfgd) & ~DDR0_02_START_MASK) | DDR0_02_START_ON; +	mtdcr(ddrcfgd, val); + +	sync(); +	eieio(); + +	/* -----------------------------------------------------------+ +	 * Wait for the DCC master delay line to finish calibration +	 * ----------------------------------------------------------*/ +	if (denali_wait_for_dlllock() != 0) { +		printf("dll lock did not occur !!!\n"); +		hang(); +	} +	sync(); +	eieio(); + +	if (wait_for_dram_init_complete() != 0) { +		printf("dram init complete did not occur !!!\n"); +		hang(); +	} +	udelay(100); /* wait 100us to ensure init is really completed !!! */ +} +#endif /* defined(CONFIG_DDR_DATA_EYE) */ +#endif /* defined(CONFIG_440EPX) || defined(CONFIG_440GRX) */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/denali_spd_ddr2.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/denali_spd_ddr2.c new file mode 100644 index 00000000..916451a2 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/denali_spd_ddr2.c @@ -0,0 +1,1231 @@ +/* + * arch/powerpc/cpu/ppc4xx/denali_spd_ddr2.c + * This SPD SDRAM detection code supports AMCC PPC44x CPUs with a Denali-core + * DDR2 controller, specifically the 440EPx/GRx. + * + * (C) Copyright 2007-2008 + * Larry Johnson, lrj@acm.org. + * + * Based primarily on arch/powerpc/cpu/ppc4xx/4xx_spd_ddr2.c, which is... + * + * (C) Copyright 2007 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * COPYRIGHT   AMCC   CORPORATION 2004 + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <command.h> +#include <asm/ppc4xx.h> +#include <i2c.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/mmu.h> +#include <asm/cache.h> + +#if defined(CONFIG_SPD_EEPROM) &&				\ +	(defined(CONFIG_440EPX) || defined(CONFIG_440GRX)) + +/*-----------------------------------------------------------------------------+ + * Defines + *-----------------------------------------------------------------------------*/ +#define MAXDIMMS	2 +#define MAXRANKS	2 + +#define ONE_BILLION	1000000000 + +#define MULDIV64(m1, m2, d)	(u32)(((u64)(m1) * (u64)(m2)) / (u64)(d)) + +#define DLL_DQS_DELAY	0x19 +#define DLL_DQS_BYPASS	0x0B +#define DQS_OUT_SHIFT	0x7F + +/* + * This DDR2 setup code can dynamically setup the TLB entries for the DDR2 memory + * region. Right now the cache should still be disabled in U-Boot because of the + * EMAC driver, that need it's buffer descriptor to be located in non cached + * memory. + * + * If at some time this restriction doesn't apply anymore, just define + * CONFIG_4xx_DCACHE in the board config file and this code should setup + * everything correctly. + */ +#if defined(CONFIG_4xx_DCACHE) +#define MY_TLB_WORD2_I_ENABLE	0			/* enable caching on SDRAM */ +#else +#define MY_TLB_WORD2_I_ENABLE	TLB_WORD2_I_ENABLE	/* disable caching on SDRAM */ +#endif + +/*-----------------------------------------------------------------------------+ + * Prototypes + *-----------------------------------------------------------------------------*/ +extern int denali_wait_for_dlllock(void); +extern void denali_core_search_data_eye(void); +extern void dcbz_area(u32 start_address, u32 num_bytes); + +/* + * Board-specific Platform code can reimplement spd_ddr_init_hang () if needed + */ +void __spd_ddr_init_hang(void) +{ +	hang(); +} +void spd_ddr_init_hang(void) +    __attribute__ ((weak, alias("__spd_ddr_init_hang"))); + +#if defined(DEBUG) +static void print_mcsr(void) +{ +	printf("MCSR = 0x%08X\n", mfspr(SPRN_MCSR)); +} + +static void denali_sdram_register_dump(void) +{ +	unsigned int sdram_data; + +	printf("\n  Register Dump:\n"); +	mfsdram(DDR0_00, sdram_data); +	printf("        DDR0_00 = 0x%08X", sdram_data); +	mfsdram(DDR0_01, sdram_data); +	printf("        DDR0_01 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_02, sdram_data); +	printf("        DDR0_02 = 0x%08X", sdram_data); +	mfsdram(DDR0_03, sdram_data); +	printf("        DDR0_03 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_04, sdram_data); +	printf("        DDR0_04 = 0x%08X", sdram_data); +	mfsdram(DDR0_05, sdram_data); +	printf("        DDR0_05 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_06, sdram_data); +	printf("        DDR0_06 = 0x%08X", sdram_data); +	mfsdram(DDR0_07, sdram_data); +	printf("        DDR0_07 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_08, sdram_data); +	printf("        DDR0_08 = 0x%08X", sdram_data); +	mfsdram(DDR0_09, sdram_data); +	printf("        DDR0_09 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_10, sdram_data); +	printf("        DDR0_10 = 0x%08X", sdram_data); +	mfsdram(DDR0_11, sdram_data); +	printf("        DDR0_11 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_12, sdram_data); +	printf("        DDR0_12 = 0x%08X", sdram_data); +	mfsdram(DDR0_14, sdram_data); +	printf("        DDR0_14 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_17, sdram_data); +	printf("        DDR0_17 = 0x%08X", sdram_data); +	mfsdram(DDR0_18, sdram_data); +	printf("        DDR0_18 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_19, sdram_data); +	printf("        DDR0_19 = 0x%08X", sdram_data); +	mfsdram(DDR0_20, sdram_data); +	printf("        DDR0_20 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_21, sdram_data); +	printf("        DDR0_21 = 0x%08X", sdram_data); +	mfsdram(DDR0_22, sdram_data); +	printf("        DDR0_22 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_23, sdram_data); +	printf("        DDR0_23 = 0x%08X", sdram_data); +	mfsdram(DDR0_24, sdram_data); +	printf("        DDR0_24 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_25, sdram_data); +	printf("        DDR0_25 = 0x%08X", sdram_data); +	mfsdram(DDR0_26, sdram_data); +	printf("        DDR0_26 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_27, sdram_data); +	printf("        DDR0_27 = 0x%08X", sdram_data); +	mfsdram(DDR0_28, sdram_data); +	printf("        DDR0_28 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_31, sdram_data); +	printf("        DDR0_31 = 0x%08X", sdram_data); +	mfsdram(DDR0_32, sdram_data); +	printf("        DDR0_32 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_33, sdram_data); +	printf("        DDR0_33 = 0x%08X", sdram_data); +	mfsdram(DDR0_34, sdram_data); +	printf("        DDR0_34 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_35, sdram_data); +	printf("        DDR0_35 = 0x%08X", sdram_data); +	mfsdram(DDR0_36, sdram_data); +	printf("        DDR0_36 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_37, sdram_data); +	printf("        DDR0_37 = 0x%08X", sdram_data); +	mfsdram(DDR0_38, sdram_data); +	printf("        DDR0_38 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_39, sdram_data); +	printf("        DDR0_39 = 0x%08X", sdram_data); +	mfsdram(DDR0_40, sdram_data); +	printf("        DDR0_40 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_41, sdram_data); +	printf("        DDR0_41 = 0x%08X", sdram_data); +	mfsdram(DDR0_42, sdram_data); +	printf("        DDR0_42 = 0x%08X\n", sdram_data); +	mfsdram(DDR0_43, sdram_data); +	printf("        DDR0_43 = 0x%08X", sdram_data); +	mfsdram(DDR0_44, sdram_data); +	printf("        DDR0_44 = 0x%08X\n", sdram_data); +} +#else +static inline void denali_sdram_register_dump(void) +{ +} + +inline static void print_mcsr(void) +{ +} +#endif /* defined(DEBUG) */ + +static int is_ecc_enabled(void) +{ +	u32 val; + +	mfsdram(DDR0_22, val); +	return 0x3 == DDR0_22_CTRL_RAW_DECODE(val); +} + +static unsigned char spd_read(u8 chip, unsigned int addr) +{ +	u8 data[2]; + +	if (0 != i2c_probe(chip) || 0 != i2c_read(chip, addr, 1, data, 1)) { +		debug("spd_read(0x%02X, 0x%02X) failed\n", chip, addr); +		return 0; +	} +	debug("spd_read(0x%02X, 0x%02X) returned 0x%02X\n", +	      chip, addr, data[0]); +	return data[0]; +} + +static unsigned long get_tcyc(unsigned char reg) +{ +	/* +	 * Byte 9, et al: Cycle time for CAS Latency=X, is split into two +	 * nibbles: the higher order nibble (bits 4-7) designates the cycle time +	 * to a granularity of 1ns; the value presented by the lower order +	 * nibble (bits 0-3) has a granularity of .1ns and is added to the value +	 * designated by the higher nibble. In addition, four lines of the lower +	 * order nibble are assigned to support +.25, +.33, +.66, and +.75. +	 */ + +	unsigned char subfield_b = reg & 0x0F; + +	switch (subfield_b & 0x0F) { +	case 0x0: +	case 0x1: +	case 0x2: +	case 0x3: +	case 0x4: +	case 0x5: +	case 0x6: +	case 0x7: +	case 0x8: +	case 0x9: +		return 1000 * (reg >> 4) + 100 * subfield_b; +	case 0xA: +		return 1000 * (reg >> 4) + 250; +	case 0xB: +		return 1000 * (reg >> 4) + 333; +	case 0xC: +		return 1000 * (reg >> 4) + 667; +	case 0xD: +		return 1000 * (reg >> 4) + 750; +	} +	return 0; +} + +/*------------------------------------------------------------------ + * Find the installed DIMMs, make sure that the are DDR2, and fill + * in the dimm_ranks array.  Then dimm_ranks[dimm_num] > 0 iff the + * DIMM and dimm_num is present. + * Note: Because there are only two chip-select lines, it is assumed + * that a board with a single socket can support two ranks on that + * socket, while a board with two sockets can support only one rank + * on each socket. + *-----------------------------------------------------------------*/ +static void get_spd_info(unsigned long dimm_ranks[], +			 unsigned long *ranks, +			 unsigned char const iic0_dimm_addr[], +			 unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long dimm_found = false; +	unsigned long const max_ranks_per_dimm = (1 == num_dimm_banks) ? 2 : 1; +	unsigned char num_of_bytes; +	unsigned char total_size; + +	*ranks = 0; +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		num_of_bytes = 0; +		total_size = 0; + +		num_of_bytes = spd_read(iic0_dimm_addr[dimm_num], 0); +		total_size = spd_read(iic0_dimm_addr[dimm_num], 1); +		if ((num_of_bytes != 0) && (total_size != 0)) { +			unsigned char const dimm_type = +			    spd_read(iic0_dimm_addr[dimm_num], 2); + +			unsigned long ranks_on_dimm = +			    (spd_read(iic0_dimm_addr[dimm_num], 5) & 0x07) + 1; + +			if (8 != dimm_type) { +				switch (dimm_type) { +				case 1: +					printf("ERROR: Standard Fast Page Mode " +					       "DRAM DIMM"); +					break; +				case 2: +					printf("ERROR: EDO DIMM"); +					break; +				case 3: +					printf("ERROR: Pipelined Nibble DIMM"); +					break; +				case 4: +					printf("ERROR: SDRAM DIMM"); +					break; +				case 5: +					printf("ERROR: Multiplexed ROM DIMM"); +					break; +				case 6: +					printf("ERROR: SGRAM DIMM"); +					break; +				case 7: +					printf("ERROR: DDR1 DIMM"); +					break; +				default: +					printf("ERROR: Unknown DIMM (type %d)", +					       (unsigned int)dimm_type); +					break; +				} +				printf(" detected in slot %lu.\n", dimm_num); +				printf("Only DDR2 SDRAM DIMMs are supported." +				       "\n"); +				printf("Replace the module with a DDR2 DIMM." +				       "\n\n"); +				spd_ddr_init_hang(); +			} +			dimm_found = true; +			debug("DIMM slot %lu: populated with %lu-rank DDR2 DIMM" +			      "\n", dimm_num, ranks_on_dimm); +			if (ranks_on_dimm > max_ranks_per_dimm) { +				printf("WARNING: DRAM DIMM in slot %lu has %lu " +				       "ranks.\n", dimm_num, ranks_on_dimm); +				if (1 == max_ranks_per_dimm) { +					printf("Only one rank will be used.\n"); +				} else { +					printf +					    ("Only two ranks will be used.\n"); +				} +				ranks_on_dimm = max_ranks_per_dimm; +			} +			dimm_ranks[dimm_num] = ranks_on_dimm; +			*ranks += ranks_on_dimm; +		} else { +			dimm_ranks[dimm_num] = 0; +			debug("DIMM slot %lu: Not populated\n", dimm_num); +		} +	} +	if (dimm_found == false) { +		printf("ERROR: No memory installed.\n"); +		printf("Install at least one DDR2 DIMM.\n\n"); +		spd_ddr_init_hang(); +	} +	debug("Total number of ranks = %ld\n", *ranks); +} + +/*------------------------------------------------------------------ + * For the memory DIMMs installed, this routine verifies that + * frequency previously calculated is supported. + *-----------------------------------------------------------------*/ +static void check_frequency(unsigned long *dimm_ranks, +			    unsigned char const iic0_dimm_addr[], +			    unsigned long num_dimm_banks, +			    unsigned long sdram_freq) +{ +	unsigned long dimm_num; +	unsigned long cycle_time; +	unsigned long calc_cycle_time; + +	/* +	 * calc_cycle_time is calculated from DDR frequency set by board/chip +	 * and is expressed in picoseconds to match the way DIMM cycle time is +	 * calculated below. +	 */ +	calc_cycle_time = MULDIV64(ONE_BILLION, 1000, sdram_freq); + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_ranks[dimm_num]) { +			cycle_time = +			    get_tcyc(spd_read(iic0_dimm_addr[dimm_num], 9)); +			debug("cycle_time=%ld ps\n", cycle_time); + +			if (cycle_time > (calc_cycle_time + 10)) { +				/* +				 * the provided sdram cycle_time is too small +				 * for the available DIMM cycle_time. The +				 * additionnal 10ps is here to accept a small +				 * incertainty. +				 */ +				printf +				    ("ERROR: DRAM DIMM detected with cycle_time %d ps in " +				     "slot %d \n while calculated cycle time is %d ps.\n", +				     (unsigned int)cycle_time, +				     (unsigned int)dimm_num, +				     (unsigned int)calc_cycle_time); +				printf +				    ("Replace the DIMM, or change DDR frequency via " +				     "strapping bits.\n\n"); +				spd_ddr_init_hang(); +			} +		} +	} +} + +/*------------------------------------------------------------------ + * This routine gets size information for the installed memory + * DIMMs. + *-----------------------------------------------------------------*/ +static void get_dimm_size(unsigned long dimm_ranks[], +			  unsigned char const iic0_dimm_addr[], +			  unsigned long num_dimm_banks, +			  unsigned long *const rows, +			  unsigned long *const banks, +			  unsigned long *const cols, unsigned long *const width) +{ +	unsigned long dimm_num; + +	*rows = 0; +	*banks = 0; +	*cols = 0; +	*width = 0; +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_ranks[dimm_num]) { +			unsigned long t; + +			/* Rows */ +			t = spd_read(iic0_dimm_addr[dimm_num], 3); +			if (0 == *rows) { +				*rows = t; +			} else if (t != *rows) { +				printf("ERROR: DRAM DIMM modules do not all " +				       "have the same number of rows.\n\n"); +				spd_ddr_init_hang(); +			} +			/* Banks */ +			t = spd_read(iic0_dimm_addr[dimm_num], 17); +			if (0 == *banks) { +				*banks = t; +			} else if (t != *banks) { +				printf("ERROR: DRAM DIMM modules do not all " +				       "have the same number of banks.\n\n"); +				spd_ddr_init_hang(); +			} +			/* Columns */ +			t = spd_read(iic0_dimm_addr[dimm_num], 4); +			if (0 == *cols) { +				*cols = t; +			} else if (t != *cols) { +				printf("ERROR: DRAM DIMM modules do not all " +				       "have the same number of columns.\n\n"); +				spd_ddr_init_hang(); +			} +			/* Data width */ +			t = spd_read(iic0_dimm_addr[dimm_num], 6); +			if (0 == *width) { +				*width = t; +			} else if (t != *width) { +				printf("ERROR: DRAM DIMM modules do not all " +				       "have the same data width.\n\n"); +				spd_ddr_init_hang(); +			} +		} +	} +	debug("Number of rows = %ld\n", *rows); +	debug("Number of columns = %ld\n", *cols); +	debug("Number of banks = %ld\n", *banks); +	debug("Data width = %ld\n", *width); +	if (*rows > 14) { +		printf("ERROR: DRAM DIMM modules have %lu address rows.\n", +		       *rows); +		printf("Only modules with 14 or fewer rows are supported.\n\n"); +		spd_ddr_init_hang(); +	} +	if (4 != *banks && 8 != *banks) { +		printf("ERROR: DRAM DIMM modules have %lu banks.\n", *banks); +		printf("Only modules with 4 or 8 banks are supported.\n\n"); +		spd_ddr_init_hang(); +	} +	if (*cols > 12) { +		printf("ERROR: DRAM DIMM modules have %lu address columns.\n", +		       *cols); +		printf("Only modules with 12 or fewer columns are " +		       "supported.\n\n"); +		spd_ddr_init_hang(); +	} +	if (32 != *width && 40 != *width && 64 != *width && 72 != *width) { +		printf("ERROR: DRAM DIMM modules have a width of %lu bit.\n", +		       *width); +		printf("Only modules with widths of 32, 40, 64, and 72 bits " +		       "are supported.\n\n"); +		spd_ddr_init_hang(); +	} +} + +/*------------------------------------------------------------------ + * Only 1.8V modules are supported.  This routine verifies this. + *-----------------------------------------------------------------*/ +static void check_voltage_type(unsigned long dimm_ranks[], +			       unsigned char const iic0_dimm_addr[], +			       unsigned long num_dimm_banks) +{ +	unsigned long dimm_num; +	unsigned long voltage_type; + +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		if (dimm_ranks[dimm_num]) { +			voltage_type = spd_read(iic0_dimm_addr[dimm_num], 8); +			if (0x05 != voltage_type) {	/* 1.8V for DDR2 */ +				printf("ERROR: Slot %lu provides 1.8V for DDR2 " +				       "DIMMs.\n", dimm_num); +				switch (voltage_type) { +				case 0x00: +					printf("This DIMM is 5.0 Volt/TTL.\n"); +					break; +				case 0x01: +					printf("This DIMM is LVTTL.\n"); +					break; +				case 0x02: +					printf("This DIMM is 1.5 Volt.\n"); +					break; +				case 0x03: +					printf("This DIMM is 3.3 Volt/TTL.\n"); +					break; +				case 0x04: +					printf("This DIMM is 2.5 Volt.\n"); +					break; +				default: +					printf("This DIMM is an unknown " +					       "voltage.\n"); +					break; +				} +				printf("Replace it with a 1.8V DDR2 DIMM.\n\n"); +				spd_ddr_init_hang(); +			} +		} +	} +} + +static void program_ddr0_03(unsigned long dimm_ranks[], +			    unsigned char const iic0_dimm_addr[], +			    unsigned long num_dimm_banks, +			    unsigned long sdram_freq, +			    unsigned long rows, unsigned long *cas_latency) +{ +	unsigned long dimm_num; +	unsigned long cas_index; +	unsigned long cycle_2_0_clk; +	unsigned long cycle_3_0_clk; +	unsigned long cycle_4_0_clk; +	unsigned long cycle_5_0_clk; +	unsigned long max_2_0_tcyc_ps = 100; +	unsigned long max_3_0_tcyc_ps = 100; +	unsigned long max_4_0_tcyc_ps = 100; +	unsigned long max_5_0_tcyc_ps = 100; +	unsigned char cas_available = 0x3C;	/* value for DDR2 */ +	u32 ddr0_03 = DDR0_03_BSTLEN_ENCODE(0x2) | DDR0_03_INITAREF_ENCODE(0x2); +	unsigned int const tcyc_addr[3] = { 9, 23, 25 }; + +	/*------------------------------------------------------------------ +	 * Get the board configuration info. +	 *-----------------------------------------------------------------*/ +	debug("sdram_freq = %ld\n", sdram_freq); + +	/*------------------------------------------------------------------ +	 * Handle the timing.  We need to find the worst case timing of all +	 * the dimm modules installed. +	 *-----------------------------------------------------------------*/ +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_ranks[dimm_num]) { +			unsigned char const cas_bit = +			    spd_read(iic0_dimm_addr[dimm_num], 18); +			unsigned char cas_mask; + +			cas_available &= cas_bit; +			for (cas_mask = 0x80; cas_mask; cas_mask >>= 1) { +				if (cas_bit & cas_mask) +					break; +			} +			debug("cas_bit (SPD byte 18) = %02X, cas_mask = %02X\n", +			      cas_bit, cas_mask); + +			for (cas_index = 0; cas_index < 3; +			     cas_mask >>= 1, cas_index++) { +				unsigned long cycle_time_ps; + +				if (!(cas_available & cas_mask)) { +					continue; +				} +				cycle_time_ps = +				    get_tcyc(spd_read(iic0_dimm_addr[dimm_num], +						      tcyc_addr[cas_index])); + +				debug("cas_index = %ld: cycle_time_ps = %ld\n", +				      cas_index, cycle_time_ps); +				/* +				 * DDR2 devices use the following bitmask for CAS latency: +				 *  Bit   7    6    5    4    3    2    1    0 +				 *       TBD  6.0  5.0  4.0  3.0  2.0  TBD  TBD +				 */ +				switch (cas_mask) { +				case 0x20: +					max_5_0_tcyc_ps = +					    max(max_5_0_tcyc_ps, cycle_time_ps); +					break; +				case 0x10: +					max_4_0_tcyc_ps = +					    max(max_4_0_tcyc_ps, cycle_time_ps); +					break; +				case 0x08: +					max_3_0_tcyc_ps = +					    max(max_3_0_tcyc_ps, cycle_time_ps); +					break; +				case 0x04: +					max_2_0_tcyc_ps = +					    max(max_2_0_tcyc_ps, cycle_time_ps); +					break; +				} +			} +		} +	} +	debug("cas_available (bit map) = 0x%02X\n", cas_available); + +	/*------------------------------------------------------------------ +	 * Set the SDRAM mode, SDRAM_MMODE +	 *-----------------------------------------------------------------*/ + +	/* add 10 here because of rounding problems */ +	cycle_2_0_clk = MULDIV64(ONE_BILLION, 1000, max_2_0_tcyc_ps) + 10; +	cycle_3_0_clk = MULDIV64(ONE_BILLION, 1000, max_3_0_tcyc_ps) + 10; +	cycle_4_0_clk = MULDIV64(ONE_BILLION, 1000, max_4_0_tcyc_ps) + 10; +	cycle_5_0_clk = MULDIV64(ONE_BILLION, 1000, max_5_0_tcyc_ps) + 10; +	debug("cycle_2_0_clk = %ld\n", cycle_2_0_clk); +	debug("cycle_3_0_clk = %ld\n", cycle_3_0_clk); +	debug("cycle_4_0_clk = %ld\n", cycle_4_0_clk); +	debug("cycle_5_0_clk = %ld\n", cycle_5_0_clk); + +	if ((cas_available & 0x04) && (sdram_freq <= cycle_2_0_clk)) { +		*cas_latency = 2; +		ddr0_03 |= DDR0_03_CASLAT_ENCODE(0x2) | +		    DDR0_03_CASLAT_LIN_ENCODE(0x4); +	} else if ((cas_available & 0x08) && (sdram_freq <= cycle_3_0_clk)) { +		*cas_latency = 3; +		ddr0_03 |= DDR0_03_CASLAT_ENCODE(0x3) | +		    DDR0_03_CASLAT_LIN_ENCODE(0x6); +	} else if ((cas_available & 0x10) && (sdram_freq <= cycle_4_0_clk)) { +		*cas_latency = 4; +		ddr0_03 |= DDR0_03_CASLAT_ENCODE(0x4) | +		    DDR0_03_CASLAT_LIN_ENCODE(0x8); +	} else if ((cas_available & 0x20) && (sdram_freq <= cycle_5_0_clk)) { +		*cas_latency = 5; +		ddr0_03 |= DDR0_03_CASLAT_ENCODE(0x5) | +		    DDR0_03_CASLAT_LIN_ENCODE(0xA); +	} else { +		printf("ERROR: Cannot find a supported CAS latency with the " +		       "installed DIMMs.\n"); +		printf("Only DDR2 DIMMs with CAS latencies of 2.0, 3.0, 4.0, " +		       "and 5.0 are supported.\n"); +		printf("Make sure the PLB speed is within the supported range " +		       "of the DIMMs.\n"); +		printf("sdram_freq=%ld cycle2=%ld cycle3=%ld cycle4=%ld " +		       "cycle5=%ld\n\n", sdram_freq, cycle_2_0_clk, +		       cycle_3_0_clk, cycle_4_0_clk, cycle_5_0_clk); +		spd_ddr_init_hang(); +	} +	debug("CAS latency = %ld\n", *cas_latency); +	mtsdram(DDR0_03, ddr0_03); +} + +static void program_ddr0_04(unsigned long dimm_ranks[], +			    unsigned char const iic0_dimm_addr[], +			    unsigned long num_dimm_banks, +			    unsigned long sdram_freq) +{ +	unsigned long dimm_num; +	unsigned long t_rc_ps = 0; +	unsigned long t_rrd_ps = 0; +	unsigned long t_rtp_ps = 0; +	unsigned long t_rc_clk; +	unsigned long t_rrd_clk; +	unsigned long t_rtp_clk; + +	/*------------------------------------------------------------------ +	 * Handle the timing.  We need to find the worst case timing of all +	 * the dimm modules installed. +	 *-----------------------------------------------------------------*/ +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_ranks[dimm_num]) { +			unsigned long ps; + +			/* tRC */ +			ps = 1000 * spd_read(iic0_dimm_addr[dimm_num], 41); +			switch (spd_read(iic0_dimm_addr[dimm_num], 40) >> 4) { +			case 0x1: +				ps += 250; +				break; +			case 0x2: +				ps += 333; +				break; +			case 0x3: +				ps += 500; +				break; +			case 0x4: +				ps += 667; +				break; +			case 0x5: +				ps += 750; +				break; +			} +			t_rc_ps = max(t_rc_ps, ps); +			/* tRRD */ +			ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 28); +			t_rrd_ps = max(t_rrd_ps, ps); +			/* tRTP */ +			ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 38); +			t_rtp_ps = max(t_rtp_ps, ps); +		} +	} +	debug("t_rc_ps  = %ld\n", t_rc_ps); +	t_rc_clk = (MULDIV64(sdram_freq, t_rc_ps, ONE_BILLION) + 999) / 1000; +	debug("t_rrd_ps = %ld\n", t_rrd_ps); +	t_rrd_clk = (MULDIV64(sdram_freq, t_rrd_ps, ONE_BILLION) + 999) / 1000; +	debug("t_rtp_ps = %ld\n", t_rtp_ps); +	t_rtp_clk = (MULDIV64(sdram_freq, t_rtp_ps, ONE_BILLION) + 999) / 1000; +	mtsdram(DDR0_04, DDR0_04_TRC_ENCODE(t_rc_clk) | +		DDR0_04_TRRD_ENCODE(t_rrd_clk) | +		DDR0_04_TRTP_ENCODE(t_rtp_clk)); +} + +static void program_ddr0_05(unsigned long dimm_ranks[], +			    unsigned char const iic0_dimm_addr[], +			    unsigned long num_dimm_banks, +			    unsigned long sdram_freq) +{ +	unsigned long dimm_num; +	unsigned long t_rp_ps = 0; +	unsigned long t_ras_ps = 0; +	unsigned long t_rp_clk; +	unsigned long t_ras_clk; +	u32 ddr0_05 = DDR0_05_TMRD_ENCODE(0x2) | DDR0_05_TEMRS_ENCODE(0x2); + +	/*------------------------------------------------------------------ +	 * Handle the timing.  We need to find the worst case timing of all +	 * the dimm modules installed. +	 *-----------------------------------------------------------------*/ +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_ranks[dimm_num]) { +			unsigned long ps; + +			/* tRP */ +			ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 27); +			t_rp_ps = max(t_rp_ps, ps); +			/* tRAS */ +			ps = 1000 * spd_read(iic0_dimm_addr[dimm_num], 30); +			t_ras_ps = max(t_ras_ps, ps); +		} +	} +	debug("t_rp_ps  = %ld\n", t_rp_ps); +	t_rp_clk = (MULDIV64(sdram_freq, t_rp_ps, ONE_BILLION) + 999) / 1000; +	debug("t_ras_ps = %ld\n", t_ras_ps); +	t_ras_clk = (MULDIV64(sdram_freq, t_ras_ps, ONE_BILLION) + 999) / 1000; +	mtsdram(DDR0_05, ddr0_05 | DDR0_05_TRP_ENCODE(t_rp_clk) | +		DDR0_05_TRAS_MIN_ENCODE(t_ras_clk)); +} + +static void program_ddr0_06(unsigned long dimm_ranks[], +			    unsigned char const iic0_dimm_addr[], +			    unsigned long num_dimm_banks, +			    unsigned long sdram_freq) +{ +	unsigned long dimm_num; +	unsigned char spd_40; +	unsigned long t_wtr_ps = 0; +	unsigned long t_rfc_ps = 0; +	unsigned long t_wtr_clk; +	unsigned long t_rfc_clk; +	u32 ddr0_06 = +	    DDR0_06_WRITEINTERP_ENCODE(0x1) | DDR0_06_TDLL_ENCODE(200); + +	/*------------------------------------------------------------------ +	 * Handle the timing.  We need to find the worst case timing of all +	 * the dimm modules installed. +	 *-----------------------------------------------------------------*/ +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_ranks[dimm_num]) { +			unsigned long ps; + +			/* tWTR */ +			ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 37); +			t_wtr_ps = max(t_wtr_ps, ps); +			/* tRFC */ +			ps = 1000 * spd_read(iic0_dimm_addr[dimm_num], 42); +			spd_40 = spd_read(iic0_dimm_addr[dimm_num], 40); +			ps += 256000 * (spd_40 & 0x01); +			switch ((spd_40 & 0x0E) >> 1) { +			case 0x1: +				ps += 250; +				break; +			case 0x2: +				ps += 333; +				break; +			case 0x3: +				ps += 500; +				break; +			case 0x4: +				ps += 667; +				break; +			case 0x5: +				ps += 750; +				break; +			} +			t_rfc_ps = max(t_rfc_ps, ps); +		} +	} +	debug("t_wtr_ps = %ld\n", t_wtr_ps); +	t_wtr_clk = (MULDIV64(sdram_freq, t_wtr_ps, ONE_BILLION) + 999) / 1000; +	debug("t_rfc_ps = %ld\n", t_rfc_ps); +	t_rfc_clk = (MULDIV64(sdram_freq, t_rfc_ps, ONE_BILLION) + 999) / 1000; +	mtsdram(DDR0_06, ddr0_06 | DDR0_06_TWTR_ENCODE(t_wtr_clk) | +		DDR0_06_TRFC_ENCODE(t_rfc_clk)); +} + +static void program_ddr0_10(unsigned long dimm_ranks[], unsigned long ranks) +{ +	unsigned long csmap; + +	if (2 == ranks) { +		/* Both chip selects in use */ +		csmap = 0x03; +	} else { +		/* One chip select in use */ +		csmap = (1 == dimm_ranks[0]) ? 0x1 : 0x2; +	} +	mtsdram(DDR0_10, DDR0_10_WRITE_MODEREG_ENCODE(0x0) | +		DDR0_10_CS_MAP_ENCODE(csmap) | +		DDR0_10_OCD_ADJUST_PUP_CS_0_ENCODE(0)); +} + +static void program_ddr0_11(unsigned long sdram_freq) +{ +	unsigned long const t_xsnr_ps = 200000;	/* 200 ns */ +	unsigned long t_xsnr_clk; + +	debug("t_xsnr_ps = %ld\n", t_xsnr_ps); +	t_xsnr_clk = +	    (MULDIV64(sdram_freq, t_xsnr_ps, ONE_BILLION) + 999) / 1000; +	mtsdram(DDR0_11, DDR0_11_SREFRESH_ENCODE(0) | +		DDR0_11_TXSNR_ENCODE(t_xsnr_clk) | DDR0_11_TXSR_ENCODE(200)); +} + +static void program_ddr0_22(unsigned long dimm_ranks[], +			    unsigned char const iic0_dimm_addr[], +			    unsigned long num_dimm_banks, unsigned long width) +{ +#if defined(CONFIG_DDR_ECC) +	unsigned long dimm_num; +	unsigned long ecc_available = width >= 64; +	u32 ddr0_22 = DDR0_22_DQS_OUT_SHIFT_BYPASS_ENCODE(0x26) | +	    DDR0_22_DQS_OUT_SHIFT_ENCODE(DQS_OUT_SHIFT) | +	    DDR0_22_DLL_DQS_BYPASS_8_ENCODE(DLL_DQS_BYPASS); + +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_ranks[dimm_num]) { +			/* Check for ECC */ +			if (0 == (spd_read(iic0_dimm_addr[dimm_num], 11) & +				  0x02)) { +				ecc_available = false; +			} +		} +	} +	if (ecc_available) { +		debug("ECC found on all DIMMs present\n"); +		mtsdram(DDR0_22, ddr0_22 | DDR0_22_CTRL_RAW_ENCODE(0x3)); +	} else { +		debug("ECC not found on some or all DIMMs present\n"); +		mtsdram(DDR0_22, ddr0_22 | DDR0_22_CTRL_RAW_ENCODE(0x0)); +	} +#else +	mtsdram(DDR0_22, DDR0_22_CTRL_RAW_ENCODE(0x0) | +		DDR0_22_DQS_OUT_SHIFT_BYPASS_ENCODE(0x26) | +		DDR0_22_DQS_OUT_SHIFT_ENCODE(DQS_OUT_SHIFT) | +		DDR0_22_DLL_DQS_BYPASS_8_ENCODE(DLL_DQS_BYPASS)); +#endif /* defined(CONFIG_DDR_ECC) */ +} + +static void program_ddr0_24(unsigned long ranks) +{ +	u32 ddr0_24 = DDR0_24_RTT_PAD_TERMINATION_ENCODE(0x1) |	/* 75 ohm */ +	    DDR0_24_ODT_RD_MAP_CS1_ENCODE(0x0); + +	if (2 == ranks) { +		/* Both chip selects in use */ +		ddr0_24 |= DDR0_24_ODT_WR_MAP_CS1_ENCODE(0x1) | +		    DDR0_24_ODT_WR_MAP_CS0_ENCODE(0x2); +	} else { +		/* One chip select in use */ +		/* One of the two fields added to ddr0_24 is a "don't care" */ +		ddr0_24 |= DDR0_24_ODT_WR_MAP_CS1_ENCODE(0x2) | +		    DDR0_24_ODT_WR_MAP_CS0_ENCODE(0x1); +	} +	mtsdram(DDR0_24, ddr0_24); +} + +static void program_ddr0_26(unsigned long sdram_freq) +{ +	unsigned long const t_ref_ps = 7800000;	/* 7.8 us. refresh */ +	/* TODO: check definition of tRAS_MAX */ +	unsigned long const t_ras_max_ps = 9 * t_ref_ps; +	unsigned long t_ras_max_clk; +	unsigned long t_ref_clk; + +	/* Round down t_ras_max_clk and t_ref_clk */ +	debug("t_ras_max_ps = %ld\n", t_ras_max_ps); +	t_ras_max_clk = MULDIV64(sdram_freq, t_ras_max_ps, ONE_BILLION) / 1000; +	debug("t_ref_ps     = %ld\n", t_ref_ps); +	t_ref_clk = MULDIV64(sdram_freq, t_ref_ps, ONE_BILLION) / 1000; +	mtsdram(DDR0_26, DDR0_26_TRAS_MAX_ENCODE(t_ras_max_clk) | +		DDR0_26_TREF_ENCODE(t_ref_clk)); +} + +static void program_ddr0_27(unsigned long sdram_freq) +{ +	unsigned long const t_init_ps = 200000000;	/* 200 us. init */ +	unsigned long t_init_clk; + +	debug("t_init_ps = %ld\n", t_init_ps); +	t_init_clk = +	    (MULDIV64(sdram_freq, t_init_ps, ONE_BILLION) + 999) / 1000; +	mtsdram(DDR0_27, DDR0_27_EMRS_DATA_ENCODE(0x0000) | +		DDR0_27_TINIT_ENCODE(t_init_clk)); +} + +static void program_ddr0_43(unsigned long dimm_ranks[], +			    unsigned char const iic0_dimm_addr[], +			    unsigned long num_dimm_banks, +			    unsigned long sdram_freq, +			    unsigned long cols, unsigned long banks) +{ +	unsigned long dimm_num; +	unsigned long t_wr_ps = 0; +	unsigned long t_wr_clk; +	u32 ddr0_43 = DDR0_43_APREBIT_ENCODE(10) | +	    DDR0_43_COLUMN_SIZE_ENCODE(12 - cols) | +	    DDR0_43_EIGHT_BANK_MODE_ENCODE(8 == banks ? 1 : 0); + +	/*------------------------------------------------------------------ +	 * Handle the timing.  We need to find the worst case timing of all +	 * the dimm modules installed. +	 *-----------------------------------------------------------------*/ +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_ranks[dimm_num]) { +			unsigned long ps; + +			ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 36); +			t_wr_ps = max(t_wr_ps, ps); +		} +	} +	debug("t_wr_ps = %ld\n", t_wr_ps); +	t_wr_clk = (MULDIV64(sdram_freq, t_wr_ps, ONE_BILLION) + 999) / 1000; +	mtsdram(DDR0_43, ddr0_43 | DDR0_43_TWR_ENCODE(t_wr_clk)); +} + +static void program_ddr0_44(unsigned long dimm_ranks[], +			    unsigned char const iic0_dimm_addr[], +			    unsigned long num_dimm_banks, +			    unsigned long sdram_freq) +{ +	unsigned long dimm_num; +	unsigned long t_rcd_ps = 0; +	unsigned long t_rcd_clk; + +	/*------------------------------------------------------------------ +	 * Handle the timing.  We need to find the worst case timing of all +	 * the dimm modules installed. +	 *-----------------------------------------------------------------*/ +	/* loop through all the DIMM slots on the board */ +	for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) { +		/* If a dimm is installed in a particular slot ... */ +		if (dimm_ranks[dimm_num]) { +			unsigned long ps; + +			ps = 250 * spd_read(iic0_dimm_addr[dimm_num], 29); +			t_rcd_ps = max(t_rcd_ps, ps); +		} +	} +	debug("t_rcd_ps = %ld\n", t_rcd_ps); +	t_rcd_clk = (MULDIV64(sdram_freq, t_rcd_ps, ONE_BILLION) + 999) / 1000; +	mtsdram(DDR0_44, DDR0_44_TRCD_ENCODE(t_rcd_clk)); +} + +/*-----------------------------------------------------------------------------+ + * initdram.  Initializes the 440EPx/GPx DDR SDRAM controller. + * Note: This routine runs from flash with a stack set up in the chip's + * sram space.  It is important that the routine does not require .sbss, .bss or + * .data sections.  It also cannot call routines that require these sections. + *-----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------- + * Function:	 initdram + * Description:  Configures SDRAM memory banks for DDR operation. + *		 Auto Memory Configuration option reads the DDR SDRAM EEPROMs + *		 via the IIC bus and then configures the DDR SDRAM memory + *		 banks appropriately. If Auto Memory Configuration is + *		 not used, it is assumed that no DIMM is plugged + *-----------------------------------------------------------------------------*/ +phys_size_t initdram(int board_type) +{ +	unsigned char const iic0_dimm_addr[] = SPD_EEPROM_ADDRESS; +	unsigned long dimm_ranks[MAXDIMMS]; +	unsigned long ranks; +	unsigned long rows; +	unsigned long banks; +	unsigned long cols; +	unsigned long width; +	unsigned long const sdram_freq = get_bus_freq(0); +	unsigned long const num_dimm_banks = sizeof(iic0_dimm_addr);	/* on board dimm banks */ +	unsigned long cas_latency = 0;	/* to quiet initialization warning */ +	unsigned long dram_size; + +	debug("\nEntering initdram()\n"); + +	/*------------------------------------------------------------------ +	 * Stop the DDR-SDRAM controller. +	 *-----------------------------------------------------------------*/ +	mtsdram(DDR0_02, DDR0_02_START_ENCODE(0)); + +	/* +	 * Make sure I2C controller is initialized +	 * before continuing. +	 */ +	/* switch to correct I2C bus */ +	i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM); + +	/*------------------------------------------------------------------ +	 * Clear out the serial presence detect buffers. +	 * Perform IIC reads from the dimm.  Fill in the spds. +	 * Check to see if the dimm slots are populated +	 *-----------------------------------------------------------------*/ +	get_spd_info(dimm_ranks, &ranks, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Check the frequency supported for the dimms plugged. +	 *-----------------------------------------------------------------*/ +	check_frequency(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + +	/*------------------------------------------------------------------ +	 * Check and get size information. +	 *-----------------------------------------------------------------*/ +	get_dimm_size(dimm_ranks, iic0_dimm_addr, num_dimm_banks, &rows, &banks, +		      &cols, &width); + +	/*------------------------------------------------------------------ +	 * Check the voltage type for the dimms plugged. +	 *-----------------------------------------------------------------*/ +	check_voltage_type(dimm_ranks, iic0_dimm_addr, num_dimm_banks); + +	/*------------------------------------------------------------------ +	 * Program registers for SDRAM controller. +	 *-----------------------------------------------------------------*/ +	mtsdram(DDR0_00, DDR0_00_DLL_INCREMENT_ENCODE(0x19) | +		DDR0_00_DLL_START_POINT_DECODE(0x0A)); + +	mtsdram(DDR0_01, DDR0_01_PLB0_DB_CS_LOWER_ENCODE(0x01) | +		DDR0_01_PLB0_DB_CS_UPPER_ENCODE(0x00) | +		DDR0_01_INT_MASK_ENCODE(0xFF)); + +	program_ddr0_03(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq, +			rows, &cas_latency); + +	program_ddr0_04(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + +	program_ddr0_05(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + +	program_ddr0_06(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + +	/* +	 * TODO: tFAW not found in SPD.  Value of 13 taken from Sequoia +	 * board SDRAM, but may be overly conservative. +	 */ +	mtsdram(DDR0_07, DDR0_07_NO_CMD_INIT_ENCODE(0) | +		DDR0_07_TFAW_ENCODE(13) | +		DDR0_07_AUTO_REFRESH_MODE_ENCODE(1) | +		DDR0_07_AREFRESH_ENCODE(0)); + +	mtsdram(DDR0_08, DDR0_08_WRLAT_ENCODE(cas_latency - 1) | +		DDR0_08_TCPD_ENCODE(200) | DDR0_08_DQS_N_EN_ENCODE(0) | +		DDR0_08_DDRII_ENCODE(1)); + +	mtsdram(DDR0_09, DDR0_09_OCD_ADJUST_PDN_CS_0_ENCODE(0x00) | +		DDR0_09_RTT_0_ENCODE(0x1) | +		DDR0_09_WR_DQS_SHIFT_BYPASS_ENCODE(0x1D) | +		DDR0_09_WR_DQS_SHIFT_ENCODE(DQS_OUT_SHIFT - 0x20)); + +	program_ddr0_10(dimm_ranks, ranks); + +	program_ddr0_11(sdram_freq); + +	mtsdram(DDR0_12, DDR0_12_TCKE_ENCODE(3)); + +	mtsdram(DDR0_14, DDR0_14_DLL_BYPASS_MODE_ENCODE(0) | +		DDR0_14_REDUC_ENCODE(width <= 40 ? 1 : 0) | +		DDR0_14_REG_DIMM_ENABLE_ENCODE(0)); + +	mtsdram(DDR0_17, DDR0_17_DLL_DQS_DELAY_0_ENCODE(DLL_DQS_DELAY)); + +	mtsdram(DDR0_18, DDR0_18_DLL_DQS_DELAY_4_ENCODE(DLL_DQS_DELAY) | +		DDR0_18_DLL_DQS_DELAY_3_ENCODE(DLL_DQS_DELAY) | +		DDR0_18_DLL_DQS_DELAY_2_ENCODE(DLL_DQS_DELAY) | +		DDR0_18_DLL_DQS_DELAY_1_ENCODE(DLL_DQS_DELAY)); + +	mtsdram(DDR0_19, DDR0_19_DLL_DQS_DELAY_8_ENCODE(DLL_DQS_DELAY) | +		DDR0_19_DLL_DQS_DELAY_7_ENCODE(DLL_DQS_DELAY) | +		DDR0_19_DLL_DQS_DELAY_6_ENCODE(DLL_DQS_DELAY) | +		DDR0_19_DLL_DQS_DELAY_5_ENCODE(DLL_DQS_DELAY)); + +	mtsdram(DDR0_20, DDR0_20_DLL_DQS_BYPASS_3_ENCODE(DLL_DQS_BYPASS) | +		DDR0_20_DLL_DQS_BYPASS_2_ENCODE(DLL_DQS_BYPASS) | +		DDR0_20_DLL_DQS_BYPASS_1_ENCODE(DLL_DQS_BYPASS) | +		DDR0_20_DLL_DQS_BYPASS_0_ENCODE(DLL_DQS_BYPASS)); + +	mtsdram(DDR0_21, DDR0_21_DLL_DQS_BYPASS_7_ENCODE(DLL_DQS_BYPASS) | +		DDR0_21_DLL_DQS_BYPASS_6_ENCODE(DLL_DQS_BYPASS) | +		DDR0_21_DLL_DQS_BYPASS_5_ENCODE(DLL_DQS_BYPASS) | +		DDR0_21_DLL_DQS_BYPASS_4_ENCODE(DLL_DQS_BYPASS)); + +	program_ddr0_22(dimm_ranks, iic0_dimm_addr, num_dimm_banks, width); + +	mtsdram(DDR0_23, DDR0_23_ODT_RD_MAP_CS0_ENCODE(0x0) | +		DDR0_23_FWC_ENCODE(0)); + +	program_ddr0_24(ranks); + +	program_ddr0_26(sdram_freq); + +	program_ddr0_27(sdram_freq); + +	mtsdram(DDR0_28, DDR0_28_EMRS3_DATA_ENCODE(0x0000) | +		DDR0_28_EMRS2_DATA_ENCODE(0x0000)); + +	mtsdram(DDR0_31, DDR0_31_XOR_CHECK_BITS_ENCODE(0x0000)); + +	mtsdram(DDR0_42, DDR0_42_ADDR_PINS_ENCODE(14 - rows) | +		DDR0_42_CASLAT_LIN_GATE_ENCODE(2 * cas_latency)); + +	program_ddr0_43(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq, +			cols, banks); + +	program_ddr0_44(dimm_ranks, iic0_dimm_addr, num_dimm_banks, sdram_freq); + +	denali_sdram_register_dump(); + +	dram_size = (width >= 64) ? 8 : 4; +	dram_size *= 1 << cols; +	dram_size *= banks; +	dram_size *= 1 << rows; +	dram_size *= ranks; +	debug("dram_size = %lu\n", dram_size); + +	/* Start the SDRAM controler */ +	mtsdram(DDR0_02, DDR0_02_START_ENCODE(1)); +	denali_wait_for_dlllock(); + +#if defined(CONFIG_DDR_DATA_EYE) +	/* +	 * Map the first 1 MiB of memory in the TLB, and perform the data eye +	 * search. +	 */ +	program_tlb(0, CONFIG_SYS_SDRAM_BASE, TLB_1MB_SIZE, TLB_WORD2_I_ENABLE); +	denali_core_search_data_eye(); +	denali_sdram_register_dump(); +	remove_tlb(CONFIG_SYS_SDRAM_BASE, TLB_1MB_SIZE); +#endif + +#if defined(CONFIG_ZERO_SDRAM) || defined(CONFIG_DDR_ECC) +	program_tlb(0, CONFIG_SYS_SDRAM_BASE, dram_size, 0); +	sync(); +	/* Zero the memory */ +	debug("Zeroing SDRAM..."); +#if defined(CONFIG_SYS_MEM_TOP_HIDE) +	dcbz_area(CONFIG_SYS_SDRAM_BASE, dram_size - CONFIG_SYS_MEM_TOP_HIDE); +#else +#error Please define CONFIG_SYS_MEM_TOP_HIDE (see README) in your board config file +#endif +	/* Write modified dcache lines back to memory */ +	clean_dcache_range(CONFIG_SYS_SDRAM_BASE, CONFIG_SYS_SDRAM_BASE + dram_size - CONFIG_SYS_MEM_TOP_HIDE); +	debug("Completed\n"); +	sync(); +	remove_tlb(CONFIG_SYS_SDRAM_BASE, dram_size); + +#if defined(CONFIG_DDR_ECC) +	/* +	 * If ECC is enabled, clear and enable interrupts +	 */ +	if (is_ecc_enabled()) { +		u32 val; + +		sync(); +		/* Clear error status */ +		mfsdram(DDR0_00, val); +		mtsdram(DDR0_00, val | DDR0_00_INT_ACK_ALL); +		/* Set 'int_mask' parameter to functionnal value */ +		mfsdram(DDR0_01, val); +		mtsdram(DDR0_01, (val & ~DDR0_01_INT_MASK_MASK) | +			DDR0_01_INT_MASK_ALL_OFF); +#if defined(CONFIG_DDR_DATA_EYE) +		/* +		 * Running denali_core_search_data_eye() when ECC is enabled +		 * causes non-ECC machine checks.  This clears them. +		 */ +		print_mcsr(); +		mtspr(SPRN_MCSR, mfspr(SPRN_MCSR)); +		print_mcsr(); +#endif +		sync(); +	} +#endif /* defined(CONFIG_DDR_ECC) */ +#endif /* defined(CONFIG_ZERO_SDRAM) || defined(CONFIG_DDR_ECC) */ + +	program_tlb(0, CONFIG_SYS_SDRAM_BASE, dram_size, MY_TLB_WORD2_I_ENABLE); +	return dram_size; +} + +void board_add_ram_info(int use_default) +{ +	u32 val; + +	printf(" (ECC"); +	if (!is_ecc_enabled()) { +		printf(" not"); +	} +	printf(" enabled, %ld MHz", (2 * get_bus_freq(0)) / 1000000); + +	mfsdram(DDR0_03, val); +	printf(", CL%d)", DDR0_03_CASLAT_LIN_DECODE(val) >> 1); +} +#endif /* CONFIG_SPD_EEPROM */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/ecc.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/ecc.c new file mode 100644 index 00000000..88a4605a --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/ecc.c @@ -0,0 +1,188 @@ +/* + *    Copyright (c) 2008 Nuovation System Designs, LLC + *      Grant Erickson <gerickson@nuovations.com> + * + *    (C) Copyright 2005-2009 + *    Stefan Roese, DENX Software Engineering, sr@denx.de. + * + *    (C) Copyright 2002 + *    Jun Gu, Artesyn Technology, jung@artesyncp.com + * + *    (C) Copyright 2001 + *    Bill Hunter, Wave 7 Optics, williamhunter@attbi.com + * + * SPDX-License-Identifier:	GPL-2.0+ + * + *    Description: + *	This file implements generic DRAM ECC initialization for + *	PowerPC processors using a SDRAM DDR/DDR2 controller, + *	including the 405EX(r), 440GP/GX/EP/GR, 440SP(E), and + *	460EX/GT. + */ + +#include <common.h> +#include <asm/ppc4xx.h> +#include <ppc_asm.tmpl> +#include <ppc_defs.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/mmu.h> +#include <asm/cache.h> + +#include "ecc.h" + +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) || \ +    defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2) +#if defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC) + +#if defined(CONFIG_405EX) +/* + * Currently only 405EX uses 16bit data bus width as an alternative + * option to 32bit data width (SDRAM0_MCOPT1_WDTH) + */ +#define SDRAM_DATA_ALT_WIDTH	2 +#else +#define SDRAM_DATA_ALT_WIDTH	8 +#endif + +static void wait_ddr_idle(void) +{ +	u32 val; + +	do { +		mfsdram(SDRAM_MCSTAT, val); +	} while ((val & SDRAM_MCSTAT_IDLE_MASK) == SDRAM_MCSTAT_IDLE_NOT); +} + +static void program_ecc_addr(unsigned long start_address, +			     unsigned long num_bytes, +			     unsigned long tlb_word2_i_value) +{ +	unsigned long current_address; +	unsigned long end_address; +	unsigned long address_increment; +	unsigned long mcopt1; +	char str[] = "ECC generation -"; +	char slash[] = "\\|/-\\|/-"; +	int loop = 0; +	int loopi = 0; + +	current_address = start_address; +	mfsdram(SDRAM_MCOPT1, mcopt1); +	if ((mcopt1 & SDRAM_MCOPT1_MCHK_MASK) != SDRAM_MCOPT1_MCHK_NON) { +		mtsdram(SDRAM_MCOPT1, +			(mcopt1 & ~SDRAM_MCOPT1_MCHK_MASK) | SDRAM_MCOPT1_MCHK_GEN); +		sync(); +		eieio(); +		wait_ddr_idle(); + +		puts(str); + +#ifdef CONFIG_440 +		if (tlb_word2_i_value == TLB_WORD2_I_ENABLE) { +#endif +			/* ECC bit set method for non-cached memory */ +			if ((mcopt1 & SDRAM_MCOPT1_DMWD_MASK) == SDRAM_MCOPT1_DMWD_32) +				address_increment = 4; +			else +				address_increment = SDRAM_DATA_ALT_WIDTH; +			end_address = current_address + num_bytes; + +			while (current_address < end_address) { +				*((unsigned long *)current_address) = 0; +				current_address += address_increment; + +				if ((loop++ % (2 << 20)) == 0) { +					putc('\b'); +					putc(slash[loopi++ % 8]); +				} +			} +#ifdef CONFIG_440 +		} else { +			/* ECC bit set method for cached memory */ +			dcbz_area(start_address, num_bytes); +			/* Write modified dcache lines back to memory */ +			clean_dcache_range(start_address, start_address + num_bytes); +		} +#endif /* CONFIG_440 */ + +		blank_string(strlen(str)); + +		sync(); +		eieio(); +		wait_ddr_idle(); + +		/* clear ECC error repoting registers */ +		mtsdram(SDRAM_ECCES, 0xffffffff); +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) +		/* +		 * IBM DDR(1) core (440GX): +		 * Clear Mx bits in SDRAM0_BESR0/1 +		 */ +		mtsdram(SDRAM0_BESR0, 0xffffffff); +		mtsdram(SDRAM0_BESR1, 0xffffffff); +#elif defined(CONFIG_440) +		/* +		 * 440/460 DDR2 core: +		 * Clear EMID (Error PLB Master ID) in MQ0_ESL +		 */ +		mtdcr(SDRAM_ERRSTATLL, 0xfff00000); +#else +		/* +		 * 405EX(r) DDR2 core: +		 * Clear M0ID (Error PLB Master ID) in SDRAM_BESR +		 */ +		mtsdram(SDRAM_BESR, 0xf0000000); +#endif + +		mtsdram(SDRAM_MCOPT1, +			(mcopt1 & ~SDRAM_MCOPT1_MCHK_MASK) | SDRAM_MCOPT1_MCHK_CHK_REP); +		sync(); +		eieio(); +		wait_ddr_idle(); +	} +} + +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) +void ecc_init(unsigned long * const start, unsigned long size) +{ +	/* +	 * Init ECC with cache disabled (on PPC's with IBM DDR +	 * controller (non DDR2), not tested with cache enabled yet +	 */ +	program_ecc_addr((u32)start, size, TLB_WORD2_I_ENABLE); +} +#endif + +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2) +void do_program_ecc(unsigned long tlb_word2_i_value) +{ +	unsigned long mcopt1; +	unsigned long mcopt2; +	unsigned long mcstat; +	phys_size_t memsize = sdram_memsize(); + +	if (memsize > CONFIG_MAX_MEM_MAPPED) { +		printf("\nWarning: Can't enable ECC on systems with more than 2GB of SDRAM!\n"); +		return; +	} + +	mfsdram(SDRAM_MCOPT1, mcopt1); +	mfsdram(SDRAM_MCOPT2, mcopt2); + +	if ((mcopt1 & SDRAM_MCOPT1_MCHK_MASK) != SDRAM_MCOPT1_MCHK_NON) { +		/* DDR controller must be enabled and not in self-refresh. */ +		mfsdram(SDRAM_MCSTAT, mcstat); +		if (((mcopt2 & SDRAM_MCOPT2_DCEN_MASK) == SDRAM_MCOPT2_DCEN_ENABLE) +		    && ((mcopt2 & SDRAM_MCOPT2_SREN_MASK) == SDRAM_MCOPT2_SREN_EXIT) +		    && ((mcstat & (SDRAM_MCSTAT_MIC_MASK | SDRAM_MCSTAT_SRMS_MASK)) +			== (SDRAM_MCSTAT_MIC_COMP | SDRAM_MCSTAT_SRMS_NOT_SF))) { + +			program_ecc_addr(0, memsize, tlb_word2_i_value); +		} +	} +} +#endif + +#endif /* defined(CONFIG_DDR_ECC) || defined(CONFIG_SDRAM_ECC) */ +#endif /* defined(CONFIG_SDRAM_PPC4xx_IBM_DDR)... */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/ecc.h b/roms/u-boot/arch/powerpc/cpu/ppc4xx/ecc.h new file mode 100644 index 00000000..bc94257f --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/ecc.h @@ -0,0 +1,58 @@ +/* + *    Copyright (c) 2008 Nuovation System Designs, LLC + *	Grant Erickson <gerickson@nuovations.com> + * + *    Copyright (c) 2007-2009 DENX Software Engineering, GmbH + *	Stefan Roese <sr@denx.de> + * + * SPDX-License-Identifier:	GPL-2.0+ + * + *    Description: + *	This file implements ECC initialization for PowerPC processors + *	using the IBM SDRAM DDR1 & DDR2 controller. + */ + +#ifndef _ECC_H_ +#define _ECC_H_ + +/* + * Since the IBM DDR controller used on 440GP/GX/EP/GR is not register + * compatible to the IBM DDR/2 controller used on 405EX/440SP/SPe/460EX/GT + * we need to make some processor dependant defines used later on by the + * driver. + */ + +/* For 440GP/GX/EP/GR */ +#if defined(CONFIG_SDRAM_PPC4xx_IBM_DDR) +#define SDRAM_MCOPT1		SDRAM_CFG0 +#define SDRAM_MCOPT1_MCHK_MASK	SDRAM_CFG0_MCHK_MASK +#define SDRAM_MCOPT1_MCHK_NON	SDRAM_CFG0_MCHK_NON +#define SDRAM_MCOPT1_MCHK_GEN	SDRAM_CFG0_MCHK_GEN +#define SDRAM_MCOPT1_MCHK_CHK	SDRAM_CFG0_MCHK_CHK +#define SDRAM_MCOPT1_MCHK_CHK_REP SDRAM_CFG0_MCHK_CHK +#define SDRAM_MCOPT1_DMWD_MASK	SDRAM_CFG0_DMWD_MASK +#define SDRAM_MCOPT1_DMWD_32	SDRAM_CFG0_DMWD_32 + +#define SDRAM_MCSTAT		SDRAM0_MCSTS +#define SDRAM_MCSTAT_IDLE_MASK	SDRAM_MCSTS_CIS +#define SDRAM_MCSTAT_IDLE_NOT	SDRAM_MCSTS_IDLE_NOT + +#define SDRAM_ECCES		SDRAM0_ECCESR +#endif + +void ecc_init(unsigned long * const start, unsigned long size); +void do_program_ecc(unsigned long tlb_word2_i_value); + +static void inline blank_string(int size) +{ +	int i; + +	for (i = 0; i < size; i++) +		putc('\b'); +	for (i = 0; i < size; i++) +		putc(' '); +	for (i = 0; i < size; i++) +		putc('\b'); +} + +#endif /* _ECC_H_ */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/fdt.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/fdt.c new file mode 100644 index 00000000..bd905d15 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/fdt.c @@ -0,0 +1,160 @@ +/* + * (C) Copyright 2007-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/cache.h> +#include <asm/ppc4xx.h> + +#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) +#include <libfdt.h> +#include <fdt_support.h> +#include <asm/4xx_pcie.h> + +DECLARE_GLOBAL_DATA_PTR; + +void __ft_board_setup(void *blob, bd_t *bd) +{ +	int rc; +	int i; +	u32 bxcr; +	u32 ranges[EBC_NUM_BANKS * 4]; +	u32 *p = ranges; +	char ebc_path[] = "/plb/opb/ebc"; + +	ft_cpu_setup(blob, bd); + +	/* +	 * Read 4xx EBC bus bridge registers to get mappings of the +	 * peripheral banks into the OPB/PLB address space +	 */ +	for (i = 0; i < EBC_NUM_BANKS; i++) { +		mtdcr(EBC0_CFGADDR, EBC_BXCR(i)); +		bxcr = mfdcr(EBC0_CFGDATA); + +		if ((bxcr & EBC_BXCR_BU_MASK) != EBC_BXCR_BU_NONE) { +			*p++ = i; +			*p++ = 0; +			*p++ = bxcr & EBC_BXCR_BAS_MASK; +			*p++ = EBC_BXCR_BANK_SIZE(bxcr); +		} +	} + + +#ifdef CONFIG_FDT_FIXUP_NOR_FLASH_SIZE +	/* Update reg property in all nor flash nodes too */ +	fdt_fixup_nor_flash_size(blob); +#endif + +	/* Some 405 PPC's have EBC as direct PLB child in the dts */ +	if (fdt_path_offset(blob, ebc_path) < 0) +		strcpy(ebc_path, "/plb/ebc"); +	rc = fdt_find_and_setprop(blob, ebc_path, "ranges", ranges, +				  (p - ranges) * sizeof(u32), 1); +	if (rc) { +		printf("Unable to update property EBC mappings, err=%s\n", +		       fdt_strerror(rc)); +	} +} +void ft_board_setup(void *blob, bd_t *bd) __attribute__((weak, alias("__ft_board_setup"))); + +/* + * Fixup all PCIe nodes by setting the device_type property + * to "pci-endpoint" instead is "pci" for endpoint ports. + * This property will get checked later by the Linux driver + * to properly configure the PCIe port in Linux (again). + */ +void fdt_pcie_setup(void *blob) +{ +	const char *compat = "ibm,plb-pciex"; +	const char *prop = "device_type"; +	const char *prop_val = "pci-endpoint"; +	const u32 *port; +	int no; +	int rc; + +	/* Search first PCIe node */ +	no = fdt_node_offset_by_compatible(blob, -1, compat); +	while (no != -FDT_ERR_NOTFOUND) { +		port = fdt_getprop(blob, no, "port", NULL); +		if (port == NULL) { +			printf("WARNING: could not find port property\n"); +		} else { +			if (is_end_point(*port)) { +				rc = fdt_setprop(blob, no, prop, prop_val, +						 strlen(prop_val) + 1); +				if (rc < 0) +					printf("WARNING: could not set %s for %s: %s.\n", +					       prop, compat, fdt_strerror(rc)); +			} +		} + +		/* Jump to next PCIe node */ +		no = fdt_node_offset_by_compatible(blob, no, compat); +	} +} + +void ft_cpu_setup(void *blob, bd_t *bd) +{ +	sys_info_t sys_info; +	int off, ndepth = 0; + +	get_sys_info(&sys_info); + +	do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, "timebase-frequency", +			     bd->bi_intfreq, 1); +	do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, "clock-frequency", +			     bd->bi_intfreq, 1); +	do_fixup_by_path_u32(blob, "/plb", "clock-frequency", sys_info.freqPLB, 1); +	do_fixup_by_path_u32(blob, "/plb/opb", "clock-frequency", sys_info.freqOPB, 1); + +	if (fdt_path_offset(blob, "/plb/opb/ebc") >= 0) +		do_fixup_by_path_u32(blob, "/plb/opb/ebc", "clock-frequency", +			sys_info.freqEBC, 1); +	else +		do_fixup_by_path_u32(blob, "/plb/ebc", "clock-frequency", +			sys_info.freqEBC, 1); + +	fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); + +	/* +	 * Fixup all UART clocks for CPU internal UARTs +	 * (only these UARTs are definitely clocked by gd->arch.uart_clk) +	 * +	 * These UARTs are direct childs of /plb/opb. This code +	 * does not touch any UARTs that are connected to the ebc. +	 */ +	off = fdt_path_offset(blob, "/plb/opb"); +	while ((off = fdt_next_node(blob, off, &ndepth)) >= 0) { +		/* +		 * process all sub nodes and stop when we are back +		 * at the starting depth +		 */ +		if (ndepth <= 0) +			break; + +		/* only update direct childs */ +		if ((ndepth == 1) && +		    (fdt_node_check_compatible(blob, off, "ns16550") == 0)) +			fdt_setprop(blob, off, +				    "clock-frequency", +				    (void *)&gd->arch.uart_clk, 4); +	} + +	/* +	 * Fixup all ethernet nodes +	 * Note: aliases in the dts are required for this +	 */ +	fdt_fixup_ethernet(blob); + +	/* +	 * Fixup all available PCIe nodes by setting the device_type property +	 */ +	fdt_pcie_setup(blob); +} +#endif /* CONFIG_OF_LIBFDT && CONFIG_OF_BOARD_SETUP */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/gpio.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/gpio.c new file mode 100644 index 00000000..6d480dab --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/gpio.c @@ -0,0 +1,244 @@ +/* + * (C) Copyright 2007-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/ppc4xx-gpio.h> + +/* Only compile this file for boards with GPIO support */ +#if defined(GPIO0_BASE) + +#if defined(CONFIG_SYS_4xx_GPIO_TABLE) +gpio_param_s const gpio_tab[GPIO_GROUP_MAX][GPIO_MAX] = CONFIG_SYS_4xx_GPIO_TABLE; +#endif + +#if defined(GPIO0_OSRL) +/* Only some 4xx variants support alternate funtions on the GPIO's */ +void gpio_config(int pin, int in_out, int gpio_alt, int out_val) +{ +	u32 mask; +	u32 mask2; +	u32 val; +	u32 offs = 0; +	u32 offs2 = 0; +	int pin2 = pin << 1; + +	if (pin >= GPIO_MAX) { +		offs = 0x100; +		pin -= GPIO_MAX; +	} + +	if (pin >= GPIO_MAX/2) { +		offs2 = 0x4; +		pin2 = (pin - GPIO_MAX/2) << 1; +	} + +	mask = 0x80000000 >> pin; +	mask2 = 0xc0000000 >> pin2; + +	/* first set TCR to 0 */ +	out_be32((void *)GPIO0_TCR + offs, in_be32((void *)GPIO0_TCR + offs) & ~mask); + +	if (in_out == GPIO_OUT) { +		val = in_be32((void *)GPIO0_OSRL + offs + offs2) & ~mask2; +		switch (gpio_alt) { +		case GPIO_ALT1: +			val |= GPIO_ALT1_SEL >> pin2; +			break; +		case GPIO_ALT2: +			val |= GPIO_ALT2_SEL >> pin2; +			break; +		case GPIO_ALT3: +			val |= GPIO_ALT3_SEL >> pin2; +			break; +		} +		out_be32((void *)GPIO0_OSRL + offs + offs2, val); + +		/* setup requested output value */ +		if (out_val == GPIO_OUT_0) +			out_be32((void *)GPIO0_OR + offs, +				 in_be32((void *)GPIO0_OR + offs) & ~mask); +		else if (out_val == GPIO_OUT_1) +			out_be32((void *)GPIO0_OR + offs, +				 in_be32((void *)GPIO0_OR + offs) | mask); + +		/* now configure TCR to drive output if selected */ +		out_be32((void *)GPIO0_TCR + offs, +			 in_be32((void *)GPIO0_TCR + offs) | mask); +	} else { +		val = in_be32((void *)GPIO0_ISR1L + offs + offs2) & ~mask2; +		val |= GPIO_IN_SEL >> pin2; +		out_be32((void *)GPIO0_ISR1L + offs + offs2, val); +	} +} +#endif /* GPIO_OSRL */ + +void gpio_write_bit(int pin, int val) +{ +	u32 offs = 0; + +	if (pin >= GPIO_MAX) { +		offs = 0x100; +		pin -= GPIO_MAX; +	} + +	if (val) +		out_be32((void *)GPIO0_OR + offs, +			 in_be32((void *)GPIO0_OR + offs) | GPIO_VAL(pin)); +	else +		out_be32((void *)GPIO0_OR + offs, +			 in_be32((void *)GPIO0_OR + offs) & ~GPIO_VAL(pin)); +} + +int gpio_read_out_bit(int pin) +{ +	u32 offs = 0; + +	if (pin >= GPIO_MAX) { +		offs = 0x100; +		pin -= GPIO_MAX; +	} + +	return (in_be32((void *)GPIO0_OR + offs) & GPIO_VAL(pin) ? 1 : 0); +} + +int gpio_read_in_bit(int pin) +{ +	u32 offs = 0; + +	if (pin >= GPIO_MAX) { +		offs = 0x100; +		pin -= GPIO_MAX; +	} + +	return (in_be32((void *)GPIO0_IR + offs) & GPIO_VAL(pin) ? 1 : 0); +} + +#if defined(CONFIG_SYS_4xx_GPIO_TABLE) +void gpio_set_chip_configuration(void) +{ +	unsigned char i=0, j=0, offs=0, gpio_core; +	unsigned long reg, core_add; + +	for (gpio_core=0; gpio_core<GPIO_GROUP_MAX; gpio_core++) { +		j = 0; +		offs = 0; +		/* GPIO config of the GPIOs 0 to 31 */ +		for (i=0; i<GPIO_MAX; i++, j++) { +			if (i == GPIO_MAX/2) { +				offs = 4; +				j = i-16; +			} + +			core_add = gpio_tab[gpio_core][i].add; + +			if ((gpio_tab[gpio_core][i].in_out == GPIO_IN) || +			    (gpio_tab[gpio_core][i].in_out == GPIO_BI)) { + +				switch (gpio_tab[gpio_core][i].alt_nb) { +				case GPIO_SEL: +					break; + +				case GPIO_ALT1: +					reg = in_be32((void *)GPIO_IS1(core_add+offs)) +						& ~(GPIO_MASK >> (j*2)); +					reg = reg | (GPIO_IN_SEL >> (j*2)); +					out_be32((void *)GPIO_IS1(core_add+offs), reg); +					break; + +				case GPIO_ALT2: +					reg = in_be32((void *)GPIO_IS2(core_add+offs)) +						& ~(GPIO_MASK >> (j*2)); +					reg = reg | (GPIO_IN_SEL >> (j*2)); +					out_be32((void *)GPIO_IS2(core_add+offs), reg); +					break; + +				case GPIO_ALT3: +					reg = in_be32((void *)GPIO_IS3(core_add+offs)) +						& ~(GPIO_MASK >> (j*2)); +					reg = reg | (GPIO_IN_SEL >> (j*2)); +					out_be32((void *)GPIO_IS3(core_add+offs), reg); +					break; +				} +			} + +			if ((gpio_tab[gpio_core][i].in_out == GPIO_OUT) || +			    (gpio_tab[gpio_core][i].in_out == GPIO_BI)) { + +				u32 gpio_alt_sel = 0; + +				switch (gpio_tab[gpio_core][i].alt_nb) { +				case GPIO_SEL: +					/* +					 * Setup output value +					 * 1 -> high level +					 * 0 -> low level +					 * else -> don't touch +					 */ +					reg = in_be32((void *)GPIO_OR(core_add)); +					if (gpio_tab[gpio_core][i].out_val == GPIO_OUT_1) +						reg |= (0x80000000 >> (i)); +					else if (gpio_tab[gpio_core][i].out_val == GPIO_OUT_0) +						reg &= ~(0x80000000 >> (i)); +					out_be32((void *)GPIO_OR(core_add), reg); + +					reg = in_be32((void *)GPIO_TCR(core_add)) | +						(0x80000000 >> (i)); +					out_be32((void *)GPIO_TCR(core_add), reg); + +					reg = in_be32((void *)GPIO_OS(core_add+offs)) +						& ~(GPIO_MASK >> (j*2)); +					out_be32((void *)GPIO_OS(core_add+offs), reg); +					reg = in_be32((void *)GPIO_TS(core_add+offs)) +						& ~(GPIO_MASK >> (j*2)); +					out_be32((void *)GPIO_TS(core_add+offs), reg); +					break; + +				case GPIO_ALT1: +					gpio_alt_sel = GPIO_ALT1_SEL; +					break; + +				case GPIO_ALT2: +					gpio_alt_sel = GPIO_ALT2_SEL; +					break; + +				case GPIO_ALT3: +					gpio_alt_sel = GPIO_ALT3_SEL; +					break; +				} + +				if (0 != gpio_alt_sel) { +					reg = in_be32((void *)GPIO_OS(core_add+offs)) +						& ~(GPIO_MASK >> (j*2)); +					reg = reg | (gpio_alt_sel >> (j*2)); +					out_be32((void *)GPIO_OS(core_add+offs), reg); + +					if (gpio_tab[gpio_core][i].out_val == GPIO_OUT_1) { +						reg = in_be32((void *)GPIO_TCR(core_add)) +							| (0x80000000 >> (i)); +						out_be32((void *)GPIO_TCR(core_add), reg); +						reg = in_be32((void *)GPIO_TS(core_add+offs)) +							& ~(GPIO_MASK >> (j*2)); +						out_be32((void *)GPIO_TS(core_add+offs), reg); +					} else { +						reg = in_be32((void *)GPIO_TCR(core_add)) +							& ~(0x80000000 >> (i)); +						out_be32((void *)GPIO_TCR(core_add), reg); +						reg = in_be32((void *)GPIO_TS(core_add+offs)) +							& ~(GPIO_MASK >> (j*2)); +						reg = reg | (gpio_alt_sel >> (j*2)); +						out_be32((void *)GPIO_TS(core_add+offs), reg); +					} +				} +			} +		} +	} +} + +#endif /* GPIO0_BASE */ +#endif /* CONFIG_SYS_4xx_GPIO_TABLE */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/interrupts.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/interrupts.c new file mode 100644 index 00000000..d9b56546 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/interrupts.c @@ -0,0 +1,193 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 (440 port) + * Scott McNutt, Artesyn Communication Producs, smcnutt@artsyncp.com + * + * (C) Copyright 2003 (440GX port) + * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.com + * + * (C) Copyright 2008 (PPC440X05 port for Virtex 5 FX) + * Ricardo Ribalda-Universidad Autonoma de Madrid-ricardo.ribalda@uam.es + * Work supported by Qtechnology (htpp://qtec.com) + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/interrupt.h> +#include <asm/ppc4xx.h> +#include <ppc_asm.tmpl> +#include <commproc.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * CPM interrupt vector functions. + */ +struct	irq_action { +	interrupt_handler_t *handler; +	void *arg; +	int count; +}; +static struct irq_action irq_vecs[IRQ_MAX]; + +#if defined(CONFIG_440) + +/* SPRN changed in 440 */ +static __inline__ void set_evpr(unsigned long val) +{ +	asm volatile("mtspr 0x03f,%0" : : "r" (val)); +} + +#else /* !defined(CONFIG_440) */ + +static __inline__ void set_pit(unsigned long val) +{ +	asm volatile("mtpit %0" : : "r" (val)); +} + +static __inline__ void set_evpr(unsigned long val) +{ +	asm volatile("mtevpr %0" : : "r" (val)); +} +#endif /* defined(CONFIG_440 */ + +int interrupt_init_cpu (unsigned *decrementer_count) +{ +	int vec; +	unsigned long val; + +	/* decrementer is automatically reloaded */ +	*decrementer_count = 0; + +	/* +	 * Mark all irqs as free +	 */ +	for (vec = 0; vec < IRQ_MAX; vec++) { +		irq_vecs[vec].handler = NULL; +		irq_vecs[vec].arg = NULL; +		irq_vecs[vec].count = 0; +	} + +#ifdef CONFIG_4xx +	/* +	 * Init PIT +	 */ +#if defined(CONFIG_440) +	val = mfspr( SPRN_TCR ); +	val &= (~0x04400000);		/* clear DIS & ARE */ +	mtspr( SPRN_TCR, val ); +	mtspr( SPRN_DEC, 0 );		/* Prevent exception after TSR clear*/ +	mtspr( SPRN_DECAR, 0 );		/* clear reload */ +	mtspr( SPRN_TSR, 0x08000000 );	/* clear DEC status */ +	val = gd->bd->bi_intfreq/1000;	/* 1 msec */ +	mtspr( SPRN_DECAR, val );		/* Set auto-reload value */ +	mtspr( SPRN_DEC, val );		/* Set inital val */ +#else +	set_pit(gd->bd->bi_intfreq / 1000); +#endif +#endif  /* CONFIG_4xx */ + +#ifdef CONFIG_ADCIOP +	/* +	 * Init PIT +	 */ +	set_pit(66000); +#endif + +	/* +	 * Enable PIT +	 */ +	val = mfspr(SPRN_TCR); +	val |= 0x04400000; +	mtspr(SPRN_TCR, val); + +	/* +	 * Set EVPR to 0 +	 */ +	set_evpr(0x00000000); + +	/* +	 * Call uic or xilinx_irq pic_enable +	 */ +	pic_enable(); + +	return (0); +} + +void timer_interrupt_cpu(struct pt_regs *regs) +{ +	/* nothing to do here */ +	return; +} + +void interrupt_run_handler(int vec) +{ +	irq_vecs[vec].count++; + +	if (irq_vecs[vec].handler != NULL) { +		/* call isr */ +		(*irq_vecs[vec].handler) (irq_vecs[vec].arg); +	} else { +		pic_irq_disable(vec); +		printf("Masking bogus interrupt vector %d\n", vec); +	} + +	pic_irq_ack(vec); +	return; +} + +void irq_install_handler(int vec, interrupt_handler_t * handler, void *arg) +{ +	/* +	 * Print warning when replacing with a different irq vector +	 */ +	if ((irq_vecs[vec].handler != NULL) && (irq_vecs[vec].handler != handler)) { +		printf("Interrupt vector %d: handler 0x%x replacing 0x%x\n", +		       vec, (uint) handler, (uint) irq_vecs[vec].handler); +	} +	irq_vecs[vec].handler = handler; +	irq_vecs[vec].arg = arg; + +	pic_irq_enable(vec); +	return; +} + +void irq_free_handler(int vec) +{ +	debug("Free interrupt for vector %d ==> %p\n", +	      vec, irq_vecs[vec].handler); + +	pic_irq_disable(vec); + +	irq_vecs[vec].handler = NULL; +	irq_vecs[vec].arg = NULL; +	return; +} + +#if defined(CONFIG_CMD_IRQ) +int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	int vec; + +	printf ("Interrupt-Information:\n"); +	printf ("Nr  Routine   Arg       Count\n"); + +	for (vec = 0; vec < IRQ_MAX; vec++) { +		if (irq_vecs[vec].handler != NULL) { +			printf ("%02d  %08lx  %08lx  %d\n", +				vec, +				(ulong)irq_vecs[vec].handler, +				(ulong)irq_vecs[vec].arg, +				irq_vecs[vec].count); +		} +	} + +	return 0; +} +#endif diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/kgdb.S b/roms/u-boot/arch/powerpc/cpu/ppc4xx/kgdb.S new file mode 100644 index 00000000..f274c5d5 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/kgdb.S @@ -0,0 +1,61 @@ +/* + *  Copyright (C) 2000	Murray Jensen <Murray.Jensen@cmst.csiro.au> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <config.h> +#include <command.h> +#include <asm/ppc4xx.h> +#include <version.h> + +#define CONFIG_405GP 1		/* needed for Linux kernel header files */ + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> + +#if defined(CONFIG_CMD_KGDB) + /* + * cache flushing routines for kgdb + */ + +	.globl	kgdb_flush_cache_all +kgdb_flush_cache_all: +	/* icache */ +	iccci   r0,r0		/* iccci invalidates the entire I cache */ +	/* dcache */ +	addi    r6,0,0x0000     /* clear GPR 6 */ +	addi    r7,r0, 128	/* do loop for # of dcache lines */ +				/* NOTE: dccci invalidates both */ +	mtctr   r7              /* ways in the D cache */ +..dcloop: +	dccci   0,r6            /* invalidate line */ +	addi    r6,r6, 32	/* bump to next line */ +	bdnz    ..dcloop +	blr + +	.globl	kgdb_flush_cache_range +kgdb_flush_cache_range: +	li	r5,L1_CACHE_BYTES-1 +	andc	r3,r3,r5 +	subf	r4,r3,r4 +	add	r4,r4,r5 +	srwi.	r4,r4,L1_CACHE_SHIFT +	beqlr +	mtctr	r4 +	mr	r6,r3 +1:	dcbst	0,r3 +	addi	r3,r3,L1_CACHE_BYTES +	bdnz	1b +	sync			/* wait for dcbst's to get to ram */ +	mtctr	r4 +2:	icbi	0,r6 +	addi	r6,r6,L1_CACHE_BYTES +	bdnz	2b +	SYNC +	blr + +#endif diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/miiphy.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/miiphy.c new file mode 100644 index 00000000..10147de0 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/miiphy.c @@ -0,0 +1,346 @@ +/* + * SPDX-License-Identifier:	GPL-2.0	IBM-pibs + */ +/*-----------------------------------------------------------------------------+ +  | +  |  File Name:	 miiphy.c +  | +  |  Function:	 This module has utilities for accessing the MII PHY through +  |	       the EMAC3 macro. +  | +  |  Author:	 Mark Wisner +  | +  +-----------------------------------------------------------------------------*/ + +/* define DEBUG for debugging output (obviously ;-)) */ +#if 0 +#define DEBUG +#endif + +#include <common.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <ppc_asm.tmpl> +#include <commproc.h> +#include <asm/ppc4xx-emac.h> +#include <asm/ppc4xx-mal.h> +#include <miiphy.h> + +#if !defined(CONFIG_PHY_CLK_FREQ) +#define CONFIG_PHY_CLK_FREQ	0 +#endif + +/***********************************************************/ +/* Dump out to the screen PHY regs			   */ +/***********************************************************/ + +void miiphy_dump (char *devname, unsigned char addr) +{ +	unsigned long i; +	unsigned short data; + +	for (i = 0; i < 0x1A; i++) { +		if (miiphy_read (devname, addr, i, &data)) { +			printf ("read error for reg %lx\n", i); +			return; +		} +		printf ("Phy reg %lx ==> %4x\n", i, data); + +		/* jump to the next set of regs */ +		if (i == 0x07) +			i = 0x0f; + +	}			/* end for loop */ +}				/* end dump */ + +/***********************************************************/ +/* (Re)start autonegotiation				   */ +/***********************************************************/ +int phy_setup_aneg (char *devname, unsigned char addr) +{ +	u16 bmcr; + +#if defined(CONFIG_PHY_DYNAMIC_ANEG) +	/* +	 * Set up advertisement based on capablilities reported by the PHY. +	 * This should work for both copper and fiber. +	 */ +	u16 bmsr; +#if defined(CONFIG_PHY_GIGE) +	u16 exsr = 0x0000; +#endif + +	miiphy_read (devname, addr, MII_BMSR, &bmsr); + +#if defined(CONFIG_PHY_GIGE) +	if (bmsr & BMSR_ESTATEN) +		miiphy_read (devname, addr, MII_ESTATUS, &exsr); + +	if (exsr & (ESTATUS_1000XF | ESTATUS_1000XH)) { +		/* 1000BASE-X */ +		u16 anar = 0x0000; + +		if (exsr & ESTATUS_1000XF) +			anar |= ADVERTISE_1000XFULL; + +		if (exsr & ESTATUS_1000XH) +			anar |= ADVERTISE_1000XHALF; + +		miiphy_write (devname, addr, MII_ADVERTISE, anar); +	} else +#endif +	{ +		u16 anar, btcr; + +		miiphy_read (devname, addr, MII_ADVERTISE, &anar); +		anar &= ~(0x5000 | LPA_100BASE4 | LPA_100FULL | +			  LPA_100HALF | LPA_10FULL | LPA_10HALF); + +		miiphy_read (devname, addr, MII_CTRL1000, &btcr); +		btcr &= ~(0x00FF | PHY_1000BTCR_1000FD | PHY_1000BTCR_1000HD); + +		if (bmsr & BMSR_100BASE4) +			anar |= LPA_100BASE4; + +		if (bmsr & BMSR_100FULL) +			anar |= LPA_100FULL; + +		if (bmsr & BMSR_100HALF) +			anar |= LPA_100HALF; + +		if (bmsr & BMSR_10FULL) +			anar |= LPA_10FULL; + +		if (bmsr & BMSR_10HALF) +			anar |= LPA_10HALF; + +		miiphy_write (devname, addr, MII_ADVERTISE, anar); + +#if defined(CONFIG_PHY_GIGE) +		if (exsr & ESTATUS_1000_TFULL) +			btcr |= PHY_1000BTCR_1000FD; + +		if (exsr & ESTATUS_1000_THALF) +			btcr |= PHY_1000BTCR_1000HD; + +		miiphy_write (devname, addr, MII_CTRL1000, btcr); +#endif +	} + +#else /* defined(CONFIG_PHY_DYNAMIC_ANEG) */ +	/* +	 * Set up standard advertisement +	 */ +	u16 adv; + +	miiphy_read (devname, addr, MII_ADVERTISE, &adv); +	adv |= (LPA_LPACK  | LPA_100FULL | LPA_100HALF | +		LPA_10FULL | LPA_10HALF); +	miiphy_write (devname, addr, MII_ADVERTISE, adv); + +	miiphy_read (devname, addr, MII_CTRL1000, &adv); +	adv |= (0x0300); +	miiphy_write (devname, addr, MII_CTRL1000, adv); + +#endif /* defined(CONFIG_PHY_DYNAMIC_ANEG) */ + +	/* Start/Restart aneg */ +	miiphy_read (devname, addr, MII_BMCR, &bmcr); +	bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); +	miiphy_write (devname, addr, MII_BMCR, bmcr); + +	return 0; +} + +/***********************************************************/ +/* read a phy reg and return the value with a rc	   */ +/***********************************************************/ +/* AMCC_TODO: + * Find out of the choice for the emac for MDIO is from the bridges, + * i.e. ZMII or RGMII as approporiate.  If the bridges are not used + * to determine the emac for MDIO, then is the SDR0_ETH_CFG[MDIO_SEL] + * used?  If so, then this routine below does not apply to the 460EX/GT. + * + * sr: Currently on 460EX only EMAC0 works with MDIO, so we always + * return EMAC0 offset here + * vg: For 460EX/460GT if internal GPCS PHY address is specified + * return appropriate EMAC offset + */ +unsigned int miiphy_getemac_offset(u8 addr) +{ +#if defined(CONFIG_440) && \ +    !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \ +    !defined(CONFIG_460EX) && !defined(CONFIG_460GT) +	unsigned long zmii; +	unsigned long eoffset; + +	/* Need to find out which mdi port we're using */ +	zmii = in_be32((void *)ZMII0_FER); + +	if (zmii & (ZMII_FER_MDI << ZMII_FER_V (0))) +		/* using port 0 */ +		eoffset = 0; + +	else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (1))) +		/* using port 1 */ +		eoffset = 0x100; + +	else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (2))) +		/* using port 2 */ +		eoffset = 0x400; + +	else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (3))) +		/* using port 3 */ +		eoffset = 0x600; + +	else { +		/* None of the mdi ports are enabled! */ +		/* enable port 0 */ +		zmii |= ZMII_FER_MDI << ZMII_FER_V (0); +		out_be32((void *)ZMII0_FER, zmii); +		eoffset = 0; +		/* need to soft reset port 0 */ +		zmii = in_be32((void *)EMAC0_MR0); +		zmii |= EMAC_MR0_SRST; +		out_be32((void *)EMAC0_MR0, zmii); +	} + +	return (eoffset); +#else + +#if defined(CONFIG_405EX) +	unsigned long rgmii; +	int devnum = 1; + +	rgmii = in_be32((void *)RGMII_FER); +	if (rgmii & (1 << (19 - devnum))) +		return 0x100; +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +	u32 eoffset = 0; + +	switch (addr) { +#if defined(CONFIG_HAS_ETH1) && defined(CONFIG_GPCS_PHY1_ADDR) +	case CONFIG_GPCS_PHY1_ADDR: +		if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x100))) +			eoffset = 0x100; +		break; +#endif +#if defined(CONFIG_HAS_ETH2) && defined(CONFIG_GPCS_PHY2_ADDR) +	case CONFIG_GPCS_PHY2_ADDR: +		if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x300))) +			eoffset = 0x300; +		break; +#endif +#if defined(CONFIG_HAS_ETH3) && defined(CONFIG_GPCS_PHY3_ADDR) +	case CONFIG_GPCS_PHY3_ADDR: +		if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x400))) +			eoffset = 0x400; +		break; +#endif +	default: +		eoffset = 0; +		break; +	} +	return eoffset; +#endif + +	return 0; +#endif +} + +static int emac_miiphy_wait(u32 emac_reg) +{ +	u32 sta_reg; +	int i; + +	/* wait for completion */ +	i = 0; +	do { +		sta_reg = in_be32((void *)EMAC0_STACR + emac_reg); +		if (i++ > 5) { +			debug("%s [%d]: Timeout! EMAC0_STACR=0x%0x\n", __func__, +			      __LINE__, sta_reg); +			return -1; +		} +		udelay(10); +	} while ((sta_reg & EMAC_STACR_OC) == EMAC_STACR_OC_MASK); + +	return 0; +} + +static int emac_miiphy_command(u8 addr, u8 reg, int cmd, u16 value) +{ +	u32 emac_reg; +	u32 sta_reg; + +	emac_reg = miiphy_getemac_offset(addr); + +	/* wait for completion */ +	if (emac_miiphy_wait(emac_reg) != 0) +		return -1; + +	sta_reg = reg;		/* reg address */ + +	/* set clock (50MHz) and read flags */ +#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \ +    defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ +    defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ +    defined(CONFIG_405EX) +#if defined(CONFIG_IBM_EMAC4_V4)	/* EMAC4 V4 changed bit setting */ +	sta_reg = (sta_reg & ~EMAC_STACR_OP_MASK) | cmd; +#else +	sta_reg |= cmd; +#endif +#else +	sta_reg = (sta_reg | cmd) & ~EMAC_STACR_CLK_100MHZ; +#endif + +	/* Some boards (mainly 405EP based) define the PHY clock freqency fixed */ +	sta_reg = sta_reg | CONFIG_PHY_CLK_FREQ; +	sta_reg = sta_reg | ((u32)addr << 5);	/* Phy address */ +	sta_reg = sta_reg | EMAC_STACR_OC_MASK;	/* new IBM emac v4 */ +	if (cmd == EMAC_STACR_WRITE) +		memcpy(&sta_reg, &value, 2);	/* put in data */ + +	out_be32((void *)EMAC0_STACR + emac_reg, sta_reg); +	debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg); + +	/* wait for completion */ +	if (emac_miiphy_wait(emac_reg) != 0) +		return -1; + +	debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg); +	if ((sta_reg & EMAC_STACR_PHYE) != 0) +		return -1; + +	return 0; +} + +int emac4xx_miiphy_read (const char *devname, unsigned char addr, unsigned char reg, +			 unsigned short *value) +{ +	unsigned long sta_reg; +	unsigned long emac_reg; + +	emac_reg = miiphy_getemac_offset(addr); + +	if (emac_miiphy_command(addr, reg, EMAC_STACR_READ, 0) != 0) +		return -1; + +	sta_reg = in_be32((void *)EMAC0_STACR + emac_reg); +	*value = sta_reg >> 16; + +	return 0; +} + +/***********************************************************/ +/* write a phy reg and return the value with a rc	    */ +/***********************************************************/ + +int emac4xx_miiphy_write (const char *devname, unsigned char addr, unsigned char reg, +			  unsigned short value) +{ +	return emac_miiphy_command(addr, reg, EMAC_STACR_WRITE, value); +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/reginfo.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/reginfo.c new file mode 100644 index 00000000..339d38aa --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/reginfo.c @@ -0,0 +1,357 @@ +/* + *(C) Copyright 2005-2009 Netstal Maschinen AG + *    Bruno Hars (Bruno.Hars@netstal.com) + *    Niklaus Giger (Niklaus.Giger@netstal.com) + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* + * reginfo.c - register dump of HW-configuratin register for PPC4xx based board + */ + +#include <common.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/ppc4xx-uic.h> +#include <asm/ppc4xx-emac.h> + +enum REGISTER_TYPE { +	IDCR1,	/* Indirectly Accessed DCR via SDRAM0_CFGADDR/SDRAM0_CFGDATA */ +	IDCR2,	/* Indirectly Accessed DCR via EBC0_CFGADDR/EBC0_CFGDATA */ +	IDCR3,	/* Indirectly Accessed DCR via EBM0_CFGADDR/EBM0_CFGDATA */ +	IDCR4,	/* Indirectly Accessed DCR via PPM0_CFGADDR/PPM0_CFGDATA */ +	IDCR5,	/* Indirectly Accessed DCR via CPR0_CFGADDR/CPR0_CFGDATA */ +	IDCR6,	/* Indirectly Accessed DCR via SDR0_CFGADDR/SDR0_CFGDATA */ +	MM	/* Directly Accessed MMIO Register */ +}; + +struct cpu_register { +	char *name; +	enum REGISTER_TYPE type; +	u32 address; +}; + +/* + * PPC440EPx registers ordered for output + * name           type    addr            size + * ------------------------------------------- + */ + +const struct cpu_register ppc4xx_reg[] = { +	{"PB0CR",		IDCR2,	PB0CR}, +	{"PB0AP",		IDCR2,	PB0AP}, +	{"PB1CR",		IDCR2,	PB1CR}, +	{"PB1AP",		IDCR2,	PB1AP}, +	{"PB2CR",		IDCR2,	PB2CR}, +	{"PB2AP",		IDCR2,	PB2AP}, +	{"PB3CR",		IDCR2,	PB3CR}, +	{"PB3AP",		IDCR2,	PB3AP}, + +	{"PB4CR",		IDCR2,	PB4CR}, +	{"PB4AP",		IDCR2,	PB4AP}, +#if !defined(CONFIG_405EP) +	{"PB5CR",		IDCR2,	PB5CR}, +	{"PB5AP",		IDCR2,	PB5AP}, +	{"PB6CR",		IDCR2,	PB6CR}, +	{"PB6AP",		IDCR2,	PB6AP}, +	{"PB7CR",		IDCR2,	PB7CR}, +	{"PB7AP",		IDCR2,	PB7AP}, +#endif + +	{"PBEAR",		IDCR2,	PBEAR}, +#if defined(CONFIG_405EP) || defined (CONFIG_405GP) +	{"PBESR0",		IDCR2,	PBESR0}, +	{"PBESR1",		IDCR2,	PBESR1}, +#endif +	{"EBC0_CFG",		IDCR2,	EBC0_CFG}, + +#ifdef CONFIG_405GP +	{"SDRAM0_BESR0",	IDCR1,	SDRAM0_BESR0}, +	{"SDRAM0_BESRS0",	IDCR1,	SDRAM0_BESRS0}, +	{"SDRAM0_BESR1",	IDCR1,	SDRAM0_BESR1}, +	{"SDRAM0_BESRS1",	IDCR1,	SDRAM0_BESRS1}, +	{"SDRAM0_BEAR",		IDCR1,	SDRAM0_BEAR}, +	{"SDRAM0_CFG",		IDCR1,	SDRAM0_CFG}, +	{"SDRAM0_RTR",		IDCR1,	SDRAM0_RTR}, +	{"SDRAM0_PMIT",		IDCR1,	SDRAM0_PMIT}, + +	{"SDRAM0_B0CR",		IDCR1,	SDRAM0_B0CR}, +	{"SDRAM0_B1CR",		IDCR1,	SDRAM0_B1CR}, +	{"SDRAM0_B2CR",		IDCR1,	SDRAM0_B2CR}, +	{"SDRAM0_B3CR",		IDCR1,	SDRAM0_B1CR}, +	{"SDRAM0_TR",		IDCR1,	SDRAM0_TR}, +	{"SDRAM0_ECCCFG",	IDCR1,	SDRAM0_B1CR}, +	{"SDRAM0_ECCESR",	IDCR1,	SDRAM0_ECCESR}, + + +#endif + +#ifdef CONFIG_440EPX +	{"SDR0_SDSTP0",		IDCR6,	SDR0_SDSTP0}, +	{"SDR0_SDSTP1",		IDCR6,	SDR0_SDSTP1}, +	{"SDR0_SDSTP2",		IDCR6,	SDR0_SDSTP2}, +	{"SDR0_SDSTP3",		IDCR6,	SDR0_SDSTP3}, +	{"SDR0_CUST0",		IDCR6,	SDR0_CUST0}, +	{"SDR0_CUST1",		IDCR6,	SDR0_CUST1}, +	{"SDR0_EBC",		IDCR6,	SDR0_EBC}, +	{"SDR0_AMP0",		IDCR6,	SDR0_AMP0}, +	{"SDR0_AMP1",		IDCR6,	SDR0_AMP1}, +	{"SDR0_CP440",		IDCR6,	SDR0_CP440}, +	{"SDR0_CRYP0",		IDCR6,	SDR0_CRYP0}, +	{"SDR0_DDRCFG",		IDCR6,	SDR0_DDRCFG}, +	{"SDR0_EMAC0RXST",	IDCR6,	SDR0_EMAC0RXST}, +	{"SDR0_EMAC0TXST",	IDCR6,	SDR0_EMAC0TXST}, +	{"SDR0_MFR",		IDCR6,	SDR0_MFR}, +	{"SDR0_PCI0",		IDCR6,	SDR0_PCI0}, +	{"SDR0_PFC0",		IDCR6,	SDR0_PFC0}, +	{"SDR0_PFC1",		IDCR6,	SDR0_PFC1}, +	{"SDR0_PFC2",		IDCR6,	SDR0_PFC2}, +	{"SDR0_PFC4",		IDCR6,	SDR0_PFC4}, +	{"SDR0_UART0",		IDCR6,	SDR0_UART0}, +	{"SDR0_UART1",		IDCR6,	SDR0_UART1}, +	{"SDR0_UART2",		IDCR6,	SDR0_UART2}, +	{"SDR0_UART3",		IDCR6,	SDR0_UART3}, +	{"DDR0_02",		IDCR1,	DDR0_02}, +	{"DDR0_00",		IDCR1,	DDR0_00}, +	{"DDR0_01",		IDCR1,	DDR0_01}, +	{"DDR0_03",		IDCR1,	DDR0_03}, +	{"DDR0_04",		IDCR1,	DDR0_04}, +	{"DDR0_05",		IDCR1,	DDR0_05}, +	{"DDR0_06",		IDCR1,	DDR0_06}, +	{"DDR0_07",		IDCR1,	DDR0_07}, +	{"DDR0_08",		IDCR1,	DDR0_08}, +	{"DDR0_09",		IDCR1,	DDR0_09}, +	{"DDR0_10",		IDCR1,	DDR0_10}, +	{"DDR0_11",		IDCR1,	DDR0_11}, +	{"DDR0_12",		IDCR1,	DDR0_12}, +	{"DDR0_14",		IDCR1,	DDR0_14}, +	{"DDR0_17",		IDCR1,	DDR0_17}, +	{"DDR0_18",		IDCR1,	DDR0_18}, +	{"DDR0_19",		IDCR1,	DDR0_19}, +	{"DDR0_20",		IDCR1,	DDR0_20}, +	{"DDR0_21",		IDCR1,	DDR0_21}, +	{"DDR0_22",		IDCR1,	DDR0_22}, +	{"DDR0_23",		IDCR1,	DDR0_23}, +	{"DDR0_24",		IDCR1,	DDR0_24}, +	{"DDR0_25",		IDCR1,	DDR0_25}, +	{"DDR0_26",		IDCR1,	DDR0_26}, +	{"DDR0_27",		IDCR1,	DDR0_27}, +	{"DDR0_28",		IDCR1,	DDR0_28}, +	{"DDR0_31",		IDCR1,	DDR0_31}, +	{"DDR0_32",		IDCR1,	DDR0_32}, +	{"DDR0_33",		IDCR1,	DDR0_33}, +	{"DDR0_34",		IDCR1,	DDR0_34}, +	{"DDR0_35",		IDCR1,	DDR0_35}, +	{"DDR0_36",		IDCR1,	DDR0_36}, +	{"DDR0_37",		IDCR1,	DDR0_37}, +	{"DDR0_38",		IDCR1,	DDR0_38}, +	{"DDR0_39",		IDCR1,	DDR0_39}, +	{"DDR0_40",		IDCR1,	DDR0_40}, +	{"DDR0_41",		IDCR1,	DDR0_41}, +	{"DDR0_42",		IDCR1,	DDR0_42}, +	{"DDR0_43",		IDCR1,	DDR0_43}, +	{"DDR0_44",		IDCR1,	DDR0_44}, +	{"CPR0_ICFG",		IDCR5,	CPR0_ICFG}, +	{"CPR0_MALD",		IDCR5,	CPR0_MALD}, +	{"CPR0_OPBD00",		IDCR5,	CPR0_OPBD0}, +	{"CPR0_PERD0",		IDCR5,	CPR0_PERD}, +	{"CPR0_PLLC0",		IDCR5,	CPR0_PLLC}, +	{"CPR0_PLLD0",		IDCR5,	CPR0_PLLD}, +	{"CPR0_PRIMAD0",	IDCR5,	CPR0_PRIMAD0}, +	{"CPR0_PRIMBD0",	IDCR5,	CPR0_PRIMBD0}, +	{"CPR0_SPCID",		IDCR5,	CPR0_SPCID}, +	{"SPI0_MODE",		MM,	SPI0_MODE}, +	{"IIC0_CLKDIV",		MM,	PCIL0_PMM1MA}, +	{"PCIL0_PMM0MA",	MM,	PCIL0_PMM0MA}, +	{"PCIL0_PMM1MA",	MM,	PCIL0_PMM1MA}, +	{"PCIL0_PTM1LA",	MM,	PCIL0_PMM1MA}, +	{"PCIL0_PTM1MS",	MM,	PCIL0_PTM1MS}, +	{"PCIL0_PTM2LA",	MM,	PCIL0_PMM1MA}, +	{"PCIL0_PTM2MS",	MM,	PCIL0_PTM2MS}, +	{"ZMII0_FER",		MM,	ZMII0_FER}, +	{"ZMII0_SSR",		MM,	ZMII0_SSR}, +	{"EMAC0_IPGVR",		MM,	EMAC0_IPGVR}, +	{"EMAC0_MR1",		MM,	EMAC0_MR1}, +	{"EMAC0_PTR",		MM,	EMAC0_PTR}, +	{"EMAC0_RWMR",		MM,	EMAC0_RWMR}, +	{"EMAC0_STACR",		MM,	EMAC0_STACR}, +	{"EMAC0_TMR0",		MM,	EMAC0_TMR0}, +	{"EMAC0_TMR1",		MM,	EMAC0_TMR1}, +	{"EMAC0_TRTR",		MM,	EMAC0_TRTR}, +	{"EMAC1_MR1",		MM,	EMAC1_MR1}, +	{"GPIO0_OR",		MM,	GPIO0_OR}, +	{"GPIO1_OR",		MM,	GPIO1_OR}, +	{"GPIO0_TCR",		MM,	GPIO0_TCR}, +	{"GPIO1_TCR",		MM,	GPIO1_TCR}, +	{"GPIO0_ODR",		MM,	GPIO0_ODR}, +	{"GPIO1_ODR",		MM,	GPIO1_ODR}, +	{"GPIO0_OSRL",		MM,	GPIO0_OSRL}, +	{"GPIO0_OSRH",		MM,	GPIO0_OSRH}, +	{"GPIO1_OSRL",		MM,	GPIO1_OSRL}, +	{"GPIO1_OSRH",		MM,	GPIO1_OSRH}, +	{"GPIO0_TSRL",		MM,	GPIO0_TSRL}, +	{"GPIO0_TSRH",		MM,	GPIO0_TSRH}, +	{"GPIO1_TSRL",		MM,	GPIO1_TSRL}, +	{"GPIO1_TSRH",		MM,	GPIO1_TSRH}, +	{"GPIO0_IR",		MM,	GPIO0_IR}, +	{"GPIO1_IR",		MM,	GPIO1_IR}, +	{"GPIO0_ISR1L",		MM,	GPIO0_ISR1L}, +	{"GPIO0_ISR1H",		MM,	GPIO0_ISR1H}, +	{"GPIO1_ISR1L",		MM,	GPIO1_ISR1L}, +	{"GPIO1_ISR1H",		MM,	GPIO1_ISR1H}, +	{"GPIO0_ISR2L",		MM,	GPIO0_ISR2L}, +	{"GPIO0_ISR2H",		MM,	GPIO0_ISR2H}, +	{"GPIO1_ISR2L",		MM,	GPIO1_ISR2L}, +	{"GPIO1_ISR2H",		MM,	GPIO1_ISR2H}, +	{"GPIO0_ISR3L",		MM,	GPIO0_ISR3L}, +	{"GPIO0_ISR3H",		MM,	GPIO0_ISR3H}, +	{"GPIO1_ISR3L",		MM,	GPIO1_ISR3L}, +	{"GPIO1_ISR3H",		MM,	GPIO1_ISR3H}, +	{"SDR0_USB2PHY0CR",	IDCR6,	SDR0_USB2PHY0CR}, +	{"SDR0_USB2H0CR",	IDCR6,	SDR0_USB2H0CR}, +	{"SDR0_USB2D0CR",	IDCR6,	SDR0_USB2D0CR}, +#endif +}; + +/* + * CPU Register dump of PPC4xx HW configuration registers + * Output: first all DCR-registers, then in order of struct ppc4xx_reg + */ +#define PRINT_DCR(dcr) 	printf("0x%08x %-16s: 0x%08x\n", dcr,#dcr, mfdcr(dcr)); + +void ppc4xx_reginfo(void) +{ +	unsigned int i; +	unsigned int n; +	u32 value; +	enum REGISTER_TYPE type; +#if defined (CONFIG_405EP) +	printf("Dump PPC405EP HW configuration registers\n\n"); +#elif CONFIG_405GP +	printf ("Dump 405GP HW configuration registers\n\n"); +#elif CONFIG_440EPX +	printf("Dump PPC440EPx HW configuration registers\n\n"); +#endif +	printf("MSR: 0x%08x\n", mfmsr()); + +	printf ("\nUniversal Interrupt Controller Regs\n"); +	PRINT_DCR(UIC0SR); +	PRINT_DCR(UIC0ER); +	PRINT_DCR(UIC0CR); +	PRINT_DCR(UIC0PR); +	PRINT_DCR(UIC0TR); +	PRINT_DCR(UIC0MSR); +	PRINT_DCR(UIC0VR); +	PRINT_DCR(UIC0VCR); + +#if (UIC_MAX > 1) +	PRINT_DCR(UIC2SR); +	PRINT_DCR(UIC2ER); +	PRINT_DCR(UIC2CR); +	PRINT_DCR(UIC2PR); +	PRINT_DCR(UIC2TR); +	PRINT_DCR(UIC2MSR); +	PRINT_DCR(UIC2VR); +	PRINT_DCR(UIC2VCR); +#endif + +#if (UIC_MAX > 2) +	PRINT_DCR(UIC2SR); +	PRINT_DCR(UIC2ER); +	PRINT_DCR(UIC2CR); +	PRINT_DCR(UIC2PR); +	PRINT_DCR(UIC2TR); +	PRINT_DCR(UIC2MSR); +	PRINT_DCR(UIC2VR); +	PRINT_DCR(UIC2VCR); +#endif + +#if (UIC_MAX > 3) +	PRINT_DCR(UIC3SR); +	PRINT_DCR(UIC3ER); +	PRINT_DCR(UIC3CR); +	PRINT_DCR(UIC3PR); +	PRINT_DCR(UIC3TR); +	PRINT_DCR(UIC3MSR); +	PRINT_DCR(UIC3VR); +	PRINT_DCR(UIC3VCR); +#endif + +#if defined (CONFIG_405EP) || defined (CONFIG_405GP) +	printf ("\n\nDMA Channels\n"); +	PRINT_DCR(DMASR); +	PRINT_DCR(DMASGC); +	PRINT_DCR(DMAADR); + +	PRINT_DCR(DMACR0); +	PRINT_DCR(DMACT0); +	PRINT_DCR(DMADA0); +	PRINT_DCR(DMASA0); +	PRINT_DCR(DMASB0); + +	PRINT_DCR(DMACR1); +	PRINT_DCR(DMACT1); +	PRINT_DCR(DMADA1); +	PRINT_DCR(DMASA1); +	PRINT_DCR(DMASB1); + +	PRINT_DCR(DMACR2); +	PRINT_DCR(DMACT2); +	PRINT_DCR(DMADA2); +	PRINT_DCR(DMASA2); +	PRINT_DCR(DMASB2); + +	PRINT_DCR(DMACR3); +	PRINT_DCR(DMACT3); +	PRINT_DCR(DMADA3); +	PRINT_DCR(DMASA3); +	PRINT_DCR(DMASB3); +#endif + +	printf ("\n\nVarious HW-Configuration registers\n"); +#if defined (CONFIG_440EPX) +	PRINT_DCR(MAL0_CFG); +	PRINT_DCR(CPM0_ER); +	PRINT_DCR(CPM1_ER); +	PRINT_DCR(PLB4A0_ACR); +	PRINT_DCR(PLB4A1_ACR); +	PRINT_DCR(PLB3A0_ACR); +	PRINT_DCR(OPB2PLB40_BCTRL); +	PRINT_DCR(P4P3BO0_CFG); +#endif +	n = sizeof(ppc4xx_reg) / sizeof(ppc4xx_reg[0]); +	for (i = 0; i < n; i++) { +		value = 0; +		type = ppc4xx_reg[i].type; +		switch (type) { +		case IDCR1:	/* Indirect via SDRAM0_CFGADDR/DDR0_CFGDATA */ +			mtdcr(SDRAM0_CFGADDR, ppc4xx_reg[i].address); +			value = mfdcr(SDRAM0_CFGDATA); +			break; +		case IDCR2:	/* Indirect via EBC0_CFGADDR/EBC0_CFGDATA */ +			mtdcr(EBC0_CFGADDR, ppc4xx_reg[i].address); +			value = mfdcr(EBC0_CFGDATA); +			break; +		case IDCR5:	/* Indirect via CPR0_CFGADDR/CPR0_CFGDATA */ +			mtdcr(CPR0_CFGADDR, ppc4xx_reg[i].address); +			value = mfdcr(CPR0_CFGDATA); +			break; +		case IDCR6:	/* Indirect via SDR0_CFGADDR/SDR0_CFGDATA */ +			mtdcr(SDR0_CFGADDR, ppc4xx_reg[i].address); +			value = mfdcr(SDR0_CFGDATA); +			break; +		case MM:	/* Directly Accessed MMIO Register */ +			value = in_be32((const volatile unsigned __iomem *) +				ppc4xx_reg[i].address); +			break; +		default: +			printf("\nERROR: struct entry %d: unknown register" +				"type\n", i); +			break; +		} +		printf("0x%08x %-16s: 0x%08x\n",ppc4xx_reg[i].address, +			ppc4xx_reg[i].name, value); +	} +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/resetvec.S b/roms/u-boot/arch/powerpc/cpu/ppc4xx/resetvec.S new file mode 100644 index 00000000..b3308bd6 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/resetvec.S @@ -0,0 +1,12 @@ +/* Copyright MontaVista Software Incorporated, 2000 */ +#include <config.h> +	.section .resetvec,"ax" +#if defined(CONFIG_440) +	b _start_440 +#else +#if defined(CONFIG_BOOT_PCI) && defined(CONFIG_MIP405) +	b _start_pci +#else +	b _start +#endif +#endif diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/sdram.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/sdram.c new file mode 100644 index 00000000..d4ef36d3 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/sdram.c @@ -0,0 +1,452 @@ +/* + * (C) Copyright 2005-2007 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * (C) Copyright 2006 + * DAVE Srl <www.dave-tech.it> + * + * (C) Copyright 2002-2004 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/ppc4xx.h> +#include <asm/processor.h> +#include "sdram.h" +#include "ecc.h" + +#ifdef CONFIG_SDRAM_BANK0 + +#ifndef CONFIG_440 + +#ifndef CONFIG_SYS_SDRAM_TABLE +sdram_conf_t mb0cf[] = { +	{(128 << 20), 13, 0x000A4001},	    /* (0-128MB) Address Mode 3, 13x10(4) */ +	{(64 << 20),  13, 0x00084001},	    /* (0-64MB) Address Mode 3, 13x9(4)	  */ +	{(32 << 20),  12, 0x00062001},	    /* (0-32MB) Address Mode 2, 12x9(4)	  */ +	{(16 << 20),  12, 0x00046001},	    /* (0-16MB) Address Mode 4, 12x8(4)	  */ +	{(4 << 20),   11, 0x00008001},	    /* (0-4MB) Address Mode 5, 11x8(2)	  */ +}; +#else +sdram_conf_t mb0cf[] = CONFIG_SYS_SDRAM_TABLE; +#endif + +#define N_MB0CF (sizeof(mb0cf) / sizeof(mb0cf[0])) + +#ifdef CONFIG_SYS_SDRAM_CASL +static ulong ns2clks(ulong ns) +{ +	ulong bus_period_x_10 = ONE_BILLION / (get_bus_freq(0) / 10); + +	return ((ns * 10) + bus_period_x_10) / bus_period_x_10; +} +#endif /* CONFIG_SYS_SDRAM_CASL */ + +static ulong compute_sdtr1(ulong speed) +{ +#ifdef CONFIG_SYS_SDRAM_CASL +	ulong tmp; +	ulong sdtr1 = 0; + +	/* CASL */ +	if (CONFIG_SYS_SDRAM_CASL < 2) +		sdtr1 |= (1 << SDRAM0_TR_CASL); +	else +		if (CONFIG_SYS_SDRAM_CASL > 4) +			sdtr1 |= (3 << SDRAM0_TR_CASL); +		else +			sdtr1 |= ((CONFIG_SYS_SDRAM_CASL-1) << SDRAM0_TR_CASL); + +	/* PTA */ +	tmp = ns2clks(CONFIG_SYS_SDRAM_PTA); +	if ((tmp >= 2) && (tmp <= 4)) +		sdtr1 |= ((tmp-1) << SDRAM0_TR_PTA); +	else +		sdtr1 |= ((4-1) << SDRAM0_TR_PTA); + +	/* CTP */ +	tmp = ns2clks(CONFIG_SYS_SDRAM_CTP); +	if ((tmp >= 2) && (tmp <= 4)) +		sdtr1 |= ((tmp-1) << SDRAM0_TR_CTP); +	else +		sdtr1 |= ((4-1) << SDRAM0_TR_CTP); + +	/* LDF */ +	tmp = ns2clks(CONFIG_SYS_SDRAM_LDF); +	if ((tmp >= 2) && (tmp <= 4)) +		sdtr1 |= ((tmp-1) << SDRAM0_TR_LDF); +	else +		sdtr1 |= ((2-1) << SDRAM0_TR_LDF); + +	/* RFTA */ +	tmp = ns2clks(CONFIG_SYS_SDRAM_RFTA); +	if ((tmp >= 4) && (tmp <= 10)) +		sdtr1 |= ((tmp-4) << SDRAM0_TR_RFTA); +	else +		sdtr1 |= ((10-4) << SDRAM0_TR_RFTA); + +	/* RCD */ +	tmp = ns2clks(CONFIG_SYS_SDRAM_RCD); +	if ((tmp >= 2) && (tmp <= 4)) +		sdtr1 |= ((tmp-1) << SDRAM0_TR_RCD); +	else +		sdtr1 |= ((4-1) << SDRAM0_TR_RCD); + +	return sdtr1; +#else /* CONFIG_SYS_SDRAM_CASL */ +	/* +	 * If no values are configured in the board config file +	 * use the default values, which seem to be ok for most +	 * boards. +	 * +	 * REMARK: +	 * For new board ports we strongly recommend to define the +	 * correct values for the used SDRAM chips in your board +	 * config file (see PPChameleonEVB.h) +	 */ +	if (speed > 100000000) { +		/* +		 * 133 MHz SDRAM +		 */ +		return 0x01074015; +	} else { +		/* +		 * default: 100 MHz SDRAM +		 */ +		return 0x0086400d; +	} +#endif /* CONFIG_SYS_SDRAM_CASL */ +} + +/* refresh is expressed in ms */ +static ulong compute_rtr(ulong speed, ulong rows, ulong refresh) +{ +#ifdef CONFIG_SYS_SDRAM_CASL +	ulong tmp; + +	tmp = ((refresh*1000*1000) / (1 << rows)) * (speed / 1000); +	tmp /= 1000000; + +	return ((tmp & 0x00003FF8) << 16); +#else /* CONFIG_SYS_SDRAM_CASL */ +	if (speed > 100000000) { +		/* +		 * 133 MHz SDRAM +		 */ +		return 0x07f00000; +	} else { +		/* +		 * default: 100 MHz SDRAM +		 */ +		return 0x05f00000; +	} +#endif /* CONFIG_SYS_SDRAM_CASL */ +} + +/* + * Autodetect onboard SDRAM on 405 platforms + */ +phys_size_t initdram(int board_type) +{ +	ulong speed; +	ulong sdtr1; +	int i; + +	/* +	 * Determine SDRAM speed +	 */ +	speed = get_bus_freq(0); /* parameter not used on ppc4xx */ + +	/* +	 * sdtr1 (register SDRAM0_TR) must take into account timings listed +	 * in SDRAM chip datasheet. rtr (register SDRAM0_RTR) must take into +	 * account actual SDRAM size. So we can set up sdtr1 according to what +	 * is specified in board configuration file while rtr dependds on SDRAM +	 * size we are assuming before detection. +	 */ +	sdtr1 = compute_sdtr1(speed); + +	for (i=0; i<N_MB0CF; i++) { +		/* +		 * Disable memory controller. +		 */ +		mtsdram(SDRAM0_CFG, 0x00000000); + +		/* +		 * Set MB0CF for bank 0. +		 */ +		mtsdram(SDRAM0_B0CR, mb0cf[i].reg); +		mtsdram(SDRAM0_TR, sdtr1); +		mtsdram(SDRAM0_RTR, compute_rtr(speed, mb0cf[i].rows, 64)); + +		udelay(200); + +		/* +		 * Set memory controller options reg, MCOPT1. +		 * Set DC_EN to '1' and BRD_PRF to '01' for 16 byte PLB Burst +		 * read/prefetch. +		 */ +		mtsdram(SDRAM0_CFG, 0x80800000); + +		udelay(10000); + +		if (get_ram_size(0, mb0cf[i].size) == mb0cf[i].size) { +			phys_size_t size = mb0cf[i].size; + +			/* +			 * OK, size detected.  Enable second bank if +			 * defined (assumes same type as bank 0) +			 */ +#ifdef CONFIG_SDRAM_BANK1 +			mtsdram(SDRAM0_CFG, 0x00000000); +			mtsdram(SDRAM0_B1CR, mb0cf[i].size | mb0cf[i].reg); +			mtsdram(SDRAM0_CFG, 0x80800000); +			udelay(10000); + +			/* +			 * Check if 2nd bank is really available. +			 * If the size not equal to the size of the first +			 * bank, then disable the 2nd bank completely. +			 */ +			if (get_ram_size((long *)mb0cf[i].size, mb0cf[i].size) != +			    mb0cf[i].size) { +				mtsdram(SDRAM0_B1CR, 0); +				mtsdram(SDRAM0_CFG, 0); +			} else { +				/* +				 * We have two identical banks, so the size +				 * is twice the bank size +				 */ +				size = 2 * size; +			} +#endif + +			/* +			 * OK, size detected -> all done +			 */ +			return size; +		} +	} + +	return 0; +} + +#else /* CONFIG_440 */ + +/* + * Define some default values. Those can be overwritten in the + * board config file. + */ + +#ifndef CONFIG_SYS_SDRAM_TABLE +sdram_conf_t mb0cf[] = { +	{(256 << 20), 13, 0x000C4001},	/* 256MB mode 3, 13x10(4)	*/ +	{(128 << 20), 13, 0x000A4001},	/* 128MB mode 3, 13x10(4)	*/ +	{(64 << 20),  12, 0x00082001}	/* 64MB mode 2, 12x9(4)		*/ +}; +#else +sdram_conf_t mb0cf[] = CONFIG_SYS_SDRAM_TABLE; +#endif + +#ifndef CONFIG_SYS_SDRAM0_TR0 +#define	CONFIG_SYS_SDRAM0_TR0		0x41094012 +#endif + +#ifndef CONFIG_SYS_SDRAM0_WDDCTR +#define CONFIG_SYS_SDRAM0_WDDCTR	0x00000000  /* wrcp=0 dcd=0	*/ +#endif + +#ifndef CONFIG_SYS_SDRAM0_RTR +#define CONFIG_SYS_SDRAM0_RTR 		0x04100000 /* 7.8us @ 133MHz PLB */ +#endif + +#ifndef CONFIG_SYS_SDRAM0_CFG0 +#define CONFIG_SYS_SDRAM0_CFG0		0x82000000 /* DCEN=1, PMUD=0, 64-bit */ +#endif + +#define N_MB0CF (sizeof(mb0cf) / sizeof(mb0cf[0])) + +#define NUM_TRIES 64 +#define NUM_READS 10 + +static void sdram_tr1_set(int ram_address, int* tr1_value) +{ +	int i; +	int j, k; +	volatile unsigned int* ram_pointer = (unsigned int *)ram_address; +	int first_good = -1, last_bad = 0x1ff; + +	unsigned long test[NUM_TRIES] = { +		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, +		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, +		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, +		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, +		0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, +		0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, +		0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, +		0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, +		0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55 }; + +	/* go through all possible SDRAM0_TR1[RDCT] values */ +	for (i=0; i<=0x1ff; i++) { +		/* set the current value for TR1 */ +		mtsdram(SDRAM0_TR1, (0x80800800 | i)); + +		/* write values */ +		for (j=0; j<NUM_TRIES; j++) { +			ram_pointer[j] = test[j]; + +			/* clear any cache at ram location */ +			__asm__("dcbf 0,%0": :"r" (&ram_pointer[j])); +		} + +		/* read values back */ +		for (j=0; j<NUM_TRIES; j++) { +			for (k=0; k<NUM_READS; k++) { +				/* clear any cache at ram location */ +				__asm__("dcbf 0,%0": :"r" (&ram_pointer[j])); + +				if (ram_pointer[j] != test[j]) +					break; +			} + +			/* read error */ +			if (k != NUM_READS) +				break; +		} + +		/* we have a SDRAM0_TR1[RDCT] that is part of the window */ +		if (j == NUM_TRIES) { +			if (first_good == -1) +				first_good = i;		/* found beginning of window */ +		} else { /* bad read */ +			/* if we have not had a good read then don't care */ +			if (first_good != -1) { +				/* first failure after a good read */ +				last_bad = i-1; +				break; +			} +		} +	} + +	/* return the current value for TR1 */ +	*tr1_value = (first_good + last_bad) / 2; +} + +/* + * Autodetect onboard DDR SDRAM on 440 platforms + * + * NOTE: Some of the hardcoded values are hardware dependant, + *	 so this should be extended for other future boards + *	 using this routine! + */ +phys_size_t initdram(int board_type) +{ +	int i; +	int tr1_bank1; + +#if defined(CONFIG_440GX) || defined(CONFIG_440EP) || \ +    defined(CONFIG_440GR) || defined(CONFIG_440SP) +	/* +	 * Soft-reset SDRAM controller. +	 */ +	mtsdr(SDR0_SRST, SDR0_SRST_DMC); +	mtsdr(SDR0_SRST, 0x00000000); +#endif + +	for (i=0; i<N_MB0CF; i++) { +		/* +		 * Disable memory controller. +		 */ +		mtsdram(SDRAM0_CFG0, 0x00000000); + +		/* +		 * Setup some default +		 */ +		mtsdram(SDRAM0_UABBA, 0x00000000); /* ubba=0 (default)		*/ +		mtsdram(SDRAM0_SLIO, 0x00000000);	/* rdre=0 wrre=0 rarw=0		*/ +		mtsdram(SDRAM0_DEVOPT, 0x00000000); /* dll=0 ds=0 (normal)		*/ +		mtsdram(SDRAM0_WDDCTR, CONFIG_SYS_SDRAM0_WDDCTR); +		mtsdram(SDRAM0_CLKTR, 0x40000000); /* clkp=1 (90 deg wr) dcdt=0	*/ + +		/* +		 * Following for CAS Latency = 2.5 @ 133 MHz PLB +		 */ +		mtsdram(SDRAM0_B0CR, mb0cf[i].reg); +		mtsdram(SDRAM0_TR0, CONFIG_SYS_SDRAM0_TR0); +		mtsdram(SDRAM0_TR1, 0x80800800);	/* SS=T2 SL=STAGE 3 CD=1 CT=0x00*/ +		mtsdram(SDRAM0_RTR, CONFIG_SYS_SDRAM0_RTR); +		mtsdram(SDRAM0_CFG1, 0x00000000);	/* Self-refresh exit, disable PM*/ +		udelay(400);			/* Delay 200 usecs (min)	*/ + +		/* +		 * Enable the controller, then wait for DCEN to complete +		 */ +		mtsdram(SDRAM0_CFG0, CONFIG_SYS_SDRAM0_CFG0); +		udelay(10000); + +		if (get_ram_size(0, mb0cf[i].size) == mb0cf[i].size) { +			phys_size_t size = mb0cf[i].size; +			/* +			 * Optimize TR1 to current hardware environment +			 */ +			sdram_tr1_set(0x00000000, &tr1_bank1); +			mtsdram(SDRAM0_TR1, (tr1_bank1 | 0x80800800)); + + +			/* +			 * OK, size detected.  Enable second bank if +			 * defined (assumes same type as bank 0) +			 */ +#ifdef CONFIG_SDRAM_BANK1 +			mtsdram(SDRAM0_CFG0, 0); +			mtsdram(SDRAM0_B1CR, mb0cf[i].size | mb0cf[i].reg); +			mtsdram(SDRAM0_CFG0, CONFIG_SYS_SDRAM0_CFG0); +			udelay(10000); + +			/* +			 * Check if 2nd bank is really available. +			 * If the size not equal to the size of the first +			 * bank, then disable the 2nd bank completely. +			 */ +			if (get_ram_size((long *)mb0cf[i].size, mb0cf[i].size) +			    != mb0cf[i].size) { +				mtsdram(SDRAM0_CFG0, 0); +				mtsdram(SDRAM0_B1CR, 0); +				mtsdram(SDRAM0_CFG0, CONFIG_SYS_SDRAM0_CFG0); +				udelay(10000); +			} else { +				/* +				 * We have two identical banks, so the size +				 * is twice the bank size +				 */ +				size = 2 * size; +			} +#endif + +#ifdef CONFIG_SDRAM_ECC +			ecc_init(0, size); +#endif + +			/* +			 * OK, size detected -> all done +			 */ +			return size; +		} +	} + +	return 0;				/* nothing found !		*/ +} + +#endif /* CONFIG_440 */ + +#endif /* CONFIG_SDRAM_BANK0 */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/sdram.h b/roms/u-boot/arch/powerpc/cpu/ppc4xx/sdram.h new file mode 100644 index 00000000..1e249f40 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/sdram.h @@ -0,0 +1,60 @@ +/* + * (C) Copyright 2006 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * (C) Copyright 2006 + * DAVE Srl <www.dave-tech.it> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifndef _SDRAM_H_ +#define _SDRAM_H_ + +#include <config.h> + +#define ONE_BILLION	1000000000 + +struct sdram_conf_s { +	unsigned long size; +	int rows; +	unsigned long reg; +}; + +typedef struct sdram_conf_s sdram_conf_t; + +/* Bitfields offsets */ +#define SDRAM0_TR_CASL		(31 - 8) +#define SDRAM0_TR_PTA		(31 - 13) +#define SDRAM0_TR_CTP		(31 - 15) +#define SDRAM0_TR_LDF		(31 - 17) +#define SDRAM0_TR_RFTA		(31 - 29) +#define SDRAM0_TR_RCD		(31 - 31) + +#ifdef CONFIG_SYS_SDRAM_CL +/* SDRAM timings [ns] according to AMCC/IBM names (see SDRAM_faq.doc) */ +#define CONFIG_SYS_SDRAM_CASL		CONFIG_SYS_SDRAM_CL +#define CONFIG_SYS_SDRAM_PTA		CONFIG_SYS_SDRAM_tRP +#define CONFIG_SYS_SDRAM_CTP		(CONFIG_SYS_SDRAM_tRC - CONFIG_SYS_SDRAM_tRCD - CONFIG_SYS_SDRAM_tRP) +#define CONFIG_SYS_SDRAM_LDF		0 +#ifdef CONFIG_SYS_SDRAM_tRFC +#define CONFIG_SYS_SDRAM_RFTA		CONFIG_SYS_SDRAM_tRFC +#else +#define CONFIG_SYS_SDRAM_RFTA		CONFIG_SYS_SDRAM_tRC +#endif +#define CONFIG_SYS_SDRAM_RCD		CONFIG_SYS_SDRAM_tRCD +#endif /* #ifdef CONFIG_SYS_SDRAM_CL */ + +/* + * Some defines for the 440 DDR controller + */ +#define SDRAM_CFG0_DC_EN	0x80000000	/* SDRAM Controller Enable	*/ +#define SDRAM_CFG0_MEMCHK	0x30000000	/* Memory data error checking mask*/ +#define SDRAM_CFG0_MEMCHK_NON	0x00000000	/* No ECC generation		*/ +#define SDRAM_CFG0_MEMCHK_GEN	0x20000000	/* ECC generation		*/ +#define SDRAM_CFG0_MEMCHK_CHK	0x30000000	/* ECC generation and checking	*/ +#define SDRAM_CFG0_DRAMWDTH	0x02000000	/* DRAM width mask		*/ +#define SDRAM_CFG0_DRAMWDTH_32	0x00000000	/* 32 bits			*/ +#define SDRAM_CFG0_DRAMWDTH_64	0x02000000	/* 64 bits			*/ + +#endif diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/speed.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/speed.c new file mode 100644 index 00000000..7e077d5a --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/speed.c @@ -0,0 +1,1209 @@ +/* + * (C) Copyright 2000-2008 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <ppc_asm.tmpl> +#include <asm/ppc4xx.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define ONE_BILLION        1000000000 +#ifdef DEBUG +#define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGF(fmt,args...) +#endif + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#if defined(CONFIG_405GP) + +void get_sys_info (PPC4xx_SYS_INFO * sysInfo) +{ +	unsigned long pllmr; +	unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ / 1000); +	uint pvr = get_pvr(); +	unsigned long psr; +	unsigned long m; + +	/* +	 * Read PLL Mode register +	 */ +	pllmr = mfdcr (CPC0_PLLMR); + +	/* +	 * Read Pin Strapping register +	 */ +	psr = mfdcr (CPC0_PSR); + +	/* +	 * Determine FWD_DIV. +	 */ +	sysInfo->pllFwdDiv = 8 - ((pllmr & PLLMR_FWD_DIV_MASK) >> 29); + +	/* +	 * Determine FBK_DIV. +	 */ +	sysInfo->pllFbkDiv = ((pllmr & PLLMR_FB_DIV_MASK) >> 25); +	if (sysInfo->pllFbkDiv == 0) { +		sysInfo->pllFbkDiv = 16; +	} + +	/* +	 * Determine PLB_DIV. +	 */ +	sysInfo->pllPlbDiv = ((pllmr & PLLMR_CPU_TO_PLB_MASK) >> 17) + 1; + +	/* +	 * Determine PCI_DIV. +	 */ +	sysInfo->pllPciDiv = ((pllmr & PLLMR_PCI_TO_PLB_MASK) >> 13) + 1; + +	/* +	 * Determine EXTBUS_DIV. +	 */ +	sysInfo->pllExtBusDiv = ((pllmr & PLLMR_EXB_TO_PLB_MASK) >> 11) + 2; + +	/* +	 * Determine OPB_DIV. +	 */ +	sysInfo->pllOpbDiv = ((pllmr & PLLMR_OPB_TO_PLB_MASK) >> 15) + 1; + +	/* +	 * Check if PPC405GPr used (mask minor revision field) +	 */ +	if ((pvr & 0xfffffff0) == (PVR_405GPR_RB & 0xfffffff0)) { +		/* +		 * Determine FWD_DIV B (only PPC405GPr with new mode strapping). +		 */ +		sysInfo->pllFwdDivB = 8 - (pllmr & PLLMR_FWDB_DIV_MASK); + +		/* +		 * Determine factor m depending on PLL feedback clock source +		 */ +		if (!(psr & PSR_PCI_ASYNC_EN)) { +			if (psr & PSR_NEW_MODE_EN) { +				/* +				 * sync pci clock used as feedback (new mode) +				 */ +				m = 1 * sysInfo->pllFwdDivB * 2 * sysInfo->pllPciDiv; +			} else { +				/* +				 * sync pci clock used as feedback (legacy mode) +				 */ +				m = 1 * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv * sysInfo->pllPciDiv; +			} +		} else if (psr & PSR_NEW_MODE_EN) { +			if (psr & PSR_PERCLK_SYNC_MODE_EN) { +				/* +				 * PerClk used as feedback (new mode) +				 */ +				m = 1 * sysInfo->pllFwdDivB * 2 * sysInfo->pllExtBusDiv; +			} else { +				/* +				 * CPU clock used as feedback (new mode) +				 */ +				m = sysInfo->pllFbkDiv * sysInfo->pllFwdDiv; +			} +		} else if (sysInfo->pllExtBusDiv == sysInfo->pllFbkDiv) { +			/* +			 * PerClk used as feedback (legacy mode) +			 */ +			m = 1 * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv * sysInfo->pllExtBusDiv; +		} else { +			/* +			 * PLB clock used as feedback (legacy mode) +			 */ +			m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivB * sysInfo->pllPlbDiv; +		} + +		sysInfo->freqVCOHz = (1000000000000LL * (unsigned long long)m) / +			(unsigned long long)sysClkPeriodPs; +		sysInfo->freqProcessor = sysInfo->freqVCOHz / sysInfo->pllFwdDiv; +		sysInfo->freqPLB = sysInfo->freqVCOHz / (sysInfo->pllFwdDivB * sysInfo->pllPlbDiv); +	} else { +		/* +		 * Check pllFwdDiv to see if running in bypass mode where the CPU speed +		 * is equal to the 405GP SYS_CLK_FREQ. If not in bypass mode, check VCO +		 * to make sure it is within the proper range. +		 *    spec:    VCO = SYS_CLOCK x FBKDIV x PLBDIV x FWDDIV +		 * Note freqVCO is calculated in MHz to avoid errors introduced by rounding. +		 */ +		if (sysInfo->pllFwdDiv == 1) { +			sysInfo->freqProcessor = CONFIG_SYS_CLK_FREQ; +			sysInfo->freqPLB = CONFIG_SYS_CLK_FREQ / sysInfo->pllPlbDiv; +		} else { +			sysInfo->freqVCOHz = ( 1000000000000LL * +					       (unsigned long long)sysInfo->pllFwdDiv * +					       (unsigned long long)sysInfo->pllFbkDiv * +					       (unsigned long long)sysInfo->pllPlbDiv +				) / (unsigned long long)sysClkPeriodPs; +			sysInfo->freqPLB = (ONE_BILLION / ((sysClkPeriodPs * 10) / +							   sysInfo->pllFbkDiv)) * 10000; +			sysInfo->freqProcessor = sysInfo->freqPLB * sysInfo->pllPlbDiv; +		} +	} + +	sysInfo->freqOPB = sysInfo->freqPLB / sysInfo->pllOpbDiv; +	sysInfo->freqEBC = sysInfo->freqPLB / sysInfo->pllExtBusDiv; +	sysInfo->freqUART = sysInfo->freqProcessor; +} + + +/******************************************** + * get_PCI_freq + * return PCI bus freq in Hz + *********************************************/ +ulong get_PCI_freq (void) +{ +	ulong val; +	PPC4xx_SYS_INFO sys_info; + +	get_sys_info (&sys_info); +	val = sys_info.freqPLB / sys_info.pllPciDiv; +	return val; +} + + +#elif defined(CONFIG_440) + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ +    defined(CONFIG_460SX) || defined(CONFIG_APM821XX) +static u8 pll_fwdv_multi_bits[] = { +	/* values for:  1 - 16 */ +	0x00, 0x01, 0x0f, 0x04, 0x09, 0x0a, 0x0d, 0x0e, 0x03, 0x0c, +	0x05, 0x08, 0x07, 0x02, 0x0b, 0x06 +}; + +u32 get_cpr0_fwdv(unsigned long cpr_reg_fwdv) +{ +	u32 index; + +	for (index = 0; index < ARRAY_SIZE(pll_fwdv_multi_bits); index++) +		if (cpr_reg_fwdv == (u32)pll_fwdv_multi_bits[index]) +			return index + 1; + +	return 0; +} + +static u8 pll_fbdv_multi_bits[] = { +	/* values for:  1 - 100 */ +	0x00, 0xff, 0x7e, 0xfd, 0x7a, 0xf5, 0x6a, 0xd5, 0x2a, 0xd4, +	0x29, 0xd3, 0x26, 0xcc, 0x19, 0xb3, 0x67, 0xce, 0x1d, 0xbb, +	0x77, 0xee, 0x5d, 0xba, 0x74, 0xe9, 0x52, 0xa5, 0x4b, 0x96, +	0x2c, 0xd8, 0x31, 0xe3, 0x46, 0x8d, 0x1b, 0xb7, 0x6f, 0xde, +	0x3d, 0xfb, 0x76, 0xed, 0x5a, 0xb5, 0x6b, 0xd6, 0x2d, 0xdb, +	0x36, 0xec, 0x59, 0xb2, 0x64, 0xc9, 0x12, 0xa4, 0x48, 0x91, +	0x23, 0xc7, 0x0e, 0x9c, 0x38, 0xf0, 0x61, 0xc2, 0x05, 0x8b, +	0x17, 0xaf, 0x5f, 0xbe, 0x7c, 0xf9, 0x72, 0xe5, 0x4a, 0x95, +	0x2b, 0xd7, 0x2e, 0xdc, 0x39, 0xf3, 0x66, 0xcd, 0x1a, 0xb4, +	0x68, 0xd1, 0x22, 0xc4, 0x09, 0x93, 0x27, 0xcf, 0x1e, 0xbc, +	/* values for:  101 - 200 */ +	0x78, 0xf1, 0x62, 0xc5, 0x0a, 0x94, 0x28, 0xd0, 0x21, 0xc3, +	0x06, 0x8c, 0x18, 0xb0, 0x60, 0xc1, 0x02, 0x84, 0x08, 0x90, +	0x20, 0xc0, 0x01, 0x83, 0x07, 0x8f, 0x1f, 0xbf, 0x7f, 0xfe, +	0x7d, 0xfa, 0x75, 0xea, 0x55, 0xaa, 0x54, 0xa9, 0x53, 0xa6, +	0x4c, 0x99, 0x33, 0xe7, 0x4e, 0x9d, 0x3b, 0xf7, 0x6e, 0xdd, +	0x3a, 0xf4, 0x69, 0xd2, 0x25, 0xcb, 0x16, 0xac, 0x58, 0xb1, +	0x63, 0xc6, 0x0d, 0x9b, 0x37, 0xef, 0x5e, 0xbd, 0x7b, 0xf6, +	0x6d, 0xda, 0x35, 0xeb, 0x56, 0xad, 0x5b, 0xb6, 0x6c, 0xd9, +	0x32, 0xe4, 0x49, 0x92, 0x24, 0xc8, 0x11, 0xa3, 0x47, 0x8e, +	0x1c, 0xb8, 0x70, 0xe1, 0x42, 0x85, 0x0b, 0x97, 0x2f, 0xdf, +	/* values for:  201 - 255 */ +	0x3e, 0xfc, 0x79, 0xf2, 0x65, 0xca, 0x15, 0xab, 0x57, 0xae, +	0x5c, 0xb9, 0x73, 0xe6, 0x4d, 0x9a, 0x34, 0xe8, 0x51, 0xa2, +	0x44, 0x89, 0x13, 0xa7, 0x4f, 0x9e, 0x3c, 0xf8, 0x71, 0xe2, +	0x45, 0x8a, 0x14, 0xa8, 0x50, 0xa1, 0x43, 0x86, 0x0c, 0x98, +	0x30, 0xe0, 0x41, 0x82, 0x04, 0x88, 0x10, 0xa0, 0x40, 0x81, +	0x03, 0x87, 0x0f, 0x9f, 0x3f  /* END */ +}; + +u32 get_cpr0_fbdv(unsigned long cpr_reg_fbdv) +{ +	u32 index; + +	for (index = 0; index < ARRAY_SIZE(pll_fbdv_multi_bits); index++) +		if (cpr_reg_fbdv == (u32)pll_fbdv_multi_bits[index]) +			return index + 1; + +	return 0; +} + +#if defined(CONFIG_APM821XX) + +void get_sys_info(sys_info_t *sysInfo) +{ +	unsigned long plld; +	unsigned long temp; +	unsigned long mul; +	unsigned long cpudv; +	unsigned long plb2dv; +	unsigned long ddr2dv; + +	/* Calculate Forward divisor A and Feeback divisor */ +	mfcpr(CPR0_PLLD, plld); + +	temp = CPR0_PLLD_FWDVA(plld); +	sysInfo->pllFwdDivA = get_cpr0_fwdv(temp); + +	temp = CPR0_PLLD_FDV(plld); +	sysInfo->pllFbkDiv = get_cpr0_fbdv(temp); + +	/* Calculate OPB clock divisor */ +	mfcpr(CPR0_OPBD, temp); +	temp = CPR0_OPBD_OPBDV(temp); +	sysInfo->pllOpbDiv = temp ? temp : 4; + +	/* Calculate Peripheral clock divisor */ +	mfcpr(CPR0_PERD, temp); +	temp = CPR0_PERD_PERDV(temp); +	sysInfo->pllExtBusDiv = temp ? temp : 4; + +	/* Calculate CPU clock divisor */ +	mfcpr(CPR0_CPUD, temp); +	temp = CPR0_CPUD_CPUDV(temp); +	cpudv = temp ? temp : 8; + +	/* Calculate PLB2 clock divisor */ +	mfcpr(CPR0_PLB2D, temp); +	temp = CPR0_PLB2D_PLB2DV(temp); +	plb2dv = temp ? temp : 4; + +	/* Calculate DDR2 clock divisor */ +	mfcpr(CPR0_DDR2D, temp); +	temp = CPR0_DDR2D_DDR2DV(temp); +	ddr2dv = temp ? temp : 4; + +	/* Calculate 'M' based on feedback source */ +	mfcpr(CPR0_PLLC, temp); +	temp = CPR0_PLLC_SEL(temp); +	if (temp == 0) { +		/* PLL internal feedback */ +		mul = sysInfo->pllFbkDiv; +	} else { +		/* PLL PerClk feedback */ +		mul = sysInfo->pllFwdDivA * sysInfo->pllFbkDiv * cpudv +			* plb2dv * 2 * sysInfo->pllOpbDiv * +			  sysInfo->pllExtBusDiv; +	} + +	/* Now calculate the individual clocks */ +	sysInfo->freqVCOMhz = (mul * CONFIG_SYS_CLK_FREQ) + (mul >> 1); +	sysInfo->freqProcessor = sysInfo->freqVCOMhz / +		sysInfo->pllFwdDivA / cpudv; +	sysInfo->freqPLB = sysInfo->freqVCOMhz / +		sysInfo->pllFwdDivA / cpudv / plb2dv / 2; +	sysInfo->freqOPB = sysInfo->freqPLB / sysInfo->pllOpbDiv; +	sysInfo->freqEBC = sysInfo->freqOPB / sysInfo->pllExtBusDiv; +	sysInfo->freqDDR = sysInfo->freqVCOMhz / +		sysInfo->pllFwdDivA / cpudv / ddr2dv / 2; +	sysInfo->freqUART = sysInfo->freqPLB; +} + +#else +/* + * AMCC_TODO: verify this routine against latest EAS, cause stuff changed + *            with latest EAS + */ +void get_sys_info (sys_info_t * sysInfo) +{ +	unsigned long strp0; +	unsigned long strp1; +	unsigned long temp; +	unsigned long m; +	unsigned long plbedv0; + +	/* Extract configured divisors */ +	mfsdr(SDR0_SDSTP0, strp0); +	mfsdr(SDR0_SDSTP1, strp1); + +	temp = ((strp0 & PLLSYS0_FWD_DIV_A_MASK) >> 4); +	sysInfo->pllFwdDivA = get_cpr0_fwdv(temp); + +	temp = (strp0 & PLLSYS0_FWD_DIV_B_MASK); +	sysInfo->pllFwdDivB = get_cpr0_fwdv(temp); + +	temp = (strp0 & PLLSYS0_FB_DIV_MASK) >> 8; +	sysInfo->pllFbkDiv = get_cpr0_fbdv(temp); + +	temp = (strp1 & PLLSYS0_OPB_DIV_MASK) >> 26; +	sysInfo->pllOpbDiv = temp ? temp : 4; + +	/* AMCC_TODO: verify the SDR0_SDSTP1.PERDV0 value sysInfo->pllExtBusDiv */ +	temp = (strp1 & PLLSYS0_PERCLK_DIV_MASK) >> 24; +	sysInfo->pllExtBusDiv = temp ? temp : 4; + +	temp = (strp1 & PLLSYS0_PLBEDV0_DIV_MASK) >> 29; +	plbedv0 = temp ? temp: 8; + +	/* Calculate 'M' based on feedback source */ +	temp = (strp0 & PLLSYS0_SEL_MASK) >> 27; +	if (temp == 0) { +		/* PLL internal feedback */ +		m = sysInfo->pllFbkDiv; +	} else { +		/* PLL PerClk feedback */ +		m = sysInfo->pllFwdDivA * plbedv0 * sysInfo->pllOpbDiv * +			sysInfo->pllExtBusDiv; +	} + +	/* Now calculate the individual clocks */ +	sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m >> 1); +	sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; +	sysInfo->freqPLB = sysInfo->freqVCOMhz / sysInfo->pllFwdDivA / plbedv0; +	sysInfo->freqOPB = sysInfo->freqPLB / sysInfo->pllOpbDiv; +	sysInfo->freqEBC = sysInfo->freqOPB / sysInfo->pllExtBusDiv; +	sysInfo->freqDDR = sysInfo->freqPLB; +	sysInfo->freqUART = sysInfo->freqPLB; + +	return; +} +#endif + +#elif defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ +    defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +void get_sys_info (sys_info_t *sysInfo) +{ +	unsigned long temp; +	unsigned long reg; +	unsigned long lfdiv; +	unsigned long m; +	unsigned long prbdv0; +	/* +	  WARNING: ASSUMES the following: +	  ENG=1 +	  PRADV0=1 +	  PRBDV0=1 +	*/ + +	/* Decode CPR0_PLLD0 for divisors */ +	mfcpr(CPR0_PLLD, reg); +	temp = (reg & PLLD_FWDVA_MASK) >> 16; +	sysInfo->pllFwdDivA = temp ? temp : 16; +	temp = (reg & PLLD_FWDVB_MASK) >> 8; +	sysInfo->pllFwdDivB = temp ? temp: 8 ; +	temp = (reg & PLLD_FBDV_MASK) >> 24; +	sysInfo->pllFbkDiv = temp ? temp : 32; +	lfdiv = reg & PLLD_LFBDV_MASK; + +	mfcpr(CPR0_OPBD0, reg); +	temp = (reg & OPBDDV_MASK) >> 24; +	sysInfo->pllOpbDiv = temp ? temp : 4; + +	mfcpr(CPR0_PERD, reg); +	temp = (reg & PERDV_MASK) >> 24; +	sysInfo->pllExtBusDiv = temp ? temp : 8; + +	mfcpr(CPR0_PRIMBD0, reg); +	temp = (reg & PRBDV_MASK) >> 24; +	prbdv0 = temp ? temp : 8; + +	mfcpr(CPR0_SPCID, reg); +	temp = (reg & SPCID_MASK) >> 24; +	sysInfo->pllPciDiv = temp ? temp : 4; + +	/* Calculate 'M' based on feedback source */ +	mfsdr(SDR0_SDSTP0, reg); +	temp = (reg & PLLSYS0_SEL_MASK) >> 27; +	if (temp == 0) { /* PLL output */ +		/* Figure which pll to use */ +		mfcpr(CPR0_PLLC, reg); +		temp = (reg & PLLC_SRC_MASK) >> 29; +		if (!temp) /* PLLOUTA */ +			m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivA; +		else       /* PLLOUTB */ +			m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivB; +	} +	else if (temp == 1) /* CPU output */ +		m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; +	else /* PerClk */ +		m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; + +	/* Now calculate the individual clocks */ +	sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m>>1); +	sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; +	sysInfo->freqPLB = sysInfo->freqVCOMhz/sysInfo->pllFwdDivB/prbdv0; +	sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; +	sysInfo->freqEBC = sysInfo->freqPLB/sysInfo->pllExtBusDiv; +	sysInfo->freqPCI = sysInfo->freqPLB/sysInfo->pllPciDiv; +	sysInfo->freqUART = sysInfo->freqPLB; + +	/* Figure which timer source to use */ +	if (mfspr(SPRN_CCR1) & 0x0080) { +		/* External Clock, assume same as SYS_CLK */ +		temp = sysInfo->freqProcessor / 2;  /* Max extern clock speed */ +		if (CONFIG_SYS_CLK_FREQ > temp) +			sysInfo->freqTmrClk = temp; +		else +			sysInfo->freqTmrClk = CONFIG_SYS_CLK_FREQ; +	} +	else  /* Internal clock */ +		sysInfo->freqTmrClk = sysInfo->freqProcessor; +} + +/******************************************** + * get_PCI_freq + * return PCI bus freq in Hz + *********************************************/ +ulong get_PCI_freq (void) +{ +	sys_info_t sys_info; +	get_sys_info (&sys_info); +	return sys_info.freqPCI; +} + +#elif !defined(CONFIG_440GX) && !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) \ +	&& !defined(CONFIG_XILINX_440) +void get_sys_info (sys_info_t * sysInfo) +{ +	unsigned long strp0; +	unsigned long temp; +	unsigned long m; + +	/* Extract configured divisors */ +	strp0 = mfdcr( CPC0_STRP0 ); +	sysInfo->pllFwdDivA = 8 - ((strp0 & PLLSYS0_FWD_DIV_A_MASK) >> 15); +	sysInfo->pllFwdDivB = 8 - ((strp0 & PLLSYS0_FWD_DIV_B_MASK) >> 12); +	temp = (strp0 & PLLSYS0_FB_DIV_MASK) >> 18; +	sysInfo->pllFbkDiv = temp ? temp : 16; +	sysInfo->pllOpbDiv = 1 + ((strp0 & PLLSYS0_OPB_DIV_MASK) >> 10); +	sysInfo->pllExtBusDiv = 1 + ((strp0 & PLLSYS0_EPB_DIV_MASK) >> 8); + +	/* Calculate 'M' based on feedback source */ +	if( strp0 & PLLSYS0_EXTSL_MASK ) +		m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; +	else +		m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; + +	/* Now calculate the individual clocks */ +	sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m>>1); +	sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; +	sysInfo->freqPLB = sysInfo->freqVCOMhz/sysInfo->pllFwdDivB; +	if( get_pvr() == PVR_440GP_RB ) /* Rev B divs an extra 2 -- geez! */ +		sysInfo->freqPLB >>= 1; +	sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; +	sysInfo->freqEBC = sysInfo->freqOPB/sysInfo->pllExtBusDiv; +	sysInfo->freqUART = sysInfo->freqPLB; +} +#else + +#if !defined(CONFIG_XILINX_440) +void get_sys_info (sys_info_t * sysInfo) +{ +	unsigned long strp0; +	unsigned long strp1; +	unsigned long temp; +	unsigned long temp1; +	unsigned long lfdiv; +	unsigned long m; +	unsigned long prbdv0; + +#if defined(CONFIG_YUCCA) +	unsigned long sys_freq; +	unsigned long sys_per=0; +	unsigned long msr; +	unsigned long pci_clock_per; +	unsigned long sdr_ddrpll; + +	/*-------------------------------------------------------------------------+ +	 | Get the system clock period. +	 +-------------------------------------------------------------------------*/ +	sys_per = determine_sysper(); + +	msr = (mfmsr () & ~(MSR_EE));	/* disable interrupts */ + +	/*-------------------------------------------------------------------------+ +	 | Calculate the system clock speed from the period. +	 +-------------------------------------------------------------------------*/ +	sys_freq = (ONE_BILLION / sys_per) * 1000; +#endif + +	/* Extract configured divisors */ +	mfsdr( SDR0_SDSTP0,strp0 ); +	mfsdr( SDR0_SDSTP1,strp1 ); + +	temp = ((strp0 & PLLSYS0_FWD_DIV_A_MASK) >> 8); +	sysInfo->pllFwdDivA = temp ? temp : 16 ; +	temp = ((strp0 & PLLSYS0_FWD_DIV_B_MASK) >> 5); +	sysInfo->pllFwdDivB = temp ? temp: 8 ; +	temp = (strp0 & PLLSYS0_FB_DIV_MASK) >> 12; +	sysInfo->pllFbkDiv = temp ? temp : 32; +	temp = (strp0 & PLLSYS0_OPB_DIV_MASK); +	sysInfo->pllOpbDiv = temp ? temp : 4; +	temp = (strp1 & PLLSYS1_PERCLK_DIV_MASK) >> 24; +	sysInfo->pllExtBusDiv = temp ? temp : 4; +	prbdv0 = (strp0 >> 2) & 0x7; + +	/* Calculate 'M' based on feedback source */ +	temp = (strp0 & PLLSYS0_SEL_MASK) >> 27; +	temp1 = (strp1 & PLLSYS1_LF_DIV_MASK) >> 26; +	lfdiv = temp1 ? temp1 : 64; +	if (temp == 0) { /* PLL output */ +		/* Figure which pll to use */ +		temp = (strp0 & PLLSYS0_SRC_MASK) >> 30; +		if (!temp) +			m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivA; +		else +			m = sysInfo->pllFbkDiv * lfdiv * sysInfo->pllFwdDivB; +	} +	else if (temp == 1) /* CPU output */ +		m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivA; +	else /* PerClk */ +		m = sysInfo->pllExtBusDiv * sysInfo->pllOpbDiv * sysInfo->pllFwdDivB; + +	/* Now calculate the individual clocks */ +#if defined(CONFIG_YUCCA) +	sysInfo->freqVCOMhz = (m * sys_freq) ; +#else +	sysInfo->freqVCOMhz = (m * CONFIG_SYS_CLK_FREQ) + (m >> 1); +#endif +	sysInfo->freqProcessor = sysInfo->freqVCOMhz/sysInfo->pllFwdDivA; +	sysInfo->freqPLB = sysInfo->freqVCOMhz/sysInfo->pllFwdDivB/prbdv0; +	sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; +	sysInfo->freqEBC = sysInfo->freqOPB/sysInfo->pllExtBusDiv; + +#if defined(CONFIG_YUCCA) +	/* Determine PCI Clock Period */ +	pci_clock_per = determine_pci_clock_per(); +	sysInfo->freqPCI = (ONE_BILLION/pci_clock_per) * 1000; +	mfsdr(SDR0_DDR0, sdr_ddrpll); +	sysInfo->freqDDR = ((sysInfo->freqPLB) * SDR0_DDR0_DDRM_DECODE(sdr_ddrpll)); +#endif + +	sysInfo->freqUART = sysInfo->freqPLB; +} + +#endif +#endif /* CONFIG_XILINX_440 */ + +#if defined(CONFIG_YUCCA) +unsigned long determine_sysper(void) +{ +	unsigned int fpga_clocking_reg; +	unsigned int master_clock_selection; +	unsigned long master_clock_per = 0; +	unsigned long fb_div_selection; +	unsigned int vco_div_reg_value; +	unsigned long vco_div_selection; +	unsigned long sys_per = 0; +	int extClkVal; + +	/*-------------------------------------------------------------------------+ +	 | Read FPGA reg 0 and reg 1 to get FPGA reg information +	 +-------------------------------------------------------------------------*/ +	fpga_clocking_reg = in16(FPGA_REG16); + + +	/* Determine Master Clock Source Selection */ +	master_clock_selection = fpga_clocking_reg & FPGA_REG16_MASTER_CLK_MASK; + +	switch(master_clock_selection) { +		case FPGA_REG16_MASTER_CLK_66_66: +			master_clock_per = PERIOD_66_66MHZ; +			break; +		case FPGA_REG16_MASTER_CLK_50: +			master_clock_per = PERIOD_50_00MHZ; +			break; +		case FPGA_REG16_MASTER_CLK_33_33: +			master_clock_per = PERIOD_33_33MHZ; +			break; +		case FPGA_REG16_MASTER_CLK_25: +			master_clock_per = PERIOD_25_00MHZ; +			break; +		case FPGA_REG16_MASTER_CLK_EXT: +			if ((extClkVal==EXTCLK_33_33) +					&& (extClkVal==EXTCLK_50) +					&& (extClkVal==EXTCLK_66_66) +					&& (extClkVal==EXTCLK_83)) { +				/* calculate master clock period from external clock value */ +				master_clock_per=(ONE_BILLION/extClkVal) * 1000; +			} else { +				/* Unsupported */ +				DEBUGF ("%s[%d] *** master clock selection failed ***\n", __FUNCTION__,__LINE__); +				hang(); +			} +			break; +		default: +			/* Unsupported */ +			DEBUGF ("%s[%d] *** master clock selection failed ***\n", __FUNCTION__,__LINE__); +			hang(); +			break; +	} + +	/* Determine FB divisors values */ +	if ((fpga_clocking_reg & FPGA_REG16_FB1_DIV_MASK) == FPGA_REG16_FB1_DIV_LOW) { +		if ((fpga_clocking_reg & FPGA_REG16_FB2_DIV_MASK) == FPGA_REG16_FB2_DIV_LOW) +			fb_div_selection = FPGA_FB_DIV_6; +		else +			fb_div_selection = FPGA_FB_DIV_12; +	} else { +		if ((fpga_clocking_reg & FPGA_REG16_FB2_DIV_MASK) == FPGA_REG16_FB2_DIV_LOW) +			fb_div_selection = FPGA_FB_DIV_10; +		else +			fb_div_selection = FPGA_FB_DIV_20; +	} + +	/* Determine VCO divisors values */ +	vco_div_reg_value = fpga_clocking_reg & FPGA_REG16_VCO_DIV_MASK; + +	switch(vco_div_reg_value) { +		case FPGA_REG16_VCO_DIV_4: +			vco_div_selection = FPGA_VCO_DIV_4; +			break; +		case FPGA_REG16_VCO_DIV_6: +			vco_div_selection = FPGA_VCO_DIV_6; +			break; +		case FPGA_REG16_VCO_DIV_8: +			vco_div_selection = FPGA_VCO_DIV_8; +			break; +		case FPGA_REG16_VCO_DIV_10: +		default: +			vco_div_selection = FPGA_VCO_DIV_10; +			break; +	} + +	if (master_clock_selection == FPGA_REG16_MASTER_CLK_EXT) { +		switch(master_clock_per) { +			case PERIOD_25_00MHZ: +				if (fb_div_selection == FPGA_FB_DIV_12) { +					if (vco_div_selection == FPGA_VCO_DIV_4) +						sys_per = PERIOD_75_00MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_6) +						sys_per = PERIOD_50_00MHZ; +				} +				break; +			case PERIOD_33_33MHZ: +				if (fb_div_selection == FPGA_FB_DIV_6) { +					if (vco_div_selection == FPGA_VCO_DIV_4) +						sys_per = PERIOD_50_00MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_6) +						sys_per = PERIOD_33_33MHZ; +				} +				if (fb_div_selection == FPGA_FB_DIV_10) { +					if (vco_div_selection == FPGA_VCO_DIV_4) +						sys_per = PERIOD_83_33MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_10) +						sys_per = PERIOD_33_33MHZ; +				} +				if (fb_div_selection == FPGA_FB_DIV_12) { +					if (vco_div_selection == FPGA_VCO_DIV_4) +						sys_per = PERIOD_100_00MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_6) +						sys_per = PERIOD_66_66MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_8) +						sys_per = PERIOD_50_00MHZ; +				} +				break; +			case PERIOD_50_00MHZ: +				if (fb_div_selection == FPGA_FB_DIV_6) { +					if (vco_div_selection == FPGA_VCO_DIV_4) +						sys_per = PERIOD_75_00MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_6) +						sys_per = PERIOD_50_00MHZ; +				} +				if (fb_div_selection == FPGA_FB_DIV_10) { +					if (vco_div_selection == FPGA_VCO_DIV_6) +						sys_per = PERIOD_83_33MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_10) +						sys_per = PERIOD_50_00MHZ; +				} +				if (fb_div_selection == FPGA_FB_DIV_12) { +					if (vco_div_selection == FPGA_VCO_DIV_6) +						sys_per = PERIOD_100_00MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_8) +						sys_per = PERIOD_75_00MHZ; +				} +				break; +			case PERIOD_66_66MHZ: +				if (fb_div_selection == FPGA_FB_DIV_6) { +					if (vco_div_selection == FPGA_VCO_DIV_4) +						sys_per = PERIOD_100_00MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_6) +						sys_per = PERIOD_66_66MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_8) +						sys_per = PERIOD_50_00MHZ; +				} +				if (fb_div_selection == FPGA_FB_DIV_10) { +					if (vco_div_selection == FPGA_VCO_DIV_8) +						sys_per = PERIOD_83_33MHZ; +					if (vco_div_selection == FPGA_VCO_DIV_10) +						sys_per = PERIOD_66_66MHZ; +				} +				if (fb_div_selection == FPGA_FB_DIV_12) { +					if (vco_div_selection == FPGA_VCO_DIV_8) +						sys_per = PERIOD_100_00MHZ; +				} +				break; +			default: +				break; +		} + +		if (sys_per == 0) { +			/* Other combinations are not supported */ +			DEBUGF ("%s[%d] *** sys period compute failed ***\n", __FUNCTION__,__LINE__); +			hang(); +		} +	} else { +		/* calcul system clock without cheking */ +		/* if engineering option clock no check is selected */ +		/* sys_per = master_clock_per * vco_div_selection / fb_div_selection */ +		sys_per = (master_clock_per/fb_div_selection) * vco_div_selection; +	} + +	return(sys_per); +} + +/*-------------------------------------------------------------------------+ +| determine_pci_clock_per. ++-------------------------------------------------------------------------*/ +unsigned long determine_pci_clock_per(void) +{ +	unsigned long pci_clock_selection,  pci_period; + +	/*-------------------------------------------------------------------------+ +	 | Read FPGA reg 6 to get PCI 0 FPGA reg information +	 +-------------------------------------------------------------------------*/ +	pci_clock_selection = in16(FPGA_REG16);	/* was reg6 averifier */ + + +	pci_clock_selection = pci_clock_selection & FPGA_REG16_PCI0_CLK_MASK; + +	switch (pci_clock_selection) { +		case FPGA_REG16_PCI0_CLK_133_33: +			pci_period = PERIOD_133_33MHZ; +			break; +		case FPGA_REG16_PCI0_CLK_100: +			pci_period = PERIOD_100_00MHZ; +			break; +		case FPGA_REG16_PCI0_CLK_66_66: +			pci_period = PERIOD_66_66MHZ; +			break; +		default: +			pci_period = PERIOD_33_33MHZ;; +			break; +	} + +	return(pci_period); +} +#endif + +#elif defined(CONFIG_XILINX_405) +extern void get_sys_info (sys_info_t * sysInfo); +extern ulong get_PCI_freq (void); + +#elif defined(CONFIG_405) + +void get_sys_info (sys_info_t * sysInfo) +{ +	sysInfo->freqVCOMhz=3125000; +	sysInfo->freqProcessor=12*1000*1000; +	sysInfo->freqPLB=50*1000*1000; +	sysInfo->freqPCI=66*1000*1000; +} + +#elif defined(CONFIG_405EP) +void get_sys_info (PPC4xx_SYS_INFO * sysInfo) +{ +	unsigned long pllmr0; +	unsigned long pllmr1; +	unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ / 1000); +	unsigned long m; +	unsigned long pllmr0_ccdv; + +	/* +	 * Read PLL Mode registers +	 */ +	pllmr0 = mfdcr (CPC0_PLLMR0); +	pllmr1 = mfdcr (CPC0_PLLMR1); + +	/* +	 * Determine forward divider A +	 */ +	sysInfo->pllFwdDiv = 8 - ((pllmr1 & PLLMR1_FWDVA_MASK) >> 16); + +	/* +	 * Determine forward divider B (should be equal to A) +	 */ +	sysInfo->pllFwdDivB = 8 - ((pllmr1 & PLLMR1_FWDVB_MASK) >> 12); + +	/* +	 * Determine FBK_DIV. +	 */ +	sysInfo->pllFbkDiv = ((pllmr1 & PLLMR1_FBMUL_MASK) >> 20); +	if (sysInfo->pllFbkDiv == 0) +		sysInfo->pllFbkDiv = 16; + +	/* +	 * Determine PLB_DIV. +	 */ +	sysInfo->pllPlbDiv = ((pllmr0 & PLLMR0_CPU_TO_PLB_MASK) >> 16) + 1; + +	/* +	 * Determine PCI_DIV. +	 */ +	sysInfo->pllPciDiv = (pllmr0 & PLLMR0_PCI_TO_PLB_MASK) + 1; + +	/* +	 * Determine EXTBUS_DIV. +	 */ +	sysInfo->pllExtBusDiv = ((pllmr0 & PLLMR0_EXB_TO_PLB_MASK) >> 8) + 2; + +	/* +	 * Determine OPB_DIV. +	 */ +	sysInfo->pllOpbDiv = ((pllmr0 & PLLMR0_OPB_TO_PLB_MASK) >> 12) + 1; + +	/* +	 * Determine the M factor +	 */ +	m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivB; + +	/* +	 * Determine VCO clock frequency +	 */ +	sysInfo->freqVCOHz = (1000000000000LL * (unsigned long long)m) / +		(unsigned long long)sysClkPeriodPs; + +	/* +	 * Determine CPU clock frequency +	 */ +	pllmr0_ccdv = ((pllmr0 & PLLMR0_CPU_DIV_MASK) >> 20) + 1; +	if (pllmr1 & PLLMR1_SSCS_MASK) { +		/* +		 * This is true if FWDVA == FWDVB: +		 * sysInfo->freqProcessor = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv) +		 *	/ pllmr0_ccdv; +		 */ +		sysInfo->freqProcessor = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv * sysInfo->pllFwdDivB) +			/ sysInfo->pllFwdDiv / pllmr0_ccdv; +	} else { +		sysInfo->freqProcessor = CONFIG_SYS_CLK_FREQ / pllmr0_ccdv; +	} + +	/* +	 * Determine PLB clock frequency +	 */ +	sysInfo->freqPLB = sysInfo->freqProcessor / sysInfo->pllPlbDiv; + +	sysInfo->freqEBC = sysInfo->freqPLB / sysInfo->pllExtBusDiv; + +	sysInfo->freqOPB = sysInfo->freqPLB / sysInfo->pllOpbDiv; + +	sysInfo->freqUART = sysInfo->freqProcessor * pllmr0_ccdv; +} + + +/******************************************** + * get_PCI_freq + * return PCI bus freq in Hz + *********************************************/ +ulong get_PCI_freq (void) +{ +	ulong val; +	PPC4xx_SYS_INFO sys_info; + +	get_sys_info (&sys_info); +	val = sys_info.freqPLB / sys_info.pllPciDiv; +	return val; +} + +#elif defined(CONFIG_405EZ) +void get_sys_info (PPC4xx_SYS_INFO * sysInfo) +{ +	unsigned long cpr_plld; +	unsigned long cpr_pllc; +	unsigned long cpr_primad; +	unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ/1000); +	unsigned long primad_cpudv; +	unsigned long m; +	unsigned long plloutb; + +	/* +	 * Read PLL Mode registers +	 */ +	mfcpr(CPR0_PLLD, cpr_plld); +	mfcpr(CPR0_PLLC, cpr_pllc); + +	/* +	 * Determine forward divider A +	 */ +	sysInfo->pllFwdDiv = ((cpr_plld & PLLD_FWDVA_MASK) >> 16); + +	/* +	 * Determine forward divider B +	 */ +	sysInfo->pllFwdDivB = ((cpr_plld & PLLD_FWDVB_MASK) >> 8); +	if (sysInfo->pllFwdDivB == 0) +		sysInfo->pllFwdDivB = 8; + +	/* +	 * Determine FBK_DIV. +	 */ +	sysInfo->pllFbkDiv = ((cpr_plld & PLLD_FBDV_MASK) >> 24); +	if (sysInfo->pllFbkDiv == 0) +		sysInfo->pllFbkDiv = 256; + +	/* +	 * Read CPR_PRIMAD register +	 */ +	mfcpr(CPR0_PRIMAD, cpr_primad); + +	/* +	 * Determine PLB_DIV. +	 */ +	sysInfo->pllPlbDiv = ((cpr_primad & PRIMAD_PLBDV_MASK) >> 16); +	if (sysInfo->pllPlbDiv == 0) +		sysInfo->pllPlbDiv = 16; + +	/* +	 * Determine EXTBUS_DIV. +	 */ +	sysInfo->pllExtBusDiv = (cpr_primad & PRIMAD_EBCDV_MASK); +	if (sysInfo->pllExtBusDiv == 0) +		sysInfo->pllExtBusDiv = 16; + +	/* +	 * Determine OPB_DIV. +	 */ +	sysInfo->pllOpbDiv = ((cpr_primad & PRIMAD_OPBDV_MASK) >> 8); +	if (sysInfo->pllOpbDiv == 0) +		sysInfo->pllOpbDiv = 16; + +	/* +	 * Determine the M factor +	 */ +	if (cpr_pllc & PLLC_SRC_MASK) +		m = sysInfo->pllFbkDiv * sysInfo->pllFwdDivB; +	else +		m = sysInfo->pllFbkDiv * sysInfo->pllFwdDiv; + +	/* +	 * Determine VCO clock frequency +	 */ +	sysInfo->freqVCOHz = (1000000000000LL * (unsigned long long)m) / +		(unsigned long long)sysClkPeriodPs; + +	/* +	 * Determine CPU clock frequency +	 */ +	primad_cpudv = ((cpr_primad & PRIMAD_CPUDV_MASK) >> 24); +	if (primad_cpudv == 0) +		primad_cpudv = 16; + +	sysInfo->freqProcessor = (CONFIG_SYS_CLK_FREQ * m) / +		sysInfo->pllFwdDiv / primad_cpudv; + +	/* +	 * Determine PLB clock frequency +	 */ +	sysInfo->freqPLB = (CONFIG_SYS_CLK_FREQ * m) / +		sysInfo->pllFwdDiv / sysInfo->pllPlbDiv; + +	sysInfo->freqOPB = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv) / +		sysInfo->pllOpbDiv; + +	sysInfo->freqEBC = (CONFIG_SYS_CLK_FREQ * sysInfo->pllFbkDiv) / +		sysInfo->pllExtBusDiv; + +	plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ? +		sysInfo->pllFwdDivB : sysInfo->pllFwdDiv) * sysInfo->pllFbkDiv) / +		sysInfo->pllFwdDivB); +	sysInfo->freqUART = plloutb; +} + +#elif defined(CONFIG_405EX) + +/* + * TODO: We need to get the CPR registers and calculate these values correctly!!!! + *   We need the specs!!!! + */ +static unsigned char get_fbdv(unsigned char index) +{ +	unsigned char ret = 0; +	/* This is table should be 256 bytes. +	 * Only take first 52 values. +	 */ +	unsigned char fbdv_tb[] = { +		0x00, 0xff, 0x7f, 0xfd, +		0x7a, 0xf5, 0x6a, 0xd5, +		0x2a, 0xd4, 0x29, 0xd3, +		0x26, 0xcc, 0x19, 0xb3, +		0x67, 0xce, 0x1d, 0xbb, +		0x77, 0xee, 0x5d, 0xba, +		0x74, 0xe9, 0x52, 0xa5, +		0x4b, 0x96, 0x2c, 0xd8, +		0x31, 0xe3, 0x46, 0x8d, +		0x1b, 0xb7, 0x6f, 0xde, +		0x3d, 0xfb, 0x76, 0xed, +		0x5a, 0xb5, 0x6b, 0xd6, +		0x2d, 0xdb, 0x36, 0xec, + +	}; + +	if ((index & 0x7f) == 0) +		return 1; +	while (ret < sizeof (fbdv_tb)) { +		if (fbdv_tb[ret] == index) +			break; +		ret++; +	} +	ret++; + +	return ret; +} + +#define PLL_FBK_PLL_LOCAL	0 +#define PLL_FBK_CPU		1 +#define PLL_FBK_PERCLK		5 + +void get_sys_info (sys_info_t * sysInfo) +{ +	unsigned long sysClkPeriodPs = ONE_BILLION / (CONFIG_SYS_CLK_FREQ / 1000); +	unsigned long m = 1; +	unsigned int  tmp; +	unsigned char fwdva[16] = { +		1, 2, 14, 9, 4, 11, 16, 13, +		12, 5, 6, 15, 10, 7, 8, 3, +	}; +	unsigned char sel, cpudv0, plb2xDiv; + +	mfcpr(CPR0_PLLD, tmp); + +	/* +	 * Determine forward divider A +	 */ +	sysInfo->pllFwdDiv = fwdva[((tmp >> 16) & 0x0f)];	/* FWDVA */ + +	/* +	 * Determine FBK_DIV. +	 */ +	sysInfo->pllFbkDiv = get_fbdv(((tmp >> 24) & 0x0ff)); /* FBDV */ + +	/* +	 * Determine PLBDV0 +	 */ +	sysInfo->pllPlbDiv = 2; + +	/* +	 * Determine PERDV0 +	 */ +	mfcpr(CPR0_PERD, tmp); +	tmp = (tmp >> 24) & 0x03; +	sysInfo->pllExtBusDiv = (tmp == 0) ? 4 : tmp; + +	/* +	 * Determine OPBDV0 +	 */ +	mfcpr(CPR0_OPBD0, tmp); +	tmp = (tmp >> 24) & 0x03; +	sysInfo->pllOpbDiv = (tmp == 0) ? 4 : tmp; + +	/* Determine PLB2XDV0 */ +	mfcpr(CPR0_PLBD, tmp); +	tmp = (tmp >> 16) & 0x07; +	plb2xDiv = (tmp == 0) ? 8 : tmp; + +	/* Determine CPUDV0 */ +	mfcpr(CPR0_CPUD, tmp); +	tmp = (tmp >> 24) & 0x07; +	cpudv0 = (tmp == 0) ? 8 : tmp; + +	/* Determine SEL(5:7) in CPR0_PLLC */ +	mfcpr(CPR0_PLLC, tmp); +	sel = (tmp >> 24) & 0x07; + +	/* +	 * Determine the M factor +	 * PLL local: M = FBDV +	 * CPU clock: M = FBDV * FWDVA * CPUDV0 +	 * PerClk	: M = FBDV * FWDVA * PLB2XDV0 * PLBDV0(2) * OPBDV0 * PERDV0 +	 * +	 */ +	switch (sel) { +	case PLL_FBK_CPU: +		m = sysInfo->pllFwdDiv * cpudv0; +		break; +	case PLL_FBK_PERCLK: +		m = sysInfo->pllFwdDiv * plb2xDiv * 2 +			* sysInfo->pllOpbDiv * sysInfo->pllExtBusDiv; +		break; +	case PLL_FBK_PLL_LOCAL: +		break; +	default: +		printf("%s unknown m\n", __FUNCTION__); +		return; + +	} +	m *= sysInfo->pllFbkDiv; + +	/* +	 * Determine VCO clock frequency +	 */ +	sysInfo->freqVCOHz = (1000000000000LL * (unsigned long long)m) / +		(unsigned long long)sysClkPeriodPs; + +	/* +	 * Determine CPU clock frequency +	 */ +	sysInfo->freqProcessor = sysInfo->freqVCOHz / (sysInfo->pllFwdDiv * cpudv0); + +	/* +	 * Determine PLB clock frequency, ddr1x should be the same +	 */ +	sysInfo->freqPLB = sysInfo->freqVCOHz / (sysInfo->pllFwdDiv * plb2xDiv * 2); +	sysInfo->freqOPB = sysInfo->freqPLB/sysInfo->pllOpbDiv; +	sysInfo->freqDDR = sysInfo->freqPLB; +	sysInfo->freqEBC = sysInfo->freqOPB / sysInfo->pllExtBusDiv; +	sysInfo->freqUART = sysInfo->freqPLB; +} + +#endif + +int get_clocks (void) +{ +	sys_info_t sys_info; + +	get_sys_info (&sys_info); +	gd->cpu_clk = sys_info.freqProcessor; +	gd->bus_clk = sys_info.freqPLB; + +	return (0); +} + + +/******************************************** + * get_bus_freq + * return PLB bus freq in Hz + *********************************************/ +ulong get_bus_freq (ulong dummy) +{ +	ulong val; + +#if defined(CONFIG_405GP) || \ +    defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ +    defined(CONFIG_405EX) || defined(CONFIG_405) || \ +    defined(CONFIG_440) +	sys_info_t sys_info; + +	get_sys_info (&sys_info); +	val = sys_info.freqPLB; +#else +# error get_bus_freq() not implemented +#endif + +	return val; +} + +ulong get_OPB_freq (void) +{ +	PPC4xx_SYS_INFO sys_info; + +	get_sys_info (&sys_info); + +	return sys_info.freqOPB; +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/spl_boot.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/spl_boot.c new file mode 100644 index 00000000..318f23b6 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/spl_boot.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Stefan Roese <sr@denx.de> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <spl.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Return selected boot device. On PPC4xx its only NOR flash right now. + */ +u32 spl_boot_device(void) +{ +	return BOOT_DEVICE_NOR; +} + +/* + * SPL version of board_init_f() + */ +void board_init_f(ulong bootflag) +{ +	/* +	 * First we need to initialize the SDRAM, so that the real +	 * U-Boot or the OS (Linux) can be loaded +	 */ +	initdram(0); + +	/* Clear bss */ +	memset(__bss_start, '\0', __bss_end - __bss_start); + +	/* +	 * Init global_data pointer. Has to be done before calling +	 * get_clocks(), as it stores some clock values into gd needed +	 * later on in the serial driver. +	 */ +	/* 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)); + +	/* +	 * get_clocks() needs to be called so that the serial driver +	 * works correctly +	 */ +	get_clocks(); + +	/* +	 * Do rudimental console / serial setup +	 */ +	preloader_console_init(); + +	/* +	 * Call board_init_r() (SPL framework version) to load and boot +	 * real U-Boot or OS +	 */ +	board_init_r(NULL, 0); +	/* Does not return!!! */ +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/start.S b/roms/u-boot/arch/powerpc/cpu/ppc4xx/start.S new file mode 100644 index 00000000..11b55d5a --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/start.S @@ -0,0 +1,1954 @@ +/* + *  Copyright (C) 1998	Dan Malek <dmalek@jlc.net> + *  Copyright (C) 1999	Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + *  Copyright (C) 2000,2001,2002 Wolfgang Denk <wd@denx.de> + *  Copyright (C) 2007 Stefan Roese <sr@denx.de>, DENX Software Engineering + *  Copyright (c) 2008 Nuovation System Designs, LLC + *    Grant Erickson <gerickson@nuovations.com> + * + * SPDX-License-Identifier:	GPL-2.0	IBM-pibs + */ + +/* + * Startup code for IBM/AMCC PowerPC 4xx (PPC4xx) based boards + * + * The following description only applies to the NOR flash style booting. + * NAND booting is different. For more details about NAND booting on 4xx + * take a look at doc/README.nand-boot-ppc440. + * + * The CPU starts at address 0xfffffffc (last word in the address space). + * The U-Boot image therefore has to be located in the "upper" area of the + * flash (e.g. 512MiB - 0xfff80000 ... 0xffffffff). The default value for + * the boot chip-select (CS0) is quite big and covers this area. On the + * 405EX this is for example 0xffe00000 ... 0xffffffff. U-Boot will + * reconfigure this CS0 (and other chip-selects as well when configured + * this way) in the boot process to the "correct" values matching the + * board layout. + */ + +#include <asm-offsets.h> +#include <config.h> +#include <asm/ppc4xx.h> +#include <version.h> + +#include <ppc_asm.tmpl> +#include <ppc_defs.h> + +#include <asm/cache.h> +#include <asm/mmu.h> +#include <asm/ppc4xx-isram.h> + +#ifdef CONFIG_SYS_INIT_DCACHE_CS +# if (CONFIG_SYS_INIT_DCACHE_CS == 0) +#  define PBxAP PB1AP +#  define PBxCR PB0CR +#  if (defined(CONFIG_SYS_EBC_PB0AP) && defined(CONFIG_SYS_EBC_PB0CR)) +#   define PBxAP_VAL CONFIG_SYS_EBC_PB0AP +#   define PBxCR_VAL CONFIG_SYS_EBC_PB0CR +#  endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 1) +#  define PBxAP PB1AP +#  define PBxCR PB1CR +#  if (defined(CONFIG_SYS_EBC_PB1AP) && defined(CONFIG_SYS_EBC_PB1CR)) +#   define PBxAP_VAL CONFIG_SYS_EBC_PB1AP +#   define PBxCR_VAL CONFIG_SYS_EBC_PB1CR +#  endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 2) +#  define PBxAP PB2AP +#  define PBxCR PB2CR +#  if (defined(CONFIG_SYS_EBC_PB2AP) && defined(CONFIG_SYS_EBC_PB2CR)) +#   define PBxAP_VAL CONFIG_SYS_EBC_PB2AP +#   define PBxCR_VAL CONFIG_SYS_EBC_PB2CR +#  endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 3) +#  define PBxAP PB3AP +#  define PBxCR PB3CR +#  if (defined(CONFIG_SYS_EBC_PB3AP) && defined(CONFIG_SYS_EBC_PB3CR)) +#   define PBxAP_VAL CONFIG_SYS_EBC_PB3AP +#   define PBxCR_VAL CONFIG_SYS_EBC_PB3CR +#  endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 4) +#  define PBxAP PB4AP +#  define PBxCR PB4CR +#  if (defined(CONFIG_SYS_EBC_PB4AP) && defined(CONFIG_SYS_EBC_PB4CR)) +#   define PBxAP_VAL CONFIG_SYS_EBC_PB4AP +#   define PBxCR_VAL CONFIG_SYS_EBC_PB4CR +#  endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 5) +#  define PBxAP PB5AP +#  define PBxCR PB5CR +#  if (defined(CONFIG_SYS_EBC_PB5AP) && defined(CONFIG_SYS_EBC_PB5CR)) +#   define PBxAP_VAL CONFIG_SYS_EBC_PB5AP +#   define PBxCR_VAL CONFIG_SYS_EBC_PB5CR +#  endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 6) +#  define PBxAP PB6AP +#  define PBxCR PB6CR +#  if (defined(CONFIG_SYS_EBC_PB6AP) && defined(CONFIG_SYS_EBC_PB6CR)) +#   define PBxAP_VAL CONFIG_SYS_EBC_PB6AP +#   define PBxCR_VAL CONFIG_SYS_EBC_PB6CR +#  endif +# endif +# if (CONFIG_SYS_INIT_DCACHE_CS == 7) +#  define PBxAP PB7AP +#  define PBxCR PB7CR +#  if (defined(CONFIG_SYS_EBC_PB7AP) && defined(CONFIG_SYS_EBC_PB7CR)) +#   define PBxAP_VAL CONFIG_SYS_EBC_PB7AP +#   define PBxCR_VAL CONFIG_SYS_EBC_PB7CR +#  endif +# endif +# ifndef PBxAP_VAL +#  define PBxAP_VAL	0 +# endif +# ifndef PBxCR_VAL +#  define PBxCR_VAL	0 +# endif +/* + * Memory Bank x (nothingness) initialization CONFIG_SYS_INIT_RAM_ADDR + 64 MiB + * used as temporary stack pointer for the primordial stack + */ +# ifndef CONFIG_SYS_INIT_DCACHE_PBxAR +#  define CONFIG_SYS_INIT_DCACHE_PBxAR	(EBC_BXAP_BME_DISABLED			| \ +				 EBC_BXAP_TWT_ENCODE(7)			| \ +				 EBC_BXAP_BCE_DISABLE			| \ +				 EBC_BXAP_BCT_2TRANS			| \ +				 EBC_BXAP_CSN_ENCODE(0)			| \ +				 EBC_BXAP_OEN_ENCODE(0)			| \ +				 EBC_BXAP_WBN_ENCODE(0)			| \ +				 EBC_BXAP_WBF_ENCODE(0)			| \ +				 EBC_BXAP_TH_ENCODE(2)			| \ +				 EBC_BXAP_RE_DISABLED			| \ +				 EBC_BXAP_SOR_NONDELAYED		| \ +				 EBC_BXAP_BEM_WRITEONLY			| \ +				 EBC_BXAP_PEN_DISABLED) +# endif /* CONFIG_SYS_INIT_DCACHE_PBxAR */ +# ifndef CONFIG_SYS_INIT_DCACHE_PBxCR +#  define CONFIG_SYS_INIT_DCACHE_PBxCR	(EBC_BXCR_BAS_ENCODE(CONFIG_SYS_INIT_RAM_ADDR)	| \ +				 EBC_BXCR_BS_64MB			| \ +				 EBC_BXCR_BU_RW				| \ +				 EBC_BXCR_BW_16BIT) +# endif /* CONFIG_SYS_INIT_DCACHE_PBxCR */ +# ifndef CONFIG_SYS_INIT_RAM_PATTERN +#  define CONFIG_SYS_INIT_RAM_PATTERN	0xDEADDEAD +# endif +#endif /* CONFIG_SYS_INIT_DCACHE_CS */ + +#if (defined(CONFIG_SYS_INIT_RAM_DCACHE) && (CONFIG_SYS_INIT_RAM_SIZE > (4 << 10))) +#error Only 4k of init-ram is supported - please adjust CONFIG_SYS_INIT_RAM_SIZE! +#endif + +/* + * Unless otherwise overriden, enable two 128MB cachable instruction regions + * at CONFIG_SYS_SDRAM_BASE and another 128MB cacheable instruction region covering + * NOR flash at CONFIG_SYS_FLASH_BASE. Disable all cacheable data regions. + */ +#if !defined(CONFIG_SYS_FLASH_BASE) +/* If not already defined, set it to the "last" 128MByte region */ +# define CONFIG_SYS_FLASH_BASE		0xf8000000 +#endif +#if !defined(CONFIG_SYS_ICACHE_SACR_VALUE) +# define CONFIG_SYS_ICACHE_SACR_VALUE		\ +		(PPC_128MB_SACR_VALUE(CONFIG_SYS_SDRAM_BASE + (  0 << 20)) | \ +		 PPC_128MB_SACR_VALUE(CONFIG_SYS_SDRAM_BASE + (128 << 20)) | \ +		 PPC_128MB_SACR_VALUE(CONFIG_SYS_FLASH_BASE)) +#endif /* !defined(CONFIG_SYS_ICACHE_SACR_VALUE) */ + +#if !defined(CONFIG_SYS_DCACHE_SACR_VALUE) +# define CONFIG_SYS_DCACHE_SACR_VALUE		\ +		(0x00000000) +#endif /* !defined(CONFIG_SYS_DCACHE_SACR_VALUE) */ + +#if !defined(CONFIG_SYS_TLB_FOR_BOOT_FLASH) +#define CONFIG_SYS_TLB_FOR_BOOT_FLASH	0	/* use TLB 0 as default */ +#endif + +#define function_prolog(func_name)	.text; \ +					.align 2; \ +					.globl func_name; \ +					func_name: +#define function_epilog(func_name)	.type func_name,@function; \ +					.size func_name,.-func_name + +/* We don't want the  MMU yet. +*/ +#undef	MSR_KERNEL +#define MSR_KERNEL ( MSR_ME  )	/* Machine Check */ + + +	.extern ext_bus_cntlr_init + +/* + * Set up GOT: Global Offset Table + * + * Use r12 to access the GOT + */ +#if !defined(CONFIG_SPL_BUILD) +	START_GOT +	GOT_ENTRY(_GOT2_TABLE_) +	GOT_ENTRY(_FIXUP_TABLE_) + +	GOT_ENTRY(_start) +	GOT_ENTRY(_start_of_vectors) +	GOT_ENTRY(_end_of_vectors) +	GOT_ENTRY(transfer_to_handler) + +	GOT_ENTRY(__init_end) +	GOT_ENTRY(__bss_end) +	GOT_ENTRY(__bss_start) +	END_GOT +#endif /* CONFIG_SPL_BUILD */ + +#if defined(CONFIG_SYS_RAMBOOT) || defined(CONFIG_BOOT_FROM_XMD) +	/* +	 * 4xx RAM-booting U-Boot image is started from offset 0 +	 */ +	.text +	bl	_start_440 +#endif + +#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD) +	/* +	 * This is the entry of the real U-Boot from a board port +	 * that supports SPL booting on the PPC4xx. We only need +	 * to call board_init_f() here. Everything else has already +	 * been done in the SPL u-boot version. +	 */ +	GET_GOT			/* initialize GOT access		*/ +	bl	board_init_f	/* run 1st part of board init code (in Flash)*/ +	/* NOTREACHED - board_init_f() does not return */ +#endif + +/* + * 440 Startup -- on reset only the top 4k of the effective + * address space is mapped in by an entry in the instruction + * and data shadow TLB. The .bootpg section is located in the + * top 4k & does only what's necessary to map in the the rest + * of the boot rom. Once the boot rom is mapped in we can + * proceed with normal startup. + * + * NOTE: CS0 only covers the top 2MB of the effective address + * space after reset. + */ + +#if defined(CONFIG_440) +    .section .bootpg,"ax" +    .globl _start_440 + +/**************************************************************************/ +_start_440: +	/*--------------------------------------------------------------------+ +	| 440EPX BUP Change - Hardware team request +	+--------------------------------------------------------------------*/ +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +	sync +	nop +	nop +#endif +	/*----------------------------------------------------------------+ +	| Core bug fix.  Clear the esr +	+-----------------------------------------------------------------*/ +	li	r0,0 +	mtspr	SPRN_ESR,r0 +	/*----------------------------------------------------------------*/ +	/* Clear and set up some registers. */ +	/*----------------------------------------------------------------*/ +	iccci	r0,r0		/* NOTE: operands not used for 440 */ +	dccci	r0,r0		/* NOTE: operands not used for 440 */ +	sync +	li	r0,0 +	mtspr	SPRN_SRR0,r0 +	mtspr	SPRN_SRR1,r0 +	mtspr	SPRN_CSRR0,r0 +	mtspr	SPRN_CSRR1,r0 +	/* NOTE: 440GX adds machine check status regs */ +#if defined(CONFIG_440) && !defined(CONFIG_440GP) +	mtspr	SPRN_MCSRR0,r0 +	mtspr	SPRN_MCSRR1,r0 +	mfspr	r1,SPRN_MCSR +	mtspr	SPRN_MCSR,r1 +#endif + +	/*----------------------------------------------------------------*/ +	/* CCR0 init */ +	/*----------------------------------------------------------------*/ +	/* Disable store gathering & broadcast, guarantee inst/data +	* cache block touch, force load/store alignment +	* (see errata 1.12: 440_33) +	*/ +	lis	r1,0x0030	/* store gathering & broadcast disable */ +	ori	r1,r1,0x6000	/* cache touch */ +	mtspr	SPRN_CCR0,r1 + +	/*----------------------------------------------------------------*/ +	/* Initialize debug */ +	/*----------------------------------------------------------------*/ +	mfspr	r1,SPRN_DBCR0 +	andis.	r1, r1, 0x8000	/* test DBCR0[EDM] bit			*/ +	bne	skip_debug_init	/* if set, don't clear debug register	*/ +	mfspr	r1,SPRN_CCR0 +	ori	r1,r1,CCR0_DTB@l /* Disable Trace Broadcast */ +	mtspr	SPRN_CCR0,r1 +	mtspr	SPRN_DBCR0,r0 +	mtspr	SPRN_DBCR1,r0 +	mtspr	SPRN_DBCR2,r0 +	mtspr	SPRN_IAC1,r0 +	mtspr	SPRN_IAC2,r0 +	mtspr	SPRN_IAC3,r0 +	mtspr	SPRN_DAC1,r0 +	mtspr	SPRN_DAC2,r0 +	mtspr	SPRN_DVC1,r0 +	mtspr	SPRN_DVC2,r0 + +	mfspr	r1,SPRN_DBSR +	mtspr	SPRN_DBSR,r1	/* Clear all valid bits */ +skip_debug_init: + +#if defined (CONFIG_440SPE) +	/*----------------------------------------------------------------+ +	| Initialize Core Configuration Reg1. +	| a. ICDPEI: Record even parity. Normal operation. +	| b. ICTPEI: Record even parity. Normal operation. +	| c. DCTPEI: Record even parity. Normal operation. +	| d. DCDPEI: Record even parity. Normal operation. +	| e. DCUPEI: Record even parity. Normal operation. +	| f. DCMPEI: Record even parity. Normal operation. +	| g. FCOM:   Normal operation +	| h. MMUPEI: Record even parity. Normal operation. +	| i. FFF:    Flush only as much data as necessary. +	| j. TCS:    Timebase increments from CPU clock. +	+-----------------------------------------------------------------*/ +	li	r0,0 +	mtspr	SPRN_CCR1, r0 + +	/*----------------------------------------------------------------+ +	| Reset the timebase. +	| The previous write to CCR1 sets the timebase source. +	+-----------------------------------------------------------------*/ +	mtspr	SPRN_TBWL, r0 +	mtspr	SPRN_TBWU, r0 +#endif + +	/*----------------------------------------------------------------*/ +	/* Setup interrupt vectors */ +	/*----------------------------------------------------------------*/ +	mtspr	SPRN_IVPR,r0		/* Vectors start at 0x0000_0000 */ +	li	r1,0x0100 +	mtspr	SPRN_IVOR0,r1	/* Critical input */ +	li	r1,0x0200 +	mtspr	SPRN_IVOR1,r1	/* Machine check */ +	li	r1,0x0300 +	mtspr	SPRN_IVOR2,r1	/* Data storage */ +	li	r1,0x0400 +	mtspr	SPRN_IVOR3,r1	/* Instruction storage */ +	li	r1,0x0500 +	mtspr	SPRN_IVOR4,r1	/* External interrupt */ +	li	r1,0x0600 +	mtspr	SPRN_IVOR5,r1	/* Alignment */ +	li	r1,0x0700 +	mtspr	SPRN_IVOR6,r1	/* Program check */ +	li	r1,0x0800 +	mtspr	SPRN_IVOR7,r1	/* Floating point unavailable */ +	li	r1,0x0c00 +	mtspr	SPRN_IVOR8,r1	/* System call */ +	li	r1,0x0a00 +	mtspr	SPRN_IVOR9,r1	/* Auxiliary Processor unavailable */ +	li	r1,0x0900 +	mtspr	SPRN_IVOR10,r1	/* Decrementer */ +	li	r1,0x1300 +	mtspr	SPRN_IVOR13,r1	/* Data TLB error */ +	li	r1,0x1400 +	mtspr	SPRN_IVOR14,r1	/* Instr TLB error */ +	li	r1,0x2000 +	mtspr	SPRN_IVOR15,r1	/* Debug */ + +	/*----------------------------------------------------------------*/ +	/* Configure cache regions  */ +	/*----------------------------------------------------------------*/ +	mtspr	SPRN_INV0,r0 +	mtspr	SPRN_INV1,r0 +	mtspr	SPRN_INV2,r0 +	mtspr	SPRN_INV3,r0 +	mtspr	SPRN_DNV0,r0 +	mtspr	SPRN_DNV1,r0 +	mtspr	SPRN_DNV2,r0 +	mtspr	SPRN_DNV3,r0 +	mtspr	SPRN_ITV0,r0 +	mtspr	SPRN_ITV1,r0 +	mtspr	SPRN_ITV2,r0 +	mtspr	SPRN_ITV3,r0 +	mtspr	SPRN_DTV0,r0 +	mtspr	SPRN_DTV1,r0 +	mtspr	SPRN_DTV2,r0 +	mtspr	SPRN_DTV3,r0 + +	/*----------------------------------------------------------------*/ +	/* Cache victim limits */ +	/*----------------------------------------------------------------*/ +	/* floors 0, ceiling max to use the entire cache -- nothing locked +	*/ +	lis	r1,0x0001 +	ori	r1,r1,0xf800 +	mtspr	SPRN_IVLIM,r1 +	mtspr	SPRN_DVLIM,r1 + +	/*----------------------------------------------------------------+ +	|Initialize MMUCR[STID] = 0. +	+-----------------------------------------------------------------*/ +	mfspr	r0,SPRN_MMUCR +	addis	r1,0,0xFFFF +	ori	r1,r1,0xFF00 +	and	r0,r0,r1 +	mtspr	SPRN_MMUCR,r0 + +	/*----------------------------------------------------------------*/ +	/* Clear all TLB entries -- TID = 0, TS = 0 */ +	/*----------------------------------------------------------------*/ +	addis	r0,0,0x0000 +#ifdef CONFIG_SYS_RAMBOOT +	li	r4,0		/* Start with TLB #0 */ +#else +	li	r4,1		/* Start with TLB #1 */ +#endif +	li	r1,64		/* 64 TLB entries */ +	sub	r1,r1,r4	/* calculate last TLB # */ +	mtctr	r1 +rsttlb: +#ifdef CONFIG_SYS_RAMBOOT +	tlbre	r3,r4,0		/* Read contents from TLB word #0 to get EPN */ +	rlwinm.	r3,r3,0,0xfffffc00	/* Mask EPN */ +	beq	tlbnxt		/* Skip EPN=0 TLB, this is the SDRAM TLB */ +#endif +	tlbwe	r0,r4,0		/* Invalidate all entries (V=0)*/ +	tlbwe	r0,r4,1 +	tlbwe	r0,r4,2 +tlbnxt:	addi	r4,r4,1		/* Next TLB */ +	bdnz	rsttlb + +	/*----------------------------------------------------------------*/ +	/* TLB entry setup -- step thru tlbtab */ +	/*----------------------------------------------------------------*/ +#if defined(CONFIG_440SPE_REVA) +	/*----------------------------------------------------------------*/ +	/* We have different TLB tables for revA and rev B of 440SPe */ +	/*----------------------------------------------------------------*/ +	mfspr	r1, PVR +	lis	r0,0x5342 +	ori	r0,r0,0x1891 +	cmpw	r7,r1,r0 +	bne	r7,..revA +	bl	tlbtabB +	b	..goon +..revA: +	bl	tlbtabA +..goon: +#else +	bl	tlbtab		/* Get tlbtab pointer */ +#endif +	mr	r5,r0 +	li	r1,0x003f	/* 64 TLB entries max */ +	mtctr	r1 +	li	r4,0		/* TLB # */ + +	addi	r5,r5,-4 +1: +#ifdef CONFIG_SYS_RAMBOOT +	tlbre	r3,r4,0		/* Read contents from TLB word #0 */ +	rlwinm.	r3,r3,0,0x00000200	/* Mask V (valid) bit */ +	bne	tlbnx2		/* Skip V=1 TLB, this is the SDRAM TLB */ +#endif +	lwzu	r0,4(r5) +	cmpwi	r0,0 +	beq	2f		/* 0 marks end */ +	lwzu	r1,4(r5) +	lwzu	r2,4(r5) +	tlbwe	r0,r4,0		/* TLB Word 0 */ +	tlbwe	r1,r4,1		/* TLB Word 1 */ +	tlbwe	r2,r4,2		/* TLB Word 2 */ +tlbnx2:	addi	r4,r4,1		/* Next TLB */ +	bdnz	1b + +	/*----------------------------------------------------------------*/ +	/* Continue from 'normal' start */ +	/*----------------------------------------------------------------*/ +2: +	bl	3f +	b	_start + +3:	li	r0,0 +	mtspr	SPRN_SRR1,r0		/* Keep things disabled for now */ +	mflr	r1 +	mtspr	SPRN_SRR0,r1 +	rfi +#endif /* CONFIG_440 */ + +/* + * r3 - 1st arg to board_init(): IMMP pointer + * r4 - 2nd arg to board_init(): boot flag + */ +#if !defined(CONFIG_SPL_BUILD) +	.text +	.long	0x27051956		/* U-Boot Magic Number			*/ +	.globl	version_string +version_string: +	.ascii U_BOOT_VERSION_STRING, "\0" + +	. = EXC_OFF_SYS_RESET +	.globl	_start_of_vectors +_start_of_vectors: + +/* Critical input. */ +	CRIT_EXCEPTION(0x100, CritcalInput, UnknownException) + +#ifdef CONFIG_440 +/* Machine check */ +	MCK_EXCEPTION(0x200, MachineCheck, MachineCheckException) +#else +	CRIT_EXCEPTION(0x200, MachineCheck, MachineCheckException) +#endif /* CONFIG_440 */ + +/* Data Storage exception. */ +	STD_EXCEPTION(0x300, DataStorage, UnknownException) + +/* Instruction Storage exception. */ +	STD_EXCEPTION(0x400, InstStorage, UnknownException) + +/* External Interrupt exception. */ +	STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt) + +/* Alignment exception. */ +	. = 0x600 +Alignment: +	EXCEPTION_PROLOG(SRR0, SRR1) +	mfspr	r4,DAR +	stw	r4,_DAR(r21) +	mfspr	r5,DSISR +	stw	r5,_DSISR(r21) +	addi	r3,r1,STACK_FRAME_OVERHEAD +	EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE) + +/* Program check exception */ +	. = 0x700 +ProgramCheck: +	EXCEPTION_PROLOG(SRR0, SRR1) +	addi	r3,r1,STACK_FRAME_OVERHEAD +	EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException, +		MSR_KERNEL, COPY_EE) + +#ifdef CONFIG_440 +	STD_EXCEPTION(0x800, FPUnavailable, UnknownException) +	STD_EXCEPTION(0x900, Decrementer, DecrementerPITException) +	STD_EXCEPTION(0xa00, APU, UnknownException) +#endif +	STD_EXCEPTION(0xc00, SystemCall, UnknownException) + +#ifdef CONFIG_440 +	STD_EXCEPTION(0x1300, DataTLBError, UnknownException) +	STD_EXCEPTION(0x1400, InstructionTLBError, UnknownException) +#else +	STD_EXCEPTION(0x1000, PIT, DecrementerPITException) +	STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException) +	STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException) +#endif +	CRIT_EXCEPTION(0x2000, DebugBreakpoint, DebugException ) + +	.globl	_end_of_vectors +_end_of_vectors: +	. = _START_OFFSET +#endif +	.globl	_start +_start: + +#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD) +	/* +	 * This is the entry of the real U-Boot from a board port +	 * that supports SPL booting on the PPC4xx. We only need +	 * to call board_init_f() here. Everything else has already +	 * been done in the SPL u-boot version. +	 */ +	GET_GOT			/* initialize GOT access		*/ +	bl	board_init_f	/* run 1st part of board init code (in Flash)*/ +	/* NOTREACHED - board_init_f() does not return */ +#endif + +/*****************************************************************************/ +#if defined(CONFIG_440) + +	/*----------------------------------------------------------------*/ +	/* Clear and set up some registers. */ +	/*----------------------------------------------------------------*/ +	li	r0,0x0000 +	lis	r1,0xffff +	mtspr	SPRN_DEC,r0			/* prevent dec exceptions */ +	mtspr	SPRN_TBWL,r0			/* prevent fit & wdt exceptions */ +	mtspr	SPRN_TBWU,r0 +	mtspr	SPRN_TSR,r1			/* clear all timer exception status */ +	mtspr	SPRN_TCR,r0			/* disable all */ +	mtspr	SPRN_ESR,r0			/* clear exception syndrome register */ +	mtxer	r0			/* clear integer exception register */ + +	/*----------------------------------------------------------------*/ +	/* Debug setup -- some (not very good) ice's need an event*/ +	/* to establish control :-( Define CONFIG_SYS_INIT_DBCR to the dbsr */ +	/* value you need in this case 0x8cff 0000 should do the trick */ +	/*----------------------------------------------------------------*/ +#if defined(CONFIG_SYS_INIT_DBCR) +	lis	r1,0xffff +	ori	r1,r1,0xffff +	mtspr	SPRN_DBSR,r1			/* Clear all status bits */ +	lis	r0,CONFIG_SYS_INIT_DBCR@h +	ori	r0,r0,CONFIG_SYS_INIT_DBCR@l +	mtspr	SPRN_DBCR0,r0 +	isync +#endif + +	/*----------------------------------------------------------------*/ +	/* Setup the internal SRAM */ +	/*----------------------------------------------------------------*/ +	li	r0,0 + +#ifdef CONFIG_SYS_INIT_RAM_DCACHE +	/* Clear Dcache to use as RAM */ +	addis	r3,r0,CONFIG_SYS_INIT_RAM_ADDR@h +	ori	r3,r3,CONFIG_SYS_INIT_RAM_ADDR@l +	addis	r4,r0,CONFIG_SYS_INIT_RAM_SIZE@h +	ori	r4,r4,CONFIG_SYS_INIT_RAM_SIZE@l +	rlwinm. r5,r4,0,27,31 +	rlwinm	r5,r4,27,5,31 +	beq	..d_ran +	addi	r5,r5,0x0001 +..d_ran: +	mtctr	r5 +..d_ag: +	dcbz	r0,r3 +	addi	r3,r3,32 +	bdnz	..d_ag + +	/* +	 * Lock the init-ram/stack in d-cache, so that other regions +	 * may use d-cache as well +	 * Note, that this current implementation locks exactly 4k +	 * of d-cache, so please make sure that you don't define a +	 * bigger init-ram area. Take a look at the lwmon5 440EPx +	 * implementation as a reference. +	 */ +	msync +	isync +	/* 8. set TFLOOR/NFLOOR to 8 (-> 8*16*32 bytes locked -> 4k) */ +	lis	r1,0x0201 +	ori	r1,r1,0xf808 +	mtspr	SPRN_DVLIM,r1 +	lis	r1,0x0808 +	ori	r1,r1,0x0808 +	mtspr	SPRN_DNV0,r1 +	mtspr	SPRN_DNV1,r1 +	mtspr	SPRN_DNV2,r1 +	mtspr	SPRN_DNV3,r1 +	mtspr	SPRN_DTV0,r1 +	mtspr	SPRN_DTV1,r1 +	mtspr	SPRN_DTV2,r1 +	mtspr	SPRN_DTV3,r1 +	msync +	isync +#endif /* CONFIG_SYS_INIT_RAM_DCACHE */ + +	/* 440EP & 440GR are only 440er PPC's without internal SRAM */ +#if !defined(CONFIG_440EP) && !defined(CONFIG_440GR) +	/* not all PPC's have internal SRAM usable as L2-cache */ +#if defined(CONFIG_440GX) || \ +    defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ +    defined(CONFIG_460SX) +	mtdcr	L2_CACHE_CFG,r0		/* Ensure L2 Cache is off */ +#elif defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ +      defined(CONFIG_APM821XX) +	lis	r1, 0x0000 +	ori	r1,r1,0x0008		/* Set L2_CACHE_CFG[RDBW]=1 */ +	mtdcr	L2_CACHE_CFG,r1 +#endif + +	lis	r2,0x7fff +	ori	r2,r2,0xffff +	mfdcr	r1,ISRAM0_DPC +	and	r1,r1,r2		/* Disable parity check */ +	mtdcr	ISRAM0_DPC,r1 +	mfdcr	r1,ISRAM0_PMEG +	and	r1,r1,r2		/* Disable pwr mgmt */ +	mtdcr	ISRAM0_PMEG,r1 + +	lis	r1,0x8000		/* BAS = 8000_0000 */ +#if defined(CONFIG_440GX) || defined(CONFIG_440SP) +	ori	r1,r1,0x0980		/* first 64k */ +	mtdcr	ISRAM0_SB0CR,r1 +	lis	r1,0x8001 +	ori	r1,r1,0x0980		/* second 64k */ +	mtdcr	ISRAM0_SB1CR,r1 +	lis	r1, 0x8002 +	ori	r1,r1, 0x0980		/* third 64k */ +	mtdcr	ISRAM0_SB2CR,r1 +	lis	r1, 0x8003 +	ori	r1,r1, 0x0980		/* fourth 64k */ +	mtdcr	ISRAM0_SB3CR,r1 +#elif defined(CONFIG_440SPE) || defined(CONFIG_460EX) || \ +      defined(CONFIG_460GT) || defined(CONFIG_APM821XX) +	lis	r1,0x0000		/* BAS = X_0000_0000 */ +	ori	r1,r1,0x0984		/* first 64k */ +	mtdcr	ISRAM0_SB0CR,r1 +	lis	r1,0x0001 +	ori	r1,r1,0x0984		/* second 64k */ +	mtdcr	ISRAM0_SB1CR,r1 +	lis	r1, 0x0002 +	ori	r1,r1, 0x0984		/* third 64k */ +	mtdcr	ISRAM0_SB2CR,r1 +	lis	r1, 0x0003 +	ori	r1,r1, 0x0984		/* fourth 64k */ +	mtdcr	ISRAM0_SB3CR,r1 +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ +    defined(CONFIG_APM821XX) +	lis	r2,0x7fff +	ori	r2,r2,0xffff +	mfdcr	r1,ISRAM1_DPC +	and	r1,r1,r2		/* Disable parity check */ +	mtdcr	ISRAM1_DPC,r1 +	mfdcr	r1,ISRAM1_PMEG +	and	r1,r1,r2		/* Disable pwr mgmt */ +	mtdcr	ISRAM1_PMEG,r1 + +	lis	r1,0x0004		/* BAS = 4_0004_0000 */ +	ori     r1,r1,ISRAM1_SIZE       /* ocm size */ +	mtdcr	ISRAM1_SB0CR,r1 +#endif +#elif defined(CONFIG_460SX) +	lis     r1,0x0000               /* BAS = 0000_0000 */ +	ori     r1,r1,0x0B84            /* first 128k */ +	mtdcr   ISRAM0_SB0CR,r1 +	lis     r1,0x0001 +	ori     r1,r1,0x0B84            /* second 128k */ +	mtdcr   ISRAM0_SB1CR,r1 +	lis     r1, 0x0002 +	ori     r1,r1, 0x0B84           /* third 128k */ +	mtdcr   ISRAM0_SB2CR,r1 +	lis     r1, 0x0003 +	ori     r1,r1, 0x0B84           /* fourth 128k */ +	mtdcr   ISRAM0_SB3CR,r1 +#elif defined(CONFIG_440GP) +	ori	r1,r1,0x0380		/* 8k rw */ +	mtdcr	ISRAM0_SB0CR,r1 +	mtdcr	ISRAM0_SB1CR,r0		/* Disable bank 1 */ +#endif +#endif /* #if !defined(CONFIG_440EP) && !defined(CONFIG_440GR) */ + +	/*----------------------------------------------------------------*/ +	/* Setup the stack in internal SRAM */ +	/*----------------------------------------------------------------*/ +	lis	r1,CONFIG_SYS_INIT_RAM_ADDR@h +	ori	r1,r1,CONFIG_SYS_INIT_SP_OFFSET@l +	li	r0,0 +	stwu	r0,-4(r1) +	stwu	r0,-4(r1)		/* Terminate call chain */ + +	stwu	r1,-8(r1)		/* Save back chain and move SP */ +	lis	r0,RESET_VECTOR@h	/* Address of reset vector */ +	ori	r0,r0, RESET_VECTOR@l +	stwu	r1,-8(r1)		/* Save back chain and move SP */ +	stw	r0,+12(r1)		/* Save return addr (underflow vect) */ + +#ifndef CONFIG_SPL_BUILD +	GET_GOT +#endif + +	bl	cpu_init_f	/* run low-level CPU init code	   (from Flash) */ +	bl	board_init_f +	/* NOTREACHED - board_init_f() does not return */ + +#endif /* CONFIG_440 */ + +/*****************************************************************************/ +#if defined(CONFIG_405GP) || \ +    defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \ +    defined(CONFIG_405EX) || defined(CONFIG_405) +	/*----------------------------------------------------------------------- */ +	/* Clear and set up some registers. */ +	/*----------------------------------------------------------------------- */ +	addi	r4,r0,0x0000 +#if !defined(CONFIG_405EX) +	mtspr	SPRN_SGR,r4 +#else +	/* +	 * On 405EX, completely clearing the SGR leads to PPC hangup +	 * upon PCIe configuration access. The PCIe memory regions +	 * need to be guarded! +	 */ +	lis	r3,0x0000 +	ori	r3,r3,0x7FFC +	mtspr	SPRN_SGR,r3 +#endif +	mtspr	SPRN_DCWR,r4 +	mtesr	r4			/* clear Exception Syndrome Reg */ +	mttcr	r4			/* clear Timer Control Reg */ +	mtxer	r4			/* clear Fixed-Point Exception Reg */ +	mtevpr	r4			/* clear Exception Vector Prefix Reg */ +	addi	r4,r0,(0xFFFF-0x10000)		/* set r4 to 0xFFFFFFFF (status in the */ +					/* dbsr is cleared by setting bits to 1) */ +	mtdbsr	r4			/* clear/reset the dbsr */ + +	/* Invalidate the i- and d-caches. */ +	bl	invalidate_icache +	bl	invalidate_dcache + +	/* Set-up icache cacheability. */ +	lis	r4, CONFIG_SYS_ICACHE_SACR_VALUE@h +	ori	r4, r4, CONFIG_SYS_ICACHE_SACR_VALUE@l +	mticcr	r4 +	isync + +	/* Set-up dcache cacheability. */ +	lis	r4, CONFIG_SYS_DCACHE_SACR_VALUE@h +	ori	r4, r4, CONFIG_SYS_DCACHE_SACR_VALUE@l +	mtdccr	r4 + +#if !(defined(CONFIG_SYS_EBC_PB0AP) && defined(CONFIG_SYS_EBC_PB0CR))\ +				&& !defined (CONFIG_XILINX_405) +	/*----------------------------------------------------------------------- */ +	/* Tune the speed and size for flash CS0  */ +	/*----------------------------------------------------------------------- */ +	bl	ext_bus_cntlr_init +#endif + +#if !(defined(CONFIG_SYS_INIT_DCACHE_CS) || defined(CONFIG_SYS_TEMP_STACK_OCM)) +	/* +	 * For boards that don't have OCM and can't use the data cache +	 * for their primordial stack, setup stack here directly after the +	 * SDRAM is initialized in ext_bus_cntlr_init. +	 */ +	lis	r1, CONFIG_SYS_INIT_RAM_ADDR@h +	ori	r1,r1,CONFIG_SYS_INIT_SP_OFFSET /* set up the stack in SDRAM */ + +	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	*/ +	/* +	 * Set up a dummy frame to store reset vector as return address. +	 * this causes stack underflow to reset board. +	 */ +	stwu	r1, -8(r1)		/* Save back chain and move SP */ +	lis	r0, RESET_VECTOR@h	/* Address of reset vector */ +	ori	r0, r0, RESET_VECTOR@l +	stwu	r1, -8(r1)		/* Save back chain and move SP */ +	stw	r0, +12(r1)		/* Save return addr (underflow vect) */ +#endif /* !(CONFIG_SYS_INIT_DCACHE_CS	|| !CONFIG_SYS_TEM_STACK_OCM) */ + +#if defined(CONFIG_405EP) +	/*----------------------------------------------------------------------- */ +	/* DMA Status, clear to come up clean */ +	/*----------------------------------------------------------------------- */ +	addis	r3,r0, 0xFFFF		/* Clear all existing DMA status */ +	ori	r3,r3, 0xFFFF +	mtdcr	DMASR, r3 + +	bl	ppc405ep_init		/* do ppc405ep specific init */ +#endif /* CONFIG_405EP */ + +#if defined(CONFIG_SYS_OCM_DATA_ADDR) && defined(CONFIG_SYS_OCM_DATA_SIZE) +#if defined(CONFIG_405EZ) +	/******************************************************************** +	 * Setup OCM - On Chip Memory - PPC405EZ uses OCM Controller V2 +	 *******************************************************************/ +	/* +	 * We can map the OCM on the PLB3, so map it at +	 * CONFIG_SYS_OCM_DATA_ADDR + 0x8000 +	 */ +	lis	r3,CONFIG_SYS_OCM_DATA_ADDR@h	/* OCM location */ +	ori	r3,r3,CONFIG_SYS_OCM_DATA_ADDR@l +	ori	r3,r3,0x0270		/* 16K for Bank 1, R/W/Enable */ +	mtdcr	OCM0_PLBCR1,r3		/* Set PLB Access */ +	ori	r3,r3,0x4000		/* Add 0x4000 for bank 2 */ +	mtdcr	OCM0_PLBCR2,r3		/* Set PLB Access */ +	isync + +	lis	r3,CONFIG_SYS_OCM_DATA_ADDR@h	/* OCM location */ +	ori	r3,r3,CONFIG_SYS_OCM_DATA_ADDR@l +	ori	r3,r3,0x0270		/* 16K for Bank 1, R/W/Enable */ +	mtdcr	OCM0_DSRC1, r3		/* Set Data Side */ +	mtdcr	OCM0_ISRC1, r3		/* Set Instruction Side */ +	ori	r3,r3,0x4000		/* Add 0x4000 for bank 2 */ +	mtdcr	OCM0_DSRC2, r3		/* Set Data Side */ +	mtdcr	OCM0_ISRC2, r3		/* Set Instruction Side */ +	addis	r3,0,0x0800		/* OCM Data Parity Disable - 1 Wait State */ +	mtdcr	OCM0_DISDPC,r3 + +	isync +#else /* CONFIG_405EZ */ +	/******************************************************************** +	 * Setup OCM - On Chip Memory +	 *******************************************************************/ +	/* Setup OCM */ +	lis	r0, 0x7FFF +	ori	r0, r0, 0xFFFF +	mfdcr	r3, OCM0_ISCNTL		/* get instr-side IRAM config */ +	mfdcr	r4, OCM0_DSCNTL		/* get data-side IRAM config */ +	and	r3, r3, r0		/* disable data-side IRAM */ +	and	r4, r4, r0		/* disable data-side IRAM */ +	mtdcr	OCM0_ISCNTL, r3		/* set instr-side IRAM config */ +	mtdcr	OCM0_DSCNTL, r4		/* set data-side IRAM config */ +	isync + +	lis	r3,CONFIG_SYS_OCM_DATA_ADDR@h	/* OCM location */ +	ori	r3,r3,CONFIG_SYS_OCM_DATA_ADDR@l +	mtdcr	OCM0_DSARC, r3 +	addis	r4, 0, 0xC000		/* OCM data area enabled */ +	mtdcr	OCM0_DSCNTL, r4 +	isync +#endif /* CONFIG_405EZ */ +#endif + +	/*----------------------------------------------------------------------- */ +	/* Setup temporary stack in DCACHE or OCM if needed for SDRAM SPD. */ +	/*----------------------------------------------------------------------- */ +#ifdef CONFIG_SYS_INIT_DCACHE_CS +	li	r4, PBxAP +	mtdcr	EBC0_CFGADDR, r4 +	lis	r4, CONFIG_SYS_INIT_DCACHE_PBxAR@h +	ori	r4, r4, CONFIG_SYS_INIT_DCACHE_PBxAR@l +	mtdcr	EBC0_CFGDATA, r4 + +	addi	r4, 0, PBxCR +	mtdcr	EBC0_CFGADDR, r4 +	lis	r4, CONFIG_SYS_INIT_DCACHE_PBxCR@h +	ori	r4, r4, CONFIG_SYS_INIT_DCACHE_PBxCR@l +	mtdcr	EBC0_CFGDATA, r4 + +	/* +	 * Enable the data cache for the 128MB storage access control region +	 * at CONFIG_SYS_INIT_RAM_ADDR. +	 */ +	mfdccr	r4 +	oris	r4, r4, PPC_128MB_SACR_VALUE(CONFIG_SYS_INIT_RAM_ADDR)@h +	ori	r4, r4, PPC_128MB_SACR_VALUE(CONFIG_SYS_INIT_RAM_ADDR)@l +	mtdccr	r4 + +	/* +	 * Preallocate data cache lines to be used to avoid a subsequent +	 * cache miss and an ensuing machine check exception when exceptions +	 * are enabled. +	 */ +	li	r0, 0 + +	lis	r3, CONFIG_SYS_INIT_RAM_ADDR@h +	ori	r3, r3, CONFIG_SYS_INIT_RAM_ADDR@l + +	lis	r4, CONFIG_SYS_INIT_RAM_SIZE@h +	ori	r4, r4, CONFIG_SYS_INIT_RAM_SIZE@l + +	/* +	 * Convert the size, in bytes, to the number of cache lines/blocks +	 * to preallocate. +	 */ +	clrlwi. r5, r4, (32 - L1_CACHE_SHIFT) +	srwi	r5, r4, L1_CACHE_SHIFT +	beq	..load_counter +	addi	r5, r5, 0x0001 +..load_counter: +	mtctr	r5 + +	/* Preallocate the computed number of cache blocks. */ +..alloc_dcache_block: +	dcba	r0, r3 +	addi	r3, r3, L1_CACHE_BYTES +	bdnz	..alloc_dcache_block +	sync + +	/* +	 * Load the initial stack pointer and data area and convert the size, +	 * in bytes, to the number of words to initialize to a known value. +	 */ +	lis	r1, CONFIG_SYS_INIT_RAM_ADDR@h +	ori	r1, r1, CONFIG_SYS_INIT_SP_OFFSET@l + +	lis	r4, (CONFIG_SYS_INIT_RAM_SIZE >> 2)@h +	ori	r4, r4, (CONFIG_SYS_INIT_RAM_SIZE >> 2)@l +	mtctr	r4 + +	lis	r2, CONFIG_SYS_INIT_RAM_ADDR@h +	ori	r2, r2, CONFIG_SYS_INIT_RAM_SIZE@l + +	lis	r4, CONFIG_SYS_INIT_RAM_PATTERN@h +	ori	r4, r4, CONFIG_SYS_INIT_RAM_PATTERN@l + +..stackloop: +	stwu	r4, -4(r2) +	bdnz	..stackloop + +	/* +	 * Make room for stack frame header and clear final stack frame so +	 * that stack backtraces terminate cleanly. +	 */ +	stwu	r0, -4(r1) +	stwu	r0, -4(r1) + +	/* +	 * Set up a dummy frame to store reset vector as return address. +	 * this causes stack underflow to reset board. +	 */ +	stwu	r1, -8(r1)		/* Save back chain and move SP */ +	addis	r0, 0, RESET_VECTOR@h	/* Address of reset vector */ +	ori	r0, r0, RESET_VECTOR@l +	stwu	r1, -8(r1)		/* Save back chain and move SP */ +	stw	r0, +12(r1)		/* Save return addr (underflow vect) */ + +#elif defined(CONFIG_SYS_TEMP_STACK_OCM) && \ +	(defined(CONFIG_SYS_OCM_DATA_ADDR) && defined(CONFIG_SYS_OCM_DATA_SIZE)) +	/* +	 * Stack in OCM. +	 */ + +	/* Set up Stack at top of OCM */ +	lis	r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)@h +	ori	r1, r1, (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)@l + +	/* Set up a zeroized stack frame so that backtrace works right */ +	li	r0, 0 +	stwu	r0, -4(r1) +	stwu	r0, -4(r1) + +	/* +	 * Set up a dummy frame to store reset vector as return address. +	 * this causes stack underflow to reset board. +	 */ +	stwu	r1, -8(r1)		/* Save back chain and move SP */ +	lis	r0, RESET_VECTOR@h	/* Address of reset vector */ +	ori	r0, r0, RESET_VECTOR@l +	stwu	r1, -8(r1)		/* Save back chain and move SP */ +	stw	r0, +12(r1)		/* Save return addr (underflow vect) */ +#endif /* CONFIG_SYS_INIT_DCACHE_CS */ + +	GET_GOT			/* initialize GOT access			*/ + +	bl	cpu_init_f	/* run low-level CPU init code	   (from Flash) */ + +	bl	board_init_f	/* run first part of init code (from Flash)	*/ +	/* NOTREACHED - board_init_f() does not return */ + +#endif	/* CONFIG_405GP || CONFIG_405 || CONFIG_405EP */ +	/*----------------------------------------------------------------------- */ + + +#if !defined(CONFIG_SPL_BUILD) +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. + * Register r21 is pointer into trap frame, r1 has new stack pointer. + */ +	.globl	transfer_to_handler +transfer_to_handler: +	stw	r22,_NIP(r21) +	lis	r22,MSR_POW@h +	andc	r23,r23,r22 +	stw	r23,_MSR(r21) +	SAVE_GPR(7, r21) +	SAVE_4GPRS(8, r21) +	SAVE_8GPRS(12, r21) +	SAVE_8GPRS(24, r21) +	mflr	r23 +	andi.	r24,r23,0x3f00		/* get vector offset */ +	stw	r24,TRAP(r21) +	li	r22,0 +	stw	r22,RESULT(r21) +	mtspr	SPRG2,r22		/* r1 is now kernel sp */ +	lwz	r24,0(r23)		/* virtual address of handler */ +	lwz	r23,4(r23)		/* where to go when done */ +	mtspr	SRR0,r24 +	mtspr	SRR1,r20 +	mtlr	r23 +	SYNC +	rfi				/* jump to handler, enable MMU */ + +int_return: +	mfmsr	r28		/* Disable interrupts */ +	li	r4,0 +	ori	r4,r4,MSR_EE +	andc	r28,r28,r4 +	SYNC			/* Some chip revs need this... */ +	mtmsr	r28 +	SYNC +	lwz	r2,_CTR(r1) +	lwz	r0,_LINK(r1) +	mtctr	r2 +	mtlr	r0 +	lwz	r2,_XER(r1) +	lwz	r0,_CCR(r1) +	mtspr	XER,r2 +	mtcrf	0xFF,r0 +	REST_10GPRS(3, r1) +	REST_10GPRS(13, r1) +	REST_8GPRS(23, r1) +	REST_GPR(31, r1) +	lwz	r2,_NIP(r1)	/* Restore environment */ +	lwz	r0,_MSR(r1) +	mtspr	SRR0,r2 +	mtspr	SRR1,r0 +	lwz	r0,GPR0(r1) +	lwz	r2,GPR2(r1) +	lwz	r1,GPR1(r1) +	SYNC +	rfi + +crit_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	SPRN_CSRR0,r2 +	mtspr	SPRN_CSRR1,r0 +	lwz	r0,GPR0(r1) +	lwz	r2,GPR2(r1) +	lwz	r1,GPR1(r1) +	SYNC +	rfci + +#ifdef CONFIG_440 +mck_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	SPRN_MCSRR0,r2 +	mtspr	SPRN_MCSRR1,r0 +	lwz	r0,GPR0(r1) +	lwz	r2,GPR2(r1) +	lwz	r1,GPR1(r1) +	SYNC +	rfmci +#endif /* CONFIG_440 */ + + +	.globl get_pvr +get_pvr: +	mfspr	r3, PVR +	blr + +/*------------------------------------------------------------------------------- */ +/* Function:	 out16 */ +/* Description:	 Output 16 bits */ +/*------------------------------------------------------------------------------- */ +	.globl	out16 +out16: +	sth	r4,0x0000(r3) +	blr + +/*------------------------------------------------------------------------------- */ +/* Function:	 out16r */ +/* Description:	 Byte reverse and output 16 bits */ +/*------------------------------------------------------------------------------- */ +	.globl	out16r +out16r: +	sthbrx	r4,r0,r3 +	blr + +/*------------------------------------------------------------------------------- */ +/* Function:	 out32r */ +/* Description:	 Byte reverse and output 32 bits */ +/*------------------------------------------------------------------------------- */ +	.globl	out32r +out32r: +	stwbrx	r4,r0,r3 +	blr + +/*------------------------------------------------------------------------------- */ +/* Function:	 in16 */ +/* Description:	 Input 16 bits */ +/*------------------------------------------------------------------------------- */ +	.globl	in16 +in16: +	lhz	r3,0x0000(r3) +	blr + +/*------------------------------------------------------------------------------- */ +/* Function:	 in16r */ +/* Description:	 Input 16 bits and byte reverse */ +/*------------------------------------------------------------------------------- */ +	.globl	in16r +in16r: +	lhbrx	r3,r0,r3 +	blr + +/*------------------------------------------------------------------------------- */ +/* Function:	 in32r */ +/* Description:	 Input 32 bits and byte reverse */ +/*------------------------------------------------------------------------------- */ +	.globl	in32r +in32r: +	lwbrx	r3,r0,r3 +	blr + +#if !defined(CONFIG_SPL_BUILD) +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r3 = Relocated stack pointer + * r4 = Relocated global data pointer + * r5 = Relocated text pointer + */ +	.globl	relocate_code +relocate_code: +#if defined(CONFIG_4xx_DCACHE) || defined(CONFIG_SYS_INIT_DCACHE_CS) +	/* +	 * We need to flush the initial global data (gd_t) and bd_info +	 * before the dcache will be invalidated. +	 */ + +	/* Save registers */ +	mr	r9, r3 +	mr	r10, r4 +	mr	r11, r5 + +	/* +	 * Flush complete dcache, this is faster than flushing the +	 * ranges for global_data and bd_info instead. +	 */ +	bl	flush_dcache + +#if defined(CONFIG_SYS_INIT_DCACHE_CS) +	/* +	 * Undo the earlier data cache set-up for the primordial stack and +	 * data area. First, invalidate the data cache and then disable data +	 * cacheability for that area. Finally, restore the EBC values, if +	 * any. +	 */ + +	/* Invalidate the primordial stack and data area in cache */ +	lis	r3, CONFIG_SYS_INIT_RAM_ADDR@h +	ori	r3, r3, CONFIG_SYS_INIT_RAM_ADDR@l + +	lis	r4, CONFIG_SYS_INIT_RAM_SIZE@h +	ori	r4, r4, CONFIG_SYS_INIT_RAM_SIZE@l +	add	r4, r4, r3 + +	bl	invalidate_dcache_range + +	/* Disable cacheability for the region */ +	mfdccr	r3 +	lis     r4, ~PPC_128MB_SACR_VALUE(CONFIG_SYS_INIT_RAM_ADDR)@h +	ori     r4, r4, ~PPC_128MB_SACR_VALUE(CONFIG_SYS_INIT_RAM_ADDR)@l +	and     r3, r3, r4 +	mtdccr  r3 + +	/* Restore the EBC parameters */ +	li	r3, PBxAP +	mtdcr	EBC0_CFGADDR, r3 +	lis	r3, PBxAP_VAL@h +	ori	r3, r3, PBxAP_VAL@l +	mtdcr	EBC0_CFGDATA, r3 + +	li	r3, PBxCR +	mtdcr	EBC0_CFGADDR, r3 +	lis	r3, PBxCR_VAL@h +	ori	r3, r3, PBxCR_VAL@l +	mtdcr	EBC0_CFGDATA, r3 +#endif /* defined(CONFIG_SYS_INIT_DCACHE_CS) */ + +	/* Restore registers */ +	mr	r3, r9 +	mr	r4, r10 +	mr	r5, r11 +#endif /* defined(CONFIG_4xx_DCACHE) || defined(CONFIG_SYS_INIT_DCACHE_CS) */ + +#ifdef CONFIG_SYS_INIT_RAM_DCACHE +	/* +	 * Unlock the previously locked d-cache +	 */ +	msync +	isync +	/* set TFLOOR/NFLOOR to 0 again */ +	lis	r6,0x0001 +	ori	r6,r6,0xf800 +	mtspr	SPRN_DVLIM,r6 +	lis	r6,0x0000 +	ori	r6,r6,0x0000 +	mtspr	SPRN_DNV0,r6 +	mtspr	SPRN_DNV1,r6 +	mtspr	SPRN_DNV2,r6 +	mtspr	SPRN_DNV3,r6 +	mtspr	SPRN_DTV0,r6 +	mtspr	SPRN_DTV1,r6 +	mtspr	SPRN_DTV2,r6 +	mtspr	SPRN_DTV3,r6 +	msync +	isync + +	/* Invalidate data cache, now no longer our stack */ +	dccci	0,0 +	sync +	isync +#endif /* CONFIG_SYS_INIT_RAM_DCACHE */ + +	/* +	 * On some 440er platforms the cache is enabled in the first TLB (Boot-CS) +	 * to speed up the boot process. Now this cache needs to be disabled. +	 */ +#if defined(CONFIG_440) +	/* Clear all potential pending exceptions */ +	mfspr	r1,SPRN_MCSR +	mtspr	SPRN_MCSR,r1 +	addi	r1,r0,CONFIG_SYS_TLB_FOR_BOOT_FLASH	/* Use defined TLB */ +	tlbre	r0,r1,0x0002		/* Read contents */ +	ori	r0,r0,0x0c00		/* Or in the inhibit, write through bit */ +	tlbwe	r0,r1,0x0002		/* Save it out */ +	sync +	isync +#endif /* defined(CONFIG_440) */ +	mr	r1,  r3		/* Set new stack pointer		*/ +	mr	r9,  r4		/* Save copy of Init Data pointer	*/ +	mr	r10, r5		/* Save copy of Destination Address	*/ + +	GET_GOT +	mr	r3,  r5				/* Destination Address	*/ +	lis	r4, CONFIG_SYS_MONITOR_BASE@h		/* Source      Address	*/ +	ori	r4, r4, CONFIG_SYS_MONITOR_BASE@l +	lwz	r5, GOT(__init_end) +	sub	r5, r5, r4 +	li	r6, L1_CACHE_BYTES		/* Cache Line Size	*/ + +	/* +	 * Fix GOT pointer: +	 * +	 * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address +	 * +	 * Offset: +	 */ +	sub	r15, r10, r4 + +	/* First our own GOT */ +	add	r12, r12, r15 +	/* then the one used by the C code */ +	add	r30, r30, r15 + +	/* +	 * Now relocate code +	 */ + +	cmplw	cr1,r3,r4 +	addi	r0,r5,3 +	srwi.	r0,r0,2 +	beq	cr1,4f		/* In place copy is not necessary	*/ +	beq	7f		/* Protect against 0 count		*/ +	mtctr	r0 +	bge	cr1,2f + +	la	r8,-4(r4) +	la	r7,-4(r3) +1:	lwzu	r0,4(r8) +	stwu	r0,4(r7) +	bdnz	1b +	b	4f + +2:	slwi	r0,r0,2 +	add	r8,r4,r0 +	add	r7,r3,r0 +3:	lwzu	r0,-4(r8) +	stwu	r0,-4(r7) +	bdnz	3b + +/* + * Now flush the cache: note that we must start from a cache aligned + * address. Otherwise we might miss one cache line. + */ +4:	cmpwi	r6,0 +	add	r5,r3,r5 +	beq	7f		/* Always flush prefetch queue in any case */ +	subi	r0,r6,1 +	andc	r3,r3,r0 +	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 + _START_OFFSET +	mtlr	r0 +	blr				/* NEVER RETURNS! */ + +in_ram: + +	/* +	 * Relocation Function, r12 point to got2+0x8000 +	 * +	 * Adjust got2 pointers, no need to check for 0, this code +	 * already puts a few entries in the table. +	 */ +	li	r0,__got2_entries@sectoff@l +	la	r3,GOT(_GOT2_TABLE_) +	lwz	r11,GOT(_GOT2_TABLE_) +	mtctr	r0 +	sub	r11,r3,r11 +	addi	r3,r3,-4 +1:	lwzu	r0,4(r3) +	cmpwi	r0,0 +	beq-	2f +	add	r0,r0,r11 +	stw	r0,0(r3) +2:	bdnz	1b + +	/* +	 * Now adjust the fixups and the pointers to the fixups +	 * in case we need to move ourselves again. +	 */ +	li	r0,__fixup_entries@sectoff@l +	lwz	r3,GOT(_FIXUP_TABLE_) +	cmpwi	r0,0 +	mtctr	r0 +	addi	r3,r3,-4 +	beq	4f +3:	lwzu	r4,4(r3) +	lwzux	r0,r4,r11 +	cmpwi	r0,0 +	add	r0,r0,r11 +	stw	r4,0(r3) +	beq-	5f +	stw	r0,0(r4) +5:	bdnz	3b +4: +clear_bss: +	/* +	 * Now clear BSS segment +	 */ +	lwz	r3,GOT(__bss_start) +	lwz	r4,GOT(__bss_end) + +	cmplw	0, r3, r4 +	beq	7f + +	li	r0, 0 + +	andi.	r5, r4, 3 +	beq	6f +	sub	r4, r4, r5 +	mtctr	r5 +	mr	r5, r4 +5:	stb	r0, 0(r5) +	addi	r5, r5, 1 +	bdnz	5b +6: +	stw	r0, 0(r3) +	addi	r3, r3, 4 +	cmplw	0, r3, r4 +	bne	6b + +7: +	mr	r3, r9		/* Init Data pointer		*/ +	mr	r4, r10		/* Destination Address		*/ +	bl	board_init_r + +	/* +	 * Copy exception vector code to low memory +	 * +	 * r3: dest_addr +	 * r7: source address, r8: end address, r9: target address +	 */ +	.globl	trap_init +trap_init: +	mflr	r4			/* save link register		*/ +	GET_GOT +	lwz	r7, GOT(_start_of_vectors) +	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 + _START_OFFSET +	li	r8, Alignment - _start + _START_OFFSET +2: +	bl	trap_reloc +	addi	r7, r7, 0x100		/* next exception vector */ +	cmplw	0, r7, r8 +	blt	2b + +	li	r7, .L_Alignment - _start + _START_OFFSET +	bl	trap_reloc + +	li	r7, .L_ProgramCheck - _start + _START_OFFSET +	bl	trap_reloc + +#ifdef CONFIG_440 +	li	r7, .L_FPUnavailable - _start + _START_OFFSET +	bl	trap_reloc + +	li	r7, .L_Decrementer - _start + _START_OFFSET +	bl	trap_reloc + +	li	r7, .L_APU - _start + _START_OFFSET +	bl	trap_reloc + +	li	r7, .L_InstructionTLBError - _start + _START_OFFSET +	bl	trap_reloc + +	li	r7, .L_DataTLBError - _start + _START_OFFSET +	bl	trap_reloc +#else /* CONFIG_440 */ +	li	r7, .L_PIT - _start + _START_OFFSET +	bl	trap_reloc + +	li	r7, .L_InstructionTLBMiss - _start + _START_OFFSET +	bl	trap_reloc + +	li	r7, .L_DataTLBMiss - _start + _START_OFFSET +	bl	trap_reloc +#endif /* CONFIG_440 */ + +	li	r7, .L_DebugBreakpoint - _start + _START_OFFSET +	bl	trap_reloc + +#if !defined(CONFIG_440) +	addi	r7,r0,0x1000		/* set ME bit (Machine Exceptions) */ +	oris	r7,r7,0x0002		/* set CE bit (Critical Exceptions) */ +	mtmsr	r7			/* change MSR */ +#else +	bl	__440_msr_set +	b	__440_msr_continue + +__440_msr_set: +	addi	r7,r0,0x1000		/* set ME bit (Machine Exceptions) */ +	oris	r7,r7,0x0002		/* set CE bit (Critical Exceptions) */ +	mtspr	SPRN_SRR1,r7 +	mflr	r7 +	mtspr	SPRN_SRR0,r7 +	rfi +__440_msr_continue: +#endif + +	mtlr	r4			/* restore link register	*/ +	blr +#endif /* CONFIG_SPL_BUILD */ + +#if defined(CONFIG_440) +/*----------------------------------------------------------------------------+ +| dcbz_area. ++----------------------------------------------------------------------------*/ +	function_prolog(dcbz_area) +	rlwinm. r5,r4,0,27,31 +	rlwinm	r5,r4,27,5,31 +	beq	..d_ra2 +	addi	r5,r5,0x0001 +..d_ra2:mtctr	r5 +..d_ag2:dcbz	r0,r3 +	addi	r3,r3,32 +	bdnz	..d_ag2 +	sync +	blr +	function_epilog(dcbz_area) +#endif /* CONFIG_440 */ +#endif /* CONFIG_SPL_BUILD */ + +/*------------------------------------------------------------------------------- */ +/* Function:	 in8 */ +/* Description:	 Input 8 bits */ +/*------------------------------------------------------------------------------- */ +	.globl	in8 +in8: +	lbz	r3,0x0000(r3) +	blr + +/*------------------------------------------------------------------------------- */ +/* Function:	 out8 */ +/* Description:	 Output 8 bits */ +/*------------------------------------------------------------------------------- */ +	.globl	out8 +out8: +	stb	r4,0x0000(r3) +	blr + +/*------------------------------------------------------------------------------- */ +/* Function:	 out32 */ +/* Description:	 Output 32 bits */ +/*------------------------------------------------------------------------------- */ +	.globl	out32 +out32: +	stw	r4,0x0000(r3) +	blr + +/*------------------------------------------------------------------------------- */ +/* Function:	 in32 */ +/* Description:	 Input 32 bits */ +/*------------------------------------------------------------------------------- */ +	.globl	in32 +in32: +	lwz	3,0x0000(3) +	blr + +/**************************************************************************/ +/* PPC405EP specific stuff						  */ +/**************************************************************************/ +#ifdef CONFIG_405EP +ppc405ep_init: + +#ifdef CONFIG_BUBINGA +	/* +	 * Initialize EBC chip selects 1 & 4 and GPIO pins (for alternate +	 * function) to support FPGA and NVRAM accesses below. +	 */ + +	lis	r3,GPIO0_OSRH@h		/* config GPIO output select */ +	ori	r3,r3,GPIO0_OSRH@l +	lis	r4,CONFIG_SYS_GPIO0_OSRH@h +	ori	r4,r4,CONFIG_SYS_GPIO0_OSRH@l +	stw	r4,0(r3) +	lis	r3,GPIO0_OSRL@h +	ori	r3,r3,GPIO0_OSRL@l +	lis	r4,CONFIG_SYS_GPIO0_OSRL@h +	ori	r4,r4,CONFIG_SYS_GPIO0_OSRL@l +	stw	r4,0(r3) + +	lis	r3,GPIO0_ISR1H@h	/* config GPIO input select */ +	ori	r3,r3,GPIO0_ISR1H@l +	lis	r4,CONFIG_SYS_GPIO0_ISR1H@h +	ori	r4,r4,CONFIG_SYS_GPIO0_ISR1H@l +	stw	r4,0(r3) +	lis	r3,GPIO0_ISR1L@h +	ori	r3,r3,GPIO0_ISR1L@l +	lis	r4,CONFIG_SYS_GPIO0_ISR1L@h +	ori	r4,r4,CONFIG_SYS_GPIO0_ISR1L@l +	stw	r4,0(r3) + +	lis	r3,GPIO0_TSRH@h		/* config GPIO three-state select */ +	ori	r3,r3,GPIO0_TSRH@l +	lis	r4,CONFIG_SYS_GPIO0_TSRH@h +	ori	r4,r4,CONFIG_SYS_GPIO0_TSRH@l +	stw	r4,0(r3) +	lis	r3,GPIO0_TSRL@h +	ori	r3,r3,GPIO0_TSRL@l +	lis	r4,CONFIG_SYS_GPIO0_TSRL@h +	ori	r4,r4,CONFIG_SYS_GPIO0_TSRL@l +	stw	r4,0(r3) + +	lis	r3,GPIO0_TCR@h		/* config GPIO driver output enables */ +	ori	r3,r3,GPIO0_TCR@l +	lis	r4,CONFIG_SYS_GPIO0_TCR@h +	ori	r4,r4,CONFIG_SYS_GPIO0_TCR@l +	stw	r4,0(r3) + +	li	r3,PB1AP		/* program EBC bank 1 for RTC access */ +	mtdcr	EBC0_CFGADDR,r3 +	lis	r3,CONFIG_SYS_EBC_PB1AP@h +	ori	r3,r3,CONFIG_SYS_EBC_PB1AP@l +	mtdcr	EBC0_CFGDATA,r3 +	li	r3,PB1CR +	mtdcr	EBC0_CFGADDR,r3 +	lis	r3,CONFIG_SYS_EBC_PB1CR@h +	ori	r3,r3,CONFIG_SYS_EBC_PB1CR@l +	mtdcr	EBC0_CFGDATA,r3 + +	li	r3,PB1AP		/* program EBC bank 1 for RTC access */ +	mtdcr	EBC0_CFGADDR,r3 +	lis	r3,CONFIG_SYS_EBC_PB1AP@h +	ori	r3,r3,CONFIG_SYS_EBC_PB1AP@l +	mtdcr	EBC0_CFGDATA,r3 +	li	r3,PB1CR +	mtdcr	EBC0_CFGADDR,r3 +	lis	r3,CONFIG_SYS_EBC_PB1CR@h +	ori	r3,r3,CONFIG_SYS_EBC_PB1CR@l +	mtdcr	EBC0_CFGDATA,r3 + +	li	r3,PB4AP		/* program EBC bank 4 for FPGA access */ +	mtdcr	EBC0_CFGADDR,r3 +	lis	r3,CONFIG_SYS_EBC_PB4AP@h +	ori	r3,r3,CONFIG_SYS_EBC_PB4AP@l +	mtdcr	EBC0_CFGDATA,r3 +	li	r3,PB4CR +	mtdcr	EBC0_CFGADDR,r3 +	lis	r3,CONFIG_SYS_EBC_PB4CR@h +	ori	r3,r3,CONFIG_SYS_EBC_PB4CR@l +	mtdcr	EBC0_CFGDATA,r3 +#endif + +	/* +	!----------------------------------------------------------------------- +	! Check to see if chip is in bypass mode. +	! If so, write stored CPC0_PLLMR0 and CPC0_PLLMR1 values and perform a +	! CPU reset   Otherwise, skip this step and keep going. +	! Note:	 Running BIOS in bypass mode is not supported since PLB speed +	!	 will not be fast enough for the SDRAM (min 66MHz) +	!----------------------------------------------------------------------- +	*/ +	mfdcr	r5, CPC0_PLLMR1 +	rlwinm	r4,r5,1,0x1		/* get system clock source (SSCS) */ +	cmpi	cr0,0,r4,0x1 + +	beq    pll_done			/* if SSCS =b'1' then PLL has */ +					/* already been set */ +					/* and CPU has been reset */ +					/* so skip to next section */ + +#ifdef CONFIG_BUBINGA +	/* +	!----------------------------------------------------------------------- +	! Read NVRAM to get value to write in PLLMR. +	! If value has not been correctly saved, write default value +	! Default config values (assuming on-board 33MHz SYS_CLK) are above. +	! See CPU_DEFAULT_200 and CPU_DEFAULT_266 above. +	! +	! WARNING:  This code assumes the first three words in the nvram_t +	!	    structure in openbios.h.  Changing the beginning of +	!	    the structure will break this code. +	! +	!----------------------------------------------------------------------- +	*/ +	addis	r3,0,NVRAM_BASE@h +	addi	r3,r3,NVRAM_BASE@l + +	lwz	r4, 0(r3) +	addis	r5,0,NVRVFY1@h +	addi	r5,r5,NVRVFY1@l +	cmp	cr0,0,r4,r5		/* Compare 1st NVRAM Magic number*/ +	bne	..no_pllset +	addi	r3,r3,4 +	lwz	r4, 0(r3) +	addis	r5,0,NVRVFY2@h +	addi	r5,r5,NVRVFY2@l +	cmp	cr0,0,r4,r5		/* Compare 2 NVRAM Magic number */ +	bne	..no_pllset +	addi	r3,r3,8			/* Skip over conf_size */ +	lwz	r4, 4(r3)		/* Load PLLMR1 value from NVRAM */ +	lwz	r3, 0(r3)		/* Load PLLMR0 value from NVRAM */ +	rlwinm	r5,r4,1,0x1		/* get system clock source (SSCS) */ +	cmpi	 cr0,0,r5,1		/* See if PLL is locked */ +	beq	pll_write +..no_pllset: +#endif /* CONFIG_BUBINGA */ + +#ifdef CONFIG_TAIHU +	mfdcr	r4, CPC0_BOOT +	andi.	r5, r4, CPC0_BOOT_SEP@l +	bne	strap_1			/* serial eeprom present */ +	addis	r5,0,CPLD_REG0_ADDR@h +	ori	r5,r5,CPLD_REG0_ADDR@l +	andi.	r5, r5, 0x10 +	bne	_pci_66mhz +#endif /* CONFIG_TAIHU */ + +#if defined(CONFIG_ZEUS) +	mfdcr	r4, CPC0_BOOT +	andi.	r5, r4, CPC0_BOOT_SEP@l +	bne	strap_1			/* serial eeprom present */ +	lis	r3,0x0000 +	addi	r3,r3,0x3030 +	lis	r4,0x8042 +	addi	r4,r4,0x223e +	b	1f +strap_1: +	mfdcr	r3, CPC0_PLLMR0 +	mfdcr	r4, CPC0_PLLMR1 +	b	1f +#endif + +	addis	r3,0,PLLMR0_DEFAULT@h	/* PLLMR0 default value */ +	ori	r3,r3,PLLMR0_DEFAULT@l	/* */ +	addis	r4,0,PLLMR1_DEFAULT@h	/* PLLMR1 default value */ +	ori	r4,r4,PLLMR1_DEFAULT@l	/* */ + +#ifdef CONFIG_TAIHU +	b	1f +_pci_66mhz: +	addis	r3,0,PLLMR0_DEFAULT_PCI66@h +	ori	r3,r3,PLLMR0_DEFAULT_PCI66@l +	addis	r4,0,PLLMR1_DEFAULT_PCI66@h +	ori	r4,r4,PLLMR1_DEFAULT_PCI66@l +	b	1f +strap_1: +	mfdcr	r3, CPC0_PLLMR0 +	mfdcr	r4, CPC0_PLLMR1 +#endif /* CONFIG_TAIHU */ + +1: +	b	pll_write		/* Write the CPC0_PLLMR with new value */ + +pll_done: +	/* +	!----------------------------------------------------------------------- +	! Clear Soft Reset Register +	! This is needed to enable PCI if not booting from serial EPROM +	!----------------------------------------------------------------------- +		*/ +	addi	r3, 0, 0x0 +	mtdcr	CPC0_SRR, r3 + +	addis	 r3,0,0x0010 +	mtctr	r3 +pci_wait: +	bdnz	pci_wait + +	blr				/* return to main code */ + +/* +!----------------------------------------------------------------------------- +! Function:	pll_write +! Description:	Updates the value of the CPC0_PLLMR according to CMOS27E documentation +!		That is: +!			  1.  Pll is first disabled (de-activated by putting in bypass mode) +!			  2.  PLL is reset +!			  3.  Clock dividers are set while PLL is held in reset and bypassed +!			  4.  PLL Reset is cleared +!			  5.  Wait 100us for PLL to lock +!			  6.  A core reset is performed +! Input: r3 = Value to write to CPC0_PLLMR0 +! Input: r4 = Value to write to CPC0_PLLMR1 +! Output r3 = none +!----------------------------------------------------------------------------- +*/ +	.globl	pll_write +pll_write: +	mfdcr  r5, CPC0_UCR +	andis. r5,r5,0xFFFF +	ori    r5,r5,0x0101		/* Stop the UART clocks */ +	mtdcr  CPC0_UCR,r5		/* Before changing PLL */ + +	mfdcr  r5, CPC0_PLLMR1 +	rlwinm r5,r5,0,0x7FFFFFFF	/* Disable PLL */ +	mtdcr	CPC0_PLLMR1,r5 +	oris   r5,r5,0x4000		/* Set PLL Reset */ +	mtdcr	CPC0_PLLMR1,r5 + +	mtdcr	CPC0_PLLMR0,r3		/* Set clock dividers */ +	rlwinm r5,r4,0,0x3FFFFFFF	/* Reset & Bypass new PLL dividers */ +	oris   r5,r5,0x4000		/* Set PLL Reset */ +	mtdcr	CPC0_PLLMR1,r5		/* Set clock dividers */ +	rlwinm r5,r5,0,0xBFFFFFFF	/* Clear PLL Reset */ +	mtdcr	CPC0_PLLMR1,r5 + +		/* +	! Wait min of 100us for PLL to lock. +	! See CMOS 27E databook for more info. +	! At 200MHz, that means waiting 20,000 instructions +		 */ +	addi	r3,0,20000		/* 2000 = 0x4e20 */ +	mtctr	r3 +pll_wait: +	bdnz	pll_wait + +	oris   r5,r5,0x8000		/* Enable PLL */ +	mtdcr	CPC0_PLLMR1,r5		/* Engage */ + +	/* +	 * Reset CPU to guarantee timings are OK +	 * Not sure if this is needed... +	 */ +	addis r3,0,0x1000 +	mtspr SPRN_DBCR0,r3		/* This will cause a CPU core reset, and */ +					/* execution will continue from the poweron */ +					/* vector of 0xfffffffc */ +#endif /* CONFIG_405EP */ + +#if defined(CONFIG_440) +/*----------------------------------------------------------------------------+ +| mttlb3. ++----------------------------------------------------------------------------*/ +	function_prolog(mttlb3) +	TLBWE(4,3,2) +	blr +	function_epilog(mttlb3) + +/*----------------------------------------------------------------------------+ +| mftlb3. ++----------------------------------------------------------------------------*/ +	function_prolog(mftlb3) +	TLBRE(3,3,2) +	blr +	function_epilog(mftlb3) + +/*----------------------------------------------------------------------------+ +| mttlb2. ++----------------------------------------------------------------------------*/ +	function_prolog(mttlb2) +	TLBWE(4,3,1) +	blr +	function_epilog(mttlb2) + +/*----------------------------------------------------------------------------+ +| mftlb2. ++----------------------------------------------------------------------------*/ +	function_prolog(mftlb2) +	TLBRE(3,3,1) +	blr +	function_epilog(mftlb2) + +/*----------------------------------------------------------------------------+ +| mttlb1. ++----------------------------------------------------------------------------*/ +	function_prolog(mttlb1) +	TLBWE(4,3,0) +	blr +	function_epilog(mttlb1) + +/*----------------------------------------------------------------------------+ +| mftlb1. ++----------------------------------------------------------------------------*/ +	function_prolog(mftlb1) +	TLBRE(3,3,0) +	blr +	function_epilog(mftlb1) +#endif /* CONFIG_440 */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/tlb.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/tlb.c new file mode 100644 index 00000000..3cb09bda --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/tlb.c @@ -0,0 +1,336 @@ +/* + * (C) Copyright 2007 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> + +#if defined(CONFIG_440) + +#include <asm/ppc440.h> +#include <asm/cache.h> +#include <asm/io.h> +#include <asm/mmu.h> + +typedef struct region { +	u64 base; +	u32 size; +	u32 tlb_word2_i_value; +} region_t; + +void remove_tlb(u32 vaddr, u32 size) +{ +	int i; +	u32 tlb_word0_value; +	u32 tlb_vaddr; +	u32 tlb_size = 0; + +	for (i=0; i<PPC4XX_TLB_SIZE; i++) { +		tlb_word0_value = mftlb1(i); +		tlb_vaddr = TLB_WORD0_EPN_DECODE(tlb_word0_value); +		if (((tlb_word0_value & TLB_WORD0_V_MASK) == TLB_WORD0_V_ENABLE) && +		    (tlb_vaddr >= vaddr)) { +			/* +			 * TLB is enabled and start address is lower or equal +			 * than the area we are looking for. Now we only have +			 * to check the size/end address for a match. +			 */ +			switch (tlb_word0_value & TLB_WORD0_SIZE_MASK) { +			case TLB_WORD0_SIZE_1KB: +				tlb_size = 1 << 10; +				break; +			case TLB_WORD0_SIZE_4KB: +				tlb_size = 4 << 10; +				break; +			case TLB_WORD0_SIZE_16KB: +				tlb_size = 16 << 10; +				break; +			case TLB_WORD0_SIZE_64KB: +				tlb_size = 64 << 10; +				break; +			case TLB_WORD0_SIZE_256KB: +				tlb_size = 256 << 10; +				break; +			case TLB_WORD0_SIZE_1MB: +				tlb_size = 1 << 20; +				break; +			case TLB_WORD0_SIZE_16MB: +				tlb_size = 16 << 20; +				break; +			case TLB_WORD0_SIZE_256MB: +				tlb_size = 256 << 20; +				break; +			} + +			/* +			 * Now check the end-address if it's in the range +			 */ +			if ((tlb_vaddr + tlb_size - 1) <= (vaddr + size - 1)) +				/* +				 * Found a TLB in the range. +				 * Disable it by writing 0 to tlb0 word. +				 */ +				mttlb1(i, 0); +		} +	} + +	/* Execute an ISYNC instruction so that the new TLB entry takes effect */ +	asm("isync"); +} + +/* + * Change the I attribute (cache inhibited) of a TLB or multiple TLB's. + * This function is used to either turn cache on or off in a specific + * memory area. + */ +void change_tlb(u32 vaddr, u32 size, u32 tlb_word2_i_value) +{ +	int i; +	u32 tlb_word0_value; +	u32 tlb_word2_value; +	u32 tlb_vaddr; +	u32 tlb_size = 0; + +	for (i=0; i<PPC4XX_TLB_SIZE; i++) { +		tlb_word0_value = mftlb1(i); +		tlb_vaddr = TLB_WORD0_EPN_DECODE(tlb_word0_value); +		if (((tlb_word0_value & TLB_WORD0_V_MASK) == TLB_WORD0_V_ENABLE) && +		    (tlb_vaddr >= vaddr)) { +			/* +			 * TLB is enabled and start address is lower or equal +			 * than the area we are looking for. Now we only have +			 * to check the size/end address for a match. +			 */ +			switch (tlb_word0_value & TLB_WORD0_SIZE_MASK) { +			case TLB_WORD0_SIZE_1KB: +				tlb_size = 1 << 10; +				break; +			case TLB_WORD0_SIZE_4KB: +				tlb_size = 4 << 10; +				break; +			case TLB_WORD0_SIZE_16KB: +				tlb_size = 16 << 10; +				break; +			case TLB_WORD0_SIZE_64KB: +				tlb_size = 64 << 10; +				break; +			case TLB_WORD0_SIZE_256KB: +				tlb_size = 256 << 10; +				break; +			case TLB_WORD0_SIZE_1MB: +				tlb_size = 1 << 20; +				break; +			case TLB_WORD0_SIZE_16MB: +				tlb_size = 16 << 20; +				break; +			case TLB_WORD0_SIZE_256MB: +				tlb_size = 256 << 20; +				break; +			} + +			/* +			 * Now check the end-address if it's in the range +			 */ +			if (((tlb_vaddr + tlb_size - 1) <= (vaddr + size - 1)) || +			    ((tlb_vaddr < (vaddr + size - 1)) && +			     ((tlb_vaddr + tlb_size - 1) > (vaddr + size - 1)))) { +				/* +				 * Found a TLB in the range. +				 * Change cache attribute in tlb2 word. +				 */ +				tlb_word2_value = +					TLB_WORD2_U0_DISABLE | TLB_WORD2_U1_DISABLE | +					TLB_WORD2_U2_DISABLE | TLB_WORD2_U3_DISABLE | +					TLB_WORD2_W_DISABLE | tlb_word2_i_value | +					TLB_WORD2_M_DISABLE | TLB_WORD2_G_DISABLE | +					TLB_WORD2_E_DISABLE | TLB_WORD2_UX_ENABLE | +					TLB_WORD2_UW_ENABLE | TLB_WORD2_UR_ENABLE | +					TLB_WORD2_SX_ENABLE | TLB_WORD2_SW_ENABLE | +					TLB_WORD2_SR_ENABLE; + +				/* +				 * Now either flush or invalidate the dcache +				 */ +				if (tlb_word2_i_value) +					flush_dcache(); +				else +					invalidate_dcache(); + +				mttlb3(i, tlb_word2_value); +				asm("iccci 0,0"); +			} +		} +	} + +	/* Execute an ISYNC instruction so that the new TLB entry takes effect */ +	asm("isync"); +} + +static int add_tlb_entry(u64 phys_addr, +			 u32 virt_addr, +			 u32 tlb_word0_size_value, +			 u32 tlb_word2_i_value) +{ +	int i; +	unsigned long tlb_word0_value; +	unsigned long tlb_word1_value; +	unsigned long tlb_word2_value; + +	/* First, find the index of a TLB entry not being used */ +	for (i=0; i<PPC4XX_TLB_SIZE; i++) { +		tlb_word0_value = mftlb1(i); +		if ((tlb_word0_value & TLB_WORD0_V_MASK) == TLB_WORD0_V_DISABLE) +			break; +	} +	if (i >= PPC4XX_TLB_SIZE) +		return -1; + +	/* Second, create the TLB entry */ +	tlb_word0_value = TLB_WORD0_EPN_ENCODE(virt_addr) | TLB_WORD0_V_ENABLE | +		TLB_WORD0_TS_0 | tlb_word0_size_value; +	tlb_word1_value = TLB_WORD1_RPN_ENCODE((u32)phys_addr) | +		TLB_WORD1_ERPN_ENCODE(phys_addr >> 32); +	tlb_word2_value = TLB_WORD2_U0_DISABLE | TLB_WORD2_U1_DISABLE | +		TLB_WORD2_U2_DISABLE | TLB_WORD2_U3_DISABLE | +		TLB_WORD2_W_DISABLE | tlb_word2_i_value | +		TLB_WORD2_M_DISABLE | TLB_WORD2_G_DISABLE | +		TLB_WORD2_E_DISABLE | TLB_WORD2_UX_ENABLE | +		TLB_WORD2_UW_ENABLE | TLB_WORD2_UR_ENABLE | +		TLB_WORD2_SX_ENABLE | TLB_WORD2_SW_ENABLE | +		TLB_WORD2_SR_ENABLE; + +	/* Wait for all memory accesses to complete */ +	sync(); + +	/* Third, add the TLB entries */ +	mttlb1(i, tlb_word0_value); +	mttlb2(i, tlb_word1_value); +	mttlb3(i, tlb_word2_value); + +	/* Execute an ISYNC instruction so that the new TLB entry takes effect */ +	asm("isync"); + +	return 0; +} + +static void program_tlb_addr(u64 phys_addr, +			     u32 virt_addr, +			     u32 mem_size, +			     u32 tlb_word2_i_value) +{ +	int rc; +	int tlb_i; + +	tlb_i = tlb_word2_i_value; +	while (mem_size != 0) { +		rc = 0; +		/* Add the TLB entries in to map the region. */ +		if (((phys_addr & TLB_256MB_ALIGN_MASK) == phys_addr) && +		    (mem_size >= TLB_256MB_SIZE)) { +			/* Add a 256MB TLB entry */ +			if ((rc = add_tlb_entry(phys_addr, virt_addr, +						TLB_WORD0_SIZE_256MB, tlb_i)) == 0) { +				mem_size -= TLB_256MB_SIZE; +				phys_addr += TLB_256MB_SIZE; +				virt_addr += TLB_256MB_SIZE; +			} +		} else if (((phys_addr & TLB_16MB_ALIGN_MASK) == phys_addr) && +			   (mem_size >= TLB_16MB_SIZE)) { +			/* Add a 16MB TLB entry */ +			if ((rc = add_tlb_entry(phys_addr, virt_addr, +						TLB_WORD0_SIZE_16MB, tlb_i)) == 0) { +				mem_size -= TLB_16MB_SIZE; +				phys_addr += TLB_16MB_SIZE; +				virt_addr += TLB_16MB_SIZE; +			} +		} else if (((phys_addr & TLB_1MB_ALIGN_MASK) == phys_addr) && +			   (mem_size >= TLB_1MB_SIZE)) { +			/* Add a 1MB TLB entry */ +			if ((rc = add_tlb_entry(phys_addr, virt_addr, +						TLB_WORD0_SIZE_1MB, tlb_i)) == 0) { +				mem_size -= TLB_1MB_SIZE; +				phys_addr += TLB_1MB_SIZE; +				virt_addr += TLB_1MB_SIZE; +			} +		} else if (((phys_addr & TLB_256KB_ALIGN_MASK) == phys_addr) && +			   (mem_size >= TLB_256KB_SIZE)) { +			/* Add a 256KB TLB entry */ +			if ((rc = add_tlb_entry(phys_addr, virt_addr, +						TLB_WORD0_SIZE_256KB, tlb_i)) == 0) { +				mem_size -= TLB_256KB_SIZE; +				phys_addr += TLB_256KB_SIZE; +				virt_addr += TLB_256KB_SIZE; +			} +		} else if (((phys_addr & TLB_64KB_ALIGN_MASK) == phys_addr) && +			   (mem_size >= TLB_64KB_SIZE)) { +			/* Add a 64KB TLB entry */ +			if ((rc = add_tlb_entry(phys_addr, virt_addr, +						TLB_WORD0_SIZE_64KB, tlb_i)) == 0) { +				mem_size -= TLB_64KB_SIZE; +				phys_addr += TLB_64KB_SIZE; +				virt_addr += TLB_64KB_SIZE; +			} +		} else if (((phys_addr & TLB_16KB_ALIGN_MASK) == phys_addr) && +			   (mem_size >= TLB_16KB_SIZE)) { +			/* Add a 16KB TLB entry */ +			if ((rc = add_tlb_entry(phys_addr, virt_addr, +						TLB_WORD0_SIZE_16KB, tlb_i)) == 0) { +				mem_size -= TLB_16KB_SIZE; +				phys_addr += TLB_16KB_SIZE; +				virt_addr += TLB_16KB_SIZE; +			} +		} else if (((phys_addr & TLB_4KB_ALIGN_MASK) == phys_addr) && +			   (mem_size >= TLB_4KB_SIZE)) { +			/* Add a 4KB TLB entry */ +			if ((rc = add_tlb_entry(phys_addr, virt_addr, +						TLB_WORD0_SIZE_4KB, tlb_i)) == 0) { +				mem_size -= TLB_4KB_SIZE; +				phys_addr += TLB_4KB_SIZE; +				virt_addr += TLB_4KB_SIZE; +			} +		} else if (((phys_addr & TLB_1KB_ALIGN_MASK) == phys_addr) && +			   (mem_size >= TLB_1KB_SIZE)) { +			/* Add a 1KB TLB entry */ +			if ((rc = add_tlb_entry(phys_addr, virt_addr, +						TLB_WORD0_SIZE_1KB, tlb_i)) == 0) { +				mem_size -= TLB_1KB_SIZE; +				phys_addr += TLB_1KB_SIZE; +				virt_addr += TLB_1KB_SIZE; +			} +		} else { +			printf("ERROR: no TLB size exists for the base address 0x%llx.\n", +				phys_addr); +		} + +		if (rc != 0) +			printf("ERROR: no TLB entries available for the base addr 0x%llx.\n", +				phys_addr); +	} + +	return; +} + +/* + * Program one (or multiple) TLB entries for one memory region + * + * Common usage for boards with SDRAM DIMM modules to dynamically + * configure the TLB's for the SDRAM + */ +void program_tlb(u64 phys_addr, u32 virt_addr, u32 size, u32 tlb_word2_i_value) +{ +	region_t region_array; + +	region_array.base = phys_addr; +	region_array.size = size; +	region_array.tlb_word2_i_value = tlb_word2_i_value;	/* en-/disable cache */ + +	/* Call the routine to add in the tlb entries for the memory regions */ +	program_tlb_addr(region_array.base, virt_addr, region_array.size, +			 region_array.tlb_word2_i_value); + +	return; +} + +#endif /* CONFIG_440 */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/traps.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/traps.c new file mode 100644 index 00000000..df527e4b --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/traps.c @@ -0,0 +1,392 @@ +/* + * linux/arch/powerpc/kernel/traps.c + * + * Copyright (C) 1995-1996  Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <kgdb.h> +#include <asm/processor.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Returns 0 if exception not found and fixup otherwise.  */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. + */ +#define END_OF_MEM	(gd->bd->bi_memstart + gd->bd->bi_memsize) + +static __inline__ unsigned long get_esr(void) +{ +	unsigned long val; + +#if defined(CONFIG_440) +	asm volatile("mfspr %0, 0x03e" : "=r" (val) :); +#else +	asm volatile("mfesr %0" : "=r" (val) :); +#endif +	return val; +} + +#define ESR_MCI 0x80000000 +#define ESR_PIL 0x08000000 +#define ESR_PPR 0x04000000 +#define ESR_PTR 0x02000000 +#define ESR_DST 0x00800000 +#define ESR_DIZ 0x00400000 +#define ESR_U0F 0x00008000 + +#if defined(CONFIG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* + * Trap & Exception support + */ + +static void print_backtrace(unsigned long *sp) +{ +	int cnt = 0; +	unsigned long i; + +	printf("Call backtrace: "); +	while (sp) { +		if ((uint)sp > END_OF_MEM) +			break; + +		i = sp[1]; +		if (cnt++ % 7 == 0) +			printf("\n"); +		printf("%08lX ", i); +		if (cnt > 32) break; +		sp = (unsigned long *)*sp; +	} +	printf("\n"); +} + +void show_regs(struct pt_regs *regs) +{ +	int i; + +	printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DEAR: %08lX\n", +	       regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); +	printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", +	       regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, +	       regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, +	       regs->msr&MSR_IR ? 1 : 0, +	       regs->msr&MSR_DR ? 1 : 0); + +	printf("\n"); +	for (i = 0;  i < 32;  i++) { +		if ((i % 8) == 0) { +			printf("GPR%02d: ", i); +		} + +		printf("%08lX ", regs->gpr[i]); +		if ((i % 8) == 7) { +			printf("\n"); +		} +	} +} + + +static void _exception(int signr, struct pt_regs *regs) +{ +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("Exception"); +} + +void MachineCheckException(struct pt_regs *regs) +{ +	unsigned long fixup, val; +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +	u32 value2; +	int corr_ecc = 0; +	int uncorr_ecc = 0; +#endif + +	if ((fixup = search_exception_table(regs->nip)) != 0) { +		regs->nip = fixup; +		val = mfspr(MCSR); +		/* Clear MCSR */ +		mtspr(SPRN_MCSR, val); +		return; +	} + +#if defined(CONFIG_CMD_KGDB) +	if (debugger_exception_handler && (*debugger_exception_handler)(regs)) +		return; +#endif + +	printf("Machine Check Exception.\n"); +	printf("Caused by (from msr): "); +	printf("regs %p ", regs); + +	val = get_esr(); + +#if !defined(CONFIG_440) && !defined(CONFIG_405EX) +	if (val& ESR_IMCP) { +		printf("Instruction"); +		mtspr(ESR, val & ~ESR_IMCP); +	} else { +		printf("Data"); +	} +	printf(" machine check.\n"); + +#elif defined(CONFIG_440) || defined(CONFIG_405EX) +	if (val& ESR_IMCP){ +		printf("Instruction Synchronous Machine Check exception\n"); +		mtspr(SPRN_ESR, val & ~ESR_IMCP); +	} else { +		val = mfspr(MCSR); +		if (val & MCSR_IB) +			printf("Instruction Read PLB Error\n"); +#if defined(CONFIG_440) +		if (val & MCSR_DRB) +			printf("Data Read PLB Error\n"); +		if (val & MCSR_DWB) +			printf("Data Write PLB Error\n"); +#else +		if (val & MCSR_DB) +			printf("Data PLB Error\n"); +#endif +		if (val & MCSR_TLBP) +			printf("TLB Parity Error\n"); +		if (val & MCSR_ICP){ +			/*flush_instruction_cache(); */ +			printf("I-Cache Parity Error\n"); +		} +		if (val & MCSR_DCSP) +			printf("D-Cache Search Parity Error\n"); +		if (val & MCSR_DCFP) +			printf("D-Cache Flush Parity Error\n"); +		if (val & MCSR_IMPE) +			printf("Machine Check exception is imprecise\n"); + +		/* Clear MCSR */ +		mtspr(SPRN_MCSR, val); +	} + +#if defined(CONFIG_DDR_ECC) && defined(CONFIG_SDRAM_PPC4xx_IBM_DDR2) +	/* +	 * Read and print ECC status register/info: +	 * The faulting address is only known upon uncorrectable ECC +	 * errors. +	 */ +	mfsdram(SDRAM_ECCES, val); +	if (val & SDRAM_ECCES_CE) +		printf("ECC: Correctable error\n"); +	if (val & SDRAM_ECCES_UE) { +		printf("ECC: Uncorrectable error at 0x%02x%08x\n", +		       mfdcr(SDRAM_ERRADDULL), mfdcr(SDRAM_ERRADDLLL)); +	} +#endif /* CONFIG_DDR_ECC ... */ + +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +	mfsdram(DDR0_00, val) ; +	printf("DDR0: DDR0_00 %lx\n", val); +	val = (val >> 16) & 0xff; +	if (val & 0x80) +		printf("DDR0: At least one interrupt active\n"); +	if (val & 0x40) +		printf("DDR0: DRAM initialization complete.\n"); +	if (val & 0x20) { +		printf("DDR0: Multiple uncorrectable ECC events.\n"); +		uncorr_ecc = 1; +	} +	if (val & 0x10) { +		printf("DDR0: Single uncorrectable ECC event.\n"); +		uncorr_ecc = 1; +	} +	if (val & 0x08) { +		printf("DDR0: Multiple correctable ECC events.\n"); +		corr_ecc = 1; +	} +	if (val & 0x04) { +		printf("DDR0: Single correctable ECC event.\n"); +		corr_ecc = 1; +	} +	if (val & 0x02) +		printf("Multiple accesses outside the defined" +		       " physical memory space detected\n"); +	if (val & 0x01) +		printf("DDR0: Single access outside the defined" +		       " physical memory space detected.\n"); + +	mfsdram(DDR0_01, val); +	val = (val >> 8) & 0x7; +	switch (val ) { +	case 0: +		printf("DDR0: Write Out-of-Range command\n"); +		break; +	case 1: +		printf("DDR0: Read Out-of-Range command\n"); +		break; +	case 2: +		printf("DDR0: Masked write Out-of-Range command\n"); +		break; +	case 4: +		printf("DDR0: Wrap write Out-of-Range command\n"); +		break; +	case 5: +		printf("DDR0: Wrap read Out-of-Range command\n"); +		break; +	default: +		mfsdram(DDR0_01, value2); +		printf("DDR0: No DDR0 error know 0x%lx %x\n", val, value2); +	} +	mfsdram(DDR0_23, val); +	if (((val >> 16) & 0xff) && corr_ecc) +		printf("DDR0: Syndrome for correctable ECC event 0x%lx\n", +		       (val >> 16) & 0xff); +	mfsdram(DDR0_23, val); +	if (((val >> 8) & 0xff) && uncorr_ecc) +		printf("DDR0: Syndrome for uncorrectable ECC event 0x%lx\n", +		       (val >> 8) & 0xff); +	mfsdram(DDR0_33, val); +	if (val) +		printf("DDR0: Address of command that caused an " +		       "Out-of-Range interrupt %lx\n", val); +	mfsdram(DDR0_34, val); +	if (val && uncorr_ecc) +		printf("DDR0: Address of uncorrectable ECC event %lx\n", val); +	mfsdram(DDR0_35, val); +	if (val && uncorr_ecc) +		printf("DDR0: Address of uncorrectable ECC event %lx\n", val); +	mfsdram(DDR0_36, val); +	if (val && uncorr_ecc) +		printf("DDR0: Data of uncorrectable ECC event 0x%08lx\n", val); +	mfsdram(DDR0_37, val); +	if (val && uncorr_ecc) +		printf("DDR0: Data of uncorrectable ECC event 0x%08lx\n", val); +	mfsdram(DDR0_38, val); +	if (val && corr_ecc) +		printf("DDR0: Address of correctable ECC event %lx\n", val); +	mfsdram(DDR0_39, val); +	if (val && corr_ecc) +		printf("DDR0: Address of correctable ECC event %lx\n", val); +	mfsdram(DDR0_40, val); +	if (val && corr_ecc) +		printf("DDR0: Data of correctable ECC event 0x%08lx\n", val); +	mfsdram(DDR0_41, val); +	if (val && corr_ecc) +		printf("DDR0: Data of correctable ECC event 0x%08lx\n", val); +#endif /* CONFIG_440EPX */ +#endif /* CONFIG_440 */ +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("machine check"); +} + +void AlignmentException(struct pt_regs *regs) +{ +#if defined(CONFIG_CMD_KGDB) +	if (debugger_exception_handler && (*debugger_exception_handler)(regs)) +		return; +#endif + +	show_regs(regs); +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("Alignment Exception"); +} + +void ProgramCheckException(struct pt_regs *regs) +{ +	long esr_val; + +#if defined(CONFIG_CMD_KGDB) +	if (debugger_exception_handler && (*debugger_exception_handler)(regs)) +		return; +#endif + +	show_regs(regs); + +	esr_val = get_esr(); +	if( esr_val & ESR_PIL ) +		printf( "** Illegal Instruction **\n" ); +	else if( esr_val & ESR_PPR ) +		printf( "** Privileged Instruction **\n" ); +	else if( esr_val & ESR_PTR ) +		printf( "** Trap Instruction **\n" ); + +	print_backtrace((unsigned long *)regs->gpr[1]); +	panic("Program Check Exception"); +} + +void DecrementerPITException(struct pt_regs *regs) +{ +	/* +	 * Reset PIT interrupt +	 */ +	mtspr(SPRN_TSR, 0x08000000); + +	/* +	 * Call timer_interrupt routine in interrupts.c +	 */ +	timer_interrupt(NULL); +} + + +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); +} + +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/roms/u-boot/arch/powerpc/cpu/ppc4xx/u-boot-spl.lds b/roms/u-boot/arch/powerpc/cpu/ppc4xx/u-boot-spl.lds new file mode 100644 index 00000000..b4e49aae --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/u-boot-spl.lds @@ -0,0 +1,61 @@ +/* + * Copyright 2012-2013 Stefan Roese <sr@denx.de> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +MEMORY +{ +	sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, +		LENGTH = CONFIG_SPL_BSS_MAX_SIZE +	flash : ORIGIN = CONFIG_SPL_TEXT_BASE, +		LENGTH = CONFIG_SYS_SPL_MAX_LEN +} + +OUTPUT_ARCH(powerpc) +ENTRY(_start) +SECTIONS +{ +#ifdef CONFIG_440 +	.bootpg 0xfffff000 : +	{ +		arch/powerpc/cpu/ppc4xx/start.o	(.bootpg) + +		/* +		 * PPC440 board need a board specific object with the +		 * TLB definitions. This needs to get included right after +		 * start.o, since the first shadow TLB only covers 4k +		 * of address space. +		 */ +		CONFIG_BOARDDIR/init.o	(.bootpg) +	} > flash +#endif + +	.resetvec 0xFFFFFFFC : +	{ +		KEEP(*(.resetvec)) +	} > flash + +	.text : +	{ +		__start = .; +		arch/powerpc/cpu/ppc4xx/start.o	(.text) +		CONFIG_BOARDDIR/init.o	(.text) +		*(.text*) +	} > flash + +	. = ALIGN(4); +	.data : { *(SORT_BY_ALIGNMENT(.data*)) } > flash + +	. = ALIGN(4); +	.rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } > flash + +	.bss : +	{ +		. = ALIGN(4); +		__bss_start = .; +		*(.bss*) +		. = ALIGN(4); +		__bss_end = .; +	} > sdram +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/u-boot.lds b/roms/u-boot/arch/powerpc/cpu/ppc4xx/u-boot.lds new file mode 100644 index 00000000..87731785 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/u-boot.lds @@ -0,0 +1,133 @@ +/* + * Copyright 2007-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include "config.h"	/* CONFIG_BOARDDIR */ + +#ifndef RESET_VECTOR_ADDRESS +#ifdef CONFIG_RESET_VECTOR_ADDRESS +#define RESET_VECTOR_ADDRESS	CONFIG_RESET_VECTOR_ADDRESS +#else +#define RESET_VECTOR_ADDRESS	0xfffffffc +#endif +#endif + +OUTPUT_ARCH(powerpc) + +PHDRS +{ +  text PT_LOAD; +  bss PT_LOAD; +} + +SECTIONS +{ +  /* Read-only sections, merged into text segment: */ +  . = + SIZEOF_HEADERS; +  .text      : +  { +    *(.text*) +   } :text +    _etext = .; +    PROVIDE (etext = .); +    .rodata    : +   { +    *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) +  } :text + +  /* Read-write section, merged into data segment: */ +  . = (. + 0x00FF) & 0xFFFFFF00; +  _erotext = .; +  PROVIDE (erotext = .); +  .reloc   : +  { +    _GOT2_TABLE_ = .; +    KEEP(*(.got2)) +    KEEP(*(.got)) +    PROVIDE(_GLOBAL_OFFSET_TABLE_ = . + 4); +    _FIXUP_TABLE_ = .; +    KEEP(*(.fixup)) +  } +  __got2_entries = ((_GLOBAL_OFFSET_TABLE_ - _GOT2_TABLE_) >> 2) - 1; +  __fixup_entries = (. - _FIXUP_TABLE_) >> 2; + +  .data    : +  { +    *(.data*) +    *(.sdata*) +  } +  _edata  =  .; +  PROVIDE (edata = .); + +  . = .; + +  . = ALIGN(4); +  .u_boot_list : { +	KEEP(*(SORT(.u_boot_list*))); +  } + +  . = .; +  __start___ex_table = .; +  __ex_table : { *(__ex_table) } +  __stop___ex_table = .; + +  . = ALIGN(256); +  __init_begin = .; +  .text.init : { *(.text.init) } +  .data.init : { *(.data.init) } +  . = ALIGN(256); +  __init_end = .; + +#ifndef CONFIG_SPL +#ifdef CONFIG_440 +  .bootpg RESET_VECTOR_ADDRESS - 0xffc : +  { +    arch/powerpc/cpu/ppc4xx/start.o	(.bootpg) + +    /* +     * PPC440 board need a board specific object with the +     * TLB definitions. This needs to get included right after +     * start.o, since the first shadow TLB only covers 4k +     * of address space. +     */ +#ifdef CONFIG_INIT_TLB +    CONFIG_INIT_TLB (.bootpg) +#else +    CONFIG_BOARDDIR/init.o	(.bootpg) +#endif +  } :text = 0xffff +#endif + +  .resetvec RESET_VECTOR_ADDRESS : +  { +    KEEP(*(.resetvec)) +  } :text = 0xffff + +  . = RESET_VECTOR_ADDRESS + 0x4; + +  /* +   * Make sure that the bss segment isn't linked at 0x0, otherwise its +   * address won't be updated during relocation fixups.  Note that +   * this is a temporary fix.  Code to dynamically the fixup the bss +   * location will be added in the future.  When the bss relocation +   * fixup code is present this workaround should be removed. +   */ +#if (RESET_VECTOR_ADDRESS == 0xfffffffc) +  . |= 0x10; +#endif +#endif /* CONFIG_SPL */ + +  __bss_start = .; +  .bss (NOLOAD)       : +  { +   *(.bss*) +   *(.sbss*) +   *(COMMON) +  } :bss + +  . = ALIGN(4); +  __bss_end = . ; +  PROVIDE (end = .); +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/uic.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/uic.c new file mode 100644 index 00000000..bd955ed8 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/uic.c @@ -0,0 +1,164 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 (440 port) + * Scott McNutt, Artesyn Communication Producs, smcnutt@artsyncp.com + * + * (C) Copyright 2003 (440GX port) + * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.com + * + * (C) Copyright 2008 (PPC440X05 port for Virtex 5 FX) + * Ricardo Ribalda-Universidad Autonoma de Madrid-ricardo.ribalda@uam.es + * Work supported by Qtechnology (htpp://qtec.com) + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/interrupt.h> +#include <asm/ppc4xx.h> +#include <ppc_asm.tmpl> +#include <commproc.h> + +#if (UIC_MAX > 3) +#define UICB0_ALL	(UIC_MASK(VECNUM_UIC1CI) | UIC_MASK(VECNUM_UIC1NCI) | \ +			 UIC_MASK(VECNUM_UIC2CI) | UIC_MASK(VECNUM_UIC2NCI) | \ +			 UIC_MASK(VECNUM_UIC3CI) | UIC_MASK(VECNUM_UIC3NCI)) +#elif (UIC_MAX > 2) +#define UICB0_ALL	(UIC_MASK(VECNUM_UIC1CI) | UIC_MASK(VECNUM_UIC1NCI) | \ +			 UIC_MASK(VECNUM_UIC2CI) | UIC_MASK(VECNUM_UIC2NCI)) +#elif (UIC_MAX > 1) +#define UICB0_ALL	(UIC_MASK(VECNUM_UIC1CI) | UIC_MASK(VECNUM_UIC1NCI)) +#else +#define UICB0_ALL	0 +#endif + +u32 get_dcr(u16); + +DECLARE_GLOBAL_DATA_PTR; + +void pic_enable(void) +{ +#if (UIC_MAX > 1) +	/* Install the UIC1 handlers */ +	irq_install_handler(VECNUM_UIC1NCI, (void *)(void *)external_interrupt, 0); +	irq_install_handler(VECNUM_UIC1CI, (void *)(void *)external_interrupt, 0); +#endif +#if (UIC_MAX > 2) +	irq_install_handler(VECNUM_UIC2NCI, (void *)(void *)external_interrupt, 0); +	irq_install_handler(VECNUM_UIC2CI, (void *)(void *)external_interrupt, 0); +#endif +#if (UIC_MAX > 3) +	irq_install_handler(VECNUM_UIC3NCI, (void *)(void *)external_interrupt, 0); +	irq_install_handler(VECNUM_UIC3CI, (void *)(void *)external_interrupt, 0); +#endif +} + +/* Handler for UIC interrupt */ +static void uic_interrupt(u32 uic_base, int vec_base) +{ +	u32 uic_msr; +	u32 msr_shift; +	int vec; + +	/* +	 * Read masked interrupt status register to determine interrupt source +	 */ +	uic_msr = get_dcr(uic_base + UIC_MSR); +	msr_shift = uic_msr; +	vec = vec_base; + +	while (msr_shift != 0) { +		if (msr_shift & 0x80000000) +			interrupt_run_handler(vec); +		/* +		 * Shift msr to next position and increment vector +		 */ +		msr_shift <<= 1; +		vec++; +	} +} + +/* + * Handle external interrupts + */ +void external_interrupt(struct pt_regs *regs) +{ +	u32 uic_msr; + +	/* +	 * Read masked interrupt status register to determine interrupt source +	 */ +	uic_msr = mfdcr(UIC0MSR); + +#if (UIC_MAX > 1) +	if ((UIC_MASK(VECNUM_UIC1CI) & uic_msr) || +	    (UIC_MASK(VECNUM_UIC1NCI) & uic_msr)) +		uic_interrupt(UIC1_DCR_BASE, 32); +#endif + +#if (UIC_MAX > 2) +	if ((UIC_MASK(VECNUM_UIC2CI) & uic_msr) || +	    (UIC_MASK(VECNUM_UIC2NCI) & uic_msr)) +		uic_interrupt(UIC2_DCR_BASE, 64); +#endif + +#if (UIC_MAX > 3) +	if ((UIC_MASK(VECNUM_UIC3CI) & uic_msr) || +	    (UIC_MASK(VECNUM_UIC3NCI) & uic_msr)) +		uic_interrupt(UIC3_DCR_BASE, 96); +#endif + +	mtdcr(UIC0SR, (uic_msr & UICB0_ALL)); + +	if (uic_msr & ~(UICB0_ALL)) +		uic_interrupt(UIC0_DCR_BASE, 0); + +	return; +} + +void pic_irq_ack(unsigned int vec) +{ +	if ((vec >= 0) && (vec < 32)) +		mtdcr(UIC0SR, UIC_MASK(vec)); +	else if ((vec >= 32) && (vec < 64)) +		mtdcr(UIC1SR, UIC_MASK(vec)); +	else if ((vec >= 64) && (vec < 96)) +		mtdcr(UIC2SR, UIC_MASK(vec)); +	else if (vec >= 96) +		mtdcr(UIC3SR, UIC_MASK(vec)); +} + +/* + * Install and free a interrupt handler. + */ +void pic_irq_enable(unsigned int vec) +{ + +	if ((vec >= 0) && (vec < 32)) +		mtdcr(UIC0ER, mfdcr(UIC0ER) | UIC_MASK(vec)); +	else if ((vec >= 32) && (vec < 64)) +		mtdcr(UIC1ER, mfdcr(UIC1ER) | UIC_MASK(vec)); +	else if ((vec >= 64) && (vec < 96)) +		mtdcr(UIC2ER, mfdcr(UIC2ER) | UIC_MASK(vec)); +	else if (vec >= 96) +		mtdcr(UIC3ER, mfdcr(UIC3ER) | UIC_MASK(vec)); + +	debug("Install interrupt vector %d\n", vec); +} + +void pic_irq_disable(unsigned int vec) +{ +	if ((vec >= 0) && (vec < 32)) +		mtdcr(UIC0ER, mfdcr(UIC0ER) & ~UIC_MASK(vec)); +	else if ((vec >= 32) && (vec < 64)) +		mtdcr(UIC1ER, mfdcr(UIC1ER) & ~UIC_MASK(vec)); +	else if ((vec >= 64) && (vec < 96)) +		mtdcr(UIC2ER, mfdcr(UIC2ER) & ~UIC_MASK(vec)); +	else if (vec >= 96) +		mtdcr(UIC3ER, mfdcr(UIC3ER) & ~UIC_MASK(vec)); +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/usb.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/usb.c new file mode 100644 index 00000000..16b44cce --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/usb.c @@ -0,0 +1,45 @@ +/* + * (C) Copyright 2007 + * Markus Klotzbuecher, DENX Software Engineering <mk@denx.de> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> + +#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) + +#ifdef CONFIG_4xx_DCACHE +#include <asm/mmu.h> +DECLARE_GLOBAL_DATA_PTR; +#endif + +int usb_cpu_init(void) +{ +#ifdef CONFIG_4xx_DCACHE +	/* disable cache */ +	change_tlb(gd->bd->bi_memstart, gd->bd->bi_memsize, TLB_WORD2_I_ENABLE); +#endif + +	return 0; +} + +int usb_cpu_stop(void) +{ +#ifdef CONFIG_4xx_DCACHE +	/* enable cache */ +	change_tlb(gd->bd->bi_memstart, gd->bd->bi_memsize, 0); +#endif +	return 0; +} + +int usb_cpu_init_fail(void) +{ +#ifdef CONFIG_4xx_DCACHE +	/* enable cache */ +	change_tlb(gd->bd->bi_memstart, gd->bd->bi_memsize, 0); +#endif +	return 0; +} + +#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/usb_ohci.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/usb_ohci.c new file mode 100644 index 00000000..d1e78f6b --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/usb_ohci.c @@ -0,0 +1,1525 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the PPC440EP. + * + * (C) Copyright 2003-2004 + * Gary Jennejohn, DENX Software Engineering <garyj@denx.de> + * + * (C) Copyright 2004 + * Pierre Aubert, Staubli Faverges <p.aubert@staubli.com> + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell + * + * SPDX-License-Identifier:	GPL-2.0+ + */ +/* + * IMPORTANT NOTES + * 1 - this driver is intended for use with USB Mass Storage Devices + *     (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include <common.h> + +#ifdef CONFIG_USB_OHCI + +#include <malloc.h> +#include <usb.h> +#include "usb_ohci.h" + +#define OHCI_USE_NPS		/* force NoPowerSwitching mode */ +#undef OHCI_VERBOSE_DEBUG	/* not always helpful */ +#undef DEBUG +#undef SHOW_INFO +#undef OHCI_FILL_TRACE + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ +	(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define readl(a) (*((volatile u32 *)(a))) +#define writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) + +#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) swap_16(x) +#define m32_swap(x) swap_32(x) + +#if defined(CONFIG_405EZ) || defined(CONFIG_440EP) || defined(CONFIG_440EPX) +#define ohci_cpu_to_le16(x) (x) +#define ohci_cpu_to_le32(x) (x) +#else +#define ohci_cpu_to_le16(x) swap_16(x) +#define ohci_cpu_to_le32(x) swap_32(x) +#endif + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +urb_priv_t urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; +/* flag guarding URB transation */ +int urb_finished = 0; + +/*-------------------------------------------------------------------------*/ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect.  AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define OHCI_QUIRK_AMD756 0xabcd +#define read_roothub(hc, register, mask) ({ \ +	u32 temp = readl (&hc->regs->roothub.register); \ +	if (hc->flags & OHCI_QUIRK_AMD756) \ +		while (temp & mask) \ +			temp = readl (&hc->regs->roothub.register); \ +	temp; }) + +static u32 roothub_a (struct ohci *hc) +	{ return read_roothub (hc, a, 0xfc0fe000); } +static inline u32 roothub_b (struct ohci *hc) +	{ return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) +	{ return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) +	{ return read_roothub (hc, portstatus [i], 0xffe0fce0); } + + +/* forward declaration */ +static int hc_interrupt (void); +static void +td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer, +	int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv (urb_priv_t * urb) +{ +	int		i; +	int		last; +	struct td	* td; + +	last = urb->length - 1; +	if (last >= 0) { +		for (i = 0; i <= last; i++) { +			td = urb->td[i]; +			if (td) { +				td->usb_dev = NULL; +				urb->td[i] = NULL; +			} +		} +	} +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number (struct usb_device * dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer, +	int transfer_len, struct devrequest * setup, char * str, int small) +{ +	urb_priv_t * purb = &urb_priv; + +	dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", +			str, +			sohci_get_current_frame_number (dev), +			usb_pipedevice (pipe), +			usb_pipeendpoint (pipe), +			usb_pipeout (pipe)? 'O': 'I', +			usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): +				(usb_pipecontrol (pipe)? "CTRL": "BULK"), +			purb->actual_length, +			transfer_len, dev->status); +#ifdef	OHCI_VERBOSE_DEBUG +	if (!small) { +		int i, len; + +		if (usb_pipecontrol (pipe)) { +			printf (__FILE__ ": cmd(8):"); +			for (i = 0; i < 8 ; i++) +				printf (" %02x", ((__u8 *) setup) [i]); +			printf ("\n"); +		} +		if (transfer_len > 0 && buffer) { +			printf (__FILE__ ": data(%d/%d):", +				purb->actual_length, +				transfer_len); +			len = usb_pipeout (pipe)? +					transfer_len: purb->actual_length; +			for (i = 0; i < 16 && i < len; i++) +				printf (" %02x", ((__u8 *) buffer) [i]); +			printf ("%s\n", i < len? "...": ""); +		} +	} +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t *ohci, char * str) { +	int i, j; +	 __u32 * ed_p; +	for (i= 0; i < 32; i++) { +		j = 5; +		ed_p = &(ohci->hcca->int_table [i]); +		if (*ed_p == 0) +		    continue; +		printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i); +		while (*ed_p != 0 && j--) { +			ed_t *ed = (ed_t *)ohci_cpu_to_le32(ed_p); +			printf (" ed: %4x;", ed->hwINFO); +			ed_p = &ed->hwNextED; +		} +		printf ("\n"); +	} +} + +static void ohci_dump_intr_mask (char *label, __u32 mask) +{ +	dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", +		label, +		mask, +		(mask & OHCI_INTR_MIE) ? " MIE" : "", +		(mask & OHCI_INTR_OC) ? " OC" : "", +		(mask & OHCI_INTR_RHSC) ? " RHSC" : "", +		(mask & OHCI_INTR_FNO) ? " FNO" : "", +		(mask & OHCI_INTR_UE) ? " UE" : "", +		(mask & OHCI_INTR_RD) ? " RD" : "", +		(mask & OHCI_INTR_SF) ? " SF" : "", +		(mask & OHCI_INTR_WDH) ? " WDH" : "", +		(mask & OHCI_INTR_SO) ? " SO" : "" +		); +} + +static void maybe_print_eds (char *label, __u32 value) +{ +	ed_t *edp = (ed_t *)value; + +	if (value) { +		dbg ("%s %08x", label, value); +		dbg ("%08x", edp->hwINFO); +		dbg ("%08x", edp->hwTailP); +		dbg ("%08x", edp->hwHeadP); +		dbg ("%08x", edp->hwNextED); +	} +} + +static char * hcfs2string (int state) +{ +	switch (state) { +		case OHCI_USB_RESET:	return "reset"; +		case OHCI_USB_RESUME:	return "resume"; +		case OHCI_USB_OPER:	return "operational"; +		case OHCI_USB_SUSPEND:	return "suspend"; +	} +	return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status (ohci_t *controller) +{ +	struct ohci_regs	*regs = controller->regs; +	__u32			temp; + +	temp = readl (®s->revision) & 0xff; +	if (temp != 0x10) +		dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + +	temp = readl (®s->control); +	dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, +		(temp & OHCI_CTRL_RWE) ? " RWE" : "", +		(temp & OHCI_CTRL_RWC) ? " RWC" : "", +		(temp & OHCI_CTRL_IR) ? " IR" : "", +		hcfs2string (temp & OHCI_CTRL_HCFS), +		(temp & OHCI_CTRL_BLE) ? " BLE" : "", +		(temp & OHCI_CTRL_CLE) ? " CLE" : "", +		(temp & OHCI_CTRL_IE) ? " IE" : "", +		(temp & OHCI_CTRL_PLE) ? " PLE" : "", +		temp & OHCI_CTRL_CBSR +		); + +	temp = readl (®s->cmdstatus); +	dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, +		(temp & OHCI_SOC) >> 16, +		(temp & OHCI_OCR) ? " OCR" : "", +		(temp & OHCI_BLF) ? " BLF" : "", +		(temp & OHCI_CLF) ? " CLF" : "", +		(temp & OHCI_HCR) ? " HCR" : "" +		); + +	ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); +	ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + +	maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + +	maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); +	maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + +	maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); +	maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + +	maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ +	__u32			temp, ndp, i; + +	temp = roothub_a (controller); +	ndp = (temp & RH_A_NDP); + +	if (verbose) { +		dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, +			((temp & RH_A_POTPGT) >> 24) & 0xff, +			(temp & RH_A_NOCP) ? " NOCP" : "", +			(temp & RH_A_OCPM) ? " OCPM" : "", +			(temp & RH_A_DT) ? " DT" : "", +			(temp & RH_A_NPS) ? " NPS" : "", +			(temp & RH_A_PSM) ? " PSM" : "", +			ndp +			); +		temp = roothub_b (controller); +		dbg ("roothub.b: %08x PPCM=%04x DR=%04x", +			temp, +			(temp & RH_B_PPCM) >> 16, +			(temp & RH_B_DR) +			); +		temp = roothub_status (controller); +		dbg ("roothub.status: %08x%s%s%s%s%s%s", +			temp, +			(temp & RH_HS_CRWE) ? " CRWE" : "", +			(temp & RH_HS_OCIC) ? " OCIC" : "", +			(temp & RH_HS_LPSC) ? " LPSC" : "", +			(temp & RH_HS_DRWE) ? " DRWE" : "", +			(temp & RH_HS_OCI) ? " OCI" : "", +			(temp & RH_HS_LPS) ? " LPS" : "" +			); +	} + +	for (i = 0; i < ndp; i++) { +		temp = roothub_portstatus (controller, i); +		dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", +			i, +			temp, +			(temp & RH_PS_PRSC) ? " PRSC" : "", +			(temp & RH_PS_OCIC) ? " OCIC" : "", +			(temp & RH_PS_PSSC) ? " PSSC" : "", +			(temp & RH_PS_PESC) ? " PESC" : "", +			(temp & RH_PS_CSC) ? " CSC" : "", + +			(temp & RH_PS_LSDA) ? " LSDA" : "", +			(temp & RH_PS_PPS) ? " PPS" : "", +			(temp & RH_PS_PRS) ? " PRS" : "", +			(temp & RH_PS_POCI) ? " POCI" : "", +			(temp & RH_PS_PSS) ? " PSS" : "", + +			(temp & RH_PS_PES) ? " PES" : "", +			(temp & RH_PS_CCS) ? " CCS" : "" +			); +	} +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ +	dbg ("OHCI controller usb-%s state", controller->slot_name); + +	/* dumps some of the state we know about */ +	ohci_dump_status (controller); +	if (verbose) +		ep_print_int_eds (controller, "hcca"); +	dbg ("hcca frame #%04x", controller->hcca->frame_no); +	ohci_dump_roothub (controller, 1); +} + + +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, +		int transfer_len, struct devrequest *setup, int interval) +{ +	ohci_t *ohci; +	ed_t * ed; +	urb_priv_t *purb_priv; +	int i, size = 0; + +	ohci = &gohci; + +	/* when controller's hung, permit only roothub cleanup attempts +	 * such as powering down ports */ +	if (ohci->disabled) { +		err("sohci_submit_job: EPIPE"); +		return -1; +	} + +	/* if we have an unfinished URB from previous transaction let's +	 * fail and scream as quickly as possible so as not to corrupt +	 * further communication */ +	if (!urb_finished) { +		err("sohci_submit_job: URB NOT FINISHED"); +		return -1; +	} +	/* we're about to begin a new transaction here so mark the URB unfinished */ +	urb_finished = 0; + +	/* every endpoint has a ed, locate and fill it */ +	if (!(ed = ep_add_ed (dev, pipe))) { +		err("sohci_submit_job: ENOMEM"); +		return -1; +	} + +	/* for the private part of the URB we need the number of TDs (size) */ +	switch (usb_pipetype (pipe)) { +		case PIPE_BULK: /* one TD for every 4096 Byte */ +			size = (transfer_len - 1) / 4096 + 1; +			break; +		case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ +			size = (transfer_len == 0)? 2: +						(transfer_len - 1) / 4096 + 3; +			break; +	} + +	if (size >= (N_URB_TD - 1)) { +		err("need %d TDs, only have %d", size, N_URB_TD); +		return -1; +	} +	purb_priv = &urb_priv; +	purb_priv->pipe = pipe; + +	/* fill the private part of the URB */ +	purb_priv->length = size; +	purb_priv->ed = ed; +	purb_priv->actual_length = 0; + +	/* allocate the TDs */ +	/* note that td[0] was allocated in ep_add_ed */ +	for (i = 0; i < size; i++) { +		purb_priv->td[i] = td_alloc (dev); +		if (!purb_priv->td[i]) { +			purb_priv->length = i; +			urb_free_priv (purb_priv); +			err("sohci_submit_job: ENOMEM"); +			return -1; +		} +	} + +	if (ed->state == ED_NEW || (ed->state & ED_DEL)) { +		urb_free_priv (purb_priv); +		err("sohci_submit_job: EINVAL"); +		return -1; +	} + +	/* link the ed into a chain if is not already */ +	if (ed->state != ED_OPER) +		ep_link (ohci, ed); + +	/* fill the TDs and link it to the ed */ +	td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); + +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ +	ohci_t *ohci = &gohci; + +	return ohci_cpu_to_le16 (ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link (ohci_t *ohci, ed_t *edi) +{ +	volatile ed_t *ed = edi; + +	ed->state = ED_OPER; + +	switch (ed->type) { +	case PIPE_CONTROL: +		ed->hwNextED = 0; +		if (ohci->ed_controltail == NULL) { +			writel (ed, &ohci->regs->ed_controlhead); +		} else { +			ohci->ed_controltail->hwNextED = ohci_cpu_to_le32 ((unsigned long)ed); +		} +		ed->ed_prev = ohci->ed_controltail; +		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && +			!ohci->ed_rm_list[1] && !ohci->sleeping) { +			ohci->hc_control |= OHCI_CTRL_CLE; +			writel (ohci->hc_control, &ohci->regs->control); +		} +		ohci->ed_controltail = edi; +		break; + +	case PIPE_BULK: +		ed->hwNextED = 0; +		if (ohci->ed_bulktail == NULL) { +			writel (ed, &ohci->regs->ed_bulkhead); +		} else { +			ohci->ed_bulktail->hwNextED = ohci_cpu_to_le32 ((unsigned long)ed); +		} +		ed->ed_prev = ohci->ed_bulktail; +		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && +			!ohci->ed_rm_list[1] && !ohci->sleeping) { +			ohci->hc_control |= OHCI_CTRL_BLE; +			writel (ohci->hc_control, &ohci->regs->control); +		} +		ohci->ed_bulktail = edi; +		break; +	} +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink (ohci_t *ohci, ed_t *edi) +{ +	volatile ed_t *ed = edi; + +	ed->hwINFO |= ohci_cpu_to_le32 (OHCI_ED_SKIP); + +	switch (ed->type) { +	case PIPE_CONTROL: +		if (ed->ed_prev == NULL) { +			if (!ed->hwNextED) { +				ohci->hc_control &= ~OHCI_CTRL_CLE; +				writel (ohci->hc_control, &ohci->regs->control); +			} +			writel (ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); +		} else { +			ed->ed_prev->hwNextED = ed->hwNextED; +		} +		if (ohci->ed_controltail == ed) { +			ohci->ed_controltail = ed->ed_prev; +		} else { +			((ed_t *)ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; +		} +		break; + +	case PIPE_BULK: +		if (ed->ed_prev == NULL) { +			if (!ed->hwNextED) { +				ohci->hc_control &= ~OHCI_CTRL_BLE; +				writel (ohci->hc_control, &ohci->regs->control); +			} +			writel (ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); +		} else { +			ed->ed_prev->hwNextED = ed->hwNextED; +		} +		if (ohci->ed_bulktail == ed) { +			ohci->ed_bulktail = ed->ed_prev; +		} else { +			((ed_t *)ohci_cpu_to_le32 (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; +		} +		break; +	} +	ed->state = ED_UNLINK; +	return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless	so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) +{ +	td_t *td; +	ed_t *ed_ret; +	volatile ed_t *ed; + +	ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | +			(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; + +	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { +		err("ep_add_ed: pending delete"); +		/* pending delete request */ +		return NULL; +	} + +	if (ed->state == ED_NEW) { +		ed->hwINFO = ohci_cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ +		/* dummy td; end of td list for ed */ +		td = td_alloc (usb_dev); +		ed->hwTailP = ohci_cpu_to_le32 ((unsigned long)td); +		ed->hwHeadP = ed->hwTailP; +		ed->state = ED_UNLINK; +		ed->type = usb_pipetype (pipe); +		ohci_dev.ed_cnt++; +	} + +	ed->hwINFO = ohci_cpu_to_le32 (usb_pipedevice (pipe) +			| usb_pipeendpoint (pipe) << 7 +			| (usb_pipeisoc (pipe)? 0x8000: 0) +			| (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) +			| (usb_dev->speed == USB_SPEED_LOW) << 13 +			| usb_maxpacket (usb_dev, pipe) << 16); + +	return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill (ohci_t *ohci, unsigned int info, +	void *data, int len, +	struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ +	volatile td_t  *td, *td_pt; +#ifdef OHCI_FILL_TRACE +	int i; +#endif + +	if (index > urb_priv->length) { +		err("index > length"); +		return; +	} +	/* use this td as the next dummy */ +	td_pt = urb_priv->td [index]; +	td_pt->hwNextTD = 0; + +	/* fill the old dummy TD */ +	td = urb_priv->td [index] = (td_t *)(ohci_cpu_to_le32 (urb_priv->ed->hwTailP) & ~0xf); + +	td->ed = urb_priv->ed; +	td->next_dl_td = NULL; +	td->index = index; +	td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE +	if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { +		for (i = 0; i < len; i++) +		printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); +		printf("\n"); +	} +#endif +	if (!len) +		data = 0; + +	td->hwINFO = ohci_cpu_to_le32 (info); +	td->hwCBP = ohci_cpu_to_le32 ((unsigned long)data); +	if (data) +		td->hwBE = ohci_cpu_to_le32 ((unsigned long)(data + len - 1)); +	else +		td->hwBE = 0; +	td->hwNextTD = ohci_cpu_to_le32 ((unsigned long)td_pt); + +	/* append to queue */ +	td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ +static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, +	int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) +{ +	ohci_t *ohci = &gohci; +	int data_len = transfer_len; +	void *data; +	int cnt = 0; +	__u32 info = 0; +	unsigned int toggle = 0; + +	/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ +	if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { +		toggle = TD_T_TOGGLE; +	} else { +		toggle = TD_T_DATA0; +		usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); +	} +	urb->td_cnt = 0; +	if (data_len) +		data = buffer; +	else +		data = 0; + +	switch (usb_pipetype (pipe)) { +	case PIPE_BULK: +		info = usb_pipeout (pipe)? +			TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; +		while(data_len > 4096) { +			td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); +			data += 4096; data_len -= 4096; cnt++; +		} +		info = usb_pipeout (pipe)? +			TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; +		td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); +		cnt++; + +		if (!ohci->sleeping) +			writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ +		break; + +	case PIPE_CONTROL: +		info = TD_CC | TD_DP_SETUP | TD_T_DATA0; +		td_fill (ohci, info, setup, 8, dev, cnt++, urb); +		if (data_len > 0) { +			info = usb_pipeout (pipe)? +				TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; +			/* NOTE:  mishandles transfers >8K, some >4K */ +			td_fill (ohci, info, data, data_len, dev, cnt++, urb); +		} +		info = usb_pipeout (pipe)? +			TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; +		td_fill (ohci, info, data, 0, dev, cnt++, urb); +		if (!ohci->sleeping) +			writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ +		break; +	} +	if (urb->length != cnt) +		dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ +	__u32 tdBE, tdCBP; +	urb_priv_t *lurb_priv = &urb_priv; + +	tdBE   = ohci_cpu_to_le32 (td->hwBE); +	tdCBP  = ohci_cpu_to_le32 (td->hwCBP); + + +	if (!(usb_pipecontrol(lurb_priv->pipe) && +	    ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { +		if (tdBE != 0) { +			if (td->hwCBP == 0) +				lurb_priv->actual_length += tdBE - td->data + 1; +			else +				lurb_priv->actual_length += tdCBP - td->data; +		} +	} +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static td_t * dl_reverse_done_list (ohci_t *ohci) +{ +	__u32 td_list_hc; +	td_t *td_rev = NULL; +	td_t *td_list = NULL; +	urb_priv_t *lurb_priv = NULL; + +	td_list_hc = ohci_cpu_to_le32 (ohci->hcca->done_head) & 0xfffffff0; +	ohci->hcca->done_head = 0; + +	while (td_list_hc) { +		td_list = (td_t *)td_list_hc; + +		if (TD_CC_GET (ohci_cpu_to_le32 (td_list->hwINFO))) { +			lurb_priv = &urb_priv; +			dbg(" USB-error/status: %x : %p", +					TD_CC_GET (ohci_cpu_to_le32 (td_list->hwINFO)), td_list); +			if (td_list->ed->hwHeadP & ohci_cpu_to_le32 (0x1)) { +				if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { +					td_list->ed->hwHeadP = +						(lurb_priv->td[lurb_priv->length - 1]->hwNextTD & ohci_cpu_to_le32 (0xfffffff0)) | +									(td_list->ed->hwHeadP & ohci_cpu_to_le32 (0x2)); +					lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; +				} else +					td_list->ed->hwHeadP &= ohci_cpu_to_le32 (0xfffffff2); +			} +#ifdef CONFIG_MPC5200 +			td_list->hwNextTD = 0; +#endif +		} + +		td_list->next_dl_td = td_rev; +		td_rev = td_list; +		td_list_hc = ohci_cpu_to_le32 (td_list->hwNextTD) & 0xfffffff0; +	} +	return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* td done list */ +static int dl_done_list (ohci_t *ohci, td_t *td_list) +{ +	td_t *td_list_next = NULL; +	ed_t *ed; +	int cc = 0; +	int stat = 0; +	/* urb_t *urb; */ +	urb_priv_t *lurb_priv; +	__u32 tdINFO, edHeadP, edTailP; + +	while (td_list) { +		td_list_next = td_list->next_dl_td; + +		lurb_priv = &urb_priv; +		tdINFO = ohci_cpu_to_le32 (td_list->hwINFO); + +		ed = td_list->ed; + +		dl_transfer_length(td_list); + +		/* error code of transfer */ +		cc = TD_CC_GET (tdINFO); +		if (++(lurb_priv->td_cnt) == lurb_priv->length) { +			if ((ed->state & (ED_OPER | ED_UNLINK)) +					&& (lurb_priv->state != URB_DEL)) { +				dbg("ConditionCode %#x", cc); +				stat = cc_to_error[cc]; +				urb_finished = 1; +			} +		} + +		if (ed->state != ED_NEW) { +			edHeadP = ohci_cpu_to_le32 (ed->hwHeadP) & 0xfffffff0; +			edTailP = ohci_cpu_to_le32 (ed->hwTailP); + +			/* unlink eds if they are not busy */ +			if ((edHeadP == edTailP) && (ed->state == ED_OPER)) +				ep_unlink (ohci, ed); +		} + +		td_list = td_list_next; +	} +	return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +#include <usbroothubdes.h> + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x)			len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x)		{info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} +#define WR_RH_PORTSTAT(x)	{info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} +#else +#define WR_RH_STAT(x)		writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x)	writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT		roothub_status(&gohci) +#define RD_RH_PORTSTAT		roothub_portstatus(&gohci,wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ +	__u32 temp, ndp, i; +	int res; + +	res = -1; +	temp = roothub_a (controller); +	ndp = (temp & RH_A_NDP); +	for (i = 0; i < ndp; i++) { +		temp = roothub_portstatus (controller, i); +		/* check for a device disconnect */ +		if (((temp & (RH_PS_PESC | RH_PS_CSC)) == +			(RH_PS_PESC | RH_PS_CSC)) && +			((temp & RH_PS_CCS) == 0)) { +			res = i; +			break; +		} +	} +	return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, +		void *buffer, int transfer_len, struct devrequest *cmd) +{ +	void * data = buffer; +	int leni = transfer_len; +	int len = 0; +	int stat = 0; +	__u32 datab[4]; +	__u8 *data_buf = (__u8 *)datab; +	__u16 bmRType_bReq; +	__u16 wValue; +	__u16 wIndex; +	__u16 wLength; + +#ifdef DEBUG +urb_priv.actual_length = 0; +pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +#endif +	if (usb_pipeint(pipe)) { +		info("Root-Hub submit IRQ: NOT implemented"); +		return 0; +	} + +	bmRType_bReq  = cmd->requesttype | (cmd->request << 8); +	wValue	      = m16_swap (cmd->value); +	wIndex	      = m16_swap (cmd->index); +	wLength	      = m16_swap (cmd->length); + +	info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", +		dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + +	switch (bmRType_bReq) { +	/* Request Destination: +	   without flags: Device, +	   RH_INTERFACE: interface, +	   RH_ENDPOINT: endpoint, +	   RH_CLASS means HUB here, +	   RH_OTHER | RH_CLASS	almost ever means HUB_PORT here +	*/ + +	case RH_GET_STATUS: +			*(__u16 *) data_buf = m16_swap (1); OK (2); +	case RH_GET_STATUS | RH_INTERFACE: +			*(__u16 *) data_buf = m16_swap (0); OK (2); +	case RH_GET_STATUS | RH_ENDPOINT: +			*(__u16 *) data_buf = m16_swap (0); OK (2); +	case RH_GET_STATUS | RH_CLASS: +			*(__u32 *) data_buf = m32_swap ( +				RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); +			OK (4); +	case RH_GET_STATUS | RH_OTHER | RH_CLASS: +			*(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); + +	case RH_CLEAR_FEATURE | RH_ENDPOINT: +		switch (wValue) { +			case (RH_ENDPOINT_STALL): OK (0); +		} +		break; + +	case RH_CLEAR_FEATURE | RH_CLASS: +		switch (wValue) { +			case RH_C_HUB_LOCAL_POWER: +				OK(0); +			case (RH_C_HUB_OVER_CURRENT): +					WR_RH_STAT(RH_HS_OCIC); OK (0); +		} +		break; + +	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: +		switch (wValue) { +			case (RH_PORT_ENABLE): +					WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); +			case (RH_PORT_SUSPEND): +					WR_RH_PORTSTAT (RH_PS_POCI); OK (0); +			case (RH_PORT_POWER): +					WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); +			case (RH_C_PORT_CONNECTION): +					WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); +			case (RH_C_PORT_ENABLE): +					WR_RH_PORTSTAT (RH_PS_PESC); OK (0); +			case (RH_C_PORT_SUSPEND): +					WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); +			case (RH_C_PORT_OVER_CURRENT): +					WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); +			case (RH_C_PORT_RESET): +					WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); +		} +		break; + +	case RH_SET_FEATURE | RH_OTHER | RH_CLASS: +		switch (wValue) { +			case (RH_PORT_SUSPEND): +					WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); +			case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ +					if (RD_RH_PORTSTAT & RH_PS_CCS) +					    WR_RH_PORTSTAT (RH_PS_PRS); +					OK (0); +			case (RH_PORT_POWER): +					WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); +			case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ +					if (RD_RH_PORTSTAT & RH_PS_CCS) +					    WR_RH_PORTSTAT (RH_PS_PES ); +					OK (0); +		} +		break; + +	case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); + +	case RH_GET_DESCRIPTOR: +		switch ((wValue & 0xff00) >> 8) { +			case (0x01): /* device descriptor */ +				len = min_t(unsigned int, +					  leni, +					  min_t(unsigned int, +					      sizeof (root_hub_dev_des), +					      wLength)); +				data_buf = root_hub_dev_des; OK(len); +			case (0x02): /* configuration descriptor */ +				len = min_t(unsigned int, +					  leni, +					  min_t(unsigned int, +					      sizeof (root_hub_config_des), +					      wLength)); +				data_buf = root_hub_config_des; OK(len); +			case (0x03): /* string descriptors */ +				if(wValue==0x0300) { +					len = min_t(unsigned int, +						  leni, +						  min_t(unsigned int, +						      sizeof (root_hub_str_index0), +						      wLength)); +					data_buf = root_hub_str_index0; +					OK(len); +				} +				if(wValue==0x0301) { +					len = min_t(unsigned int, +						  leni, +						  min_t(unsigned int, +						      sizeof (root_hub_str_index1), +						      wLength)); +					data_buf = root_hub_str_index1; +					OK(len); +			} +			default: +				stat = USB_ST_STALLED; +		} +		break; + +	case RH_GET_DESCRIPTOR | RH_CLASS: +	    { +		    __u32 temp = roothub_a (&gohci); + +		    data_buf [0] = 9;		/* min length; */ +		    data_buf [1] = 0x29; +		    data_buf [2] = temp & RH_A_NDP; +		    data_buf [3] = 0; +		    if (temp & RH_A_PSM)	/* per-port power switching? */ +			data_buf [3] |= 0x1; +		    if (temp & RH_A_NOCP)	/* no overcurrent reporting? */ +			data_buf [3] |= 0x10; +		    else if (temp & RH_A_OCPM)	/* per-port overcurrent reporting? */ +			data_buf [3] |= 0x8; + +		    /* corresponds to data_buf[4-7] */ +		    datab [1] = 0; +		    data_buf [5] = (temp & RH_A_POTPGT) >> 24; +		    temp = roothub_b (&gohci); +		    data_buf [7] = temp & RH_B_DR; +		    if (data_buf [2] < 7) { +			data_buf [8] = 0xff; +		    } else { +			data_buf [0] += 2; +			data_buf [8] = (temp & RH_B_DR) >> 8; +			data_buf [10] = data_buf [9] = 0xff; +		    } + +		    len = min_t(unsigned int, leni, +			      min_t(unsigned int, data_buf [0], wLength)); +		    OK (len); +		} + +	case RH_GET_CONFIGURATION:	*(__u8 *) data_buf = 0x01; OK (1); + +	case RH_SET_CONFIGURATION:	WR_RH_STAT (0x10000); OK (0); + +	default: +		dbg ("unsupported root hub command"); +		stat = USB_ST_STALLED; +	} + +#ifdef	DEBUG +	ohci_dump_roothub (&gohci, 1); +#endif + +	len = min_t(int, len, leni); +	if (data != data_buf) +	    memcpy (data, data_buf, len); +	dev->act_len = len; +	dev->status = stat; + +#ifdef DEBUG +	if (transfer_len) +		urb_priv.actual_length = transfer_len; +	pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#endif + +	return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		int transfer_len, struct devrequest *setup, int interval) +{ +	int stat = 0; +	int maxsize = usb_maxpacket(dev, pipe); +	int timeout; + +	/* device pulled? Shortcut the action. */ +	if (devgone == dev) { +		dev->status = USB_ST_CRC_ERR; +		return 0; +	} + +#ifdef DEBUG +	urb_priv.actual_length = 0; +	pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#endif +	if (!maxsize) { +		err("submit_common_message: pipesize for pipe %lx is zero", +			pipe); +		return -1; +	} + +	if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { +		err("sohci_submit_job failed"); +		return -1; +	} + +	/* allow more time for a BULK device to react - some are slow */ +#define BULK_TO	 5000	/* timeout in milliseconds */ +	if (usb_pipebulk(pipe)) +		timeout = BULK_TO; +	else +		timeout = 100; + +	/* wait for it to complete */ +	for (;;) { +		/* check whether the controller is done */ +		stat = hc_interrupt(); +		if (stat < 0) { +			stat = USB_ST_CRC_ERR; +			break; +		} + +		/* NOTE: since we are not interrupt driven in U-Boot and always +		 * handle only one URB at a time, we cannot assume the +		 * transaction finished on the first successful return from +		 * hc_interrupt().. unless the flag for current URB is set, +		 * meaning that all TD's to/from device got actually +		 * transferred and processed. If the current URB is not +		 * finished we need to re-iterate this loop so as +		 * hc_interrupt() gets called again as there needs to be some +		 * more TD's to process still */ +		if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { +			/* 0xff is returned for an SF-interrupt */ +			break; +		} + +		if (--timeout) { +			mdelay(1); +			if (!urb_finished) +				dbg("\%"); + +		} else { +			err("CTL:TIMEOUT "); +			dbg("submit_common_msg: TO status %x\n", stat); +			stat = USB_ST_CRC_ERR; +			urb_finished = 1; +			break; +		} +	} +#if 0 +	/* we got an Root Hub Status Change interrupt */ +	if (got_rhsc) { +#ifdef DEBUG +		ohci_dump_roothub (&gohci, 1); +#endif +		got_rhsc = 0; +		/* abuse timeout */ +		timeout = rh_check_port_status(&gohci); +		if (timeout >= 0) { +#if 0 /* this does nothing useful, but leave it here in case that changes */ +			/* the called routine adds 1 to the passed value */ +			usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); +#endif +			/* +			 * XXX +			 * This is potentially dangerous because it assumes +			 * that only one device is ever plugged in! +			 */ +			devgone = dev; +		} +	} +#endif + +	dev->status = stat; +	dev->act_len = transfer_len; + +#ifdef DEBUG +	pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe)); +#endif + +	/* free TDs in urb_priv */ +	urb_free_priv (&urb_priv); +	return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		int transfer_len) +{ +	info("submit_bulk_msg"); +	return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		int transfer_len, struct devrequest *setup) +{ +	int maxsize = usb_maxpacket(dev, pipe); + +	info("submit_control_msg"); +#ifdef DEBUG +	urb_priv.actual_length = 0; +	pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#endif +	if (!maxsize) { +		err("submit_control_message: pipesize for pipe %lx is zero", +			pipe); +		return -1; +	} +	if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { +		gohci.rh.dev = dev; +		/* root hub - redirect */ +		return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, +			setup); +	} + +	return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		int transfer_len, int interval) +{ +	info("submit_int_msg"); +	return -1; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset (ohci_t *ohci) +{ +	int timeout = 30; +	int smm_timeout = 50; /* 0,5 sec */ + +	if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ +		writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ +		info("USB HC TakeOver from SMM"); +		while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { +			mdelay (10); +			if (--smm_timeout == 0) { +				err("USB HC TakeOver failed!"); +				return -1; +			} +		} +	} + +	/* Disable HC interrupts */ +	writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + +	dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;", +		ohci->slot_name, +		readl (&ohci->regs->control)); + +	/* Reset USB (needed by some controllers) */ +	ohci->hc_control = 0; +	writel (ohci->hc_control, &ohci->regs->control); + +	/* HC Reset requires max 10 us delay */ +	writel (OHCI_HCR,  &ohci->regs->cmdstatus); +	while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { +		if (--timeout == 0) { +			err("USB HC reset timed out!"); +			return -1; +		} +		udelay (1); +	} +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start (ohci_t * ohci) +{ +	__u32 mask; +	unsigned int fminterval; + +	ohci->disabled = 1; + +	/* Tell the controller where the control and bulk lists are +	 * The lists are empty now. */ + +	writel (0, &ohci->regs->ed_controlhead); +	writel (0, &ohci->regs->ed_bulkhead); + +	writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */ + +	fminterval = 0x2edf; +	writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); +	fminterval |= ((((fminterval - 210) * 6) / 7) << 16); +	writel (fminterval, &ohci->regs->fminterval); +	writel (0x628, &ohci->regs->lsthresh); + +	/* start controller operations */ +	ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; +	ohci->disabled = 0; +	writel (ohci->hc_control, &ohci->regs->control); + +	/* disable all interrupts */ +	mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | +			OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | +			OHCI_INTR_OC | OHCI_INTR_MIE); +	writel (mask, &ohci->regs->intrdisable); +	/* clear all interrupts */ +	mask &= ~OHCI_INTR_MIE; +	writel (mask, &ohci->regs->intrstatus); +	/* Choose the interrupts we care about now  - but w/o MIE */ +	mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; +	writel (mask, &ohci->regs->intrenable); + +#ifdef	OHCI_USE_NPS +	/* required for AMD-756 and some Mac platforms */ +	writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, +		&ohci->regs->roothub.a); +	writel (RH_HS_LPSC, &ohci->regs->roothub.status); +#endif	/* OHCI_USE_NPS */ + +	/* POTPGT delay is bits 24-31, in 2 ms units. */ +	mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + +	/* connect the virtual root hub */ +	ohci->rh.devnum = 0; + +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int +hc_interrupt (void) +{ +	ohci_t *ohci = &gohci; +	struct ohci_regs *regs = ohci->regs; +	int ints; +	int stat = -1; + +	if ((ohci->hcca->done_head != 0) && +	     !(ohci_cpu_to_le32(ohci->hcca->done_head) & 0x01)) { + +		ints =  OHCI_INTR_WDH; + +	} else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { +		ohci->disabled++; +		err ("%s device removed!", ohci->slot_name); +		return -1; + +	} else if ((ints &= readl (®s->intrenable)) == 0) { +		dbg("hc_interrupt: returning..\n"); +		return 0xff; +	} + +	/* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ + +	if (ints & OHCI_INTR_RHSC) { +		got_rhsc = 1; +		stat = 0xff; +	} + +	if (ints & OHCI_INTR_UE) { +		ohci->disabled++; +		err ("OHCI Unrecoverable Error, controller usb-%s disabled", +			ohci->slot_name); +		/* e.g. due to PCI Master/Target Abort */ + +#ifdef	DEBUG +		ohci_dump (ohci, 1); +#endif +		/* FIXME: be optimistic, hope that bug won't repeat often. */ +		/* Make some non-interrupt context restart the controller. */ +		/* Count and limit the retries though; either hardware or */ +		/* software errors can go forever... */ +		hc_reset (ohci); +		return -1; +	} + +	if (ints & OHCI_INTR_WDH) { +		writel (OHCI_INTR_WDH, ®s->intrdisable); +		stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); +		writel (OHCI_INTR_WDH, ®s->intrenable); +	} + +	if (ints & OHCI_INTR_SO) { +		dbg("USB Schedule overrun\n"); +		writel (OHCI_INTR_SO, ®s->intrenable); +		stat = -1; +	} + +	/* FIXME:  this assumes SOF (1/ms) interrupts don't get lost... */ +	if (ints & OHCI_INTR_SF) { +		unsigned int frame = ohci_cpu_to_le16 (ohci->hcca->frame_no) & 1; +		mdelay(1); +		writel (OHCI_INTR_SF, ®s->intrdisable); +		if (ohci->ed_rm_list[frame] != NULL) +			writel (OHCI_INTR_SF, ®s->intrenable); +		stat = 0xff; +	} + +	writel (ints, ®s->intrstatus); +	return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci (ohci_t *ohci) +{ +	dbg ("USB HC release ohci usb-%s", ohci->slot_name); + +	if (!ohci->disabled) +		hc_reset (ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ +	memset (&gohci, 0, sizeof (ohci_t)); +	memset (&urb_priv, 0, sizeof (urb_priv_t)); + +	/* align the storage */ +	if ((__u32)&ghcca[0] & 0xff) { +		err("HCCA not aligned!!"); +		return -1; +	} +	phcca = &ghcca[0]; +	info("aligned ghcca %p", phcca); +	memset(&ohci_dev, 0, sizeof(struct ohci_device)); +	if ((__u32)&ohci_dev.ed[0] & 0x7) { +		err("EDs not aligned!!"); +		return -1; +	} +	memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); +	if ((__u32)gtd & 0x7) { +		err("TDs not aligned!!"); +		return -1; +	} +	ptd = gtd; +	gohci.hcca = phcca; +	memset (phcca, 0, sizeof (struct ohci_hcca)); + +	gohci.disabled = 1; +	gohci.sleeping = 0; +	gohci.irq = -1; +#if defined(CONFIG_440EP) +	gohci.regs = (struct ohci_regs *)(CONFIG_SYS_PERIPHERAL_BASE | 0x1000); +#elif defined(CONFIG_440EPX) || defined(CONFIG_SYS_USB_HOST) +	gohci.regs = (struct ohci_regs *)(CONFIG_SYS_USB_HOST); +#endif + +	gohci.flags = 0; +	gohci.slot_name = "ppc440"; + +	if (hc_reset (&gohci) < 0) { +		hc_release_ohci (&gohci); +		return -1; +	} + +	if (hc_start (&gohci) < 0) { +		err ("can't start usb-%s", gohci.slot_name); +		hc_release_ohci (&gohci); +		return -1; +	} + +#ifdef	DEBUG +	ohci_dump (&gohci, 1); +#endif +	ohci_inited = 1; +	urb_finished = 1; + +	return 0; +} + +int usb_lowlevel_stop(int index) +{ +	/* this gets called really early - before the controller has */ +	/* even been initialized! */ +	if (!ohci_inited) +		return 0; +	/* TODO release any interrupts, etc. */ +	/* call hc_release_ohci() here ? */ +	hc_reset (&gohci); +	return 0; +} + +#endif /* CONFIG_USB_OHCI */ diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/usb_ohci.h b/roms/u-boot/arch/powerpc/cpu/ppc4xx/usb_ohci.h new file mode 100644 index 00000000..2c3dc4f9 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/usb_ohci.h @@ -0,0 +1,410 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net> + * + * usb-ohci.h + */ + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ +	/* No  Error  */ 0, +	/* CRC Error  */ USB_ST_CRC_ERR, +	/* Bit Stuff  */ USB_ST_BIT_ERR, +	/* Data Togg  */ USB_ST_CRC_ERR, +	/* Stall      */ USB_ST_STALLED, +	/* DevNotResp */ -1, +	/* PIDCheck   */ USB_ST_BIT_ERR, +	/* UnExpPID   */ USB_ST_BIT_ERR, +	/* DataOver   */ USB_ST_BUF_ERR, +	/* DataUnder  */ USB_ST_BUF_ERR, +	/* reservd    */ -1, +	/* reservd    */ -1, +	/* BufferOver */ USB_ST_BUF_ERR, +	/* BuffUnder  */ USB_ST_BUF_ERR, +	/* Not Access */ -1, +	/* Not Access */ -1 +}; + +/* ED States */ + +#define ED_NEW		0x00 +#define ED_UNLINK	0x01 +#define ED_OPER		0x02 +#define ED_DEL		0x04 +#define ED_URB_DEL	0x08 + +/* usb_ohci_ed */ +struct ed { +	__u32 hwINFO; +	__u32 hwTailP; +	__u32 hwHeadP; +	__u32 hwNextED; + +	struct ed *ed_prev; +	__u8 int_period; +	__u8 int_branch; +	__u8 int_load; +	__u8 int_interval; +	__u8 state; +	__u8 type; +	__u16 last_iso; +	struct ed *ed_rm_list; + +	struct usb_device *usb_dev; +	__u32 unused[3]; +} __attribute__((aligned(16))); +typedef struct ed ed_t; + +/* TD info field */ +#define TD_CC	    0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC	    0x0C000000 +#define TD_T	    0x03000000 +#define TD_T_DATA0  0x02000000 +#define TD_T_DATA1  0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R	    0x00040000 +#define TD_DI	    0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP	    0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN    0x00100000 +#define TD_DP_OUT   0x00080000 + +#define TD_ISO	    0x00010000 +#define TD_DEL	    0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR	   0x00 +#define TD_CC_CRC	   0x01 +#define TD_CC_BITSTUFFING  0x02 +#define TD_CC_DATATOGGLEM  0x03 +#define TD_CC_STALL	   0x04 +#define TD_DEVNOTRESP	   0x05 +#define TD_PIDCHECKFAIL	   0x06 +#define TD_UNEXPECTEDPID   0x07 +#define TD_DATAOVERRUN	   0x08 +#define TD_DATAUNDERRUN	   0x09 +#define TD_BUFFEROVERRUN   0x0C +#define TD_BUFFERUNDERRUN  0x0D +#define TD_NOTACCESSED	   0x0F + +#define MAXPSW 1 + +struct td { +	__u32 hwINFO; +	__u32 hwCBP;		/* Current Buffer Pointer */ +	__u32 hwNextTD;		/* Next TD Pointer */ +	__u32 hwBE;		/* Memory Buffer End Pointer */ + +	__u16 hwPSW[MAXPSW]; +	__u8 unused; +	__u8 index; +	struct ed *ed; +	struct td *next_dl_td; +	struct usb_device *usb_dev; +	int transfer_len; +	__u32 data; + +	__u32 unused2[2]; +} __attribute__((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP	(1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of.  It must be 256-byte aligned. + */ + +#define NUM_INTS 32		/* part of the OHCI standard */ +struct ohci_hcca { +	__u32 int_table[NUM_INTS];	/* Interrupt ED table */ +#if defined(CONFIG_MPC5200) +	__u16 pad1;		/* set to 0 on each frame_no change */ +	__u16 frame_no;		/* current frame number */ +#else +	__u16 frame_no;		/* current frame number */ +	__u16 pad1;		/* set to 0 on each frame_no change */ +#endif +	__u32 done_head;	/* info returned for an interrupt */ +	u8 reserved_for_hc[116]; +} __attribute__((aligned(256))); + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS	15	/* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region.  This is Memory Mapped I/O.	You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { +	/* control and status registers */ +	__u32 revision; +	__u32 control; +	__u32 cmdstatus; +	__u32 intrstatus; +	__u32 intrenable; +	__u32 intrdisable; +	/* memory pointers */ +	__u32 hcca; +	__u32 ed_periodcurrent; +	__u32 ed_controlhead; +	__u32 ed_controlcurrent; +	__u32 ed_bulkhead; +	__u32 ed_bulkcurrent; +	__u32 donehead; +	/* frame counters */ +	__u32 fminterval; +	__u32 fmremaining; +	__u32 fmnumber; +	__u32 periodicstart; +	__u32 lsthresh; +	/* Root hub ports */ +	struct ohci_roothub_regs { +		__u32 a; +		__u32 b; +		__u32 status; +		__u32 portstatus[MAX_ROOT_PORTS]; +	} roothub; +} __attribute__((aligned(32))); + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */ +#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */ +#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */ +#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */ +#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */ +#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */ +#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */ +#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */ +#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +#	define OHCI_USB_RESET	(0 << 6) +#	define OHCI_USB_RESUME	(1 << 6) +#	define OHCI_USB_OPER	(2 << 6) +#	define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR	(1 << 0)	/* host controller reset */ +#define OHCI_CLF	(1 << 1)	/* control list filled */ +#define OHCI_BLF	(1 << 2)	/* bulk list filled */ +#define OHCI_OCR	(1 << 3)	/* ownership change request */ +#define OHCI_SOC	(3 << 16)	/* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */ +#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */ +#define OHCI_INTR_SF	(1 << 2)	/* start frame */ +#define OHCI_INTR_RD	(1 << 3)	/* resume detect */ +#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */ +#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */ +#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */ +#define OHCI_INTR_OC	(1 << 30)	/* ownership change */ +#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */ + +/* Virtual Root HUB */ +struct virt_root_hub { +	int devnum;		/* Address of Root Hub endpoint */ +	void *dev;		/* was urb */ +	void *int_addr; +	int send; +	int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE		   0x01 +#define RH_ENDPOINT		   0x02 +#define RH_OTHER		   0x03 + +#define RH_CLASS		   0x20 +#define RH_VENDOR		   0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS		0x0080 +#define RH_CLEAR_FEATURE	0x0100 +#define RH_SET_FEATURE		0x0300 +#define RH_SET_ADDRESS		0x0500 +#define RH_GET_DESCRIPTOR	0x0680 +#define RH_SET_DESCRIPTOR	0x0700 +#define RH_GET_CONFIGURATION	0x0880 +#define RH_SET_CONFIGURATION	0x0900 +#define RH_GET_STATE		0x0280 +#define RH_GET_INTERFACE	0x0A80 +#define RH_SET_INTERFACE	0x0B00 +#define RH_SYNC_FRAME		0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP		0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION	   0x00 +#define RH_PORT_ENABLE		   0x01 +#define RH_PORT_SUSPEND		   0x02 +#define RH_PORT_OVER_CURRENT	   0x03 +#define RH_PORT_RESET		   0x04 +#define RH_PORT_POWER		   0x08 +#define RH_PORT_LOW_SPEED	   0x09 + +#define RH_C_PORT_CONNECTION	   0x10 +#define RH_C_PORT_ENABLE	   0x11 +#define RH_C_PORT_SUSPEND	   0x12 +#define RH_C_PORT_OVER_CURRENT	   0x13 +#define RH_C_PORT_RESET		   0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER	   0x00 +#define RH_C_HUB_OVER_CURRENT	   0x01 + +#define RH_DEVICE_REMOTE_WAKEUP	   0x00 +#define RH_ENDPOINT_STALL	   0x01 + +#define RH_ACK			   0x01 +#define RH_REQ_ERR		   -1 +#define RH_NACK			   0x00 + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS	     0x00000001	/* current connect status */ +#define RH_PS_PES	     0x00000002	/* port enable status */ +#define RH_PS_PSS	     0x00000004	/* port suspend status */ +#define RH_PS_POCI	     0x00000008	/* port over current indicator */ +#define RH_PS_PRS	     0x00000010	/* port reset status */ +#define RH_PS_PPS	     0x00000100	/* port power status */ +#define RH_PS_LSDA	     0x00000200	/* low speed device attached */ +#define RH_PS_CSC	     0x00010000	/* connect status change */ +#define RH_PS_PESC	     0x00020000	/* port enable status change */ +#define RH_PS_PSSC	     0x00040000	/* port suspend status change */ +#define RH_PS_OCIC	     0x00080000	/* over current indicator change */ +#define RH_PS_PRSC	     0x00100000	/* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS	     0x00000001	/* local power status */ +#define RH_HS_OCI	     0x00000002	/* over current indicator */ +#define RH_HS_DRWE	     0x00008000	/* device remote wakeup enable */ +#define RH_HS_LPSC	     0x00010000	/* local power status change */ +#define RH_HS_OCIC	     0x00020000	/* over current indicator change */ +#define RH_HS_CRWE	     0x80000000	/* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR		0x0000ffff	/* device removable flags */ +#define RH_B_PPCM	0xffff0000	/* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP	(0xff << 0)	/* number of downstream ports */ +#define RH_A_PSM	(1 << 8)	/* power switching mode */ +#define RH_A_NPS	(1 << 9)	/* no power switching */ +#define RH_A_DT		(1 << 10)	/* device type (mbz) */ +#define RH_A_OCPM	(1 << 11)	/* over current protection mode */ +#define RH_A_NOCP	(1 << 12)	/* no over current protection */ +#define RH_A_POTPGT	(0xff << 24)	/* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct { +	ed_t *ed; +	__u16 length;		/* number of tds associated with this request */ +	__u16 td_cnt;		/* number of tds already serviced */ +	int state; +	unsigned long pipe; +	int actual_length; +	td_t *td[N_URB_TD];	/* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + +typedef struct ohci { +	struct ohci_hcca *hcca;	/* hcca */ +	/*dma_addr_t hcca_dma; */ + +	int irq; +	int disabled;		/* e.g. got a UE, we're hung */ +	int sleeping; +	unsigned long flags;	/* for HC bugs */ + +	struct ohci_regs *regs;	/* OHCI controller's memory */ + +	ed_t *ed_rm_list[2];	/* lists of all endpoints to be removed */ +	ed_t *ed_bulktail;	/* last endpoint of bulk list */ +	ed_t *ed_controltail;	/* last endpoint of control list */ +	int intrstatus; +	__u32 hc_control;	/* copy of the hc control reg */ +	struct usb_device *dev[32]; +	struct virt_root_hub rh; + +	const char *slot_name; +} ohci_t; + +#define NUM_EDS 8		/* num of preallocated endpoint descriptors */ + +struct ohci_device { +	ed_t ed[NUM_EDS]; +	int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD + 1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td *td_alloc(struct usb_device *usb_dev) +{ +	int i; +	struct td *td; + +	td = NULL; +	for (i = 0; i < NUM_TD; i++) { +		if (ptd[i].usb_dev == NULL) { +			td = &ptd[i]; +			td->usb_dev = usb_dev; +			break; +		} +	} + +	return td; +} + +static inline void ed_free(struct ed *ed) +{ +	ed->usb_dev = NULL; +} diff --git a/roms/u-boot/arch/powerpc/cpu/ppc4xx/xilinx_irq.c b/roms/u-boot/arch/powerpc/cpu/ppc4xx/xilinx_irq.c new file mode 100644 index 00000000..71e1be02 --- /dev/null +++ b/roms/u-boot/arch/powerpc/cpu/ppc4xx/xilinx_irq.c @@ -0,0 +1,89 @@ +/* + * (C) Copyright 2008 + * Ricado Ribalda-Universidad Autonoma de Madrid-ricardo.ribalda@uam.es + * This work has been supported by: QTechnology  http://qtec.com/ + * Based on interrupts.c Wolfgang Denk-DENX Software Engineering-wd@denx.de + * SPDX-License-Identifier:	GPL-2.0+ +*/ +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <asm/processor.h> +#include <asm/interrupt.h> +#include <asm/ppc4xx.h> +#include <ppc_asm.tmpl> +#include <commproc.h> +#include <asm/io.h> +#include <asm/xilinx_irq.h> + +DECLARE_GLOBAL_DATA_PTR; + +void pic_enable(void) +{ +	debug("Xilinx PIC at 0x%8x\n", intc); + +	/* +	 * Disable all external interrupts until they are +	 * explicitly requested. +	 */ +	out_be32((u32 *) IER, 0); + +	/* Acknowledge any pending interrupts just in case. */ +	out_be32((u32 *) IAR, 0xffffffff); + +	/* Turn on the Master Enable. */ +	out_be32((u32 *) MER, 0x3UL); + +	return; +} + +int xilinx_pic_irq_get(void) +{ +	u32 irq; +	irq = in_be32((u32 *) IVR); + +	/* If no interrupt is pending then all bits of the IVR are set to 1. As +	 * the IVR is as many bits wide as numbers of inputs are available. +	 * Therefore, if all bits of the IVR are set to one, its content will +	 * be bigger than XPAR_INTC_MAX_NUM_INTR_INPUTS. +	 */ +	if (irq >= XPAR_INTC_MAX_NUM_INTR_INPUTS) +		irq = -1;	/* report no pending interrupt. */ + +	debug("get_irq: %d\n", irq); +	return (irq); +} + +void pic_irq_enable(unsigned int irq) +{ +	u32 mask = IRQ_MASK(irq); +	debug("enable: %d\n", irq); +	out_be32((u32 *) SIE, mask); +} + +void pic_irq_disable(unsigned int irq) +{ +	u32 mask = IRQ_MASK(irq); +	debug("disable: %d\n", irq); +	out_be32((u32 *) CIE, mask); +} + +void pic_irq_ack(unsigned int irq) +{ +	u32 mask = IRQ_MASK(irq); +	debug("ack: %d\n", irq); +	out_be32((u32 *) IAR, mask); +} + +void external_interrupt(struct pt_regs *regs) +{ +	int irq; + +	irq = xilinx_pic_irq_get(); +	if (irq < 0) +		return; + +	interrupt_run_handler(irq); + +	return; +}  | 
