| /******************************************************************* |
| * |
| * File: ddr_oxsemi.c |
| * |
| * Description: Declarations for DDR routines and data objects |
| * |
| * Author: Julien Margetts |
| * |
| * Copyright: Oxford Semiconductor Ltd, 2009 |
| */ |
| #include <common.h> |
| #include <asm/arch/clock.h> |
| |
| #include "ddr.h" |
| |
| typedef unsigned int UINT; |
| |
| // DDR TIMING PARAMETERS |
| typedef struct { |
| unsigned int holdoff_cmd_A; |
| unsigned int holdoff_cmd_ARW; |
| unsigned int holdoff_cmd_N; |
| unsigned int holdoff_cmd_LM; |
| unsigned int holdoff_cmd_R; |
| unsigned int holdoff_cmd_W; |
| unsigned int holdoff_cmd_PC; |
| unsigned int holdoff_cmd_RF; |
| unsigned int holdoff_bank_R; |
| unsigned int holdoff_bank_W; |
| unsigned int holdoff_dir_RW; |
| unsigned int holdoff_dir_WR; |
| unsigned int holdoff_FAW; |
| unsigned int latency_CAS; |
| unsigned int latency_WL; |
| unsigned int recovery_WR; |
| unsigned int width_update; |
| unsigned int odt_offset; |
| unsigned int odt_drive_all; |
| unsigned int use_fixed_re; |
| unsigned int delay_wr_to_re; |
| unsigned int wr_slave_ratio; |
| unsigned int rd_slave_ratio0; |
| unsigned int rd_slave_ratio1; |
| } T_DDR_TIMING_PARAMETERS; |
| |
| // DDR CONFIG PARAMETERS |
| |
| typedef struct { |
| unsigned int ddr_mode; |
| unsigned int width; |
| unsigned int blocs; |
| unsigned int banks8; |
| unsigned int rams; |
| unsigned int asize; |
| unsigned int speed; |
| unsigned int cmd_mode_wr_cl_bl; |
| } T_DDR_CONFIG_PARAMETERS; |
| |
| //cmd_mode_wr_cl_bl |
| //when SDR : cmd_mode_wr_cl_bl = 0x80200002 + (latency_CAS_RAM * 16) + (recovery_WR - 1) * 512; -- Sets write rec XX, CL=XX; BL=8 |
| //else cmd_mode_wr_cl_bl = 0x80200003 + (latency_CAS_RAM * 16) + (recovery_WR - 1) * 512; -- Sets write rec XX, CL=XX; BL=8 |
| |
| // cmd_ bank_ dir_ lat_ rec_ width_ odt_ odt_ fix delay ratio |
| // A F C update offset all re re_to_we w r0 r1 |
| // R L P R R W A A W W |
| //Timing Parameters A W N M R W C F R W W R W S L R |
| static const T_DDR_TIMING_PARAMETERS C_TP_DDR2_25E_CL5_1GB = { 4, 5, 0, 2, 4, 4, |
| 5, 51, 23, 24, 9, 11, 18, 5, 4, 6, 3, 2, 0, 1, 2, 75, 56, 56 }; //elida device. |
| static const T_DDR_TIMING_PARAMETERS C_TP_DDR2_25E_CL5_2GB = { 4, 5, 0, 2, 4, 4, |
| 5, 79, 22, 24, 9, 11, 20, 5, 4, 6, 3, 2, 0, 1, 2, 75, 56, 56 }; |
| static const T_DDR_TIMING_PARAMETERS C_TP_DDR2_25_CL6_1GB = { 4, 5, 0, 2, 4, 4, |
| 4, 51, 22, 26, 10, 12, 18, 6, 5, 6, 3, 2, 0, 1, 2, 75, 56, 56 }; // 400MHz, Speedgrade 25 timings (1Gb parts) |
| |
| // D B B R A S |
| // D W L K A S P |
| //Config Parameters R D C 8 M Z D CMD_MODE |
| //static const T_DDR_CONFIG_PARAMETERS C_CP_DDR2_25E_CL5 = { 2,16, 1, 0, 1, 32,25,0x80200A53}; // 64 MByte |
| static const T_DDR_CONFIG_PARAMETERS C_CP_DDR2_25E_CL5 = { 2, 16, 1, 1, 1, 64, |
| 25, 0x80200A53 }; // 128 MByte |
| static const T_DDR_CONFIG_PARAMETERS C_CP_DDR2_25_CL6 = { 2, 16, 1, 1, 1, 128, |
| 25, 0x80200A63 }; // 256 MByte |
| |
| static void ddr_phy_poll_until_locked(void) |
| { |
| volatile UINT reg_tmp = 0; |
| volatile UINT locked = 0; |
| |
| //Extra read to put in delay before starting to poll... |
| reg_tmp = *(volatile UINT *) C_DDR_REG_PHY2; // read |
| |
| //POLL C_DDR_PHY2_REG register until clock and flock |
| //!!! Ideally have a timeout on this. |
| while (locked == 0) { |
| reg_tmp = *(volatile UINT *) C_DDR_REG_PHY2; // read |
| |
| //locked when bits 30 and 31 are set |
| if (reg_tmp & 0xC0000000) { |
| locked = 1; |
| } |
| } |
| } |
| |
| static void ddr_poll_until_not_busy(void) |
| { |
| volatile UINT reg_tmp = 0; |
| volatile UINT busy = 1; |
| |
| //Extra read to put in delay before starting to poll... |
| reg_tmp = *(volatile UINT *) C_DDR_STAT_REG; // read |
| |
| //POLL DDR_STAT register until no longer busy |
| //!!! Ideally have a timeout on this. |
| while (busy == 1) { |
| reg_tmp = *(volatile UINT *) C_DDR_STAT_REG; // read |
| |
| //when bit 31 is clear - core is no longer busy |
| if ((reg_tmp & 0x80000000) == 0x00000000) { |
| busy = 0; |
| } |
| } |
| } |
| |
| static void ddr_issue_command(int commmand) |
| { |
| *(volatile UINT *) C_DDR_CMD_REG = commmand; |
| ddr_poll_until_not_busy(); |
| } |
| |
| static void ddr_timing_initialisation( |
| const T_DDR_TIMING_PARAMETERS *ddr_timing_parameters) |
| { |
| volatile UINT reg_tmp = 0; |
| /* update the DDR controller registers for timing parameters */ |
| reg_tmp = (ddr_timing_parameters->holdoff_cmd_A << 0); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_cmd_ARW << 4); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_cmd_N << 8); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_cmd_LM << 12); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_cmd_R << 16); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_cmd_W << 20); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_cmd_PC << 24); |
| *(volatile UINT *) C_DDR_REG_TIMING0 = reg_tmp; |
| |
| reg_tmp = (ddr_timing_parameters->holdoff_cmd_RF << 0); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_bank_R << 8); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_bank_W << 16); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_dir_RW << 24); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_dir_WR << 28); |
| *(volatile UINT *) C_DDR_REG_TIMING1 = reg_tmp; |
| |
| reg_tmp = (ddr_timing_parameters->latency_CAS << 0); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->latency_WL << 4); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->holdoff_FAW << 8); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->width_update << 16); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->odt_offset << 21); |
| reg_tmp = reg_tmp + (ddr_timing_parameters->odt_drive_all << 24); |
| |
| *(volatile UINT *) C_DDR_REG_TIMING2 = reg_tmp; |
| |
| /* Program the timing parameters in the PHY too */ |
| reg_tmp = (ddr_timing_parameters->use_fixed_re << 16) |
| | (ddr_timing_parameters->delay_wr_to_re << 8) |
| | (ddr_timing_parameters->latency_WL << 4) |
| | (ddr_timing_parameters->latency_CAS << 0); |
| |
| *(volatile UINT *) C_DDR_REG_PHY_TIMING = reg_tmp; |
| |
| reg_tmp = ddr_timing_parameters->wr_slave_ratio; |
| |
| *(volatile UINT *) C_DDR_REG_PHY_WR_RATIO = reg_tmp; |
| |
| reg_tmp = ddr_timing_parameters->rd_slave_ratio0; |
| reg_tmp += ddr_timing_parameters->rd_slave_ratio1 << 8; |
| |
| *(volatile UINT *) C_DDR_REG_PHY_RD_RATIO = reg_tmp; |
| |
| } |
| |
| static void ddr_normal_initialisation( |
| const T_DDR_CONFIG_PARAMETERS *ddr_config_parameters, int mhz) |
| { |
| int i; |
| volatile UINT tmp = 0; |
| volatile UINT reg_tmp = 0; |
| volatile UINT emr_cmd = 0; |
| UINT refresh; |
| |
| //Total size of memory in Mbits... |
| tmp = ddr_config_parameters->rams * ddr_config_parameters->asize |
| * ddr_config_parameters->width; |
| //Deduce value to program into DDR_CFG register... |
| switch (tmp) { |
| case 16: |
| reg_tmp = 0x00020000 * 1; |
| break; |
| case 32: |
| reg_tmp = 0x00020000 * 2; |
| break; |
| case 64: |
| reg_tmp = 0x00020000 * 3; |
| break; |
| case 128: |
| reg_tmp = 0x00020000 * 4; |
| break; |
| case 256: |
| reg_tmp = 0x00020000 * 5; |
| break; |
| case 512: |
| reg_tmp = 0x00020000 * 6; |
| break; |
| case 1024: |
| reg_tmp = 0x00020000 * 7; |
| break; |
| case 2048: |
| reg_tmp = 0x00020000 * 8; |
| break; |
| default: |
| reg_tmp = 0; //forces sims not to work if badly configured |
| } |
| |
| //Memory width |
| tmp = ddr_config_parameters->rams * ddr_config_parameters->width; |
| switch (tmp) { |
| case 8: |
| reg_tmp = reg_tmp + 0x00400000; |
| break; |
| case 16: |
| reg_tmp = reg_tmp + 0x00200000; |
| break; |
| case 32: |
| reg_tmp = reg_tmp + 0x00000000; |
| break; |
| default: |
| reg_tmp = 0; //forces sims not to work if badly configured |
| } |
| |
| //Setup DDR Mode |
| switch (ddr_config_parameters->ddr_mode) { |
| case 0: |
| reg_tmp = reg_tmp + 0x00000000; |
| break; //SDR |
| case 1: |
| reg_tmp = reg_tmp + 0x40000000; |
| break; //DDR |
| case 2: |
| reg_tmp = reg_tmp + 0x80000000; |
| break; //DDR2 |
| default: |
| reg_tmp = 0; //forces sims not to work if badly configured |
| } |
| |
| //Setup Banks |
| if (ddr_config_parameters->banks8 == 1) { |
| reg_tmp = reg_tmp + 0x00800000; |
| } |
| |
| //Program DDR_CFG register... |
| *(volatile UINT *) C_DDR_CFG_REG = reg_tmp; |
| |
| //Configure PHY0 reg - se_mode is bit 1, |
| //needs to be 1 for DDR (single_ended drive) |
| switch (ddr_config_parameters->ddr_mode) { |
| case 0: |
| reg_tmp = 2 + (0 << 4); |
| break; //SDR |
| case 1: |
| reg_tmp = 2 + (4 << 4); |
| break; //DDR |
| case 2: |
| reg_tmp = 0 + (4 << 4); |
| break; //DDR2 |
| default: |
| reg_tmp = 0; |
| } |
| |
| //Program DDR_PHY0 register... |
| *(volatile UINT *) C_DDR_REG_PHY0 = reg_tmp; |
| |
| //Read DDR_PHY* registers to exercise paths for vcd |
| reg_tmp = *(volatile UINT *) C_DDR_REG_PHY3; |
| reg_tmp = *(volatile UINT *) C_DDR_REG_PHY2; |
| reg_tmp = *(volatile UINT *) C_DDR_REG_PHY1; |
| reg_tmp = *(volatile UINT *) C_DDR_REG_PHY0; |
| |
| //Start up sequences - Different dependant on DDR mode |
| switch (ddr_config_parameters->ddr_mode) { |
| case 2: //DDR2 |
| //Start-up sequence: follows procedure described in Micron datasheet. |
| //start up DDR PHY DLL |
| reg_tmp = 0x00022828; // dll on, start point and inc = h28 |
| *(volatile UINT *) C_DDR_REG_PHY2 = reg_tmp; |
| |
| reg_tmp = 0x00032828; // start on, dll on, start point and inc = h28 |
| *(volatile UINT *) C_DDR_REG_PHY2 = reg_tmp; |
| |
| ddr_phy_poll_until_locked(); |
| |
| udelay(200); //200us |
| |
| //Startup SDRAM... |
| //!!! Software: CK should be running for 200us before wake-up |
| ddr_issue_command( C_CMD_WAKE_UP); |
| ddr_issue_command( C_CMD_NOP); |
| ddr_issue_command( C_CMD_PRECHARGE_ALL); |
| ddr_issue_command( C_CMD_DDR2_EMR2); |
| ddr_issue_command( C_CMD_DDR2_EMR3); |
| |
| emr_cmd = C_CMD_DDR2_EMR1 + C_CMD_ODT_75 + C_CMD_REDUCED_DRIVE |
| + C_CMD_ENABLE_DLL; |
| |
| ddr_issue_command(emr_cmd); |
| //Sets CL=3; BL=8 but also reset DLL to trigger a DLL initialisation... |
| udelay(1); //1us |
| ddr_issue_command( |
| ddr_config_parameters->cmd_mode_wr_cl_bl |
| + C_CMD_RESET_DLL); |
| udelay(1); //1us |
| |
| //!!! Software: Wait 200 CK cycles before... |
| //for(i=1; i<=2; i++) { |
| ddr_issue_command(C_CMD_PRECHARGE_ALL); |
| // !!! Software: Wait here at least 8 CK cycles |
| //} |
| //need a wait here to ensure PHY DLL lock before the refresh is issued |
| udelay(1); //1us |
| for (i = 1; i <= 2; i++) { |
| ddr_issue_command( C_CMD_AUTO_REFRESH); |
| //!!! Software: Wait here at least 8 CK cycles to satify tRFC |
| udelay(1); //1us |
| } |
| //As before but without 'RESET_DLL' bit set... |
| ddr_issue_command(ddr_config_parameters->cmd_mode_wr_cl_bl); |
| udelay(1); //1us |
| // OCD commands |
| ddr_issue_command(emr_cmd + C_CMD_MODE_DDR2_OCD_DFLT); |
| ddr_issue_command(emr_cmd + C_CMD_MODE_DDR2_OCD_EXIT); |
| break; |
| |
| default: |
| break; //Do nothing |
| } |
| |
| //Enable auto-refresh |
| |
| // 8192 Refreshes required every 64ms, so maximum refresh period is 7.8125 us |
| // We have a 400 MHz DDR clock (2.5ns period) so max period is 3125 cycles |
| // Our core now does 8 refreshes in a go, so we multiply this period by 8 |
| |
| refresh = (64000 * mhz) / 8192; // Refresh period in clocks |
| |
| reg_tmp = *(volatile UINT *) C_DDR_CFG_REG; // read |
| #ifdef BURST_REFRESH_ENABLE |
| reg_tmp |= C_CFG_REFRESH_ENABLE | (refresh * 8); |
| reg_tmp |= C_CFG_BURST_REFRESH_ENABLE; |
| #else |
| reg_tmp |= C_CFG_REFRESH_ENABLE | (refresh * 1); |
| reg_tmp &= ~C_CFG_BURST_REFRESH_ENABLE; |
| #endif |
| *(volatile UINT *) C_DDR_CFG_REG = reg_tmp; |
| |
| //Verify register contents |
| reg_tmp = *(volatile UINT *) C_DDR_REG_PHY2; // read |
| //printf("Warning XXXXXXXXXXXXXXXXXXXXXX - get bad read data from C_DDR_PHY2_REG, though it looks OK on bus XXXXXXXXXXXXXXXXXX"); |
| //TBD Check_data (read_data, dll_reg, "Error: bad C_DDR_PHY2_REG read", tb_pass); |
| reg_tmp = *(volatile UINT *) C_DDR_CFG_REG; // read |
| //TBD Check_data (read_data, cfg_reg, "Error: bad DDR_CFG read", tb_pass); |
| |
| //disable optimised wrapping |
| if (ddr_config_parameters->ddr_mode == 2) { |
| reg_tmp = 0xFFFF0000; |
| *(volatile UINT *) C_DDR_REG_IGNORE = reg_tmp; |
| } |
| |
| //enable midbuffer followon |
| reg_tmp = *(volatile UINT *) C_DDR_ARB_REG; // read |
| reg_tmp = 0xFFFF0000 | reg_tmp; |
| *(volatile UINT *) C_DDR_ARB_REG = reg_tmp; |
| |
| // Enable write behind coherency checking for all clients |
| |
| reg_tmp = 0xFFFF0000; |
| *(volatile UINT *) C_DDR_AHB4_REG = reg_tmp; |
| |
| //Wait for 200 clock cycles for SDRAM DLL to lock... |
| udelay(1); //1us |
| } |
| |
| // Function used to Setup DDR core |
| |
| void ddr_setup(int mhz) |
| { |
| static const T_DDR_TIMING_PARAMETERS *ddr_timing_parameters = |
| &C_TP_DDR2_25_CL6_1GB; |
| static const T_DDR_CONFIG_PARAMETERS *ddr_config_parameters = |
| &C_CP_DDR2_25_CL6; |
| |
| //Bring core out of Reset |
| *(volatile UINT *) C_DDR_BLKEN_REG = C_BLKEN_DDR_ON; |
| |
| //DDR TIMING INITIALISTION |
| ddr_timing_initialisation(ddr_timing_parameters); |
| |
| //DDR NORMAL INITIALISATION |
| ddr_normal_initialisation(ddr_config_parameters, mhz); |
| |
| // route all writes through one client |
| *(volatile UINT *) C_DDR_TRANSACTION_ROUTING = (0 |
| << DDR_ROUTE_CPU0_INSTR_SHIFT) |
| | (1 << DDR_ROUTE_CPU0_RDDATA_SHIFT) |
| | (3 << DDR_ROUTE_CPU0_WRDATA_SHIFT) |
| | (2 << DDR_ROUTE_CPU1_INSTR_SHIFT) |
| | (3 << DDR_ROUTE_CPU1_RDDATA_SHIFT) |
| | (3 << DDR_ROUTE_CPU1_WRDATA_SHIFT); |
| |
| //Bring all clients out of reset |
| *(volatile UINT *) C_DDR_BLKEN_REG = C_BLKEN_DDR_ON + 0x0000FFFF; |
| |
| } |
| |
| void set_ddr_timing(unsigned int w, unsigned int i) |
| { |
| unsigned int reg; |
| unsigned int wnow = 16; |
| unsigned int inow = 32; |
| |
| /* reset all timing controls to known value (31) */ |
| writel(DDR_PHY_TIMING_W_RST | DDR_PHY_TIMING_I_RST, DDR_PHY_TIMING); |
| writel(DDR_PHY_TIMING_W_RST | DDR_PHY_TIMING_I_RST | DDR_PHY_TIMING_CK, |
| DDR_PHY_TIMING); |
| writel(DDR_PHY_TIMING_W_RST | DDR_PHY_TIMING_I_RST, DDR_PHY_TIMING); |
| |
| /* step up or down read delay to the requested value */ |
| while (wnow != w) { |
| if (wnow < w) { |
| reg = DDR_PHY_TIMING_INC; |
| wnow++; |
| } else { |
| reg = 0; |
| wnow--; |
| } |
| writel(DDR_PHY_TIMING_W_CE | reg, DDR_PHY_TIMING); |
| writel(DDR_PHY_TIMING_CK | DDR_PHY_TIMING_W_CE | reg, |
| DDR_PHY_TIMING); |
| writel(DDR_PHY_TIMING_W_CE | reg, DDR_PHY_TIMING); |
| } |
| |
| /* now write delay */ |
| while (inow != i) { |
| if (inow < i) { |
| reg = DDR_PHY_TIMING_INC; |
| inow++; |
| } else { |
| reg = 0; |
| inow--; |
| } |
| writel(DDR_PHY_TIMING_I_CE | reg, DDR_PHY_TIMING); |
| writel(DDR_PHY_TIMING_CK | DDR_PHY_TIMING_I_CE | reg, |
| DDR_PHY_TIMING); |
| writel(DDR_PHY_TIMING_I_CE | reg, DDR_PHY_TIMING); |
| } |
| } |
| |
| //Function used to Setup SDRAM in DDR/SDR mode |
| void init_ddr(int mhz) |
| { |
| /* start clocks */ |
| enable_clock(SYS_CTRL_CLK_DDRPHY); |
| enable_clock(SYS_CTRL_CLK_DDR); |
| enable_clock(SYS_CTRL_CLK_DDRCK); |
| |
| /* bring phy and core out of reset */ |
| reset_block(SYS_CTRL_RST_DDR_PHY, 0); |
| reset_block(SYS_CTRL_RST_DDR, 0); |
| |
| /* DDR runs at half the speed of the CPU */ |
| ddr_setup(mhz >> 1); |
| return; |
| } |