diff options
Diffstat (limited to 'arch/x86/cpu/sc520/sc520_sdram.c')
| -rw-r--r-- | arch/x86/cpu/sc520/sc520_sdram.c | 532 | 
1 files changed, 532 insertions, 0 deletions
| diff --git a/arch/x86/cpu/sc520/sc520_sdram.c b/arch/x86/cpu/sc520/sc520_sdram.c new file mode 100644 index 000000000..f3623f53f --- /dev/null +++ b/arch/x86/cpu/sc520/sc520_sdram.c @@ -0,0 +1,532 @@ +/* + * (C) Copyright 2010,2011 + * Graeme Russ, <graeme.russ@gmail.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/processor-flags.h> +#include <asm/ic/sc520.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct sc520_sdram_info { +	u8 banks; +	u8 columns; +	u8 rows; +	u8 size; +}; + +static void sc520_sizemem(void); +static void sc520_set_dram_timing(void); +static void sc520_set_dram_refresh_rate(void); +static void sc520_enable_dram_refresh(void); +static void sc520_enable_sdram(void); +#if CONFIG_SYS_SDRAM_ECC_ENABLE +static void sc520_enable_ecc(void) +#endif + +int dram_init_f(void) +{ +	sc520_sizemem(); +	sc520_set_dram_timing(); +	sc520_set_dram_refresh_rate(); +	sc520_enable_dram_refresh(); +	sc520_enable_sdram(); +#if CONFIG_SYS_SDRAM_ECC_ENABLE +	sc520_enable_ecc(); +#endif + +	return 0; +} + +static inline void sc520_dummy_write(void) +{ +	writew(0x0000, CACHELINESZ); +} +static inline void sc520_issue_sdram_op_mode_select(u8 command) +{ +	writeb(command, &sc520_mmcr->drcctl); +	sc520_dummy_write(); +} + +static inline int check_long(u32 test_long) +{ +	u8 i; +	u8 tmp_byte = (u8)(test_long & 0x000000ff); + +	for (i = 1; i < 4; i++) { +		if ((u8)((test_long >> (i * 8)) & 0x000000ff) != tmp_byte) +				return -1; +	} + +	return 0; +} + +static inline int write_and_test(u32 data, u32 address) +{ +	writel(data, address); +	if (readl(address) == data) +		return 0; /* Good */ +	else +		return -1; /* Bad */ +} + +static void sc520_enable_sdram(void) +{ +	u32 par_config; + +	/* Enable Writes, Caching and Code Execution to SDRAM */ +	par_config = readl(&sc520_mmcr->par[3]); +	par_config &= ~(SC520_PAR_EXEC_DIS | +			SC520_PAR_CACHE_DIS | +			SC520_PAR_WRITE_DIS); +	writel(par_config, &sc520_mmcr->par[3]); + +	par_config = readl(&sc520_mmcr->par[4]); +	par_config &= ~(SC520_PAR_EXEC_DIS | +			SC520_PAR_CACHE_DIS | +			SC520_PAR_WRITE_DIS); +	writel(par_config, &sc520_mmcr->par[4]); +} + +static void sc520_set_dram_timing(void) +{ +	u8 drctmctl = 0x00; + +#if defined CONFIG_SYS_SDRAM_DRCTMCTL +	/* just have your hardware designer _GIVE_ you what you need here! */ +	drctmctl = CONFIG_SYS_SDRAM_DRCTMCTL; +#else +	switch (CONFIG_SYS_SDRAM_RAS_CAS_DELAY) { +	case 2: +		break; +	case 3: +		drctmctl |= 0x01; +		break; +	case 4: +	default: +		drctmctl |= 0x02; +		break; +	} + +	switch (CONFIG_SYS_SDRAM_PRECHARGE_DELAY) { +	case 2: +		break; +	case 3: +		drctmctl |= 0x04; +		break; +	case 4: +	default: +		drctmctl |= 0x08; +		break; + +	case 6: +		drctmctl |= 0x0c; +		break; +	} + +	switch (CONFIG_SYS_SDRAM_CAS_LATENCY) { +	case 2: +		break; +	case 3: +	default: +		drctmctl |= 0x10; +		break; +	} +#endif +	writeb(drctmctl, &sc520_mmcr->drctmctl); + +	/* Issue load mode register command */ +	sc520_issue_sdram_op_mode_select(0x03); +} + +static void sc520_set_dram_refresh_rate(void) +{ +	u8 drctl; + +	drctl = readb(&sc520_mmcr->drcctl); +	drctl &= 0xcf; + +	switch (CONFIG_SYS_SDRAM_REFRESH_RATE) { +	case 78: +		break; +	case 156: +	default: +		drctl |= 0x10; +		break; +	case 312: +		drctl |= 0x20; +		break; +	case 624: +		drctl |= 0x30; +		break; +	} + +	writeb(drctl, &sc520_mmcr->drcctl); +} + +static void sc520_enable_dram_refresh(void) +{ +	u8 drctl; + +	drctl = readb(&sc520_mmcr->drcctl); +	drctl &= 0x30; /* keep refresh rate */ +	drctl |= 0x08; /* enable refresh, normal mode */ + +	writeb(drctl, &sc520_mmcr->drcctl); +} + +static void sc520_get_bank_info(int bank, struct sc520_sdram_info *bank_info) +{ +	u32 col_data; +	u32 row_data; + +	u32 drcbendadr; +	u16 drccfg; + +	u8 banks = 0x00; +	u8 columns = 0x00; +	u8 rows = 0x00; + +	bank_info->banks = 0x00; +	bank_info->columns = 0x00; +	bank_info->rows = 0x00; +	bank_info->size = 0x00; + +	if ((bank < 0) || (bank > 3)) { +		printf("Bad Bank ID\n"); +		return; +	} + +	/* Save configuration */ +	drcbendadr = readl(&sc520_mmcr->drcbendadr); +	drccfg = readw(&sc520_mmcr->drccfg); + +	/* Setup SDRAM Bank to largest possible size */ +	writew(0x000b << (bank * 4), &sc520_mmcr->drccfg); + +	/* Set ending address for this bank */ +	writel(0x000000ff << (bank * 8), &sc520_mmcr->drcbendadr); + +	/* write col 11 wrap adr */ +	if (write_and_test(COL11_DATA, COL11_ADR) != 0) +		goto restore_and_exit; + +	/* write col 10 wrap adr */ +	if (write_and_test(COL10_DATA, COL10_ADR) != 0) +		goto restore_and_exit; + +	/* write col 9 wrap adr */ +	if (write_and_test(COL09_DATA, COL09_ADR) != 0) +		goto restore_and_exit; + +	/* write col 8 wrap adr */ +	if (write_and_test(COL08_DATA, COL08_ADR) != 0) +		goto restore_and_exit; + +	col_data = readl(COL11_ADR); + +	/* All four bytes in the read long must be the same */ +	if (check_long(col_data) < 0) +		goto restore_and_exit; + +	if ((col_data >= COL08_DATA) && (col_data <= COL11_DATA)) +		columns = (u8)(col_data & 0x000000ff); +	else +		goto restore_and_exit; + +	/* write row 14 wrap adr */ +	if (write_and_test(ROW14_DATA, ROW14_ADR) != 0) +		goto restore_and_exit; + +	/* write row 13 wrap adr */ +	if (write_and_test(ROW13_DATA, ROW13_ADR) != 0) +		goto restore_and_exit; + +	/* write row 12 wrap adr */ +	if (write_and_test(ROW12_DATA, ROW12_ADR) != 0) +		goto restore_and_exit; + +	/* write row 11 wrap adr */ +	if (write_and_test(ROW11_DATA, ROW11_ADR) != 0) +		goto restore_and_exit; + +	if (write_and_test(ROW10_DATA, ROW10_ADR) != 0) +		goto restore_and_exit; + +	/* +	 * read data @ row 12 wrap adr to determine number of banks, +	 * and read data @ row 14 wrap adr to determine number of rows. +	 * if data @ row 12 wrap adr is not AA, 11 or 12 we have bad RAM. +	 * if data @ row 12 wrap == AA, we only have 2 banks, NOT 4 +	 * if data @ row 12 wrap == 11 or 12, we have 4 banks, +	 */ +	row_data = readl(ROW12_ADR); + +	/* All four bytes in the read long must be the same */ +	if (check_long(row_data) != 0) +		goto restore_and_exit; + +	switch (row_data) { +	case ROW10_DATA: +		banks = 2; +		break; + +	case ROW11_DATA: +	case ROW12_DATA: +		banks = 4; +		break; + +	default: +		goto restore_and_exit; +	} + +	row_data = readl(ROW14_ADR); + +	/* All four bytes in the read long must be the same */ +	if (check_long(row_data) != 0) +		goto restore_and_exit; + +	switch (row_data) { +	case ROW11_DATA: +	case ROW12_DATA: +	case ROW13_DATA: +	case ROW14_DATA: +		rows = (u8)(row_data & 0x000000ff); +		break; + +	default: +		goto restore_and_exit; +	} + +	bank_info->banks = banks; +	bank_info->columns = columns; +	bank_info->rows = rows; + +	if ((bank_info->banks != 0) && +	    (bank_info->columns != 0) && +	    (bank_info->rows != 0)) { +		bank_info->size = bank_info->rows; +		bank_info->size >>= (11 - bank_info->columns); +		bank_info->size++; +	} + +restore_and_exit: +	/* Restore configuration */ +	writel(drcbendadr, &sc520_mmcr->drcbendadr); +	writew(drccfg, &sc520_mmcr->drccfg); +} + +static void sc520_setup_sizemem(void) +{ +	u8 i; + +	/* Disable write buffer */ +	writeb(0x00, &sc520_mmcr->dbctl); + +	/* Disable ECC */ +	writeb(0x00, &sc520_mmcr->eccctl); + +	/* Set slowest SDRAM timing */ +	writeb(0x1e, &sc520_mmcr->drctmctl); + +	/* Issue a NOP to all SDRAM banks */ +	sc520_issue_sdram_op_mode_select(0x01); + +	/* Delay for 100 microseconds */ +	udelay(100); + +	/* Issue 'All Banks Precharge' command */ +	sc520_issue_sdram_op_mode_select(0x02); + +	/* Issue 2 'Auto Refresh Enable' command */ +	sc520_issue_sdram_op_mode_select(0x04); +	sc520_dummy_write(); + +	/* Issue 'Load Mode Register' command */ +	sc520_issue_sdram_op_mode_select(0x03); + +	/* Issue 8 more 'Auto Refresh Enable' commands */ +	sc520_issue_sdram_op_mode_select(0x04); +	for (i = 0; i < 7; i++) +		sc520_dummy_write(); + +	/* Set control register to 'Normal Mode' */ +	writeb(0x00, &sc520_mmcr->drcctl); +} + +static void sc520_sizemem(void) +{ +	struct sc520_sdram_info sdram_info[4]; +	u8 bank_config = 0x00; +	u8 end_addr = 0x00; +	u16 drccfg = 0x0000; +	u32 drcbendadr = 0x00000000; +	u8 i; + +	/* Use PARs to disable caching of maximum allowable 256MB SDRAM */ +	writel(SC520_SDRAM1_PAR | SC520_PAR_CACHE_DIS, &sc520_mmcr->par[3]); +	writel(SC520_SDRAM2_PAR | SC520_PAR_CACHE_DIS, &sc520_mmcr->par[4]); + +	sc520_setup_sizemem(); + +	gd->ram_size = 0; + +	/* Size each SDRAM bank */ +	for (i = 0; i <= 3; i++) { +		sc520_get_bank_info(i, &sdram_info[i]); + +		if (sdram_info[i].banks != 0) { +			/* Update Configuration register */ +			bank_config = sdram_info[i].columns - 8; + +			if (sdram_info[i].banks == 4) +				bank_config |= 0x08; + +			drccfg |= bank_config << (i * 4); + +			/* Update End Address register */ +			end_addr += sdram_info[i].size; +			drcbendadr |= (end_addr | 0x80) << (i * 8); + +			gd->ram_size += sdram_info[i].size << 22; +		} + +		/* Issue 'All Banks Precharge' command */ +		sc520_issue_sdram_op_mode_select(0x02); + +		/* Set control register to 'Normal Mode' */ +		writeb(0x00, &sc520_mmcr->drcctl); +	} + +	writel(drcbendadr, &sc520_mmcr->drcbendadr); +	writew(drccfg, &sc520_mmcr->drccfg); + +	/* Clear PARs preventing caching of SDRAM */ +	writel(0x00000000, &sc520_mmcr->par[3]); +	writel(0x00000000, &sc520_mmcr->par[4]); +} + +#if CONFIG_SYS_SDRAM_ECC_ENABLE +static void sc520_enable_ecc(void) + +	/* A nominal memory test: just a byte at each address line */ +	movl	%eax, %ecx +	shrl	$0x1, %ecx +	movl	$0x1, %edi +memtest0: +	movb	$0xa5, (%edi) +	cmpb	$0xa5, (%edi) +	jne	out +	shrl	$0x1, %ecx +	andl	%ecx, %ecx +	jz	set_ecc +	shll	$0x1, %edi +	jmp	memtest0 + +set_ecc: +	/* clear all ram with a memset */ +	movl	%eax, %ecx +	xorl	%esi, %esi +	xorl	%edi, %edi +	xorl	%eax, %eax +	shrl	$0x2, %ecx +	cld +	rep	stosl + +	/* enable read, write buffers */ +	movb	$0x11, %al +	movl	$DBCTL, %edi +	movb	%al, (%edi) + +	/* enable NMI mapping for ECC */ +	movl	$ECCINT, %edi +	movb	$0x10, %al +	movb	%al, (%edi) + +	/* Turn on ECC */ +	movl	$ECCCTL, %edi +	movb	$0x05, %al +	movb	%al,(%edi) + +out: +	jmp	init_ecc_ret +} +#endif + +int dram_init(void) +{ +	ulong dram_ctrl; +	ulong dram_present = 0x00000000; + +	/* +	 * We read-back the configuration of the dram +	 * controller that the assembly code wrote +	 */ +	dram_ctrl = readl(&sc520_mmcr->drcbendadr); + +	gd->bd->bi_dram[0].start = 0; +	if (dram_ctrl & 0x80) { +		/* bank 0 enabled */ +		gd->bd->bi_dram[1].start = (dram_ctrl & 0x7f) << 22; +		dram_present = gd->bd->bi_dram[1].start; +		gd->bd->bi_dram[0].size = gd->bd->bi_dram[1].start; +	} else { +		gd->bd->bi_dram[0].size = 0; +		gd->bd->bi_dram[1].start = gd->bd->bi_dram[0].start; +	} + +	if (dram_ctrl & 0x8000) { +		/* bank 1 enabled */ +		gd->bd->bi_dram[2].start = (dram_ctrl & 0x7f00) << 14; +		dram_present = gd->bd->bi_dram[2].start; +		gd->bd->bi_dram[1].size = gd->bd->bi_dram[2].start - +				gd->bd->bi_dram[1].start; +	} else { +		gd->bd->bi_dram[1].size = 0; +		gd->bd->bi_dram[2].start = gd->bd->bi_dram[1].start; +	} + +	if (dram_ctrl & 0x800000) { +		/* bank 2 enabled */ +		gd->bd->bi_dram[3].start = (dram_ctrl & 0x7f0000) << 6; +		dram_present = gd->bd->bi_dram[3].start; +		gd->bd->bi_dram[2].size = gd->bd->bi_dram[3].start - +				gd->bd->bi_dram[2].start; +	} else { +		gd->bd->bi_dram[2].size = 0; +		gd->bd->bi_dram[3].start = gd->bd->bi_dram[2].start; +	} + +	if (dram_ctrl & 0x80000000) { +		/* bank 3 enabled */ +		dram_present  = (dram_ctrl & 0x7f000000) >> 2; +		gd->bd->bi_dram[3].size = dram_present - +				gd->bd->bi_dram[3].start; +	} else { +		gd->bd->bi_dram[3].size = 0; +	} + +	gd->ram_size = dram_present; + +	return 0; +} |