diff options
Diffstat (limited to 'arch/powerpc/ddr-8xxx')
-rw-r--r-- | arch/powerpc/ddr-8xxx/Makefile | 4 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/common_timing_params.h | 46 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/ctrl_regs.c | 778 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/ddr.h | 116 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/ddr2_dimm_params.c | 296 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/ddr3_dimm_params.c | 193 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/ddr_setctrl.c | 90 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/lc_common_dimm_params.c | 255 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/main.c | 246 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/options.c | 147 | ||||
-rw-r--r-- | arch/powerpc/ddr-8xxx/util.c | 100 |
11 files changed, 2271 insertions, 0 deletions
diff --git a/arch/powerpc/ddr-8xxx/Makefile b/arch/powerpc/ddr-8xxx/Makefile new file mode 100644 index 0000000000..43ae3a41df --- /dev/null +++ b/arch/powerpc/ddr-8xxx/Makefile @@ -0,0 +1,4 @@ +obj-y += main.o util.o ctrl_regs.o options.o lc_common_dimm_params.o +obj-y += ddr_setctrl.o +obj-$(CONFIG_FSL_DDR2) += ddr2_dimm_params.o +obj-$(CONFIG_FSL_DDR3) += ddr3_dimm_params.o diff --git a/arch/powerpc/ddr-8xxx/common_timing_params.h b/arch/powerpc/ddr-8xxx/common_timing_params.h new file mode 100644 index 0000000000..85a1e2868f --- /dev/null +++ b/arch/powerpc/ddr-8xxx/common_timing_params.h @@ -0,0 +1,46 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#ifndef COMMON_TIMING_PARAMS_H +#define COMMON_TIMING_PARAMS_H + +struct common_timing_params_s { + uint32_t tCKmin_X_ps; + uint32_t tCKmax_ps; + uint32_t tCKmax_max_ps; + uint32_t tRCD_ps; + uint32_t tRP_ps; + uint32_t tRAS_ps; + uint32_t tWR_ps; /* maximum = 63750 ps */ + uint32_t tWTR_ps; /* maximum = 63750 ps */ + uint32_t tRFC_ps; /* maximum = 255 ns + 256 ns + .75 ns + = 511750 ps */ + uint32_t tRRD_ps; /* maximum = 63750 ps */ + uint32_t tRC_ps; /* maximum = 254 ns + .75 ns = 254750 ps */ + uint32_t refresh_rate_ps; + uint32_t extended_op_srt; + uint32_t tIS_ps; /* byte 32, spd->ca_setup */ + uint32_t tIH_ps; /* byte 33, spd->ca_hold */ + uint32_t tDS_ps; /* byte 34, spd->data_setup */ + uint32_t tDH_ps; /* byte 35, spd->data_hold */ + uint32_t tRTP_ps; /* byte 38, spd->trtp */ + uint32_t tDQSQ_max_ps; /* byte 44, spd->tdqsq */ + uint32_t tQHS_ps; /* byte 45, spd->tqhs */ + uint32_t ndimms_present; + uint32_t lowest_common_SPD_caslat; + uint32_t highest_common_derated_caslat; + uint32_t additive_latency; + uint32_t all_DIMMs_burst_lengths_bitmask; + uint32_t all_DIMMs_registered; + uint32_t all_DIMMs_unbuffered; + uint32_t all_DIMMs_ECC_capable; + uint64_t total_mem; + uint64_t base_address; +}; + +#endif diff --git a/arch/powerpc/ddr-8xxx/ctrl_regs.c b/arch/powerpc/ddr-8xxx/ctrl_regs.c new file mode 100644 index 0000000000..e3d43ab09e --- /dev/null +++ b/arch/powerpc/ddr-8xxx/ctrl_regs.c @@ -0,0 +1,778 @@ +/* + * Copyright 2008-2012 Freescale Semiconductor, Inc. + * + * 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. + */ + +/* + * Generic driver for Freescale DDR2/DDR3 memory controller. + * Based on code from spd_sdram.c + * Author: James Yang [at freescale.com] + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> +#include "ddr.h" + +static uint32_t compute_cas_write_latency(void) +{ + uint32_t cwl; + const uint32_t mclk_ps = get_memory_clk_period_ps(); + + if (mclk_ps >= 2500) + cwl = 5; + else if (mclk_ps >= 1875) + cwl = 6; + else if (mclk_ps >= 1500) + cwl = 7; + else if (mclk_ps >= 1250) + cwl = 8; + else if (mclk_ps >= 1070) + cwl = 9; + else if (mclk_ps >= 935) + cwl = 10; + else if (mclk_ps >= 833) + cwl = 11; + else if (mclk_ps >= 750) + cwl = 12; + else + cwl = 12; + + return cwl; +} + +static void set_csn_config(int dimm_number, int i, + struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct dimm_params_s *dimm) +{ + uint32_t cs_n_en = 0, ap_n_en = 0, odt_rd_cfg = 0, odt_wr_cfg = 0, + ba_bits_cs_n = 0, row_bits_cs_n = 0, col_bits_cs_n = 0, + n_banks_per_sdram_device; + int go_config = 0; + + switch (i) { + case 0: + if (dimm[dimm_number].n_ranks > 0) + go_config = 1; + break; + case 1: + if ((dimm_number == 0 && dimm[0].n_ranks > 1) || + (dimm_number == 1 && dimm[1].n_ranks > 0)) + go_config = 1; + break; + case 2: + if ((dimm_number == 0 && dimm[0].n_ranks > 2) || + (dimm_number >= 1 && dimm[dimm_number].n_ranks > 0)) + go_config = 1; + break; + case 3: + if ((dimm_number == 0 && dimm[0].n_ranks > 3) || + (dimm_number == 1 && dimm[1].n_ranks > 1) || + (dimm_number == 3 && dimm[3].n_ranks > 0)) + go_config = 1; + break; + default: + break; + } + + if (go_config) { + /* Chip Select enable */ + cs_n_en = 1; + /* CSn auto-precharge enable */ + ap_n_en = popts->cs_local_opts[i].auto_precharge; + /* ODT for reads configuration */ + odt_rd_cfg = popts->cs_local_opts[i].odt_rd_cfg; + /* ODT for writes configuration */ + odt_wr_cfg = popts->cs_local_opts[i].odt_wr_cfg; + /* Num of bank bits for SDRAM on CSn */ + n_banks_per_sdram_device = + dimm[dimm_number].n_banks_per_sdram_device; + ba_bits_cs_n = __ilog2(n_banks_per_sdram_device) - 2; + /* Num of row bits for SDRAM on CSn */ + row_bits_cs_n = dimm[dimm_number].n_row_addr - 12; + /* Num of ocl bits for SDRAM on CSn */ + col_bits_cs_n = dimm[dimm_number].n_col_addr - 8; + } + + ddr->cs[i].config = (((cs_n_en & 0x1) << 31) + | ((ap_n_en & 0x1) << 23) + | ((odt_rd_cfg & 0x7) << 20) + | ((odt_wr_cfg & 0x7) << 16) + | ((ba_bits_cs_n & 0x3) << 14) + | ((row_bits_cs_n & 0x7) << 8) + | ((col_bits_cs_n & 0x7) << 0)); +} + +static void set_timing_cfg_0(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct dimm_params_s *dimm) +{ + uint32_t trwt_mclk = 0, twrt_mclk = 0, act_pd_exit_mclk, + pre_pd_exit_mclk, taxpd_mclk, tmrd_mclk, txp, + data_rate = fsl_get_ddr_freq(0); + + if (popts->sdram_type == SDRAM_TYPE_DDR2) { + act_pd_exit_mclk = popts->txard; + pre_pd_exit_mclk = popts->txp; + taxpd_mclk = popts->taxpd; + tmrd_mclk = popts->tmrd; + } else { + /* + * tXARD is not part of the DDR3 specification, use the + * parameter txp instead of it. That is: + * txp=max(3nCK, 7.5ns). As well, use tAXPD=1. + */ + txp = max_t(uint32_t, (get_memory_clk_period_ps() * 3), 7500); + data_rate = fsl_get_ddr_freq(0); + tmrd_mclk = 4; + + /* for faster clock, need more time for data setup */ + if (popts->trwt_override) + trwt_mclk = popts->trwt; + else if (data_rate / 1000000 > 1800) + trwt_mclk = 2; + else + trwt_mclk = 0; + + if (data_rate / 1000000 > 1150) + twrt_mclk = 1; + else + twrt_mclk = 0; + + taxpd_mclk = 1; + if (popts->dynamic_power == 0) { + act_pd_exit_mclk = 1; + pre_pd_exit_mclk = 1; + } else { + /* act_pd_exit_mclk = tXARD, see above */ + act_pd_exit_mclk = picos_to_mclk(txp); + /* Mode register MR0[A12] is '1' - fast exit */ + pre_pd_exit_mclk = act_pd_exit_mclk; + } + } + + ddr->timing_cfg_0 = (((trwt_mclk & 0x3) << 30) + | ((twrt_mclk & 0x3) << 28) + | ((act_pd_exit_mclk & 0xf) << 20) + | ((pre_pd_exit_mclk & 0xf) << 16) + | ((taxpd_mclk & 0xf) << 8) + | ((tmrd_mclk & 0x1f) << 0) + ); +} + +static void set_timing_cfg_3(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct common_timing_params_s *dimm, + uint32_t cas_latency, uint32_t additive_latency) +{ + uint32_t ext_pretoact, ext_acttopre, ext_acttorw, ext_refrec, ext_wrrec; + + ext_pretoact = picos_to_mclk(dimm->tRP_ps) >> 4; + ext_acttopre = picos_to_mclk(dimm->tRAS_ps) >> 4; + ext_acttorw = picos_to_mclk(dimm->tRCD_ps) >> 4; + cas_latency = ((cas_latency << 1) - 1) >> 4; + additive_latency = additive_latency >> 4; + ext_refrec = (picos_to_mclk(dimm->tRFC_ps) - 8) >> 4; + /* ext_wrrec only deals with 16 clock and above, or 14 with OTF */ + ext_wrrec = (picos_to_mclk(dimm->tWR_ps) + + (popts->otf_burst_chop_en ? 2 : 0)) >> 4; + + ddr->timing_cfg_3 = (((ext_pretoact & 0x1) << 28) + | ((ext_acttopre & 0x3) << 24) + | ((ext_acttorw & 0x1) << 22) + | ((ext_refrec & 0x1F) << 16) + | ((cas_latency & 0x3) << 12) + | ((additive_latency & 0x1) << 10) + | ((ext_wrrec & 0x1) << 8) + ); +} + +static void set_timing_cfg_1(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct common_timing_params_s *dimm, + uint32_t cas_latency) +{ + uint32_t pretoact_mclk, acttopre_mclk, acttorw_mclk, refrec_ctrl, + wrrec_mclk, acttoact_mclk, wrtord_mclk; + /* DDR_SDRAM_MODE doesn't support 9,11,13,15 */ + static const u8 wrrec_table[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 10, 10, 12, 12, 14, 14, 0, 0 + }; + + pretoact_mclk = picos_to_mclk(dimm->tRP_ps); + acttopre_mclk = picos_to_mclk(dimm->tRAS_ps); + acttorw_mclk = picos_to_mclk(dimm->tRCD_ps); + + /* + * Translate CAS Latency to a DDR controller field value: + * + * CAS Lat DDR II Ctrl + * Clocks SPD Bit Value + * ------- ------- ------ + * 1.0 0001 + * 1.5 0010 + * 2.0 2 0011 + * 2.5 0100 + * 3.0 3 0101 + * 3.5 0110 + * 4.0 4 0111 + * 4.5 1000 + * 5.0 5 1001 + */ + cas_latency = (cas_latency << 1) - 1; + refrec_ctrl = picos_to_mclk(dimm->tRFC_ps) - 8; + acttoact_mclk = picos_to_mclk(dimm->tRRD_ps); + + wrrec_mclk = picos_to_mclk(dimm->tWR_ps); + if (wrrec_mclk <= 16) + wrrec_mclk = wrrec_table[wrrec_mclk - 1]; + if (popts->otf_burst_chop_en) + wrrec_mclk += 2; + + wrtord_mclk = picos_to_mclk(dimm->tWTR_ps); + if (popts->sdram_type == SDRAM_TYPE_DDR2) { + wrtord_mclk = max_t(uint32_t, wrtord_mclk, 2); + } else { + wrtord_mclk = max_t(uint32_t, wrtord_mclk, 4); + acttoact_mclk = max_t(uint32_t, acttoact_mclk, 4); + } + + if (popts->otf_burst_chop_en) + wrtord_mclk += 2; + + ddr->timing_cfg_1 = (((pretoact_mclk & 0x0F) << 28) + | ((acttopre_mclk & 0x0F) << 24) + | ((acttorw_mclk & 0xF) << 20) + | ((cas_latency & 0xF) << 16) + | ((refrec_ctrl & 0xF) << 12) + | ((wrrec_mclk & 0x0F) << 8) + | ((acttoact_mclk & 0x0F) << 4) + | ((wrtord_mclk & 0x0F) << 0)); +} + +static void set_timing_cfg_2(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct common_timing_params_s *dimm, + uint32_t cas_latency, uint32_t additive_latency) +{ + uint32_t cpo, rd_to_pre, wr_data_delay, cke_pls, four_act; + + cpo = popts->cpo_override; + rd_to_pre = picos_to_mclk(dimm->tRTP_ps); + if (popts->sdram_type == SDRAM_TYPE_DDR2) { + cas_latency = cas_latency - 1; + rd_to_pre = max_t(uint32_t, rd_to_pre, 2); + } else { + cas_latency = compute_cas_write_latency(); + rd_to_pre = max_t(uint32_t, rd_to_pre, 4); + } + + if (popts->otf_burst_chop_en) + rd_to_pre += 2; + + wr_data_delay = popts->write_data_delay; + cke_pls = picos_to_mclk(popts->tCKE_clock_pulse_width_ps); + four_act = picos_to_mclk(popts->tFAW_window_four_activates_ps); + + ddr->timing_cfg_2 = (((additive_latency & 0xf) << 28) + | ((cpo & 0x1f) << 23) + | ((cas_latency & 0xf) << 19) + | ((rd_to_pre & 7) << 13) + | ((wr_data_delay & 7) << 10) + | ((cke_pls & 0x7) << 6) + | ((four_act & 0x3f) << 0)); +} + +static void set_ddr_sdram_cfg(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct common_timing_params_s *dimm) +{ + uint32_t mem_en, sren, ecc_en, sdram_type, dyn_pwr, dbw, twoT_en, hse, + threet_en, eight_be = 0; + + mem_en = 1; + sren = popts->self_refresh_in_sleep; + if (dimm->all_DIMMs_ECC_capable) + ecc_en = popts->ECC_mode; + else + ecc_en = 0; + + sdram_type = popts->sdram_type; + twoT_en = popts->twoT_en; + dyn_pwr = popts->dynamic_power; + dbw = popts->data_bus_width; + hse = popts->half_strength_driver_enable; + threet_en = popts->threet_en; + + if (sdram_type == SDRAM_TYPE_DDR3) + if ((popts->burst_length == DDR_BL8) || (dbw == 1)) + eight_be = 1; + + ddr->ddr_sdram_cfg = (((mem_en & 0x1) << 31) + | ((sren & 0x1) << 30) + | ((ecc_en & 0x1) << 29) + | ((sdram_type & 0x7) << 24) + | ((dyn_pwr & 0x1) << 21) + | ((dbw & 0x3) << 19) + | ((eight_be & 0x1) << 18) + | ((threet_en & 0x1) << 16) + | ((twoT_en & 0x1) << 15) + | ((hse & 0x1) << 3)); +} + +static void set_ddr_sdram_cfg_2(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts) +{ + struct ddr_board_info_s *bi = popts->board_info; + uint32_t i, dll_rst_dis, dqs_cfg, odt_cfg = 0, num_pr, d_init = 0, + obc_cfg = 0, x4_en, md_en = 0, rcw_en = 0; + + dll_rst_dis = popts->dll_rst_dis; + dqs_cfg = popts->DQS_config; + + /* + * Check for On-Die Termination options and + * assert ODT only during reads to DRAM. + */ + for (i = 0; i < bi->cs_per_ctrl; i++) + if (popts->cs_local_opts[i].odt_rd_cfg || + popts->cs_local_opts[i].odt_wr_cfg) { + odt_cfg = SDRAM_CFG2_ODT_ONLY_READ; + break; + } + + /* Default number of posted refresh */ + num_pr = 1; + + if (popts->ECC_init_using_memctl) { + d_init = 1; + ddr->ddr_data_init = popts->data_init; + } + + if (popts->sdram_type == SDRAM_TYPE_DDR3) { + obc_cfg = popts->otf_burst_chop_en; + md_en = popts->mirrored_dimm; + } + + x4_en = popts->x4_en ? 1 : 0; + + ddr->ddr_sdram_cfg_2 = (((dll_rst_dis & 0x1) << 29) + | ((dqs_cfg & 0x3) << 26) + | ((odt_cfg & 0x3) << 21) + | ((num_pr & 0xf) << 12) + | (x4_en << 10) + | ((obc_cfg & 0x1) << 6) + | ((d_init & 0x1) << 4) + | ((rcw_en & 0x1) << 2) + | ((md_en & 0x1) << 0) + ); +} + +static void set_ddr_sdram_mode_2(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct common_timing_params_s *dimm) +{ + uint16_t esdmode2; + uint32_t rtt_wr, srt = 0, cwl; + + cwl = compute_cas_write_latency() - 5; + + if (popts->rtt_override) + rtt_wr = popts->rtt_wr_override_value; + else + rtt_wr = popts->cs_local_opts[0].odt_rtt_wr; + + if (dimm->extended_op_srt) + srt = dimm->extended_op_srt; + + esdmode2 = (((rtt_wr & 0x3) << 9) + | ((srt & 0x1) << 7) + | ((cwl & 0x7) << 3) + ); + + ddr->ddr_sdram_mode_2 = (esdmode2 & 0xffff) << 16; +} + +static void +set_ddr_sdram_interval(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct common_timing_params_s *dimm) +{ + uint32_t refint, bstopre; + + refint = picos_to_mclk(dimm->refresh_rate_ps); + /* Precharge interval */ + bstopre = popts->bstopre; + + ddr->ddr_sdram_interval = (((refint & 0xFFFF) << 16) + | ((bstopre & 0x3FFF) << 0)); +} +void set_ddr3_sdram_mode(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct common_timing_params_s *dimm, + uint32_t cas_latency, uint32_t additive_latency) +{ + uint16_t esdmode, sdmode; + /* Mode Register - MR1 */ + uint32_t rtt, al; + /* Mode Register - MR0 */ + uint32_t dll_on, wr = 0, dll_rst, mode, caslat = 4, bt, bl, wr_mclk; + /* + * DDR_SDRAM_MODE doesn't support 9,11,13,15 + * Please refer JEDEC Standard No. 79-3E for Mode Register MR0 + * for this table + */ + static const u8 wr_table[] = {1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0}; + uint8_t cas_latency_table[] = { /* From 5 to 16 clocks */ + 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x1, 0x3, 0x5, 0x7, 0x9, + }; + const unsigned int mclk_ps = get_memory_clk_period_ps(); + + if (popts->rtt_override) + rtt = popts->rtt_override_value; + else + rtt = popts->cs_local_opts[0].odt_rtt_norm; + + if (additive_latency == (cas_latency - 1)) + al = 1; + else if (additive_latency == (cas_latency - 2)) + al = 2; + else + al = 0; + + /* + * The esdmode value will also be used for writing + * MR1 during write leveling for DDR3, although the + * bits specifically related to the write leveling + * scheme will be handled automatically by the DDR + * controller. So wrlvl_en is set to 0 here. + */ + esdmode = (((rtt & 0x4) << 7) + | ((rtt & 0x2) << 5) + | ((al & 0x3) << 3) + | ((rtt & 0x1) << 2) + ); + + /* + * DLL control for precharge PD + * 0=slow exit DLL off (tXPDLL) + * 1=fast exit DLL on (tXP) + */ + dll_on = 1; + + wr_mclk = (dimm->tWR_ps + mclk_ps - 1) / mclk_ps; + if (wr_mclk <= 16) + wr = wr_table[wr_mclk - 5]; + + dll_rst = 0; /* dll no reset */ + mode = 0; /* normal mode */ + + /* look up table to get the cas latency bits */ + if (cas_latency >= 5 && cas_latency <= 16) + caslat = cas_latency_table[cas_latency - 5]; + + /* BT: Burst Type (0=Nibble Sequential, 1=Interleaved) */ + bt = 0; + + switch (popts->burst_length) { + case DDR_BL8: + bl = 0; + break; + case DDR_OTF: + bl = 1; + break; + case DDR_BC4: + bl = 2; + break; + default: + bl = 1; + break; + } + + sdmode = (((dll_on & 0x1) << 12) + | ((wr & 0x7) << 9) + | ((dll_rst & 0x1) << 8) + | ((mode & 0x1) << 7) + | (((caslat >> 1) & 0x7) << 4) + | ((bt & 0x1) << 3) + | ((caslat & 1) << 2) + | ((bl & 0x3) << 0) + ); + + ddr->ddr_sdram_mode = (((esdmode & 0xffff) << 16) + | ((sdmode & 0xffff) << 0) + ); +} + +void set_ddr2_sdram_mode(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct common_timing_params_s *dimm, + uint32_t cas_latency, uint32_t additive_latency) +{ + uint16_t esdmode, sdmode; + uint32_t dqs_en, rtt, al, wr, bl; + const uint32_t mclk_ps = get_memory_clk_period_ps(); + + /* DQS# Enable: 0=enable, 1=disable */ + dqs_en = !popts->DQS_config; + /* Posted CAS# additive latency (AL) */ + al = additive_latency; + /* Internal Termination Resistor */ + if (popts->rtt_override) + rtt = popts->rtt_override_value; + else + rtt = popts->cs_local_opts[0].odt_rtt_norm; + + /* + * Extended SDRAM mode. + * The variable also selects: + * - OCD set to exit mode + * - all outputs bit i.e DQ, DQS, RDQS output enabled + * - RDQS ball disabled + * - DQS ball enabled + * - DLL enabled + * - Output drive strength: full strength. + */ + esdmode = (((dqs_en & 0x1) << 10) + | ((rtt & 0x2) << 5) + | ((al & 0x7) << 3) + | ((rtt & 0x1) << 2)); + + /* Write recovery */ + wr = ((dimm->tWR_ps + mclk_ps - 1) / mclk_ps) - 1; + + switch (popts->burst_length) { + case DDR_BL4: + bl = 2; + break; + case DDR_BL8: + bl = 3; + break; + default: + bl = 2; + break; + } + + /* SDRAM mode + * The variable also selects: + * - power down mode: fast exit (normal) + * - DLL reset disabled. + * - burst type: sequential + */ + sdmode = (((wr & 0x7) << 9) + | ((cas_latency & 0x7) << 4) + | ((bl & 0x7) << 0)); + + ddr->ddr_sdram_mode = (((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0)); +} + +void set_ddrx_sdram_mode(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + const struct common_timing_params_s *dimm, + uint32_t cas_latency, uint32_t additive_latency) +{ + if (popts->sdram_type == SDRAM_TYPE_DDR2) + set_ddr2_sdram_mode(ddr, popts, dimm, cas_latency, + additive_latency); + else + set_ddr3_sdram_mode(ddr, popts, dimm, cas_latency, + additive_latency); +} + +uint32_t check_fsl_memctl_config_regs(const struct fsl_ddr_cfg_regs_s *ddr) +{ + /* + * DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN should not + * be set at the same time. + */ + if ((ddr->ddr_sdram_cfg & 0x10000000) && + (ddr->ddr_sdram_cfg & 0x00008000)) + return 1; + + return 0; +} + +static void set_timing_cfg_4(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts) +{ + uint32_t rrt = 0, wwt = 0, dll_lock = 1; + + if (popts->burst_length != DDR_BL8) + rrt = wwt = 2; + + ddr->timing_cfg_4 = (((rrt & 0xf) << 20) + | ((wwt & 0xf) << 16) + | (dll_lock & 0x3) + ); +} + +static void set_timing_cfg_5(struct fsl_ddr_cfg_regs_s *ddr, + const struct memctl_options_s *popts, + uint32_t cas_latency) +{ + uint32_t rodt_on, rodt_off = 4, wodt_on = 1, wodt_off = 4; + + /* rodt_on = timing_cfg_1[caslat] - timing_cfg_2[wrlat] + 1 */ + rodt_on = cas_latency - ((ddr->timing_cfg_2 & 0x00780000) >> 19) + 1; + + ddr->timing_cfg_5 = (((rodt_on & 0x1f) << 24) + | ((rodt_off & 0x7) << 20) + | ((wodt_on & 0x1f) << 12) + | ((wodt_off & 0x7) << 8) + ); +} + +static void set_ddr_zq_cntl(struct fsl_ddr_cfg_regs_s *ddr, uint32_t zq_en) +{ + uint32_t zqinit = 0, zqoper = 0, zqcs = 0; + + if (zq_en) { + zqinit = 9; + zqoper = 8; + zqcs = 6; + } + + ddr->ddr_zq_cntl = (((zq_en & 0x1) << 31) + | ((zqinit & 0xf) << 24) + | ((zqoper & 0xf) << 16) + | ((zqcs & 0xf) << 8) + ); +} + +static void set_ddr_wrlvl_cntl(struct fsl_ddr_cfg_regs_s *ddr, + uint32_t wrlvl_en, const struct memctl_options_s *popts) +{ + uint32_t wrlvl_mrd = 0, wrlvl_odten = 0, wrlvl_dqsen = 0, + wrlvl_wlr = 0, wrlvl_start = 0, wrlvl_smpl = 0; + + /* Enable write leveling for DDR3 due to fly-by topology */ + if (wrlvl_en) { + wrlvl_mrd = 0x6; + wrlvl_odten = 0x7; + wrlvl_dqsen = 0x5; + /* + * Write leveling sample time at least need 6 clocks + * higher than tWLO to allow enough time for progagation + * delay and sampling the prime data bits. + */ + wrlvl_smpl = 0xf; + /* + * Write leveling repetition time. At least tWLO + 6 clocks + * Set it to 64 + */ + wrlvl_wlr = 0x6; + /* + * Write leveling start time + * The value use for the DQS_ADJUST for the first sample + * when write leveling is enabled. It probably needs to be + * overriden per platform. + */ + wrlvl_start = 0x8; + if (popts->wrlvl_override) { + wrlvl_smpl = popts->wrlvl_sample; + wrlvl_start = popts->wrlvl_start; + } + } + + ddr->ddr_wrlvl_cntl = (((wrlvl_en & 0x1) << 31) + | ((wrlvl_mrd & 0x7) << 24) + | ((wrlvl_odten & 0x7) << 20) + | ((wrlvl_dqsen & 0x7) << 16) + | ((wrlvl_smpl & 0xf) << 12) + | ((wrlvl_wlr & 0x7) << 8) + | ((wrlvl_start & 0x1f) << 0) + ); +} + +uint32_t +compute_fsl_memctl_config_regs(const struct memctl_options_s *popts, + struct fsl_ddr_cfg_regs_s *ddr, + const struct common_timing_params_s *dimm, + const struct dimm_params_s *dimmp, + uint32_t dbw_cap_adj) +{ + struct ddr_board_info_s *binfo = popts->board_info; + uint32_t cas_latency, additive_latency, i, cs_per_dimm, + dimm_number, zq_en, wrlvl_en, sr_it = 0; + uint64_t ea, sa, rank_density; + + if (dimm == NULL) + return 1; + + memset(ddr, 0, sizeof(struct fsl_ddr_cfg_regs_s)); + + /* Process overrides first. */ + if (popts->cas_latency_override) + cas_latency = popts->cas_latency_override_value; + else + cas_latency = dimm->lowest_common_SPD_caslat; + + if (popts->additive_latency_override) + additive_latency = popts->additive_latency_override_value; + else + additive_latency = dimm->additive_latency; + + if (popts->auto_self_refresh_en) + sr_it = popts->sr_it; + + /* Chip Select Memory Bounds (CSn_BNDS) */ + for (i = 0; i < binfo->cs_per_ctrl; i++) { + cs_per_dimm = binfo->cs_per_ctrl / binfo->dimm_slots_per_ctrl; + dimm_number = i / cs_per_dimm; + rank_density = + dimmp[dimm_number].rank_density >> dbw_cap_adj; + + if (dimmp[dimm_number].n_ranks == 0) + continue; + + sa = dimmp[dimm_number].base_address; + ea = sa + rank_density - 1; + if (dimmp[dimm_number].n_ranks > (i % cs_per_dimm)) { + sa += (i % cs_per_dimm) * rank_density; + ea += (i % cs_per_dimm) * rank_density; + } else { + sa = 0; + ea = 0; + } + sa >>= 24; + ea >>= 24; + + ddr->cs[i].bnds = + (((sa & 0xffff) << 16) | ((ea & 0xffff) << 0)); + set_csn_config(dimm_number, i, ddr, popts, dimmp); + } + + set_timing_cfg_0(ddr, popts, dimmp); + set_timing_cfg_3(ddr, popts, dimm, cas_latency, additive_latency); + set_timing_cfg_1(ddr, popts, dimm, cas_latency); + set_timing_cfg_2(ddr, popts, dimm, cas_latency, additive_latency); + + ddr->ddr_cdr1 = popts->ddr_cdr1; + ddr->ddr_cdr1 = popts->ddr_cdr2; + + set_ddr_sdram_cfg(ddr, popts, dimm); + set_ddr_sdram_cfg_2(ddr, popts); + set_ddrx_sdram_mode(ddr, popts, dimm, cas_latency, additive_latency); + if (popts->sdram_type == SDRAM_TYPE_DDR3) { + set_ddr_sdram_mode_2(ddr, popts, dimm); + set_timing_cfg_4(ddr, popts); + set_timing_cfg_5(ddr, popts, cas_latency); + zq_en = (popts->zq_en) ? 1 : 0; + set_ddr_zq_cntl(ddr, zq_en); + wrlvl_en = (popts->wrlvl_en) ? 1 : 0; + set_ddr_wrlvl_cntl(ddr, wrlvl_en, popts); + } + set_ddr_sdram_interval(ddr, popts, dimm); + + ddr->ddr_data_init = popts->data_init; + ddr->ddr_sdram_clk_cntl = (popts->clk_adjust & 0xF) << 23; + + ddr->ddr_sr_cntr = (sr_it & 0xf) << 16; + + return check_fsl_memctl_config_regs(ddr); +} diff --git a/arch/powerpc/ddr-8xxx/ddr.h b/arch/powerpc/ddr-8xxx/ddr.h new file mode 100644 index 0000000000..2ef87f2776 --- /dev/null +++ b/arch/powerpc/ddr-8xxx/ddr.h @@ -0,0 +1,116 @@ +/* + * Copyright 2013 GE Intelligent Platforms, Inc + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#ifndef FSL_DDR_MAIN_H +#define FSL_DDR_MAIN_H + +#include <asm/fsl_ddr_sdram.h> +#include <asm/fsl_ddr_dimm_params.h> +#include <mach/fsl_i2c.h> +#include <mach/clock.h> +#include "common_timing_params.h" + +#ifdef CONFIG_MPC85xx +#include <mach/immap_85xx.h> +#endif + +/* Record of computed register values. */ +struct fsl_ddr_cfg_regs_s { + struct { + uint32_t bnds; + uint32_t config; + uint32_t config_2; + } cs[MAX_CHIP_SELECTS_PER_CTRL]; + uint32_t timing_cfg_3; + uint32_t timing_cfg_0; + uint32_t timing_cfg_1; + uint32_t timing_cfg_2; + uint32_t ddr_sdram_cfg; + uint32_t ddr_sdram_cfg_2; + uint32_t ddr_sdram_mode; + uint32_t ddr_sdram_mode_2; + uint32_t ddr_sdram_mode_3; + uint32_t ddr_sdram_mode_4; + uint32_t ddr_sdram_mode_5; + uint32_t ddr_sdram_mode_6; + uint32_t ddr_sdram_mode_7; + uint32_t ddr_sdram_mode_8; + uint32_t ddr_sdram_md_cntl; + uint32_t ddr_sdram_interval; + uint32_t ddr_data_init; + uint32_t ddr_sdram_clk_cntl; + uint32_t ddr_init_addr; + uint32_t ddr_init_ext_addr; + uint32_t timing_cfg_4; + uint32_t timing_cfg_5; + uint32_t ddr_zq_cntl; + uint32_t ddr_wrlvl_cntl; + uint32_t ddr_wrlvl_cntl_2; + uint32_t ddr_wrlvl_cntl_3; + uint32_t ddr_sr_cntr; + uint32_t ddr_sdram_rcw_1; + uint32_t ddr_sdram_rcw_2; + uint32_t ddr_cdr1; + uint32_t ddr_cdr2; + uint32_t err_disable; + uint32_t err_int_en; + uint32_t debug[32]; +}; + +/* + * Data Structures + * + * All data structures have to be on the stack + */ +struct fsl_ddr_info_s { + generic_spd_eeprom_t + spd_installed_dimms[MAX_DIMM_SLOTS_PER_CTLR]; + struct dimm_params_s + dimm_params[MAX_DIMM_SLOTS_PER_CTLR]; + struct memctl_options_s memctl_opts; + struct common_timing_params_s common_timing_params; + struct fsl_ddr_cfg_regs_s fsl_ddr_config_reg; + struct ddr_board_info_s board_info; +}; + +uint32_t mclk_to_picos(uint32_t mclk); +uint32_t get_memory_clk_period_ps(void); +uint32_t picos_to_mclk(uint32_t picos); +uint32_t check_fsl_memctl_config_regs(const struct fsl_ddr_cfg_regs_s *ddr); +uint64_t fsl_ddr_compute(struct fsl_ddr_info_s *pinfo); +uint32_t compute_fsl_memctl_config_regs( + const struct memctl_options_s *popts, + struct fsl_ddr_cfg_regs_s *ddr, + const struct common_timing_params_s *common_dimm, + const struct dimm_params_s *dimm_parameters, + uint32_t dbw_capacity_adjust); +uint32_t compute_dimm_parameters( + const generic_spd_eeprom_t *spdin, + struct dimm_params_s *pdimm); +void compute_lowest_common_dimm_parameters( + const struct fsl_ddr_info_s *pinfo, + struct common_timing_params_s *outpdimm, + uint32_t number_of_dimms); +uint32_t populate_memctl_options( + int all_DIMMs_registered, + struct memctl_options_s *popts, + struct dimm_params_s *pdimm); +int fsl_ddr_set_lawbar( + const struct common_timing_params_s *memctl_common_params, + uint32_t memctl_interleaved); +int fsl_ddr_get_spd( + generic_spd_eeprom_t *ctrl_dimms_spd, + struct ddr_board_info_s *binfo); +int fsl_ddr_set_memctl_regs( + const struct fsl_ddr_info_s *info); +void fsl_ddr_board_options( + struct memctl_options_s *popts, + struct dimm_params_s *pdimm); +void fsl_ddr_board_info(struct ddr_board_info_s *info); +#endif diff --git a/arch/powerpc/ddr-8xxx/ddr2_dimm_params.c b/arch/powerpc/ddr-8xxx/ddr2_dimm_params.c new file mode 100644 index 0000000000..22c05ca6da --- /dev/null +++ b/arch/powerpc/ddr-8xxx/ddr2_dimm_params.c @@ -0,0 +1,296 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> +#include "ddr.h" +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Table comes from Byte 31 of JEDEC SPD Spec. + * + * DDR II + * Bit Size Size + * --- ----- + * 7 high 512MB + * 6 256MB + * 5 128MB + * 4 16GB + * 3 8GB + * 2 4GB + * 1 2GB + * 0 low 1GB + * + * Reorder Table to be linear by stripping the bottom + * 2 or 5 bits off and shifting them up to the top. + * + */ +static uint64_t compute_ranksize(uint32_t mem_type, unsigned char row_dens) +{ + uint64_t bsize; + + bsize = ((row_dens >> 5) | ((row_dens & 31) << 3)); + bsize <<= 27ULL; + + return bsize; +} + +/* + * Convert a two-nibble BCD value into a cycle time. + * While the spec calls for nano-seconds, picos are returned. + */ +static uint32_t convert_bcd_tenths_to_cycle_time_ps(uint32_t spd_val) +{ + uint32_t tenths_ps[16] = { + 0, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 250, + 330, + 660, + 750, + 0, + 0 + }; + uint32_t whole_ns = (spd_val & 0xF0) >> 4; + uint32_t tenth_ns = spd_val & 0x0F; + uint32_t ps = (whole_ns * 1000) + tenths_ps[tenth_ns]; + + return ps; +} + +static uint32_t convert_bcd_hundredths_to_cycle_time_ps(uint32_t spd_val) +{ + uint32_t tenth_ns = (spd_val & 0xF0) >> 4; + uint32_t hundredth_ns = spd_val & 0x0F; + uint32_t ps = (tenth_ns * 100) + (hundredth_ns * 10); + + return ps; +} + +static uint32_t byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, + 0 +}; + +static uint32_t +compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc) +{ + uint32_t trfc_ps; + + trfc_ps = (((trctrfc_ext & 0x1) * 256) + trfc) * 1000; + trfc_ps += byte40_table_ps[(trctrfc_ext >> 1) & 0x7]; + + return trfc_ps; +} + +static uint32_t +compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc) +{ + uint32_t trc_ps; + + trc_ps = (trc * 1000); + trc_ps += byte40_table_ps[(trctrfc_ext >> 4) & 0x7]; + + return trc_ps; +} + +/* + * Determine Refresh Rate. + * Table from SPD Spec, Byte 12, converted to picoseconds and + * filled in with "default" normal values. + */ +static uint32_t determine_refresh_rate_ps(const uint32_t spd_refresh) +{ + uint32_t refresh_time_ps[8] = { + 15625000, /* 0 Normal 1.00x */ + 3900000, /* 1 Reduced .25x */ + 7800000, /* 2 Extended .50x */ + 31300000, /* 3 Extended 2.00x */ + 62500000, /* 4 Extended 4.00x */ + 125000000, /* 5 Extended 8.00x */ + 15625000, /* 6 Normal 1.00x filler */ + 15625000, /* 7 Normal 1.00x filler */ + }; + + return refresh_time_ps[spd_refresh & 0x7]; +} + +/* + * The purpose of this function is to compute a suitable + * CAS latency given the DRAM clock period. The SPD only + * defines at most 3 CAS latencies. Typically the slower in + * frequency the DIMM runs at, the shorter its CAS latency can. + * be. If the DIMM is operating at a sufficiently low frequency, + * it may be able to run at a CAS latency shorter than the + * shortest SPD-defined CAS latency. + * + * If a CAS latency is not found, 0 is returned. + * + * Do this by finding in the standard speed table the longest + * tCKmin that doesn't exceed the value of mclk_ps (tCK). + * + * An assumption made is that the SDRAM device allows the + * CL to be programmed for a value that is lower than those + * advertised by the SPD. This is not always the case, + * as those modes not defined in the SPD are optional. + * + * CAS latency de-rating based upon values JEDEC Standard No. 79-2C + * Table 40, "DDR2 SDRAM standard speed bins and tCK, tRCD, tRP, tRAS, + * and tRC for corresponding bin" + * + * ordinal 2, ddr2_speed_bins[1] contains tCK for CL=3 + * Not certain if any good value exists for CL=2 + */ + /* CL2 CL3 CL4 CL5 CL6 CL7 */ +uint16_t ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500, 1875 }; + +uint32_t compute_derated_DDR2_CAS_latency(uint32_t mclk_ps) +{ + const uint32_t num_speed_bins = ARRAY_SIZE(ddr2_speed_bins); + uint32_t lowest_tCKmin_found = 0, lowest_tCKmin_CL = 0, i, x; + + for (i = 0; i < num_speed_bins; i++) { + x = ddr2_speed_bins[i]; + if (x && (x <= mclk_ps) && (x >= lowest_tCKmin_found)) { + lowest_tCKmin_found = x; + lowest_tCKmin_CL = i + 2; + } + } + + return lowest_tCKmin_CL; +} + +/* + * compute_dimm_parameters for DDR2 SPD + * + * Compute DIMM parameters based upon the SPD information in SPD. + * Writes the results to the dimm_params_s structure pointed by pdimm. + */ +uint32_t +compute_dimm_parameters(const generic_spd_eeprom_t *spdin, + struct dimm_params_s *pdimm) +{ + const struct ddr2_spd_eeprom *spd = spdin; + int retval; + + if (spd->mem_type != SPD_MEMTYPE_DDR2) + goto error; + + retval = ddr2_spd_check(spd); + if (retval) + goto error; + + /* + * The part name in ASCII in the SPD EEPROM is not null terminated. + * Guarantee null termination here by presetting all bytes to 0 + * and copying the part name in ASCII from the SPD onto it + */ + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = (spd->mod_ranks & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd->mem_type, spd->rank_dens); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = spd->dataw; + pdimm->primary_sdram_width = spd->primw; + pdimm->ec_sdram_width = spd->ecw; + + /* These are all the types defined by the JEDEC DDR2 SPD 1.3 spec */ + switch (spd->dimm_type) { + case DDR2_SPD_DIMMTYPE_RDIMM: + case DDR2_SPD_DIMMTYPE_72B_SO_RDIMM: + case DDR2_SPD_DIMMTYPE_MINI_RDIMM: + /* Registered/buffered DIMMs */ + pdimm->registered_dimm = 1; + break; + + case DDR2_SPD_DIMMTYPE_UDIMM: + case DDR2_SPD_DIMMTYPE_SO_DIMM: + case DDR2_SPD_DIMMTYPE_MICRO_DIMM: + case DDR2_SPD_DIMMTYPE_MINI_UDIMM: + /* Unbuffered DIMMs */ + pdimm->registered_dimm = 0; + break; + + case DDR2_SPD_DIMMTYPE_72B_SO_CDIMM: + default: + goto error; + } + + pdimm->n_row_addr = spd->nrow_addr; + pdimm->n_col_addr = spd->ncol_addr; + pdimm->n_banks_per_sdram_device = spd->nbanks; + pdimm->edc_config = spd->config; + pdimm->burst_lengths_bitmask = spd->burstl; + pdimm->row_density = spd->rank_dens; + + /* + * Calculate the Maximum Data Rate based on the Minimum Cycle time. + * The SPD clk_cycle field (tCKmin) is measured in tenths of + * nanoseconds and represented as BCD. + */ + pdimm->tCKmin_X_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle); + pdimm->tCKmin_X_minus_1_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2); + pdimm->tCKmin_X_minus_2_ps + = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3); + pdimm->tCKmax_ps = convert_bcd_tenths_to_cycle_time_ps(spd->tckmax); + + /* + * Compute CAS latencies defined by SPD + * The SPD caslat_X should have at least 1 and at most 3 bits set. + * + * If cas_lat after masking is 0, the __ilog2 function returns + * 255 into the variable. This behavior is abused once. + */ + pdimm->caslat_X = __ilog2(spd->cas_lat); + pdimm->caslat_X_minus_1 = __ilog2(spd->cas_lat + & ~(1 << pdimm->caslat_X)); + pdimm->caslat_X_minus_2 = __ilog2(spd->cas_lat & ~(1 << pdimm->caslat_X) + & ~(1 << pdimm->caslat_X_minus_1)); + pdimm->caslat_lowest_derated + = compute_derated_DDR2_CAS_latency(get_memory_clk_period_ps()); + pdimm->tRCD_ps = spd->trcd * 250; + pdimm->tRP_ps = spd->trp * 250; + pdimm->tRAS_ps = spd->tras * 1000; + pdimm->tWR_ps = spd->twr * 250; + pdimm->tWTR_ps = spd->twtr * 250; + pdimm->tRFC_ps = compute_trfc_ps_from_spd(spd->trctrfc_ext, spd->trfc); + pdimm->tRRD_ps = spd->trrd * 250; + pdimm->tRC_ps = compute_trc_ps_from_spd(spd->trctrfc_ext, spd->trc); + pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh); + pdimm->tIS_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup); + pdimm->tIH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold); + pdimm->tDS_ps + = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup); + pdimm->tDH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold); + pdimm->tRTP_ps = spd->trtp * 250; + pdimm->tDQSQ_max_ps = spd->tdqsq * 10; + pdimm->tQHS_ps = spd->tqhs * 10; + + return 0; +error: + return 1; +} diff --git a/arch/powerpc/ddr-8xxx/ddr3_dimm_params.c b/arch/powerpc/ddr-8xxx/ddr3_dimm_params.c new file mode 100644 index 0000000000..4f44925ab9 --- /dev/null +++ b/arch/powerpc/ddr-8xxx/ddr3_dimm_params.c @@ -0,0 +1,193 @@ +/* + * Copyright 2008-2012 Freescale Semiconductor, Inc. + * Dave Liu <daveliu@freescale.com> + * + * calculate the organization and timing parameter + * from ddr3 spd, please refer to the spec + * JEDEC standard No.21-C 4_01_02_11R18.pdf + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> +#include "ddr.h" + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * each rank size = + * sdram capacity(bit) / 8 * primary bus width / sdram width + * + * where: sdram capacity = spd byte4[3:0] + * primary bus width = spd byte8[2:0] + * sdram width = spd byte7[2:0] + * + * SPD byte4 - sdram density and banks + * bit[3:0] size(bit) size(byte) + * 0000 256Mb 32MB + * 0001 512Mb 64MB + * 0010 1Gb 128MB + * 0011 2Gb 256MB + * 0100 4Gb 512MB + * 0101 8Gb 1GB + * 0110 16Gb 2GB + * + * SPD byte8 - module memory bus width + * bit[2:0] primary bus width + * 000 8bits + * 001 16bits + * 010 32bits + * 011 64bits + * + * SPD byte7 - module organiztion + * bit[2:0] sdram device width + * 000 4bits + * 001 8bits + * 010 16bits + * 011 32bits + */ +static uint64_t compute_ranksize(const struct ddr3_spd_eeprom *spd) +{ + uint64_t bsize; + int sdram_cap_bsize = 0, prim_bus_width = 0, sdram_width = 0; + + if ((spd->density_banks & 0xf) < 7) + sdram_cap_bsize = (spd->density_banks & 0xf) + 28; + if ((spd->bus_width & 0x7) < 4) + prim_bus_width = (spd->bus_width & 0x7) + 3; + if ((spd->organization & 0x7) < 4) + sdram_width = (spd->organization & 0x7) + 2; + + bsize = 1ULL << (sdram_cap_bsize - 3 + prim_bus_width - sdram_width); + + return bsize; +} + +/* + * compute_dimm_parameters for DDR3 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_s structure pointed by pdimm. + * + */ +uint32_t +compute_dimm_parameters(const generic_spd_eeprom_t *spdin, + struct dimm_params_s *pdimm) +{ + const struct ddr3_spd_eeprom *spd = spdin; + uint32_t mtb_ps; + int retval, ftb_tmp; + + if (spd->mem_type != SPD_MEMTYPE_DDR3) + goto error; + + retval = ddr3_spd_check(spd); + if (retval) + goto error; + + memset(pdimm->mpart, 0, sizeof(pdimm->mpart)); + if ((spd->info_size_crc & 0xf) > 1) + memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1); + + /* DIMM organization parameters */ + pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1; + pdimm->rank_density = compute_ranksize(spd); + pdimm->capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = pdimm->primary_sdram_width + pdimm->ec_sdram_width; + pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7)); + if ((spd->bus_width >> 3) & 0x3) + pdimm->ec_sdram_width = 8; + else + pdimm->ec_sdram_width = 0; + pdimm->device_width = 1 << ((spd->organization & 0x7) + 2); + + /* These are the types defined by the JEDEC DDR3 SPD spec */ + pdimm->mirrored_dimm = 0; + pdimm->registered_dimm = 0; + switch (spd->module_type & DDR3_SPD_MODULETYPE_MASK) { + case DDR3_SPD_MODULETYPE_UDIMM: + /* Unbuffered DIMMs */ + if (spd->mod_section.unbuffered.addr_mapping & 0x1) + pdimm->mirrored_dimm = 1; + break; + + default: + goto error; + } + + /* SDRAM device parameters */ + pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12; + pdimm->n_col_addr = (spd->addressing & 0x7) + 9; + pdimm->n_banks_per_sdram_device = + 8 << ((spd->density_banks >> 4) & 0x7); + + /* + * The SPD spec does not define an ECC bit. The DIMM is considered + * to have ECC capability if the extension bus exists. + */ + if (pdimm->ec_sdram_width) + pdimm->edc_config = 0x02; + else + pdimm->edc_config = 0x00; + + /* + * The SPD spec does not define the burst length byte. + * but the DDR3 spec defines BL8 and BC4, on bit 3 and + * bit 2. + */ + pdimm->burst_lengths_bitmask = 0x0c; + pdimm->row_density = __ilog2(pdimm->rank_density); + + mtb_ps = (spd->mtb_dividend * 1000) / spd->mtb_divisor; + pdimm->mtb_ps = mtb_ps; + + ftb_tmp = spd->ftb_div & 0xf0; + pdimm->ftb_10th_ps = ((ftb_tmp >> 4) * 10) / ftb_tmp; + + pdimm->tCKmin_X_ps = spd->tck_min * mtb_ps + + (spd->fine_tck_min * ftb_tmp) / 10; + pdimm->caslat_X = ((spd->caslat_msb << 8) | spd->caslat_lsb) << 4; + + pdimm->taa_ps = spd->taa_min * mtb_ps + + (spd->fine_taa_min * ftb_tmp) / 10; + + pdimm->tRCD_ps = spd->trcd_min * mtb_ps + + (spd->fine_trcd_min * ftb_tmp) / 10; + + pdimm->tRP_ps = spd->trp_min * mtb_ps + + (spd->fine_trp_min * ftb_tmp) / 10; + pdimm->tRAS_ps = (((spd->tras_trc_ext & 0xf) << 8) | spd->tras_min_lsb) + * mtb_ps; + pdimm->tWR_ps = spd->twr_min * mtb_ps; + pdimm->tWTR_ps = spd->twtr_min * mtb_ps; + pdimm->tRFC_ps = ((spd->trfc_min_msb << 8) | spd->trfc_min_lsb) + * mtb_ps; + pdimm->tRRD_ps = spd->trrd_min * mtb_ps; + pdimm->tRC_ps = (((spd->tras_trc_ext & 0xf0) << 4) | spd->trc_min_lsb); + pdimm->tRC_ps *= mtb_ps; + pdimm->tRC_ps += (spd->fine_trc_min * ftb_tmp) / 10; + + pdimm->tRTP_ps = spd->trtp_min * mtb_ps; + + /* + * Average periodic refresh interval + * tREFI = 7.8 us at normal temperature range + * = 3.9 us at ext temperature range + */ + pdimm->refresh_rate_ps = 7800000; + if ((spd->therm_ref_opt & 0x1) && !(spd->therm_ref_opt & 0x2)) { + pdimm->refresh_rate_ps = 3900000; + pdimm->extended_op_srt = 1; + } + + pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min) + * mtb_ps; + + return 0; +error: + return 1; +} diff --git a/arch/powerpc/ddr-8xxx/ddr_setctrl.c b/arch/powerpc/ddr-8xxx/ddr_setctrl.c new file mode 100644 index 0000000000..115fb42070 --- /dev/null +++ b/arch/powerpc/ddr-8xxx/ddr_setctrl.c @@ -0,0 +1,90 @@ +/* + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <config.h> +#include <asm/io.h> +#include <asm/fsl_ddr_sdram.h> +#include <asm/processor.h> +#include <mach/early_udelay.h> +#include "ddr.h" + +int fsl_ddr_set_memctl_regs(const struct fsl_ddr_info_s *info) +{ + uint32_t i, temp_sdram_cfg; + void __iomem *ddr; + const struct fsl_ddr_cfg_regs_s *regs; + + regs = &info->fsl_ddr_config_reg; + ddr = info->board_info.ddr_base; + + if (in_be32(ddr + DDR_OFF(SDRAM_CFG)) & SDRAM_CFG_MEM_EN) + return 0; + + for (i = 0; i < info->board_info.cs_per_ctrl; i++) { + out_be32(ddr + DDR_OFF(CS0_BNDS) + (i << 3), regs->cs[i].bnds); + out_be32(ddr + DDR_OFF(CS0_CONFIG) + (i << 2), + regs->cs[i].config); + if (info->memctl_opts.sdram_type == SDRAM_TYPE_DDR3) + out_be32(ddr + DDR_OFF(CS0_CONFIG_2) + (i << 2), + regs->cs[i].config_2); + } + + out_be32(ddr + DDR_OFF(TIMING_CFG_3), regs->timing_cfg_3); + out_be32(ddr + DDR_OFF(TIMING_CFG_0), regs->timing_cfg_0); + out_be32(ddr + DDR_OFF(TIMING_CFG_1), regs->timing_cfg_1); + out_be32(ddr + DDR_OFF(TIMING_CFG_2), regs->timing_cfg_2); + out_be32(ddr + DDR_OFF(SDRAM_CFG_2), regs->ddr_sdram_cfg_2); + out_be32(ddr + DDR_OFF(SDRAM_MODE), regs->ddr_sdram_mode); + out_be32(ddr + DDR_OFF(SDRAM_MODE_2), regs->ddr_sdram_mode_2); + out_be32(ddr + DDR_OFF(SDRAM_MD_CNTL), regs->ddr_sdram_md_cntl); + out_be32(ddr + DDR_OFF(SDRAM_INTERVAL), regs->ddr_sdram_interval); + out_be32(ddr + DDR_OFF(SDRAM_DATA_INIT), regs->ddr_data_init); + out_be32(ddr + DDR_OFF(SDRAM_CLK_CNTL), regs->ddr_sdram_clk_cntl); + out_be32(ddr + DDR_OFF(SDRAM_INIT_ADDR), regs->ddr_init_addr); + out_be32(ddr + DDR_OFF(SDRAM_INIT_ADDR_EXT), regs->ddr_init_ext_addr); + + if (info->memctl_opts.sdram_type == SDRAM_TYPE_DDR3) { + out_be32(ddr + DDR_OFF(TIMING_CFG_4), regs->timing_cfg_4); + out_be32(ddr + DDR_OFF(TIMING_CFG_5), regs->timing_cfg_5); + out_be32(ddr + DDR_OFF(ZQ_CNTL), regs->ddr_zq_cntl); + out_be32(ddr + DDR_OFF(WRLVL_CNTL), regs->ddr_wrlvl_cntl); + + if (regs->ddr_wrlvl_cntl_2) + out_be32(ddr + DDR_OFF(WRLVL_CNTL_2), + regs->ddr_wrlvl_cntl_2); + if (regs->ddr_wrlvl_cntl_3) + out_be32(ddr + DDR_OFF(WRLVL_CNTL_3), + regs->ddr_wrlvl_cntl_3); + + out_be32(ddr + DDR_OFF(SR_CNTL), regs->ddr_sr_cntr); + out_be32(ddr + DDR_OFF(SDRAM_RCW_1), regs->ddr_sdram_rcw_1); + out_be32(ddr + DDR_OFF(SDRAM_RCW_2), regs->ddr_sdram_rcw_2); + out_be32(ddr + DDR_OFF(DDRCDR1), regs->ddr_cdr1); + out_be32(ddr + DDR_OFF(DDRCDR2), regs->ddr_cdr2); + } + + out_be32(ddr + DDR_OFF(ERR_DISABLE), regs->err_disable); + out_be32(ddr + DDR_OFF(ERR_INT_EN), regs->err_int_en); + + temp_sdram_cfg = regs->ddr_sdram_cfg; + temp_sdram_cfg &= ~(SDRAM_CFG_MEM_EN); + out_be32(ddr + DDR_OFF(SDRAM_CFG), temp_sdram_cfg); + + early_udelay(500); + /* Make sure all instructions are completed before enabling memory.*/ + asm volatile("sync;isync"); + temp_sdram_cfg = in_be32(ddr + DDR_OFF(SDRAM_CFG)) & ~SDRAM_CFG_BI; + out_be32(ddr + DDR_OFF(SDRAM_CFG), temp_sdram_cfg | SDRAM_CFG_MEM_EN); + asm volatile("sync;isync"); + + while (in_be32(ddr + DDR_OFF(SDRAM_CFG_2)) & SDRAM_CFG2_D_INIT) + early_udelay(10000); + + return 0; +} diff --git a/arch/powerpc/ddr-8xxx/lc_common_dimm_params.c b/arch/powerpc/ddr-8xxx/lc_common_dimm_params.c new file mode 100644 index 0000000000..9d90fb76db --- /dev/null +++ b/arch/powerpc/ddr-8xxx/lc_common_dimm_params.c @@ -0,0 +1,255 @@ +/* + * Copyright 2008-2012 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <config.h> +#include <asm/fsl_ddr_sdram.h> + +#include "ddr.h" + +static uint32_t +compute_cas_latency_ddr3(const struct dimm_params_s *dimm_params, + uint32_t number_of_dimms) +{ + uint32_t i, taamin_ps = 0, tckmin_x_ps = 0, common_caslat, + caslat_actual, retry = 16; + const uint32_t mclk_ps = get_memory_clk_period_ps(); + + /* compute the common CAS latency supported between slots */ + common_caslat = dimm_params[0].caslat_X; + for (i = 1; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) + common_caslat &= dimm_params[i].caslat_X; + } + + for (i = 0; i < number_of_dimms; i++) { + taamin_ps = max(taamin_ps, dimm_params[i].taa_ps); + tckmin_x_ps = max(tckmin_x_ps, dimm_params[i].tCKmin_X_ps); + } + + caslat_actual = (taamin_ps + mclk_ps - 1) / mclk_ps; + /* check if the dimms support the CAS latency */ + while (!(common_caslat & (1 << caslat_actual)) && retry > 0) { + caslat_actual++; + retry--; + } + + return caslat_actual; +} + +static unsigned int common_burst_length( + const struct dimm_params_s *dimm_params, + const unsigned int number_of_dimms) +{ + unsigned int i, temp; + + temp = 0xff; + for (i = 0; i < number_of_dimms; i++) + if (dimm_params[i].n_ranks) + temp &= dimm_params[i].burst_lengths_bitmask; + return temp; +} + +/* Compute a CAS latency suitable for all DIMMs */ +static unsigned int compute_lowest_caslat( + const struct dimm_params_s *dimm_params, + const unsigned int number_of_dimms) +{ + uint32_t temp1, temp2, i, not_ok, lowest_good_caslat, + tCKmin_X_minus_1_ps, tCKmin_X_minus_2_ps; + const unsigned int mclk_ps = get_memory_clk_period_ps(); + + /* + * Step 1: find CAS latency common to all DIMMs using bitwise + * operation. + */ + temp1 = 0xFF; + for (i = 0; i < number_of_dimms; i++) + if (dimm_params[i].n_ranks) { + temp2 = 0; + temp2 |= 1 << dimm_params[i].caslat_X; + temp2 |= 1 << dimm_params[i].caslat_X_minus_1; + temp2 |= 1 << dimm_params[i].caslat_X_minus_2; + /* + * FIXME: If there was no entry for X-2 (X-1) in + * the SPD, then caslat_X_minus_2 + * (caslat_X_minus_1) contains either 255 or + * 0xFFFFFFFF because that's what the __ilog2 + * function returns for an input of 0. + * On 32-bit PowerPC, left shift counts with bit + * 26 set (that the value of 255 or 0xFFFFFFFF + * will have), cause the destination register to + * be 0. That is why this works. + */ + temp1 &= temp2; + } + + /* + * Step 2: check each common CAS latency against tCK of each + * DIMM's SPD. + */ + lowest_good_caslat = 0; + temp2 = 0; + while (temp1) { + not_ok = 0; + temp2 = __ilog2(temp1); + + for (i = 0; i < number_of_dimms; i++) { + if (!dimm_params[i].n_ranks) + continue; + + if (dimm_params[i].caslat_X == temp2) { + if (mclk_ps >= dimm_params[i].tCKmin_X_ps) + continue; + else + not_ok++; + } + + if (dimm_params[i].caslat_X_minus_1 == temp2) { + tCKmin_X_minus_1_ps = + dimm_params[i].tCKmin_X_minus_1_ps; + if (mclk_ps >= tCKmin_X_minus_1_ps) + continue; + else + not_ok++; + } + + if (dimm_params[i].caslat_X_minus_2 == temp2) { + tCKmin_X_minus_2_ps + = dimm_params[i].tCKmin_X_minus_2_ps; + if (mclk_ps >= tCKmin_X_minus_2_ps) + continue; + else + not_ok++; + } + } + + if (!not_ok) + lowest_good_caslat = temp2; + + temp1 &= ~(1 << temp2); + } + return lowest_good_caslat; +} + +/* + * compute_lowest_common_dimm_parameters() + * + * Determine the worst-case DIMM timing parameters from the set of DIMMs + * whose parameters have been computed into the array pointed to + * by dimm_params. + */ +void compute_lowest_common_dimm_parameters(const struct fsl_ddr_info_s *pinfo, + struct common_timing_params_s *out, + const unsigned int number_of_dimms) +{ + uint32_t temp1, i; + struct common_timing_params_s tmp = {0}; + const struct dimm_params_s *dimm = pinfo->dimm_params; + const struct memctl_options_s *popts = &pinfo->memctl_opts; + + tmp.tCKmax_ps = 0xFFFFFFFF; + tmp.extended_op_srt = 1; + temp1 = 0; + for (i = 0; i < number_of_dimms; i++) { + if (dimm[i].n_ranks == 0) { + temp1++; + continue; + } + + /* + * Find minimum tCKmax_ps to find fastest slow speed, + * i.e., this is the slowest the whole system can go. + */ + tmp.tCKmax_ps = min(tmp.tCKmax_ps, dimm[i].tCKmax_ps); + + /* Find maximum value to determine slowest speed, delay, etc */ + tmp.tCKmin_X_ps = max(tmp.tCKmin_X_ps, dimm[i].tCKmin_X_ps); + tmp.tCKmax_max_ps = max(tmp.tCKmax_max_ps, dimm[i].tCKmax_ps); + tmp.tRCD_ps = max(tmp.tRCD_ps, dimm[i].tRCD_ps); + tmp.tRP_ps = max(tmp.tRP_ps, dimm[i].tRP_ps); + tmp.tRAS_ps = max(tmp.tRAS_ps, dimm[i].tRAS_ps); + tmp.tWR_ps = max(tmp.tWR_ps, dimm[i].tWR_ps); + tmp.tWTR_ps = max(tmp.tWTR_ps, dimm[i].tWTR_ps); + tmp.tRFC_ps = max(tmp.tRFC_ps, dimm[i].tRFC_ps); + tmp.tRRD_ps = max(tmp.tRRD_ps, dimm[i].tRRD_ps); + tmp.tRC_ps = max(tmp.tRC_ps, dimm[i].tRC_ps); + tmp.tIS_ps = max(tmp.tIS_ps, dimm[i].tIS_ps); + tmp.tIH_ps = max(tmp.tIH_ps, dimm[i].tIH_ps); + tmp.tDS_ps = max(tmp.tDS_ps, dimm[i].tDS_ps); + tmp.tDH_ps = max(tmp.tDH_ps, dimm[i].tDH_ps); + tmp.tRTP_ps = max(tmp.tRTP_ps, dimm[i].tRTP_ps); + tmp.tQHS_ps = max(tmp.tQHS_ps, dimm[i].tQHS_ps); + tmp.refresh_rate_ps = max(tmp.refresh_rate_ps, + dimm[i].refresh_rate_ps); + tmp.extended_op_srt = min(tmp.extended_op_srt, + dimm[i].extended_op_srt); + /* Find maximum tDQSQ_max_ps to find slowest timing. */ + tmp.tDQSQ_max_ps = max(tmp.tDQSQ_max_ps, dimm[i].tDQSQ_max_ps); + } + tmp.ndimms_present = number_of_dimms - temp1; + + if (temp1 == number_of_dimms) + return; + + temp1 = common_burst_length(dimm, number_of_dimms); + tmp.all_DIMMs_burst_lengths_bitmask = temp1; + + /* Support only unbuffered DIMMs */ + tmp.all_DIMMs_registered = 0; + tmp.all_DIMMs_unbuffered = 1; + + if (popts->sdram_type == SDRAM_TYPE_DDR3) { + tmp.lowest_common_SPD_caslat = compute_cas_latency_ddr3(dimm, + number_of_dimms); + } else { + tmp.lowest_common_SPD_caslat = compute_lowest_caslat(dimm, + number_of_dimms); + /* + * Compute a common 'de-rated' CAS latency. + * + * The strategy here is to find the *highest* de-rated cas + * latency with the assumption that all of the DIMMs will + * support a de-rated CAS latency higher than or equal to + * their lowest de-rated value. + */ + temp1 = 0; + for (i = 0; i < number_of_dimms; i++) + temp1 = max(temp1, dimm[i].caslat_lowest_derated); + tmp.highest_common_derated_caslat = temp1; + } + + temp1 = 1; + for (i = 0; i < number_of_dimms; i++) + if (dimm[i].n_ranks && !(dimm[i].edc_config & EDC_ECC)) { + temp1 = 0; + break; + } + tmp.all_DIMMs_ECC_capable = temp1; + + /* + * AL must be less or equal to tRCD. Typically, AL would + * be AL = tRCD - 1; + * + * When ODT read or write is enabled the sum of CAS latency + + * additive latency must be at least 3 cycles. + */ + tmp.additive_latency = 0; + if (popts->sdram_type == SDRAM_TYPE_DDR2) { + if ((tmp.lowest_common_SPD_caslat < 4) && + (picos_to_mclk(tmp.tRCD_ps) > + tmp.lowest_common_SPD_caslat)) + tmp.additive_latency = picos_to_mclk(tmp.tRCD_ps) - + tmp.lowest_common_SPD_caslat; + + if (mclk_to_picos(tmp.additive_latency) > tmp.tRCD_ps) + tmp.additive_latency = picos_to_mclk(tmp.tRCD_ps); + } + + memcpy(out, &tmp, sizeof(struct common_timing_params_s)); +} diff --git a/arch/powerpc/ddr-8xxx/main.c b/arch/powerpc/ddr-8xxx/main.c new file mode 100644 index 0000000000..99b877b5ca --- /dev/null +++ b/arch/powerpc/ddr-8xxx/main.c @@ -0,0 +1,246 @@ +/* + * Copyright 2008-2012 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +/* + * Generic driver for Freescale DDR2 memory controller. + * Based on code from spd_sdram.c + * Author: James Yang [at freescale.com] + */ + +#include <common.h> +#include <config.h> +#include <asm/fsl_law.h> +#include <asm/fsl_ddr_sdram.h> +#include "ddr.h" + +static int get_spd(generic_spd_eeprom_t *spd, + struct ddr_board_info_s *bi, u8 i2c_address) +{ + int ret; + + fsl_i2c_init(bi->i2c_base, bi->i2c_speed, bi->i2c_slave); + ret = fsl_i2c_read(bi->i2c_base, i2c_address, 0, 1, (uchar *) spd, + sizeof(generic_spd_eeprom_t)); + fsl_i2c_stop(bi->i2c_base); + + return ret; +} + +int fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd, + struct ddr_board_info_s *binfo) +{ + uint32_t i; + uint8_t i2c_address; + + for (i = 0; i < binfo->dimm_slots_per_ctrl; i++) { + if (binfo->spd_i2c_addr == NULL) + goto error; + i2c_address = binfo->spd_i2c_addr[i]; + if (get_spd(&(ctrl_dimms_spd[i]), binfo, i2c_address)) + goto error; + } + + return 0; +error: + return 1; +} + +static uint64_t step_assign_addresses(struct fsl_ddr_info_s *pinfo, + uint32_t dbw_cap_adj) +{ + uint64_t total_mem, current_mem_base, total_ctlr_mem, cap; + uint32_t ndimm, dw, j, found = 0; + + ndimm = pinfo->board_info.dimm_slots_per_ctrl; + /* + * If a reduced data width is requested, but the SPD + * specifies a physically wider device, adjust the + * computed dimm capacities accordingly before + * assigning addresses. + */ + switch (pinfo->memctl_opts.data_bus_width) { + case 2: + /* 16-bit */ + for (j = 0; j < ndimm; j++) { + if (!pinfo->dimm_params[j].n_ranks) + continue; + dw = pinfo->dimm_params[j].primary_sdram_width; + if ((dw == 72 || dw == 64)) { + dbw_cap_adj = 2; + break; + } else if ((dw == 40 || dw == 32)) { + dbw_cap_adj = 1; + break; + } + } + break; + case 1: + /* 32-bit */ + for (j = 0; j < ndimm; j++) { + dw = pinfo->dimm_params[j].data_width; + if (pinfo->dimm_params[j].n_ranks + && (dw == 72 || dw == 64)) { + /* + * FIXME: can't really do it + * like this because this just + * further reduces the memory + */ + found = 1; + break; + } + } + if (found) + dbw_cap_adj = 1; + break; + case 0: + /* 64-bit */ + break; + default: + return 1; + } + + current_mem_base = 0ull; + total_mem = 0; + total_ctlr_mem = 0; + pinfo->common_timing_params.base_address = current_mem_base; + + for (j = 0; j < ndimm; j++) { + cap = pinfo->dimm_params[j].capacity >> dbw_cap_adj; + pinfo->dimm_params[j].base_address = current_mem_base; + current_mem_base += cap; + total_ctlr_mem += cap; + + } + + pinfo->common_timing_params.total_mem = total_ctlr_mem; + total_mem += total_ctlr_mem; + + return total_mem; +} + +static uint32_t retrieve_max_size(struct fsl_ddr_cfg_regs_s *ddr_reg, + uint32_t ncs) +{ + uint32_t max_end = 0, end, i; + + for (i = 0; i < ncs; i++) + if (ddr_reg->cs[i].config & 0x80000000) { + end = ddr_reg->cs[i].bnds & 0xFFF; + if (end > max_end) + max_end = end; + } + + return max_end; +} + +static uint32_t compute_dimm_param(struct fsl_ddr_info_s *pinfo, uint32_t ndimm) +{ + struct dimm_params_s *pdimm; + generic_spd_eeprom_t *spd; + uint32_t i, retval; + + spd = &(pinfo->spd_installed_dimms[0]); + if (spd->mem_type == SPD_MEMTYPE_DDR3) + pinfo->memctl_opts.sdram_type = SDRAM_TYPE_DDR3; + else if (spd->mem_type == SPD_MEMTYPE_DDR2) + pinfo->memctl_opts.sdram_type = SDRAM_TYPE_DDR2; + else + return 1; + + for (i = 0; i < ndimm; i++) { + spd = &(pinfo->spd_installed_dimms[i]); + pdimm = &(pinfo->dimm_params[i]); + + retval = compute_dimm_parameters(spd, pdimm); + + if (retval == 2) /* Fatal error */ + return 1; + } + + return 0; +} + +uint64_t fsl_ddr_compute(struct fsl_ddr_info_s *pinfo) +{ + uint64_t total_mem = 0; + uint32_t ncs, ndimm, max_end = 0; + struct fsl_ddr_cfg_regs_s *ddr_reg; + struct common_timing_params_s *timing_params; + /* data bus width capacity adjust shift amount */ + uint32_t dbw_capacity_adjust; + + ddr_reg = &pinfo->fsl_ddr_config_reg; + timing_params = &pinfo->common_timing_params; + ncs = pinfo->board_info.cs_per_ctrl; + ndimm = pinfo->board_info.dimm_slots_per_ctrl; + dbw_capacity_adjust = 0; + pinfo->memctl_opts.board_info = &pinfo->board_info; + + /* STEP 1: Gather all DIMM SPD data */ + if (fsl_ddr_get_spd(pinfo->spd_installed_dimms, + pinfo->memctl_opts.board_info)) + return 0; + + /* STEP 2: Compute DIMM parameters from SPD data */ + if (compute_dimm_param(pinfo, ndimm)) + return 0; + + /* + * STEP 3: Compute a common set of timing parameters + * suitable for all of the DIMMs on each memory controller + */ + compute_lowest_common_dimm_parameters(pinfo, timing_params, ndimm); + + /* STEP 4: Gather configuration requirements from user */ + populate_memctl_options(timing_params->all_DIMMs_registered, + &pinfo->memctl_opts, + pinfo->dimm_params); + + /* STEP 5: Assign addresses to chip selects */ + total_mem = step_assign_addresses(pinfo, dbw_capacity_adjust); + + /* STEP 6: compute controller register values */ + if (timing_params->ndimms_present == 0) + memset(ddr_reg, 0, sizeof(struct fsl_ddr_cfg_regs_s)); + + compute_fsl_memctl_config_regs(&pinfo->memctl_opts, + ddr_reg, timing_params, + pinfo->dimm_params, + dbw_capacity_adjust); + + max_end = retrieve_max_size(ddr_reg, ncs); + total_mem = 1 + (((uint64_t)max_end << 24ULL) | 0xFFFFFFULL); + + return total_mem; +} + +/* + * fsl_ddr_sdram(): + * This is the main function to initialize the memory. + * + * It returns amount of memory configured in bytes. + */ +phys_size_t fsl_ddr_sdram(void) +{ + uint64_t total_memory; + struct fsl_ddr_info_s info; + + memset(&info, 0, sizeof(struct fsl_ddr_info_s)); + /* Gather board information on the memory controller and I2C bus. */ + fsl_ddr_board_info(&info.board_info); + + total_memory = fsl_ddr_compute(&info); + + if (info.common_timing_params.ndimms_present == 0) + return 0; + + fsl_ddr_set_memctl_regs(&info); + fsl_ddr_set_lawbar(&info.common_timing_params, LAW_TRGT_IF_DDR_1); + + return total_memory; +} diff --git a/arch/powerpc/ddr-8xxx/options.c b/arch/powerpc/ddr-8xxx/options.c new file mode 100644 index 0000000000..ccf5d5e9d8 --- /dev/null +++ b/arch/powerpc/ddr-8xxx/options.c @@ -0,0 +1,147 @@ +/* + * Copyright 2008, 2010-2012 Freescale Semiconductor, Inc. + * + * 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. + */ + +#include <common.h> +#include <asm/fsl_ddr_sdram.h> +#include "ddr.h" + +uint32_t populate_memctl_options(int all_DIMMs_registered, + struct memctl_options_s *popts, + struct dimm_params_s *pdimm) +{ + const struct ddr_board_info_s *binfo = popts->board_info; + uint32_t i; + + for (i = 0; i < binfo->cs_per_ctrl; i++) { + popts->cs_local_opts[i].odt_rd_cfg = FSL_DDR_ODT_NEVER; + popts->cs_local_opts[i].odt_wr_cfg = FSL_DDR_ODT_ALL; + popts->cs_local_opts[i].odt_rtt_norm = DDR2_RTT_50_OHM; + popts->cs_local_opts[i].odt_rtt_wr = DDR2_RTT_OFF; + popts->cs_local_opts[i].auto_precharge = 0; + } + + /* Memory Organization Parameters */ + popts->registered_dimm_en = all_DIMMs_registered; + popts->ECC_mode = 0; /* 0 = disabled, 1 = enabled */ + popts->ECC_init_using_memctl = 1; /* 0 = use DMA, 1 = use memctl */ + + /* Choose DQS config - 1 for DDR2 */ + popts->DQS_config = 1; + + /* Choose self-refresh during sleep. */ + popts->self_refresh_in_sleep = 1; + + /* Choose dynamic power management mode. */ + popts->dynamic_power = 0; + + /* + * check first dimm for primary sdram width + * assuming all dimms are similar + * 0 = 64-bit, 1 = 32-bit, 2 = 16-bit + */ + if (pdimm->n_ranks != 0) { + if (popts->sdram_type == SDRAM_TYPE_DDR3) { + if (pdimm[0].primary_sdram_width == 64) + popts->data_bus_width = 0; + else if (pdimm[0].primary_sdram_width == 32) + popts->data_bus_width = 1; + else if (pdimm[0].primary_sdram_width == 16) + popts->data_bus_width = 2; + else + hang(); + } else { + if ((pdimm->data_width >= 64) && + (pdimm->data_width <= 72)) + popts->data_bus_width = 0; + else if ((pdimm->data_width >= 32) && + (pdimm->data_width <= 40)) + popts->data_bus_width = 1; + else + hang(); + } + } + + if (popts->sdram_type == SDRAM_TYPE_DDR3) { + if (popts->data_bus_width == 0) { + popts->otf_burst_chop_en = 1; + popts->burst_length = DDR_OTF; + } else { + /* 32-bit or 16-bit bus */ + popts->otf_burst_chop_en = 0; + popts->burst_length = DDR_BL8; + } + popts->mirrored_dimm = pdimm[0].mirrored_dimm; + } else { + /* Must be a burst length of 4 for DDR2 */ + popts->burst_length = DDR_BL4; + } + + /* Decide whether to use the computed de-rated latency */ + popts->use_derated_caslat = 0; + + /* + * 2T_EN setting + * + * Factors to consider for 2T_EN: + * - number of DIMMs installed + * - number of components, number of active ranks + * - how much time you want to spend playing around + */ + popts->twoT_en = 0; + popts->threet_en = 0; + + /* + * Default BSTTOPRE precharge interval + * + * Set the parameter to 0 for global auto precharge in + * the board options function. + */ + popts->bstopre = 0x100; + + /* Minimum CKE pulse width -- tCKE(MIN) */ + popts->tCKE_clock_pulse_width_ps + = mclk_to_picos(FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR); + + /* + * Window for four activates -- tFAW + * + * Set according to specification for the memory used. + * The default value below would work for x4/x8 wide memory. + * + */ + if (popts->sdram_type == SDRAM_TYPE_DDR2) { + popts->tFAW_window_four_activates_ps = 37500; + } else { + /* + * Due to ddr3 dimm fly-by topology, enable write leveling + * to meet the tQDSS under different loading. + */ + popts->tFAW_window_four_activates_ps = pdimm[0].tfaw_ps; + popts->wrlvl_en = 1; + popts->zq_en = 1; + popts->wrlvl_override = 0; + } + + /* + * Default powerdown exit timings. + * Set according to specifications for the memory used in + * the board options function. + */ + popts->txard = 3; + popts->txp = 3; + popts->taxpd = 11; + + /* Default value for load mode cycle time */ + popts->tmrd = 2; + + /* Specific board override parameters. */ + fsl_ddr_board_options(popts, pdimm); + + return 0; +} diff --git a/arch/powerpc/ddr-8xxx/util.c b/arch/powerpc/ddr-8xxx/util.c new file mode 100644 index 0000000000..626b5f3f9b --- /dev/null +++ b/arch/powerpc/ddr-8xxx/util.c @@ -0,0 +1,100 @@ +/* + * Copyright 2008-2012 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/fsl_law.h> +#include <asm-generic/div64.h> +#include <mach/clock.h> +#include "ddr.h" + +#define ULL_2E12 2000000000000ULL +#define UL_5POW12 244140625UL +#define UL_2POW13 (1UL << 13) +#define ULL_8FS 0xFFFFFFFFULL + +/* + * Round up mclk_ps to nearest 1 ps in memory controller code + * if the error is 0.5ps or more. + * + * If an imprecise data rate is too high due to rounding error + * propagation, compute a suitably rounded mclk_ps to compute + * a working memory controller configuration. + */ +uint32_t get_memory_clk_period_ps(void) +{ + uint32_t result, data_rate = fsl_get_ddr_freq(0); + /* Round to nearest 10ps, being careful about 64-bit multiply/divide */ + uint64_t rem, mclk_ps = ULL_2E12; + + /* Now perform the big divide, the result fits in 32-bits */ + rem = do_div(mclk_ps, data_rate); + if (rem >= (data_rate >> 1)) + result = mclk_ps + 1; + else + result = mclk_ps; + + return result; +} + +/* Convert picoseconds into DRAM clock cycles (rounding up if needed). */ +uint32_t picos_to_mclk(uint32_t picos) +{ + uint64_t clks, clks_rem; + uint32_t data_rate = fsl_get_ddr_freq(0); + + if (!picos) + return 0; + + /* First multiply the time by the data rate (32x32 => 64) */ + clks = picos * (uint64_t)data_rate; + /* + * Now divide by 5^12 and track the 32-bit remainder, then divide + * by 2*(2^12) using shifts (and updating the remainder). + */ + clks_rem = do_div(clks, UL_5POW12); + clks_rem += (clks & (UL_2POW13 - 1)) * UL_5POW12; + clks >>= 13; + + /* If we had a remainder greater than the 1ps error, then round up */ + if (clks_rem > data_rate) + clks++; + + if (clks > ULL_8FS) + clks = ULL_8FS; + + return (uint32_t)clks; +} + +uint32_t mclk_to_picos(unsigned int mclk) +{ + return get_memory_clk_period_ps() * mclk; +} + +int fsl_ddr_set_lawbar( + const struct common_timing_params_s *params, + uint32_t law_memctl) +{ + uint64_t base = params->base_address; + uint64_t size = params->total_mem; + + if (!params->ndimms_present) + goto out; + + if (base >= MAX_MEM_MAPPED) + goto error; + + if ((base + size) >= MAX_MEM_MAPPED) + size = MAX_MEM_MAPPED - base; + + if (fsl_set_ddr_laws(base, size, law_memctl) < 0) + goto error; +out: + return 0; +error: + return 1; +} |