diff options
Diffstat (limited to 'arch/ppc/cpu/mpc8220/dramSetup.c')
-rw-r--r-- | arch/ppc/cpu/mpc8220/dramSetup.c | 752 |
1 files changed, 752 insertions, 0 deletions
diff --git a/arch/ppc/cpu/mpc8220/dramSetup.c b/arch/ppc/cpu/mpc8220/dramSetup.c new file mode 100644 index 0000000..52cf133 --- /dev/null +++ b/arch/ppc/cpu/mpc8220/dramSetup.c @@ -0,0 +1,752 @@ +/* + * (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; +} |