diff options
Diffstat (limited to 'cpu')
| -rw-r--r-- | cpu/ppc4xx/44x_spd_ddr2.c | 158 | ||||
| -rw-r--r-- | cpu/ppc4xx/4xx_enet.c | 168 | ||||
| -rw-r--r-- | cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c | 1212 | ||||
| -rw-r--r-- | cpu/ppc4xx/Makefile | 3 | ||||
| -rw-r--r-- | cpu/ppc4xx/miiphy.c | 37 | 
5 files changed, 1504 insertions, 74 deletions
diff --git a/cpu/ppc4xx/44x_spd_ddr2.c b/cpu/ppc4xx/44x_spd_ddr2.c index 15250d470..f1d76840f 100644 --- a/cpu/ppc4xx/44x_spd_ddr2.c +++ b/cpu/ppc4xx/44x_spd_ddr2.c @@ -60,8 +60,6 @@  		       "SDRAM_" #mnemonic, SDRAM_##mnemonic, data);	\  	} while (0) -static inline void ppc4xx_ibm_ddr2_register_dump(void); -  #if defined(CONFIG_SPD_EEPROM)  /*-----------------------------------------------------------------------------+ @@ -260,62 +258,19 @@ static void program_ecc_addr(unsigned long start_address,  			     unsigned long num_bytes,  			     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); +				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  int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);  void dcbz_area(u32 start_address, u32 num_bytes); -static 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; -} - -static 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); -	} -} -  static unsigned char spd_read(uchar chip, uint addr)  {  	unsigned char data[2]; @@ -609,7 +564,11 @@ phys_size_t initdram(int board_type)  	/*------------------------------------------------------------------  	 * DQS calibration.  	 *-----------------------------------------------------------------*/ +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +	DQS_autocalibration(); +#else  	program_DQS_calibration(dimm_populated, iic0_dimm_addr, num_dimm_banks); +#endif  #ifdef CONFIG_DDR_ECC  	/*------------------------------------------------------------------ @@ -2329,18 +2288,6 @@ static unsigned long is_ecc_enabled(void)  	return ecc;  } -static void 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'); -} -  #ifdef CONFIG_DDR_ECC  /*-----------------------------------------------------------------------------+   * program_ecc. @@ -2468,6 +2415,7 @@ static void program_ecc_addr(unsigned long start_address,  }  #endif +#if !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION)  /*-----------------------------------------------------------------------------+   * program_DQS_calibration.   *-----------------------------------------------------------------------------*/ @@ -3001,7 +2949,8 @@ static void test(void)  		(ppcMfdcr_sdram(SDRAM_MCOPT1) & ~SDRAM_MCOPT1_MCHK_MASK)  		| ecc_temp);  } -#endif +#endif /* !HARD_CODED_DQS */ +#endif /* !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) */  #else /* CONFIG_SPD_EEPROM */ @@ -3104,9 +3053,12 @@ phys_size_t initdram(int board_type)  	/* Set Delay Control Registers */  	mtsdram(SDRAM_DLCR, CFG_SDRAM0_DLCR); + +#if !defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION)  	mtsdram(SDRAM_RDCC, CFG_SDRAM0_RDCC);  	mtsdram(SDRAM_RQDC, CFG_SDRAM0_RQDC);  	mtsdram(SDRAM_RFDC, CFG_SDRAM0_RFDC); +#endif /* !CONFIG_PPC4xx_DDR_AUTOCALIBRATION */  	/*  	 * Enable Controller by SDRAM0_MCOPT2[DCEN] = 1: @@ -3115,18 +3067,98 @@ phys_size_t initdram(int board_type)  	mfsdram(SDRAM_MCOPT2, val);  	mtsdram(SDRAM_MCOPT2, val | SDRAM_MCOPT2_DCEN_ENABLE); +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) +#if !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) +	/*------------------------------------------------------------------ +	 | DQS calibration. +	 +-----------------------------------------------------------------*/ +	DQS_autocalibration(); +#endif /* !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) */ +#endif /* CONFIG_PPC4xx_DDR_AUTOCALIBRATION */ +  #if defined(CONFIG_DDR_ECC)  	ecc_init(CFG_SDRAM_BASE, CFG_MBYTES_SDRAM << 20);  #endif /* defined(CONFIG_DDR_ECC) */  	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 */ +  #endif /* !defined(CONFIG_NAND_U_BOOT) || defined(CONFIG_NAND_SPL) */  	return (CFG_MBYTES_SDRAM << 20);  }  #endif /* CONFIG_SPD_EEPROM */ -static inline void ppc4xx_ibm_ddr2_register_dump(void) +#if !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) +#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) */ + +void 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 /* !defined(CONFIG_NAND_U_BOOT) &&  !defined(CONFIG_NAND_SPL) */ + +inline void ppc4xx_ibm_ddr2_register_dump(void)  {  #if defined(DEBUG)  	printf("\nPPC4xx IBM DDR2 Register Dump:\n"); diff --git a/cpu/ppc4xx/4xx_enet.c b/cpu/ppc4xx/4xx_enet.c index 8a3833513..6d4d043e0 100644 --- a/cpu/ppc4xx/4xx_enet.c +++ b/cpu/ppc4xx/4xx_enet.c @@ -198,6 +198,7 @@  #define BI_PHYMODE_RMII  8  #endif  #endif +#define BI_PHYMODE_SGMII 9  #if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \      defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ @@ -216,6 +217,52 @@  #define MAL_RX_CHAN_MUL	1  #endif +/*--------------------------------------------------------------------+ + * Fixed PHY (PHY-less) support for Ethernet Ports. + *--------------------------------------------------------------------*/ + +/* + * Some boards do not have a PHY for each ethernet port. These ports + * are known as Fixed PHY (or PHY-less) ports. For such ports, set + * the appropriate CONFIG_PHY_ADDR equal to CONFIG_FIXED_PHY and + * then define CFG_FIXED_PHY_PORTS to define what the speed and + * duplex should be for these ports in the board configuration + * file. + * + * For Example: + *     #define CONFIG_FIXED_PHY   0xFFFFFFFF + * + *     #define CONFIG_PHY_ADDR    CONFIG_FIXED_PHY + *     #define CONFIG_PHY1_ADDR   1 + *     #define CONFIG_PHY2_ADDR   CONFIG_FIXED_PHY + *     #define CONFIG_PHY3_ADDR   3 + * + *     #define CFG_FIXED_PHY_PORT(devnum,speed,duplex) \ + *                     {devnum, speed, duplex}, + * + *     #define CFG_FIXED_PHY_PORTS \ + *                     CFG_FIXED_PHY_PORT(0,1000,FULL) \ + *                     CFG_FIXED_PHY_PORT(2,100,HALF) + */ + +#ifndef CONFIG_FIXED_PHY +#define CONFIG_FIXED_PHY	0xFFFFFFFF /* Fixed PHY (PHY-less) */ +#endif + +#ifndef CFG_FIXED_PHY_PORTS +#define CFG_FIXED_PHY_PORTS	/* default is an empty array */ +#endif + +struct fixed_phy_port { +	unsigned int devnum;	/* ethernet port */ +	unsigned int speed;	/* specified speed 10,100 or 1000 */ +	unsigned int duplex;	/* specified duplex FULL or HALF */ +}; + +static const struct fixed_phy_port fixed_phy_port[] = { +	CFG_FIXED_PHY_PORTS	/* defined in board configuration file */ +}; +  /*-----------------------------------------------------------------------------+   * Global variables. TX and RX descriptors and buffers.   *-----------------------------------------------------------------------------*/ @@ -611,8 +658,17 @@ int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis)  #if defined(CONFIG_460EX)  	mode = 9; +	mfsdr(SDR0_ETH_CFG, eth_cfg); +	if (((eth_cfg & SDR0_ETH_CFG_SGMII0_ENABLE) > 0) && +	    ((eth_cfg & SDR0_ETH_CFG_SGMII1_ENABLE) > 0)) +		mode = 11; /* config SGMII */  #else  	mode = 10; +	mfsdr(SDR0_ETH_CFG, eth_cfg); +	if (((eth_cfg & SDR0_ETH_CFG_SGMII0_ENABLE) > 0) && +	    ((eth_cfg & SDR0_ETH_CFG_SGMII1_ENABLE) > 0) && +	    ((eth_cfg & SDR0_ETH_CFG_SGMII2_ENABLE) > 0)) +		mode = 12; /* config SGMII */  #endif  	/* TODO: @@ -635,6 +691,8 @@ int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis)  	/*  	 * Right now only 2*RGMII is supported. Please extend when needed.  	 * sr - 2008-02-19 +	 * Add SGMII support. +	 * vg - 2008-07-28  	 */  	switch (mode) {  	case 1: @@ -761,6 +819,20 @@ int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis)  		bis->bi_phymode[2] = BI_PHYMODE_RGMII;  		bis->bi_phymode[3] = BI_PHYMODE_RGMII;  		break; +	case 11: +		/* 2 SGMII - 460EX */ +		bis->bi_phymode[0] = BI_PHYMODE_SGMII; +		bis->bi_phymode[1] = BI_PHYMODE_SGMII; +		bis->bi_phymode[2] = BI_PHYMODE_NONE; +		bis->bi_phymode[3] = BI_PHYMODE_NONE; +		break; +	case 12: +		/* 3 SGMII - 460GT */ +		bis->bi_phymode[0] = BI_PHYMODE_SGMII; +		bis->bi_phymode[1] = BI_PHYMODE_SGMII; +		bis->bi_phymode[2] = BI_PHYMODE_SGMII; +		bis->bi_phymode[3] = BI_PHYMODE_NONE; +		break;  	default:  		break;  	} @@ -945,9 +1017,50 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)  	out_be32((void *)EMAC_M1 + hw_p->hw_addr, mode_reg);  #endif /* defined(CONFIG_440GX) || defined(CONFIG_440SP) */ +#if defined(CONFIG_GPCS_PHY_ADDR) || defined(CONFIG_GPCS_PHY1_ADDR) || \ +    defined(CONFIG_GPCS_PHY2_ADDR) || defined(CONFIG_GPCS_PHY3_ADDR) +	if (bis->bi_phymode[devnum] == BI_PHYMODE_SGMII) { +		/* +		 * In SGMII mode, GPCS access is needed for +		 * communication with the internal SGMII SerDes. +		 */ +		switch (devnum) { +#if defined(CONFIG_GPCS_PHY_ADDR) +		case 0: +			reg = CONFIG_GPCS_PHY_ADDR; +			break; +#endif +#if defined(CONFIG_GPCS_PHY1_ADDR) +		case 1: +			reg = CONFIG_GPCS_PHY1_ADDR; +			break; +#endif +#if defined(CONFIG_GPCS_PHY2_ADDR) +		case 2: +			reg = CONFIG_GPCS_PHY2_ADDR; +			break; +#endif +#if defined(CONFIG_GPCS_PHY3_ADDR) +		case 3: +			reg = CONFIG_GPCS_PHY3_ADDR; +			break; +#endif +		} + +		mode_reg = in_be32((void *)EMAC_M1 + hw_p->hw_addr); +		mode_reg |= EMAC_M1_MF_1000GPCS | EMAC_M1_IPPA_SET(reg); +		out_be32((void *)EMAC_M1 + hw_p->hw_addr, mode_reg); + +		/* Configure GPCS interface to recommended setting for SGMII */ +		miiphy_reset(dev->name, reg); +		miiphy_write(dev->name, reg, 0x04, 0x8120); /* AsymPause, FDX */ +		miiphy_write(dev->name, reg, 0x07, 0x2801); /* msg_pg, toggle */ +		miiphy_write(dev->name, reg, 0x00, 0x0140); /* 1Gbps, FDX     */ +	} +#endif /* defined(CONFIG_GPCS_PHY_ADDR) */ +  	/* wait for PHY to complete auto negotiation */  	reg_short = 0; -#ifndef CONFIG_CS8952_PHY  	switch (devnum) {  	case 0:  		reg = CONFIG_PHY_ADDR; @@ -974,6 +1087,9 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)  	bis->bi_phynum[devnum] = reg; +	if (reg == CONFIG_FIXED_PHY) +		goto get_speed; +  #if defined(CONFIG_PHY_RESET)  	/*  	 * Reset the phy, only if its the first time through @@ -986,6 +1102,27 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)  		miiphy_write (dev->name, reg, 0x09, 0x0e00);  		miiphy_write (dev->name, reg, 0x04, 0x01e1);  #endif +#if defined(CONFIG_M88E1112_PHY) +		if (bis->bi_phymode[devnum] == BI_PHYMODE_SGMII) { +			/* +			 * Marvell 88E1112 PHY needs to have the SGMII MAC +			 * interace (page 2) properly configured to +			 * communicate with the 460EX/GT GPCS interface. +			 */ + +			/* Set access to Page 2 */ +			miiphy_write(dev->name, reg, 0x16, 0x0002); + +			miiphy_write(dev->name, reg, 0x00, 0x0040); /* 1Gbps */ +			miiphy_read(dev->name, reg, 0x1a, ®_short); +			reg_short |= 0x8000; /* bypass Auto-Negotiation */ +			miiphy_write(dev->name, reg, 0x1a, reg_short); +			miiphy_reset(dev->name, reg); /* reset MAC interface */ + +			/* Reset access to Page 0 */ +			miiphy_write(dev->name, reg, 0x16, 0x0000); +		} +#endif /* defined(CONFIG_M88E1112_PHY) */  		miiphy_reset (dev->name, reg);  #if defined(CONFIG_440GX) || \ @@ -1022,7 +1159,7 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)  			miiphy_write (dev->name, reg, 0x1f, 0x0000);  			/* end Vitesse/Cicada errata */  		} -#endif +#endif /* defined(CONFIG_CIS8201_PHY) */  #if defined(CONFIG_ET1011C_PHY)  		/* @@ -1041,9 +1178,9 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)  			miiphy_write(dev->name, reg, 0x1c, 0x74f0);  		} -#endif +#endif /* defined(CONFIG_ET1011C_PHY) */ -#endif +#endif /* defined(CONFIG_440GX) ... */  		/* Start/Restart autonegotiation */  		phy_setup_aneg (dev->name, reg);  		udelay (1000); @@ -1073,15 +1210,30 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)  			}  			udelay (1000);	/* 1 ms */  			miiphy_read (dev->name, reg, PHY_BMSR, ®_short); -  		}  		puts (" done\n");  		udelay (500000);	/* another 500 ms (results in faster booting) */  	} -#endif /* #ifndef CONFIG_CS8952_PHY */ -	speed = miiphy_speed (dev->name, reg); -	duplex = miiphy_duplex (dev->name, reg); +get_speed: +	if (reg == CONFIG_FIXED_PHY) { +		for (i = 0; i < ARRAY_SIZE(fixed_phy_port); i++) { +			if (devnum == fixed_phy_port[i].devnum) { +				speed = fixed_phy_port[i].speed; +				duplex = fixed_phy_port[i].duplex; +				break; +			} +		} + +		if (i == ARRAY_SIZE(fixed_phy_port)) { +			printf("ERROR: PHY (%s) not configured correctly!\n", +				dev->name); +			return -1; +		} +	} else { +		speed = miiphy_speed(dev->name, reg); +		duplex = miiphy_duplex(dev->name, reg); +	}  	if (hw_p->print_speed) {  		hw_p->print_speed = 0; diff --git a/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c b/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c new file mode 100644 index 000000000..83b9883a7 --- /dev/null +++ b/cpu/ppc4xx/4xx_ibm_ddr2_autocalib.c @@ -0,0 +1,1212 @@ +/* + * 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 + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +/* define DEBUG for debugging output (obviously ;-)) */ +#undef DEBUG + +#include <common.h> +#include <ppc4xx.h> +#include <asm/io.h> +#include <asm/processor.h> + +#if defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) + +/* + * Only compile the DDR auto-calibration code for NOR boot and + * not for NAND boot (NAND SPL and NAND U-Boot - NUB) + */ +#if !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) + +#define MAXBXCF			4 +#define SDRAM_RXBAS_SHIFT_1M	20 + +#if defined(CFG_DECREMENT_PATTERNS) +#define NUMMEMTESTS		24 +#else +#define NUMMEMTESTS		8 +#endif /* CFG_DECREMENT_PATTERNS */ +#define NUMLOOPS		1	/* configure as you deem approporiate */ +#define NUMMEMWORDS		16 + +/* 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 { +	u32 wrdtr; +	u32 clktr; +}; + +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) */ + +ulong __ddr_scan_option(ulong default_val) +{ +	return default_val; +} +ulong ddr_scan_option(ulong) __attribute__((weak, alias("__ddr_scan_option"))); + +static u32 *get_membase(int bxcr_num) +{ +	ulong bxcf; +	u32 *membase; + +#if defined(SDRAM_R0BAS) +	/* BAS from Memory Queue rank reg. */ +	membase = +	    (u32 *)(SDRAM_RXBAS_SDBA_DECODE(mfdcr_any(SDRAM_R0BAS+bxcr_num))); +	bxcf = 0;	/* just to satisfy the compiler */ +#else +	/* 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_ECCCR, 0xffffffff); +#if defined(SDRAM_R0BAS) +	mtdcr(SDRAM_ERRSTATLL, 0xffffffff); +#endif +} + +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_ECCCR, 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(CFG_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 /* CFG_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(); +		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(); +		} /* 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, SDRAM_RDCC_RDSS_T1 | 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, 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; +	ulong rffd; + +	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"); + +	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)); + +	rffd = rffd_average; +	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%08X\n", rqdc_reg); +	debug("RFDC: 0x%08X\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. + */ +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)); + +	ddr_scan_option((ulong)full_scan_options); + +	scan_list = +	      (struct sdram_timing *)ddr_scan_option((ulong)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_r("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); + +		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. +		 */ +		if (result != 0) { +			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. +			 */ +			if (result > best_result) { +				/* +				 * want the lowest Read Sample Cycle Select +				 */ +				val = (val & SDRAM_RDCC_RDSS_MASK) >> 30; +				debug("*** (%d) (%d) current_rdcc, best_rdcc\n", +							val, best_rdcc); +				if (val <= best_rdcc) { +					best_rdcc = val; +					tcal.clocks.wrdtr = wdtr; +					tcal.clocks.clktr = clkp; +					tcal.clocks.rdcc = (val << 30); +					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 (val <= best_rdcc) */ +			} /* if (result >= best_result) */ +		} /* if (result != 0) */ +		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); + +		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%08x\n", +				rqdc_reg); + +		mfsdram(SDRAM_RFDC, rfdc_reg); +		rfdc_reg &= ~(SDRAM_RFDC_RFFD_MASK); +		mtsdram(SDRAM_RFDC, rfdc_reg | +				SDRAM_RFDC_RFFD_ENCODE(tcal.autocal.rffd)); + +		mfsdram(SDRAM_RFDC, rfdc_reg); +		debug("*** best_result: read value SDRAM_RFDC 0x%08x\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; +} +#else /* defined(CONFIG_NAND_U_BOOT) || defined(CONFIG_NAND_SPL) */ +u32 DQS_autocalibration(void) +{ +	return 0; +} +#endif /* !defined(CONFIG_NAND_U_BOOT) && !defined(CONFIG_NAND_SPL) */ +#endif /* defined(CONFIG_PPC4xx_DDR_AUTOCALIBRATION) */ diff --git a/cpu/ppc4xx/Makefile b/cpu/ppc4xx/Makefile index c773400a5..463b57566 100644 --- a/cpu/ppc4xx/Makefile +++ b/cpu/ppc4xx/Makefile @@ -35,6 +35,9 @@ SOBJS	+= kgdb.o  COBJS	:= 40x_spd_sdram.o  COBJS	+= 44x_spd_ddr.o  COBJS	+= 44x_spd_ddr2.o +ifdef CONFIG_PPC4xx_DDR_AUTOCALIBRATION +COBJS	+= 4xx_ibm_ddr2_autocalib.o +endif  COBJS	+= 4xx_pci.o  COBJS	+= 4xx_pcie.o  COBJS	+= bedbug_405.o diff --git a/cpu/ppc4xx/miiphy.c b/cpu/ppc4xx/miiphy.c index c8827201e..84b1bbe54 100644 --- a/cpu/ppc4xx/miiphy.c +++ b/cpu/ppc4xx/miiphy.c @@ -180,8 +180,10 @@ int phy_setup_aneg (char *devname, unsigned char addr)   *   * 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 (void) +unsigned int miiphy_getemac_offset(u8 addr)  {  #if (defined(CONFIG_440) && \      !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \ @@ -233,6 +235,35 @@ unsigned int miiphy_getemac_offset (void)  		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_M1_IPPA_GET(in_be32((void *)EMAC_M1 + 0x100))) +			eoffset = 0x100; +		break; +#endif +#if defined(CONFIG_HAS_ETH2) && defined(CONFIG_GPCS_PHY2_ADDR) +	case CONFIG_GPCS_PHY2_ADDR: +		if (addr == EMAC_M1_IPPA_GET(in_be32((void *)EMAC_M1 + 0x300))) +			eoffset = 0x300; +		break; +#endif +#if defined(CONFIG_HAS_ETH3) && defined(CONFIG_GPCS_PHY3_ADDR) +	case CONFIG_GPCS_PHY3_ADDR: +		if (addr == EMAC_M1_IPPA_GET(in_be32((void *)EMAC_M1 + 0x400))) +			eoffset = 0x400; +		break; +#endif +	default: +		eoffset = 0; +		break; +	} +	return eoffset; +#endif +  	return 0;  #endif  } @@ -262,7 +293,7 @@ static int emac_miiphy_command(u8 addr, u8 reg, int cmd, u16 value)  	u32 emac_reg;  	u32 sta_reg; -	emac_reg = miiphy_getemac_offset(); +	emac_reg = miiphy_getemac_offset(addr);  	/* wait for completion */  	if (emac_miiphy_wait(emac_reg) != 0) @@ -311,7 +342,7 @@ int emac4xx_miiphy_read (char *devname, unsigned char addr, unsigned char reg,  	unsigned long sta_reg;  	unsigned long emac_reg; -	emac_reg = miiphy_getemac_offset (); +	emac_reg = miiphy_getemac_offset(addr);  	if (emac_miiphy_command(addr, reg, EMAC_STACR_READ, 0) != 0)  		return -1;  |