From a3f513ecb201ac0000a81e5d337dbf4d22b59080 Mon Sep 17 00:00:00 2001 From: James Yang Date: Mon, 9 Jun 2008 10:35:49 -0500 Subject: [RFC][FSL DDR 2/8] Rewrite the FSL mpc8xxx DDR controller setup code. --- cpu/mpc85xx/Makefile | 9 + cpu/mpc86xx/Makefile | 4 + cpu/mpc8xxx/Makefile | 30 + cpu/mpc8xxx/common_timing_params.h | 54 + cpu/mpc8xxx/ddr2_dimm_params.h | 99 ++ cpu/mpc8xxx/fsl_ddr1.c | 365 ++++++ cpu/mpc8xxx/fsl_ddr2.c | 384 ++++++ cpu/mpc8xxx/fsl_ddr_sdram.c | 2448 ++++++++++++++++++++++++++++++++++++ cpu/mpc8xxx/fsl_ddr_sdram.h | 87 ++ cpu/mpc8xxx/fsl_memctrl.h | 48 + cpu/mpc8xxx/memctl_options.h | 100 ++ 11 files changed, 3628 insertions(+), 0 deletions(-) create mode 100644 cpu/mpc8xxx/Makefile create mode 100644 cpu/mpc8xxx/common_timing_params.h create mode 100644 cpu/mpc8xxx/ddr2_dimm_params.h create mode 100644 cpu/mpc8xxx/fsl_ddr1.c create mode 100644 cpu/mpc8xxx/fsl_ddr2.c create mode 100644 cpu/mpc8xxx/fsl_ddr_sdram.c create mode 100644 cpu/mpc8xxx/fsl_ddr_sdram.h create mode 100644 cpu/mpc8xxx/fsl_memctrl.h create mode 100644 cpu/mpc8xxx/memctl_options.h diff --git a/cpu/mpc85xx/Makefile b/cpu/mpc85xx/Makefile index adbc585..c180dbd 100644 --- a/cpu/mpc85xx/Makefile +++ b/cpu/mpc85xx/Makefile @@ -33,6 +33,15 @@ SOBJS-$(CONFIG_MP) += release.o SOBJS = $(SOBJS-y) COBJS-$(CONFIG_MP) += mp.o COBJS-$(CONFIG_OF_LIBFDT) += fdt.o + +ifneq ($(CONFIG_FSL_DDR3),y) +ifneq ($(CONFIG_FSL_DDR2),y) +ifneq ($(CONFIG_FSL_DDR1),y) +COBJS-y += spd_sdram.o +endif +endif +endif + COBJS = traps.o cpu.o cpu_init.o speed.o interrupts.o tlb.o \ pci.o serial_scc.o commproc.o ether_fcc.o spd_sdram.o qe_io.o \ $(COBJS-y) diff --git a/cpu/mpc86xx/Makefile b/cpu/mpc86xx/Makefile index 537f62a..4227053 100644 --- a/cpu/mpc86xx/Makefile +++ b/cpu/mpc86xx/Makefile @@ -40,6 +40,10 @@ COBJS-y += spd_sdram.o COBJS-$(CONFIG_OF_LIBFDT) += fdt.o +ifneq ($(CONFIG_FSL_DDR2),y) +COBJS-y += spd_sdram.o +endif + SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS-y)) START := $(addprefix $(obj),$(START)) diff --git a/cpu/mpc8xxx/Makefile b/cpu/mpc8xxx/Makefile new file mode 100644 index 0000000..30f81ff --- /dev/null +++ b/cpu/mpc8xxx/Makefile @@ -0,0 +1,30 @@ +# +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)libmpc8xxx.a + +COBJS-$(CONFIG_FSL_DDR1) += fsl_ddr_sdram.o +COBJS-$(CONFIG_FSL_DDR1) += fsl_ddr1.o + +COBJS-$(CONFIG_FSL_DDR2) += fsl_ddr_sdram.o +COBJS-$(CONFIG_FSL_DDR2) += fsl_ddr2.o + + +SRCS := $(START:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) + +all: $(obj).depend $(LIB) + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend diff --git a/cpu/mpc8xxx/common_timing_params.h b/cpu/mpc8xxx/common_timing_params.h new file mode 100644 index 0000000..6201e78 --- /dev/null +++ b/cpu/mpc8xxx/common_timing_params.h @@ -0,0 +1,54 @@ +/* + * 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 + +#include "fsl_ddr_sdram.h" + +typedef struct { + /* parameters to constrict */ + + unsigned int tCKmin_X_ps; + unsigned int tCKmax_ps; + unsigned int tCKmax_max_ps; + unsigned int tRCD_ps; + unsigned int tRP_ps; + unsigned int tRAS_ps; + + unsigned int tWR_ps; /* maximum = 63750 ps */ + unsigned int tWTR_ps; /* maximum = 63750 ps */ + unsigned int tRFC_ps; /* maximum = 255 ns + 256 ns + .75 ns = 511750 ps */ + + unsigned int tRRD_ps; /* maximum = 63750 ps */ + unsigned int tRC_ps; /* maximum = 254 ns + .75 ns = 254750 ps */ + + unsigned int refresh_rate_ns; + + unsigned int tIS_ps; /* byte 32, spd->ca_setup */ + unsigned int tIH_ps; /* byte 33, spd->ca_hold */ + unsigned int tDS_ps; /* byte 34, spd->data_setup */ + unsigned int tDH_ps; /* byte 35, spd->data_hold */ + unsigned int tRTP_ps; /* byte 38, spd->trtp */ + unsigned int tDQSQ_max_ps; /* byte 44, spd->tdqsq */ + unsigned int tQHS_ps; /* byte 45, spd->tqhs */ + + unsigned int ndimms_present; + unsigned int lowest_common_SPD_caslat; + unsigned int highest_common_derated_caslat; + unsigned int additive_latency; + unsigned int all_DIMMs_burst_lengths_bitmask; + unsigned int all_DIMMs_registered; + unsigned int all_DIMMs_unbuffered; + unsigned int all_DIMMs_ECC_capable; + + unsigned long long total_mem; + unsigned long long base_address; +} common_timing_params_t; + +#endif diff --git a/cpu/mpc8xxx/ddr2_dimm_params.h b/cpu/mpc8xxx/ddr2_dimm_params.h new file mode 100644 index 0000000..478dc85 --- /dev/null +++ b/cpu/mpc8xxx/ddr2_dimm_params.h @@ -0,0 +1,99 @@ +/* + * 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 DDR2_DIMM_PARAMS_H +#define DDR2_DIMM_PARAMS_H + +#include "fsl_ddr_sdram.h" + + +/* + * Parameters for a DDR2 dimm computed from the SPD + */ +typedef struct dimm_params_s { + + /* + * DIMM organization parameters + */ + char mpart[19]; /* guaranteed null terminated */ + + unsigned int n_ranks; + unsigned long long rank_density; + unsigned long long dimm_capacity; + unsigned int data_width; + unsigned int primary_sdram_width; + unsigned int ec_sdram_width; + unsigned int registered_dimm; + + /* + * SDRAM device parameters + */ + unsigned int n_row_addr; + unsigned int n_col_addr; + unsigned int edc_config; /* 0 = none, 1 = parity, 2 = ECC */ + unsigned int n_banks_per_sdram_device; + unsigned int burst_lengths_bitmask; /* BL=4 bit 2, BL=8 = bit 3 */ + unsigned int row_density; + + /* used in computing base address of DIMMs */ + unsigned long long base_address; + + /* + * DIMM timing parameters + */ + + /* + * SDRAM clock periods + * The range for these are 1000-10000 so a short should be sufficient + */ + unsigned int tCKmin_X_ps; + unsigned int tCKmin_X_minus_1_ps; + unsigned int tCKmin_X_minus_2_ps; + unsigned int tCKmax_ps; + + /* + * SPD-defined CAS latencies + */ + unsigned int caslat_X; + unsigned int caslat_X_minus_1; + unsigned int caslat_X_minus_2; + + unsigned int caslat_lowest_derated; /* Derated CAS latency */ + + /* basic timing parameters */ + unsigned int tRCD_ps; + unsigned int tRP_ps; + unsigned int tRAS_ps; + + unsigned int tWR_ps; /* maximum = 63750 ps */ + unsigned int tWTR_ps; /* maximum = 63750 ps */ + unsigned int tRFC_ps; /* max = 255 ns + 256 ns + .75 ns = 511750 ps */ + + unsigned int tRRD_ps; /* maximum = 63750 ps */ + unsigned int tRC_ps; /* maximum = 254 ns + .75 ns = 254750 ps */ + + unsigned int refresh_rate_ns; + + unsigned int tIS_ps; /* byte 32, spd->ca_setup */ + unsigned int tIH_ps; /* byte 33, spd->ca_hold */ + unsigned int tDS_ps; /* byte 34, spd->data_setup */ + unsigned int tDH_ps; /* byte 35, spd->data_hold */ + unsigned int tRTP_ps; /* byte 38, spd->trtp */ + unsigned int tDQSQ_max_ps; /* byte 44, spd->tdqsq */ + unsigned int tQHS_ps; /* byte 45, spd->tqhs */ +} dimm_params_t; + + +extern unsigned int ddr2_compute_dimm_parameters( + const generic_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number); + +extern void reset_dimm_parameters(dimm_params_t *pdimm); + +#endif diff --git a/cpu/mpc8xxx/fsl_ddr1.c b/cpu/mpc8xxx/fsl_ddr1.c new file mode 100644 index 0000000..7483cb8 --- /dev/null +++ b/cpu/mpc8xxx/fsl_ddr1.c @@ -0,0 +1,365 @@ +/* + * 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 + +#include "fsl_ddr_sdram.h" + + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Study these table from Byte 31 of JEDEC SPD Spec. + * + * DDR I DDR II + * Bit Size Size + * --- ----- ------ + * 7 high 512MB 512MB + * 6 256MB 256MB + * 5 128MB 128MB + * 4 64MB 16GB + * 3 32MB 8GB + * 2 16MB 4GB + * 1 2GB 2GB + * 0 low 1GB 1GB + * + * Reorder Table to be linear by stripping the bottom + * 2 or 5 bits off and shifting them up to the top. + * + * FIXME: Should this go into a memory technology specific file? + * FIXME: This currently handles DDR 1 and 2, so might be left here. + * FIXME: Can DDR 3 be added to it easily? spd_utils.c? + */ + +static phys_size_t +compute_ranksize(unsigned int mem_type, unsigned char row_dens) +{ + phys_size_t bsize; + + if (mem_type == SPD_MEMTYPE_DDR) { + /* Bottom 2 bits up to the top. */ + bsize = ((row_dens >> 2) | ((row_dens & 3) << 6)); + bsize <<= 24ULL; + debug("DDR: DDR I rank density = 0x%08x\n", bsize); + } else { + /* Bottom 5 bits up to the top. */ + bsize = ((row_dens >> 5) | ((row_dens & 31) << 3)); + bsize <<= 27ULL; + debug("DDR: DDR II rank density = 0x%08x\n", bsize); + } + + return bsize; +} + + +/* + * Convert a two-nibble BCD value into a cycle time. + * While the spec calls for nano-seconds, picos are returned. + * + * This implements the tables for bytes 9, 23 and 25 for both + * DDR I and II. No allowance for distinguishing the invalid + * fields absent for DDR I yet present in DDR II is made. + * (That is, cycle times of .25, .33, .66 and .75 ns are + * allowed for both DDR II and I.) + */ + +static unsigned int +convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val) +{ + /* + * Table look up the lower nibble, allow DDR I & II. + */ + unsigned int tenths_ps[16] = { + 0, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 250, /* This and the next 3 entries valid ... */ + 330, /* ... only for tCK calculations. */ + 660, + 750, + 0, /* undefined */ + 0 /* undefined */ + }; + + unsigned int whole_ns = (spd_val & 0xF0) >> 4; + unsigned int tenth_ns = spd_val & 0x0F; + unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns]; + + return ps; +} + + +static unsigned int +convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val) +{ + unsigned int tenth_ns = (spd_val & 0xF0) >> 4; + unsigned int hundredth_ns = spd_val & 0x0F; + unsigned int ps = tenth_ns * 100 + hundredth_ns * 10; + + return ps; +} + + +static unsigned int byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, /* supposed to be RFC, but not sure what that means */ + 0 /* Undefined */ +}; + + +static unsigned int +compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc) +{ + unsigned int trfc_ps; + + trfc_ps = (((trctrfc_ext & 0x1) * 256) + trfc) * 1000 + + byte40_table_ps[(trctrfc_ext >> 1) & 0x7]; + + return trfc_ps; +} + + +static unsigned int +compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc) +{ + unsigned int trc_ps; + + trc_ps = trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7]; + + return trc_ps; +} + + +/* + * Determine Refresh Rate. Ignore self refresh bit on DDR I. + * Table from SPD Spec, Byte 12, converted to picoseconds and + * filled in with "default" normal values. + */ +static unsigned int +determine_refresh_rate_ns(const unsigned int spd_refresh) +{ + unsigned int refresh_time_ns[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_ns[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 bin 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 alwqys 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 stanadard 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 */ +unsigned short ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500 }; + +unsigned int +compute_derated_DDR2_CAS_latency(unsigned int mclk_ps) +{ + const unsigned int num_speed_bins = ARRAY_SIZE(ddr2_speed_bins); + unsigned int lowest_tCKmin_found = 0; + unsigned int lowest_tCKmin_CL = 0; + unsigned int i; + + debug("mclk_ps = %u\n", mclk_ps); + + for (i = 0; i < num_speed_bins; i++) { + unsigned int x = ddr2_speed_bins[i]; + debug("i=%u, x = %u, lowest_tCKmin_found = %u\n", + i, x, lowest_tCKmin_found); + if (x && x <= mclk_ps && x >= lowest_tCKmin_found ) { + lowest_tCKmin_found = x; + lowest_tCKmin_CL = i + 2; + } + } + + debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL); + + return lowest_tCKmin_CL; +} + + +/* + * ddr2_compute_dimm_parameters for DDR2 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_t structure pointed by pdimm. + * + * FIXME: use #define for the retvals + */ + +unsigned int +ddr2_compute_dimm_parameters(const ddr1_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + unsigned int retval; + + /* + * FIXME debate: shouldn't have to dynamically determine memory type + * This ought to be determined through a config #define + */ + + if (spd->mem_type) { + if (spd->mem_type != SPD_MEMTYPE_DDR2) { + /* + * FIXME; this code does not currently support + * anything but DDR2 + */ + printf("DIMM %u: is not a DDR2 SPD.\n", dimm_number); + return 1; + } + } else { + reset_dimm_parameters(pdimm); + return 1; + } + + retval = ddr1_spd_check(spd); + if (retval) { + printf("DIMM %u: failed checksum\n", dimm_number); + return 2; + } + + /* + * 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->nrows; + pdimm->rank_density = compute_ranksize(spd->mem_type, spd->bank_dens); + pdimm->dimm_capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = spd->dataw_lsb; + pdimm->primary_sdram_width = spd->primw; + pdimm->ec_sdram_width = spd->ecw; + + /* + * FIXME: Need to determine registered_dimm status. + * 1 == register buffered + * 0 == unbuffered + */ + pdimm->registered_dimm = 0; /* unbuffered */ + + /* + * SDRAM device parameters + */ + 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->bank_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)); + + /* + * Compute CAS latencies below that defined by SPD + */ + pdimm->caslat_lowest_derated + = compute_derated_DDR2_CAS_latency(get_memory_clk_period_ps()); + + /* + * Compute timing parameters + */ + pdimm->tRCD_ps = spd->trcd * 250; + pdimm->tRP_ps = spd->trp * 250; + pdimm->tRAS_ps = spd->tras * 1000; + + pdimm->tWR_ps = mclk_to_picos(3); + pdimm->tWTR_ps = mclk_to_picos(1); + pdimm->tRFC_ps = compute_trfc_ps_from_spd(0, spd->trfc); + + pdimm->tRRD_ps = spd->trrd * 250; + pdimm->tRC_ps = compute_trc_ps_from_spd(0, spd->trc); + + pdimm->refresh_rate_ns = determine_refresh_rate_ns(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 = mclk_to_picos(2); /* By the book. */ + pdimm->tDQSQ_max_ps = spd->tdqsq * 10; + pdimm->tQHS_ps = spd->tqhs * 10; + + return 0; +} diff --git a/cpu/mpc8xxx/fsl_ddr2.c b/cpu/mpc8xxx/fsl_ddr2.c new file mode 100644 index 0000000..1a13d76 --- /dev/null +++ b/cpu/mpc8xxx/fsl_ddr2.c @@ -0,0 +1,384 @@ +/* + * 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 + +#include "fsl_ddr_sdram.h" + + +/* + * Calculate the Density of each Physical Rank. + * Returned size is in bytes. + * + * Study these table from Byte 31 of JEDEC SPD Spec. + * + * DDR I DDR II + * Bit Size Size + * --- ----- ------ + * 7 high 512MB 512MB + * 6 256MB 256MB + * 5 128MB 128MB + * 4 64MB 16GB + * 3 32MB 8GB + * 2 16MB 4GB + * 1 2GB 2GB + * 0 low 1GB 1GB + * + * Reorder Table to be linear by stripping the bottom + * 2 or 5 bits off and shifting them up to the top. + * + * FIXME: Should this go into a memory technology specific file? + * FIXME: This currently handles DDR 1 and 2, so might be left here. + * FIXME: Can DDR 3 be added to it easily? ddr_utils.c? + */ + +static phys_size_t +compute_ranksize(unsigned int mem_type, unsigned char row_dens) +{ + phys_size_t bsize; + + if (mem_type == SPD_MEMTYPE_DDR) { + /* Bottom 2 bits up to the top. */ + bsize = ((row_dens >> 2) | ((row_dens & 3) << 6)); + bsize <<= 24ULL; + debug("DDR: DDR I rank density = 0x%08x\n", bsize); + } else { + /* Bottom 5 bits up to the top. */ + bsize = ((row_dens >> 5) | ((row_dens & 31) << 3)); + bsize <<= 27ULL; + debug("DDR: DDR II rank density = 0x%08x\n", bsize); + } + + return bsize; +} + + +/* + * Convert a two-nibble BCD value into a cycle time. + * While the spec calls for nano-seconds, picos are returned. + * + * This implements the tables for bytes 9, 23 and 25 for both + * DDR I and II. No allowance for distinguishing the invalid + * fields absent for DDR I yet present in DDR II is made. + * (That is, cycle times of .25, .33, .66 and .75 ns are + * allowed for both DDR II and I.) + */ + +static unsigned int +convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val) +{ + /* + * Table look up the lower nibble, allow DDR I & II. + */ + unsigned int tenths_ps[16] = { + 0, + 100, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 250, /* This and the next 3 entries valid ... */ + 330, /* ... only for tCK calculations. */ + 660, + 750, + 0, /* undefined */ + 0 /* undefined */ + }; + + unsigned int whole_ns = (spd_val & 0xF0) >> 4; + unsigned int tenth_ns = spd_val & 0x0F; + unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns]; + + return ps; +} + + +static unsigned int +convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val) +{ + unsigned int tenth_ns = (spd_val & 0xF0) >> 4; + unsigned int hundredth_ns = spd_val & 0x0F; + unsigned int ps = tenth_ns * 100 + hundredth_ns * 10; + + return ps; +} + + +static unsigned int byte40_table_ps[8] = { + 0, + 250, + 330, + 500, + 660, + 750, + 0, /* supposed to be RFC, but not sure what that means */ + 0 /* Undefined */ +}; + + +static unsigned int +compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc) +{ + unsigned int trfc_ps; + + trfc_ps = (((trctrfc_ext & 0x1) * 256) + trfc) * 1000 + + byte40_table_ps[(trctrfc_ext >> 1) & 0x7]; + + return trfc_ps; +} + + +static unsigned int +compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc) +{ + unsigned int trc_ps; + + trc_ps = trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7]; + + return trc_ps; +} + + +/* + * Determine Refresh Rate. Ignore self refresh bit on DDR I. + * Table from SPD Spec, Byte 12, converted to picoseconds and + * filled in with "default" normal values. + */ +static unsigned int +determine_refresh_rate_ns(const unsigned int spd_refresh) +{ + unsigned int refresh_time_ns[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_ns[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 bin 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 alwqys 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 stanadard 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 */ +unsigned short ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500 }; + +unsigned int +compute_derated_DDR2_CAS_latency(unsigned int mclk_ps) +{ + const unsigned int num_speed_bins = ARRAY_SIZE(ddr2_speed_bins); + unsigned int lowest_tCKmin_found = 0; + unsigned int lowest_tCKmin_CL = 0; + unsigned int i; + + debug("mclk_ps = %u\n", mclk_ps); + + for (i = 0; i < num_speed_bins; i++) { + unsigned int x = ddr2_speed_bins[i]; + debug("i=%u, x = %u, lowest_tCKmin_found = %u\n", + i, x, lowest_tCKmin_found); + if (x && x <= mclk_ps && x >= lowest_tCKmin_found ) { + lowest_tCKmin_found = x; + lowest_tCKmin_CL = i + 2; + } + } + + debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL); + + return lowest_tCKmin_CL; +} + + +/* + * ddr2_compute_dimm_parameters for DDR2 SPD + * + * Compute DIMM parameters based upon the SPD information in spd. + * Writes the results to the dimm_params_t structure pointed by pdimm. + * + * FIXME: use #define for the retvals + */ + +unsigned int +ddr2_compute_dimm_parameters(const ddr2_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + unsigned int retval; + + /* + * FIXME debate: shouldn't have to dynamically determine memory type + * This ought to be determined through a config #define + */ + + if (spd->mem_type) { + if (spd->mem_type != SPD_MEMTYPE_DDR2) { + /* + * FIXME; this code does not currently support + * anything but DDR2 + */ + printf("DIMM %u: is not a DDR2 SPD.\n", dimm_number); + return 1; + } + } else { + reset_dimm_parameters(pdimm); + return 1; + } + + retval = ddr2_spd_check(spd); + if (retval) { + printf("DIMM %u: failed checksum\n", dimm_number); + return 2; + } + + /* + * 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->dimm_capacity = pdimm->n_ranks * pdimm->rank_density; + pdimm->data_width = spd->dataw; + pdimm->primary_sdram_width = spd->primw; + pdimm->ec_sdram_width = spd->ecw; + + { + unsigned char dimm_type = spd->dimm_type; + + /* + * FIXME: what about registered SO-DIMM? + */ + switch (dimm_type) { + case 0x01: /* RDIMM */ + case 0x10: /* Mini-RDIMM */ + pdimm->registered_dimm = 1; /* register buffered */ + break; + + case 0x02: /* UDIMM */ + case 0x04: /* SO-DIMM */ + case 0x08: /* Micro-DIMM */ + case 0x20: /* Mini-UDIMM */ + pdimm->registered_dimm = 0; /* unbuffered */ + break; + + default: + printf("unknown dimm_type 0x%02X\n", dimm_type); + return 1; + break; + } + } + + /* + * SDRAM device parameters + */ + 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)); + + /* + * Compute CAS latencies below that defined by SPD + */ + pdimm->caslat_lowest_derated + = compute_derated_DDR2_CAS_latency(get_memory_clk_period_ps()); + + /* + * Compute timing parameters + */ + 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_ns = determine_refresh_rate_ns(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; +} diff --git a/cpu/mpc8xxx/fsl_ddr_sdram.c b/cpu/mpc8xxx/fsl_ddr_sdram.c new file mode 100644 index 0000000..72af6ac --- /dev/null +++ b/cpu/mpc8xxx/fsl_ddr_sdram.c @@ -0,0 +1,2448 @@ +/* + * 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. + */ + +/* + * Generic driver for Freescale DDR/DDR2/DDR3 memory controller. + * Based on code from spd_sdram.c + * Author: James Yang [at freescale.com] + */ + +#include + +#include "fsl_ddr_sdram.h" + +/* + * Board-specific functions defined in each board's ddr.c + */ + +unsigned int fsl_ddr_sdram_get_mem_data_rate(void); +void fsl_ddr_sdram_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd, + unsigned int ctrl_num); +void fsl_ddr_sdram_dump_memctl_regs(unsigned int ctrl_num); +void fsl_ddr_sdram_set_memctl_regs(const fsl_memctl_config_regs_t *regs, + unsigned int ctrl_num); +void fsl_ddr_sdram_set_lawbar( + const common_timing_params_t *memctl_common_params, + unsigned int memctl_interleaved, + unsigned int ctrl_num); +unsigned int fsl_ddr_sdram_type_function(void); +unsigned int fsl_ddr_sdram_clk_adjust_function(void); +unsigned int fsl_ddr_sdram_cpo_override_function(void); +unsigned int fsl_ddr_sdram_write_data_delay_function(void); +unsigned int fsl_ddr_sdram_half_strength_driver_enable_function(void); + + +/* + * ASSUMPTIONS: + * - Same number of CONFIG_DIMM_SLOTS_PER_CTLR on each controller + * - Same memory data bus width on all controllers + * + * NOTES: + * + * The memory controller and associated documentation use confusing + * terminology when referring to the orgranization of DRAM. + * + * Here is a terminology translation table: + * + * memory controller/documention |industry |this code |signals + * -------------------------------|-----------|-----------|----------------- + * physical bank/bank |rank |rank |chip select (CS) + * logical bank/sub-bank |bank |bank |bank address (BA) + * page/row |row |page |row address + * ??? |column |column |column address + * + * The naming confusion is further exacerbated by the descriptions of the + * memory controller interleaving feature, where accesses are interleaved + * _BETWEEN_ two seperate memory controllers. This is configured only in + * CS0_CONFIG[INTLV_CTL] of each memory controller. + * + * memory controller documentation | number of chip selects + * | per memory controller supported + * --------------------------------|----------------------------------------- + * cache line interleaving | 1 (CS0 only) + * page interleaving | 1 (CS0 only) + * bank interleaving | 1 (CS0 only) + * superbank interleraving | depends on bank (chip select) + * | interleraving [rank interleaving] + * | mode used on every memory controller + * + * Even further confusing is the existence of the interleaving feature + * _WITHIN_ each memory controller. The feature is referred to in + * documentation as chip select interleaving or bank interleaving, + * although it is configured in the DDR_SDRAM_CFG field. + * + * Name of field | documentation name | this code + * -----------------------------|-----------------------|------------------ + * DDR_SDRAM_CFG[BA_INTLV_CTL] | Bank (chip select) | rank interleaving + * | interleaving + */ + + +const char *step_string_tbl[] = { + "STEP_GET_SPD", + "STEP_COMPUTE_DIMM_PARMS", + "STEP_COMPUTE_COMMON_PARMS", + "STEP_GATHER_OPTS", + "STEP_ASSIGN_ADDRESSES", + "STEP_COMPUTE_REGS", + "STEP_PROGRAM_REGS", + "STEP_ALL" /* FIXME: probably shouldn't be here */ +}; + +const char * step_to_string(unsigned int step) { + return step_string_tbl[step]; +} + +/* + * Round mclk_ps to nearest 10 ps in memory controller code. + * + * 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. + */ +unsigned int get_memory_clk_period_ps(void) +{ + unsigned int mclk_ps; + + mclk_ps = 2000000000000ULL / fsl_ddr_sdram_get_mem_data_rate(); + /* round to nearest 10 ps */ + return 10 * ((mclk_ps + 5) / 10); +} + +/* + * Convert picoseconds into DRAM clock cycles (rounding up if needed). + */ +static unsigned int picos_to_mclk(unsigned int picos) +{ + const unsigned long long ULL_2e12 = 2000000000000ULL; + const unsigned long long ULL_8Fs = 0xFFFFFFFFULL; + unsigned long long clks; + unsigned long long clks_temp; + + if (!picos) + return 0; + + clks = fsl_ddr_sdram_get_mem_data_rate() * (unsigned long long) picos; + clks_temp = clks; + clks = clks / ULL_2e12; + if (clks_temp % ULL_2e12) { + clks++; + } + + if (clks > ULL_8Fs) { + clks = ULL_8Fs; + } + + return (unsigned int) clks; +} + +unsigned int mclk_to_picos(unsigned int mclk) +{ + return get_memory_clk_period_ps() * mclk; +} + +void reset_dimm_parameters(dimm_params_t *pdimm) +{ + memset(pdimm, 0, sizeof(dimm_params_t)); +} + +/* + * 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. + */ + +static unsigned int +compute_lowest_common_dimm_parameters(const dimm_params_t *dimm_params, + common_timing_params_t *outpdimm, + unsigned int number_of_dimms) +{ + unsigned int i; + + unsigned int tCKmin_X_ps = 0; + unsigned int tCKmax_ps = 0xFFFFFFFF; + unsigned int tCKmax_max_ps = 0; + unsigned int tRCD_ps = 0; + unsigned int tRP_ps = 0; + unsigned int tRAS_ps = 0; + unsigned int tWR_ps = 0; + unsigned int tWTR_ps = 0; + unsigned int tRFC_ps = 0; + unsigned int tRRD_ps = 0; + unsigned int tRC_ps = 0; + unsigned int refresh_rate_ns = 0; + unsigned int tIS_ps = 0; + unsigned int tIH_ps = 0; + unsigned int tDS_ps = 0; + unsigned int tDH_ps = 0; + unsigned int tRTP_ps = 0; + unsigned int tDQSQ_max_ps = 0; + unsigned int tQHS_ps = 0; + + unsigned int temp1, temp2; + unsigned int lowest_good_caslat; + unsigned int additive_latency = 0; + const unsigned int mclk_ps = get_memory_clk_period_ps(); + unsigned int not_ok; + + debug("using mclk_ps = %u\n", mclk_ps); + + temp1 = 0; + for (i = 0; i < number_of_dimms; i++) { + /* + * If there are no ranks on this DIMM, + * it probably doesn't exist, so skip it. + */ + if (dimm_params[i].n_ranks == 0) { + temp1++; + continue; + } + + /* + * Find maximum tCKmin_X_ps to find slowest DIMM. + */ + tCKmin_X_ps = max(tCKmin_X_ps, dimm_params[i].tCKmin_X_ps); + + /* + * Find minimum tCKmax_ps to find fastest slow speed, + * i.e., this is the slowest the whole system can go. + */ + tCKmax_ps = min(tCKmax_ps, dimm_params[i].tCKmax_ps); + + /* + * Find maximum tCKmax_ps to find slowest slow speed, + * i.e., this is the slowest any dimm can go. + */ + tCKmax_max_ps = max(tCKmax_max_ps, dimm_params[i].tCKmax_ps); + + /* + * Find maximum tRCD_ps to find slowest ras-to-cas delay. + */ + tRCD_ps = max(tRCD_ps, dimm_params[i].tRCD_ps); + + /* + * Find maximum tRP_ps to find slowest row precharge time. + */ + tRP_ps = max(tRP_ps, dimm_params[i].tRP_ps); + + /* + * Find maximum tRAS_ps to find slowest active to + * precharge time. + */ + tRAS_ps = max(tRAS_ps, dimm_params[i].tRAS_ps); + + /* + * Find maximum tWR_ps to find slowest write recovery time. + */ + tWR_ps = max(tWR_ps, dimm_params[i].tWR_ps); + + /* + * Find maximum tWTR_ps to find slowest write-to-read + * command delay. + */ + tWTR_ps = max(tWTR_ps, dimm_params[i].tWTR_ps); + + /* + * Find maximum tRFC_ps to find slowest auto-refresh to + * active/auto refresh command period. + */ + tRFC_ps = max(tRFC_ps, dimm_params[i].tRFC_ps); + + /* + * Find maximum tRRD_ps to find slowest row active to + * row active delay. + */ + tRRD_ps = max(tRRD_ps, dimm_params[i].tRRD_ps); + + /* + * Find maximum tRC_ps to find slowest + * active/auto-refresh time. + */ + tRC_ps = max(tRC_ps, dimm_params[i].tRC_ps); + + /* + * Find maximum refresh_rate_ns to find slowest refresh time. + */ + refresh_rate_ns = max(refresh_rate_ns, + dimm_params[i].refresh_rate_ns); + + /* + * Find maximum tIS_ps to find slowest. + */ + tIS_ps = max(tIS_ps, dimm_params[i].tIS_ps); + + /* + * Find maximum tIH_ps to find slowest. + */ + tIH_ps = max(tIH_ps, dimm_params[i].tIH_ps); + + /* + * Find maximum tDS_ps to find slowest. + */ + tDS_ps = max(tDS_ps, dimm_params[i].tDS_ps); + + /* + * Find maximum tDH_ps to find slowest. + */ + tDH_ps = max(tDH_ps, dimm_params[i].tDH_ps); + + /* + * Find maximum tRTP_ps to find slowest. + */ + tRTP_ps = max(tRTP_ps, dimm_params[i].tRTP_ps); + + /* + * Find maximum tDQSQ_max_ps to find slowest. + * + * FIXME: is finding the slowest value the correct + * strategy for this parameter? + */ + tDQSQ_max_ps = max(tDQSQ_max_ps, dimm_params[i].tDQSQ_max_ps); + + /* + * Find maximum tQHS_ps to find slowest. + */ + tQHS_ps = max(tQHS_ps, dimm_params[i].tQHS_ps); + } + + outpdimm->ndimms_present = number_of_dimms - temp1; + + if (temp1 == number_of_dimms) { + debug("no dimms this memory controller\n"); + return 0; + } + + outpdimm->tCKmin_X_ps = tCKmin_X_ps; + outpdimm->tCKmax_ps = tCKmax_ps; + outpdimm->tCKmax_max_ps = tCKmax_max_ps; + outpdimm->tRCD_ps = tRCD_ps; + outpdimm->tRP_ps = tRP_ps; + outpdimm->tRAS_ps = tRAS_ps; + outpdimm->tWR_ps = tWR_ps; + outpdimm->tWTR_ps = tWTR_ps; + outpdimm->tRFC_ps = tRFC_ps; + outpdimm->tRRD_ps = tRRD_ps; + outpdimm->tRC_ps = tRC_ps; + outpdimm->refresh_rate_ns = refresh_rate_ns; + outpdimm->tIS_ps = tIS_ps; + outpdimm->tIH_ps = tIH_ps; + outpdimm->tDS_ps = tDS_ps; + outpdimm->tDH_ps = tDH_ps; + outpdimm->tRTP_ps = tRTP_ps; + outpdimm->tDQSQ_max_ps = tDQSQ_max_ps; + outpdimm->tQHS_ps = tQHS_ps; + + /* + * Determine common burst length for all DIMMs. + */ + temp1 = 0xff; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) { + temp1 &= dimm_params[i].burst_lengths_bitmask; + } + } + outpdimm->all_DIMMs_burst_lengths_bitmask = temp1; + + /* + * Determine if all DIMMs registered buffered. + */ + temp1 = temp2 = 0; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks) { + if (dimm_params[i].registered_dimm) + temp1 = 1; + if (!dimm_params[i].registered_dimm) + temp2 = 1; + } + } + + outpdimm->all_DIMMs_registered = 0; + if (temp1 && !temp2) { + outpdimm->all_DIMMs_registered = 1; + } + + outpdimm->all_DIMMs_unbuffered = 0; + if (!temp1 && temp2) { + outpdimm->all_DIMMs_unbuffered = 1; + } + + /* CHECKME: */ + if (!outpdimm->all_DIMMs_registered + && !outpdimm->all_DIMMs_unbuffered) { + printf("ERROR: Mix of registered buffered and unbuffered DIMMs detected!\n"); + } + + + /* + * Compute a CAS latency suitable for all DIMMs + * + * Strategy for SPD-defined latencies: compute only + * CAS latency defined by all DIMMs. + */ + + /* + * 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 glorious + * __ilog2 function retursn 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); + debug("checking common caslat = %u\n", temp2); + + /* + * Check if this CAS latency will work on all DIMMs at tCK. + */ + 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) { + debug("CL = %u ok on DIMM %u at tCK=%u ps with its tCKmin_X_ps of %u\n", + temp2, i, mclk_ps, + dimm_params[i].tCKmin_X_ps); + continue; + } else { + not_ok++; + } + } + + if (dimm_params[i].caslat_X_minus_1 == temp2) { + if (mclk_ps >= dimm_params[i].tCKmin_X_minus_1_ps) { + debug("CL = %u ok on DIMM %u at tCK=%u ps with its tCKmin_X_minus_1_ps of %u\n", + temp2, i, mclk_ps, dimm_params[i].tCKmin_X_minus_1_ps); + continue; + } else { + not_ok++; + } + } + + if (dimm_params[i].caslat_X_minus_2 == temp2) { + if (mclk_ps >= dimm_params[i].tCKmin_X_minus_2_ps) { + debug("CL = %u ok on DIMM %u at tCK=%u ps with its tCKmin_X_minus_2_ps of %u\n", + temp2, i, mclk_ps, dimm_params[i].tCKmin_X_minus_2_ps); + continue; + } else { + not_ok++; + } + } + } + + if (!not_ok) { + lowest_good_caslat = temp2; + } + + temp1 &= ~(1 << temp2); + } + + debug("lowest common SPD-defined CAS latency = %u\n", + lowest_good_caslat); + outpdimm->lowest_common_SPD_caslat = lowest_good_caslat; + + + /* + * Compute a common 'de-rated' CAS latency. + * + * The strategy here is to find the *highest* dereated cas latency + * with the assumption that all of the DIMMs will support a dereated + * CAS latency higher than or equal to their lowest dereated value. + */ + temp1 = 0; + for (i = 0; i < number_of_dimms; i++) { + temp1 = max(temp1, dimm_params[i].caslat_lowest_derated); + } + outpdimm->highest_common_derated_caslat = temp1; + debug("highest common dereated CAS latency = %u\n", temp1); + + + /* + * Determine if all DIMMs ECC capable. + */ + temp1 = 1; + for (i = 0; i < number_of_dimms; i++) { + if (dimm_params[i].n_ranks && dimm_params[i].edc_config != 2) { + temp1 = 0; + break; + } + } + if (temp1) { + debug("all DIMMs ECC capable\n"); + } else { + debug("Warning: not all DIMMs ECC capable, can't enable ECC\n"); + } + outpdimm->all_DIMMs_ECC_capable = temp1; + + + /* + * FIXME: move to somewhere else to validate. + */ + if (mclk_ps > tCKmax_max_ps) { + printf("Warning: ome of the installed DIMMs can not operate this slowly.\n"); + return 1; + } + + /* + * Compute additive latency. + * + * For DDR1, additive latency should be 0. + * + * For DDR2, with ODT enabled, use "a value" less than ACTTORW, + * which comes from Trcd, and also note that: + * add_lat + caslat must be >= 4 + * + * For DDR3, FIXME additive latency determination + * + * When to use additive latency for DDR2: + * + * I. Because you are using CL=3 and need to do ODT on writes and + * want functionality. + * 1. Are you going to use ODT? (Does your board not have + * additional termination circuitry for DQ, DQS, DQS_, + * DM, RDQS, RDQS_ for x4/x8 configs?) + * 2. If so, is your lowest supported CL going to be 3? + * 3. If so, then you must set AL=1 because + * + * WL >= 3 for ODT on writes + * RL = AL + CL + * WL = RL - 1 + * -> + * WL = AL + CL - 1 + * AL + CL - 1 >= 3 + * AL + CL >= 4 + * QED + * + * RL >= 3 for ODT on reads + * RL = AL + CL + * + * Since CL aren't usually less than 2, AL=0 is a minimum, + * so the WL-derived AL should be the -- FIXME? + * + * II. Because you are using auto-precharge globally and want to + * use additive latency (posted CAS) to get more bandwidth. + * 1. Are you going to use auto-precharge mode globally? + * + * Use addtivie latency and compute AL to be 1 cycle less than + * tRCD, i.e. the READ or WRITE command is in the cycle + * immediately following the ACTIVATE command.. + * + * III. Because you feel like it or want to do some sort of + * degraded-performance experiment. + * 1. Do you just want to use additive latency because you feel + * like it? + * + * Validation: AL is less than tRCD, and within the other + * read-to-precharge constraints. + */ + + additive_latency = 0; + +#if defined(CONFIG_FSL_DDR2) + if (lowest_good_caslat < 4) { + additive_latency = picos_to_mclk(tRCD_ps) - lowest_good_caslat; + if (mclk_to_picos(additive_latency) > tRCD_ps) { + additive_latency = picos_to_mclk(tRCD_ps); + debug("setting additive_latency to %u because it was greater than tRCD_ps\n", additive_latency); + } + } + +#elif defined(CONFIG_FSL_DDR3) +error "FIXME determine additive latency for DDR3" +#endif + + /* + * Validate additive latency + * FIXME: move to somewhere else to validate + * + * AL <= tRCD(min) + */ + if (mclk_to_picos(additive_latency) > tRCD_ps) { + printf("Error: invalid additive latency exceeds tRCD(min).\n"); + return 1; + } + + /* + * FIXME: RL = CL + AL; RL >= 3 for ODT_RD_CFG to be enabled + * FIXME: WL = RL - 1; WL >= 3 for ODT_WL_CFG to be enabled + * FIXME: ADD_LAT (the register) must be set to a value less + * FIXME: than ACTTORW if WL = 1, then AL must be set to 1 + * FIXME: RD_TO_PRE (the register) must be set to a minimum + * FIXME: tRTP + AL if AL is nonzero + */ + + /* + * Additive latency will be applied only if the memctl option to + * use it. + */ + outpdimm->additive_latency = additive_latency; + + return 0; +} + +/* + * Dummy function to init memctl options -- ultimately want an + * interactive version of this function + */ +unsigned int +populate_memctl_options(const common_timing_params_t *pcommon_params, + memctl_options_t *popts, + unsigned int ctrl_num) +{ + unsigned int i; + + /* + * Chip select options. + */ + + /* + * Pick chip-select local options. + */ + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + /* + * If not DDR2, odt_rd_cfg and odt_wr_cfg need to be 0. + */ + + /* only for single CS? */ + popts->cs_local_opts[i].odt_rd_cfg = 0; + + popts->cs_local_opts[i].odt_wr_cfg = 1; + popts->cs_local_opts[i].auto_precharge = 0; + } + + /* + * Pick interleaving mode. + */ + + /* + * 0 = no interleaving + * 1 = interleaving between 2 controllers + */ + popts->memctl_interleaving = 0; + + /* + * 0 = cacheline + * 1 = page + * 2 = (logical) bank + * 3 = superbank (only if CS interleaving is enabled) + */ + popts->memctl_interleaving_mode = 0; + + /* + * 0: cacheline: bit 30 of the 36-bit physical addr selects the memctl + * 1: page: bit to the left of the column bits selects the memctl + * 2: bank: bit to the left of the bank bits selects the memctl + * 3: superbank: bit to the left of the chip select selects the memctl + * + * NOTE: ba_intlv (rank interleaving) is independent of memory + * controller interleaving; it is only within a memory controller. + * Must use superbank interleaving if rank interleaving is used and + * memory controller interleaving is enabled. + */ + + /* + * 0 = no + * 0x40 = CS0,CS1 + * 0x20 = CS2,CS3 + * 0x60 = CS0,CS1 + CS2,CS3 + * 0x04 = CS0,CS1,CS2,CS3 + */ + popts->ba_intlv_ctl = 0; + + + /* + * Memory Organization Parameters + */ + popts->registered_dimm_en = pcommon_params->all_DIMMs_registered; + + + /* + * Operational Mode Paramters + */ + + /* + * Pick SDRAM type + * + * 2 = DDR1 + * 3 = DDR2 + * 6 = LPDDR1 + * 7 = DDR3 + */ + popts->sdram_type = fsl_ddr_sdram_type_function(); + + /* + * Pick ECC modes + */ + popts->ECC_mode = 0; /* 0 = disabled, 1 = enabled */ + popts->ECC_init_using_memctl = 1; /* 0 = use DMA, 1 = use memctl */ + + + /* + * Choose DQS config + */ + popts->DQS_config = 1; /* 0 for DDR1, 1 for DDR2 */ + + /* + * Choose self-refresh during sleep. + */ + popts->self_refresh_in_sleep = 1; + + /* + * Choose dynamic power management mode. + */ + popts->dynamic_power = 0; + + /* + * Choose dynamic power management mode. + * + * 0 = 64-bit + * 1 = 32-bit + * 2 = 16-bit + */ + popts->data_bus_width = 0; + + + /* + * Choose burst length. + */ + popts->burst_length = 4; /* has to be 4 for DDR2 */ + + /* + * Global Timing Parameters. + */ + debug("mclk_ps = %u ps\n", get_memory_clk_period_ps()); + + /* + * Pick a caslat override. + */ + popts->cas_latency_override = 0; + popts->cas_latency_override_value = 3; + if (popts->cas_latency_override) { + debug("using caslat override value = %u\n", + popts->cas_latency_override_value); + } + + /* + * Decide whether to use the computed dereated latency + */ + popts->use_derated_caslat = 0; // XXX: ought to be board-specific? + + /* + * Choose an additive latency. + */ + popts->additive_latency_override = 0; + popts->additive_latency_override_value = 3; + if (popts->additive_latency_override) { + debug("using additive latency override value = %u\n", + popts->additive_latency_override_value); + } + + /* + * Compute write latency. + * + * Total write latency = WR_LAT + ADD_LAT + * WL = Read latency - 1 = CL + AL - 1 + */ + + /* + * popts->write_latency = popts->cas_latency + * + popts->additive_latency - 1; + */ + + /* + * Select clock adjust + * + * Factors to consider for clock adjust: + * - number of chips on bus + * - position of slot + * - DDR1 vs. DDR2? + * - ??? + * + * This needs to be determined on a board-by-board basis. + */ + popts->clk_adjust = fsl_ddr_sdram_clk_adjust_function(); + + /* + * Select CPO override + * + * Dactors to consider for CPO: + * - frequency + * - ddr1 vs. ddr2 + */ + popts->cpo_override = fsl_ddr_sdram_cpo_override_function(); + + /* + * Select write data delay + * + * Factors to consider for write data delay: + * - number of DIMMs + * + * 1 = 1/4 clock delay + * 2 = 1/2 clock delay + * 3 = 3/4 clock delay + * 4 = 1 clock delay + * 5 = 5/4 clock delay + * 6 = 3/2 clock delay + */ + popts->write_data_delay = fsl_ddr_sdram_write_data_delay_function(); + + /* + * FIXME -- Move to a board-specific file + * + * Half-strength driver enable + * + * Factors to consider for half-strength driver enable: + * - number of DIMMs installed + */ + popts->half_strength_driver_enable + = fsl_ddr_sdram_half_strength_driver_enable_function(); + + + /* + * 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->twoT_en = 1; +// popts->threeT_en = 1; /* XXX: 3T_EN, 8572 */ + popts->threeT_en = 0; /* XXX: 3T_EN, 8572 */ + + /* + * BSTTOPRE precharge interval + * + * Set this to 0 for global auto precharge + * + * FIXME: Should this be configured in picoseconds? + * Why it should be in ps: better understanding of this + * relative to actual DRAM timing parameters such as tRAS. + * e.g. tRAS(min) = 40 ns + */ + popts->bstopre = 1000; + + /* + * 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 + * + * FIXME: UM: applies only to DDR2/DDR3 with eight logical banks only + * FIXME: varies depending upon number of column addresses or data + * FIXME: width, was considering looking at pdimm->primary_sdram_width + */ +#if defined(CONFIG_FSL_DDR1) + popts->tFAW_window_four_activates_ps = mclk_to_picos(1); + +#elif defined(CONFIG_FSL_DDR2) + // x4/x8; some datasheets have 35000 + popts->tFAW_window_four_activates_ps = 37500; + // x16 wide columns only? +// popts->tFAW_window_four_activates_ps = 50000; + +#elif defined(CONFIG_FSL_DDR3) +#error "FIXME determine four activates for DDR3" +#endif + + + /* + * Checks + */ + + /* + * CAS latency plus additive latency must be at least 3 cycles + * for ODT_RD_CFG to be enabled. + */ +#if 0 + /* + * This check should be in fsl ddr regs. + */ + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (popts->cs_local_opts[i].odt_rd_cfg) { + if (popts->cas_latency_override_value + popts->additive_latency_override_value < 3) { + printf("CHECKME: CAS latency and Additive latency must be at least 3 cycles for ODT_RD_CFG to be enabled (chip select = %u)\n", i); + } + } + } +#endif + +#if 0 + /* + * This check should be in fsl ddr regs. + */ + + /* + * Write latency plus additive latency must be at least 3 cycles + * for ODT_WR_CFG to be enabled. + */ + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (popts->cs_local_opts[i].odt_wr_cfg) { + if (popts->write_latency + popts->additive_latency < 3) { + printf("CHECKME: Write Latency plus Additive Latency must be at least 3 cycles for ODT_WR_CFG to be enabled\n"); + } + } + } +#endif + + /* + * ODT should only be used for DDR2 + */ + + /* FIXME? */ + + /* + * Interleaving checks. + * + * If memory controller interleaving is enabled, then the data + * bus widths must be programmed identically for the 2 memory + * controllers. + */ + + return 0; +} + + + +/* + * Maybe all of these register related functions should go into + * chip-specific file. + */ + +void +reset_fsl_memctl_config_regs(fsl_memctl_config_regs_t *ddr) +{ + unsigned int i; + + /* FIMME: why not just memset */ + + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + ddr->cs[i].bnds = 0; + ddr->cs[i].config = 0; + ddr->cs[i].config_2 = 0; + } + + ddr->timing_cfg_3 = 0; + ddr->timing_cfg_0 = 0; + ddr->timing_cfg_1 = 0; + ddr->timing_cfg_2 = 0; + ddr->ddr_sdram_cfg = 0; + ddr->ddr_sdram_cfg_2 = 0; + ddr->ddr_sdram_mode = 0; + ddr->ddr_sdram_mode_2 = 0; + ddr->ddr_sdram_interval = 0; + ddr->ddr_data_init = 0; + ddr->ddr_sdram_clk_cntl = 0; + ddr->ddr_init_addr = 0; + ddr->ddr_init_ext_addr = 0; + ddr->timing_cfg_4 = 0; + ddr->timing_cfg_5 = 0; + ddr->ddr_zq_cntl = 0; + ddr->ddr_wrlvl_cntl = 0; + ddr->ddr_pd_cntl = 0; + ddr->ddr_sr_cntr = 0; + ddr->ddr_sdram_rcw_1 = 0; + ddr->ddr_sdram_rcw_2 = 0; +} + +unsigned int +check_fsl_memctl_config_regs(const fsl_memctl_config_regs_t *ddr) +{ + unsigned int res = 0; + + /* + * Check that DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN] are + * not set at the same time. + */ + if (ddr->ddr_sdram_cfg & 0x10000000 + && ddr->ddr_sdram_cfg & 0x00008000) { + printf("Error: DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN] should not be set at the same time.\n"); + res++; + } + + return res; +} + +static unsigned int +compute_fsl_memctl_config_regs(const memctl_options_t *popts, + fsl_memctl_config_regs_t *ddr, + const common_timing_params_t *common_dimm, + const dimm_params_t *dimm_parameters, + unsigned int dbw_capacity_adjust) +{ + unsigned int i; + unsigned int cas_latency; + unsigned int additive_latency; + + reset_fsl_memctl_config_regs(ddr); + + if (common_dimm == NULL) { + printf("Error: subset DIMM params struct null pointer\n"); + return 1; + } + + /* + * Process overrides first. + * + * FIXME: somehow add dereated caslat to this + */ + cas_latency = (popts->cas_latency_override) + ? popts->cas_latency_override_value + : common_dimm->lowest_common_SPD_caslat; + + additive_latency = (popts->additive_latency_override) + ? popts->additive_latency_override_value + : common_dimm->additive_latency; + + /* + * Chip Select Memory Bounds (CSn_BNDS) + */ + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (popts->ba_intlv_ctl && i > 0) { + /* + * Do not set up boundaries if bank interleaving + * is used. + */ + break; + } + + if (dimm_parameters[i/2].n_ranks == 0) { + debug("Skipping setup of CS%u because n_ranks on DIMM %u is 0\n", i, i/2); + continue; + } + + { + phys_size_t sa = 0; + phys_size_t ea = 0; + + if (dimm_parameters[i/2].n_ranks) { + + if (popts->memctl_interleaving + && popts->ba_intlv_ctl) { + /* + * This works superbank 2CS + * There are 2 memory + * controllers configured + * identically, memory is + * interleaved between them, and + * each controller uses rank + * interleaving within itself. + * Therefore the starting and + * ending address on each + * controller is twice the + * amount present on each + * controller. + */ + ea = (2 * common_dimm->total_mem >> dbw_capacity_adjust) - 1; + + } else if (! popts->memctl_interleaving + && popts->ba_intlv_ctl) { + /* + * If memory interleaving + * between controllers is NOT + * enabled, the starting address + * for each memory controller is + * distinct. However, because + * rank interleaving is enabled, + * the starting and ending + * addresses of the total memory + * on that memory controller + * needs to be programmed into + * its respective CS0_BNDS. + */ + sa = common_dimm->base_address; + ea = sa + (common_dimm->total_mem >> dbw_capacity_adjust)- 1; + + } else if (popts->memctl_interleaving + && !popts->ba_intlv_ctl) { + /* + * Only the rank on CS0 of each + * memory controller may be used + * if memory controller + * interleaving is used without + * rank interleaving within each + * memory controller. However, + * the ending address programmed + * into each CS0 must be the sum + * of the amount of memory in + * the two CS0 ranks. + */ + if (i == 0) { + ea = (2 * (dimm_parameters[0].rank_density >> dbw_capacity_adjust)) - 1; + } + + } else if (!popts->memctl_interleaving + && !popts->ba_intlv_ctl) { + /* + * No rank interleaving and no + * memory controller + * interleaving. + */ + sa = dimm_parameters[i/2].base_address; + ea = sa + (dimm_parameters[i/2].rank_density >> dbw_capacity_adjust) - 1; + if (i&1) { + if ((dimm_parameters[i/2].n_ranks == 1)) { + /* + * Odd chip select, + * single-rank dimm + */ + sa = 0; + ea = 0; + } else { + /* + * Odd chip select, + * dual-rank DIMM + */ + sa += dimm_parameters[i/2].rank_density >> dbw_capacity_adjust; + ea += dimm_parameters[i/2].rank_density >> dbw_capacity_adjust; + } + } + } + } + + sa >>= 24; + ea >>= 24; + + /* + * FIXME: 32-bit physical ram versions use 2 nibbles + * FIXME: 36-bit physical ram versions use 3 nibbles + */ + ddr->cs[i].bnds = (0 + | ((sa & 0xFFF) << 16) /* starting address MSB */ + | ((ea & 0xFFF) << 0) /* ending address MSB */ + ); + } + + /* + * Chip Select Configuration (CSn_CONFIG) + */ + { + /* CS_n_EN: Chip Select enable */ + unsigned int cs_n_en; + /* INTLV_EN: Memory controller interleave enable */ + unsigned int intlv_en; + /* INTLV_CTL: Interleaving control */ + unsigned int intlv_ctl; + /* AP_n_EN: Chip select n auto-precharge enable */ + unsigned int ap_n_en; + /* ODT_RD_CFG: ODT for reads configuration */ + unsigned int odt_rd_cfg; + /* ODT_WR_CFG: ODT for writes configuration */ + unsigned int odt_wr_cfg; + /* BA_BITS_CS_n: Number of bank bits for SDRAM on chip select n */ + unsigned int ba_bits_cs_n; + /* ROW_BITS_CS_n: Number of row bits for SDRAM on chip select n */ + unsigned int row_bits_cs_n; + /* + * COL_BITS_CS_n: Number of ocl bits for + * SDRAM on chip select n + */ + unsigned int col_bits_cs_n; + + cs_n_en = 0; + intlv_en = 0; + intlv_ctl = 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; + + /* + * Compute CS_CONFIG only for existing ranks + * of each DIMM. + */ + if ((((i&1) == 0) + && (dimm_parameters[i/2].n_ranks == 1)) + || (dimm_parameters[i/2].n_ranks == 2)) { + cs_n_en = 1; + if (i == 0) { + /* + * These fields only available + * in CS0_CONFIG + */ + intlv_en = popts->memctl_interleaving; + intlv_ctl = popts->memctl_interleaving_mode; + } + ap_n_en = popts->cs_local_opts[i].auto_precharge; + odt_rd_cfg = popts->cs_local_opts[i].odt_rd_cfg; + odt_wr_cfg = popts->cs_local_opts[i].odt_wr_cfg; + ba_bits_cs_n = __ilog2(dimm_parameters[i/2].n_banks_per_sdram_device) - 2; + row_bits_cs_n = dimm_parameters[i/2].n_row_addr - 12; + col_bits_cs_n = dimm_parameters[i/2].n_col_addr - 8; + } + + /* + * FIXME: intlv_en, intlv_ctl only on CS0_CONFIG + */ + if (i != 0) { + intlv_en = 0; + intlv_ctl = 0; + } + + ddr->cs[i].config = (0 + | ((cs_n_en & 0x1) << 31) + | ((intlv_en & 0x3) << 29) + | ((intlv_en & 0xf) << 24) + | ((ap_n_en & 0x1) << 23) + + /* + * XXX: some implementation only have 1 bit + * starting at left. + */ + | ((odt_rd_cfg & 0x7) << 20) + + /* + * FIXME: Some implementation + * only have 1 bit starting at left. + */ + | ((odt_wr_cfg & 0x7) << 16) + + | ((ba_bits_cs_n & 0x3) << 14) + | ((row_bits_cs_n & 0x7) << 8) + | ((col_bits_cs_n & 0x7) << 0) + ); + } + + /* + * Chip Select Configuration 2 (CSn_CONFIG_2) + */ + + /* FIXME: 8572 */ + + { + /* PASR_CFG: Partial array self refresh config */ + unsigned int pasr_cfg; + + pasr_cfg = 0; + ddr->cs[i].config_2 = ((pasr_cfg & 7) << 24); + } + } + + /* + * -3E = 667 CL5, -25 = CL6 800, -25E = CL5 800 + */ + +#if defined(CONFIG_FSL_DDR2) + /* + * DDR SDRAM Timing Configuration 0 (TIMING_CFG_0) + * + * Avoid writing for DDR I. The new PQ38 DDR controller + * dreams up non-zero default values to be backwards compatible. + */ + { + unsigned char trwt_mclk; /* RWT: Read-to-write turnaround */ + unsigned char twrt_mclk; /* WRT: Write-to-read turnaround */ + + /* 7.5 ns on -3E; 0 means WL - CL + BL/2 + 1 */ + unsigned char trrt_mclk; /* RRT: Read-to-read turnaround */ + unsigned char twwt_mclk; /* WWT: Write-to-write turnaround */ + + /* + * ACT_PD_EXIT: Active powerdown exit timing + * (tXARD and tXARDS). Empirical? + */ + unsigned char act_pd_exit_mclk; + + /* + * FIXME: tXARD = 2, tXARDS = 7 - AL + * PRE_PD_EXIT: Precharge powerdown exit timing (tXP). + * Empirical? + */ + unsigned char pre_pd_exit_mclk; + + /* FIXME: tXP = 2 on Micron 667 MHz DIMM */ + /* ODT_PD_EXIT: Precharge powerdown exit timing (tAXPD). */ + unsigned char taxpd_mclk; + + /* MRS_CYC: Mode register set cycle time (tMRD). */ + unsigned char tmrd_mclk; + + /* + * FIXME: Are these configurable? + */ + trwt_mclk = 0; /* RWT: Read-to-write turnaround */ + twrt_mclk = 0; /* WRT: Write-to-read turnaround */ + /* 7.5 ns on -3E; 0 means WL - CL + BL/2 + 1 */ + trrt_mclk = 0; /* RRT: Read-to-read turnaround */ + twwt_mclk = 0; /* WWT: Write-to-write turnaround */ + + /* + * ACT_PD_EXIT: Active powerdown exit timing + * (tXARD and tXARDS). Empirical? + */ + act_pd_exit_mclk = 2; + + /* + * XXX: tXARD = 2, tXARDS = 7 - AL + * + * PRE_PD_EXIT: Precharge powerdown exit timing (tXP). + * Empirical? + */ + pre_pd_exit_mclk = 6; + + /* + * FIXME: tXP = 2 on Micron 667 MHz DIMM + * + * ODT_PD_EXIT: Precharge powerdown exit timing (tAXPD). + * FIXME: configurable? + */ + taxpd_mclk = 8; + + /* + * MRS_CYC: Mode register set cycle time (tMRD). + * FIXME: configurable? + */ + tmrd_mclk = 2; + + ddr->timing_cfg_0 = (0 + | ((trwt_mclk & 0x3) << 30) /* RWT */ + | ((twrt_mclk & 0x3) << 28) /* WRT */ + | ((trrt_mclk & 0x3) << 26) /* RRT */ + | ((twwt_mclk & 0x3) << 24) /* WWT */ + | ((act_pd_exit_mclk & 0x7) << 20) /* ACT_PD_EXIT */ + | ((pre_pd_exit_mclk & 0x7) << 16) /* PRE_PD_EXIT */ + | ((taxpd_mclk & 0xf) << 8) /* ODT_PD_EXIT */ + | ((tmrd_mclk & 0xf) << 0) /* MRS_CYC */ + ); +// printf("FSLDDR: timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0); + } +#endif /* defined(CONFIG_FSL_DDR2) */ + + + /* + * DDR SDRAM Timing Configuration 3 (TIMING_CFG_3) + */ + { + /* + * EXT_ACTTOPRE: Extended Activate to precharge interval + * (tRAS) + */ + unsigned int ext_refrec; + + ext_refrec = (picos_to_mclk(common_dimm->tRFC_ps) - 8) >> 4; + ddr->timing_cfg_3 = (0 + | ((ext_refrec & 0x7) << 16) /* EXT_ACTTOPRE */ + ); + } + + /* + * DDR SDRAM Timing Configuration 1 (TIMING_CFG_1) + */ + { + /* PRETOACT: Precharge-to-activate interval (tRP) */ + unsigned char pretoact_mclk; + /* ACTTOPRE: Activate to precharge interval (tRAS) */ + unsigned char acttopre_mclk; + /* ACTTORW: Activate to read/write interval (tRCD) */ + unsigned char acttorw_mclk; + /* CASLAT */ + unsigned char caslat_ctrl; + /* REFREC: Refresh recovery time (tRFC) ; trfc_low */ + unsigned char refrec_ctrl; + /* WRREC: Last data to precharge minimum interval (tWR) */ + unsigned char wrrec_mclk; + /* ACTTOACT: Activate-to-activate interval (tRRD) */ + unsigned char acttoact_mclk; + /* + * WRTTORD: Last write data pair to read + * command issue interval (tWTR) + */ + unsigned char wrtord_mclk; + + pretoact_mclk = picos_to_mclk(common_dimm->tRP_ps); + acttopre_mclk = picos_to_mclk(common_dimm->tRAS_ps); + acttorw_mclk = picos_to_mclk(common_dimm->tRCD_ps); + +#if defined(CONFIG_FSL_DDR1) + caslat_ctrl = (cas_latency + 1) & 0x07; +#elif defined(CONFIG_FSL_DDR2) + caslat_ctrl = 2 * cas_latency - 1; +#elif defiend(CONFIG_FSL_DDR2) +#error "Need CAS Latency help for DDR3 in fsl_ddr_sdram.c" +#endif + + refrec_ctrl = picos_to_mclk(common_dimm->tRFC_ps) - 8; + wrrec_mclk = picos_to_mclk(common_dimm->tWR_ps); + acttoact_mclk = picos_to_mclk(common_dimm->tRRD_ps); + wrtord_mclk = picos_to_mclk(common_dimm->tWTR_ps); + + ddr->timing_cfg_1 = (0 + | ((pretoact_mclk & 0x07) << 28) /* PRETOACT */ + | ((acttopre_mclk & 0x0F) << 24) /* ACTTOPRE */ + | ((acttorw_mclk & 0x7) << 20) /* ACTTORW */ + | ((caslat_ctrl & 0xF) << 16) /* CASLAT */ + | ((refrec_ctrl & 0xF) << 12) /* REFEC */ + | ((wrrec_mclk & 0x07) << 8) /* WRRREC */ + | ((acttoact_mclk & 0x07) << 4) /* ACTTOACT */ + | ((wrtord_mclk & 0x07) << 0) /* WRTORD */ + ); + } + + /* + * DDR SDRAM Timing Configuration 2 (TIMING_CFG_2) + */ + { + /* ADD_LAT: Additive latency */ + unsigned char add_lat_mclk; + /* CPO: CAS-to-preamble override */ + unsigned short cpo; + /* WR_LAT: Write latency */ + unsigned char wr_lat; + /* RD_TO_PRE: Read to precharge (tRTP) */ + unsigned char rd_to_pre; + /* + * WR_DATA_DELAY: Write command to write data + * strobe timing adjustment + */ + unsigned char wr_data_delay; + /* CKE_PLS: Minimum CKE pulse width (tCKE) */ + unsigned char cke_pls; + /* Window for four activates (tFAW) */ + unsigned short four_act; + + /* FIXME add check that this must be less than acttorw_mclk */ + add_lat_mclk = additive_latency; + cpo = popts->cpo_override; +#if 0 + wr_lat = popts->write_latency; +#endif + wr_lat = cas_latency + additive_latency - 1; + + rd_to_pre = picos_to_mclk(common_dimm->tRTP_ps); + 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 = (0 + | ((add_lat_mclk & 0x7) << 28) /* ADD_LAT */ + | ((cpo & 0x1f) << 23) /* CPO */ + | ((wr_lat & 0x7) << 19) /* WR_LAT */ + | ((rd_to_pre & 0x7) << 13) /* RD_TO_PRE */ + | ((wr_data_delay & 0x7) << 10) /* WR_DATA_DELAY */ + | ((cke_pls & 0x7) << 6) /* CKE_PLS */ + | ((four_act & 0x1f) << 0) /* FOUR_ACT */ + ); + } + + /* + * DDR SDRAM control configuration (DDR_SDRAM_CFG) + */ + { + /* MEM_EN: DDR SDRAM interface logic enable */ + unsigned int mem_en; + /* SREN: Self refresh enable (during sleep) */ + unsigned int sren; + unsigned int ecc_en; /* ECC_EN: ECC enable. */ + unsigned int rd_en; /* RD_EN: Registered DIMM enable */ + unsigned int sdram_type; /* SDRAM_TYPE: Type of SDRAM */ + /* DYN_PWR: Dynamic power management mode */ + unsigned int dyn_pwr; + unsigned int dbw; /* DBW: DRAM dta bus width */ + unsigned int eight_be; /* 8_BE: 8-beat burst enable */ + unsigned int ncap; /* NCAP: Non-concurrent auto-precharge */ + unsigned int threeT_en; /* 3T_EN: Enable 3T timing */ + unsigned int twoT_en; /* 2T_EN: Enable 2T timing */ + /* BA_INTLV_CTL: Bank (chip select) interleaving control */ + unsigned int ba_intlv_ctl; + unsigned int x32_en; /* X32_EN: x32 enable */ + unsigned int pchb8; /* PCHB8: precharge bit 8 enable */ + unsigned int hse; /* HSE: Global half strength override */ + /* MEM_HALT: DDR memory controller halt */ + unsigned int mem_halt; + unsigned int bi; /* BI: Bypass initialization */ + + mem_en = 1; + sren = popts->self_refresh_in_sleep; + if (common_dimm->all_DIMMs_ECC_capable) { + /* + * Allow setting of ECC only if all DIMMs are ECC. + */ + ecc_en = popts->ECC_mode; + } else { + ecc_en = 0; + } + +// rd_en = popts->registered_dimm_en; + + rd_en = (common_dimm->all_DIMMs_registered + && !common_dimm->all_DIMMs_unbuffered); + + sdram_type = 3; /* 3 = DDR2 */ + dyn_pwr = popts->dynamic_power; + dbw = popts->data_bus_width; + eight_be = 0; /* always 0 for DDR2 */ + ncap = 0; + threeT_en = 0; /* FIXME: make this configurable */ + twoT_en = popts->twoT_en; + ba_intlv_ctl = popts->ba_intlv_ctl; + x32_en = 0; + pchb8 = 0; + hse = popts->half_strength_driver_enable; + mem_halt = 0; + bi = 0; + + ddr->ddr_sdram_cfg = (0 + | ((mem_en & 0x1) << 31) /* MEM_EN */ + | ((sren & 0x1) << 30) /* SREN */ + | ((ecc_en & 0x1) << 29) /* ECC_EN */ + | ((rd_en & 0x1) << 28) /* RD_EN */ + | ((sdram_type & 0x7) << 24) /* SDRAM_TYPE*/ + | ((dyn_pwr & 0x1) << 21) /* DYN_PWR */ + | ((dbw & 0x3) << 19) /* DBW */ + | ((eight_be & 0x1) << 18) /* 8_BE */ + | ((ncap & 0x1) << 17) /* NCAP */ + | ((threeT_en & 0x1) << 16) /* 3T_EN */ + | ((twoT_en & 0x1) << 15) /* 2T_EN */ + | ((ba_intlv_ctl & 0x7F) << 8) /*BA_INTLV_CTL*/ + | ((x32_en & 0x1) << 5) /* x32_EN */ + | ((pchb8 & 0x1) << 4) /* PCHB8 */ + | ((hse & 0x1) << 3) /* HSE */ + | ((mem_halt & 0x1) << 1) /* MEM_HALT */ + | ((bi & 0x1) << 0) /* BI */ + ); + } + + /* + * DDR SDRAM control configuration 2 (DDR_SDRAM_CFG_2) + */ + { + unsigned int frc_sr; /* FRC_SR: Force self refresh */ + unsigned int sr_ie; /* SR_IE: Self-refresh interrupt enable */ + unsigned int dll_rst_dis; /* DLL_RST_DIS: DLL reset disable */ + unsigned int dqs_cfg; /* DQS_CFG: DQS configuration */ + unsigned int odt_cfg; /* ODT_CFG: ODT configuration */ + unsigned int num_pr; /* NUM_PR: Number of posted refreshes */ + unsigned int obc_cfg; /* On-The-Fly Burst Chop Configuration */ + unsigned int ap_en; /* AP_EN: Address Parity Enable */ + unsigned int d_init; /* D_INIT: DRAM data initialization */ + unsigned int rcw_en; /* Register Control Word Enable */ + unsigned int md_en; /* MD_EN: Mirrored DIMM Enable */ + + frc_sr = 0; + sr_ie = 0; +// dll_rst_dis = 0; + dll_rst_dis = 1; /* Make this configurable */ + dqs_cfg = popts->DQS_config; + if (popts->cs_local_opts[0].odt_rd_cfg + || popts->cs_local_opts[0].odt_wr_cfg) { + /* FIXME */ + odt_cfg = 2; + } else { + odt_cfg = 0; + } + + num_pr = 1; /* Make this configurable */ + + /* + * 8572 manual says + * {TIMING_CFG_1[PRETOACT] + * + [DDR_SDRAM_CFG_2[NUM_PR] + * * ({EXT_REFREC || REFREC} + 8 + 2)]} + * << DDR_SDRAM_INTERVAL[REFINT] + */ + + obc_cfg = 0; /* Make this configurable? */ + ap_en = 0; /* Make this configurable? */ + d_init = 1; /* Make this configurable? */ +// d_init = 0; /* Make this configurable? */ + rcw_en = 0; + md_en = 0; + + ddr->ddr_sdram_cfg_2 = (0 + | ((frc_sr & 0x1) << 31) /* FRC_SR */ + | ((sr_ie & 0x1) << 30) /* SR_IE */ + | ((dll_rst_dis & 0x1) << 29) /* DLL_RST_DIS */ + | ((dqs_cfg & 0x3) << 26) /* DQS_CFG */ + | ((odt_cfg & 0x3) << 21) /* ODT_CFG */ + | ((num_pr & 0xf) << 12) /* NUM_PR */ + | ((obc_cfg & 0x1) << 6) /* OBC_CFG */ + | ((ap_en & 0x1) << 5) /* AP_EN */ + | ((d_init & 0x1) << 4) /* D_INIT */ + | ((rcw_en & 0x1) << 2) /* RCW_EN */ + | ((md_en & 0x1) << 0) /* MD_EN */ + ); + } + + /* + * DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) + */ + { + unsigned short esdmode; /* Extended SDRAM mode */ + unsigned short sdmode; /* SDRAM mode */ + + /* + * FIXME: This ought to be pre-calculated in a + * technology-specific routine, + * e.g. compute_DDR2_mode_register(), and then the + * sdmode and esdmode passed in as part of common_dimm. + */ + + /* + * Extended Mode Register + */ + { + unsigned int mrs; /* MRS: Mode Register Set */ + unsigned int outputs; /* Outputs: 0=Enabled, 1=Disabled */ + unsigned int rdqs_en; /* RDQS Enable; 0=no, 1=yes */ + unsigned int dqs_en; /* DQS# Enable; 0=enable, 1=disable */ + unsigned int ocd; /* OCD Operation: 0x0=OCD not supported, 0x7=OCD default state */ + unsigned int rtt; /* Rtt(nominal): 0 = Rtt disabled, 1=75 ohm, 2=150 ohm, 3=50 ohm */ + unsigned int al; /* Posted CAS# additive latency (AL) */ + unsigned int ods; /* ODS: Output Drive Strength; 0 = Full strength (18ohm), 1=Reduced strength (4ohm) */ + unsigned int dll_en; /* DLL Enable; 0=Enable (Normal), 1=Disable (Test/Debug) */ + + mrs = 0; /* XXX: should this be 1? old code had it as 0 */ + outputs = 0; /* XXX: make this configurable? */ + rdqs_en = 0; /* XXX: make this configurable */ + dqs_en = 0; /* XXX: make this configurable */ + ocd = 0; /* XXX: make this configurable */ +//8641 rtt = 2; /* XXX: make this configurable -- this needs to be calculated */ + rtt = 3; /* XXX: make this configurable -- this needs to be calculated */ + +#if 0 + { + unsigned int i; + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL/2; i++) { + if (popts->dimmslot[i].num_valid_cs && (popts->cs_local_opts[2*i].odt_rd_cfg || popts->cs_local_opts[2*i].odt_wr_cfg)) { + rtt = 2; /* XXX: old code sez if 667 MHz or higher, use 3 on 8572 */ + break; + } + } + } +#endif + + al = additive_latency; + ods = 0; /* XXX: make this configurable */ + dll_en = 0; /* XXX: make this configurable */ + + esdmode = (0 + | ((mrs & 0x3) << 14) + | ((outputs & 0x1) << 12) + | ((rdqs_en & 0x1) << 11) + | ((dqs_en & 0x1) << 10) + | ((ocd & 0x7) << 7) + | ((rtt & 0x2) << 5) /* rtt field is split */ + | ((al & 0x7) << 3) + | ((rtt & 0x1) << 2) /* rtt field is split */ + | ((ods & 0x1) << 1) + | ((dll_en & 0x1) << 0) + ); + } + + /* + * Mode Register (MR) + */ + { + unsigned int mr; /* Mode Register Definition */ + unsigned int pd; /* PD: Power-Down Mode */ + unsigned int wr; /* WR: Write Recovery */ + unsigned int dll_res; /* DLL: DLL Reset */ + unsigned int mode; /* Mode: Normal=0 or Test=1 */ + unsigned int caslat; /* CAS# latency */ + /* BT: Burst Type (0=Sequential, 1=Interleaved) */ + unsigned int bt; + unsigned int bl; /* BL: Burst Length */ + + const unsigned int mclk_ps + = get_memory_clk_period_ps(); + + /* FIXME: CHECKME */ + + mr = 0; + + /* + * 0 = Fast Exit (Normal) + * 1 = Slow Exit (Low Power) + * FIXME: make this configurable + */ + pd = 0; + +// wr = 2; /* XXX: hack */ + /* + * FIXME: doesn't match old code? + * FIXME: need to look at this further + */ +// wr = (common_dimm->tWR_ps + mclk_ps - 1) / mclk_ps - 1; + wr = (common_dimm->tWR_ps + mclk_ps - 1) / mclk_ps - 1; + + dll_res = 0; /* FIXME: make this configurable */ + mode = 0; /* FIXME: make this configurable */ + + caslat = cas_latency; + bt = 0; /* FIXME: make this configurable? */ + + switch (popts->burst_length) { + case 4: + bl = 2; + break; + case 8: + bl = 3; + break; + default: + printf("Error: invalid burst length of %u specified. Defaulting to 4 beats.\n", popts->burst_length); + bl = 2; + break; + } + + sdmode = (0 + | ((mr & 0x3) << 14) + | ((pd & 0x1) << 12) + | ((wr & 0x7) << 9) + | ((dll_res & 0x1) << 8) + | ((mode & 0x1) << 7) + | ((caslat & 0x7) << 4) + | ((bt & 0x1) << 3) + | ((bl & 0x7) << 0) + ); + } + + ddr->ddr_sdram_mode = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + } + + /* + * DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) + */ + { + unsigned short esdmode2; /* Extended SDRAM mode 2 */ + unsigned short esdmode3; /* Extended SDRAM mode 3 */ + + esdmode2 = 0; /* FIXME: for now OK */ + esdmode3 = 0; + + ddr->ddr_sdram_mode_2 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + } + + /* + * DDR SDRAM Interval Configuration (DDR_SDRAM_INTERVAL) + */ + { + unsigned int refint; /* REFINT: Refresh interval */ + unsigned int bstopre; /* BSTOPRE: Precharge interval */ + +// refint = picos_to_mclk(pdimm->refresh_rate_ns * 1000); + refint = 1755; /* FIXME */ + + bstopre = popts->bstopre; + + /* + * FIXME: refint field used 0x3FFF in earlier controllers + */ + ddr->ddr_sdram_interval = (0 + | ((refint & 0xFFFF) << 16) + | ((bstopre & 0x3FFF) << 0) + ); + } + + /* + * DDR SDRAM Data Initialization (DDR_DATA_INIT) + */ + { + unsigned int init_value; /* Initialization value */ + + init_value = 0xDEADBEEF; /* FIXME: should be configurable */ + ddr->ddr_data_init = init_value; + } + + /* + * DDR SDRAM Clock Control (DDR_SDRAM_CLK_CNTL) + */ + { + unsigned int clk_adjust; /* Clock adjust */ + + clk_adjust = popts->clk_adjust; + ddr->ddr_sdram_clk_cntl = (clk_adjust & 0xF) << 23; + } + + /* + * DDR Initialization Address (DDR_INIT_ADDR) + */ + { + unsigned int init_addr; /* Initialization address */ + + init_addr = 0; + ddr->ddr_init_addr = init_addr; + } + + /* + * DDR Initialization Address (DDR_INIT_EXT_ADDR) + */ + { + unsigned int uia; /* Use initialization address */ + unsigned int init_ext_addr; /* Initialization address */ + + uia = 0; + init_ext_addr = 0; + + ddr->ddr_init_ext_addr = (0 + | ((uia & 0x1) << 31) + | (init_ext_addr & 0xF) + ); + } + + /* + * DDR SDRAM Timing Configuration 4 (TIMING_CFG_4) + */ + { + /* Read-to-write turnaround for same chip select */ + unsigned int rwt; + /* Write-to-read turnaround for same chip select */ + unsigned int wrt; + /* Read-to-read turnaround for same chip select */ + unsigned int rrt; + /* Write-to-write turnaround for same chip select */ + unsigned int wwt; + /* DDR SDRAM DLL Lock Time */ + unsigned int dll_lock; + + rwt = 0; + wrt = 0; + rrt = 0; + wwt = 0; + dll_lock = 0; + + ddr->timing_cfg_4 = (0 + | ((rwt & 0xf) << 28) + | ((wrt & 0xf) << 24) + | ((rrt & 0xf) << 20) + | ((wwt & 0xf) << 16) + | (dll_lock & 0x3) + ); + } + + /* + * DDR SDRAM Timing Configuration 5 (TIMING_CFG_5) + */ + { + unsigned int rodt_on; /* RODT_ON: Read to ODT on */ + unsigned int rodt_off; /* RODT_OFF: Read to ODT off */ + unsigned int wodt_on; /* WODT_ON: Write to ODT on */ + unsigned int wodt_off; /* WODT_OFF: Write to ODT off */ + + rodt_on = 0; + rodt_off = 0; + wodt_on = 0; + wodt_off = 0; + + ddr->timing_cfg_5 = (0 + | ((rodt_on & 0xf) << 24) + | ((rodt_off & 0xf) << 20) + | ((wodt_on & 0xf) << 12) + | ((wodt_off & 0xf) << 8) + ); + } + + /* + * DDR ZQ Calibration Control (DDR_ZQ_CNTL) + */ + { + unsigned int zq_en; /* ZE_EN: ZQ Calibration Enable */ + unsigned int zqinit; /* POR ZQ Calibration Time (tZQinit) */ + /* Normal Operation Full Calibration Time (tZQoper) */ + unsigned int zqoper; + /* ZQCS: Normal Operation Short Calibration Time (tZQCS) */ + unsigned int zqcs; + + zq_en = 0; + zqinit = 0; + zqoper = 0; + zqcs = 0; + + ddr->ddr_zq_cntl = (0 + | ((zq_en & 0x1) << 31) + | ((zqinit & 0xF) << 24) + | ((zqoper & 0xF) << 16) + | ((zqcs & 0xF) << 8) + ); + } + + /* + * DDR Write Leveling Control (DDR_WRLVL_CNTL) + */ + { + /* WRLVL_EN: Write Leveling Enable */ + unsigned int wrlvl_en; + /* + * First DQS pulse rising edge after margining mode + * is programmed (tWL_MRD) + */ + unsigned int wrlvl_mrd; + /* ODT delay after margining mode is programmed (tWL_ODTEN) */ + unsigned int wrlvl_odten; + /* + * DQS/DQS_ delay after margining mode is programmed + * (tWL_DQSEN) + */ + unsigned int wrlvl_dqsen; + /* WRLVL_SMPL: Write leveling sample time */ + unsigned int wrlvl_smpl; + /* WRLVL_WLR: Write leveling repeition time */ + unsigned int wrlvl_wlr; + /* WRLVL_START: Write leveling start time */ + unsigned int wrlvl_start; + + wrlvl_en = 0; + wrlvl_mrd = 0; + wrlvl_odten = 0; + wrlvl_dqsen = 0; + wrlvl_smpl = 0; + wrlvl_wlr = 0; + wrlvl_start = 0; + + ddr->ddr_wrlvl_cntl = (0 + | ((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 & 0xF) << 0) + ); + } + + /* + * DDR Pre-Drive Conditioning Control (DDR_PD_CNTL) + */ + { + /* Termination value during pre-drive conditioning */ + unsigned int tvpd; + unsigned int pd_en; /* Pre-Drive Conditioning Enable */ + unsigned int pdar; /* Pre-Drive After Read */ + unsigned int pdaw; /* Pre-Drive After Write */ + unsigned int pd_on; /* Pre-Drive Conditioning On */ + unsigned int pd_off; /* Pre-Drive Conditioning Off */ + + pd_en = 0; + tvpd = 0; + pdar = 0; + pdaw = 0; + pd_on = 0; + pd_off = 0; + + ddr->ddr_pd_cntl = (0 + | ((pd_en & 0x1) << 31) + | ((tvpd & 0x7) << 28) + | ((pdar & 0x7F) << 20) + | ((pdaw & 0x7F) << 12) + | ((pd_on & 0x1F) << 6) + | ((pd_off & 0x1F) << 0) + ); + } + + /* + * DDR Self Refresh Counter (DDR_SR_CNTR) + */ + { + unsigned int sr_it; /* Self Refresh Idle Threshold */ + + sr_it = 0; + ddr->ddr_sr_cntr = (sr_it & 0xF) << 16; + } + + /* + * DDR SDRAM Register Control Word 1 (DDR_SDRAM_RCW_1) + */ + { + unsigned int rcw0; /* RCW0: Register Control Word 0 */ + unsigned int rcw1; /* RCW1: Register Control Word 1 */ + unsigned int rcw2; /* RCW2: Register Control Word 2 */ + unsigned int rcw3; /* RCW3: Register Control Word 3 */ + unsigned int rcw4; /* RCW4: Register Control Word 4 */ + unsigned int rcw5; /* RCW5: Register Control Word 5 */ + unsigned int rcw6; /* RCW6: Register Control Word 6 */ + unsigned int rcw7; /* RCW7: Register Control Word 7 */ + + rcw0 = 0; + rcw1 = 0; + rcw2 = 0; + rcw3 = 0; + rcw4 = 0; + rcw5 = 0; + rcw6 = 0; + rcw7 = 0; + + ddr->ddr_sdram_rcw_1 = (0 + | ((rcw0 & 0xF) << 28) + | ((rcw1 & 0xF) << 24) + | ((rcw2 & 0xF) << 20) + | ((rcw3 & 0xF) << 16) + | ((rcw4 & 0xF) << 12) + | ((rcw5 & 0xF) << 8) + | ((rcw6 & 0xF) << 4) + | ((rcw7 & 0xF) << 0) + ); + } + + /* + * DDR SDRAM Register Control Word 2 (DDR_SDRAM_RCW_2) + */ + { + unsigned int rcw8; /* RCW0: Register Control Word 8 */ + unsigned int rcw9; /* RCW1: Register Control Word 9 */ + unsigned int rcw10; /* RCW2: Register Control Word 10 */ + unsigned int rcw11; /* RCW3: Register Control Word 11 */ + unsigned int rcw12; /* RCW4: Register Control Word 12 */ + unsigned int rcw13; /* RCW5: Register Control Word 13 */ + unsigned int rcw14; /* RCW6: Register Control Word 14 */ + unsigned int rcw15; /* RCW7: Register Control Word 15 */ + + rcw8 = 0; + rcw9 = 0; + rcw10 = 0; + rcw11 = 0; + rcw12 = 0; + rcw13 = 0; + rcw14 = 0; + rcw15 = 0; + + ddr->ddr_sdram_rcw_2 = (0 + | ((rcw8 & 0xF) << 28) + | ((rcw9 & 0xF) << 24) + | ((rcw10 & 0xF) << 20) + | ((rcw11 & 0xF) << 16) + | ((rcw12 & 0xF) << 12) + | ((rcw13 & 0xF) << 8) + | ((rcw14 & 0xF) << 4) + | ((rcw15 & 0xF) << 0) + ); + } + + return 0; +} + +phys_size_t +fsl_ddr_sdram_compute(fsl_ddr_sdram_info_t *pinfo, unsigned int start_step) +{ + unsigned int i; + unsigned int j; + unsigned int all_controllers_memctl_interleaving = 0; + unsigned int all_controllers_rank_interleaving = 0; + phys_size_t cur_memsize = 0; + phys_size_t total_memory = 0; + + /* data bus width capacity adjust shift amount */ + unsigned int dbw_capacity_adjust[CONFIG_NUM_DDR_CONTROLLERS]; + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + dbw_capacity_adjust[i] = 0; + } + + debug("starting at step %u (%s)\n", + start_step, step_to_string(__ilog2(start_step))); + + switch (start_step) { + case STEP_GET_SPD: + /* + * STEP 1: Gather all DIMM SPD data + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + fsl_ddr_sdram_get_spd(pinfo->spd_installed_dimms[i], + i); + } + + case STEP_COMPUTE_DIMM_PARMS: + /* + * STEP 2: Compute DIMM parameters from SPD data + */ + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + unsigned int retval; + + retval = compute_dimm_parameters( + &(pinfo->spd_installed_dimms[i][j]), + &(pinfo->dimm_params[i][j]), + i); + if (retval) { + if (retval == 2) { + printf("Error: compute_dimm_parameters non-zero returned FATAL value for memctl=%u dimm=%u\n", i, j); + return 0; + } + debug("Warning: compute_dimm_parameters non-zero return value for memctl=%u dimm=%u\n", i, j); + } + } + } + + case STEP_COMPUTE_COMMON_PARMS: + /* + * STEP 3: Compute a common set of timing parameters + * suitable for all of the DIMMs on each memory controller + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + debug("Computing lowest common DIMM parameters for memctl=%u\n", i); + compute_lowest_common_dimm_parameters( + pinfo->dimm_params[i], + &pinfo->common_timing_params[i], + CONFIG_DIMM_SLOTS_PER_CTLR); + } + + case STEP_GATHER_OPTS: + /* + * STEP 4: Gather configuration requirements from user + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + debug("Reloading memory controller configuration options for memctl=%u\n", i); + /* + * This "reloads" the memory controller options + * to defaults. If the user "edits" an option, + * next_step points to the step after this, + * which is currently STEP_ASSIGN_ADDRESSES. + */ + populate_memctl_options( + &pinfo->common_timing_params[i], + &pinfo->memctl_options[i], + i); + } + + case STEP_ASSIGN_ADDRESSES: + /* + * STEP 5: Assign addresses to chip selects + * FIXME + */ + + /* + * If a reduced data width is requested, but the SPD + * specifies a physically wider device, adjust the + * computed dimm capacities accordingly before + * assigning addresses. + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + unsigned int found = 0; + + switch (pinfo->memctl_options[i].data_bus_width) { + case 2: + /* 16-bit */ + printf("can't handle 16-bit mode yet\n"); + break; + + case 1: + /* 32-bit */ + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + if (pinfo->dimm_params[i][j].n_ranks + && (pinfo->dimm_params[i][j].data_width == 72 + || pinfo->dimm_params[i][j].data_width == 64)) { + /* + * FIXME: can't really do + * it like this because + * this just further + * reduces the memory + */ + found = 1; + break; + } + } + if (found) { + dbw_capacity_adjust[i] = 1; + /* FIXME: 2 for 16-bit bus? */ + } + break; + + case 0: + /* 64-bit */ + break; + + default: + printf("unexpected data bus width specified controller %u\n", i); + return 0; + break; + } + } + + /* + * Check if all controllers are configured for memory + * controller interleaving. + */ + j = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (pinfo->memctl_options[i].memctl_interleaving) { + j++; + } + } + if (j == 2) { + all_controllers_memctl_interleaving = 1; + } + + /* + * Check that all controllers are rank interleaving. + */ + j = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + if (pinfo->memctl_options[i].ba_intlv_ctl) { + j++; + } + } + if (j == 2) { + all_controllers_rank_interleaving = 1; + } + + if (all_controllers_memctl_interleaving) { + phys_addr_t addr; + + /* + * If interleaving between memory controllers, + * make each controller start at a base address + * of 0. + * + * Also, if bank interleaving (chip select + * interleaving) is enabled on each memory + * controller, CS0 needs to be programmed to + * cover the entire memory range on that memory + * controller + * + * Bank interleaving also implies that each + * addressed chip select is identical in size. + */ + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + addr = 0; +/* pinfo->common_timing_params[i].base_address = addr; */ + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + pinfo->dimm_params[i][j].base_address = addr; + addr += (phys_addr_t)(pinfo->dimm_params[i][j].dimm_capacity >> dbw_capacity_adjust[i]); + } + } + } else { + /* + * Simple linear assignment if memory + * controllers are not interleaved. + */ + cur_memsize = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + phys_size_t total_mem_per_memctl = 0; + pinfo->common_timing_params[i].base_address = (phys_addr_t)cur_memsize; + for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) { + /* + * Compute DIMM base addresses. + */ + pinfo->dimm_params[i][j].base_address = (phys_addr_t)cur_memsize; + cur_memsize+= pinfo->dimm_params[i][j].dimm_capacity >> dbw_capacity_adjust[i]; + total_mem_per_memctl += pinfo->dimm_params[i][j].dimm_capacity >> dbw_capacity_adjust[i]; + } + pinfo->common_timing_params[i].total_mem = total_mem_per_memctl; + } + } + + + case STEP_COMPUTE_REGS: + /* + * STEP 6: compute controller register values + */ + debug("FSL Memory controller configuration register computation\n"); + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + unsigned int retval; + + if (pinfo->common_timing_params[i].ndimms_present == 0) { + reset_fsl_memctl_config_regs( + &pinfo->fsl_ddr_config_reg[i]); + continue; + } + + compute_fsl_memctl_config_regs( + &pinfo->memctl_options[i], + &pinfo->fsl_ddr_config_reg[i], + &pinfo->common_timing_params[i], + pinfo->dimm_params[i], + dbw_capacity_adjust[i]); + + retval = check_fsl_memctl_config_regs( + &pinfo->fsl_ddr_config_reg[i]); + if (retval) { + printf("check_fsl_memctl_config_regs result = %u\n", retval); + } + } + + default: + break; + } + + + /* + * Compute the total amount of memory. + */ + + /* + * If bank interleaving but NOT memory controller interleaving + * CS_BNDS describe the quantity of memory on each memory + * controller, so the total is the sum across. + */ + if (!all_controllers_memctl_interleaving + && all_controllers_rank_interleaving) { + total_memory = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + total_memory += pinfo->common_timing_params[i].total_mem; + } + + } else { + /* + * Compute the amount of memory available just by + * looking for the highest valid CSn_BNDS value. + * This allows us to also experiment with using + * only CS0 when using dual-rank DIMMs. + */ + unsigned int max_end = 0; + + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + for (j = 0; j < CONFIG_CHIP_SELECTS_PER_CTRL; j++) { + if (pinfo->fsl_ddr_config_reg[i].cs[j].config & 0x80000000) { + unsigned int end = pinfo->fsl_ddr_config_reg[i].cs[j].bnds & 0xFFF; + if (end > max_end) { + max_end = end; + } + } + } + } + + /* + * FIXME: do we need to reduce this if reduced memory width? + */ + total_memory = 1 + (((unsigned long long)max_end << 24ULL) | 0xFFFFFFULL); + } + + return total_memory; +} + +/* + * fsl_ddr_sdram() -- this is the main function to be called by + * initdram() in the board file. + * + * It returns amount of memory configured in bytes. + */ +phys_size_t fsl_ddr_sdram(void) +{ + unsigned int i; + unsigned int memctl_interleaved; + phys_size_t total_memory; + fsl_ddr_sdram_info_t info; + + /* + * Reset info structure. + */ + memset(&info, 0, sizeof(fsl_ddr_sdram_info_t)); + + /* + * Compute it once normally. + */ + total_memory = fsl_ddr_sdram_compute(&info, STEP_GET_SPD); + + /* + * Check for memory controller interleaving. + */ + memctl_interleaved = 0; + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + memctl_interleaved += + info.memctl_options[i].memctl_interleaving; + } + + if (memctl_interleaved) { + if (memctl_interleaved == CONFIG_NUM_DDR_CONTROLLERS) { + debug("memctl interleaving\n"); + /* + * Change the meaning of memctl_interleaved + * to be "boolean". + */ + memctl_interleaved = 1; + } else { + printf("Error: memctl interleaving not properly configured on all controllers\n"); + while (1); + } + } + + /* + * Program configuration registers. + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + debug("Programming controller %u\n", i); + if (info.common_timing_params[i].ndimms_present == 0) { + debug("No dimms present on controller %u; skipping programming\n", i); + continue; + } + + fsl_ddr_sdram_set_memctl_regs(&(info.fsl_ddr_config_reg[i]), + i); + +#ifdef DEBUG + fsl_ddr_sdram_dump_memctl_regs(i); +#endif + } + + if (memctl_interleaved) { + const unsigned int ctrl_num = 0; + + /* + * Only set LAWBAR1 if memory controller interleaving is on. + */ + fsl_ddr_sdram_set_lawbar(&info.common_timing_params[0], + memctl_interleaved, ctrl_num); + } else { + /* + * Memory controller interleaving is NOT on; + * set each lawbar individually. + */ + for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) { + fsl_ddr_sdram_set_lawbar(&info.common_timing_params[i], + 0, i); + } + } + +#ifdef CONFIG_PHYS_64BIT + if (total_memory > 0xFFFFFFFFULL) { + printf("XXX: 4GB+ memory not supported by this version of u-boot, capping to 0xFFFFFFFF...\n"); + /* + * FIXME: Does capping to 0xFFFFFFFF work? + * FIXME: Maybe the cap should be a power of 2 value? + * FIXME: Proper support requires u-boot to be FIXED + */ + total_memory = 0xFFFFFFFFULL; + } +#endif + + debug("total_memory = %llu\n", (u64)total_memory); + + return total_memory; +} diff --git a/cpu/mpc8xxx/fsl_ddr_sdram.h b/cpu/mpc8xxx/fsl_ddr_sdram.h new file mode 100644 index 0000000..0ac44a7 --- /dev/null +++ b/cpu/mpc8xxx/fsl_ddr_sdram.h @@ -0,0 +1,87 @@ +/* + * 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 FSL_DDR_MEMCTL_H +#define FSL_DDR_MEMCTL_H + +/* + * Pick a basic DDR Technology. + */ +#include + +#if defined(CONFIG_FSL_DDR1) +#define FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR (1) +typedef ddr1_spd_eeprom_t generic_spd_eeprom_t; +#elif defined(CONFIG_FSL_DDR2) +#define FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR (3) +typedef ddr2_spd_eeprom_t generic_spd_eeprom_t; +#elif defined(CONFIG_FSL_DDR3) +#define FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR (3) /* FIXME */ +typedef ddr3_spd_eeprom_t generic_spd_eeprom_t; +#endif + +/* FIXME: Trying to make this be generic... */ +#include "ddr2_dimm_params.h" + +#include "memctl_options.h" +#include "common_timing_params.h" +#include "fsl_memctrl.h" + +phys_size_t fsl_ddr_sdram(void); + +unsigned int mclk_to_picos(unsigned int mclk); +unsigned int get_memory_clk_period_ps(void); + +/* + * Data Structures + * + * All data structures have to be on the stack + */ + + +typedef struct { + generic_spd_eeprom_t spd_installed_dimms[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR]; + dimm_params_t dimm_params[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR]; + memctl_options_t memctl_options[CONFIG_NUM_DDR_CONTROLLERS]; + common_timing_params_t common_timing_params[CONFIG_NUM_DDR_CONTROLLERS]; + fsl_memctl_config_regs_t fsl_ddr_config_reg[CONFIG_NUM_DDR_CONTROLLERS]; +} fsl_ddr_sdram_info_t; + + +extern phys_size_t +fsl_ddr_sdram_compute(fsl_ddr_sdram_info_t *pinfo, + unsigned int start_step); + + +extern const char * step_to_string(unsigned int step); +/* + * Compute steps + */ +#define STEP_GET_SPD (1 << 0) +#define STEP_COMPUTE_DIMM_PARMS (1 << 1) +#define STEP_COMPUTE_COMMON_PARMS (1 << 2) +#define STEP_GATHER_OPTS (1 << 3) +#define STEP_ASSIGN_ADDRESSES (1 << 4) +#define STEP_COMPUTE_REGS (1 << 5) +#define STEP_PROGRAM_REGS (1 << 6) +#define STEP_ALL 0xFFF + +/* + * Bind the main DDR setup driver's generic names + * to this specific DDR technology. + */ + +static __inline__ int +compute_dimm_parameters(const generic_spd_eeprom_t *spd, + dimm_params_t *pdimm, + unsigned int dimm_number) +{ + /* FIXME: Squeek by callong this generic yet? */ + return ddr2_compute_dimm_parameters(spd, pdimm, dimm_number); +} +#endif diff --git a/cpu/mpc8xxx/fsl_memctrl.h b/cpu/mpc8xxx/fsl_memctrl.h new file mode 100644 index 0000000..ec97dab --- /dev/null +++ b/cpu/mpc8xxx/fsl_memctrl.h @@ -0,0 +1,48 @@ +/* + * 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 FSL_MEMCTRL_H +#define FSL_MEMCTRL_H + +/* + * Record of register values computed + */ +typedef struct fsl_memctl_config_regs_s { + struct { + unsigned int bnds; + unsigned int config; + unsigned int config_2; + } cs[CONFIG_CHIP_SELECTS_PER_CTRL]; + unsigned int timing_cfg_3; + unsigned int timing_cfg_0; + unsigned int timing_cfg_1; + unsigned int timing_cfg_2; + unsigned int ddr_sdram_cfg; + unsigned int ddr_sdram_cfg_2; + unsigned int ddr_sdram_mode; + unsigned int ddr_sdram_mode_2; + unsigned int ddr_sdram_interval; + unsigned int ddr_data_init; + unsigned int ddr_sdram_clk_cntl; + unsigned int ddr_init_addr; + unsigned int ddr_init_ext_addr; + unsigned int timing_cfg_4; + unsigned int timing_cfg_5; + unsigned int ddr_zq_cntl; + unsigned int ddr_wrlvl_cntl; + unsigned int ddr_pd_cntl; + unsigned int ddr_sr_cntr; + unsigned int ddr_sdram_rcw_1; + unsigned int ddr_sdram_rcw_2; +} fsl_memctl_config_regs_t; + + +void reset_fsl_memctl_config_regs(fsl_memctl_config_regs_t *ddr); +unsigned int check_fsl_memctl_config_regs(const fsl_memctl_config_regs_t *ddr); + +#endif /* FSL_MEMCTRL_H */ diff --git a/cpu/mpc8xxx/memctl_options.h b/cpu/mpc8xxx/memctl_options.h new file mode 100644 index 0000000..08779f7 --- /dev/null +++ b/cpu/mpc8xxx/memctl_options.h @@ -0,0 +1,100 @@ +/* + * 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 MEMCTL_OPTIONS_H +#define MEMCTL_OPTIONS_H + +// #include "ddr2_dimm_params.h" +#include "common_timing_params.h" + +typedef struct memctl_options_partial_s { + unsigned int all_DIMMs_ECC_capable; + unsigned int all_DIMMs_tCKmax_ps; + unsigned int all_DIMMs_burst_lengths_bitmask; + unsigned int all_DIMMs_registered; + unsigned int all_DIMMs_unbuffered; +// unsigned int lowest_common_SPD_caslat; + unsigned int all_DIMMs_minimum_tRCD_ps; +} memctl_options_partial_t; + + +/* + * Generalized parameters for memory controller configuration, + * might be a little specific to the FSL memory controller + */ +typedef struct memctl_options_s { + /* + * Memory organization parameters + * + * if DIMM is present in the system + * where DIMMs are with respect to chip select + * where chip selects are with respect to memory boundaries + */ + unsigned int registered_dimm_en; /* use registered DIMM support */ + + /* + * Options local to a Chip Select + */ + struct cs_local_opts_s { + unsigned int auto_precharge; + unsigned int odt_rd_cfg; + unsigned int odt_wr_cfg; + } cs_local_opts[CONFIG_CHIP_SELECTS_PER_CTRL]; + + /* + * Special configurations for chip select + */ + unsigned int memctl_interleaving; + unsigned int memctl_interleaving_mode; + unsigned int ba_intlv_ctl; + + /* + * Operational mode parameters + */ + unsigned int sdram_type; /* 2 = DDR1, 3 = DDR2, 6 = LPDDR1, 7 = DDR3 */ + unsigned int ECC_mode; /* Use ECC? */ + /* Initialize ECC using memory controller? */ + unsigned int ECC_init_using_memctl; + unsigned int DQS_config; /* Use DQS? maybe only with DDR2? */ + /* SREN - self-refresh during sleep */ + unsigned int self_refresh_in_sleep; + unsigned int dynamic_power; /* DYN_PWR */ + /* memory data width to use (16-bit, 32-bit, 64-bit) */ + unsigned int data_bus_width; + unsigned int burst_length; /* 4, 8 */ + + /* + * Global Timing Parameters + */ + unsigned int cas_latency_override; + unsigned int cas_latency_override_value; + unsigned int use_derated_caslat; + unsigned int additive_latency_override; + unsigned int additive_latency_override_value; + + /* + * this should be computed in the fsl ddr code; for DDR2 this is + * always read latency -1 + * unsigned int write_latency; + */ + unsigned int clk_adjust; /* */ + unsigned int cpo_override; + unsigned int write_data_delay; /* DQS adjust */ + unsigned int half_strength_driver_enable; + unsigned int twoT_en; + unsigned int threeT_en; + unsigned int bstopre; + unsigned int tCKE_clock_pulse_width_ps; /* tCKE */ + unsigned int tFAW_window_four_activates_ps; /* tFAW -- FOUR_ACT */ +} memctl_options_t; + +unsigned int populate_memctl_options( + const common_timing_params_t *ppartial_opts, + memctl_options_t *popts, + unsigned int ctrl_num); +#endif -- 1.5.5.1