mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-25 22:20:45 +00:00
6d0f6bcf33
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
752 lines
20 KiB
C
752 lines
20 KiB
C
/*
|
|
* (C) Copyright 2004, Freescale, Inc
|
|
* TsiChung Liew, Tsi-Chung.Liew@freescale.com
|
|
*
|
|
* See file CREDITS for list of people who contributed to this
|
|
* project.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
Read Dram spd and base on its information to calculate the memory size,
|
|
characteristics to initialize the dram on MPC8220
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <mpc8220.h>
|
|
#include "i2cCore.h"
|
|
#include "dramSetup.h"
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#define SPD_SIZE CONFIG_SYS_SDRAM_SPD_SIZE
|
|
#define DRAM_SPD (CONFIG_SYS_SDRAM_SPD_I2C_ADDR)<<1 /* on Board SPD eeprom */
|
|
#define TOTAL_BANK CONFIG_SYS_SDRAM_TOTAL_BANKS
|
|
|
|
int spd_status (volatile i2c8220_t * pi2c, u8 sta_bit, u8 truefalse)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < I2C_POLL_COUNT; i++) {
|
|
if ((pi2c->sr & sta_bit) == (truefalse ? sta_bit : 0))
|
|
return (OK);
|
|
}
|
|
|
|
return (ERROR);
|
|
}
|
|
|
|
int spd_clear (volatile i2c8220_t * pi2c)
|
|
{
|
|
pi2c->adr = 0;
|
|
pi2c->fdr = 0;
|
|
pi2c->cr = 0;
|
|
pi2c->sr = 0;
|
|
|
|
return (OK);
|
|
}
|
|
|
|
int spd_stop (volatile i2c8220_t * pi2c)
|
|
{
|
|
pi2c->cr &= ~I2C_CTL_STA; /* Generate stop signal */
|
|
if (spd_status (pi2c, I2C_STA_BB, 0) != OK)
|
|
return ERROR;
|
|
|
|
return (OK);
|
|
}
|
|
|
|
int spd_readbyte (volatile i2c8220_t * pi2c, u8 * readb, int *index)
|
|
{
|
|
pi2c->sr &= ~I2C_STA_IF; /* Clear Interrupt Bit */
|
|
*readb = pi2c->dr; /* Read a byte */
|
|
|
|
/*
|
|
Set I2C_CTRL_TXAK will cause Transfer pending and
|
|
set I2C_CTRL_STA will cause Interrupt pending
|
|
*/
|
|
if (*index != 2) {
|
|
if (spd_status (pi2c, I2C_STA_CF, 1) != OK) /* Transfer not complete? */
|
|
return ERROR;
|
|
}
|
|
|
|
if (*index != 1) {
|
|
if (spd_status (pi2c, I2C_STA_IF, 1) != OK)
|
|
return ERROR;
|
|
}
|
|
|
|
return (OK);
|
|
}
|
|
|
|
int readSpdData (u8 * spdData)
|
|
{
|
|
volatile i2c8220_t *pi2cReg;
|
|
volatile pcfg8220_t *pcfg;
|
|
u8 slvAdr = DRAM_SPD;
|
|
u8 Tmp;
|
|
int Length = SPD_SIZE;
|
|
int i = 0;
|
|
|
|
/* Enable Port Configuration for SDA and SDL signals */
|
|
pcfg = (volatile pcfg8220_t *) (MMAP_PCFG);
|
|
__asm__ ("sync");
|
|
pcfg->pcfg3 &= ~CONFIG_SYS_I2C_PORT3_CONFIG;
|
|
__asm__ ("sync");
|
|
|
|
/* Points the structure to I2c mbar memory offset */
|
|
pi2cReg = (volatile i2c8220_t *) (MMAP_I2C);
|
|
|
|
|
|
/* Clear FDR, ADR, SR and CR reg */
|
|
pi2cReg->adr = 0;
|
|
pi2cReg->fdr = 0;
|
|
pi2cReg->cr = 0;
|
|
pi2cReg->sr = 0;
|
|
|
|
/* Set for fix XLB Bus Frequency */
|
|
switch (gd->bus_clk) {
|
|
case 60000000:
|
|
pi2cReg->fdr = 0x15;
|
|
break;
|
|
case 70000000:
|
|
pi2cReg->fdr = 0x16;
|
|
break;
|
|
case 80000000:
|
|
pi2cReg->fdr = 0x3a;
|
|
break;
|
|
case 90000000:
|
|
pi2cReg->fdr = 0x17;
|
|
break;
|
|
case 100000000:
|
|
pi2cReg->fdr = 0x3b;
|
|
break;
|
|
case 110000000:
|
|
pi2cReg->fdr = 0x18;
|
|
break;
|
|
case 120000000:
|
|
pi2cReg->fdr = 0x19;
|
|
break;
|
|
case 130000000:
|
|
pi2cReg->fdr = 0x1a;
|
|
break;
|
|
}
|
|
|
|
pi2cReg->adr = CONFIG_SYS_I2C_SLAVE<<1;
|
|
|
|
pi2cReg->cr = I2C_CTL_EN; /* Set Enable */
|
|
|
|
/*
|
|
The I2C bus should be in Idle state. If the bus is busy,
|
|
clear the STA bit in control register
|
|
*/
|
|
if (spd_status (pi2cReg, I2C_STA_BB, 0) != OK) {
|
|
if ((pi2cReg->cr & I2C_CTL_STA) == I2C_CTL_STA)
|
|
pi2cReg->cr &= ~I2C_CTL_STA;
|
|
|
|
/* Check again if it is still busy, return error if found */
|
|
if (spd_status (pi2cReg, I2C_STA_BB, 1) == OK)
|
|
return ERROR;
|
|
}
|
|
|
|
pi2cReg->cr |= I2C_CTL_TX; /* Enable the I2c for TX, Ack */
|
|
pi2cReg->cr |= I2C_CTL_STA; /* Generate start signal */
|
|
|
|
if (spd_status (pi2cReg, I2C_STA_BB, 1) != OK)
|
|
return ERROR;
|
|
|
|
|
|
/* Write slave address */
|
|
pi2cReg->sr &= ~I2C_STA_IF; /* Clear Interrupt */
|
|
pi2cReg->dr = slvAdr; /* Write a byte */
|
|
|
|
if (spd_status (pi2cReg, I2C_STA_CF, 1) != OK) { /* Transfer not complete? */
|
|
spd_stop (pi2cReg);
|
|
return ERROR;
|
|
}
|
|
|
|
if (spd_status (pi2cReg, I2C_STA_IF, 1) != OK) {
|
|
spd_stop (pi2cReg);
|
|
return ERROR;
|
|
}
|
|
|
|
|
|
/* Issue the offset to start */
|
|
pi2cReg->sr &= ~I2C_STA_IF; /* Clear Interrupt */
|
|
pi2cReg->dr = 0; /* Write a byte */
|
|
|
|
if (spd_status (pi2cReg, I2C_STA_CF, 1) != OK) { /* Transfer not complete? */
|
|
spd_stop (pi2cReg);
|
|
return ERROR;
|
|
}
|
|
|
|
if (spd_status (pi2cReg, I2C_STA_IF, 1) != OK) {
|
|
spd_stop (pi2cReg);
|
|
return ERROR;
|
|
}
|
|
|
|
|
|
/* Set repeat start */
|
|
pi2cReg->cr |= I2C_CTL_RSTA; /* Repeat Start */
|
|
|
|
pi2cReg->sr &= ~I2C_STA_IF; /* Clear Interrupt */
|
|
pi2cReg->dr = slvAdr | 1; /* Write a byte */
|
|
|
|
if (spd_status (pi2cReg, I2C_STA_CF, 1) != OK) { /* Transfer not complete? */
|
|
spd_stop (pi2cReg);
|
|
return ERROR;
|
|
}
|
|
|
|
if (spd_status (pi2cReg, I2C_STA_IF, 1) != OK) {
|
|
spd_stop (pi2cReg);
|
|
return ERROR;
|
|
}
|
|
|
|
if (((pi2cReg->sr & 0x07) == 0x07) || (pi2cReg->sr & 0x01))
|
|
return ERROR;
|
|
|
|
pi2cReg->cr &= ~I2C_CTL_TX; /* Set receive mode */
|
|
|
|
if (((pi2cReg->sr & 0x07) == 0x07) || (pi2cReg->sr & 0x01))
|
|
return ERROR;
|
|
|
|
/* Dummy Read */
|
|
if (spd_readbyte (pi2cReg, &Tmp, &i) != OK) {
|
|
spd_stop (pi2cReg);
|
|
return ERROR;
|
|
}
|
|
|
|
i = 0;
|
|
while (Length) {
|
|
if (Length == 2)
|
|
pi2cReg->cr |= I2C_CTL_TXAK;
|
|
|
|
if (Length == 1)
|
|
pi2cReg->cr &= ~I2C_CTL_STA;
|
|
|
|
if (spd_readbyte (pi2cReg, spdData, &Length) != OK) {
|
|
return spd_stop (pi2cReg);
|
|
}
|
|
i++;
|
|
Length--;
|
|
spdData++;
|
|
}
|
|
|
|
/* Stop the service */
|
|
spd_stop (pi2cReg);
|
|
|
|
return OK;
|
|
}
|
|
|
|
int getBankInfo (int bank, draminfo_t * pBank)
|
|
{
|
|
int status;
|
|
int checksum;
|
|
int count;
|
|
u8 spdData[SPD_SIZE];
|
|
|
|
|
|
if (bank > 2 || pBank == 0) {
|
|
/* illegal values */
|
|
return (-42);
|
|
}
|
|
|
|
status = readSpdData (&spdData[0]);
|
|
if (status < 0)
|
|
return (-1);
|
|
|
|
/* check the checksum */
|
|
for (count = 0, checksum = 0; count < LOC_CHECKSUM; count++)
|
|
checksum += spdData[count];
|
|
|
|
checksum = checksum - ((checksum / 256) * 256);
|
|
|
|
if (checksum != spdData[LOC_CHECKSUM])
|
|
return (-2);
|
|
|
|
/* Get the memory type */
|
|
if (!
|
|
((spdData[LOC_TYPE] == TYPE_DDR)
|
|
|| (spdData[LOC_TYPE] == TYPE_SDR)))
|
|
/* not one of the types we support */
|
|
return (-3);
|
|
|
|
pBank->type = spdData[LOC_TYPE];
|
|
|
|
/* Set logical banks */
|
|
pBank->banks = spdData[LOC_LOGICAL_BANKS];
|
|
|
|
/* Check that we have enough physical banks to cover the bank we are
|
|
* figuring out. Odd-numbered banks correspond to the second bank
|
|
* on the device.
|
|
*/
|
|
if (bank & 1) {
|
|
/* Second bank of a "device" */
|
|
if (spdData[LOC_PHYS_BANKS] < 2)
|
|
/* this bank doesn't exist on the "device" */
|
|
return (-4);
|
|
|
|
if (spdData[LOC_ROWS] & 0xf0)
|
|
/* Two asymmetric banks */
|
|
pBank->rows = spdData[LOC_ROWS] >> 4;
|
|
else
|
|
pBank->rows = spdData[LOC_ROWS];
|
|
|
|
if (spdData[LOC_COLS] & 0xf0)
|
|
/* Two asymmetric banks */
|
|
pBank->cols = spdData[LOC_COLS] >> 4;
|
|
else
|
|
pBank->cols = spdData[LOC_COLS];
|
|
} else {
|
|
/* First bank of a "device" */
|
|
pBank->rows = spdData[LOC_ROWS];
|
|
pBank->cols = spdData[LOC_COLS];
|
|
}
|
|
|
|
pBank->width = spdData[LOC_WIDTH_HIGH] << 8 | spdData[LOC_WIDTH_LOW];
|
|
pBank->bursts = spdData[LOC_BURSTS];
|
|
pBank->CAS = spdData[LOC_CAS];
|
|
pBank->CS = spdData[LOC_CS];
|
|
pBank->WE = spdData[LOC_WE];
|
|
pBank->Trp = spdData[LOC_Trp];
|
|
pBank->Trcd = spdData[LOC_Trcd];
|
|
pBank->buffered = spdData[LOC_Buffered] & 1;
|
|
pBank->refresh = spdData[LOC_REFRESH];
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/* checkMuxSetting -- given a row/column device geometry, return a mask
|
|
* of the valid DRAM controller addr_mux settings for
|
|
* that geometry.
|
|
*
|
|
* Arguments: u8 rows: number of row addresses in this device
|
|
* u8 columns: number of column addresses in this device
|
|
*
|
|
* Returns: a mask of the allowed addr_mux settings for this
|
|
* geometry. Each bit in the mask represents a
|
|
* possible addr_mux settings (for example, the
|
|
* (1<<2) bit in the mask represents the 0b10 setting)/
|
|
*
|
|
*/
|
|
u8 checkMuxSetting (u8 rows, u8 columns)
|
|
{
|
|
muxdesc_t *pIdx, *pMux;
|
|
u8 mask;
|
|
int lrows, lcolumns;
|
|
u32 mux[4] = { 0x00080c04, 0x01080d03, 0x02080e02, 0xffffffff };
|
|
|
|
/* Setup MuxDescriptor in SRAM space */
|
|
/* MUXDESC AddressRuns [] = {
|
|
{ 0, 8, 12, 4 }, / setting, columns, rows, extra columns /
|
|
{ 1, 8, 13, 3 }, / setting, columns, rows, extra columns /
|
|
{ 2, 8, 14, 2 }, / setting, columns, rows, extra columns /
|
|
{ 0xff } / list terminator /
|
|
}; */
|
|
|
|
pIdx = (muxdesc_t *) & mux[0];
|
|
|
|
/* Check rows x columns against each possible address mux setting */
|
|
for (pMux = pIdx, mask = 0;; pMux++) {
|
|
lrows = rows;
|
|
lcolumns = columns;
|
|
|
|
if (pMux->MuxValue == 0xff)
|
|
break; /* end of list */
|
|
|
|
/* For a given mux setting, since we want all the memory in a
|
|
* device to be contiguous, we want the device "use up" the
|
|
* address lines such that there are no extra column or row
|
|
* address lines on the device.
|
|
*/
|
|
|
|
lcolumns -= pMux->Columns;
|
|
if (lcolumns < 0)
|
|
/* Not enough columns to get to the rows */
|
|
continue;
|
|
|
|
lrows -= pMux->Rows;
|
|
if (lrows > 0)
|
|
/* we have extra rows left -- can't do that! */
|
|
continue;
|
|
|
|
/* At this point, we either have to have used up all the
|
|
* rows or we have to have no columns left.
|
|
*/
|
|
|
|
if (lcolumns != 0 && lrows != 0)
|
|
/* rows AND columns are left. Bad! */
|
|
continue;
|
|
|
|
lcolumns -= pMux->MoreColumns;
|
|
|
|
if (lcolumns <= 0)
|
|
mask |= (1 << pMux->MuxValue);
|
|
}
|
|
|
|
return (mask);
|
|
}
|
|
|
|
|
|
u32 dramSetup (void)
|
|
{
|
|
draminfo_t DramInfo[TOTAL_BANK];
|
|
draminfo_t *pDramInfo;
|
|
u32 size, temp, cfg_value, mode_value, refresh;
|
|
u8 *ptr;
|
|
u8 bursts, Trp, Trcd, type, buffered;
|
|
u8 muxmask, rows, columns;
|
|
int count, banknum;
|
|
u32 *prefresh, *pIdx;
|
|
u32 refrate[8] = { 15625, 3900, 7800, 31300,
|
|
62500, 125000, 0xffffffff, 0xffffffff
|
|
};
|
|
volatile sysconf8220_t *sysconf;
|
|
volatile memctl8220_t *memctl;
|
|
|
|
sysconf = (volatile sysconf8220_t *) MMAP_MBAR;
|
|
memctl = (volatile memctl8220_t *) MMAP_MEMCTL;
|
|
|
|
/* Set everything in the descriptions to zero */
|
|
ptr = (u8 *) & DramInfo[0];
|
|
for (count = 0; count < sizeof (DramInfo); count++)
|
|
*ptr++ = 0;
|
|
|
|
for (banknum = 0; banknum < TOTAL_BANK; banknum++)
|
|
sysconf->cscfg[banknum];
|
|
|
|
/* Descriptions of row/column address muxing for various
|
|
* addr_mux settings.
|
|
*/
|
|
|
|
pIdx = prefresh = (u32 *) & refrate[0];
|
|
|
|
/* Get all the info for all three logical banks */
|
|
bursts = 0xff;
|
|
Trp = 0;
|
|
Trcd = 0;
|
|
type = 0;
|
|
buffered = 0xff;
|
|
refresh = 0xffffffff;
|
|
muxmask = 0xff;
|
|
|
|
/* Two bank, CS0 and CS1 */
|
|
for (banknum = 0, pDramInfo = &DramInfo[0];
|
|
banknum < TOTAL_BANK; banknum++, pDramInfo++) {
|
|
pDramInfo->ordinal = banknum; /* initial sorting */
|
|
if (getBankInfo (banknum, pDramInfo) < 0)
|
|
continue;
|
|
|
|
/* get cumulative parameters of all three banks */
|
|
if (type && pDramInfo->type != type)
|
|
return 0;
|
|
|
|
type = pDramInfo->type;
|
|
rows = pDramInfo->rows;
|
|
columns = pDramInfo->cols;
|
|
|
|
/* This chip only supports 13 DRAM memory lines, but some devices
|
|
* have 14 rows. To deal with this, ignore the 14th address line
|
|
* by limiting the number of rows (and columns) to 13. This will
|
|
* mean that for 14-row devices we will only be able to use
|
|
* half of the memory, but it's better than nothing.
|
|
*/
|
|
if (rows > 13)
|
|
rows = 13;
|
|
if (columns > 13)
|
|
columns = 13;
|
|
|
|
pDramInfo->size =
|
|
((1 << (rows + columns)) * pDramInfo->width);
|
|
pDramInfo->size *= pDramInfo->banks;
|
|
pDramInfo->size >>= 3;
|
|
|
|
/* figure out which addr_mux configurations will support this device */
|
|
muxmask &= checkMuxSetting (rows, columns);
|
|
if (muxmask == 0)
|
|
return 0;
|
|
|
|
buffered = pDramInfo->buffered;
|
|
bursts &= pDramInfo->bursts; /* union of all bursts */
|
|
if (pDramInfo->Trp > Trp) /* worst case (longest) Trp */
|
|
Trp = pDramInfo->Trp;
|
|
|
|
if (pDramInfo->Trcd > Trcd) /* worst case (longest) Trcd */
|
|
Trcd = pDramInfo->Trcd;
|
|
|
|
prefresh = pIdx;
|
|
/* worst case (shortest) Refresh period */
|
|
if (refresh > prefresh[pDramInfo->refresh & 7])
|
|
refresh = prefresh[pDramInfo->refresh & 7];
|
|
|
|
} /* for loop */
|
|
|
|
|
|
/* We only allow a burst length of 8! */
|
|
if (!(bursts & 8))
|
|
bursts = 8;
|
|
|
|
/* Sort the devices. In order to get each chip select region
|
|
* aligned properly, put the biggest device at the lowest address.
|
|
* A simple bubble sort will do the trick.
|
|
*/
|
|
for (banknum = 0, pDramInfo = &DramInfo[0];
|
|
banknum < TOTAL_BANK; banknum++, pDramInfo++) {
|
|
int i;
|
|
|
|
for (i = 0; i < TOTAL_BANK; i++) {
|
|
if (pDramInfo->size < DramInfo[i].size &&
|
|
pDramInfo->ordinal < DramInfo[i].ordinal) {
|
|
/* If the current bank is smaller, but if the ordinal is also
|
|
* smaller, swap the ordinals
|
|
*/
|
|
u8 temp8;
|
|
|
|
temp8 = DramInfo[i].ordinal;
|
|
DramInfo[i].ordinal = pDramInfo->ordinal;
|
|
pDramInfo->ordinal = temp8;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Now figure out the base address for each bank. While
|
|
* we're at it, figure out how much memory there is.
|
|
*
|
|
*/
|
|
size = 0;
|
|
for (banknum = 0; banknum < TOTAL_BANK; banknum++) {
|
|
int i;
|
|
|
|
for (i = 0; i < TOTAL_BANK; i++) {
|
|
if (DramInfo[i].ordinal == banknum
|
|
&& DramInfo[i].size != 0) {
|
|
DramInfo[i].base = size;
|
|
size += DramInfo[i].size;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set up the Drive Strength register */
|
|
sysconf->sdramds = CONFIG_SYS_SDRAM_DRIVE_STRENGTH;
|
|
|
|
/* ********************** Cfg 1 ************************* */
|
|
|
|
/* Set the single read to read/write/precharge delay */
|
|
cfg_value = CFG1_SRD2RWP ((type == TYPE_DDR) ? 7 : 0xb);
|
|
|
|
/* Set the single write to read/write/precharge delay.
|
|
* This may or may not be correct. The controller spec
|
|
* says "tWR", but "tWR" does not appear in the SPD. It
|
|
* always seems to be 15nsec for the class of device we're
|
|
* using, which turns out to be 2 clock cycles at 133MHz,
|
|
* so that's what we're going to use.
|
|
*
|
|
* HOWEVER, because of a bug in the controller, for DDR
|
|
* we need to set this to be the same as the value
|
|
* calculated for bwt2rwp.
|
|
*/
|
|
cfg_value |= CFG1_SWT2RWP ((type == TYPE_DDR) ? 7 : 2);
|
|
|
|
/* Set the Read CAS latency. We're going to use a CL of
|
|
* 2.5 for DDR and 2 SDR.
|
|
*/
|
|
cfg_value |= CFG1_RLATENCY ((type == TYPE_DDR) ? 7 : 2);
|
|
|
|
|
|
/* Set the Active to Read/Write delay. This depends
|
|
* on Trcd which is reported as nanoseconds times 4.
|
|
* We want to calculate Trcd (in nanoseconds) times XLB clock (in Hz)
|
|
* which gives us a dimensionless quantity. Play games with
|
|
* the divisions so we don't run out of dynamic ranges.
|
|
*/
|
|
/* account for megaherz and the times 4 */
|
|
temp = (Trcd * (gd->bus_clk / 1000000)) / 4;
|
|
|
|
/* account for nanoseconds and round up, with a minimum value of 2 */
|
|
temp = ((temp + 999) / 1000) - 1;
|
|
if (temp < 2)
|
|
temp = 2;
|
|
|
|
cfg_value |= CFG1_ACT2WR (temp);
|
|
|
|
/* Set the precharge to active delay. This depends
|
|
* on Trp which is reported as nanoseconds times 4.
|
|
* We want to calculate Trp (in nanoseconds) times XLB clock (in Hz)
|
|
* which gives us a dimensionless quantity. Play games with
|
|
* the divisions so we don't run out of dynamic ranges.
|
|
*/
|
|
/* account for megaherz and the times 4 */
|
|
temp = (Trp * (gd->bus_clk / 1000000)) / 4;
|
|
|
|
/* account for nanoseconds and round up, then subtract 1, with a
|
|
* minumum value of 1 and a maximum value of 7.
|
|
*/
|
|
temp = (((temp + 999) / 1000) - 1) & 7;
|
|
if (temp < 1)
|
|
temp = 1;
|
|
|
|
cfg_value |= CFG1_PRE2ACT (temp);
|
|
|
|
/* Set refresh to active delay. This depends
|
|
* on Trfc which is not reported in the SPD.
|
|
* We'll use a nominal value of 75nsec which is
|
|
* what the controller spec uses.
|
|
*/
|
|
temp = (75 * (gd->bus_clk / 1000000));
|
|
/* account for nanoseconds and round up, then subtract 1 */
|
|
cfg_value |= CFG1_REF2ACT (((temp + 999) / 1000) - 1);
|
|
|
|
/* Set the write latency, using the values given in the controller spec */
|
|
cfg_value |= CFG1_WLATENCY ((type == TYPE_DDR) ? 3 : 0);
|
|
memctl->cfg1 = cfg_value; /* cfg 1 */
|
|
asm volatile ("sync");
|
|
|
|
|
|
/* ********************** Cfg 2 ************************* */
|
|
|
|
/* Set the burst read to read/precharge delay */
|
|
cfg_value = CFG2_BRD2RP ((type == TYPE_DDR) ? 5 : 8);
|
|
|
|
/* Set the burst write to read/precharge delay. Semi-magic numbers
|
|
* based on the controller spec recommendations, assuming tWR is
|
|
* two clock cycles.
|
|
*/
|
|
cfg_value |= CFG2_BWT2RWP ((type == TYPE_DDR) ? 7 : 10);
|
|
|
|
/* Set the Burst read to write delay. Semi-magic numbers
|
|
* based on the DRAM controller documentation.
|
|
*/
|
|
cfg_value |= CFG2_BRD2WT ((type == TYPE_DDR) ? 7 : 0xb);
|
|
|
|
/* Set the burst length -- must be 8!! Well, 7, actually, becuase
|
|
* it's burst lenght minus 1.
|
|
*/
|
|
cfg_value |= CFG2_BURSTLEN (7);
|
|
memctl->cfg2 = cfg_value; /* cfg 2 */
|
|
asm volatile ("sync");
|
|
|
|
|
|
/* ********************** mode ************************* */
|
|
|
|
/* Set enable bit, CKE high/low bits, and the DDR/SDR mode bit,
|
|
* disable automatic refresh.
|
|
*/
|
|
cfg_value = CTL_MODE_ENABLE | CTL_CKE_HIGH |
|
|
((type == TYPE_DDR) ? CTL_DDR_MODE : 0);
|
|
|
|
/* Set the address mux based on whichever setting(s) is/are common
|
|
* to all the devices we have. If there is more than one, choose
|
|
* one arbitrarily.
|
|
*/
|
|
if (muxmask & 0x4)
|
|
cfg_value |= CTL_ADDRMUX (2);
|
|
else if (muxmask & 0x2)
|
|
cfg_value |= CTL_ADDRMUX (1);
|
|
else
|
|
cfg_value |= CTL_ADDRMUX (0);
|
|
|
|
/* Set the refresh interval. */
|
|
temp = ((refresh * (gd->bus_clk / 1000000)) / (1000 * 64)) - 1;
|
|
cfg_value |= CTL_REFRESH_INTERVAL (temp);
|
|
|
|
/* Set buffered/non-buffered memory */
|
|
if (buffered)
|
|
cfg_value |= CTL_BUFFERED;
|
|
|
|
memctl->ctrl = cfg_value; /* ctrl */
|
|
asm volatile ("sync");
|
|
|
|
if (type == TYPE_DDR) {
|
|
/* issue precharge all */
|
|
temp = cfg_value | CTL_PRECHARGE_CMD;
|
|
memctl->ctrl = temp; /* ctrl */
|
|
asm volatile ("sync");
|
|
}
|
|
|
|
|
|
/* Set up mode value for CAS latency */
|
|
#if (CONFIG_SYS_SDRAM_CAS_LATENCY==5) /* CL=2.5 */
|
|
mode_value = (MODE_MODE | MODE_BURSTLEN (MODE_BURSTLEN_8) |
|
|
MODE_BT_SEQUENTIAL | MODE_CL (MODE_CL_2p5) | MODE_CMD);
|
|
#else
|
|
mode_value = (MODE_MODE | MODE_BURSTLEN (MODE_BURSTLEN_8) |
|
|
MODE_BT_SEQUENTIAL | MODE_CL (MODE_CL_2) | MODE_CMD);
|
|
#endif
|
|
asm volatile ("sync");
|
|
|
|
/* Write Extended Mode - enable DLL */
|
|
if (type == TYPE_DDR) {
|
|
temp = MODE_EXTENDED | MODE_X_DLL_ENABLE |
|
|
MODE_X_DS_NORMAL | MODE_CMD;
|
|
memctl->mode = (temp >> 16); /* mode */
|
|
asm volatile ("sync");
|
|
|
|
/* Write Mode - reset DLL, set CAS latency */
|
|
temp = mode_value | MODE_OPMODE (MODE_OPMODE_RESETDLL);
|
|
memctl->mode = (temp >> 16); /* mode */
|
|
asm volatile ("sync");
|
|
}
|
|
|
|
/* Program the chip selects. */
|
|
for (banknum = 0; banknum < TOTAL_BANK; banknum++) {
|
|
if (DramInfo[banknum].size != 0) {
|
|
u32 mask;
|
|
int i;
|
|
|
|
for (i = 0, mask = 1; i < 32; mask <<= 1, i++) {
|
|
if (DramInfo[banknum].size & mask)
|
|
break;
|
|
}
|
|
temp = (DramInfo[banknum].base & 0xfff00000) | (i -
|
|
1);
|
|
|
|
sysconf->cscfg[banknum] = temp;
|
|
asm volatile ("sync");
|
|
}
|
|
}
|
|
|
|
/* Wait for DLL lock */
|
|
udelay (200);
|
|
|
|
temp = cfg_value | CTL_PRECHARGE_CMD; /* issue precharge all */
|
|
memctl->ctrl = temp; /* ctrl */
|
|
asm volatile ("sync");
|
|
|
|
temp = cfg_value | CTL_REFRESH_CMD; /* issue precharge all */
|
|
memctl->ctrl = temp; /* ctrl */
|
|
asm volatile ("sync");
|
|
|
|
memctl->ctrl = temp; /* ctrl */
|
|
asm volatile ("sync");
|
|
|
|
/* Write Mode - DLL normal */
|
|
temp = mode_value | MODE_OPMODE (MODE_OPMODE_NORMAL);
|
|
memctl->mode = (temp >> 16); /* mode */
|
|
asm volatile ("sync");
|
|
|
|
/* Enable refresh, enable DQS's (if DDR), and lock the control register */
|
|
cfg_value &= ~CTL_MODE_ENABLE; /* lock register */
|
|
cfg_value |= CTL_REFRESH_ENABLE; /* enable refresh */
|
|
|
|
if (type == TYPE_DDR)
|
|
cfg_value |= CTL_DQSOEN (0xf); /* enable DQS's for DDR */
|
|
|
|
memctl->ctrl = cfg_value; /* ctrl */
|
|
asm volatile ("sync");
|
|
|
|
return size;
|
|
}
|