* [PATCH 1/8] ppc 8xxx: DDR headers
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
@ 2013-05-31 16:53 ` Renaud Barbier
2013-05-31 16:53 ` [PATCH 2/8] ppc 8xxx: memory controller register manipulation functions Renaud Barbier
` (15 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-05-31 16:53 UTC (permalink / raw)
To: barebox
These headers are imported from U-Boot as is.
This patch contains data structures related to DIMM characteristics
and functions prototypes.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/common_timing_params.h | 56 +++++++++++++++
arch/ppc/ddr-8xxx/ddr.h | 105 ++++++++++++++++++++++++++++
arch/ppc/include/asm/fsl_ddr_dimm_params.h | 99 ++++++++++++++++++++++++++
3 files changed, 260 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/common_timing_params.h
create mode 100644 arch/ppc/ddr-8xxx/ddr.h
create mode 100644 arch/ppc/include/asm/fsl_ddr_dimm_params.h
diff --git a/arch/ppc/ddr-8xxx/common_timing_params.h b/arch/ppc/ddr-8xxx/common_timing_params.h
new file mode 100644
index 0000000..06706ed
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/common_timing_params.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+
+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_ps;
+
+ 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;
+
+ /* DDR3 RDIMM */
+ unsigned char rcw[16]; /* Register Control Word 0-15 */
+} common_timing_params_t;
+
+#endif
diff --git a/arch/ppc/ddr-8xxx/ddr.h b/arch/ppc/ddr-8xxx/ddr.h
new file mode 100644
index 0000000..4dd55fc
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef FSL_DDR_MAIN_H
+#define FSL_DDR_MAIN_H
+
+#include <asm/fsl_ddr_sdram.h>
+#include <asm/fsl_ddr_dimm_params.h>
+
+#include "common_timing_params.h"
+
+#if defined(CONFIG_DDR_SPD) || defined(CONFIG_SPD_EEPROM)
+/*
+ * 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)
+{
+ return ddr_compute_dimm_parameters(spd, pdimm, dimm_number);
+}
+#endif
+
+/*
+ * Data Structures
+ *
+ * All data structures have to be on the stack
+ */
+#define CONFIG_SYS_NUM_DDR_CTLRS CONFIG_NUM_DDR_CONTROLLERS
+#define CONFIG_SYS_DIMM_SLOTS_PER_CTLR CONFIG_DIMM_SLOTS_PER_CTLR
+
+typedef struct {
+ generic_spd_eeprom_t
+ spd_installed_dimms[CONFIG_SYS_NUM_DDR_CTLRS][CONFIG_SYS_DIMM_SLOTS_PER_CTLR];
+ struct dimm_params_s
+ dimm_params[CONFIG_SYS_NUM_DDR_CTLRS][CONFIG_SYS_DIMM_SLOTS_PER_CTLR];
+ memctl_options_t memctl_opts[CONFIG_SYS_NUM_DDR_CTLRS];
+ common_timing_params_t common_timing_params[CONFIG_SYS_NUM_DDR_CTLRS];
+ fsl_ddr_cfg_regs_t fsl_ddr_config_reg[CONFIG_SYS_NUM_DDR_CTLRS];
+} fsl_ddr_info_t;
+
+/* 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
+
+unsigned long long
+fsl_ddr_compute(fsl_ddr_info_t *pinfo, unsigned int start_step,
+ unsigned int size_only);
+
+const char *step_to_string(unsigned int step);
+
+unsigned int compute_fsl_memctl_config_regs(const memctl_options_t *popts,
+ fsl_ddr_cfg_regs_t *ddr,
+ const common_timing_params_t *common_dimm,
+ const dimm_params_t *dimm_parameters,
+ unsigned int dbw_capacity_adjust,
+ unsigned int size_only);
+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 populate_memctl_options(int all_DIMMs_registered,
+ memctl_options_t *popts,
+ dimm_params_t *pdimm,
+ unsigned int ctrl_num);
+void check_interleaving_options(fsl_ddr_info_t *pinfo);
+
+unsigned int mclk_to_picos(unsigned int mclk);
+unsigned int get_memory_clk_period_ps(void);
+unsigned int picos_to_mclk(unsigned int picos);
+void fsl_ddr_set_lawbar(
+ const common_timing_params_t *memctl_common_params,
+ unsigned int memctl_interleaved,
+ unsigned int ctrl_num);
+
+int fsl_ddr_interactive_env_var_exists(void);
+unsigned long long fsl_ddr_interactive(fsl_ddr_info_t *pinfo, int var_is_set);
+void fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd,
+ unsigned int ctrl_num);
+
+int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
+unsigned int check_fsl_memctl_config_regs(const fsl_ddr_cfg_regs_t *ddr);
+
+/* processor specific function */
+void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs,
+ unsigned int ctrl_num);
+
+/* board specific function */
+int fsl_ddr_get_dimm_params(dimm_params_t *pdimm,
+ unsigned int controller_number,
+ unsigned int dimm_number);
+#endif
diff --git a/arch/ppc/include/asm/fsl_ddr_dimm_params.h b/arch/ppc/include/asm/fsl_ddr_dimm_params.h
new file mode 100644
index 0000000..ffe4db8
--- /dev/null
+++ b/arch/ppc/include/asm/fsl_ddr_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
+
+#define EDC_DATA_PARITY 1
+#define EDC_ECC 2
+#define EDC_AC_PARITY 4
+
+/* 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 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;
+ /* mirrored DIMMs */
+ unsigned int mirrored_dimm; /* only for ddr3 */
+
+ /* DIMM timing parameters */
+
+ unsigned int mtb_ps; /* medium timebase ps, only for ddr3 */
+ unsigned int ftb_10th_ps; /* fine timebase, in 1/10 ps, only for ddr3 */
+ unsigned int tAA_ps; /* minimum CAS latency time, only for ddr3 */
+ unsigned int tFAW_ps; /* four active window delay, only for ddr3 */
+
+ /*
+ * 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_ps;
+
+ /* DDR3 doesn't need these as below */
+ 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 */
+
+ /* DDR3 RDIMM */
+ unsigned char rcw[16]; /* Register Control Word 0-15 */
+} dimm_params_t;
+
+extern unsigned int ddr_compute_dimm_parameters(
+ const generic_spd_eeprom_t *spd,
+ dimm_params_t *pdimm,
+ unsigned int dimm_number);
+
+#endif
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 2/8] ppc 8xxx: memory controller register manipulation functions
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
2013-05-31 16:53 ` [PATCH 1/8] ppc 8xxx: DDR headers Renaud Barbier
@ 2013-05-31 16:53 ` Renaud Barbier
2013-05-31 16:53 ` [PATCH 3/8] ppc 8xxx: dimm parameters calculation Renaud Barbier
` (14 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-05-31 16:53 UTC (permalink / raw)
To: barebox
This file is imported from U-Boot as is.
The functions in this file provide the 8xxx memory controller register
abstraction layer.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/ctrl_regs.c | 1645 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 1645 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/ctrl_regs.c
diff --git a/arch/ppc/ddr-8xxx/ctrl_regs.c b/arch/ppc/ddr-8xxx/ctrl_regs.c
new file mode 100644
index 0000000..26c42f7
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ctrl_regs.c
@@ -0,0 +1,1645 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * Generic driver for Freescale DDR/DDR2/DDR3 memory controller.
+ * Based on code from spd_sdram.c
+ * Author: James Yang [at freescale.com]
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+
+#include "ddr.h"
+
+#define _DDR_ADDR CONFIG_SYS_MPC8xxx_DDR_ADDR
+
+static u32 fsl_ddr_get_version(void)
+{
+ ccsr_ddr_t *ddr;
+ u32 ver_major_minor_errata;
+
+ ddr = (void *)_DDR_ADDR;
+ ver_major_minor_errata = (in_be32(&ddr->ip_rev1) & 0xFFFF) << 8;
+ ver_major_minor_errata |= (in_be32(&ddr->ip_rev2) & 0xFF00) >> 8;
+
+ return ver_major_minor_errata;
+}
+
+unsigned int picos_to_mclk(unsigned int picos);
+
+/*
+ * Determine Rtt value.
+ *
+ * This should likely be either board or controller specific.
+ *
+ * Rtt(nominal) - DDR2:
+ * 0 = Rtt disabled
+ * 1 = 75 ohm
+ * 2 = 150 ohm
+ * 3 = 50 ohm
+ * Rtt(nominal) - DDR3:
+ * 0 = Rtt disabled
+ * 1 = 60 ohm
+ * 2 = 120 ohm
+ * 3 = 40 ohm
+ * 4 = 20 ohm
+ * 5 = 30 ohm
+ *
+ * FIXME: Apparently 8641 needs a value of 2
+ * FIXME: Old code seys if 667 MHz or higher, use 3 on 8572
+ *
+ * FIXME: There was some effort down this line earlier:
+ *
+ * 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;
+ * break;
+ * }
+ * }
+ */
+static inline int fsl_ddr_get_rtt(void)
+{
+ int rtt;
+
+#if defined(CONFIG_FSL_DDR1)
+ rtt = 0;
+#elif defined(CONFIG_FSL_DDR2)
+ rtt = 3;
+#else
+ rtt = 0;
+#endif
+
+ return rtt;
+}
+
+/*
+ * compute the CAS write latency according to DDR3 spec
+ * CWL = 5 if tCK >= 2.5ns
+ * 6 if 2.5ns > tCK >= 1.875ns
+ * 7 if 1.875ns > tCK >= 1.5ns
+ * 8 if 1.5ns > tCK >= 1.25ns
+ * 9 if 1.25ns > tCK >= 1.07ns
+ * 10 if 1.07ns > tCK >= 0.935ns
+ * 11 if 0.935ns > tCK >= 0.833ns
+ * 12 if 0.833ns > tCK >= 0.75ns
+ */
+static inline unsigned int compute_cas_write_latency(void)
+{
+ unsigned int cwl;
+ const unsigned int mclk_ps = get_memory_clk_period_ps();
+
+ if (mclk_ps >= 2500)
+ cwl = 5;
+ else if (mclk_ps >= 1875)
+ cwl = 6;
+ else if (mclk_ps >= 1500)
+ cwl = 7;
+ else if (mclk_ps >= 1250)
+ cwl = 8;
+ else if (mclk_ps >= 1070)
+ cwl = 9;
+ else if (mclk_ps >= 935)
+ cwl = 10;
+ else if (mclk_ps >= 833)
+ cwl = 11;
+ else if (mclk_ps >= 750)
+ cwl = 12;
+ else {
+ cwl = 12;
+ printf("Warning: CWL is out of range\n");
+ }
+ return cwl;
+}
+
+/* Chip Select Configuration (CSn_CONFIG) */
+static void set_csn_config(int dimm_number, int i, fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const dimm_params_t *dimm_params)
+{
+ unsigned int cs_n_en = 0; /* Chip Select enable */
+ unsigned int intlv_en = 0; /* Memory controller interleave enable */
+ unsigned int intlv_ctl = 0; /* Interleaving control */
+ unsigned int ap_n_en = 0; /* Chip select n auto-precharge enable */
+ unsigned int odt_rd_cfg = 0; /* ODT for reads configuration */
+ unsigned int odt_wr_cfg = 0; /* ODT for writes configuration */
+ unsigned int ba_bits_cs_n = 0; /* Num of bank bits for SDRAM on CSn */
+ unsigned int row_bits_cs_n = 0; /* Num of row bits for SDRAM on CSn */
+ unsigned int col_bits_cs_n = 0; /* Num of ocl bits for SDRAM on CSn */
+ int go_config = 0;
+
+ /* Compute CS_CONFIG only for existing ranks of each DIMM. */
+ switch (i) {
+ case 0:
+ if (dimm_params[dimm_number].n_ranks > 0) {
+ go_config = 1;
+ /* These fields only available in CS0_CONFIG */
+ if (!popts->memctl_interleaving)
+ break;
+ switch (popts->memctl_interleaving_mode) {
+ case FSL_DDR_CACHE_LINE_INTERLEAVING:
+ case FSL_DDR_PAGE_INTERLEAVING:
+ case FSL_DDR_BANK_INTERLEAVING:
+ case FSL_DDR_SUPERBANK_INTERLEAVING:
+ intlv_en = popts->memctl_interleaving;
+ intlv_ctl = popts->memctl_interleaving_mode;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case 1:
+ if ((dimm_number == 0 && dimm_params[0].n_ranks > 1) || \
+ (dimm_number == 1 && dimm_params[1].n_ranks > 0))
+ go_config = 1;
+ break;
+ case 2:
+ if ((dimm_number == 0 && dimm_params[0].n_ranks > 2) || \
+ (dimm_number >= 1 && dimm_params[dimm_number].n_ranks > 0))
+ go_config = 1;
+ break;
+ case 3:
+ if ((dimm_number == 0 && dimm_params[0].n_ranks > 3) || \
+ (dimm_number == 1 && dimm_params[1].n_ranks > 1) || \
+ (dimm_number == 3 && dimm_params[3].n_ranks > 0))
+ go_config = 1;
+ break;
+ default:
+ break;
+ }
+ if (go_config) {
+ unsigned int n_banks_per_sdram_device;
+ cs_n_en = 1;
+ 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;
+ n_banks_per_sdram_device
+ = dimm_params[dimm_number].n_banks_per_sdram_device;
+ ba_bits_cs_n = __ilog2(n_banks_per_sdram_device) - 2;
+ row_bits_cs_n = dimm_params[dimm_number].n_row_addr - 12;
+ col_bits_cs_n = dimm_params[dimm_number].n_col_addr - 8;
+ }
+ ddr->cs[i].config = (0
+ | ((cs_n_en & 0x1) << 31)
+ | ((intlv_en & 0x3) << 29)
+ | ((intlv_ctl & 0xf) << 24)
+ | ((ap_n_en & 0x1) << 23)
+
+ /* XXX: some implementation only have 1 bit starting at left */
+ | ((odt_rd_cfg & 0x7) << 20)
+
+ /* XXX: 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)
+ );
+ debug("FSLDDR: cs[%d]_config = 0x%08x\n", i,ddr->cs[i].config);
+}
+
+/* Chip Select Configuration 2 (CSn_CONFIG_2) */
+/* FIXME: 8572 */
+static void set_csn_config_2(int i, fsl_ddr_cfg_regs_t *ddr)
+{
+ unsigned int pasr_cfg = 0; /* Partial array self refresh config */
+
+ ddr->cs[i].config_2 = ((pasr_cfg & 7) << 24);
+ debug("FSLDDR: cs[%d]_config_2 = 0x%08x\n", i, ddr->cs[i].config_2);
+}
+
+/* -3E = 667 CL5, -25 = CL6 800, -25E = CL5 800 */
+
+#if !defined(CONFIG_FSL_DDR1)
+static inline int avoid_odt_overlap(const dimm_params_t *dimm_params)
+{
+#if CONFIG_DIMM_SLOTS_PER_CTLR == 1
+ if (dimm_params[0].n_ranks == 4)
+ return 1;
+#endif
+
+#if CONFIG_DIMM_SLOTS_PER_CTLR == 2
+ if ((dimm_params[0].n_ranks == 2) &&
+ (dimm_params[1].n_ranks == 2))
+ return 1;
+
+#ifdef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE
+ if (dimm_params[0].n_ranks == 4)
+ return 1;
+#endif
+#endif
+ return 0;
+}
+
+/*
+ * 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.
+ */
+static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const dimm_params_t *dimm_params)
+{
+ unsigned char trwt_mclk = 0; /* Read-to-write turnaround */
+ unsigned char twrt_mclk = 0; /* Write-to-read turnaround */
+ /* 7.5 ns on -3E; 0 means WL - CL + BL/2 + 1 */
+ unsigned char trrt_mclk = 0; /* Read-to-read turnaround */
+ unsigned char twwt_mclk = 0; /* Write-to-write turnaround */
+
+ /* Active powerdown exit timing (tXARD and tXARDS). */
+ unsigned char act_pd_exit_mclk;
+ /* Precharge powerdown exit timing (tXP). */
+ unsigned char pre_pd_exit_mclk;
+ /* ODT powerdown exit timing (tAXPD). */
+ unsigned char taxpd_mclk;
+ /* Mode register set cycle time (tMRD). */
+ unsigned char tmrd_mclk;
+
+#ifdef CONFIG_FSL_DDR3
+ /*
+ * (tXARD and tXARDS). Empirical?
+ * The DDR3 spec has not tXARD,
+ * we use the tXP instead of it.
+ * tXP=max(3nCK, 7.5ns) for DDR3.
+ * spec has not the tAXPD, we use
+ * tAXPD=1, need design to confirm.
+ */
+ int tXP = max((get_memory_clk_period_ps() * 3), 7500); /* unit=ps */
+ unsigned int data_rate = get_ddr_freq(0);
+ tmrd_mclk = 4;
+ /* set the turnaround time */
+
+ /*
+ * for single quad-rank DIMM and two dual-rank DIMMs
+ * to avoid ODT overlap
+ */
+ if (avoid_odt_overlap(dimm_params)) {
+ twwt_mclk = 2;
+ trrt_mclk = 1;
+ }
+ /* for faster clock, need more time for data setup */
+ trwt_mclk = (data_rate/1000000 > 1800) ? 2 : 1;
+
+ if ((data_rate/1000000 > 1150) || (popts->memctl_interleaving))
+ twrt_mclk = 1;
+
+ if (popts->dynamic_power == 0) { /* powerdown is not used */
+ act_pd_exit_mclk = 1;
+ pre_pd_exit_mclk = 1;
+ taxpd_mclk = 1;
+ } else {
+ /* act_pd_exit_mclk = tXARD, see above */
+ act_pd_exit_mclk = picos_to_mclk(tXP);
+ /* Mode register MR0[A12] is '1' - fast exit */
+ pre_pd_exit_mclk = act_pd_exit_mclk;
+ taxpd_mclk = 1;
+ }
+#else /* CONFIG_FSL_DDR2 */
+ /*
+ * (tXARD and tXARDS). Empirical?
+ * tXARD = 2 for DDR2
+ * tXP=2
+ * tAXPD=8
+ */
+ act_pd_exit_mclk = 2;
+ pre_pd_exit_mclk = 2;
+ taxpd_mclk = 8;
+ tmrd_mclk = 2;
+#endif
+
+ if (popts->trwt_override)
+ trwt_mclk = popts->trwt;
+
+ 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 & 0xF) << 16) /* PRE_PD_EXIT */
+ | ((taxpd_mclk & 0xf) << 8) /* ODT_PD_EXIT */
+ | ((tmrd_mclk & 0xf) << 0) /* MRS_CYC */
+ );
+ debug("FSLDDR: timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0);
+}
+#endif /* defined(CONFIG_FSL_DDR2) */
+
+/* DDR SDRAM Timing Configuration 3 (TIMING_CFG_3) */
+static void set_timing_cfg_3(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const common_timing_params_t *common_dimm,
+ unsigned int cas_latency)
+{
+ /* Extended precharge to activate interval (tRP) */
+ unsigned int ext_pretoact = 0;
+ /* Extended Activate to precharge interval (tRAS) */
+ unsigned int ext_acttopre = 0;
+ /* Extended activate to read/write interval (tRCD) */
+ unsigned int ext_acttorw = 0;
+ /* Extended refresh recovery time (tRFC) */
+ unsigned int ext_refrec;
+ /* Extended MCAS latency from READ cmd */
+ unsigned int ext_caslat = 0;
+ /* Extended last data to precharge interval (tWR) */
+ unsigned int ext_wrrec = 0;
+ /* Control Adjust */
+ unsigned int cntl_adj = 0;
+
+ ext_pretoact = picos_to_mclk(common_dimm->tRP_ps) >> 4;
+ ext_acttopre = picos_to_mclk(common_dimm->tRAS_ps) >> 4;
+ ext_acttorw = picos_to_mclk(common_dimm->tRCD_ps) >> 4;
+ ext_caslat = (2 * cas_latency - 1) >> 4;
+ ext_refrec = (picos_to_mclk(common_dimm->tRFC_ps) - 8) >> 4;
+ /* ext_wrrec only deals with 16 clock and above, or 14 with OTF */
+ ext_wrrec = (picos_to_mclk(common_dimm->tWR_ps) +
+ (popts->OTF_burst_chop_en ? 2 : 0)) >> 4;
+
+ ddr->timing_cfg_3 = (0
+ | ((ext_pretoact & 0x1) << 28)
+ | ((ext_acttopre & 0x2) << 24)
+ | ((ext_acttorw & 0x1) << 22)
+ | ((ext_refrec & 0x1F) << 16)
+ | ((ext_caslat & 0x3) << 12)
+ | ((ext_wrrec & 0x1) << 8)
+ | ((cntl_adj & 0x7) << 0)
+ );
+ debug("FSLDDR: timing_cfg_3 = 0x%08x\n", ddr->timing_cfg_3);
+}
+
+/* DDR SDRAM Timing Configuration 1 (TIMING_CFG_1) */
+static void set_timing_cfg_1(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const common_timing_params_t *common_dimm,
+ unsigned int cas_latency)
+{
+ /* Precharge-to-activate interval (tRP) */
+ unsigned char pretoact_mclk;
+ /* Activate to precharge interval (tRAS) */
+ unsigned char acttopre_mclk;
+ /* Activate to read/write interval (tRCD) */
+ unsigned char acttorw_mclk;
+ /* CASLAT */
+ unsigned char caslat_ctrl;
+ /* Refresh recovery time (tRFC) ; trfc_low */
+ unsigned char refrec_ctrl;
+ /* Last data to precharge minimum interval (tWR) */
+ unsigned char wrrec_mclk;
+ /* Activate-to-activate interval (tRRD) */
+ unsigned char acttoact_mclk;
+ /* Last write data pair to read command issue interval (tWTR) */
+ unsigned char wrtord_mclk;
+ /* DDR_SDRAM_MODE doesn't support 9,11,13,15 */
+ static const u8 wrrec_table[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 10, 10, 12, 12, 14, 14, 0, 0};
+
+ pretoact_mclk = picos_to_mclk(common_dimm->tRP_ps);
+ acttopre_mclk = picos_to_mclk(common_dimm->tRAS_ps);
+ acttorw_mclk = picos_to_mclk(common_dimm->tRCD_ps);
+
+ /*
+ * Translate CAS Latency to a DDR controller field value:
+ *
+ * CAS Lat DDR I DDR II Ctrl
+ * Clocks SPD Bit SPD Bit Value
+ * ------- ------- ------- -----
+ * 1.0 0 0001
+ * 1.5 1 0010
+ * 2.0 2 2 0011
+ * 2.5 3 0100
+ * 3.0 4 3 0101
+ * 3.5 5 0110
+ * 4.0 4 0111
+ * 4.5 1000
+ * 5.0 5 1001
+ */
+#if defined(CONFIG_FSL_DDR1)
+ caslat_ctrl = (cas_latency + 1) & 0x07;
+#elif defined(CONFIG_FSL_DDR2)
+ caslat_ctrl = 2 * cas_latency - 1;
+#else
+ /*
+ * if the CAS latency more than 8 cycle,
+ * we need set extend bit for it at
+ * TIMING_CFG_3[EXT_CASLAT]
+ */
+ caslat_ctrl = 2 * cas_latency - 1;
+#endif
+
+ refrec_ctrl = picos_to_mclk(common_dimm->tRFC_ps) - 8;
+ wrrec_mclk = picos_to_mclk(common_dimm->tWR_ps);
+
+ if (wrrec_mclk > 16)
+ printf("Error: WRREC doesn't support more than 16 clocks\n");
+ else
+ wrrec_mclk = wrrec_table[wrrec_mclk - 1];
+ if (popts->OTF_burst_chop_en)
+ wrrec_mclk += 2;
+
+ acttoact_mclk = picos_to_mclk(common_dimm->tRRD_ps);
+ /*
+ * JEDEC has min requirement for tRRD
+ */
+#if defined(CONFIG_FSL_DDR3)
+ if (acttoact_mclk < 4)
+ acttoact_mclk = 4;
+#endif
+ wrtord_mclk = picos_to_mclk(common_dimm->tWTR_ps);
+ /*
+ * JEDEC has some min requirements for tWTR
+ */
+#if defined(CONFIG_FSL_DDR2)
+ if (wrtord_mclk < 2)
+ wrtord_mclk = 2;
+#elif defined(CONFIG_FSL_DDR3)
+ if (wrtord_mclk < 4)
+ wrtord_mclk = 4;
+#endif
+ if (popts->OTF_burst_chop_en)
+ wrtord_mclk += 2;
+
+ ddr->timing_cfg_1 = (0
+ | ((pretoact_mclk & 0x0F) << 28)
+ | ((acttopre_mclk & 0x0F) << 24)
+ | ((acttorw_mclk & 0xF) << 20)
+ | ((caslat_ctrl & 0xF) << 16)
+ | ((refrec_ctrl & 0xF) << 12)
+ | ((wrrec_mclk & 0x0F) << 8)
+ | ((acttoact_mclk & 0x0F) << 4)
+ | ((wrtord_mclk & 0x0F) << 0)
+ );
+ debug("FSLDDR: timing_cfg_1 = 0x%08x\n", ddr->timing_cfg_1);
+}
+
+/* DDR SDRAM Timing Configuration 2 (TIMING_CFG_2) */
+static void set_timing_cfg_2(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const common_timing_params_t *common_dimm,
+ unsigned int cas_latency,
+ unsigned int additive_latency)
+{
+ /* Additive latency */
+ unsigned char add_lat_mclk;
+ /* CAS-to-preamble override */
+ unsigned short cpo;
+ /* Write latency */
+ unsigned char wr_lat;
+ /* Read to precharge (tRTP) */
+ unsigned char rd_to_pre;
+ /* Write command to write data strobe timing adjustment */
+ unsigned char wr_data_delay;
+ /* 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 defined(CONFIG_FSL_DDR1)
+ /*
+ * This is a lie. It should really be 1, but if it is
+ * set to 1, bits overlap into the old controller's
+ * otherwise unused ACSM field. If we leave it 0, then
+ * the HW will magically treat it as 1 for DDR 1. Oh Yea.
+ */
+ wr_lat = 0;
+#elif defined(CONFIG_FSL_DDR2)
+ wr_lat = cas_latency - 1;
+#else
+ wr_lat = compute_cas_write_latency();
+#endif
+
+ rd_to_pre = picos_to_mclk(common_dimm->tRTP_ps);
+ /*
+ * JEDEC has some min requirements for tRTP
+ */
+#if defined(CONFIG_FSL_DDR2)
+ if (rd_to_pre < 2)
+ rd_to_pre = 2;
+#elif defined(CONFIG_FSL_DDR3)
+ if (rd_to_pre < 4)
+ rd_to_pre = 4;
+#endif
+ if (additive_latency)
+ rd_to_pre += additive_latency;
+ if (popts->OTF_burst_chop_en)
+ rd_to_pre += 2; /* according to UM */
+
+ 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 & 0xf) << 28)
+ | ((cpo & 0x1f) << 23)
+ | ((wr_lat & 0xf) << 19)
+ | ((rd_to_pre & RD_TO_PRE_MASK) << RD_TO_PRE_SHIFT)
+ | ((wr_data_delay & WR_DATA_DELAY_MASK) << WR_DATA_DELAY_SHIFT)
+ | ((cke_pls & 0x7) << 6)
+ | ((four_act & 0x3f) << 0)
+ );
+ debug("FSLDDR: timing_cfg_2 = 0x%08x\n", ddr->timing_cfg_2);
+}
+
+/* DDR SDRAM Register Control Word */
+static void set_ddr_sdram_rcw(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const common_timing_params_t *common_dimm)
+{
+ if (common_dimm->all_DIMMs_registered
+ && !common_dimm->all_DIMMs_unbuffered) {
+ if (popts->rcw_override) {
+ ddr->ddr_sdram_rcw_1 = popts->rcw_1;
+ ddr->ddr_sdram_rcw_2 = popts->rcw_2;
+ } else {
+ ddr->ddr_sdram_rcw_1 =
+ common_dimm->rcw[0] << 28 | \
+ common_dimm->rcw[1] << 24 | \
+ common_dimm->rcw[2] << 20 | \
+ common_dimm->rcw[3] << 16 | \
+ common_dimm->rcw[4] << 12 | \
+ common_dimm->rcw[5] << 8 | \
+ common_dimm->rcw[6] << 4 | \
+ common_dimm->rcw[7];
+ ddr->ddr_sdram_rcw_2 =
+ common_dimm->rcw[8] << 28 | \
+ common_dimm->rcw[9] << 24 | \
+ common_dimm->rcw[10] << 20 | \
+ common_dimm->rcw[11] << 16 | \
+ common_dimm->rcw[12] << 12 | \
+ common_dimm->rcw[13] << 8 | \
+ common_dimm->rcw[14] << 4 | \
+ common_dimm->rcw[15];
+ }
+ debug("FSLDDR: ddr_sdram_rcw_1 = 0x%08x\n", ddr->ddr_sdram_rcw_1);
+ debug("FSLDDR: ddr_sdram_rcw_2 = 0x%08x\n", ddr->ddr_sdram_rcw_2);
+ }
+}
+
+/* DDR SDRAM control configuration (DDR_SDRAM_CFG) */
+static void set_ddr_sdram_cfg(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const common_timing_params_t *common_dimm)
+{
+ unsigned int mem_en; /* DDR SDRAM interface logic enable */
+ unsigned int sren; /* Self refresh enable (during sleep) */
+ unsigned int ecc_en; /* ECC enable. */
+ unsigned int rd_en; /* Registered DIMM enable */
+ unsigned int sdram_type; /* Type of SDRAM */
+ unsigned int dyn_pwr; /* Dynamic power management mode */
+ unsigned int dbw; /* DRAM dta bus width */
+ unsigned int eight_be = 0; /* 8-beat burst enable, DDR2 is zero */
+ unsigned int ncap = 0; /* Non-concurrent auto-precharge */
+ unsigned int threeT_en; /* Enable 3T timing */
+ unsigned int twoT_en; /* Enable 2T timing */
+ unsigned int ba_intlv_ctl; /* Bank (CS) interleaving control */
+ unsigned int x32_en = 0; /* x32 enable */
+ unsigned int pchb8 = 0; /* precharge bit 8 enable */
+ unsigned int hse; /* Global half strength override */
+ unsigned int mem_halt = 0; /* memory controller halt */
+ unsigned int bi = 0; /* 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;
+ }
+
+ if (common_dimm->all_DIMMs_registered
+ && !common_dimm->all_DIMMs_unbuffered) {
+ rd_en = 1;
+ twoT_en = 0;
+ } else {
+ rd_en = 0;
+ twoT_en = popts->twoT_en;
+ }
+
+ sdram_type = CONFIG_FSL_SDRAM_TYPE;
+
+ dyn_pwr = popts->dynamic_power;
+ dbw = popts->data_bus_width;
+ /* 8-beat burst enable DDR-III case
+ * we must clear it when use the on-the-fly mode,
+ * must set it when use the 32-bits bus mode.
+ */
+ if (sdram_type == SDRAM_TYPE_DDR3) {
+ if (popts->burst_length == DDR_BL8)
+ eight_be = 1;
+ if (popts->burst_length == DDR_OTF)
+ eight_be = 0;
+ if (dbw == 0x1)
+ eight_be = 1;
+ }
+
+ threeT_en = popts->threeT_en;
+ ba_intlv_ctl = popts->ba_intlv_ctl;
+ hse = popts->half_strength_driver_enable;
+
+ ddr->ddr_sdram_cfg = (0
+ | ((mem_en & 0x1) << 31)
+ | ((sren & 0x1) << 30)
+ | ((ecc_en & 0x1) << 29)
+ | ((rd_en & 0x1) << 28)
+ | ((sdram_type & 0x7) << 24)
+ | ((dyn_pwr & 0x1) << 21)
+ | ((dbw & 0x3) << 19)
+ | ((eight_be & 0x1) << 18)
+ | ((ncap & 0x1) << 17)
+ | ((threeT_en & 0x1) << 16)
+ | ((twoT_en & 0x1) << 15)
+ | ((ba_intlv_ctl & 0x7F) << 8)
+ | ((x32_en & 0x1) << 5)
+ | ((pchb8 & 0x1) << 4)
+ | ((hse & 0x1) << 3)
+ | ((mem_halt & 0x1) << 1)
+ | ((bi & 0x1) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_cfg = 0x%08x\n", ddr->ddr_sdram_cfg);
+}
+
+/* DDR SDRAM control configuration 2 (DDR_SDRAM_CFG_2) */
+static void set_ddr_sdram_cfg_2(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const unsigned int unq_mrs_en)
+{
+ unsigned int frc_sr = 0; /* Force self refresh */
+ unsigned int sr_ie = 0; /* Self-refresh interrupt enable */
+ unsigned int dll_rst_dis; /* DLL reset disable */
+ unsigned int dqs_cfg; /* DQS configuration */
+ unsigned int odt_cfg = 0; /* ODT configuration */
+ unsigned int num_pr; /* Number of posted refreshes */
+ unsigned int slow = 0; /* DDR will be run less than 1250 */
+ unsigned int obc_cfg; /* On-The-Fly Burst Chop Cfg */
+ unsigned int ap_en; /* Address Parity Enable */
+ unsigned int d_init; /* DRAM data initialization */
+ unsigned int rcw_en = 0; /* Register Control Word Enable */
+ unsigned int md_en = 0; /* Mirrored DIMM Enable */
+ unsigned int qd_en = 0; /* quad-rank DIMM Enable */
+ int i;
+
+ dll_rst_dis = 1; /* Make this configurable */
+ dqs_cfg = popts->DQS_config;
+ for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
+ if (popts->cs_local_opts[i].odt_rd_cfg
+ || popts->cs_local_opts[i].odt_wr_cfg) {
+ odt_cfg = SDRAM_CFG2_ODT_ONLY_READ;
+ break;
+ }
+ }
+
+ 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]
+ */
+#if defined(CONFIG_FSL_DDR3)
+ obc_cfg = popts->OTF_burst_chop_en;
+#else
+ obc_cfg = 0;
+#endif
+
+#if (CONFIG_SYS_FSL_DDR_VER >= FSL_DDR_VER_4_7)
+ slow = get_ddr_freq(0) < 1249000000;
+#endif
+
+ if (popts->registered_dimm_en) {
+ rcw_en = 1;
+ ap_en = popts->ap_en;
+ } else {
+ ap_en = 0;
+ }
+
+#if defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER)
+ /* Use the DDR controller to auto initialize memory. */
+ d_init = popts->ECC_init_using_memctl;
+ ddr->ddr_data_init = CONFIG_MEM_INIT_VALUE;
+ debug("DDR: ddr_data_init = 0x%08x\n", ddr->ddr_data_init);
+#else
+ /* Memory will be initialized via DMA, or not at all. */
+ d_init = 0;
+#endif
+
+#if defined(CONFIG_FSL_DDR3)
+ md_en = popts->mirrored_dimm;
+#endif
+ qd_en = popts->quad_rank_present ? 1 : 0;
+ ddr->ddr_sdram_cfg_2 = (0
+ | ((frc_sr & 0x1) << 31)
+ | ((sr_ie & 0x1) << 30)
+ | ((dll_rst_dis & 0x1) << 29)
+ | ((dqs_cfg & 0x3) << 26)
+ | ((odt_cfg & 0x3) << 21)
+ | ((num_pr & 0xf) << 12)
+ | ((slow & 1) << 11)
+ | (qd_en << 9)
+ | (unq_mrs_en << 8)
+ | ((obc_cfg & 0x1) << 6)
+ | ((ap_en & 0x1) << 5)
+ | ((d_init & 0x1) << 4)
+ | ((rcw_en & 0x1) << 2)
+ | ((md_en & 0x1) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_cfg_2 = 0x%08x\n", ddr->ddr_sdram_cfg_2);
+}
+
+/* DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) */
+static void set_ddr_sdram_mode_2(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const unsigned int unq_mrs_en)
+{
+ unsigned short esdmode2 = 0; /* Extended SDRAM mode 2 */
+ unsigned short esdmode3 = 0; /* Extended SDRAM mode 3 */
+
+#if defined(CONFIG_FSL_DDR3)
+ int i;
+ unsigned int rtt_wr = 0; /* Rtt_WR - dynamic ODT off */
+ unsigned int srt = 0; /* self-refresh temerature, normal range */
+ unsigned int asr = 0; /* auto self-refresh disable */
+ unsigned int cwl = compute_cas_write_latency() - 5;
+ unsigned int pasr = 0; /* partial array self refresh disable */
+
+ if (popts->rtt_override)
+ rtt_wr = popts->rtt_wr_override_value;
+ else
+ rtt_wr = popts->cs_local_opts[0].odt_rtt_wr;
+ esdmode2 = (0
+ | ((rtt_wr & 0x3) << 9)
+ | ((srt & 0x1) << 7)
+ | ((asr & 0x1) << 6)
+ | ((cwl & 0x7) << 3)
+ | ((pasr & 0x7) << 0));
+#endif
+ ddr->ddr_sdram_mode_2 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_mode_2 = 0x%08x\n", ddr->ddr_sdram_mode_2);
+
+#ifdef CONFIG_FSL_DDR3
+ if (unq_mrs_en) { /* unique mode registers are supported */
+ for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
+ if (popts->rtt_override)
+ rtt_wr = popts->rtt_wr_override_value;
+ else
+ rtt_wr = popts->cs_local_opts[i].odt_rtt_wr;
+
+ esdmode2 &= 0xF9FF; /* clear bit 10, 9 */
+ esdmode2 |= (rtt_wr & 0x3) << 9;
+ switch (i) {
+ case 1:
+ ddr->ddr_sdram_mode_4 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ break;
+ case 2:
+ ddr->ddr_sdram_mode_6 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ break;
+ case 3:
+ ddr->ddr_sdram_mode_8 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ break;
+ }
+ }
+ debug("FSLDDR: ddr_sdram_mode_4 = 0x%08x\n",
+ ddr->ddr_sdram_mode_4);
+ debug("FSLDDR: ddr_sdram_mode_6 = 0x%08x\n",
+ ddr->ddr_sdram_mode_6);
+ debug("FSLDDR: ddr_sdram_mode_8 = 0x%08x\n",
+ ddr->ddr_sdram_mode_8);
+ }
+#endif
+}
+
+/* DDR SDRAM Interval Configuration (DDR_SDRAM_INTERVAL) */
+static void set_ddr_sdram_interval(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const common_timing_params_t *common_dimm)
+{
+ unsigned int refint; /* Refresh interval */
+ unsigned int bstopre; /* Precharge interval */
+
+ refint = picos_to_mclk(common_dimm->refresh_rate_ps);
+
+ bstopre = popts->bstopre;
+
+ /* refint field used 0x3FFF in earlier controllers */
+ ddr->ddr_sdram_interval = (0
+ | ((refint & 0xFFFF) << 16)
+ | ((bstopre & 0x3FFF) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_interval = 0x%08x\n", ddr->ddr_sdram_interval);
+}
+
+#if defined(CONFIG_FSL_DDR3)
+/* DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) */
+static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const common_timing_params_t *common_dimm,
+ unsigned int cas_latency,
+ unsigned int additive_latency,
+ const unsigned int unq_mrs_en)
+{
+ unsigned short esdmode; /* Extended SDRAM mode */
+ unsigned short sdmode; /* SDRAM mode */
+
+ /* Mode Register - MR1 */
+ unsigned int qoff = 0; /* Output buffer enable 0=yes, 1=no */
+ unsigned int tdqs_en = 0; /* TDQS Enable: 0=no, 1=yes */
+ unsigned int rtt;
+ unsigned int wrlvl_en = 0; /* Write level enable: 0=no, 1=yes */
+ unsigned int al = 0; /* Posted CAS# additive latency (AL) */
+ unsigned int dic = 0; /* Output driver impedance, 40ohm */
+ unsigned int dll_en = 0; /* DLL Enable 0=Enable (Normal),
+ 1=Disable (Test/Debug) */
+
+ /* Mode Register - MR0 */
+ unsigned int dll_on; /* DLL control for precharge PD, 0=off, 1=on */
+ unsigned int wr = 0; /* Write Recovery */
+ unsigned int dll_rst; /* DLL Reset */
+ unsigned int mode; /* Normal=0 or Test=1 */
+ unsigned int caslat = 4;/* CAS# latency, default set as 6 cycles */
+ /* BT: Burst Type (0=Nibble Sequential, 1=Interleaved) */
+ unsigned int bt;
+ unsigned int bl; /* BL: Burst Length */
+
+ unsigned int wr_mclk;
+ /*
+ * DDR_SDRAM_MODE doesn't support 9,11,13,15
+ * Please refer JEDEC Standard No. 79-3E for Mode Register MR0
+ * for this table
+ */
+ static const u8 wr_table[] = {1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0};
+
+ const unsigned int mclk_ps = get_memory_clk_period_ps();
+ int i;
+
+ if (popts->rtt_override)
+ rtt = popts->rtt_override_value;
+ else
+ rtt = popts->cs_local_opts[0].odt_rtt_norm;
+
+ if (additive_latency == (cas_latency - 1))
+ al = 1;
+ if (additive_latency == (cas_latency - 2))
+ al = 2;
+
+ if (popts->quad_rank_present)
+ dic = 1; /* output driver impedance 240/7 ohm */
+
+ /*
+ * The esdmode value will also be used for writing
+ * MR1 during write leveling for DDR3, although the
+ * bits specifically related to the write leveling
+ * scheme will be handled automatically by the DDR
+ * controller. so we set the wrlvl_en = 0 here.
+ */
+ esdmode = (0
+ | ((qoff & 0x1) << 12)
+ | ((tdqs_en & 0x1) << 11)
+ | ((rtt & 0x4) << 7) /* rtt field is split */
+ | ((wrlvl_en & 0x1) << 7)
+ | ((rtt & 0x2) << 5) /* rtt field is split */
+ | ((dic & 0x2) << 4) /* DIC field is split */
+ | ((al & 0x3) << 3)
+ | ((rtt & 0x1) << 2) /* rtt field is split */
+ | ((dic & 0x1) << 1) /* DIC field is split */
+ | ((dll_en & 0x1) << 0)
+ );
+
+ /*
+ * DLL control for precharge PD
+ * 0=slow exit DLL off (tXPDLL)
+ * 1=fast exit DLL on (tXP)
+ */
+ dll_on = 1;
+
+ wr_mclk = (common_dimm->tWR_ps + mclk_ps - 1) / mclk_ps;
+ if (wr_mclk <= 16) {
+ wr = wr_table[wr_mclk - 5];
+ } else {
+ printf("Error: unsupported write recovery for mode register "
+ "wr_mclk = %d\n", wr_mclk);
+ }
+
+ dll_rst = 0; /* dll no reset */
+ mode = 0; /* normal mode */
+
+ /* look up table to get the cas latency bits */
+ if (cas_latency >= 5 && cas_latency <= 16) {
+ unsigned char cas_latency_table[] = {
+ 0x2, /* 5 clocks */
+ 0x4, /* 6 clocks */
+ 0x6, /* 7 clocks */
+ 0x8, /* 8 clocks */
+ 0xa, /* 9 clocks */
+ 0xc, /* 10 clocks */
+ 0xe, /* 11 clocks */
+ 0x1, /* 12 clocks */
+ 0x3, /* 13 clocks */
+ 0x5, /* 14 clocks */
+ 0x7, /* 15 clocks */
+ 0x9, /* 16 clocks */
+ };
+ caslat = cas_latency_table[cas_latency - 5];
+ } else {
+ printf("Error: unsupported cas latency for mode register\n");
+ }
+
+ bt = 0; /* Nibble sequential */
+
+ switch (popts->burst_length) {
+ case DDR_BL8:
+ bl = 0;
+ break;
+ case DDR_OTF:
+ bl = 1;
+ break;
+ case DDR_BC4:
+ bl = 2;
+ break;
+ default:
+ printf("Error: invalid burst length of %u specified. "
+ " Defaulting to on-the-fly BC4 or BL8 beats.\n",
+ popts->burst_length);
+ bl = 1;
+ break;
+ }
+
+ sdmode = (0
+ | ((dll_on & 0x1) << 12)
+ | ((wr & 0x7) << 9)
+ | ((dll_rst & 0x1) << 8)
+ | ((mode & 0x1) << 7)
+ | (((caslat >> 1) & 0x7) << 4)
+ | ((bt & 0x1) << 3)
+ | ((caslat & 1) << 2)
+ | ((bl & 0x3) << 0)
+ );
+
+ ddr->ddr_sdram_mode = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+
+ debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode);
+
+ if (unq_mrs_en) { /* unique mode registers are supported */
+ for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
+ if (popts->rtt_override)
+ rtt = popts->rtt_override_value;
+ else
+ rtt = popts->cs_local_opts[i].odt_rtt_norm;
+
+ esdmode &= 0xFDBB; /* clear bit 9,6,2 */
+ esdmode |= (0
+ | ((rtt & 0x4) << 7) /* rtt field is split */
+ | ((rtt & 0x2) << 5) /* rtt field is split */
+ | ((rtt & 0x1) << 2) /* rtt field is split */
+ );
+ switch (i) {
+ case 1:
+ ddr->ddr_sdram_mode_3 = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ break;
+ case 2:
+ ddr->ddr_sdram_mode_5 = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ break;
+ case 3:
+ ddr->ddr_sdram_mode_7 = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ break;
+ }
+ }
+ debug("FSLDDR: ddr_sdram_mode_3 = 0x%08x\n",
+ ddr->ddr_sdram_mode_3);
+ debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n",
+ ddr->ddr_sdram_mode_5);
+ debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n",
+ ddr->ddr_sdram_mode_5);
+ }
+}
+
+#else /* !CONFIG_FSL_DDR3 */
+
+/* DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) */
+static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const common_timing_params_t *common_dimm,
+ unsigned int cas_latency,
+ unsigned int additive_latency,
+ const unsigned int unq_mrs_en)
+{
+ 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 = 0; /* Mode Register Set */
+ unsigned int outputs = 0; /* 0=Enabled, 1=Disabled */
+ unsigned int rdqs_en = 0; /* RDQS Enable: 0=no, 1=yes */
+ unsigned int dqs_en = 0; /* DQS# Enable: 0=enable, 1=disable */
+ unsigned int ocd = 0; /* 0x0=OCD not supported,
+ 0x7=OCD default state */
+ unsigned int rtt;
+ unsigned int al; /* Posted CAS# additive latency (AL) */
+ unsigned int ods = 0; /* Output Drive Strength:
+ 0 = Full strength (18ohm)
+ 1 = Reduced strength (4ohm) */
+ unsigned int dll_en = 0; /* DLL Enable 0=Enable (Normal),
+ 1=Disable (Test/Debug) */
+
+ /* Mode Register (MR) */
+ unsigned int mr; /* Mode Register Definition */
+ unsigned int pd; /* Power-Down Mode */
+ unsigned int wr; /* Write Recovery */
+ unsigned int dll_res; /* DLL Reset */
+ unsigned int mode; /* Normal=0 or Test=1 */
+ unsigned int caslat = 0;/* CAS# latency */
+ /* BT: Burst Type (0=Sequential, 1=Interleaved) */
+ unsigned int bt;
+ unsigned int bl; /* BL: Burst Length */
+
+#if defined(CONFIG_FSL_DDR2)
+ const unsigned int mclk_ps = get_memory_clk_period_ps();
+#endif
+ dqs_en = !popts->DQS_config;
+ rtt = fsl_ddr_get_rtt();
+
+ al = additive_latency;
+
+ 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)
+ );
+
+ mr = 0; /* FIXME: CHECKME */
+
+ /*
+ * 0 = Fast Exit (Normal)
+ * 1 = Slow Exit (Low Power)
+ */
+ pd = 0;
+
+#if defined(CONFIG_FSL_DDR1)
+ wr = 0; /* Historical */
+#elif defined(CONFIG_FSL_DDR2)
+ wr = (common_dimm->tWR_ps + mclk_ps - 1) / mclk_ps - 1;
+#endif
+ dll_res = 0;
+ mode = 0;
+
+#if defined(CONFIG_FSL_DDR1)
+ if (1 <= cas_latency && cas_latency <= 4) {
+ unsigned char mode_caslat_table[4] = {
+ 0x5, /* 1.5 clocks */
+ 0x2, /* 2.0 clocks */
+ 0x6, /* 2.5 clocks */
+ 0x3 /* 3.0 clocks */
+ };
+ caslat = mode_caslat_table[cas_latency - 1];
+ } else {
+ printf("Warning: unknown cas_latency %d\n", cas_latency);
+ }
+#elif defined(CONFIG_FSL_DDR2)
+ caslat = cas_latency;
+#endif
+ bt = 0;
+
+ switch (popts->burst_length) {
+ case DDR_BL4:
+ bl = 2;
+ break;
+ case DDR_BL8:
+ 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)
+ );
+ debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode);
+}
+#endif
+
+/* DDR SDRAM Data Initialization (DDR_DATA_INIT) */
+static void set_ddr_data_init(fsl_ddr_cfg_regs_t *ddr)
+{
+ unsigned int init_value; /* Initialization value */
+
+#ifdef CONFIG_MEM_INIT_VALUE
+ init_value = CONFIG_MEM_INIT_VALUE;
+#else
+ init_value = 0xDEADBEEF;
+#endif
+ ddr->ddr_data_init = init_value;
+}
+
+/*
+ * DDR SDRAM Clock Control (DDR_SDRAM_CLK_CNTL)
+ * The old controller on the 8540/60 doesn't have this register.
+ * Hope it's OK to set it (to 0) anyway.
+ */
+static void set_ddr_sdram_clk_cntl(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts)
+{
+ unsigned int clk_adjust; /* Clock adjust */
+
+ clk_adjust = popts->clk_adjust;
+ ddr->ddr_sdram_clk_cntl = (clk_adjust & 0xF) << 23;
+ debug("FSLDDR: clk_cntl = 0x%08x\n", ddr->ddr_sdram_clk_cntl);
+}
+
+/* DDR Initialization Address (DDR_INIT_ADDR) */
+static void set_ddr_init_addr(fsl_ddr_cfg_regs_t *ddr)
+{
+ unsigned int init_addr = 0; /* Initialization address */
+
+ ddr->ddr_init_addr = init_addr;
+}
+
+/* DDR Initialization Address (DDR_INIT_EXT_ADDR) */
+static void set_ddr_init_ext_addr(fsl_ddr_cfg_regs_t *ddr)
+{
+ unsigned int uia = 0; /* Use initialization address */
+ unsigned int init_ext_addr = 0; /* Initialization address */
+
+ ddr->ddr_init_ext_addr = (0
+ | ((uia & 0x1) << 31)
+ | (init_ext_addr & 0xF)
+ );
+}
+
+/* DDR SDRAM Timing Configuration 4 (TIMING_CFG_4) */
+static void set_timing_cfg_4(fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts)
+{
+ unsigned int rwt = 0; /* Read-to-write turnaround for same CS */
+ unsigned int wrt = 0; /* Write-to-read turnaround for same CS */
+ unsigned int rrt = 0; /* Read-to-read turnaround for same CS */
+ unsigned int wwt = 0; /* Write-to-write turnaround for same CS */
+ unsigned int dll_lock = 0; /* DDR SDRAM DLL Lock Time */
+
+#if defined(CONFIG_FSL_DDR3)
+ if (popts->burst_length == DDR_BL8) {
+ /* We set BL/2 for fixed BL8 */
+ rrt = 0; /* BL/2 clocks */
+ wwt = 0; /* BL/2 clocks */
+ } else {
+ /* We need to set BL/2 + 2 to BC4 and OTF */
+ rrt = 2; /* BL/2 + 2 clocks */
+ wwt = 2; /* BL/2 + 2 clocks */
+ }
+ dll_lock = 1; /* tDLLK = 512 clocks from spec */
+#endif
+ ddr->timing_cfg_4 = (0
+ | ((rwt & 0xf) << 28)
+ | ((wrt & 0xf) << 24)
+ | ((rrt & 0xf) << 20)
+ | ((wwt & 0xf) << 16)
+ | (dll_lock & 0x3)
+ );
+ debug("FSLDDR: timing_cfg_4 = 0x%08x\n", ddr->timing_cfg_4);
+}
+
+/* DDR SDRAM Timing Configuration 5 (TIMING_CFG_5) */
+static void set_timing_cfg_5(fsl_ddr_cfg_regs_t *ddr, unsigned int cas_latency)
+{
+ unsigned int rodt_on = 0; /* Read to ODT on */
+ unsigned int rodt_off = 0; /* Read to ODT off */
+ unsigned int wodt_on = 0; /* Write to ODT on */
+ unsigned int wodt_off = 0; /* Write to ODT off */
+
+#if defined(CONFIG_FSL_DDR3)
+ /* rodt_on = timing_cfg_1[caslat] - timing_cfg_2[wrlat] + 1 */
+ rodt_on = cas_latency - ((ddr->timing_cfg_2 & 0x00780000) >> 19) + 1;
+ rodt_off = 4; /* 4 clocks */
+ wodt_on = 1; /* 1 clocks */
+ wodt_off = 4; /* 4 clocks */
+#endif
+
+ ddr->timing_cfg_5 = (0
+ | ((rodt_on & 0x1f) << 24)
+ | ((rodt_off & 0x7) << 20)
+ | ((wodt_on & 0x1f) << 12)
+ | ((wodt_off & 0x7) << 8)
+ );
+ debug("FSLDDR: timing_cfg_5 = 0x%08x\n", ddr->timing_cfg_5);
+}
+
+/* DDR ZQ Calibration Control (DDR_ZQ_CNTL) */
+static void set_ddr_zq_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int zq_en)
+{
+ unsigned int zqinit = 0;/* POR ZQ Calibration Time (tZQinit) */
+ /* Normal Operation Full Calibration Time (tZQoper) */
+ unsigned int zqoper = 0;
+ /* Normal Operation Short Calibration Time (tZQCS) */
+ unsigned int zqcs = 0;
+
+ if (zq_en) {
+ zqinit = 9; /* 512 clocks */
+ zqoper = 8; /* 256 clocks */
+ zqcs = 6; /* 64 clocks */
+ }
+
+ ddr->ddr_zq_cntl = (0
+ | ((zq_en & 0x1) << 31)
+ | ((zqinit & 0xF) << 24)
+ | ((zqoper & 0xF) << 16)
+ | ((zqcs & 0xF) << 8)
+ );
+ debug("FSLDDR: zq_cntl = 0x%08x\n", ddr->ddr_zq_cntl);
+}
+
+/* DDR Write Leveling Control (DDR_WRLVL_CNTL) */
+static void set_ddr_wrlvl_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int wrlvl_en,
+ const memctl_options_t *popts)
+{
+ /*
+ * First DQS pulse rising edge after margining mode
+ * is programmed (tWL_MRD)
+ */
+ unsigned int wrlvl_mrd = 0;
+ /* ODT delay after margining mode is programmed (tWL_ODTEN) */
+ unsigned int wrlvl_odten = 0;
+ /* DQS/DQS_ delay after margining mode is programmed (tWL_DQSEN) */
+ unsigned int wrlvl_dqsen = 0;
+ /* WRLVL_SMPL: Write leveling sample time */
+ unsigned int wrlvl_smpl = 0;
+ /* WRLVL_WLR: Write leveling repeition time */
+ unsigned int wrlvl_wlr = 0;
+ /* WRLVL_START: Write leveling start time */
+ unsigned int wrlvl_start = 0;
+
+ /* suggest enable write leveling for DDR3 due to fly-by topology */
+ if (wrlvl_en) {
+ /* tWL_MRD min = 40 nCK, we set it 64 */
+ wrlvl_mrd = 0x6;
+ /* tWL_ODTEN 128 */
+ wrlvl_odten = 0x7;
+ /* tWL_DQSEN min = 25 nCK, we set it 32 */
+ wrlvl_dqsen = 0x5;
+ /*
+ * Write leveling sample time at least need 6 clocks
+ * higher than tWLO to allow enough time for progagation
+ * delay and sampling the prime data bits.
+ */
+ wrlvl_smpl = 0xf;
+ /*
+ * Write leveling repetition time
+ * at least tWLO + 6 clocks clocks
+ * we set it 64
+ */
+ wrlvl_wlr = 0x6;
+ /*
+ * Write leveling start time
+ * The value use for the DQS_ADJUST for the first sample
+ * when write leveling is enabled. It probably needs to be
+ * overriden per platform.
+ */
+ wrlvl_start = 0x8;
+ /*
+ * Override the write leveling sample and start time
+ * according to specific board
+ */
+ if (popts->wrlvl_override) {
+ wrlvl_smpl = popts->wrlvl_sample;
+ wrlvl_start = popts->wrlvl_start;
+ }
+ }
+
+ 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 & 0x1F) << 0)
+ );
+ debug("FSLDDR: wrlvl_cntl = 0x%08x\n", ddr->ddr_wrlvl_cntl);
+ ddr->ddr_wrlvl_cntl_2 = popts->wrlvl_ctl_2;
+ debug("FSLDDR: wrlvl_cntl_2 = 0x%08x\n", ddr->ddr_wrlvl_cntl_2);
+ ddr->ddr_wrlvl_cntl_3 = popts->wrlvl_ctl_3;
+ debug("FSLDDR: wrlvl_cntl_3 = 0x%08x\n", ddr->ddr_wrlvl_cntl_3);
+
+}
+
+/* DDR Self Refresh Counter (DDR_SR_CNTR) */
+static void set_ddr_sr_cntr(fsl_ddr_cfg_regs_t *ddr, unsigned int sr_it)
+{
+ /* Self Refresh Idle Threshold */
+ ddr->ddr_sr_cntr = (sr_it & 0xF) << 16;
+}
+
+static void set_ddr_eor(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts)
+{
+ if (popts->addr_hash) {
+ ddr->ddr_eor = 0x40000000; /* address hash enable */
+ puts("Address hashing enabled.\n");
+ }
+}
+
+static void set_ddr_cdr1(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts)
+{
+ ddr->ddr_cdr1 = popts->ddr_cdr1;
+ debug("FSLDDR: ddr_cdr1 = 0x%08x\n", ddr->ddr_cdr1);
+}
+
+static void set_ddr_cdr2(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts)
+{
+ ddr->ddr_cdr2 = popts->ddr_cdr2;
+ debug("FSLDDR: ddr_cdr2 = 0x%08x\n", ddr->ddr_cdr2);
+}
+
+unsigned int
+check_fsl_memctl_config_regs(const fsl_ddr_cfg_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;
+}
+
+unsigned int
+compute_fsl_memctl_config_regs(const memctl_options_t *popts,
+ fsl_ddr_cfg_regs_t *ddr,
+ const common_timing_params_t *common_dimm,
+ const dimm_params_t *dimm_params,
+ unsigned int dbw_cap_adj,
+ unsigned int size_only)
+{
+ unsigned int i;
+ unsigned int cas_latency;
+ unsigned int additive_latency;
+ unsigned int sr_it;
+ unsigned int zq_en;
+ unsigned int wrlvl_en;
+ unsigned int ip_rev = 0;
+ unsigned int unq_mrs_en = 0;
+ int cs_en = 1;
+
+ memset(ddr, 0, sizeof(fsl_ddr_cfg_regs_t));
+
+ 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;
+
+ sr_it = (popts->auto_self_refresh_en)
+ ? popts->sr_it
+ : 0;
+ /* ZQ calibration */
+ zq_en = (popts->zq_en) ? 1 : 0;
+ /* write leveling */
+ wrlvl_en = (popts->wrlvl_en) ? 1 : 0;
+
+ /* Chip Select Memory Bounds (CSn_BNDS) */
+ for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
+ unsigned long long ea, sa;
+ unsigned int cs_per_dimm
+ = CONFIG_CHIP_SELECTS_PER_CTRL / CONFIG_DIMM_SLOTS_PER_CTLR;
+ unsigned int dimm_number
+ = i / cs_per_dimm;
+ unsigned long long rank_density
+ = dimm_params[dimm_number].rank_density >> dbw_cap_adj;
+
+ if (dimm_params[dimm_number].n_ranks == 0) {
+ debug("Skipping setup of CS%u "
+ "because n_ranks on DIMM %u is 0\n", i, dimm_number);
+ continue;
+ }
+ if (popts->memctl_interleaving) {
+ switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) {
+ case FSL_DDR_CS0_CS1_CS2_CS3:
+ break;
+ case FSL_DDR_CS0_CS1:
+ case FSL_DDR_CS0_CS1_AND_CS2_CS3:
+ if (i > 1)
+ cs_en = 0;
+ break;
+ case FSL_DDR_CS2_CS3:
+ default:
+ if (i > 0)
+ cs_en = 0;
+ break;
+ }
+ sa = common_dimm->base_address;
+ ea = sa + common_dimm->total_mem - 1;
+ } else if (!popts->memctl_interleaving) {
+ /*
+ * 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.
+ */
+ switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) {
+ case FSL_DDR_CS0_CS1_CS2_CS3:
+ sa = common_dimm->base_address;
+ ea = sa + common_dimm->total_mem - 1;
+ break;
+ case FSL_DDR_CS0_CS1_AND_CS2_CS3:
+ if ((i >= 2) && (dimm_number == 0)) {
+ sa = dimm_params[dimm_number].base_address +
+ 2 * rank_density;
+ ea = sa + 2 * rank_density - 1;
+ } else {
+ sa = dimm_params[dimm_number].base_address;
+ ea = sa + 2 * rank_density - 1;
+ }
+ break;
+ case FSL_DDR_CS0_CS1:
+ if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
+ sa = dimm_params[dimm_number].base_address;
+ ea = sa + rank_density - 1;
+ if (i != 1)
+ sa += (i % cs_per_dimm) * rank_density;
+ ea += (i % cs_per_dimm) * rank_density;
+ } else {
+ sa = 0;
+ ea = 0;
+ }
+ if (i == 0)
+ ea += rank_density;
+ break;
+ case FSL_DDR_CS2_CS3:
+ if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
+ sa = dimm_params[dimm_number].base_address;
+ ea = sa + rank_density - 1;
+ if (i != 3)
+ sa += (i % cs_per_dimm) * rank_density;
+ ea += (i % cs_per_dimm) * rank_density;
+ } else {
+ sa = 0;
+ ea = 0;
+ }
+ if (i == 2)
+ ea += (rank_density >> dbw_cap_adj);
+ break;
+ default: /* No bank(chip-select) interleaving */
+ sa = dimm_params[dimm_number].base_address;
+ ea = sa + rank_density - 1;
+ if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
+ sa += (i % cs_per_dimm) * rank_density;
+ ea += (i % cs_per_dimm) * rank_density;
+ } else {
+ sa = 0;
+ ea = 0;
+ }
+ break;
+ }
+ }
+
+ sa >>= 24;
+ ea >>= 24;
+
+ if (cs_en) {
+ ddr->cs[i].bnds = (0
+ | ((sa & 0xFFF) << 16)/* starting address MSB */
+ | ((ea & 0xFFF) << 0) /* ending address MSB */
+ );
+ } else {
+ debug("FSLDDR: setting bnds to 0 for inactive CS\n");
+ ddr->cs[i].bnds = 0;
+ }
+
+ debug("FSLDDR: cs[%d]_bnds = 0x%08x\n", i, ddr->cs[i].bnds);
+ set_csn_config(dimm_number, i, ddr, popts, dimm_params);
+ set_csn_config_2(i, ddr);
+ }
+
+ /*
+ * In the case we only need to compute the ddr sdram size, we only need
+ * to set csn registers, so return from here.
+ */
+ if (size_only)
+ return 0;
+
+ set_ddr_eor(ddr, popts);
+
+#if !defined(CONFIG_FSL_DDR1)
+ set_timing_cfg_0(ddr, popts, dimm_params);
+#endif
+
+ set_timing_cfg_3(ddr, popts, common_dimm, cas_latency);
+ set_timing_cfg_1(ddr, popts, common_dimm, cas_latency);
+ set_timing_cfg_2(ddr, popts, common_dimm,
+ cas_latency, additive_latency);
+
+ set_ddr_cdr1(ddr, popts);
+ set_ddr_cdr2(ddr, popts);
+ set_ddr_sdram_cfg(ddr, popts, common_dimm);
+ ip_rev = fsl_ddr_get_version();
+ if (ip_rev > 0x40400)
+ unq_mrs_en = 1;
+
+ set_ddr_sdram_cfg_2(ddr, popts, unq_mrs_en);
+ set_ddr_sdram_mode(ddr, popts, common_dimm,
+ cas_latency, additive_latency, unq_mrs_en);
+ set_ddr_sdram_mode_2(ddr, popts, unq_mrs_en);
+ set_ddr_sdram_interval(ddr, popts, common_dimm);
+ set_ddr_data_init(ddr);
+ set_ddr_sdram_clk_cntl(ddr, popts);
+ set_ddr_init_addr(ddr);
+ set_ddr_init_ext_addr(ddr);
+ set_timing_cfg_4(ddr, popts);
+ set_timing_cfg_5(ddr, cas_latency);
+
+ set_ddr_zq_cntl(ddr, zq_en);
+ set_ddr_wrlvl_cntl(ddr, wrlvl_en, popts);
+
+ set_ddr_sr_cntr(ddr, sr_it);
+
+ set_ddr_sdram_rcw(ddr, popts, common_dimm);
+
+ return check_fsl_memctl_config_regs(ddr);
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 3/8] ppc 8xxx: dimm parameters calculation
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
2013-05-31 16:53 ` [PATCH 1/8] ppc 8xxx: DDR headers Renaud Barbier
2013-05-31 16:53 ` [PATCH 2/8] ppc 8xxx: memory controller register manipulation functions Renaud Barbier
@ 2013-05-31 16:53 ` Renaud Barbier
2013-05-31 16:54 ` [PATCH 4/8] ppc 8xxx: lowest common dimm parameters Renaud Barbier
` (13 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-05-31 16:53 UTC (permalink / raw)
To: barebox
These files are imported from U-Boot as is.
These files provide the DIMM characteristic calculation functions
needed by SPD initialisation for DDR1, DDR2 and DDR3 memory types.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/ddr1_dimm_params.c | 343 ++++++++++++++++++++++++++++++++++
arch/ppc/ddr-8xxx/ddr2_dimm_params.c | 342 +++++++++++++++++++++++++++++++++
arch/ppc/ddr-8xxx/ddr3_dimm_params.c | 336 +++++++++++++++++++++++++++++++++
3 files changed, 1021 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/ddr1_dimm_params.c
create mode 100644 arch/ppc/ddr-8xxx/ddr2_dimm_params.c
create mode 100644 arch/ppc/ddr-8xxx/ddr3_dimm_params.c
diff --git a/arch/ppc/ddr-8xxx/ddr1_dimm_params.c b/arch/ppc/ddr-8xxx/ddr1_dimm_params.c
new file mode 100644
index 0000000..376be2f
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr1_dimm_params.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+
+#include "ddr.h"
+
+/*
+ * Calculate the Density of each Physical Rank.
+ * Returned size is in bytes.
+ *
+ * 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.
+ */
+
+static unsigned long long
+compute_ranksize(unsigned int mem_type, unsigned char row_dens)
+{
+ unsigned long long bsize;
+
+ /* Bottom 2 bits up to the top. */
+ bsize = ((row_dens >> 2) | ((row_dens & 3) << 6));
+ bsize <<= 24ULL;
+ debug("DDR: DDR I rank density = 0x%16llx\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;
+}
+
+/*
+ * tCKmax from DDR I SPD Byte 43
+ *
+ * Bits 7:2 == whole ns
+ * Bits 1:0 == quarter ns
+ * 00 == 0.00 ns
+ * 01 == 0.25 ns
+ * 10 == 0.50 ns
+ * 11 == 0.75 ns
+ *
+ * Returns picoseconds.
+ */
+static unsigned int
+compute_tckmax_from_spd_ps(unsigned int byte43)
+{
+ return (byte43 >> 2) * 1000 + (byte43 & 0x3) * 250;
+}
+
+/*
+ * 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_ps(const unsigned int spd_refresh)
+{
+ unsigned int refresh_time_ps[8] = {
+ 15625000, /* 0 Normal 1.00x */
+ 3900000, /* 1 Reduced .25x */
+ 7800000, /* 2 Extended .50x */
+ 31300000, /* 3 Extended 2.00x */
+ 62500000, /* 4 Extended 4.00x */
+ 125000000, /* 5 Extended 8.00x */
+ 15625000, /* 6 Normal 1.00x filler */
+ 15625000, /* 7 Normal 1.00x filler */
+ };
+
+ return refresh_time_ps[spd_refresh & 0x7];
+}
+
+/*
+ * The purpose of this function is to compute a suitable
+ * CAS latency given the DRAM clock period. The SPD only
+ * defines at most 3 CAS latencies. Typically the slower in
+ * frequency the DIMM runs at, the shorter its CAS latency can be.
+ * If the DIMM is operating at a sufficiently low frequency,
+ * it may be able to run at a CAS latency shorter than the
+ * shortest SPD-defined CAS latency.
+ *
+ * If a CAS latency is not found, 0 is returned.
+ *
+ * Do this by finding in the standard speed 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 always the case,
+ * as those modes not defined in the SPD are optional.
+ *
+ * CAS latency de-rating based upon values JEDEC Standard No. 79-E
+ * Table 11.
+ *
+ * ordinal 2, ddr1_speed_bins[1] contains tCK for CL=2
+ */
+ /* CL2.0 CL2.5 CL3.0 */
+unsigned short ddr1_speed_bins[] = {0, 7500, 6000, 5000 };
+
+unsigned int
+compute_derated_DDR1_CAS_latency(unsigned int mclk_ps)
+{
+ const unsigned int num_speed_bins = ARRAY_SIZE(ddr1_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 = ddr1_speed_bins[i];
+ debug("i=%u, x = %u, lowest_tCKmin_found = %u\n",
+ i, x, lowest_tCKmin_found);
+ if (x && lowest_tCKmin_found <= x && x <= mclk_ps) {
+ lowest_tCKmin_found = x;
+ lowest_tCKmin_CL = i + 1;
+ }
+ }
+
+ debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL);
+
+ return lowest_tCKmin_CL;
+}
+
+/*
+ * ddr_compute_dimm_parameters for DDR1 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
+ddr_compute_dimm_parameters(const ddr1_spd_eeprom_t *spd,
+ dimm_params_t *pdimm,
+ unsigned int dimm_number)
+{
+ unsigned int retval;
+
+ if (spd->mem_type) {
+ if (spd->mem_type != SPD_MEMTYPE_DDR) {
+ printf("DIMM %u: is not a DDR1 SPD.\n", dimm_number);
+ return 1;
+ }
+ } else {
+ memset(pdimm, 0, sizeof(dimm_params_t));
+ 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->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 = compute_tckmax_from_spd_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_DDR1_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_ps = determine_refresh_rate_ps(spd->refresh);
+
+ pdimm->tIS_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup);
+ pdimm->tIH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold);
+ pdimm->tDS_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup);
+ pdimm->tDH_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold);
+
+ pdimm->tRTP_ps = 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/arch/ppc/ddr-8xxx/ddr2_dimm_params.c b/arch/ppc/ddr-8xxx/ddr2_dimm_params.c
new file mode 100644
index 0000000..f637f3d
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr2_dimm_params.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+
+#include "ddr.h"
+/*
+ * Calculate the Density of each Physical Rank.
+ * Returned size is in bytes.
+ *
+ * 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.
+ *
+ */
+static unsigned long long
+compute_ranksize(unsigned int mem_type, unsigned char row_dens)
+{
+ unsigned long long bsize;
+
+ /* Bottom 5 bits up to the top. */
+ bsize = ((row_dens >> 5) | ((row_dens & 31) << 3));
+ bsize <<= 27ULL;
+ debug("DDR: DDR II rank density = 0x%16llx\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_ps(const unsigned int spd_refresh)
+{
+ unsigned int refresh_time_ps[8] = {
+ 15625000, /* 0 Normal 1.00x */
+ 3900000, /* 1 Reduced .25x */
+ 7800000, /* 2 Extended .50x */
+ 31300000, /* 3 Extended 2.00x */
+ 62500000, /* 4 Extended 4.00x */
+ 125000000, /* 5 Extended 8.00x */
+ 15625000, /* 6 Normal 1.00x filler */
+ 15625000, /* 7 Normal 1.00x filler */
+ };
+
+ return refresh_time_ps[spd_refresh & 0x7];
+}
+
+/*
+ * The purpose of this function is to compute a suitable
+ * CAS latency given the DRAM clock period. The SPD only
+ * defines at most 3 CAS latencies. Typically the slower in
+ * frequency the DIMM runs at, the shorter its CAS latency can.
+ * be. If the DIMM is operating at a sufficiently low frequency,
+ * it may be able to run at a CAS latency shorter than the
+ * shortest SPD-defined CAS latency.
+ *
+ * If a CAS latency is not found, 0 is returned.
+ *
+ * Do this by finding in the standard speed 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 always the case,
+ * as those modes not defined in the SPD are optional.
+ *
+ * CAS latency de-rating based upon values JEDEC Standard No. 79-2C
+ * Table 40, "DDR2 SDRAM 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 CL7*/
+unsigned short ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500, 1875 };
+
+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;
+}
+
+/*
+ * ddr_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
+ddr_compute_dimm_parameters(const ddr2_spd_eeprom_t *spd,
+ dimm_params_t *pdimm,
+ unsigned int dimm_number)
+{
+ unsigned int retval;
+
+ if (spd->mem_type) {
+ if (spd->mem_type != SPD_MEMTYPE_DDR2) {
+ printf("DIMM %u: is not a DDR2 SPD.\n", dimm_number);
+ return 1;
+ }
+ } else {
+ memset(pdimm, 0, sizeof(dimm_params_t));
+ 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->capacity = pdimm->n_ranks * pdimm->rank_density;
+ pdimm->data_width = spd->dataw;
+ pdimm->primary_sdram_width = spd->primw;
+ pdimm->ec_sdram_width = spd->ecw;
+
+ /* These are all the types defined by the JEDEC DDR2 SPD 1.3 spec */
+ switch (spd->dimm_type) {
+ case DDR2_SPD_DIMMTYPE_RDIMM:
+ case DDR2_SPD_DIMMTYPE_72B_SO_RDIMM:
+ case DDR2_SPD_DIMMTYPE_MINI_RDIMM:
+ /* Registered/buffered DIMMs */
+ pdimm->registered_dimm = 1;
+ break;
+
+ case DDR2_SPD_DIMMTYPE_UDIMM:
+ case DDR2_SPD_DIMMTYPE_SO_DIMM:
+ case DDR2_SPD_DIMMTYPE_MICRO_DIMM:
+ case DDR2_SPD_DIMMTYPE_MINI_UDIMM:
+ /* Unbuffered DIMMs */
+ pdimm->registered_dimm = 0;
+ break;
+
+ case DDR2_SPD_DIMMTYPE_72B_SO_CDIMM:
+ default:
+ printf("unknown dimm_type 0x%02X\n", spd->dimm_type);
+ return 1;
+ }
+
+ /* 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_ps = determine_refresh_rate_ps(spd->refresh);
+
+ pdimm->tIS_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup);
+ pdimm->tIH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold);
+ pdimm->tDS_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup);
+ pdimm->tDH_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold);
+
+ pdimm->tRTP_ps = spd->trtp * 250;
+ pdimm->tDQSQ_max_ps = spd->tdqsq * 10;
+ pdimm->tQHS_ps = spd->tqhs * 10;
+
+ return 0;
+}
diff --git a/arch/ppc/ddr-8xxx/ddr3_dimm_params.c b/arch/ppc/ddr-8xxx/ddr3_dimm_params.c
new file mode 100644
index 0000000..3e7c269
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr3_dimm_params.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ * Dave Liu <daveliu@freescale.com>
+ *
+ * calculate the organization and timing parameter
+ * from ddr3 spd, please refer to the spec
+ * JEDEC standard No.21-C 4_01_02_11R18.pdf
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+
+#include "ddr.h"
+
+/*
+ * Calculate the Density of each Physical Rank.
+ * Returned size is in bytes.
+ *
+ * each rank size =
+ * sdram capacity(bit) / 8 * primary bus width / sdram width
+ *
+ * where: sdram capacity = spd byte4[3:0]
+ * primary bus width = spd byte8[2:0]
+ * sdram width = spd byte7[2:0]
+ *
+ * SPD byte4 - sdram density and banks
+ * bit[3:0] size(bit) size(byte)
+ * 0000 256Mb 32MB
+ * 0001 512Mb 64MB
+ * 0010 1Gb 128MB
+ * 0011 2Gb 256MB
+ * 0100 4Gb 512MB
+ * 0101 8Gb 1GB
+ * 0110 16Gb 2GB
+ *
+ * SPD byte8 - module memory bus width
+ * bit[2:0] primary bus width
+ * 000 8bits
+ * 001 16bits
+ * 010 32bits
+ * 011 64bits
+ *
+ * SPD byte7 - module organiztion
+ * bit[2:0] sdram device width
+ * 000 4bits
+ * 001 8bits
+ * 010 16bits
+ * 011 32bits
+ *
+ */
+static unsigned long long
+compute_ranksize(const ddr3_spd_eeprom_t *spd)
+{
+ unsigned long long bsize;
+
+ int nbit_sdram_cap_bsize = 0;
+ int nbit_primary_bus_width = 0;
+ int nbit_sdram_width = 0;
+
+ if ((spd->density_banks & 0xf) < 7)
+ nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28;
+ if ((spd->bus_width & 0x7) < 4)
+ nbit_primary_bus_width = (spd->bus_width & 0x7) + 3;
+ if ((spd->organization & 0x7) < 4)
+ nbit_sdram_width = (spd->organization & 0x7) + 2;
+
+ bsize = 1ULL << (nbit_sdram_cap_bsize - 3
+ + nbit_primary_bus_width - nbit_sdram_width);
+
+ debug("DDR: DDR III rank density = 0x%16llx\n", bsize);
+
+ return bsize;
+}
+
+/*
+ * ddr_compute_dimm_parameters for DDR3 SPD
+ *
+ * Compute DIMM parameters based upon the SPD information in spd.
+ * Writes the results to the dimm_params_t structure pointed by pdimm.
+ *
+ */
+unsigned int
+ddr_compute_dimm_parameters(const ddr3_spd_eeprom_t *spd,
+ dimm_params_t *pdimm,
+ unsigned int dimm_number)
+{
+ unsigned int retval;
+ unsigned int mtb_ps;
+ int ftb_10th_ps;
+ int i;
+
+ if (spd->mem_type) {
+ if (spd->mem_type != SPD_MEMTYPE_DDR3) {
+ printf("DIMM %u: is not a DDR3 SPD.\n", dimm_number);
+ return 1;
+ }
+ } else {
+ memset(pdimm, 0, sizeof(dimm_params_t));
+ return 1;
+ }
+
+ retval = ddr3_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));
+ if ((spd->info_size_crc & 0xF) > 1)
+ memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
+
+ /* DIMM organization parameters */
+ pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1;
+ pdimm->rank_density = compute_ranksize(spd);
+ pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
+ pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7));
+ if ((spd->bus_width >> 3) & 0x3)
+ pdimm->ec_sdram_width = 8;
+ else
+ pdimm->ec_sdram_width = 0;
+ pdimm->data_width = pdimm->primary_sdram_width
+ + pdimm->ec_sdram_width;
+
+ /* These are the types defined by the JEDEC DDR3 SPD spec */
+ pdimm->mirrored_dimm = 0;
+ pdimm->registered_dimm = 0;
+ switch (spd->module_type & DDR3_SPD_MODULETYPE_MASK) {
+ case DDR3_SPD_MODULETYPE_RDIMM:
+ case DDR3_SPD_MODULETYPE_MINI_RDIMM:
+ case DDR3_SPD_MODULETYPE_72B_SO_RDIMM:
+ /* Registered/buffered DIMMs */
+ pdimm->registered_dimm = 1;
+ for (i = 0; i < 16; i += 2) {
+ u8 rcw = spd->mod_section.registered.rcw[i/2];
+ pdimm->rcw[i] = (rcw >> 0) & 0x0F;
+ pdimm->rcw[i+1] = (rcw >> 4) & 0x0F;
+ }
+ break;
+
+ case DDR3_SPD_MODULETYPE_UDIMM:
+ case DDR3_SPD_MODULETYPE_SO_DIMM:
+ case DDR3_SPD_MODULETYPE_MICRO_DIMM:
+ case DDR3_SPD_MODULETYPE_MINI_UDIMM:
+ case DDR3_SPD_MODULETYPE_MINI_CDIMM:
+ case DDR3_SPD_MODULETYPE_72B_SO_UDIMM:
+ case DDR3_SPD_MODULETYPE_72B_SO_CDIMM:
+ case DDR3_SPD_MODULETYPE_LRDIMM:
+ case DDR3_SPD_MODULETYPE_16B_SO_DIMM:
+ case DDR3_SPD_MODULETYPE_32B_SO_DIMM:
+ /* Unbuffered DIMMs */
+ if (spd->mod_section.unbuffered.addr_mapping & 0x1)
+ pdimm->mirrored_dimm = 1;
+ break;
+
+ default:
+ printf("unknown module_type 0x%02X\n", spd->module_type);
+ return 1;
+ }
+
+ /* SDRAM device parameters */
+ pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12;
+ pdimm->n_col_addr = (spd->addressing & 0x7) + 9;
+ pdimm->n_banks_per_sdram_device = 8 << ((spd->density_banks >> 4) & 0x7);
+
+ /*
+ * The SPD spec has not the ECC bit,
+ * We consider the DIMM as ECC capability
+ * when the extension bus exist
+ */
+ if (pdimm->ec_sdram_width)
+ pdimm->edc_config = 0x02;
+ else
+ pdimm->edc_config = 0x00;
+
+ /*
+ * The SPD spec has not the burst length byte
+ * but DDR3 spec has nature BL8 and BC4,
+ * BL8 -bit3, BC4 -bit2
+ */
+ pdimm->burst_lengths_bitmask = 0x0c;
+ pdimm->row_density = __ilog2(pdimm->rank_density);
+
+ /* MTB - medium timebase
+ * The unit in the SPD spec is ns,
+ * We convert it to ps.
+ * eg: MTB = 0.125ns (125ps)
+ */
+ mtb_ps = (spd->mtb_dividend * 1000) /spd->mtb_divisor;
+ pdimm->mtb_ps = mtb_ps;
+
+ /*
+ * FTB - fine timebase
+ * use 1/10th of ps as our unit to avoid floating point
+ * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps
+ */
+ ftb_10th_ps =
+ ((spd->ftb_div & 0xf0) >> 4) * 10 / (spd->ftb_div & 0x0f);
+ pdimm->ftb_10th_ps = ftb_10th_ps;
+ /*
+ * sdram minimum cycle time
+ * we assume the MTB is 0.125ns
+ * eg:
+ * tCK_min=15 MTB (1.875ns) ->DDR3-1066
+ * =12 MTB (1.5ns) ->DDR3-1333
+ * =10 MTB (1.25ns) ->DDR3-1600
+ */
+ pdimm->tCKmin_X_ps = spd->tCK_min * mtb_ps +
+ (spd->fine_tCK_min * ftb_10th_ps) / 10;
+
+ /*
+ * CAS latency supported
+ * bit4 - CL4
+ * bit5 - CL5
+ * bit18 - CL18
+ */
+ pdimm->caslat_X = ((spd->caslat_msb << 8) | spd->caslat_lsb) << 4;
+
+ /*
+ * min CAS latency time
+ * eg: tAA_min =
+ * DDR3-800D 100 MTB (12.5ns)
+ * DDR3-1066F 105 MTB (13.125ns)
+ * DDR3-1333H 108 MTB (13.5ns)
+ * DDR3-1600H 90 MTB (11.25ns)
+ */
+ pdimm->tAA_ps = spd->tAA_min * mtb_ps +
+ (spd->fine_tAA_min * ftb_10th_ps) / 10;
+
+ /*
+ * min write recovery time
+ * eg:
+ * tWR_min = 120 MTB (15ns) -> all speed grades.
+ */
+ pdimm->tWR_ps = spd->tWR_min * mtb_ps;
+
+ /*
+ * min RAS to CAS delay time
+ * eg: tRCD_min =
+ * DDR3-800 100 MTB (12.5ns)
+ * DDR3-1066F 105 MTB (13.125ns)
+ * DDR3-1333H 108 MTB (13.5ns)
+ * DDR3-1600H 90 MTB (11.25)
+ */
+ pdimm->tRCD_ps = spd->tRCD_min * mtb_ps +
+ (spd->fine_tRCD_min * ftb_10th_ps) / 10;
+
+ /*
+ * min row active to row active delay time
+ * eg: tRRD_min =
+ * DDR3-800(1KB page) 80 MTB (10ns)
+ * DDR3-1333(1KB page) 48 MTB (6ns)
+ */
+ pdimm->tRRD_ps = spd->tRRD_min * mtb_ps;
+
+ /*
+ * min row precharge delay time
+ * eg: tRP_min =
+ * DDR3-800D 100 MTB (12.5ns)
+ * DDR3-1066F 105 MTB (13.125ns)
+ * DDR3-1333H 108 MTB (13.5ns)
+ * DDR3-1600H 90 MTB (11.25ns)
+ */
+ pdimm->tRP_ps = spd->tRP_min * mtb_ps +
+ (spd->fine_tRP_min * ftb_10th_ps) / 10;
+
+ /* min active to precharge delay time
+ * eg: tRAS_min =
+ * DDR3-800D 300 MTB (37.5ns)
+ * DDR3-1066F 300 MTB (37.5ns)
+ * DDR3-1333H 288 MTB (36ns)
+ * DDR3-1600H 280 MTB (35ns)
+ */
+ pdimm->tRAS_ps = (((spd->tRAS_tRC_ext & 0xf) << 8) | spd->tRAS_min_lsb)
+ * mtb_ps;
+ /*
+ * min active to actice/refresh delay time
+ * eg: tRC_min =
+ * DDR3-800D 400 MTB (50ns)
+ * DDR3-1066F 405 MTB (50.625ns)
+ * DDR3-1333H 396 MTB (49.5ns)
+ * DDR3-1600H 370 MTB (46.25ns)
+ */
+ pdimm->tRC_ps = (((spd->tRAS_tRC_ext & 0xf0) << 4) | spd->tRC_min_lsb)
+ * mtb_ps + (spd->fine_tRC_min * ftb_10th_ps) / 10;
+ /*
+ * min refresh recovery delay time
+ * eg: tRFC_min =
+ * 512Mb 720 MTB (90ns)
+ * 1Gb 880 MTB (110ns)
+ * 2Gb 1280 MTB (160ns)
+ */
+ pdimm->tRFC_ps = ((spd->tRFC_min_msb << 8) | spd->tRFC_min_lsb)
+ * mtb_ps;
+ /*
+ * min internal write to read command delay time
+ * eg: tWTR_min = 40 MTB (7.5ns) - all speed bins.
+ * tWRT is at least 4 mclk independent of operating freq.
+ */
+ pdimm->tWTR_ps = spd->tWTR_min * mtb_ps;
+
+ /*
+ * min internal read to precharge command delay time
+ * eg: tRTP_min = 40 MTB (7.5ns) - all speed bins.
+ * tRTP is at least 4 mclk independent of operating freq.
+ */
+ pdimm->tRTP_ps = spd->tRTP_min * mtb_ps;
+
+ /*
+ * Average periodic refresh interval
+ * tREFI = 7.8 us at normal temperature range
+ * = 3.9 us at ext temperature range
+ */
+ pdimm->refresh_rate_ps = 7800000;
+
+ /*
+ * min four active window delay time
+ * eg: tFAW_min =
+ * DDR3-800(1KB page) 320 MTB (40ns)
+ * DDR3-1066(1KB page) 300 MTB (37.5ns)
+ * DDR3-1333(1KB page) 240 MTB (30ns)
+ * DDR3-1600(1KB page) 240 MTB (30ns)
+ */
+ pdimm->tFAW_ps = (((spd->tFAW_msb & 0xf) << 8) | spd->tFAW_min)
+ * mtb_ps;
+
+ return 0;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 4/8] ppc 8xxx: lowest common dimm parameters
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (2 preceding siblings ...)
2013-05-31 16:53 ` [PATCH 3/8] ppc 8xxx: dimm parameters calculation Renaud Barbier
@ 2013-05-31 16:54 ` Renaud Barbier
2013-05-31 16:54 ` [PATCH 5/8] ppc 8xxx: DDR utility functions Renaud Barbier
` (12 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-05-31 16:54 UTC (permalink / raw)
To: barebox
This file is imported from U-Boot as is.
The code calculates the lowest common DIMM parameters.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/lc_common_dimm_params.c | 517 +++++++++++++++++++++++++++++
1 files changed, 517 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/lc_common_dimm_params.c
diff --git a/arch/ppc/ddr-8xxx/lc_common_dimm_params.c b/arch/ppc/ddr-8xxx/lc_common_dimm_params.c
new file mode 100644
index 0000000..e958e13
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/lc_common_dimm_params.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+
+#include "ddr.h"
+
+#if defined(CONFIG_FSL_DDR3)
+static unsigned int
+compute_cas_latency_ddr3(const dimm_params_t *dimm_params,
+ common_timing_params_t *outpdimm,
+ unsigned int number_of_dimms)
+{
+ unsigned int i;
+ unsigned int tAAmin_ps = 0;
+ unsigned int tCKmin_X_ps = 0;
+ unsigned int common_caslat;
+ unsigned int caslat_actual;
+ unsigned int retry = 16;
+ unsigned int tmp;
+ const unsigned int mclk_ps = get_memory_clk_period_ps();
+
+ /* compute the common CAS latency supported between slots */
+ tmp = dimm_params[0].caslat_X;
+ for (i = 1; i < number_of_dimms; i++) {
+ if (dimm_params[i].n_ranks)
+ tmp &= dimm_params[i].caslat_X;
+ }
+ common_caslat = tmp;
+
+ /* compute the max tAAmin tCKmin between slots */
+ for (i = 0; i < number_of_dimms; i++) {
+ tAAmin_ps = max(tAAmin_ps, dimm_params[i].tAA_ps);
+ tCKmin_X_ps = max(tCKmin_X_ps, dimm_params[i].tCKmin_X_ps);
+ }
+ /* validate if the memory clk is in the range of dimms */
+ if (mclk_ps < tCKmin_X_ps) {
+ printf("DDR clock (MCLK cycle %u ps) is faster than "
+ "the slowest DIMM(s) (tCKmin %u ps) can support.\n",
+ mclk_ps, tCKmin_X_ps);
+ }
+ /* determine the acutal cas latency */
+ caslat_actual = (tAAmin_ps + mclk_ps - 1) / mclk_ps;
+ /* check if the dimms support the CAS latency */
+ while (!(common_caslat & (1 << caslat_actual)) && retry > 0) {
+ caslat_actual++;
+ retry--;
+ }
+ /* once the caculation of caslat_actual is completed
+ * we must verify that this CAS latency value does not
+ * exceed tAAmax, which is 20 ns for all DDR3 speed grades
+ */
+ if (caslat_actual * mclk_ps > 20000) {
+ printf("The choosen cas latency %d is too large\n",
+ caslat_actual);
+ }
+ outpdimm->lowest_common_SPD_caslat = caslat_actual;
+
+ return 0;
+}
+#endif
+
+/*
+ * 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.
+ */
+unsigned int
+compute_lowest_common_dimm_parameters(const dimm_params_t *dimm_params,
+ common_timing_params_t *outpdimm,
+ const unsigned int number_of_dimms)
+{
+ unsigned int i, j;
+
+ 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_ps = 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 additive_latency = 0;
+#if !defined(CONFIG_FSL_DDR3)
+ const unsigned int mclk_ps = get_memory_clk_period_ps();
+ unsigned int lowest_good_caslat;
+ unsigned int not_ok;
+
+ debug("using mclk_ps = %u\n", mclk_ps);
+#endif
+
+ 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;
+ }
+ if (dimm_params[i].n_ranks == 4 && i != 0) {
+ printf("Found Quad-rank DIMM in wrong bank, ignored."
+ " Software may not run as expected.\n");
+ temp1++;
+ continue;
+ }
+
+ /*
+ * check if quad-rank DIMM is plugged if
+ * CONFIG_CHIP_SELECT_QUAD_CAPABLE is not defined
+ * Only the board with proper design is capable
+ */
+#ifndef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE
+ if (dimm_params[i].n_ranks == 4 && \
+ CONFIG_CHIP_SELECTS_PER_CTRL/CONFIG_DIMM_SLOTS_PER_CTLR < 4) {
+ printf("Found Quad-rank DIMM, not able to support.");
+ temp1++;
+ continue;
+ }
+#endif
+ /*
+ * 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);
+
+ /* Either find maximum value to determine slowest
+ * speed, delay, time, period, etc */
+ tCKmin_X_ps = max(tCKmin_X_ps, dimm_params[i].tCKmin_X_ps);
+ tCKmax_max_ps = max(tCKmax_max_ps, dimm_params[i].tCKmax_ps);
+ tRCD_ps = max(tRCD_ps, dimm_params[i].tRCD_ps);
+ tRP_ps = max(tRP_ps, dimm_params[i].tRP_ps);
+ tRAS_ps = max(tRAS_ps, dimm_params[i].tRAS_ps);
+ tWR_ps = max(tWR_ps, dimm_params[i].tWR_ps);
+ tWTR_ps = max(tWTR_ps, dimm_params[i].tWTR_ps);
+ tRFC_ps = max(tRFC_ps, dimm_params[i].tRFC_ps);
+ tRRD_ps = max(tRRD_ps, dimm_params[i].tRRD_ps);
+ tRC_ps = max(tRC_ps, dimm_params[i].tRC_ps);
+ tIS_ps = max(tIS_ps, dimm_params[i].tIS_ps);
+ tIH_ps = max(tIH_ps, dimm_params[i].tIH_ps);
+ tDS_ps = max(tDS_ps, dimm_params[i].tDS_ps);
+ tDH_ps = max(tDH_ps, dimm_params[i].tDH_ps);
+ tRTP_ps = max(tRTP_ps, dimm_params[i].tRTP_ps);
+ tQHS_ps = max(tQHS_ps, dimm_params[i].tQHS_ps);
+ refresh_rate_ps = max(refresh_rate_ps,
+ dimm_params[i].refresh_rate_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);
+ }
+
+ 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_ps = refresh_rate_ps;
+ 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;
+ printf("Detected RDIMM %s\n",
+ dimm_params[i].mpart);
+ } else {
+ temp2 = 1;
+ printf("Detected UDIMM %s\n",
+ dimm_params[i].mpart);
+ }
+ }
+ }
+
+ outpdimm->all_DIMMs_registered = 0;
+ outpdimm->all_DIMMs_unbuffered = 0;
+ if (temp1 && !temp2) {
+ outpdimm->all_DIMMs_registered = 1;
+ } else if (!temp1 && temp2) {
+ outpdimm->all_DIMMs_unbuffered = 1;
+ } else {
+ printf("ERROR: Mix of registered buffered and unbuffered "
+ "DIMMs detected!\n");
+ }
+
+ temp1 = 0;
+ if (outpdimm->all_DIMMs_registered)
+ for (j = 0; j < 16; j++) {
+ outpdimm->rcw[j] = dimm_params[0].rcw[j];
+ for (i = 1; i < number_of_dimms; i++) {
+ if (!dimm_params[i].n_ranks)
+ continue;
+ if (dimm_params[i].rcw[j] != dimm_params[0].rcw[j]) {
+ temp1 = 1;
+ break;
+ }
+ }
+ }
+
+ if (temp1 != 0)
+ printf("ERROR: Mix different RDIMM detected!\n");
+
+#if defined(CONFIG_FSL_DDR3)
+ if (compute_cas_latency_ddr3(dimm_params, outpdimm, number_of_dimms))
+ return 1;
+#else
+ /*
+ * 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 returns for an input of 0.
+ * On 32-bit PowerPC, left shift counts with bit
+ * 26 set (that the value of 255 or 0xFFFFFFFF
+ * will have), cause the destination register to
+ * be 0. That is why this works.
+ */
+ temp1 &= temp2;
+ }
+ }
+
+ /*
+ * Step 2: check each common CAS latency against tCK of each
+ * DIMM's SPD.
+ */
+ lowest_good_caslat = 0;
+ temp2 = 0;
+ while (temp1) {
+ not_ok = 0;
+ temp2 = __ilog2(temp1);
+ 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) {
+ unsigned int tCKmin_X_minus_1_ps
+ = dimm_params[i].tCKmin_X_minus_1_ps;
+ if (mclk_ps >= 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,
+ tCKmin_X_minus_1_ps);
+ continue;
+ } else {
+ not_ok++;
+ }
+ }
+
+ if (dimm_params[i].caslat_X_minus_2 == temp2) {
+ unsigned int tCKmin_X_minus_2_ps
+ = dimm_params[i].tCKmin_X_minus_2_ps;
+ if (mclk_ps >= 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,
+ 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);
+#endif /* #if defined(CONFIG_FSL_DDR3) */
+
+ /* 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 & EDC_ECC)) {
+ temp1 = 0;
+ break;
+ }
+ }
+ if (temp1) {
+ debug("all DIMMs ECC capable\n");
+ } else {
+ debug("Warning: not all DIMMs ECC capable, cant enable ECC\n");
+ }
+ outpdimm->all_DIMMs_ECC_capable = temp1;
+
+#ifndef CONFIG_FSL_DDR3
+ /* FIXME: move to somewhere else to validate. */
+ if (mclk_ps > tCKmax_max_ps) {
+ printf("Warning: some of the installed DIMMs "
+ "can not operate this slowly.\n");
+ return 1;
+ }
+#endif
+ /*
+ * 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, we use the AL=0
+ *
+ * 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)
+ ? picos_to_mclk(tRCD_ps) - lowest_good_caslat : 0;
+ 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)
+ /*
+ * The system will not use the global auto-precharge mode.
+ * However, it uses the page mode, so we set AL=0
+ */
+ additive_latency = 0;
+#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;
+ }
+
+ /*
+ * RL = CL + AL; RL >= 3 for ODT_RD_CFG to be enabled
+ * WL = RL - 1; WL >= 3 for ODT_WL_CFG to be enabled
+ * ADD_LAT (the register) must be set to a value less
+ * than ACTTORW if WL = 1, then AL must be set to 1
+ * RD_TO_PRE (the register) must be set to a minimum
+ * tRTP + AL if AL is nonzero
+ */
+
+ /*
+ * Additive latency will be applied only if the memctl option to
+ * use it.
+ */
+ outpdimm->additive_latency = additive_latency;
+
+ debug("tCKmin_ps = %u\n", outpdimm->tCKmin_X_ps);
+ debug("tRCD_ps = %u\n", outpdimm->tRCD_ps);
+ debug("tRP_ps = %u\n", outpdimm->tRP_ps);
+ debug("tRAS_ps = %u\n", outpdimm->tRAS_ps);
+ debug("tWR_ps = %u\n", outpdimm->tWR_ps);
+ debug("tWTR_ps = %u\n", outpdimm->tWTR_ps);
+ debug("tRFC_ps = %u\n", outpdimm->tRFC_ps);
+ debug("tRRD_ps = %u\n", outpdimm->tRRD_ps);
+ debug("tRC_ps = %u\n", outpdimm->tRC_ps);
+
+ return 0;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 5/8] ppc 8xxx: DDR utility functions
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (3 preceding siblings ...)
2013-05-31 16:54 ` [PATCH 4/8] ppc 8xxx: lowest common dimm parameters Renaud Barbier
@ 2013-05-31 16:54 ` Renaud Barbier
2013-05-31 16:54 ` [PATCH 6/8] ppc 8xxx: DDR specific options Renaud Barbier
` (11 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-05-31 16:54 UTC (permalink / raw)
To: barebox
This file is imported from U-Boot as is.
It adds utility functions to calculate clock cycles, configure the
LAW registers and print information on the memory configuration.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/util.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 264 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/util.c
diff --git a/arch/ppc/ddr-8xxx/util.c b/arch/ppc/ddr-8xxx/util.c
new file mode 100644
index 0000000..acfe1f0
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/util.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/fsl_law.h>
+#include <div64.h>
+
+#include "ddr.h"
+
+/* To avoid 64-bit full-divides, we factor this here */
+#define ULL_2E12 2000000000000ULL
+#define UL_5POW12 244140625UL
+#define UL_2POW13 (1UL << 13)
+
+#define ULL_8FS 0xFFFFFFFFULL
+
+/*
+ * Round up mclk_ps to nearest 1 ps in memory controller code
+ * if the error is 0.5ps or more.
+ *
+ * If an imprecise data rate is too high due to rounding error
+ * propagation, compute a suitably rounded mclk_ps to compute
+ * a working memory controller configuration.
+ */
+unsigned int get_memory_clk_period_ps(void)
+{
+ unsigned int data_rate = get_ddr_freq(0);
+ unsigned int result;
+
+ /* Round to nearest 10ps, being careful about 64-bit multiply/divide */
+ unsigned long long rem, mclk_ps = ULL_2E12;
+
+ /* Now perform the big divide, the result fits in 32-bits */
+ rem = do_div(mclk_ps, data_rate);
+ result = (rem >= (data_rate >> 1)) ? mclk_ps + 1 : mclk_ps;
+
+ return result;
+}
+
+/* Convert picoseconds into DRAM clock cycles (rounding up if needed). */
+unsigned int picos_to_mclk(unsigned int picos)
+{
+ unsigned long long clks, clks_rem;
+ unsigned long data_rate = get_ddr_freq(0);
+
+ /* Short circuit for zero picos */
+ if (!picos)
+ return 0;
+
+ /* First multiply the time by the data rate (32x32 => 64) */
+ clks = picos * (unsigned long long)data_rate;
+ /*
+ * Now divide by 5^12 and track the 32-bit remainder, then divide
+ * by 2*(2^12) using shifts (and updating the remainder).
+ */
+ clks_rem = do_div(clks, UL_5POW12);
+ clks_rem += (clks & (UL_2POW13-1)) * UL_5POW12;
+ clks >>= 13;
+
+ /* If we had a remainder greater than the 1ps error, then round up */
+ if (clks_rem > data_rate)
+ clks++;
+
+ /* Clamp to the maximum representable value */
+ 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
+__fsl_ddr_set_lawbar(const common_timing_params_t *memctl_common_params,
+ unsigned int law_memctl,
+ unsigned int ctrl_num)
+{
+ unsigned long long base = memctl_common_params->base_address;
+ unsigned long long size = memctl_common_params->total_mem;
+
+ /*
+ * If no DIMMs on this controller, do not proceed any further.
+ */
+ if (!memctl_common_params->ndimms_present) {
+ return;
+ }
+
+#if !defined(CONFIG_PHYS_64BIT)
+ if (base >= CONFIG_MAX_MEM_MAPPED)
+ return;
+ if ((base + size) >= CONFIG_MAX_MEM_MAPPED)
+ size = CONFIG_MAX_MEM_MAPPED - base;
+#endif
+ if (set_ddr_laws(base, size, law_memctl) < 0) {
+ printf("%s: ERROR (ctrl #%d, TRGT ID=%x)\n", __func__, ctrl_num,
+ law_memctl);
+ return ;
+ }
+ debug("setup ddr law base = 0x%llx, size 0x%llx, TRGT_ID 0x%x\n",
+ base, size, law_memctl);
+}
+
+__attribute__((weak, alias("__fsl_ddr_set_lawbar"))) void
+fsl_ddr_set_lawbar(const common_timing_params_t *memctl_common_params,
+ unsigned int memctl_interleaved,
+ unsigned int ctrl_num);
+
+void fsl_ddr_set_intl3r(const unsigned int granule_size)
+{
+#ifdef CONFIG_E6500
+ u32 *mcintl3r = (void *) (CONFIG_SYS_IMMR + 0x18004);
+ *mcintl3r = 0x80000000 | (granule_size & 0x1f);
+ debug("Enable MCINTL3R with granule size 0x%x\n", granule_size);
+#endif
+}
+
+u32 fsl_ddr_get_intl3r(void)
+{
+ u32 val = 0;
+#ifdef CONFIG_E6500
+ u32 *mcintl3r = (void *) (CONFIG_SYS_IMMR + 0x18004);
+ val = *mcintl3r;
+#endif
+ return val;
+}
+
+void board_add_ram_info(int use_default)
+{
+ ccsr_ddr_t *ddr = (void *)(CONFIG_SYS_MPC8xxx_DDR_ADDR);
+
+#if defined(CONFIG_E6500) && (CONFIG_NUM_DDR_CONTROLLERS == 3)
+ u32 *mcintl3r = (void *) (CONFIG_SYS_IMMR + 0x18004);
+#endif
+#if (CONFIG_NUM_DDR_CONTROLLERS > 1)
+ uint32_t cs0_config = in_be32(&ddr->cs0_config);
+#endif
+ uint32_t sdram_cfg = in_be32(&ddr->sdram_cfg);
+ int cas_lat;
+
+#if CONFIG_NUM_DDR_CONTROLLERS >= 2
+ if (!(sdram_cfg & SDRAM_CFG_MEM_EN)) {
+ ddr = (void __iomem *)CONFIG_SYS_MPC8xxx_DDR2_ADDR;
+ sdram_cfg = in_be32(&ddr->sdram_cfg);
+ }
+#endif
+#if CONFIG_NUM_DDR_CONTROLLERS >= 3
+ if (!(sdram_cfg & SDRAM_CFG_MEM_EN)) {
+ ddr = (void __iomem *)CONFIG_SYS_MPC8xxx_DDR3_ADDR;
+ sdram_cfg = in_be32(&ddr->sdram_cfg);
+ }
+#endif
+ puts(" (DDR");
+ switch ((sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) >>
+ SDRAM_CFG_SDRAM_TYPE_SHIFT) {
+ case SDRAM_TYPE_DDR1:
+ puts("1");
+ break;
+ case SDRAM_TYPE_DDR2:
+ puts("2");
+ break;
+ case SDRAM_TYPE_DDR3:
+ puts("3");
+ break;
+ default:
+ puts("?");
+ break;
+ }
+
+ if (sdram_cfg & SDRAM_CFG_32_BE)
+ puts(", 32-bit");
+ else if (sdram_cfg & SDRAM_CFG_16_BE)
+ puts(", 16-bit");
+ else
+ puts(", 64-bit");
+
+ /* Calculate CAS latency based on timing cfg values */
+ cas_lat = ((in_be32(&ddr->timing_cfg_1) >> 16) & 0xf) + 1;
+ if ((in_be32(&ddr->timing_cfg_3) >> 12) & 1)
+ cas_lat += (8 << 1);
+ printf(", CL=%d", cas_lat >> 1);
+ if (cas_lat & 0x1)
+ puts(".5");
+
+ if (sdram_cfg & SDRAM_CFG_ECC_EN)
+ puts(", ECC on)");
+ else
+ puts(", ECC off)");
+
+#if (CONFIG_NUM_DDR_CONTROLLERS == 3)
+#ifdef CONFIG_E6500
+ if (*mcintl3r & 0x80000000) {
+ puts("\n");
+ puts(" DDR Controller Interleaving Mode: ");
+ switch (*mcintl3r & 0x1f) {
+ case FSL_DDR_3WAY_1KB_INTERLEAVING:
+ puts("3-way 1KB");
+ break;
+ case FSL_DDR_3WAY_4KB_INTERLEAVING:
+ puts("3-way 4KB");
+ break;
+ case FSL_DDR_3WAY_8KB_INTERLEAVING:
+ puts("3-way 8KB");
+ break;
+ default:
+ puts("3-way UNKNOWN");
+ break;
+ }
+ }
+#endif
+#endif
+#if (CONFIG_NUM_DDR_CONTROLLERS >= 2)
+ if (cs0_config & 0x20000000) {
+ puts("\n");
+ puts(" DDR Controller Interleaving Mode: ");
+
+ switch ((cs0_config >> 24) & 0xf) {
+ case FSL_DDR_CACHE_LINE_INTERLEAVING:
+ puts("cache line");
+ break;
+ case FSL_DDR_PAGE_INTERLEAVING:
+ puts("page");
+ break;
+ case FSL_DDR_BANK_INTERLEAVING:
+ puts("bank");
+ break;
+ case FSL_DDR_SUPERBANK_INTERLEAVING:
+ puts("super-bank");
+ break;
+ default:
+ puts("invalid");
+ break;
+ }
+ }
+#endif
+
+ if ((sdram_cfg >> 8) & 0x7f) {
+ puts("\n");
+ puts(" DDR Chip-Select Interleaving Mode: ");
+ switch(sdram_cfg >> 8 & 0x7f) {
+ case FSL_DDR_CS0_CS1_CS2_CS3:
+ puts("CS0+CS1+CS2+CS3");
+ break;
+ case FSL_DDR_CS0_CS1:
+ puts("CS0+CS1");
+ break;
+ case FSL_DDR_CS2_CS3:
+ puts("CS2+CS3");
+ break;
+ case FSL_DDR_CS0_CS1_AND_CS2_CS3:
+ puts("CS0+CS1 and CS2+CS3");
+ break;
+ default:
+ puts("invalid");
+ break;
+ }
+ }
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 6/8] ppc 8xxx: DDR specific options
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (4 preceding siblings ...)
2013-05-31 16:54 ` [PATCH 5/8] ppc 8xxx: DDR utility functions Renaud Barbier
@ 2013-05-31 16:54 ` Renaud Barbier
2013-05-31 16:54 ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
` (10 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-05-31 16:54 UTC (permalink / raw)
To: barebox
This file is imported from U-Boot as is.
This code provides default configuration options that can be
overwritten by board specific memory configuration.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/options.c | 1148 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1148 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/options.c
diff --git a/arch/ppc/ddr-8xxx/options.c b/arch/ppc/ddr-8xxx/options.c
new file mode 100644
index 0000000..2f13b8f
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/options.c
@@ -0,0 +1,1148 @@
+/*
+ * Copyright 2008, 2010-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <common.h>
+#include <hwconfig.h>
+#include <asm/fsl_ddr_sdram.h>
+
+#include "ddr.h"
+
+/*
+ * Use our own stack based buffer before relocation to allow accessing longer
+ * hwconfig strings that might be in the environment before we've relocated.
+ * This is pretty fragile on both the use of stack and if the buffer is big
+ * enough. However we will get a warning from getenv_f for the later.
+ */
+
+/* Board-specific functions defined in each board's ddr.c */
+extern void fsl_ddr_board_options(memctl_options_t *popts,
+ dimm_params_t *pdimm,
+ unsigned int ctrl_num);
+
+struct dynamic_odt {
+ unsigned int odt_rd_cfg;
+ unsigned int odt_wr_cfg;
+ unsigned int odt_rtt_norm;
+ unsigned int odt_rtt_wr;
+};
+
+#ifdef CONFIG_FSL_DDR3
+static const struct dynamic_odt single_Q[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS_AND_OTHER_DIMM,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_120_OHM
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER, /* tied high */
+ DDR3_RTT_OFF,
+ DDR3_RTT_120_OHM
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS_AND_OTHER_DIMM,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_120_OHM
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER, /* tied high */
+ DDR3_RTT_OFF,
+ DDR3_RTT_120_OHM
+ }
+};
+
+static const struct dynamic_odt single_D[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR3_RTT_OFF,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt single_S[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+};
+
+static const struct dynamic_odt dual_DD[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_DS[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_120_OHM
+ },
+ {0, 0, 0, 0}
+};
+static const struct dynamic_odt dual_SD[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_120_OHM
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_SS[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_120_OHM
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_120_OHM
+ },
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_D0[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR3_RTT_OFF,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_0D[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR3_RTT_OFF,
+ DDR3_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_S0[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt dual_0S[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt odt_unknown[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ }
+};
+#else /* CONFIG_FSL_DDR3 */
+static const struct dynamic_odt single_Q[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt single_D[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt single_S[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+};
+
+static const struct dynamic_odt dual_DD[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_DS[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_SD[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_SS[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_D0[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_0D[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_S0[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt dual_0S[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt odt_unknown[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ }
+};
+#endif
+
+/*
+ * Automatically seleect bank interleaving mode based on DIMMs
+ * in this order: cs0_cs1_cs2_cs3, cs0_cs1, null.
+ * This function only deal with one or two slots per controller.
+ */
+static inline unsigned int auto_bank_intlv(dimm_params_t *pdimm)
+{
+#if (CONFIG_DIMM_SLOTS_PER_CTLR == 1)
+ if (pdimm[0].n_ranks == 4)
+ return FSL_DDR_CS0_CS1_CS2_CS3;
+ else if (pdimm[0].n_ranks == 2)
+ return FSL_DDR_CS0_CS1;
+#elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
+#ifdef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE
+ if (pdimm[0].n_ranks == 4)
+ return FSL_DDR_CS0_CS1_CS2_CS3;
+#endif
+ if (pdimm[0].n_ranks == 2) {
+ if (pdimm[1].n_ranks == 2)
+ return FSL_DDR_CS0_CS1_CS2_CS3;
+ else
+ return FSL_DDR_CS0_CS1;
+ }
+#endif
+ return 0;
+}
+
+unsigned int populate_memctl_options(int all_DIMMs_registered,
+ memctl_options_t *popts,
+ dimm_params_t *pdimm,
+ unsigned int ctrl_num)
+{
+ unsigned int i;
+ char buffer[HWCONFIG_BUFFER_SIZE];
+ char *buf = NULL;
+#if defined(CONFIG_FSL_DDR3) || defined(CONFIG_FSL_DDR2)
+ const struct dynamic_odt *pdodt = odt_unknown;
+#endif
+ ulong ddr_freq;
+
+ /*
+ * Extract hwconfig from environment since we have not properly setup
+ * the environment but need it for ddr config params
+ */
+ if (getenv_f("hwconfig", buffer, sizeof(buffer)) > 0)
+ buf = buffer;
+
+#if defined(CONFIG_FSL_DDR3) || defined(CONFIG_FSL_DDR2)
+ /* Chip select options. */
+ if (CONFIG_DIMM_SLOTS_PER_CTLR == 1) {
+ switch (pdimm[0].n_ranks) {
+ case 1:
+ pdodt = single_S;
+ break;
+ case 2:
+ pdodt = single_D;
+ break;
+ case 4:
+ pdodt = single_Q;
+ break;
+ }
+ } else if (CONFIG_DIMM_SLOTS_PER_CTLR == 2) {
+ switch (pdimm[0].n_ranks) {
+#ifdef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE
+ case 4:
+ pdodt = single_Q;
+ if (pdimm[1].n_ranks)
+ printf("Error: Quad- and Dual-rank DIMMs "
+ "cannot be used together\n");
+ break;
+#endif
+ case 2:
+ switch (pdimm[1].n_ranks) {
+ case 2:
+ pdodt = dual_DD;
+ break;
+ case 1:
+ pdodt = dual_DS;
+ break;
+ case 0:
+ pdodt = dual_D0;
+ break;
+ }
+ break;
+ case 1:
+ switch (pdimm[1].n_ranks) {
+ case 2:
+ pdodt = dual_SD;
+ break;
+ case 1:
+ pdodt = dual_SS;
+ break;
+ case 0:
+ pdodt = dual_S0;
+ break;
+ }
+ break;
+ case 0:
+ switch (pdimm[1].n_ranks) {
+ case 2:
+ pdodt = dual_0D;
+ break;
+ case 1:
+ pdodt = dual_0S;
+ break;
+ }
+ break;
+ }
+ }
+#endif
+
+ /* Pick chip-select local options. */
+ for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
+#if defined(CONFIG_FSL_DDR3) || defined(CONFIG_FSL_DDR2)
+ popts->cs_local_opts[i].odt_rd_cfg = pdodt[i].odt_rd_cfg;
+ popts->cs_local_opts[i].odt_wr_cfg = pdodt[i].odt_wr_cfg;
+ popts->cs_local_opts[i].odt_rtt_norm = pdodt[i].odt_rtt_norm;
+ popts->cs_local_opts[i].odt_rtt_wr = pdodt[i].odt_rtt_wr;
+#else
+ popts->cs_local_opts[i].odt_rd_cfg = FSL_DDR_ODT_NEVER;
+ popts->cs_local_opts[i].odt_wr_cfg = FSL_DDR_ODT_CS;
+#endif
+ 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 = all_DIMMs_registered;
+
+ /* Operational Mode Paramters */
+
+ /* Pick ECC modes */
+ popts->ECC_mode = 0; /* 0 = disabled, 1 = enabled */
+#ifdef CONFIG_DDR_ECC
+ if (hwconfig_sub_f("fsl_ddr", "ecc", buf)) {
+ if (hwconfig_subarg_cmp_f("fsl_ddr", "ecc", "on", buf))
+ popts->ECC_mode = 1;
+ } else
+ popts->ECC_mode = 1;
+#endif
+ popts->ECC_init_using_memctl = 1; /* 0 = use DMA, 1 = use memctl */
+
+ /*
+ * Choose DQS config
+ * 0 for DDR1
+ * 1 for DDR2
+ */
+#if defined(CONFIG_FSL_DDR1)
+ popts->DQS_config = 0;
+#elif defined(CONFIG_FSL_DDR2) || defined(CONFIG_FSL_DDR3)
+ popts->DQS_config = 1;
+#endif
+
+ /* Choose self-refresh during sleep. */
+ popts->self_refresh_in_sleep = 1;
+
+ /* Choose dynamic power management mode. */
+ popts->dynamic_power = 0;
+
+ /*
+ * check first dimm for primary sdram width
+ * presuming all dimms are similar
+ * 0 = 64-bit, 1 = 32-bit, 2 = 16-bit
+ */
+#if defined(CONFIG_FSL_DDR1) || defined(CONFIG_FSL_DDR2)
+ if (pdimm[0].n_ranks != 0) {
+ if ((pdimm[0].data_width >= 64) && \
+ (pdimm[0].data_width <= 72))
+ popts->data_bus_width = 0;
+ else if ((pdimm[0].data_width >= 32) || \
+ (pdimm[0].data_width <= 40))
+ popts->data_bus_width = 1;
+ else {
+ panic("Error: data width %u is invalid!\n",
+ pdimm[0].data_width);
+ }
+ }
+#else
+ if (pdimm[0].n_ranks != 0) {
+ if (pdimm[0].primary_sdram_width == 64)
+ popts->data_bus_width = 0;
+ else if (pdimm[0].primary_sdram_width == 32)
+ popts->data_bus_width = 1;
+ else if (pdimm[0].primary_sdram_width == 16)
+ popts->data_bus_width = 2;
+ else {
+ panic("Error: primary sdram width %u is invalid!\n",
+ pdimm[0].primary_sdram_width);
+ }
+ }
+#endif
+
+ /* Choose burst length. */
+#if defined(CONFIG_FSL_DDR3)
+#if defined(CONFIG_E500MC)
+ popts->OTF_burst_chop_en = 0; /* on-the-fly burst chop disable */
+ popts->burst_length = DDR_BL8; /* Fixed 8-beat burst len */
+#else
+ if ((popts->data_bus_width == 1) || (popts->data_bus_width == 2)) {
+ /* 32-bit or 16-bit bus */
+ popts->OTF_burst_chop_en = 0;
+ popts->burst_length = DDR_BL8;
+ } else {
+ popts->OTF_burst_chop_en = 1; /* on-the-fly burst chop */
+ popts->burst_length = DDR_OTF; /* on-the-fly BC4 and BL8 */
+ }
+#endif
+#else
+ popts->burst_length = DDR_BL4; /* has to be 4 for DDR2 */
+#endif
+
+ /* Choose ddr controller address mirror mode */
+#if defined(CONFIG_FSL_DDR3)
+ popts->mirrored_dimm = pdimm[0].mirrored_dimm;
+#endif
+
+ /* 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 derated latency */
+ popts->use_derated_caslat = 0;
+
+ /* 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);
+ }
+
+ /*
+ * 2T_EN setting
+ *
+ * Factors to consider for 2T_EN:
+ * - number of DIMMs installed
+ * - number of components, number of active ranks
+ * - how much time you want to spend playing around
+ */
+ popts->twoT_en = 0;
+ popts->threeT_en = 0;
+
+ /* for RDIMM, address parity enable */
+ popts->ap_en = 1;
+
+ /*
+ * 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 = 0x100;
+
+ /* Minimum CKE pulse width -- tCKE(MIN) */
+ popts->tCKE_clock_pulse_width_ps
+ = mclk_to_picos(FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR);
+
+ /*
+ * Window for four activates -- tFAW
+ *
+ * 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
+ * x16 wide columns only? Use 50000?
+ */
+ popts->tFAW_window_four_activates_ps = 37500;
+
+#elif defined(CONFIG_FSL_DDR3)
+ popts->tFAW_window_four_activates_ps = pdimm[0].tFAW_ps;
+#endif
+ popts->zq_en = 0;
+ popts->wrlvl_en = 0;
+#if defined(CONFIG_FSL_DDR3)
+ /*
+ * due to ddr3 dimm is fly-by topology
+ * we suggest to enable write leveling to
+ * meet the tQDSS under different loading.
+ */
+ popts->wrlvl_en = 1;
+ popts->zq_en = 1;
+ popts->wrlvl_override = 0;
+#endif
+
+ /*
+ * Check interleaving configuration from environment.
+ * Please refer to doc/README.fsl-ddr for the detail.
+ *
+ * If memory controller interleaving is enabled, then the data
+ * bus widths must be programmed identically for all memory controllers.
+ *
+ * XXX: Attempt to set all controllers to the same chip select
+ * interleaving mode. It will do a best effort to get the
+ * requested ranks interleaved together such that the result
+ * should be a subset of the requested configuration.
+ */
+#if (CONFIG_NUM_DDR_CONTROLLERS > 1)
+ if (!hwconfig_sub_f("fsl_ddr", "ctlr_intlv", buf))
+ goto done;
+
+ if (pdimm[0].n_ranks == 0) {
+ printf("There is no rank on CS0 for controller %d.\n", ctrl_num);
+ popts->memctl_interleaving = 0;
+ goto done;
+ }
+ popts->memctl_interleaving = 1;
+ /*
+ * test null first. if CONFIG_HWCONFIG is not defined
+ * hwconfig_arg_cmp returns non-zero
+ */
+ if (hwconfig_subarg_cmp_f("fsl_ddr", "ctlr_intlv",
+ "null", buf)) {
+ popts->memctl_interleaving = 0;
+ debug("memory controller interleaving disabled.\n");
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "cacheline", buf)) {
+ popts->memctl_interleaving_mode =
+ ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+ 0 : FSL_DDR_CACHE_LINE_INTERLEAVING;
+ popts->memctl_interleaving =
+ ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+ 0 : 1;
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "page", buf)) {
+ popts->memctl_interleaving_mode =
+ ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+ 0 : FSL_DDR_PAGE_INTERLEAVING;
+ popts->memctl_interleaving =
+ ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+ 0 : 1;
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "bank", buf)) {
+ popts->memctl_interleaving_mode =
+ ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+ 0 : FSL_DDR_BANK_INTERLEAVING;
+ popts->memctl_interleaving =
+ ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+ 0 : 1;
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "superbank", buf)) {
+ popts->memctl_interleaving_mode =
+ ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+ 0 : FSL_DDR_SUPERBANK_INTERLEAVING;
+ popts->memctl_interleaving =
+ ((CONFIG_NUM_DDR_CONTROLLERS == 3) && ctrl_num == 2) ?
+ 0 : 1;
+#if (CONFIG_NUM_DDR_CONTROLLERS == 3)
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "3way_1KB", buf)) {
+ popts->memctl_interleaving_mode =
+ FSL_DDR_3WAY_1KB_INTERLEAVING;
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "3way_4KB", buf)) {
+ popts->memctl_interleaving_mode =
+ FSL_DDR_3WAY_4KB_INTERLEAVING;
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "3way_8KB", buf)) {
+ popts->memctl_interleaving_mode =
+ FSL_DDR_3WAY_8KB_INTERLEAVING;
+#elif (CONFIG_NUM_DDR_CONTROLLERS == 4)
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "4way_1KB", buf)) {
+ popts->memctl_interleaving_mode =
+ FSL_DDR_4WAY_1KB_INTERLEAVING;
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "4way_4KB", buf)) {
+ popts->memctl_interleaving_mode =
+ FSL_DDR_4WAY_4KB_INTERLEAVING;
+ } else if (hwconfig_subarg_cmp_f("fsl_ddr",
+ "ctlr_intlv",
+ "4way_8KB", buf)) {
+ popts->memctl_interleaving_mode =
+ FSL_DDR_4WAY_8KB_INTERLEAVING;
+#endif
+ } else {
+ popts->memctl_interleaving = 0;
+ printf("hwconfig has unrecognized parameter for ctlr_intlv.\n");
+ }
+done:
+#endif
+ if ((hwconfig_sub_f("fsl_ddr", "bank_intlv", buf)) &&
+ (CONFIG_CHIP_SELECTS_PER_CTRL > 1)) {
+ /* test null first. if CONFIG_HWCONFIG is not defined,
+ * hwconfig_subarg_cmp_f returns non-zero */
+ if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv",
+ "null", buf))
+ debug("bank interleaving disabled.\n");
+ else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv",
+ "cs0_cs1", buf))
+ popts->ba_intlv_ctl = FSL_DDR_CS0_CS1;
+ else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv",
+ "cs2_cs3", buf))
+ popts->ba_intlv_ctl = FSL_DDR_CS2_CS3;
+ else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv",
+ "cs0_cs1_and_cs2_cs3", buf))
+ popts->ba_intlv_ctl = FSL_DDR_CS0_CS1_AND_CS2_CS3;
+ else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv",
+ "cs0_cs1_cs2_cs3", buf))
+ popts->ba_intlv_ctl = FSL_DDR_CS0_CS1_CS2_CS3;
+ else if (hwconfig_subarg_cmp_f("fsl_ddr", "bank_intlv",
+ "auto", buf))
+ popts->ba_intlv_ctl = auto_bank_intlv(pdimm);
+ else
+ printf("hwconfig has unrecognized parameter for bank_intlv.\n");
+ switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) {
+ case FSL_DDR_CS0_CS1_CS2_CS3:
+#if (CONFIG_DIMM_SLOTS_PER_CTLR == 1)
+ if (pdimm[0].n_ranks < 4) {
+ popts->ba_intlv_ctl = 0;
+ printf("Not enough bank(chip-select) for "
+ "CS0+CS1+CS2+CS3 on controller %d, "
+ "interleaving disabled!\n", ctrl_num);
+ }
+#elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
+#ifdef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE
+ if (pdimm[0].n_ranks == 4)
+ break;
+#endif
+ if ((pdimm[0].n_ranks < 2) && (pdimm[1].n_ranks < 2)) {
+ popts->ba_intlv_ctl = 0;
+ printf("Not enough bank(chip-select) for "
+ "CS0+CS1+CS2+CS3 on controller %d, "
+ "interleaving disabled!\n", ctrl_num);
+ }
+ if (pdimm[0].capacity != pdimm[1].capacity) {
+ popts->ba_intlv_ctl = 0;
+ printf("Not identical DIMM size for "
+ "CS0+CS1+CS2+CS3 on controller %d, "
+ "interleaving disabled!\n", ctrl_num);
+ }
+#endif
+ break;
+ case FSL_DDR_CS0_CS1:
+ if (pdimm[0].n_ranks < 2) {
+ popts->ba_intlv_ctl = 0;
+ printf("Not enough bank(chip-select) for "
+ "CS0+CS1 on controller %d, "
+ "interleaving disabled!\n", ctrl_num);
+ }
+ break;
+ case FSL_DDR_CS2_CS3:
+#if (CONFIG_DIMM_SLOTS_PER_CTLR == 1)
+ if (pdimm[0].n_ranks < 4) {
+ popts->ba_intlv_ctl = 0;
+ printf("Not enough bank(chip-select) for CS2+CS3 "
+ "on controller %d, interleaving disabled!\n", ctrl_num);
+ }
+#elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
+ if (pdimm[1].n_ranks < 2) {
+ popts->ba_intlv_ctl = 0;
+ printf("Not enough bank(chip-select) for CS2+CS3 "
+ "on controller %d, interleaving disabled!\n", ctrl_num);
+ }
+#endif
+ break;
+ case FSL_DDR_CS0_CS1_AND_CS2_CS3:
+#if (CONFIG_DIMM_SLOTS_PER_CTLR == 1)
+ if (pdimm[0].n_ranks < 4) {
+ popts->ba_intlv_ctl = 0;
+ printf("Not enough bank(CS) for CS0+CS1 and "
+ "CS2+CS3 on controller %d, "
+ "interleaving disabled!\n", ctrl_num);
+ }
+#elif (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
+ if ((pdimm[0].n_ranks < 2) || (pdimm[1].n_ranks < 2)) {
+ popts->ba_intlv_ctl = 0;
+ printf("Not enough bank(CS) for CS0+CS1 and "
+ "CS2+CS3 on controller %d, "
+ "interleaving disabled!\n", ctrl_num);
+ }
+#endif
+ break;
+ default:
+ popts->ba_intlv_ctl = 0;
+ break;
+ }
+ }
+
+ if (hwconfig_sub_f("fsl_ddr", "addr_hash", buf)) {
+ if (hwconfig_subarg_cmp_f("fsl_ddr", "addr_hash", "null", buf))
+ popts->addr_hash = 0;
+ else if (hwconfig_subarg_cmp_f("fsl_ddr", "addr_hash",
+ "true", buf))
+ popts->addr_hash = 1;
+ }
+
+ if (pdimm[0].n_ranks == 4)
+ popts->quad_rank_present = 1;
+
+ ddr_freq = get_ddr_freq(0) / 1000000;
+ if (popts->registered_dimm_en) {
+ popts->rcw_override = 1;
+ popts->rcw_1 = 0x000a5a00;
+ if (ddr_freq <= 800)
+ popts->rcw_2 = 0x00000000;
+ else if (ddr_freq <= 1066)
+ popts->rcw_2 = 0x00100000;
+ else if (ddr_freq <= 1333)
+ popts->rcw_2 = 0x00200000;
+ else
+ popts->rcw_2 = 0x00300000;
+ }
+
+ fsl_ddr_board_options(popts, pdimm, ctrl_num);
+
+ return 0;
+}
+
+void check_interleaving_options(fsl_ddr_info_t *pinfo)
+{
+ int i, j, k, check_n_ranks, intlv_invalid = 0;
+ unsigned int check_intlv, check_n_row_addr, check_n_col_addr;
+ unsigned long long check_rank_density;
+ struct dimm_params_s *dimm;
+ /*
+ * Check if all controllers are configured for memory
+ * controller interleaving. Identical dimms are recommended. At least
+ * the size, row and col address should be checked.
+ */
+ j = 0;
+ check_n_ranks = pinfo->dimm_params[0][0].n_ranks;
+ check_rank_density = pinfo->dimm_params[0][0].rank_density;
+ check_n_row_addr = pinfo->dimm_params[0][0].n_row_addr;
+ check_n_col_addr = pinfo->dimm_params[0][0].n_col_addr;
+ check_intlv = pinfo->memctl_opts[0].memctl_interleaving_mode;
+ for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+ dimm = &pinfo->dimm_params[i][0];
+ if (!pinfo->memctl_opts[i].memctl_interleaving) {
+ continue;
+ } else if (((check_rank_density != dimm->rank_density) ||
+ (check_n_ranks != dimm->n_ranks) ||
+ (check_n_row_addr != dimm->n_row_addr) ||
+ (check_n_col_addr != dimm->n_col_addr) ||
+ (check_intlv !=
+ pinfo->memctl_opts[i].memctl_interleaving_mode))){
+ intlv_invalid = 1;
+ break;
+ } else {
+ j++;
+ }
+
+ }
+ if (intlv_invalid) {
+ for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++)
+ pinfo->memctl_opts[i].memctl_interleaving = 0;
+ printf("Not all DIMMs are identical. "
+ "Memory controller interleaving disabled.\n");
+ } else {
+ switch (check_intlv) {
+ case FSL_DDR_CACHE_LINE_INTERLEAVING:
+ case FSL_DDR_PAGE_INTERLEAVING:
+ case FSL_DDR_BANK_INTERLEAVING:
+ case FSL_DDR_SUPERBANK_INTERLEAVING:
+ if (3 == CONFIG_NUM_DDR_CONTROLLERS)
+ k = 2;
+ else
+ k = CONFIG_NUM_DDR_CONTROLLERS;
+ break;
+ case FSL_DDR_3WAY_1KB_INTERLEAVING:
+ case FSL_DDR_3WAY_4KB_INTERLEAVING:
+ case FSL_DDR_3WAY_8KB_INTERLEAVING:
+ case FSL_DDR_4WAY_1KB_INTERLEAVING:
+ case FSL_DDR_4WAY_4KB_INTERLEAVING:
+ case FSL_DDR_4WAY_8KB_INTERLEAVING:
+ default:
+ k = CONFIG_NUM_DDR_CONTROLLERS;
+ break;
+ }
+ debug("%d of %d controllers are interleaving.\n", j, k);
+ if (j && (j != k)) {
+ for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++)
+ pinfo->memctl_opts[i].memctl_interleaving = 0;
+ printf("Not all controllers have compatible "
+ "interleaving mode. All disabled.\n");
+ }
+ }
+ debug("Checking interleaving options completed\n");
+}
+
+int fsl_use_spd(void)
+{
+ int use_spd = 0;
+
+#ifdef CONFIG_DDR_SPD
+ char buffer[HWCONFIG_BUFFER_SIZE];
+ char *buf = NULL;
+
+ /*
+ * Extract hwconfig from environment since we have not properly setup
+ * the environment but need it for ddr config params
+ */
+ if (getenv_f("hwconfig", buffer, sizeof(buffer)) > 0)
+ buf = buffer;
+
+ /* if hwconfig is not enabled, or "sdram" is not defined, use spd */
+ if (hwconfig_sub_f("fsl_ddr", "sdram", buf)) {
+ if (hwconfig_subarg_cmp_f("fsl_ddr", "sdram", "spd", buf))
+ use_spd = 1;
+ else if (hwconfig_subarg_cmp_f("fsl_ddr", "sdram",
+ "fixed", buf))
+ use_spd = 0;
+ else
+ use_spd = 1;
+ } else
+ use_spd = 1;
+#endif
+
+ return use_spd;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 7/8] ppc 8xxx: core DDR driver functions
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (5 preceding siblings ...)
2013-05-31 16:54 ` [PATCH 6/8] ppc 8xxx: DDR specific options Renaud Barbier
@ 2013-05-31 16:54 ` Renaud Barbier
2013-06-02 15:44 ` Sascha Hauer
2013-05-31 16:54 ` [PATCH 8/8] ppc 8xxx: remove interactive debugging Renaud Barbier
` (9 subsequent siblings)
16 siblings, 1 reply; 32+ messages in thread
From: Renaud Barbier @ 2013-05-31 16:54 UTC (permalink / raw)
To: barebox
This file is imported from U-Boot as is.
This code provides the 8xxx memory controller core driver interface.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/main.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 667 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/main.c
diff --git a/arch/ppc/ddr-8xxx/main.c b/arch/ppc/ddr-8xxx/main.c
new file mode 100644
index 0000000..7a8636d
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/main.c
@@ -0,0 +1,667 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+/*
+ * Generic driver for Freescale DDR/DDR2/DDR3 memory controller.
+ * Based on code from spd_sdram.c
+ * Author: James Yang [at freescale.com]
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <asm/fsl_ddr_sdram.h>
+#include <asm/fsl_law.h>
+
+#include "ddr.h"
+
+void fsl_ddr_set_lawbar(
+ const common_timing_params_t *memctl_common_params,
+ unsigned int memctl_interleaved,
+ unsigned int ctrl_num);
+void fsl_ddr_set_intl3r(const unsigned int granule_size);
+
+/* processor specific function */
+extern void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs,
+ unsigned int ctrl_num);
+
+#if defined(SPD_EEPROM_ADDRESS) || \
+ defined(SPD_EEPROM_ADDRESS1) || defined(SPD_EEPROM_ADDRESS2) || \
+ defined(SPD_EEPROM_ADDRESS3) || defined(SPD_EEPROM_ADDRESS4)
+#if (CONFIG_NUM_DDR_CONTROLLERS == 1) && (CONFIG_DIMM_SLOTS_PER_CTLR == 1)
+u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = {
+ [0][0] = SPD_EEPROM_ADDRESS,
+};
+#elif (CONFIG_NUM_DDR_CONTROLLERS == 1) && (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
+u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = {
+ [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */
+ [0][1] = SPD_EEPROM_ADDRESS2, /* controller 1 */
+};
+#elif (CONFIG_NUM_DDR_CONTROLLERS == 2) && (CONFIG_DIMM_SLOTS_PER_CTLR == 1)
+u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = {
+ [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */
+ [1][0] = SPD_EEPROM_ADDRESS2, /* controller 2 */
+};
+#elif (CONFIG_NUM_DDR_CONTROLLERS == 2) && (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
+u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = {
+ [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */
+ [0][1] = SPD_EEPROM_ADDRESS2, /* controller 1 */
+ [1][0] = SPD_EEPROM_ADDRESS3, /* controller 2 */
+ [1][1] = SPD_EEPROM_ADDRESS4, /* controller 2 */
+};
+#elif (CONFIG_NUM_DDR_CONTROLLERS == 3) && (CONFIG_DIMM_SLOTS_PER_CTLR == 1)
+u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = {
+ [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */
+ [1][0] = SPD_EEPROM_ADDRESS2, /* controller 2 */
+ [2][0] = SPD_EEPROM_ADDRESS3, /* controller 3 */
+};
+#elif (CONFIG_NUM_DDR_CONTROLLERS == 3) && (CONFIG_DIMM_SLOTS_PER_CTLR == 2)
+u8 spd_i2c_addr[CONFIG_NUM_DDR_CONTROLLERS][CONFIG_DIMM_SLOTS_PER_CTLR] = {
+ [0][0] = SPD_EEPROM_ADDRESS1, /* controller 1 */
+ [0][1] = SPD_EEPROM_ADDRESS2, /* controller 1 */
+ [1][0] = SPD_EEPROM_ADDRESS3, /* controller 2 */
+ [1][1] = SPD_EEPROM_ADDRESS4, /* controller 2 */
+ [2][0] = SPD_EEPROM_ADDRESS5, /* controller 3 */
+ [2][1] = SPD_EEPROM_ADDRESS6, /* controller 3 */
+};
+
+#endif
+
+static void __get_spd(generic_spd_eeprom_t *spd, u8 i2c_address)
+{
+ int ret = i2c_read(i2c_address, 0, 1, (uchar *)spd,
+ sizeof(generic_spd_eeprom_t));
+
+ if (ret) {
+ if (i2c_address ==
+#ifdef SPD_EEPROM_ADDRESS
+ SPD_EEPROM_ADDRESS
+#elif defined(SPD_EEPROM_ADDRESS1)
+ SPD_EEPROM_ADDRESS1
+#endif
+ ) {
+ printf("DDR: failed to read SPD from address %u\n",
+ i2c_address);
+ } else {
+ debug("DDR: failed to read SPD from address %u\n",
+ i2c_address);
+ }
+ memset(spd, 0, sizeof(generic_spd_eeprom_t));
+ }
+}
+
+__attribute__((weak, alias("__get_spd")))
+void get_spd(generic_spd_eeprom_t *spd, u8 i2c_address);
+
+void fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd,
+ unsigned int ctrl_num)
+{
+ unsigned int i;
+ unsigned int i2c_address = 0;
+
+ if (ctrl_num >= CONFIG_NUM_DDR_CONTROLLERS) {
+ printf("%s unexpected ctrl_num = %u\n", __FUNCTION__, ctrl_num);
+ return;
+ }
+
+ for (i = 0; i < CONFIG_DIMM_SLOTS_PER_CTLR; i++) {
+ i2c_address = spd_i2c_addr[ctrl_num][i];
+ get_spd(&(ctrl_dimms_spd[i]), i2c_address);
+ }
+}
+#else
+void fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd,
+ unsigned int ctrl_num)
+{
+}
+#endif /* SPD_EEPROM_ADDRESSx */
+
+/*
+ * 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"
+};
+
+const char * step_to_string(unsigned int step) {
+
+ unsigned int s = __ilog2(step);
+
+ if ((1 << s) != step)
+ return step_string_tbl[7];
+
+ return step_string_tbl[s];
+}
+
+static unsigned long long __step_assign_addresses(fsl_ddr_info_t *pinfo,
+ unsigned int dbw_cap_adj[])
+{
+ int i, j;
+ unsigned long long total_mem, current_mem_base, total_ctlr_mem;
+ unsigned long long rank_density, ctlr_density = 0;
+
+ /*
+ * 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_opts[i].data_bus_width) {
+ case 2:
+ /* 16-bit */
+ for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
+ unsigned int dw;
+ if (!pinfo->dimm_params[i][j].n_ranks)
+ continue;
+ dw = pinfo->dimm_params[i][j].primary_sdram_width;
+ if ((dw == 72 || dw == 64)) {
+ dbw_cap_adj[i] = 2;
+ break;
+ } else if ((dw == 40 || dw == 32)) {
+ dbw_cap_adj[i] = 1;
+ break;
+ }
+ }
+ break;
+
+ case 1:
+ /* 32-bit */
+ for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
+ unsigned int dw;
+ dw = pinfo->dimm_params[i][j].data_width;
+ if (pinfo->dimm_params[i][j].n_ranks
+ && (dw == 72 || dw == 64)) {
+ /*
+ * FIXME: can't really do it
+ * like this because this just
+ * further reduces the memory
+ */
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ dbw_cap_adj[i] = 1;
+ }
+ break;
+
+ case 0:
+ /* 64-bit */
+ break;
+
+ default:
+ printf("unexpected data bus width "
+ "specified controller %u\n", i);
+ return 1;
+ }
+ debug("dbw_cap_adj[%d]=%d\n", i, dbw_cap_adj[i]);
+ }
+
+ current_mem_base = 0ull;
+ total_mem = 0;
+ if (pinfo->memctl_opts[0].memctl_interleaving) {
+ rank_density = pinfo->dimm_params[0][0].rank_density >>
+ dbw_cap_adj[0];
+ switch (pinfo->memctl_opts[0].ba_intlv_ctl &
+ FSL_DDR_CS0_CS1_CS2_CS3) {
+ case FSL_DDR_CS0_CS1_CS2_CS3:
+ ctlr_density = 4 * rank_density;
+ break;
+ case FSL_DDR_CS0_CS1:
+ case FSL_DDR_CS0_CS1_AND_CS2_CS3:
+ ctlr_density = 2 * rank_density;
+ break;
+ case FSL_DDR_CS2_CS3:
+ default:
+ ctlr_density = rank_density;
+ break;
+ }
+ debug("rank density is 0x%llx, ctlr density is 0x%llx\n",
+ rank_density, ctlr_density);
+ for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+ if (pinfo->memctl_opts[i].memctl_interleaving) {
+ switch (pinfo->memctl_opts[i].memctl_interleaving_mode) {
+ case FSL_DDR_CACHE_LINE_INTERLEAVING:
+ case FSL_DDR_PAGE_INTERLEAVING:
+ case FSL_DDR_BANK_INTERLEAVING:
+ case FSL_DDR_SUPERBANK_INTERLEAVING:
+ total_ctlr_mem = 2 * ctlr_density;
+ break;
+ case FSL_DDR_3WAY_1KB_INTERLEAVING:
+ case FSL_DDR_3WAY_4KB_INTERLEAVING:
+ case FSL_DDR_3WAY_8KB_INTERLEAVING:
+ total_ctlr_mem = 3 * ctlr_density;
+ break;
+ case FSL_DDR_4WAY_1KB_INTERLEAVING:
+ case FSL_DDR_4WAY_4KB_INTERLEAVING:
+ case FSL_DDR_4WAY_8KB_INTERLEAVING:
+ total_ctlr_mem = 4 * ctlr_density;
+ break;
+ default:
+ panic("Unknown interleaving mode");
+ }
+ pinfo->common_timing_params[i].base_address =
+ current_mem_base;
+ pinfo->common_timing_params[i].total_mem =
+ total_ctlr_mem;
+ total_mem = current_mem_base + total_ctlr_mem;
+ debug("ctrl %d base 0x%llx\n", i, current_mem_base);
+ debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem);
+ } else {
+ /* when 3rd controller not interleaved */
+ current_mem_base = total_mem;
+ total_ctlr_mem = 0;
+ pinfo->common_timing_params[i].base_address =
+ current_mem_base;
+ for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
+ unsigned long long cap =
+ pinfo->dimm_params[i][j].capacity >> dbw_cap_adj[i];
+ pinfo->dimm_params[i][j].base_address =
+ current_mem_base;
+ debug("ctrl %d dimm %d base 0x%llx\n", i, j, current_mem_base);
+ current_mem_base += cap;
+ total_ctlr_mem += cap;
+ }
+ debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem);
+ pinfo->common_timing_params[i].total_mem =
+ total_ctlr_mem;
+ total_mem += total_ctlr_mem;
+ }
+ }
+ } else {
+ /*
+ * Simple linear assignment if memory
+ * controllers are not interleaved.
+ */
+ for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+ total_ctlr_mem = 0;
+ pinfo->common_timing_params[i].base_address =
+ current_mem_base;
+ for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
+ /* Compute DIMM base addresses. */
+ unsigned long long cap =
+ pinfo->dimm_params[i][j].capacity >> dbw_cap_adj[i];
+ pinfo->dimm_params[i][j].base_address =
+ current_mem_base;
+ debug("ctrl %d dimm %d base 0x%llx\n", i, j, current_mem_base);
+ current_mem_base += cap;
+ total_ctlr_mem += cap;
+ }
+ debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem);
+ pinfo->common_timing_params[i].total_mem =
+ total_ctlr_mem;
+ total_mem += total_ctlr_mem;
+ }
+ }
+ debug("Total mem by %s is 0x%llx\n", __func__, total_mem);
+
+ return total_mem;
+}
+
+/* Use weak function to allow board file to override the address assignment */
+__attribute__((weak, alias("__step_assign_addresses")))
+unsigned long long step_assign_addresses(fsl_ddr_info_t *pinfo,
+ unsigned int dbw_cap_adj[]);
+
+unsigned long long
+fsl_ddr_compute(fsl_ddr_info_t *pinfo, unsigned int start_step,
+ unsigned int size_only)
+{
+ unsigned int i, j;
+ unsigned long long total_mem = 0;
+
+ fsl_ddr_cfg_regs_t *ddr_reg = pinfo->fsl_ddr_config_reg;
+ common_timing_params_t *timing_params = pinfo->common_timing_params;
+
+ /* 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(start_step));
+
+ switch (start_step) {
+ case STEP_GET_SPD:
+#if defined(CONFIG_DDR_SPD) || defined(CONFIG_SPD_EEPROM)
+ /* STEP 1: Gather all DIMM SPD data */
+ for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+ fsl_ddr_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;
+ generic_spd_eeprom_t *spd =
+ &(pinfo->spd_installed_dimms[i][j]);
+ dimm_params_t *pdimm =
+ &(pinfo->dimm_params[i][j]);
+
+ retval = compute_dimm_parameters(spd, pdimm, i);
+#ifdef CONFIG_SYS_DDR_RAW_TIMING
+ if (!i && !j && retval) {
+ printf("SPD error on controller %d! "
+ "Trying fallback to raw timing "
+ "calculation\n", i);
+ fsl_ddr_get_dimm_params(pdimm, i, j);
+ }
+#else
+ if (retval == 2) {
+ printf("Error: compute_dimm_parameters"
+ " non-zero returned FATAL value "
+ "for memctl=%u dimm=%u\n", i, j);
+ return 0;
+ }
+#endif
+ if (retval) {
+ debug("Warning: compute_dimm_parameters"
+ " non-zero return value for memctl=%u "
+ "dimm=%u\n", i, j);
+ }
+ }
+ }
+
+#elif defined(CONFIG_SYS_DDR_RAW_TIMING)
+ case STEP_COMPUTE_DIMM_PARMS:
+ for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+ for (j = 0; j < CONFIG_DIMM_SLOTS_PER_CTLR; j++) {
+ dimm_params_t *pdimm =
+ &(pinfo->dimm_params[i][j]);
+ fsl_ddr_get_dimm_params(pdimm, i, j);
+ }
+ }
+ debug("Filling dimm parameters from board specific file\n");
+#endif
+ 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],
+ &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(
+ timing_params[i].all_DIMMs_registered,
+ &pinfo->memctl_opts[i],
+ pinfo->dimm_params[i], i);
+ }
+ case STEP_ASSIGN_ADDRESSES:
+ /* STEP 5: Assign addresses to chip selects */
+ check_interleaving_options(pinfo);
+ total_mem = step_assign_addresses(pinfo, dbw_capacity_adjust);
+
+ case STEP_COMPUTE_REGS:
+ /* STEP 6: compute controller register values */
+ debug("FSL Memory ctrl register computation\n");
+ for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+ if (timing_params[i].ndimms_present == 0) {
+ memset(&ddr_reg[i], 0,
+ sizeof(fsl_ddr_cfg_regs_t));
+ continue;
+ }
+
+ compute_fsl_memctl_config_regs(
+ &pinfo->memctl_opts[i],
+ &ddr_reg[i], &timing_params[i],
+ pinfo->dimm_params[i],
+ dbw_capacity_adjust[i],
+ size_only);
+ }
+
+ default:
+ break;
+ }
+
+ {
+ /*
+ * 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++) {
+ fsl_ddr_cfg_regs_t *reg = &ddr_reg[i];
+ if (reg->cs[j].config & 0x80000000) {
+ unsigned int end;
+ end = reg->cs[j].bnds & 0xFFF;
+ if (end > max_end) {
+ max_end = end;
+ }
+ }
+ }
+ }
+
+ total_mem = 1 + (((unsigned long long)max_end << 24ULL)
+ | 0xFFFFFFULL);
+ }
+
+ return total_mem;
+}
+
+/*
+ * 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 law_memctl = LAW_TRGT_IF_DDR_1;
+ unsigned long long total_memory;
+ fsl_ddr_info_t info;
+
+ /* Reset info structure. */
+ memset(&info, 0, sizeof(fsl_ddr_info_t));
+
+ /* Compute it once normally. */
+#ifdef CONFIG_FSL_DDR_INTERACTIVE
+ if (tstc() && (getc() == 'd')) { /* we got a key press of 'd' */
+ total_memory = fsl_ddr_interactive(&info, 0);
+ } else if (fsl_ddr_interactive_env_var_exists()) {
+ total_memory = fsl_ddr_interactive(&info, 1);
+ } else
+#endif
+ total_memory = fsl_ddr_compute(&info, STEP_GET_SPD, 0);
+
+ /* setup 3-way interleaving before enabling DDRC */
+ if (info.memctl_opts[0].memctl_interleaving) {
+ switch (info.memctl_opts[0].memctl_interleaving_mode) {
+ case FSL_DDR_3WAY_1KB_INTERLEAVING:
+ case FSL_DDR_3WAY_4KB_INTERLEAVING:
+ case FSL_DDR_3WAY_8KB_INTERLEAVING:
+ fsl_ddr_set_intl3r(
+ info.memctl_opts[0].memctl_interleaving_mode);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* 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_set_memctl_regs(&(info.fsl_ddr_config_reg[i]), i);
+ }
+
+ /* program LAWs */
+ for (i = 0; i < CONFIG_NUM_DDR_CONTROLLERS; i++) {
+ if (info.memctl_opts[i].memctl_interleaving) {
+ switch (info.memctl_opts[i].memctl_interleaving_mode) {
+ case FSL_DDR_CACHE_LINE_INTERLEAVING:
+ case FSL_DDR_PAGE_INTERLEAVING:
+ case FSL_DDR_BANK_INTERLEAVING:
+ case FSL_DDR_SUPERBANK_INTERLEAVING:
+ if (i == 0) {
+ law_memctl = LAW_TRGT_IF_DDR_INTRLV;
+ fsl_ddr_set_lawbar(&info.common_timing_params[i],
+ law_memctl, i);
+ } else if (i == 2) {
+ law_memctl = LAW_TRGT_IF_DDR_INTLV_34;
+ fsl_ddr_set_lawbar(&info.common_timing_params[i],
+ law_memctl, i);
+ }
+ break;
+ case FSL_DDR_3WAY_1KB_INTERLEAVING:
+ case FSL_DDR_3WAY_4KB_INTERLEAVING:
+ case FSL_DDR_3WAY_8KB_INTERLEAVING:
+ law_memctl = LAW_TRGT_IF_DDR_INTLV_123;
+ if (i == 0) {
+ fsl_ddr_set_lawbar(&info.common_timing_params[i],
+ law_memctl, i);
+ }
+ break;
+ case FSL_DDR_4WAY_1KB_INTERLEAVING:
+ case FSL_DDR_4WAY_4KB_INTERLEAVING:
+ case FSL_DDR_4WAY_8KB_INTERLEAVING:
+ law_memctl = LAW_TRGT_IF_DDR_INTLV_1234;
+ if (i == 0)
+ fsl_ddr_set_lawbar(&info.common_timing_params[i],
+ law_memctl, i);
+ /* place holder for future 4-way interleaving */
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (i) {
+ case 0:
+ law_memctl = LAW_TRGT_IF_DDR_1;
+ break;
+ case 1:
+ law_memctl = LAW_TRGT_IF_DDR_2;
+ break;
+ case 2:
+ law_memctl = LAW_TRGT_IF_DDR_3;
+ break;
+ case 3:
+ law_memctl = LAW_TRGT_IF_DDR_4;
+ break;
+ default:
+ break;
+ }
+ fsl_ddr_set_lawbar(&info.common_timing_params[i],
+ law_memctl, i);
+ }
+ }
+
+ debug("total_memory by %s = %llu\n", __func__, total_memory);
+
+#if !defined(CONFIG_PHYS_64BIT)
+ /* Check for 4G or more. Bad. */
+ if (total_memory >= (1ull << 32)) {
+ printf("Detected %lld MB of memory\n", total_memory >> 20);
+ printf(" This U-Boot only supports < 4G of DDR\n");
+ printf(" You could rebuild it with CONFIG_PHYS_64BIT\n");
+ printf(" "); /* re-align to match init_func_ram print */
+ total_memory = CONFIG_MAX_MEM_MAPPED;
+ }
+#endif
+
+ return total_memory;
+}
+
+/*
+ * fsl_ddr_sdram_size() - This function only returns the size of the total
+ * memory without setting ddr control registers.
+ */
+phys_size_t
+fsl_ddr_sdram_size(void)
+{
+ fsl_ddr_info_t info;
+ unsigned long long total_memory = 0;
+
+ memset(&info, 0 , sizeof(fsl_ddr_info_t));
+
+ /* Compute it once normally. */
+ total_memory = fsl_ddr_compute(&info, STEP_GET_SPD, 1);
+
+ return total_memory;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 7/8] ppc 8xxx: core DDR driver functions
2013-05-31 16:54 ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
@ 2013-06-02 15:44 ` Sascha Hauer
2013-06-12 14:51 ` Renaud Barbier
0 siblings, 1 reply; 32+ messages in thread
From: Sascha Hauer @ 2013-06-02 15:44 UTC (permalink / raw)
To: Renaud Barbier; +Cc: barebox
On Fri, May 31, 2013 at 05:54:03PM +0100, Renaud Barbier wrote:
> This file is imported from U-Boot as is.
>
> This code provides the 8xxx memory controller core driver interface.
>
> Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
> ---
> arch/ppc/ddr-8xxx/main.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 667 insertions(+), 0 deletions(-)
> create mode 100644 arch/ppc/ddr-8xxx/main.c
>
[...]
> +
> +/*
> + * 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)
> +{
THe behaviour of this function is defined by:
CONFIG_BUFFER_SIZE
CONFIG_CHIP_SELECT_QUAD_CAPABLE
CONFIG_CHIP_SELECTS_PER_CTRL
CONFIG_DDR_ECC
CONFIG_DDR_SPD
CONFIG_DIMM_SLOTS_PER_CTLR
CONFIG_E500MC
CONFIG_E6500
CONFIG_ECC_INIT_VIA_DDRCONTROLLER
CONFIG_FSL_DDR1
CONFIG_FSL_DDR2
CONFIG_FSL_DDR3
CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE
CONFIG_FSL_DDR_INTERACTIVE
CONFIG_FSL_SDRAM_TYPE
CONFIG_HWCONFIG
CONFIG_MAX_MEM_MAPPED
CONFIG_MEM_INIT_VALUE
CONFIG_NUM_DDR_CONTROLLERS
CONFIG_PHYS_64BIT
CONFIG_SPD_EEPROM
CONFIG_SYS_DDR_RAW_TIMING
CONFIG_SYS_DIMM_SLOTS_PER_CTLR
CONFIG_SYS_FSL_DDR_VER
CONFIG_SYS_IMMR
CONFIG_SYS_MPC8xxx_DDR2_ADDR
CONFIG_SYS_MPC8xxx_DDR3_ADDR
CONFIG_SYS_MPC8xxx_DDR_ADDR
CONFIG_SYS_NUM_DDR_CTLRS
Some of these a board specific, others are SoC specific, but all are
input parameters to this function. The prototype should really be:
phys_size_t mpc8xxx_setup_ddr(struct mpc8xxx_ddr_params *)
(or maybe mpc8xxx_setup_ddr1/2/3 to be able to let the linker throw away
some unused code)
Code written in CPP rather than C was one of the major reasons why I
originally wrote barebox, so please let's not reintroduce this.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 7/8] ppc 8xxx: core DDR driver functions
2013-06-02 15:44 ` Sascha Hauer
@ 2013-06-12 14:51 ` Renaud Barbier
0 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-12 14:51 UTC (permalink / raw)
To: Sascha Hauer; +Cc: barebox
On 02/06/2013 16:44, Sascha Hauer wrote:
> On Fri, May 31, 2013 at 05:54:03PM +0100, Renaud Barbier wrote:
>> This file is imported from U-Boot as is.
>>
>> This code provides the 8xxx memory controller core driver interface.
>>
>> Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
>> ---
>> arch/ppc/ddr-8xxx/main.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++
>> 1 files changed, 667 insertions(+), 0 deletions(-)
>> create mode 100644 arch/ppc/ddr-8xxx/main.c
>>
>
> [...]
>
>> +
>> +/*
>> + * 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)
>> +{
>
> THe behaviour of this function is defined by:
>
> CONFIG_BUFFER_SIZE
> CONFIG_CHIP_SELECT_QUAD_CAPABLE
> CONFIG_CHIP_SELECTS_PER_CTRL
> CONFIG_DDR_ECC
> CONFIG_DDR_SPD
> CONFIG_DIMM_SLOTS_PER_CTLR
> CONFIG_E500MC
> CONFIG_E6500
> CONFIG_ECC_INIT_VIA_DDRCONTROLLER
> CONFIG_FSL_DDR1
> CONFIG_FSL_DDR2
> CONFIG_FSL_DDR3
> CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE
> CONFIG_FSL_DDR_INTERACTIVE
> CONFIG_FSL_SDRAM_TYPE
> CONFIG_HWCONFIG
> CONFIG_MAX_MEM_MAPPED
> CONFIG_MEM_INIT_VALUE
> CONFIG_NUM_DDR_CONTROLLERS
> CONFIG_PHYS_64BIT
> CONFIG_SPD_EEPROM
> CONFIG_SYS_DDR_RAW_TIMING
> CONFIG_SYS_DIMM_SLOTS_PER_CTLR
> CONFIG_SYS_FSL_DDR_VER
> CONFIG_SYS_IMMR
> CONFIG_SYS_MPC8xxx_DDR2_ADDR
> CONFIG_SYS_MPC8xxx_DDR3_ADDR
> CONFIG_SYS_MPC8xxx_DDR_ADDR
> CONFIG_SYS_NUM_DDR_CTLRS
>
> Some of these a board specific, others are SoC specific, but all are
> input parameters to this function. The prototype should really be:
>
> phys_size_t mpc8xxx_setup_ddr(struct mpc8xxx_ddr_params *)
Actually there is already a structure "memctl_options_s" that could be
either extended or point to a board option structure. It is updated by
the board specific function: fsl_ddr_board_options.
>
> (or maybe mpc8xxx_setup_ddr1/2/3 to be able to let the linker throw away
> some unused code)
>
> Code written in CPP rather than C was one of the major reasons why I
> originally wrote barebox, so please let's not reintroduce this.
>
> Sascha
>
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 8/8] ppc 8xxx: remove interactive debugging
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (6 preceding siblings ...)
2013-05-31 16:54 ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
@ 2013-05-31 16:54 ` Renaud Barbier
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
` (8 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-05-31 16:54 UTC (permalink / raw)
To: barebox
The interactive debugging of SPD data and memory controller settings
are removed as the code supporting them is not included and for lack
of early uart initialization.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/ddr.h | 2 --
arch/ppc/ddr-8xxx/main.c | 10 +---------
2 files changed, 1 insertions(+), 11 deletions(-)
diff --git a/arch/ppc/ddr-8xxx/ddr.h b/arch/ppc/ddr-8xxx/ddr.h
index 4dd55fc..714f5e3 100644
--- a/arch/ppc/ddr-8xxx/ddr.h
+++ b/arch/ppc/ddr-8xxx/ddr.h
@@ -86,8 +86,6 @@ void fsl_ddr_set_lawbar(
unsigned int memctl_interleaved,
unsigned int ctrl_num);
-int fsl_ddr_interactive_env_var_exists(void);
-unsigned long long fsl_ddr_interactive(fsl_ddr_info_t *pinfo, int var_is_set);
void fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd,
unsigned int ctrl_num);
diff --git a/arch/ppc/ddr-8xxx/main.c b/arch/ppc/ddr-8xxx/main.c
index 7a8636d..c71d4b3 100644
--- a/arch/ppc/ddr-8xxx/main.c
+++ b/arch/ppc/ddr-8xxx/main.c
@@ -535,15 +535,7 @@ phys_size_t fsl_ddr_sdram(void)
/* Reset info structure. */
memset(&info, 0, sizeof(fsl_ddr_info_t));
- /* Compute it once normally. */
-#ifdef CONFIG_FSL_DDR_INTERACTIVE
- if (tstc() && (getc() == 'd')) { /* we got a key press of 'd' */
- total_memory = fsl_ddr_interactive(&info, 0);
- } else if (fsl_ddr_interactive_env_var_exists()) {
- total_memory = fsl_ddr_interactive(&info, 1);
- } else
-#endif
- total_memory = fsl_ddr_compute(&info, STEP_GET_SPD, 0);
+ total_memory = fsl_ddr_compute(&info, STEP_GET_SPD, 0);
/* setup 3-way interleaving before enabling DDRC */
if (info.memctl_opts[0].memctl_interleaving) {
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v2 0/8] DDR2 memory initialisaion
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (7 preceding siblings ...)
2013-05-31 16:54 ` [PATCH 8/8] ppc 8xxx: remove interactive debugging Renaud Barbier
@ 2013-06-25 10:45 ` Renaud Barbier
2013-06-26 6:34 ` Sascha Hauer
` (9 more replies)
2013-06-25 10:45 ` [PATCH 1/8] common: DDR2 SPD checksum Renaud Barbier
` (7 subsequent siblings)
16 siblings, 10 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-25 10:45 UTC (permalink / raw)
To: barebox
This patch set adds memory initialisation through SPD data for
Freescale CPUs such as the mpc8544. It is based on U-Boot tree id
a71d45d706a5b51c34
Support for DDR1, DDR3 and memory interleaving has been removed as
the code is aimed at the GEIP DA923RC board. Support for this
board will be submitted after approval of these patches by the
barebox community.
In addition, early I2C read access has been added so that the SPD
data can be retrieved from the I2C eeprom.
Renaud Barbier (8):
common: DDR2 SPD checksum.
ppc asm: DDR headers
ppc 8xxx: DDR headers
ppc 8xxx: DIMM parameters calculation
ppc 8xxx: DDR utility and memory options
ppx 8xxx: DDR registers value calculation
ppc 8xxx: core DDR driver functions
ppc 85xx: early I2C support
arch/ppc/ddr-8xxx/common_timing_params.h | 44 +++
arch/ppc/ddr-8xxx/ctrl_regs.c | 425 ++++++++++++++++++++++++++
arch/ppc/ddr-8xxx/ddr.h | 105 +++++++
arch/ppc/ddr-8xxx/ddr2_dimm_params.c | 303 ++++++++++++++++++
arch/ppc/ddr-8xxx/ddr2_setctrl.c | 58 ++++
arch/ppc/ddr-8xxx/lc_common_dimm_params.c | 214 +++++++++++++
arch/ppc/ddr-8xxx/main.c | 238 ++++++++++++++
arch/ppc/ddr-8xxx/options.c | 111 +++++++
arch/ppc/ddr-8xxx/util.c | 100 ++++++
arch/ppc/include/asm/fsl_ddr_dimm_params.h | 60 ++++
arch/ppc/include/asm/fsl_ddr_sdram.h | 131 ++++++++-
arch/ppc/mach-mpc85xx/fsl_i2c.c | 253 +++++++++++++++
arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h | 18 ++
common/Makefile | 1 +
common/ddr_spd.c | 39 +++
include/ddr_spd.h | 135 ++++++++
16 files changed, 2231 insertions(+), 4 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/common_timing_params.h
create mode 100644 arch/ppc/ddr-8xxx/ctrl_regs.c
create mode 100644 arch/ppc/ddr-8xxx/ddr.h
create mode 100644 arch/ppc/ddr-8xxx/ddr2_dimm_params.c
create mode 100644 arch/ppc/ddr-8xxx/ddr2_setctrl.c
create mode 100644 arch/ppc/ddr-8xxx/lc_common_dimm_params.c
create mode 100644 arch/ppc/ddr-8xxx/main.c
create mode 100644 arch/ppc/ddr-8xxx/options.c
create mode 100644 arch/ppc/ddr-8xxx/util.c
create mode 100644 arch/ppc/include/asm/fsl_ddr_dimm_params.h
create mode 100644 arch/ppc/mach-mpc85xx/fsl_i2c.c
create mode 100644 arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
create mode 100644 common/ddr_spd.c
create mode 100644 include/ddr_spd.h
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v2 0/8] DDR2 memory initialisaion
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
@ 2013-06-26 6:34 ` Sascha Hauer
2013-06-26 17:33 ` [PATCH v3 0/8] DDR2 memory initialisation Renaud Barbier
` (8 subsequent siblings)
9 siblings, 0 replies; 32+ messages in thread
From: Sascha Hauer @ 2013-06-26 6:34 UTC (permalink / raw)
To: Renaud Barbier; +Cc: barebox
Hi Renaud,
On Tue, Jun 25, 2013 at 11:45:29AM +0100, Renaud Barbier wrote:
> This patch set adds memory initialisation through SPD data for
> Freescale CPUs such as the mpc8544. It is based on U-Boot tree id
> a71d45d706a5b51c34
>
> Support for DDR1, DDR3 and memory interleaving has been removed as
> the code is aimed at the GEIP DA923RC board. Support for this
> board will be submitted after approval of these patches by the
> barebox community.
>
> In addition, early I2C read access has been added so that the SPD
> data can be retrieved from the I2C eeprom.
This code won't win beauty prices. However, since my main concern
of having lots of board specific defines in common code are resolved
and this code only touches architecture stuff I should be fine with
it.
There's still a lot of room for improvements. I could probably review
this code to death, but I think we both have better things to do ;)
So let's go for it with the little comment fixed I made inline.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v3 0/8] DDR2 memory initialisation
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
2013-06-26 6:34 ` Sascha Hauer
@ 2013-06-26 17:33 ` Renaud Barbier
2013-06-27 6:39 ` Sascha Hauer
2013-06-26 17:33 ` [PATCH 1/8] common: DDR2 SPD checksum Renaud Barbier
` (7 subsequent siblings)
9 siblings, 1 reply; 32+ messages in thread
From: Renaud Barbier @ 2013-06-26 17:33 UTC (permalink / raw)
To: barebox
This patch set addresses the following issues from the version v2.
It renames the function ddr2_spd_check to a more meaningful name.
All commits acknowledge that the base of the source code is thanks
to the U-Boot community.
Renaud Barbier (8):
common: DDR2 SPD checksum.
ppc asm: DDR headers
ppc 8xxx: DDR headers
ppc 8xxx: DIMM parameters calculation
ppc 8xxx: DDR utility and memory options
ppx 8xxx: DDR registers value calculation
ppc 8xxx: core DDR driver functions
ppc 85xx: early I2C support
arch/ppc/ddr-8xxx/common_timing_params.h | 44 +++
arch/ppc/ddr-8xxx/ctrl_regs.c | 425 ++++++++++++++++++++++++++
arch/ppc/ddr-8xxx/ddr.h | 105 +++++++
arch/ppc/ddr-8xxx/ddr2_dimm_params.c | 303 ++++++++++++++++++
arch/ppc/ddr-8xxx/ddr2_setctrl.c | 58 ++++
arch/ppc/ddr-8xxx/lc_common_dimm_params.c | 214 +++++++++++++
arch/ppc/ddr-8xxx/main.c | 238 ++++++++++++++
arch/ppc/ddr-8xxx/options.c | 111 +++++++
arch/ppc/ddr-8xxx/util.c | 100 ++++++
arch/ppc/include/asm/fsl_ddr_dimm_params.h | 60 ++++
arch/ppc/include/asm/fsl_ddr_sdram.h | 131 ++++++++-
arch/ppc/mach-mpc85xx/fsl_i2c.c | 253 +++++++++++++++
arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h | 18 ++
common/Makefile | 1 +
common/ddr_spd.c | 39 +++
include/ddr_spd.h | 135 ++++++++
16 files changed, 2231 insertions(+), 4 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/common_timing_params.h
create mode 100644 arch/ppc/ddr-8xxx/ctrl_regs.c
create mode 100644 arch/ppc/ddr-8xxx/ddr.h
create mode 100644 arch/ppc/ddr-8xxx/ddr2_dimm_params.c
create mode 100644 arch/ppc/ddr-8xxx/ddr2_setctrl.c
create mode 100644 arch/ppc/ddr-8xxx/lc_common_dimm_params.c
create mode 100644 arch/ppc/ddr-8xxx/main.c
create mode 100644 arch/ppc/ddr-8xxx/options.c
create mode 100644 arch/ppc/ddr-8xxx/util.c
create mode 100644 arch/ppc/include/asm/fsl_ddr_dimm_params.h
create mode 100644 arch/ppc/mach-mpc85xx/fsl_i2c.c
create mode 100644 arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
create mode 100644 common/ddr_spd.c
create mode 100644 include/ddr_spd.h
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 1/8] common: DDR2 SPD checksum.
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
2013-06-26 6:34 ` Sascha Hauer
2013-06-26 17:33 ` [PATCH v3 0/8] DDR2 memory initialisation Renaud Barbier
@ 2013-06-26 17:33 ` Renaud Barbier
2013-06-26 17:33 ` [PATCH 2/8] ppc asm: DDR headers Renaud Barbier
` (6 subsequent siblings)
9 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-26 17:33 UTC (permalink / raw)
To: barebox
The code calculates the DDR2 SPD checksum as per JEDEC standard
No 21-C Appendix X (revision 1.2)
The code is based on the equivalent files from U-Boot version
git-a71d45d.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
common/Makefile | 1 +
common/ddr_spd.c | 39 +++++++++++++++
include/ddr_spd.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 175 insertions(+), 0 deletions(-)
create mode 100644 common/ddr_spd.c
create mode 100644 include/ddr_spd.h
diff --git a/common/Makefile b/common/Makefile
index 2f0bd34..fc23421 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_CMD_LOADS) += s_record.o
obj-$(CONFIG_OFTREE) += oftree.o
obj-y += memory.o
+obj-$(CONFIG_DDR_SPD) += ddr_spd.o
obj-y += memory_display.o
obj-$(CONFIG_MALLOC_DLMALLOC) += dlmalloc.o
obj-$(CONFIG_MALLOC_TLSF) += tlsf_malloc.o
diff --git a/common/ddr_spd.c b/common/ddr_spd.c
new file mode 100644
index 0000000..c8b73ff
--- /dev/null
+++ b/common/ddr_spd.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <ddr_spd.h>
+
+uint32_t ddr2_spd_checksum_pass(const struct ddr2_spd_eeprom_s *spd)
+{
+ uint32_t i, cksum = 0;
+ const uint8_t *buf = (const uint8_t *)spd;
+ uint8_t rev, spd_cksum;
+
+ rev = spd->spd_rev;
+ spd_cksum = spd->cksum;
+
+ /* Rev 1.X or less supported by this code */
+ if (rev >= 0x20)
+ goto error;
+
+ /*
+ * The checksum is calculated on the first 64 bytes
+ * of the SPD as per JEDEC specification.
+ */
+ for (i = 0; i < 63; i++)
+ cksum += *buf++;
+ cksum &= 0xFF;
+
+ if (cksum != spd_cksum)
+ goto error;
+
+ return 0;
+error:
+ return 1;
+}
diff --git a/include/ddr_spd.h b/include/ddr_spd.h
new file mode 100644
index 0000000..c8762a2
--- /dev/null
+++ b/include/ddr_spd.h
@@ -0,0 +1,135 @@
+/*
+ * 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 _DDR_SPD_H_
+#define _DDR_SPD_H_
+
+/*
+ * Format from "JEDEC Appendix X: Serial Presence Detects for DDR2 SDRAM",
+ * SPD Revision 1.2
+ */
+struct ddr2_spd_eeprom_s {
+ uint8_t info_size; /* 0 # bytes written into serial memory */
+ uint8_t chip_size; /* 1 Total # bytes of SPD memory device */
+ uint8_t mem_type; /* 2 Fundamental memory type */
+ uint8_t nrow_addr; /* 3 # of Row Addresses on this assembly */
+ uint8_t ncol_addr; /* 4 # of Column Addrs on this assembly */
+ uint8_t mod_ranks; /* 5 Number of DIMM Ranks */
+ uint8_t dataw; /* 6 Module Data Width */
+ uint8_t res_7; /* 7 Reserved */
+ uint8_t voltage; /* 8 Voltage intf std of this assembly */
+ uint8_t clk_cycle; /* 9 SDRAM Cycle time @ CL=X */
+ uint8_t clk_access; /* 10 SDRAM Access from Clk @ CL=X (tAC) */
+ uint8_t config; /* 11 DIMM Configuration type */
+ uint8_t refresh; /* 12 Refresh Rate/Type */
+ uint8_t primw; /* 13 Primary SDRAM Width */
+ uint8_t ecw; /* 14 Error Checking SDRAM width */
+ uint8_t res_15; /* 15 Reserved */
+ uint8_t burstl; /* 16 Burst Lengths Supported */
+ uint8_t nbanks; /* 17 # of Banks on Each SDRAM Device */
+ uint8_t cas_lat; /* 18 CAS# Latencies Supported */
+ uint8_t mech_char; /* 19 DIMM Mechanical Characteristics */
+ uint8_t dimm_type; /* 20 DIMM type information */
+ uint8_t mod_attr; /* 21 SDRAM Module Attributes */
+ uint8_t dev_attr; /* 22 SDRAM Device Attributes */
+ uint8_t clk_cycle2; /* 23 Min SDRAM Cycle time @ CL=X-1 */
+ uint8_t clk_access2; /* 24 SDRAM Access from Clk @ CL=X-1 (tAC) */
+ uint8_t clk_cycle3; /* 25 Min SDRAM Cycle time @ CL=X-2 */
+ uint8_t clk_access3; /* 26 Max Access from Clk @ CL=X-2 (tAC) */
+ uint8_t trp; /* 27 Min Row Precharge Time (tRP)*/
+ uint8_t trrd; /* 28 Min Row Active to Row Active (tRRD) */
+ uint8_t trcd; /* 29 Min RAS to CAS Delay (tRCD) */
+ uint8_t tras; /* 30 Minimum RAS Pulse Width (tRAS) */
+ uint8_t rank_dens; /* 31 Density of each rank on module */
+ uint8_t ca_setup; /* 32 Addr+Cmd Setup Time Before Clk (tIS) */
+ uint8_t ca_hold; /* 33 Addr+Cmd Hold Time After Clk (tIH) */
+ uint8_t data_setup; /* 34 Data Input Setup Time Before Strobe
+ (tDS) */
+ uint8_t data_hold; /* 35 Data Input Hold Time
+ After Strobe (tDH) */
+ uint8_t twr; /* 36 Write Recovery time tWR */
+ uint8_t twtr; /* 37 Int write to read delay tWTR */
+ uint8_t trtp; /* 38 Int read to precharge delay tRTP */
+ uint8_t mem_probe; /* 39 Mem analysis probe characteristics */
+ uint8_t trctrfc_ext; /* 40 Extensions to trc and trfc */
+ uint8_t trc; /* 41 Min Active to Auto refresh time tRC */
+ uint8_t trfc; /* 42 Min Auto to Active period tRFC */
+ uint8_t tckmax; /* 43 Max device cycle time tCKmax */
+ uint8_t tdqsq; /* 44 Max DQS to DQ skew (tDQSQ max) */
+ uint8_t tqhs; /* 45 Max Read DataHold skew (tQHS) */
+ uint8_t pll_relock; /* 46 PLL Relock time */
+ uint8_t Tcasemax; /* 47 Tcasemax */
+ uint8_t psiTAdram; /* 48 Thermal Resistance of DRAM Package
+ from Top (Case) to Ambient
+ (Psi T-A DRAM) */
+ uint8_t dt0_mode; /* 49 DRAM Case Temperature Rise from
+ Ambient due to Activate-Precharge/Mode
+ Bits (DT0/Mode Bits) */
+ uint8_t dt2n_dt2q; /* 50 DRAM Case Temperature Rise from
+ Ambient due to Precharge/Quiet Standby
+ (DT2N/DT2Q) */
+ uint8_t dt2p; /* 51 DRAM Case Temperature Rise from
+ Ambient due to Precharge Power-Down
+ (DT2P) */
+ uint8_t dt3n; /* 52 DRAM Case Temperature Rise from
+ Ambient due to Active Standby (DT3N) */
+ uint8_t dt3pfast; /* 53 DRAM Case Temperature Rise from
+ Ambient due to Active Power-Down with
+ Fast PDN Exit (DT3Pfast) */
+ uint8_t dt3pslow; /* 54 DRAM Case Temperature Rise from
+ Ambient due to Active Power-Down with
+ Slow PDN Exit (DT3Pslow) */
+ uint8_t dt4r_dt4r4w; /* 55 DRAM Case Temperature Rise from
+ Ambient due to Page Open Burst
+ Read/DT4R4W Mode Bit
+ (DT4R/DT4R4W Mode Bit) */
+ uint8_t dt5b; /* 56 DRAM Case Temperature Rise from
+ Ambient due to Burst Refresh (DT5B) */
+ uint8_t dt7; /* 57 DRAM Case Temperature Rise from
+ Ambient due to Bank Interleave Reads
+ with Auto-Precharge (DT7) */
+ uint8_t psiTApll; /* 58 Thermal Resistance of PLL Package
+ form Top (Case) to Ambient
+ (Psi T-A PLL) */
+ uint8_t psiTAreg; /* 59 Thermal Reisitance of Register
+ Package from Top (Case) to Ambient
+ (Psi T-A Register) */
+ uint8_t dtpllactive; /* 60 PLL Case Temperature Rise from
+ Ambient due to PLL Active
+ (DT PLL Active) */
+ uint8_t dtregact; /* 61 Register Case Temperature Rise from
+ Ambient due to Register Active/Mode
+ Bit (DT Register Active/Mode Bit) */
+ uint8_t spd_rev; /* 62 SPD Data Revision Code */
+ uint8_t cksum; /* 63 Checksum for bytes 0-62 */
+ uint8_t mid[8]; /* 64 Mfr's JEDEC ID code per JEP-106 */
+ uint8_t mloc; /* 72 Manufacturing Location */
+ uint8_t mpart[18]; /* 73 Manufacturer's Part Number */
+ uint8_t rev[2]; /* 91 Revision Code */
+ uint8_t mdate[2]; /* 93 Manufacturing Date */
+ uint8_t sernum[4]; /* 95 Assembly Serial Number */
+ uint8_t mspec[27]; /* 99-127 Manufacturer Specific Data */
+
+};
+
+extern uint32_t ddr2_spd_checksum_pass(const struct ddr2_spd_eeprom_s *spd);
+
+/* * Byte 2 Fundamental Memory Types. */
+#define SPD_MEMTYPE_DDR2 (0x08)
+
+/* DIMM Type for DDR2 SPD (according to v1.3) */
+#define DDR2_SPD_DIMMTYPE_RDIMM (0x01)
+#define DDR2_SPD_DIMMTYPE_UDIMM (0x02)
+#define DDR2_SPD_DIMMTYPE_SO_DIMM (0x04)
+#define DDR2_SPD_DIMMTYPE_72B_SO_CDIMM (0x06)
+#define DDR2_SPD_DIMMTYPE_72B_SO_RDIMM (0x07)
+#define DDR2_SPD_DIMMTYPE_MICRO_DIMM (0x08)
+#define DDR2_SPD_DIMMTYPE_MINI_RDIMM (0x10)
+#define DDR2_SPD_DIMMTYPE_MINI_UDIMM (0x20)
+
+#endif /* _DDR_SPD_H_ */
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 2/8] ppc asm: DDR headers
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
` (2 preceding siblings ...)
2013-06-26 17:33 ` [PATCH 1/8] common: DDR2 SPD checksum Renaud Barbier
@ 2013-06-26 17:33 ` Renaud Barbier
2013-06-26 17:33 ` [PATCH 3/8] ppc 8xxx: " Renaud Barbier
` (5 subsequent siblings)
9 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-26 17:33 UTC (permalink / raw)
To: barebox
Definitions and structures have been added to record DIMM parameters
and configure memory options.
The code is based on the equivalent files in the directory
arch/powerpc/include/asm from U-boot version git-a71d45d.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/include/asm/fsl_ddr_dimm_params.h | 60 +++++++++++++
arch/ppc/include/asm/fsl_ddr_sdram.h | 131 +++++++++++++++++++++++++++-
2 files changed, 187 insertions(+), 4 deletions(-)
create mode 100644 arch/ppc/include/asm/fsl_ddr_dimm_params.h
diff --git a/arch/ppc/include/asm/fsl_ddr_dimm_params.h b/arch/ppc/include/asm/fsl_ddr_dimm_params.h
new file mode 100644
index 0000000..73c239b
--- /dev/null
+++ b/arch/ppc/include/asm/fsl_ddr_dimm_params.h
@@ -0,0 +1,60 @@
+/*
+ * 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
+
+#define EDC_ECC 2
+
+/* Parameters for a DDR2 dimm computed from the SPD */
+struct dimm_params_s {
+ char mpart[19];
+ uint32_t n_ranks;
+ uint64_t rank_density;
+ uint64_t capacity;
+ uint32_t data_width;
+ uint32_t primary_sdram_width;
+ uint32_t ec_sdram_width;
+ uint32_t registered_dimm;
+ uint32_t n_row_addr;
+ uint32_t n_col_addr;
+ uint32_t edc_config; /* 0 = none, 1 = parity, 2 = ECC */
+ uint32_t n_banks_per_sdram_device;
+ uint32_t burst_lengths_bitmask; /* BL=4 bit 2, BL=8 = bit 3 */
+ uint32_t row_density;
+ uint64_t base_address;
+ /* SDRAM clock periods */
+ uint32_t tCKmin_X_ps;
+ uint32_t tCKmin_X_minus_1_ps;
+ uint32_t tCKmin_X_minus_2_ps;
+ uint32_t tCKmax_ps;
+ /* SPD-defined CAS latencies */
+ uint32_t caslat_X;
+ uint32_t caslat_X_minus_1;
+ uint32_t caslat_X_minus_2;
+ uint32_t caslat_lowest_derated;
+ /* basic timing parameters */
+ uint32_t tRCD_ps;
+ uint32_t tRP_ps;
+ uint32_t tRAS_ps;
+ uint32_t tWR_ps; /* maximum = 63750 ps */
+ uint32_t tWTR_ps; /* maximum = 63750 ps */
+ uint32_t tRFC_ps; /* max = 255 ns + 256 ns + .75 ns = 511750 ps */
+ uint32_t tRRD_ps; /* maximum = 63750 ps */
+ uint32_t tRC_ps; /* maximum = 254 ns + .75 ns = 254750 ps */
+ uint32_t refresh_rate_ps;
+ uint32_t tIS_ps; /* byte 32, spd->ca_setup */
+ uint32_t tIH_ps; /* byte 33, spd->ca_hold */
+ uint32_t tDS_ps; /* byte 34, spd->data_setup */
+ uint32_t tDH_ps; /* byte 35, spd->data_hold */
+ uint32_t tRTP_ps; /* byte 38, spd->trtp */
+ uint32_t tDQSQ_max_ps; /* byte 44, spd->tdqsq */
+ uint32_t tQHS_ps; /* byte 45, spd->tqhs */
+};
+
+#endif
diff --git a/arch/ppc/include/asm/fsl_ddr_sdram.h b/arch/ppc/include/asm/fsl_ddr_sdram.h
index ef793c9..444bcbc 100644
--- a/arch/ppc/include/asm/fsl_ddr_sdram.h
+++ b/arch/ppc/include/asm/fsl_ddr_sdram.h
@@ -10,9 +10,40 @@
#ifndef FSL_DDR_MEMCTL_H
#define FSL_DDR_MEMCTL_H
-/*
- * DDR_SDRAM_CFG - DDR SDRAM Control Configuration
- */
+#include <ddr_spd.h>
+
+/* Basic DDR Technology. */
+#define SDRAM_TYPE_DDR1 2
+#define SDRAM_TYPE_DDR2 3
+#define SDRAM_TYPE_LPDDR1 6
+#define SDRAM_TYPE_DDR3 7
+
+#define DDR_BL4 4
+#define DDR_BL8 8
+
+#define DDR2_RTT_OFF 0
+#define DDR2_RTT_75_OHM 1
+#define DDR2_RTT_150_OHM 2
+#define DDR2_RTT_50_OHM 3
+
+#if defined(CONFIG_FSL_DDR2)
+#define FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR (3)
+typedef struct ddr2_spd_eeprom_s generic_spd_eeprom_t;
+#define FSL_SDRAM_TYPE SDRAM_TYPE_DDR2
+#endif
+
+#define FSL_DDR_ODT_NEVER 0x0
+#define FSL_DDR_ODT_CS 0x1
+#define FSL_DDR_ODT_ALL_OTHER_CS 0x2
+#define FSL_DDR_ODT_OTHER_DIMM 0x3
+#define FSL_DDR_ODT_ALL 0x4
+#define FSL_DDR_ODT_SAME_DIMM 0x5
+#define FSL_DDR_ODT_CS_AND_OTHER_DIMM 0x6
+#define FSL_DDR_ODT_OTHER_CS_ONSAMEDIMM 0x7
+
+#define SDRAM_CS_CONFIG_EN 0x80000000
+
+/* DDR_SDRAM_CFG - DDR SDRAM Control Configuration */
#define SDRAM_CFG_MEM_EN 0x80000000
#define SDRAM_CFG_SREN 0x40000000
#define SDRAM_CFG_ECC_EN 0x20000000
@@ -23,11 +54,103 @@
#define SDRAM_CFG_SDRAM_TYPE_SHIFT 24
#define SDRAM_CFG_DYN_PWR 0x00200000
#define SDRAM_CFG_32_BE 0x00080000
+#define SDRAM_CFG_16_BE 0x00100000
#define SDRAM_CFG_8_BE 0x00040000
#define SDRAM_CFG_NCAP 0x00020000
#define SDRAM_CFG_2T_EN 0x00008000
#define SDRAM_CFG_BI 0x00000001
-extern phys_size_t fixed_sdram(void);
+#define SDRAM_CFG2_D_INIT 0x00000010
+#define SDRAM_CFG2_ODT_CFG_MASK 0x00600000
+#define SDRAM_CFG2_ODT_NEVER 0
+#define SDRAM_CFG2_ODT_ONLY_WRITE 1
+#define SDRAM_CFG2_ODT_ONLY_READ 2
+#define SDRAM_CFG2_ODT_ALWAYS 3
+
+#define MAX_CHIP_SELECTS_PER_CTRL 4
+#define MAX_DIMM_SLOTS_PER_CTLR 4
+
+/*
+ * Memory controller characteristics and I2C parameters to
+ * read the SPD data.
+ */
+struct ddr_board_info_s {
+ uint32_t fsl_ddr_ver;
+ void __iomem *ddr_base;
+ uint32_t cs_per_ctrl;
+ uint32_t dimm_slots_per_ctrl;
+ uint32_t i2c_bus;
+ uint32_t i2c_slave;
+ uint32_t i2c_speed;
+ void __iomem *i2c_base;
+ uint8_t *spd_i2c_addr;
+};
+/*
+ * Generalized parameters for memory controller configuration,
+ * might be a little specific to the FSL memory controller
+ */
+struct memctl_options_s {
+ struct ddr_board_info_s *board_info;
+ uint32_t sdram_type;
+ /*
+ * 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
+ */
+ uint32_t registered_dimm_en;
+ /* Options local to a Chip Select */
+ struct cs_local_opts_s {
+ uint32_t auto_precharge;
+ uint32_t odt_rd_cfg;
+ uint32_t odt_wr_cfg;
+ uint32_t odt_rtt_norm;
+ uint32_t odt_rtt_wr;
+ } cs_local_opts[MAX_CHIP_SELECTS_PER_CTRL];
+ /* DLL reset disable */
+ uint32_t dll_rst_dis;
+ /* Operational mode parameters */
+ uint32_t ECC_mode;
+ uint32_t ECC_init_using_memctl;
+ uint32_t data_init;
+ /* Use DQS? maybe only with DDR2? */
+ uint32_t DQS_config;
+ uint32_t self_refresh_in_sleep;
+ uint32_t dynamic_power;
+ uint32_t data_bus_width;
+ uint32_t burst_length;
+ /* Global Timing Parameters */
+ uint32_t cas_latency_override;
+ uint32_t cas_latency_override_value;
+ uint32_t use_derated_caslat;
+ uint32_t additive_latency_override;
+ uint32_t additive_latency_override_value;
+ uint32_t clk_adjust;
+ uint32_t cpo_override;
+ uint32_t write_data_delay;
+ uint32_t half_strength_driver_enable;
+ uint32_t twoT_en;
+ uint32_t bstopre;
+ uint32_t tCKE_clock_pulse_width_ps;
+ uint32_t tFAW_window_four_activates_ps;
+ /* Rtt impedance */
+ uint32_t rtt_override;
+ uint32_t rtt_override_value;
+ /* Automatic self refresh */
+ uint32_t auto_self_refresh_en;
+ /* read-to-write turnaround */
+ uint32_t trwt_override;
+ uint32_t trwt;
+ /* Powerdon timings. */
+ uint32_t txp;
+ uint32_t taxpd;
+ uint32_t txard;
+ /* Load mode cycle time */
+ uint32_t tmrd;
+};
+
+extern phys_size_t fsl_ddr_sdram(void);
+extern phys_size_t fixed_sdram(void);
#endif
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 3/8] ppc 8xxx: DDR headers
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
` (3 preceding siblings ...)
2013-06-26 17:33 ` [PATCH 2/8] ppc asm: DDR headers Renaud Barbier
@ 2013-06-26 17:33 ` Renaud Barbier
2013-06-26 17:33 ` [PATCH 4/8] ppc 8xxx: DIMM parameters calculation Renaud Barbier
` (4 subsequent siblings)
9 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-26 17:33 UTC (permalink / raw)
To: barebox
Structures are defined to record the common DIMM parameters
and memory options on a per DIMM basis.
This code is based on the equivalent files in directory
arch/powerpc/cpu/mpc8xxx/ddr from U-Boot version git-a71d45d.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/common_timing_params.h | 44 ++++++++++++
arch/ppc/ddr-8xxx/ddr.h | 105 ++++++++++++++++++++++++++++++
2 files changed, 149 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/common_timing_params.h
create mode 100644 arch/ppc/ddr-8xxx/ddr.h
diff --git a/arch/ppc/ddr-8xxx/common_timing_params.h b/arch/ppc/ddr-8xxx/common_timing_params.h
new file mode 100644
index 0000000..b262193
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/common_timing_params.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef COMMON_TIMING_PARAMS_H
+#define COMMON_TIMING_PARAMS_H
+
+struct common_timing_params_s {
+ uint32_t tCKmin_X_ps;
+ uint32_t tCKmax_ps;
+ uint32_t tCKmax_max_ps;
+ uint32_t tRCD_ps;
+ uint32_t tRP_ps;
+ uint32_t tRAS_ps;
+ uint32_t tWR_ps; /* maximum = 63750 ps */
+ uint32_t tWTR_ps; /* maximum = 63750 ps */
+ uint32_t tRFC_ps; /* maximum = 255 ns + 256 ns + .75 ns
+ = 511750 ps */
+ uint32_t tRRD_ps; /* maximum = 63750 ps */
+ uint32_t tRC_ps; /* maximum = 254 ns + .75 ns = 254750 ps */
+ uint32_t refresh_rate_ps;
+ uint32_t tIS_ps; /* byte 32, spd->ca_setup */
+ uint32_t tIH_ps; /* byte 33, spd->ca_hold */
+ uint32_t tDS_ps; /* byte 34, spd->data_setup */
+ uint32_t tDH_ps; /* byte 35, spd->data_hold */
+ uint32_t tRTP_ps; /* byte 38, spd->trtp */
+ uint32_t tDQSQ_max_ps; /* byte 44, spd->tdqsq */
+ uint32_t tQHS_ps; /* byte 45, spd->tqhs */
+ uint32_t ndimms_present;
+ uint32_t lowest_common_SPD_caslat;
+ uint32_t highest_common_derated_caslat;
+ uint32_t additive_latency;
+ uint32_t all_DIMMs_burst_lengths_bitmask;
+ uint32_t all_DIMMs_registered;
+ uint32_t all_DIMMs_ECC_capable;
+ uint64_t total_mem;
+ uint64_t base_address;
+};
+
+#endif
diff --git a/arch/ppc/ddr-8xxx/ddr.h b/arch/ppc/ddr-8xxx/ddr.h
new file mode 100644
index 0000000..6574500
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef FSL_DDR_MAIN_H
+#define FSL_DDR_MAIN_H
+
+#include <asm/fsl_ddr_sdram.h>
+#include <asm/fsl_ddr_dimm_params.h>
+#include <mach/fsl_i2c.h>
+#include <mach/clock.h>
+#include "common_timing_params.h"
+
+#ifdef CONFIG_MPC85xx
+#include <mach/immap_85xx.h>
+#endif
+
+/* Record of computed register values. */
+struct fsl_ddr_cfg_regs_s {
+ struct {
+ uint32_t bnds;
+ uint32_t config;
+ uint32_t config_2;
+ } cs[MAX_CHIP_SELECTS_PER_CTRL];
+ uint32_t timing_cfg_3;
+ uint32_t timing_cfg_0;
+ uint32_t timing_cfg_1;
+ uint32_t timing_cfg_2;
+ uint32_t ddr_sdram_cfg;
+ uint32_t ddr_sdram_cfg_2;
+ uint32_t ddr_sdram_mode;
+ uint32_t ddr_sdram_mode_2;
+ uint32_t ddr_sdram_mode_3;
+ uint32_t ddr_sdram_mode_4;
+ uint32_t ddr_sdram_mode_5;
+ uint32_t ddr_sdram_mode_6;
+ uint32_t ddr_sdram_mode_7;
+ uint32_t ddr_sdram_mode_8;
+ uint32_t ddr_sdram_md_cntl;
+ uint32_t ddr_sdram_interval;
+ uint32_t ddr_data_init;
+ uint32_t ddr_sdram_clk_cntl;
+ uint32_t ddr_init_addr;
+ uint32_t ddr_init_ext_addr;
+ uint32_t err_disable;
+ uint32_t err_int_en;
+ uint32_t debug[32];
+};
+
+/*
+ * Data Structures
+ *
+ * All data structures have to be on the stack
+ */
+struct fsl_ddr_info_s {
+ generic_spd_eeprom_t
+ spd_installed_dimms[MAX_DIMM_SLOTS_PER_CTLR];
+ struct dimm_params_s
+ dimm_params[MAX_DIMM_SLOTS_PER_CTLR];
+ struct memctl_options_s memctl_opts;
+ struct common_timing_params_s common_timing_params;
+ struct fsl_ddr_cfg_regs_s fsl_ddr_config_reg;
+ struct ddr_board_info_s board_info;
+};
+
+uint32_t mclk_to_picos(uint32_t mclk);
+uint32_t get_memory_clk_period_ps(void);
+uint32_t picos_to_mclk(uint32_t picos);
+uint32_t check_fsl_memctl_config_regs(const struct fsl_ddr_cfg_regs_s *ddr);
+uint64_t fsl_ddr_compute(struct fsl_ddr_info_s *pinfo);
+uint32_t compute_fsl_memctl_config_regs(
+ const struct memctl_options_s *popts,
+ struct fsl_ddr_cfg_regs_s *ddr,
+ const struct common_timing_params_s *common_dimm,
+ const struct dimm_params_s *dimm_parameters,
+ uint32_t dbw_capacity_adjust);
+uint32_t compute_dimm_parameters(
+ const generic_spd_eeprom_t *spdin,
+ struct dimm_params_s *pdimm);
+uint32_t compute_lowest_common_dimm_parameters(
+ const struct dimm_params_s *dimm_params,
+ struct common_timing_params_s *outpdimm,
+ uint32_t number_of_dimms);
+uint32_t populate_memctl_options(
+ int all_DIMMs_registered,
+ struct memctl_options_s *popts,
+ struct dimm_params_s *pdimm);
+int fsl_ddr_set_lawbar(
+ const struct common_timing_params_s *memctl_common_params,
+ uint32_t memctl_interleaved);
+int fsl_ddr_get_spd(
+ generic_spd_eeprom_t *ctrl_dimms_spd,
+ struct ddr_board_info_s *binfo);
+int fsl_ddr_set_memctl_regs(
+ const struct fsl_ddr_info_s *info);
+void fsl_ddr_board_options(
+ struct memctl_options_s *popts,
+ struct dimm_params_s *pdimm);
+void fsl_ddr_board_info(struct ddr_board_info_s *info);
+#endif
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 4/8] ppc 8xxx: DIMM parameters calculation
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
` (4 preceding siblings ...)
2013-06-26 17:33 ` [PATCH 3/8] ppc 8xxx: " Renaud Barbier
@ 2013-06-26 17:33 ` Renaud Barbier
2013-06-26 17:33 ` [PATCH 5/8] ppc 8xxx: DDR utility and memory options Renaud Barbier
` (3 subsequent siblings)
9 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-26 17:33 UTC (permalink / raw)
To: barebox
This code calculates the DIMM characteritics i.e DIMM
organization parameters and timings for DDR2 memory based on
SPD data.
It also provides a function to find out the lowest common DIMM
parameters to be used for all DIMMs.
This code is based on the equivalent files in directory
arch/powerpc/cpu/mpc8xxx/ddr from U-Boot version git-a71d45d.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/ddr2_dimm_params.c | 303 +++++++++++++++++++++++++++++
arch/ppc/ddr-8xxx/lc_common_dimm_params.c | 214 ++++++++++++++++++++
2 files changed, 517 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/ddr2_dimm_params.c
create mode 100644 arch/ppc/ddr-8xxx/lc_common_dimm_params.c
diff --git a/arch/ppc/ddr-8xxx/ddr2_dimm_params.c b/arch/ppc/ddr-8xxx/ddr2_dimm_params.c
new file mode 100644
index 0000000..b36a888
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr2_dimm_params.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+#include "ddr.h"
+/*
+ * Calculate the Density of each Physical Rank.
+ * Returned size is in bytes.
+ *
+ * Table comes from Byte 31 of JEDEC SPD Spec.
+ *
+ * DDR II
+ * Bit Size Size
+ * --- -----
+ * 7 high 512MB
+ * 6 256MB
+ * 5 128MB
+ * 4 16GB
+ * 3 8GB
+ * 2 4GB
+ * 1 2GB
+ * 0 low 1GB
+ *
+ * Reorder Table to be linear by stripping the bottom
+ * 2 or 5 bits off and shifting them up to the top.
+ *
+ */
+static uint64_t compute_ranksize(uint32_t mem_type, unsigned char row_dens)
+{
+ uint64_t bsize;
+
+ bsize = ((row_dens >> 5) | ((row_dens & 31) << 3));
+ bsize <<= 27ULL;
+
+ return bsize;
+}
+
+/*
+ * Convert a two-nibble BCD value into a cycle time.
+ * While the spec calls for nano-seconds, picos are returned.
+ */
+static uint32_t convert_bcd_tenths_to_cycle_time_ps(uint32_t spd_val)
+{
+ uint32_t tenths_ps[16] = {
+ 0,
+ 100,
+ 200,
+ 300,
+ 400,
+ 500,
+ 600,
+ 700,
+ 800,
+ 900,
+ 250,
+ 330,
+ 660,
+ 750,
+ 0,
+ 0
+ };
+ uint32_t whole_ns = (spd_val & 0xF0) >> 4;
+ uint32_t tenth_ns = spd_val & 0x0F;
+ uint32_t ps = (whole_ns * 1000) + tenths_ps[tenth_ns];
+
+ return ps;
+}
+
+static uint32_t convert_bcd_hundredths_to_cycle_time_ps(uint32_t spd_val)
+{
+ uint32_t tenth_ns = (spd_val & 0xF0) >> 4;
+ uint32_t hundredth_ns = spd_val & 0x0F;
+ uint32_t ps = (tenth_ns * 100) + (hundredth_ns * 10);
+
+ return ps;
+}
+
+static uint32_t byte40_table_ps[8] = {
+ 0,
+ 250,
+ 330,
+ 500,
+ 660,
+ 750,
+ 0,
+ 0
+};
+
+static uint32_t
+compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc)
+{
+ uint32_t trfc_ps;
+
+ trfc_ps = (((trctrfc_ext & 0x1) * 256) + trfc) * 1000;
+ trfc_ps += byte40_table_ps[(trctrfc_ext >> 1) & 0x7];
+
+ return trfc_ps;
+}
+
+static uint32_t
+compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc)
+{
+ uint32_t trc_ps;
+
+ trc_ps = (trc * 1000);
+ trc_ps += byte40_table_ps[(trctrfc_ext >> 4) & 0x7];
+
+ return trc_ps;
+}
+
+/*
+ * Determine Refresh Rate.
+ * Table from SPD Spec, Byte 12, converted to picoseconds and
+ * filled in with "default" normal values.
+ */
+static uint32_t determine_refresh_rate_ps(const uint32_t spd_refresh)
+{
+ uint32_t refresh_time_ps[8] = {
+ 15625000, /* 0 Normal 1.00x */
+ 3900000, /* 1 Reduced .25x */
+ 7800000, /* 2 Extended .50x */
+ 31300000, /* 3 Extended 2.00x */
+ 62500000, /* 4 Extended 4.00x */
+ 125000000, /* 5 Extended 8.00x */
+ 15625000, /* 6 Normal 1.00x filler */
+ 15625000, /* 7 Normal 1.00x filler */
+ };
+
+ return refresh_time_ps[spd_refresh & 0x7];
+}
+
+/*
+ * The purpose of this function is to compute a suitable
+ * CAS latency given the DRAM clock period. The SPD only
+ * defines at most 3 CAS latencies. Typically the slower in
+ * frequency the DIMM runs at, the shorter its CAS latency can.
+ * be. If the DIMM is operating at a sufficiently low frequency,
+ * it may be able to run at a CAS latency shorter than the
+ * shortest SPD-defined CAS latency.
+ *
+ * If a CAS latency is not found, 0 is returned.
+ *
+ * Do this by finding in the standard speed table the longest
+ * tCKmin that doesn't exceed the value of mclk_ps (tCK).
+ *
+ * An assumption made is that the SDRAM device allows the
+ * CL to be programmed for a value that is lower than those
+ * advertised by the SPD. This is not always the case,
+ * as those modes not defined in the SPD are optional.
+ *
+ * CAS latency de-rating based upon values JEDEC Standard No. 79-2C
+ * Table 40, "DDR2 SDRAM standard speed bins and tCK, tRCD, tRP, tRAS,
+ * and tRC for corresponding bin"
+ *
+ * ordinal 2, ddr2_speed_bins[1] contains tCK for CL=3
+ * Not certain if any good value exists for CL=2
+ */
+ /* CL2 CL3 CL4 CL5 CL6 CL7 */
+uint16_t ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500, 1875 };
+
+uint32_t compute_derated_DDR2_CAS_latency(uint32_t mclk_ps)
+{
+ const uint32_t num_speed_bins = ARRAY_SIZE(ddr2_speed_bins);
+ uint32_t lowest_tCKmin_found = 0, lowest_tCKmin_CL = 0, i, x;
+
+ for (i = 0; i < num_speed_bins; i++) {
+ x = ddr2_speed_bins[i];
+ if (x && (x <= mclk_ps) && (x >= lowest_tCKmin_found)) {
+ lowest_tCKmin_found = x;
+ lowest_tCKmin_CL = i + 2;
+ }
+ }
+
+ return lowest_tCKmin_CL;
+}
+
+/*
+ * compute_dimm_parameters for DDR2 SPD
+ *
+ * Compute DIMM parameters based upon the SPD information in SPD.
+ * Writes the results to the dimm_params_s structure pointed by pdimm.
+ */
+uint32_t
+compute_dimm_parameters(const generic_spd_eeprom_t *spdin,
+ struct dimm_params_s *pdimm)
+{
+ const struct ddr2_spd_eeprom_s *spd = spdin;
+ uint32_t retval;
+
+ if (!spd->mem_type) {
+ memset(pdimm, 0, sizeof(struct dimm_params_s));
+ goto error;
+ }
+
+ if (spd->mem_type != SPD_MEMTYPE_DDR2)
+ goto error;
+
+ retval = ddr2_spd_checksum_pass(spd);
+ if (retval)
+ goto spd_err;
+
+ /*
+ * The part name in ASCII in the SPD EEPROM is not null terminated.
+ * Guarantee null termination here by presetting all bytes to 0
+ * and copying the part name in ASCII from the SPD onto it
+ */
+ memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
+ memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
+
+ /* DIMM organization parameters */
+ pdimm->n_ranks = (spd->mod_ranks & 0x7) + 1;
+ pdimm->rank_density = compute_ranksize(spd->mem_type, spd->rank_dens);
+ pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
+ pdimm->data_width = spd->dataw;
+ pdimm->primary_sdram_width = spd->primw;
+ pdimm->ec_sdram_width = spd->ecw;
+
+ /* These are all the types defined by the JEDEC DDR2 SPD 1.3 spec */
+ switch (spd->dimm_type) {
+ case DDR2_SPD_DIMMTYPE_RDIMM:
+ case DDR2_SPD_DIMMTYPE_72B_SO_RDIMM:
+ case DDR2_SPD_DIMMTYPE_MINI_RDIMM:
+ /* Registered/buffered DIMMs */
+ pdimm->registered_dimm = 1;
+ break;
+
+ case DDR2_SPD_DIMMTYPE_UDIMM:
+ case DDR2_SPD_DIMMTYPE_SO_DIMM:
+ case DDR2_SPD_DIMMTYPE_MICRO_DIMM:
+ case DDR2_SPD_DIMMTYPE_MINI_UDIMM:
+ /* Unbuffered DIMMs */
+ pdimm->registered_dimm = 0;
+ break;
+
+ case DDR2_SPD_DIMMTYPE_72B_SO_CDIMM:
+ default:
+ goto error;
+ }
+
+ pdimm->n_row_addr = spd->nrow_addr;
+ pdimm->n_col_addr = spd->ncol_addr;
+ pdimm->n_banks_per_sdram_device = spd->nbanks;
+ pdimm->edc_config = spd->config;
+ pdimm->burst_lengths_bitmask = spd->burstl;
+ pdimm->row_density = spd->rank_dens;
+
+ /*
+ * Calculate the Maximum Data Rate based on the Minimum Cycle time.
+ * The SPD clk_cycle field (tCKmin) is measured in tenths of
+ * nanoseconds and represented as BCD.
+ */
+ pdimm->tCKmin_X_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle);
+ pdimm->tCKmin_X_minus_1_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2);
+ pdimm->tCKmin_X_minus_2_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3);
+ pdimm->tCKmax_ps = convert_bcd_tenths_to_cycle_time_ps(spd->tckmax);
+
+ /*
+ * Compute CAS latencies defined by SPD
+ * The SPD caslat_X should have at least 1 and at most 3 bits set.
+ *
+ * If cas_lat after masking is 0, the __ilog2 function returns
+ * 255 into the variable. This behavior is abused once.
+ */
+ pdimm->caslat_X = __ilog2(spd->cas_lat);
+ pdimm->caslat_X_minus_1 = __ilog2(spd->cas_lat
+ & ~(1 << pdimm->caslat_X));
+ pdimm->caslat_X_minus_2 = __ilog2(spd->cas_lat & ~(1 << pdimm->caslat_X)
+ & ~(1 << pdimm->caslat_X_minus_1));
+ pdimm->caslat_lowest_derated
+ = compute_derated_DDR2_CAS_latency(get_memory_clk_period_ps());
+ pdimm->tRCD_ps = spd->trcd * 250;
+ pdimm->tRP_ps = spd->trp * 250;
+ pdimm->tRAS_ps = spd->tras * 1000;
+ pdimm->tWR_ps = spd->twr * 250;
+ pdimm->tWTR_ps = spd->twtr * 250;
+ pdimm->tRFC_ps = compute_trfc_ps_from_spd(spd->trctrfc_ext, spd->trfc);
+ pdimm->tRRD_ps = spd->trrd * 250;
+ pdimm->tRC_ps = compute_trc_ps_from_spd(spd->trctrfc_ext, spd->trc);
+ pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh);
+ pdimm->tIS_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup);
+ pdimm->tIH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold);
+ pdimm->tDS_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup);
+ pdimm->tDH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold);
+ pdimm->tRTP_ps = spd->trtp * 250;
+ pdimm->tDQSQ_max_ps = spd->tdqsq * 10;
+ pdimm->tQHS_ps = spd->tqhs * 10;
+
+ return 0;
+error:
+ return 1;
+spd_err:
+ return 2;
+}
diff --git a/arch/ppc/ddr-8xxx/lc_common_dimm_params.c b/arch/ppc/ddr-8xxx/lc_common_dimm_params.c
new file mode 100644
index 0000000..a1addb0
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/lc_common_dimm_params.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <config.h>
+#include <asm/fsl_ddr_sdram.h>
+
+#include "ddr.h"
+
+static unsigned int common_burst_length(
+ const struct dimm_params_s *dimm_params,
+ const unsigned int number_of_dimms)
+{
+ unsigned int i, temp;
+
+ temp = 0xff;
+ for (i = 0; i < number_of_dimms; i++)
+ if (dimm_params[i].n_ranks)
+ temp &= dimm_params[i].burst_lengths_bitmask;
+
+ return temp;
+}
+
+/* Compute a CAS latency suitable for all DIMMs */
+static unsigned int compute_lowest_caslat(
+ const struct dimm_params_s *dimm_params,
+ const unsigned int number_of_dimms)
+{
+ uint32_t temp1, temp2, i, not_ok, lowest_good_caslat,
+ tCKmin_X_minus_1_ps, tCKmin_X_minus_2_ps;
+ const unsigned int mclk_ps = get_memory_clk_period_ps();
+
+ /*
+ * Step 1: find CAS latency common to all DIMMs using bitwise
+ * operation.
+ */
+ temp1 = 0xFF;
+ for (i = 0; i < number_of_dimms; i++)
+ if (dimm_params[i].n_ranks) {
+ temp2 = 0;
+ temp2 |= 1 << dimm_params[i].caslat_X;
+ temp2 |= 1 << dimm_params[i].caslat_X_minus_1;
+ temp2 |= 1 << dimm_params[i].caslat_X_minus_2;
+ /*
+ * FIXME: If there was no entry for X-2 (X-1) in
+ * the SPD, then caslat_X_minus_2
+ * (caslat_X_minus_1) contains either 255 or
+ * 0xFFFFFFFF because that's what the __ilog2
+ * function returns for an input of 0.
+ * On 32-bit PowerPC, left shift counts with bit
+ * 26 set (that the value of 255 or 0xFFFFFFFF
+ * will have), cause the destination register to
+ * be 0. That is why this works.
+ */
+ temp1 &= temp2;
+ }
+
+ /*
+ * Step 2: check each common CAS latency against tCK of each
+ * DIMM's SPD.
+ */
+ lowest_good_caslat = 0;
+ temp2 = 0;
+ while (temp1) {
+ not_ok = 0;
+ temp2 = __ilog2(temp1);
+
+ for (i = 0; i < number_of_dimms; i++) {
+ if (!dimm_params[i].n_ranks)
+ continue;
+
+ if (dimm_params[i].caslat_X == temp2) {
+ if (mclk_ps >= dimm_params[i].tCKmin_X_ps)
+ continue;
+ else
+ not_ok++;
+ }
+
+ if (dimm_params[i].caslat_X_minus_1 == temp2) {
+ tCKmin_X_minus_1_ps =
+ dimm_params[i].tCKmin_X_minus_1_ps;
+ if (mclk_ps >= tCKmin_X_minus_1_ps)
+ continue;
+ else
+ not_ok++;
+ }
+
+ if (dimm_params[i].caslat_X_minus_2 == temp2) {
+ tCKmin_X_minus_2_ps
+ = dimm_params[i].tCKmin_X_minus_2_ps;
+ if (mclk_ps >= tCKmin_X_minus_2_ps)
+ continue;
+ else
+ not_ok++;
+ }
+ }
+
+ if (!not_ok)
+ lowest_good_caslat = temp2;
+
+ temp1 &= ~(1 << temp2);
+ }
+ return lowest_good_caslat;
+}
+
+/*
+ * compute_lowest_common_dimm_parameters()
+ *
+ * Determine the worst-case DIMM timing parameters from the set of DIMMs
+ * whose parameters have been computed into the array pointed to
+ * by dimm_params.
+ */
+unsigned int
+compute_lowest_common_dimm_parameters(const struct dimm_params_s *dimm,
+ struct common_timing_params_s *out,
+ const unsigned int number_of_dimms)
+{
+ const uint32_t mclk_ps = get_memory_clk_period_ps();
+ uint32_t temp1, i;
+ struct common_timing_params_s tmp = {0};
+
+ tmp.tCKmax_ps = 0xFFFFFFFF;
+ temp1 = 0;
+ for (i = 0; i < number_of_dimms; i++) {
+ if (dimm[i].n_ranks == 0) {
+ temp1++;
+ continue;
+ }
+
+ /*
+ * Find minimum tCKmax_ps to find fastest slow speed,
+ * i.e., this is the slowest the whole system can go.
+ */
+ tmp.tCKmax_ps = min(tmp.tCKmax_ps, dimm[i].tCKmax_ps);
+
+ /* Find maximum value to determine slowest speed, delay, etc */
+ tmp.tCKmin_X_ps = max(tmp.tCKmin_X_ps, dimm[i].tCKmin_X_ps);
+ tmp.tCKmax_max_ps = max(tmp.tCKmax_max_ps, dimm[i].tCKmax_ps);
+ tmp.tRCD_ps = max(tmp.tRCD_ps, dimm[i].tRCD_ps);
+ tmp.tRP_ps = max(tmp.tRP_ps, dimm[i].tRP_ps);
+ tmp.tRAS_ps = max(tmp.tRAS_ps, dimm[i].tRAS_ps);
+ tmp.tWR_ps = max(tmp.tWR_ps, dimm[i].tWR_ps);
+ tmp.tWTR_ps = max(tmp.tWTR_ps, dimm[i].tWTR_ps);
+ tmp.tRFC_ps = max(tmp.tRFC_ps, dimm[i].tRFC_ps);
+ tmp.tRRD_ps = max(tmp.tRRD_ps, dimm[i].tRRD_ps);
+ tmp.tRC_ps = max(tmp.tRC_ps, dimm[i].tRC_ps);
+ tmp.tIS_ps = max(tmp.tIS_ps, dimm[i].tIS_ps);
+ tmp.tIH_ps = max(tmp.tIH_ps, dimm[i].tIH_ps);
+ tmp.tDS_ps = max(tmp.tDS_ps, dimm[i].tDS_ps);
+ tmp.tDH_ps = max(tmp.tDH_ps, dimm[i].tDH_ps);
+ tmp.tRTP_ps = max(tmp.tRTP_ps, dimm[i].tRTP_ps);
+ tmp.tQHS_ps = max(tmp.tQHS_ps, dimm[i].tQHS_ps);
+ tmp.refresh_rate_ps = max(tmp.refresh_rate_ps,
+ dimm[i].refresh_rate_ps);
+ /* Find maximum tDQSQ_max_ps to find slowest timing. */
+ tmp.tDQSQ_max_ps = max(tmp.tDQSQ_max_ps, dimm[i].tDQSQ_max_ps);
+ }
+ tmp.ndimms_present = number_of_dimms - temp1;
+
+ if (temp1 == number_of_dimms)
+ return 0;
+
+ temp1 = common_burst_length(dimm, number_of_dimms);
+ tmp.all_DIMMs_burst_lengths_bitmask = temp1;
+ tmp.all_DIMMs_registered = 0;
+
+ tmp.lowest_common_SPD_caslat = compute_lowest_caslat(dimm,
+ number_of_dimms);
+ /*
+ * Compute a common 'de-rated' CAS latency.
+ *
+ * The strategy here is to find the *highest* de-rated cas latency
+ * with the assumption that all of the DIMMs will support a de-rated
+ * CAS latency higher than or equal to their lowest de-rated value.
+ */
+ temp1 = 0;
+ for (i = 0; i < number_of_dimms; i++)
+ temp1 = max(temp1, dimm[i].caslat_lowest_derated);
+ tmp.highest_common_derated_caslat = temp1;
+
+ temp1 = 1;
+ for (i = 0; i < number_of_dimms; i++)
+ if (dimm[i].n_ranks &&
+ !(dimm[i].edc_config & EDC_ECC)) {
+ temp1 = 0;
+ break;
+ }
+ tmp.all_DIMMs_ECC_capable = temp1;
+
+ if (mclk_ps > tmp.tCKmax_max_ps)
+ return 1;
+
+ /*
+ * AL must be less or equal to tRCD. Typically, AL would
+ * be AL = tRCD - 1;
+ *
+ * When ODT read or write is enabled the sum of CAS latency +
+ * additive latency must be at least 3 cycles.
+ *
+ */
+ if ((tmp.lowest_common_SPD_caslat < 4) && (picos_to_mclk(tmp.tRCD_ps) >
+ tmp.lowest_common_SPD_caslat))
+ tmp.additive_latency = picos_to_mclk(tmp.tRCD_ps) -
+ tmp.lowest_common_SPD_caslat;
+
+ memcpy(out, &tmp, sizeof(struct common_timing_params_s));
+
+ return 0;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 5/8] ppc 8xxx: DDR utility and memory options
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
` (5 preceding siblings ...)
2013-06-26 17:33 ` [PATCH 4/8] ppc 8xxx: DIMM parameters calculation Renaud Barbier
@ 2013-06-26 17:33 ` Renaud Barbier
2013-06-26 17:33 ` [PATCH 6/8] ppx 8xxx: DDR registers value calculation Renaud Barbier
` (2 subsequent siblings)
9 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-26 17:33 UTC (permalink / raw)
To: barebox
This commit adds functions to calculate clock cycles, configure the
LAW registers and populate board memory options.
This code is based on the equivalent files in directory
arch/powerpc/cpu/mpc8xxx/ddr from U-Boot version git-a71d45d.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/options.c | 111 +++++++++++++++++++++++++++++++++++++++++++
arch/ppc/ddr-8xxx/util.c | 100 ++++++++++++++++++++++++++++++++++++++
2 files changed, 211 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/options.c
create mode 100644 arch/ppc/ddr-8xxx/util.c
diff --git a/arch/ppc/ddr-8xxx/options.c b/arch/ppc/ddr-8xxx/options.c
new file mode 100644
index 0000000..22b621f
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/options.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2008, 2010-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+#include "ddr.h"
+
+uint32_t populate_memctl_options(int all_DIMMs_registered,
+ struct memctl_options_s *popts,
+ struct dimm_params_s *pdimm)
+{
+ const struct ddr_board_info_s *binfo = popts->board_info;
+ uint32_t i;
+
+ for (i = 0; i < binfo->cs_per_ctrl; i++) {
+ popts->cs_local_opts[i].odt_rd_cfg = FSL_DDR_ODT_NEVER;
+ popts->cs_local_opts[i].odt_wr_cfg = FSL_DDR_ODT_ALL;
+ popts->cs_local_opts[i].odt_rtt_norm = DDR2_RTT_50_OHM;
+ popts->cs_local_opts[i].odt_rtt_wr = DDR2_RTT_OFF;
+ popts->cs_local_opts[i].auto_precharge = 0;
+ }
+
+ /* Memory Organization Parameters */
+ popts->registered_dimm_en = all_DIMMs_registered;
+ popts->ECC_mode = 0; /* 0 = disabled, 1 = enabled */
+ popts->ECC_init_using_memctl = 1; /* 0 = use DMA, 1 = use memctl */
+
+ /* Choose DQS config - 1 for DDR2 */
+ popts->DQS_config = 1;
+
+ /* Choose self-refresh during sleep. */
+ popts->self_refresh_in_sleep = 1;
+
+ /* Choose dynamic power management mode. */
+ popts->dynamic_power = 0;
+
+ /*
+ * check first dimm for primary sdram width
+ * assuming all dimms are similar
+ * 0 = 64-bit, 1 = 32-bit, 2 = 16-bit
+ */
+ if (pdimm->n_ranks != 0) {
+ if ((pdimm->data_width >= 64) && (pdimm->data_width <= 72))
+ popts->data_bus_width = 0;
+ else if ((pdimm->data_width >= 32) ||
+ (pdimm->data_width <= 40))
+ popts->data_bus_width = 1;
+ else
+ panic("data width %u is invalid!\n",
+ pdimm->data_width);
+ }
+
+ /* Must be a burst length of 4 for DD2 */
+ popts->burst_length = DDR_BL4;
+ /* Decide whether to use the computed de-rated latency */
+ popts->use_derated_caslat = 0;
+
+ /*
+ * 2T_EN setting
+ *
+ * Factors to consider for 2T_EN:
+ * - number of DIMMs installed
+ * - number of components, number of active ranks
+ * - how much time you want to spend playing around
+ */
+ popts->twoT_en = 0;
+
+ /*
+ * Default BSTTOPRE precharge interval
+ *
+ * Set the parameter to 0 for global auto precharge in
+ * the board options function.
+ */
+ popts->bstopre = 0x100;
+
+ /* Minimum CKE pulse width -- tCKE(MIN) */
+ popts->tCKE_clock_pulse_width_ps
+ = mclk_to_picos(FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR);
+
+ /*
+ * Window for four activates -- tFAW
+ *
+ * Set according to specification for the memory used.
+ * The default value below would work for x4/x8 wide memory.
+ *
+ */
+ popts->tFAW_window_four_activates_ps = 37500;
+
+ /*
+ * Default powerdown exit timings.
+ * Set according to specifications for the memory used in
+ * the board options function.
+ */
+ popts->txard = 3;
+ popts->txp = 3;
+ popts->taxpd = 11;
+
+ /* Default value for load mode cycle time */
+ popts->tmrd = 2;
+
+ /* Specific board override parameters. */
+ fsl_ddr_board_options(popts, pdimm);
+
+ return 0;
+}
diff --git a/arch/ppc/ddr-8xxx/util.c b/arch/ppc/ddr-8xxx/util.c
new file mode 100644
index 0000000..626b5f3
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/util.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/fsl_law.h>
+#include <asm-generic/div64.h>
+#include <mach/clock.h>
+#include "ddr.h"
+
+#define ULL_2E12 2000000000000ULL
+#define UL_5POW12 244140625UL
+#define UL_2POW13 (1UL << 13)
+#define ULL_8FS 0xFFFFFFFFULL
+
+/*
+ * Round up mclk_ps to nearest 1 ps in memory controller code
+ * if the error is 0.5ps or more.
+ *
+ * If an imprecise data rate is too high due to rounding error
+ * propagation, compute a suitably rounded mclk_ps to compute
+ * a working memory controller configuration.
+ */
+uint32_t get_memory_clk_period_ps(void)
+{
+ uint32_t result, data_rate = fsl_get_ddr_freq(0);
+ /* Round to nearest 10ps, being careful about 64-bit multiply/divide */
+ uint64_t rem, mclk_ps = ULL_2E12;
+
+ /* Now perform the big divide, the result fits in 32-bits */
+ rem = do_div(mclk_ps, data_rate);
+ if (rem >= (data_rate >> 1))
+ result = mclk_ps + 1;
+ else
+ result = mclk_ps;
+
+ return result;
+}
+
+/* Convert picoseconds into DRAM clock cycles (rounding up if needed). */
+uint32_t picos_to_mclk(uint32_t picos)
+{
+ uint64_t clks, clks_rem;
+ uint32_t data_rate = fsl_get_ddr_freq(0);
+
+ if (!picos)
+ return 0;
+
+ /* First multiply the time by the data rate (32x32 => 64) */
+ clks = picos * (uint64_t)data_rate;
+ /*
+ * Now divide by 5^12 and track the 32-bit remainder, then divide
+ * by 2*(2^12) using shifts (and updating the remainder).
+ */
+ clks_rem = do_div(clks, UL_5POW12);
+ clks_rem += (clks & (UL_2POW13 - 1)) * UL_5POW12;
+ clks >>= 13;
+
+ /* If we had a remainder greater than the 1ps error, then round up */
+ if (clks_rem > data_rate)
+ clks++;
+
+ if (clks > ULL_8FS)
+ clks = ULL_8FS;
+
+ return (uint32_t)clks;
+}
+
+uint32_t mclk_to_picos(unsigned int mclk)
+{
+ return get_memory_clk_period_ps() * mclk;
+}
+
+int fsl_ddr_set_lawbar(
+ const struct common_timing_params_s *params,
+ uint32_t law_memctl)
+{
+ uint64_t base = params->base_address;
+ uint64_t size = params->total_mem;
+
+ if (!params->ndimms_present)
+ goto out;
+
+ if (base >= MAX_MEM_MAPPED)
+ goto error;
+
+ if ((base + size) >= MAX_MEM_MAPPED)
+ size = MAX_MEM_MAPPED - base;
+
+ if (fsl_set_ddr_laws(base, size, law_memctl) < 0)
+ goto error;
+out:
+ return 0;
+error:
+ return 1;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 6/8] ppx 8xxx: DDR registers value calculation
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
` (6 preceding siblings ...)
2013-06-26 17:33 ` [PATCH 5/8] ppc 8xxx: DDR utility and memory options Renaud Barbier
@ 2013-06-26 17:33 ` Renaud Barbier
2013-06-26 17:33 ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
2013-06-26 17:33 ` [PATCH 8/8] ppc 85xx: early I2C support Renaud Barbier
9 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-26 17:33 UTC (permalink / raw)
To: barebox
The functions in this file calculate and store the value for each
register of the memory controller.
This code is based on the equivalent file in directory
arch/powerpc/cpu/mpc8xxx/ddr from U-Boot version git-a71d45d.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/ctrl_regs.c | 425 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 425 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/ctrl_regs.c
diff --git a/arch/ppc/ddr-8xxx/ctrl_regs.c b/arch/ppc/ddr-8xxx/ctrl_regs.c
new file mode 100644
index 0000000..3f8ecf7
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ctrl_regs.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * Generic driver for Freescale DDR2 memory controller.
+ * Based on code from spd_sdram.c
+ * Author: James Yang [at freescale.com]
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+#include "ddr.h"
+
+static void set_csn_config(int dimm_number, int i,
+ struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct dimm_params_s *dimm)
+{
+ uint32_t cs_n_en = 0, ap_n_en = 0, odt_rd_cfg = 0, odt_wr_cfg = 0,
+ ba_bits_cs_n = 0, row_bits_cs_n = 0, col_bits_cs_n = 0,
+ n_banks_per_sdram_device;
+ int go_config = 0;
+
+ switch (i) {
+ case 0:
+ if (dimm[dimm_number].n_ranks > 0)
+ go_config = 1;
+ break;
+ case 1:
+ if ((dimm_number == 0 && dimm[0].n_ranks > 1) ||
+ (dimm_number == 1 && dimm[1].n_ranks > 0))
+ go_config = 1;
+ break;
+ case 2:
+ if ((dimm_number == 0 && dimm[0].n_ranks > 2) ||
+ (dimm_number >= 1 && dimm[dimm_number].n_ranks > 0))
+ go_config = 1;
+ break;
+ case 3:
+ if ((dimm_number == 0 && dimm[0].n_ranks > 3) ||
+ (dimm_number == 1 && dimm[1].n_ranks > 1) ||
+ (dimm_number == 3 && dimm[3].n_ranks > 0))
+ go_config = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (go_config) {
+ /* Chip Select enable */
+ cs_n_en = 1;
+ /* CSn auto-precharge enable */
+ ap_n_en = popts->cs_local_opts[i].auto_precharge;
+ /* ODT for reads configuration */
+ odt_rd_cfg = popts->cs_local_opts[i].odt_rd_cfg;
+ /* ODT for writes configuration */
+ odt_wr_cfg = popts->cs_local_opts[i].odt_wr_cfg;
+ /* Num of bank bits for SDRAM on CSn */
+ n_banks_per_sdram_device =
+ dimm[dimm_number].n_banks_per_sdram_device;
+ ba_bits_cs_n = __ilog2(n_banks_per_sdram_device) - 2;
+ /* Num of row bits for SDRAM on CSn */
+ row_bits_cs_n = dimm[dimm_number].n_row_addr - 12;
+ /* Num of ocl bits for SDRAM on CSn */
+ col_bits_cs_n = dimm[dimm_number].n_col_addr - 8;
+ }
+
+ ddr->cs[i].config = (((cs_n_en & 0x1) << 31)
+ | ((ap_n_en & 0x1) << 23)
+ | ((odt_rd_cfg & 0x7) << 20)
+ | ((odt_wr_cfg & 0x7) << 16)
+ | ((ba_bits_cs_n & 0x3) << 14)
+ | ((row_bits_cs_n & 0x7) << 8)
+ | ((col_bits_cs_n & 0x7) << 0));
+}
+
+static void set_timing_cfg_0(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts)
+{
+ uint32_t trwt_mclk = 0;
+
+ if (popts->trwt_override)
+ trwt_mclk = popts->trwt;
+
+ ddr->timing_cfg_0 = (((trwt_mclk & 0x3) << 30)
+ | ((popts->txard & 0x7) << 20)
+ | ((popts->txp & 0xF) << 16)
+ | ((popts->taxpd & 0xf) << 8)
+ | ((popts->tmrd & 0xf) << 0));
+}
+
+static void set_timing_cfg_3(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct common_timing_params_s *dimm,
+ uint32_t cas_latency)
+{
+ uint32_t ext_pretoact, ext_acttopre, ext_acttorw, ext_refrec;
+
+ ext_pretoact = picos_to_mclk(dimm->tRP_ps) >> 4;
+ ext_acttopre = picos_to_mclk(dimm->tRAS_ps) >> 4;
+ ext_acttorw = picos_to_mclk(dimm->tRCD_ps) >> 4;
+ cas_latency = ((cas_latency << 1) - 1) >> 4;
+ ext_refrec = (picos_to_mclk(dimm->tRFC_ps) - 8) >> 4;
+
+ ddr->timing_cfg_3 = (((ext_pretoact & 0x1) << 28)
+ | ((ext_acttopre & 0x2) << 24)
+ | ((ext_acttorw & 0x1) << 22)
+ | ((ext_refrec & 0x1F) << 16)
+ | ((cas_latency & 0x3) << 12));
+}
+
+static void set_timing_cfg_1(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct common_timing_params_s *dimm,
+ uint32_t cas_latency)
+{
+ uint32_t pretoact_mclk, acttopre_mclk, acttorw_mclk, refrec_ctrl,
+ wrrec_mclk, acttoact_mclk, wrtord_mclk;
+ /* DDR_SDRAM_MODE doesn't support 9,11,13,15 */
+ static const u8 wrrec_table[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 10, 10, 12, 12, 14, 14, 0, 0
+ };
+
+ pretoact_mclk = picos_to_mclk(dimm->tRP_ps);
+ acttopre_mclk = picos_to_mclk(dimm->tRAS_ps);
+ acttorw_mclk = picos_to_mclk(dimm->tRCD_ps);
+
+ /*
+ * Translate CAS Latency to a DDR controller field value:
+ *
+ * CAS Lat DDR II Ctrl
+ * Clocks SPD Bit Value
+ * ------- ------- ------
+ * 1.0 0001
+ * 1.5 0010
+ * 2.0 2 0011
+ * 2.5 0100
+ * 3.0 3 0101
+ * 3.5 0110
+ * 4.0 4 0111
+ * 4.5 1000
+ * 5.0 5 1001
+ */
+ cas_latency = (cas_latency << 1) - 1;
+ refrec_ctrl = picos_to_mclk(dimm->tRFC_ps) - 8;
+ acttoact_mclk = picos_to_mclk(dimm->tRRD_ps);
+
+ wrrec_mclk = picos_to_mclk(dimm->tWR_ps);
+ if (wrrec_mclk <= 16)
+ wrrec_mclk = wrrec_table[wrrec_mclk - 1];
+
+ wrtord_mclk = picos_to_mclk(dimm->tWTR_ps);
+ if (wrtord_mclk < 2)
+ wrtord_mclk = 2;
+
+ ddr->timing_cfg_1 = (((pretoact_mclk & 0x0F) << 28)
+ | ((acttopre_mclk & 0x0F) << 24)
+ | ((acttorw_mclk & 0xF) << 20)
+ | ((cas_latency & 0xF) << 16)
+ | ((refrec_ctrl & 0xF) << 12)
+ | ((wrrec_mclk & 0x0F) << 8)
+ | ((acttoact_mclk & 0x0F) << 4)
+ | ((wrtord_mclk & 0x0F) << 0));
+}
+
+static void set_timing_cfg_2(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct common_timing_params_s *dimm,
+ uint32_t cas_latency, uint32_t additive_latency)
+{
+ uint32_t cpo, rd_to_pre, wr_data_delay, cke_pls, four_act;
+
+ cpo = popts->cpo_override;
+ rd_to_pre = picos_to_mclk(dimm->tRTP_ps);
+ if (rd_to_pre < 2)
+ rd_to_pre = 2;
+
+ if (additive_latency)
+ rd_to_pre += additive_latency;
+
+ wr_data_delay = popts->write_data_delay;
+ cke_pls = picos_to_mclk(popts->tCKE_clock_pulse_width_ps);
+ four_act = picos_to_mclk(popts->tFAW_window_four_activates_ps);
+
+ ddr->timing_cfg_2 = (((additive_latency & 0xf) << 28)
+ | ((cpo & 0x1f) << 23)
+ | (((cas_latency - 1) & 0xf) << 19)
+ | ((rd_to_pre & 7) << 13)
+ | ((wr_data_delay & 7) << 10)
+ | ((cke_pls & 0x7) << 6)
+ | ((four_act & 0x3f) << 0));
+}
+
+static void set_ddr_sdram_cfg(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct common_timing_params_s *dimm)
+{
+ uint32_t mem_en, sren, ecc_en, sdram_type, dyn_pwr, dbw, twoT_en, hse;
+
+ mem_en = 1;
+ sren = popts->self_refresh_in_sleep;
+ if (dimm->all_DIMMs_ECC_capable)
+ ecc_en = popts->ECC_mode;
+ else
+ ecc_en = 0;
+
+ if (popts->sdram_type)
+ sdram_type = popts->sdram_type;
+ else
+ sdram_type = FSL_SDRAM_TYPE;
+
+ twoT_en = popts->twoT_en;
+ dyn_pwr = popts->dynamic_power;
+ dbw = popts->data_bus_width;
+ hse = popts->half_strength_driver_enable;
+
+ ddr->ddr_sdram_cfg = (((mem_en & 0x1) << 31)
+ | ((sren & 0x1) << 30)
+ | ((ecc_en & 0x1) << 29)
+ | ((sdram_type & 0x7) << 24)
+ | ((dyn_pwr & 0x1) << 21)
+ | ((dbw & 0x3) << 19)
+ | ((twoT_en & 0x1) << 15)
+ | ((hse & 0x1) << 3));
+}
+
+static void set_ddr_sdram_cfg_2(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts)
+{
+ struct ddr_board_info_s *bi = popts->board_info;
+ uint32_t i, dll_rst_dis, dqs_cfg, odt_cfg = 0, num_pr, d_init = 0;
+
+ dll_rst_dis = popts->dll_rst_dis;
+ dqs_cfg = popts->DQS_config;
+
+ /*
+ * Check for On-Die Termination options and
+ * assert ODT only during reads to DRAM.
+ */
+ for (i = 0; i < bi->cs_per_ctrl; i++)
+ if (popts->cs_local_opts[i].odt_rd_cfg ||
+ popts->cs_local_opts[i].odt_wr_cfg) {
+ odt_cfg = SDRAM_CFG2_ODT_ONLY_READ;
+ break;
+ }
+
+ /* Default number of posted refresh */
+ num_pr = 1;
+
+ if (popts->ECC_init_using_memctl) {
+ d_init = 1;
+ ddr->ddr_data_init = popts->data_init;
+ }
+
+ ddr->ddr_sdram_cfg_2 = (((dll_rst_dis & 0x1) << 29)
+ | ((dqs_cfg & 0x3) << 26)
+ | ((odt_cfg & 0x3) << 21)
+ | ((num_pr & 0xf) << 12)
+ | ((d_init & 0x1) << 4));
+}
+
+static void
+set_ddr_sdram_interval(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct common_timing_params_s *dimm)
+{
+ uint32_t refint, bstopre;
+
+ refint = picos_to_mclk(dimm->refresh_rate_ps);
+ /* Precharge interval */
+ bstopre = popts->bstopre;
+
+ ddr->ddr_sdram_interval = (((refint & 0xFFFF) << 16)
+ | ((bstopre & 0x3FFF) << 0));
+}
+
+static void set_ddr_sdram_mode(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct common_timing_params_s *dimm,
+ uint32_t cas_latency,
+ uint32_t additive_latency)
+{
+ uint16_t esdmode, sdmode;
+ uint32_t dqs_en, rtt, al, wr, bl;
+ const uint32_t mclk_ps = get_memory_clk_period_ps();
+
+ /* DQS# Enable: 0=enable, 1=disable */
+ dqs_en = !popts->DQS_config;
+ /* Posted CAS# additive latency (AL) */
+ al = additive_latency;
+ /* Internal Termination Resistor */
+ if (popts->rtt_override)
+ rtt = popts->rtt_override_value;
+ else
+ rtt = popts->cs_local_opts[0].odt_rtt_norm;
+
+ /*
+ * Extended SDRAM mode.
+ * The variable also selects:
+ * - OCD set to exit mode
+ * - all outputs bit i.e DQ, DQS, RDQS output enabled
+ * - RDQS ball disabled
+ * - DQS ball enabled
+ * - DLL enabled
+ * - Output drive strength: full strength.
+ */
+ esdmode = (((dqs_en & 0x1) << 10)
+ | ((rtt & 0x2) << 5)
+ | ((al & 0x7) << 3)
+ | ((rtt & 0x1) << 2));
+
+ /* Write recovery */
+ wr = (dimm->tWR_ps + mclk_ps - 1) / (mclk_ps - 1);
+
+ switch (popts->burst_length) {
+ case DDR_BL4:
+ bl = 2;
+ break;
+ case DDR_BL8:
+ bl = 3;
+ break;
+ default:
+ bl = 2;
+ break;
+ }
+
+ /* SDRAM mode
+ * The variable also selects:
+ * - power down mode: fast exit (normal)
+ * - DLL reset disabled.
+ * - burst type: sequential
+ */
+ sdmode = (((wr & 0x7) << 9)
+ | ((cas_latency & 0x7) << 4)
+ | ((bl & 0x7) << 0));
+
+ ddr->ddr_sdram_mode = (((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0));
+}
+
+uint32_t check_fsl_memctl_config_regs(const struct fsl_ddr_cfg_regs_s *ddr)
+{
+ /*
+ * DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN should not
+ * be set at the same time.
+ */
+ if ((ddr->ddr_sdram_cfg & 0x10000000) &&
+ (ddr->ddr_sdram_cfg & 0x00008000))
+ return 1;
+
+ return 0;
+}
+
+uint32_t
+compute_fsl_memctl_config_regs(const struct memctl_options_s *popts,
+ struct fsl_ddr_cfg_regs_s *ddr,
+ const struct common_timing_params_s *dimm,
+ const struct dimm_params_s *dimmp,
+ uint32_t dbw_cap_adj)
+{
+ struct ddr_board_info_s *binfo = popts->board_info;
+ uint32_t cas_latency, additive_latency, i, cs_per_dimm,
+ dimm_number;
+ uint64_t ea, sa, rank_density;
+
+ if (dimm == NULL)
+ return 1;
+
+ memset(ddr, 0, sizeof(struct fsl_ddr_cfg_regs_s));
+
+ /* Process overrides first. */
+ if (popts->cas_latency_override)
+ cas_latency = popts->cas_latency_override_value;
+ else
+ cas_latency = dimm->lowest_common_SPD_caslat;
+
+ if (popts->additive_latency_override)
+ additive_latency = popts->additive_latency_override_value;
+ else
+ additive_latency = dimm->additive_latency;
+
+ /* Chip Select Memory Bounds (CSn_BNDS) */
+ for (i = 0; i < binfo->cs_per_ctrl; i++) {
+ cs_per_dimm = binfo->cs_per_ctrl / binfo->dimm_slots_per_ctrl;
+ dimm_number = i / cs_per_dimm;
+ rank_density =
+ dimmp[dimm_number].rank_density >> dbw_cap_adj;
+
+ if (dimmp[dimm_number].n_ranks == 0)
+ continue;
+
+ sa = dimmp[dimm_number].base_address;
+ ea = sa + rank_density - 1;
+ if (dimmp[dimm_number].n_ranks > (i % cs_per_dimm)) {
+ sa += (i % cs_per_dimm) * rank_density;
+ ea += (i % cs_per_dimm) * rank_density;
+ } else {
+ sa = 0;
+ ea = 0;
+ }
+ sa >>= 24;
+ ea >>= 24;
+
+ ddr->cs[i].bnds = (((sa & 0xFFF) << 16) | ((ea & 0xFFF) << 0));
+ set_csn_config(dimm_number, i, ddr, popts, dimmp);
+ }
+
+ set_timing_cfg_0(ddr, popts);
+ set_timing_cfg_3(ddr, dimm, cas_latency);
+ set_timing_cfg_1(ddr, dimm, cas_latency);
+ set_timing_cfg_2(ddr, popts, dimm, cas_latency, additive_latency);
+ set_ddr_sdram_cfg(ddr, popts, dimm);
+ set_ddr_sdram_cfg_2(ddr, popts);
+ set_ddr_sdram_mode(ddr, popts, dimm, cas_latency, additive_latency);
+ set_ddr_sdram_interval(ddr, popts, dimm);
+
+ ddr->ddr_data_init = popts->data_init;
+ ddr->ddr_sdram_clk_cntl = (popts->clk_adjust & 0xF) << 23;
+
+ return check_fsl_memctl_config_regs(ddr);
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 7/8] ppc 8xxx: core DDR driver functions
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
` (7 preceding siblings ...)
2013-06-26 17:33 ` [PATCH 6/8] ppx 8xxx: DDR registers value calculation Renaud Barbier
@ 2013-06-26 17:33 ` Renaud Barbier
2013-06-26 17:33 ` [PATCH 8/8] ppc 85xx: early I2C support Renaud Barbier
9 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-26 17:33 UTC (permalink / raw)
To: barebox
These files are the driver interface to handle the memory
initialisation from reading the SPD data to writing the memory
controller registers.
This code is based on the equivalent files main.c in directory
arch/powerpc/cpu/mpc8xxx/ddr and ddr-gen2.c in directory
arch/powerpc/cpu/mpc85xx. Both are from U-Boot version git-a71d45d.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/ddr2_setctrl.c | 58 +++++++++
arch/ppc/ddr-8xxx/main.c | 238 ++++++++++++++++++++++++++++++++++++++
2 files changed, 296 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/ddr2_setctrl.c
create mode 100644 arch/ppc/ddr-8xxx/main.c
diff --git a/arch/ppc/ddr-8xxx/ddr2_setctrl.c b/arch/ppc/ddr-8xxx/ddr2_setctrl.c
new file mode 100644
index 0000000..14571b0
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr2_setctrl.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <config.h>
+#include <asm/io.h>
+#include <asm/fsl_ddr_sdram.h>
+#include <mach/early_udelay.h>
+#include "ddr.h"
+
+int fsl_ddr_set_memctl_regs(const struct fsl_ddr_info_s *info)
+{
+ uint32_t i;
+ void __iomem *ddr;
+ const struct fsl_ddr_cfg_regs_s *regs;
+
+ regs = &info->fsl_ddr_config_reg;
+ ddr = info->board_info.ddr_base;
+
+ if (in_be32(ddr + DDR_OFF(SDRAM_CFG)) & SDRAM_CFG_MEM_EN)
+ return 0;
+
+ for (i = 0; i < info->board_info.cs_per_ctrl; i++) {
+ out_be32(ddr + DDR_OFF(CS0_BNDS) + (i << 3), regs->cs[i].bnds);
+ out_be32(ddr + DDR_OFF(CS0_CONFIG) + (i << 2),
+ regs->cs[i].config);
+ }
+
+ out_be32(ddr + DDR_OFF(TIMING_CFG_3), regs->timing_cfg_3);
+ out_be32(ddr + DDR_OFF(TIMING_CFG_0), regs->timing_cfg_0);
+ out_be32(ddr + DDR_OFF(TIMING_CFG_1), regs->timing_cfg_1);
+ out_be32(ddr + DDR_OFF(TIMING_CFG_2), regs->timing_cfg_2);
+ out_be32(ddr + DDR_OFF(SDRAM_CFG_2), regs->ddr_sdram_cfg_2);
+ out_be32(ddr + DDR_OFF(SDRAM_MODE), regs->ddr_sdram_mode);
+ out_be32(ddr + DDR_OFF(SDRAM_MODE_2), regs->ddr_sdram_mode_2);
+ out_be32(ddr + DDR_OFF(SDRAM_MD_CNTL), regs->ddr_sdram_md_cntl);
+ out_be32(ddr + DDR_OFF(SDRAM_INTERVAL), regs->ddr_sdram_interval);
+ out_be32(ddr + DDR_OFF(SDRAM_DATA_INIT), regs->ddr_data_init);
+ out_be32(ddr + DDR_OFF(SDRAM_CLK_CNTL), regs->ddr_sdram_clk_cntl);
+ out_be32(ddr + DDR_OFF(SDRAM_INIT_ADDR), regs->ddr_init_addr);
+ out_be32(ddr + DDR_OFF(SDRAM_INIT_ADDR_EXT), regs->ddr_init_ext_addr);
+
+ early_udelay(200);
+ asm volatile("sync;isync");
+
+ out_be32(ddr + DDR_OFF(SDRAM_CFG), regs->ddr_sdram_cfg);
+
+ /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */
+ while (in_be32(ddr + DDR_OFF(SDRAM_CFG_2)) & SDRAM_CFG2_D_INIT)
+ early_udelay(10000);
+
+ return 0;
+}
diff --git a/arch/ppc/ddr-8xxx/main.c b/arch/ppc/ddr-8xxx/main.c
new file mode 100644
index 0000000..6e4a02d
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/main.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+/*
+ * Generic driver for Freescale DDR2 memory controller.
+ * Based on code from spd_sdram.c
+ * Author: James Yang [at freescale.com]
+ */
+
+#include <common.h>
+#include <config.h>
+#include <asm/fsl_law.h>
+#include "ddr.h"
+
+static int get_spd(generic_spd_eeprom_t *spd,
+ struct ddr_board_info_s *bi, u8 i2c_address)
+{
+ int ret;
+
+ fsl_i2c_init(bi->i2c_base, bi->i2c_speed, bi->i2c_slave);
+ ret = fsl_i2c_read(bi->i2c_base, i2c_address, 0, 1, (uchar *) spd,
+ sizeof(generic_spd_eeprom_t));
+ fsl_i2c_stop(bi->i2c_base);
+
+ return ret;
+}
+
+int fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd,
+ struct ddr_board_info_s *binfo)
+{
+ uint32_t i;
+ uint8_t i2c_address;
+
+ for (i = 0; i < binfo->dimm_slots_per_ctrl; i++) {
+ if (binfo->spd_i2c_addr == NULL)
+ goto error;
+ i2c_address = binfo->spd_i2c_addr[i];
+ if (get_spd(&(ctrl_dimms_spd[i]), binfo, i2c_address))
+ goto error;
+ }
+
+ return 0;
+error:
+ return 1;
+}
+
+static uint64_t step_assign_addresses(struct fsl_ddr_info_s *pinfo,
+ uint32_t dbw_cap_adj)
+{
+ uint64_t total_mem, current_mem_base, total_ctlr_mem, cap;
+ uint32_t ndimm, dw, j, found = 0;
+
+ ndimm = pinfo->board_info.dimm_slots_per_ctrl;
+ /*
+ * If a reduced data width is requested, but the SPD
+ * specifies a physically wider device, adjust the
+ * computed dimm capacities accordingly before
+ * assigning addresses.
+ */
+ switch (pinfo->memctl_opts.data_bus_width) {
+ case 2:
+ /* 16-bit */
+ for (j = 0; j < ndimm; j++) {
+ if (!pinfo->dimm_params[j].n_ranks)
+ continue;
+ dw = pinfo->dimm_params[j].primary_sdram_width;
+ if ((dw == 72 || dw == 64)) {
+ dbw_cap_adj = 2;
+ break;
+ } else if ((dw == 40 || dw == 32)) {
+ dbw_cap_adj = 1;
+ break;
+ }
+ }
+ break;
+ case 1:
+ /* 32-bit */
+ for (j = 0; j < ndimm; j++) {
+ dw = pinfo->dimm_params[j].data_width;
+ if (pinfo->dimm_params[j].n_ranks
+ && (dw == 72 || dw == 64)) {
+ /*
+ * FIXME: can't really do it
+ * like this because this just
+ * further reduces the memory
+ */
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ dbw_cap_adj = 1;
+ break;
+ case 0:
+ /* 64-bit */
+ break;
+ default:
+ return 1;
+ }
+
+ current_mem_base = 0ull;
+ total_mem = 0;
+ total_ctlr_mem = 0;
+ pinfo->common_timing_params.base_address = current_mem_base;
+
+ for (j = 0; j < ndimm; j++) {
+ cap = pinfo->dimm_params[j].capacity >> dbw_cap_adj;
+ pinfo->dimm_params[j].base_address = current_mem_base;
+ current_mem_base += cap;
+ total_ctlr_mem += cap;
+
+ }
+
+ pinfo->common_timing_params.total_mem = total_ctlr_mem;
+ total_mem += total_ctlr_mem;
+
+ return total_mem;
+}
+
+static uint32_t retrieve_max_size(struct fsl_ddr_cfg_regs_s *ddr_reg,
+ uint32_t ncs)
+{
+ uint32_t max_end = 0, end, i;
+
+ for (i = 0; i < ncs; i++)
+ if (ddr_reg->cs[i].config & 0x80000000) {
+ end = ddr_reg->cs[i].bnds & 0xFFF;
+ if (end > max_end)
+ max_end = end;
+ }
+
+ return max_end;
+}
+
+static uint32_t compute_dimm_param(struct fsl_ddr_info_s *pinfo, uint32_t ndimm)
+{
+ struct dimm_params_s *pdimm;
+ generic_spd_eeprom_t *spd;
+ uint32_t i, retval;
+
+ for (i = 0; i < ndimm; i++) {
+ spd = &(pinfo->spd_installed_dimms[i]);
+ pdimm = &(pinfo->dimm_params[i]);
+
+ retval = compute_dimm_parameters(spd, pdimm);
+
+ if (retval == 2) /* Fatal error */
+ return 1;
+ }
+
+ return 0;
+}
+
+uint64_t fsl_ddr_compute(struct fsl_ddr_info_s *pinfo)
+{
+ uint64_t total_mem = 0;
+ uint32_t ncs, ndimm, max_end = 0;
+ struct fsl_ddr_cfg_regs_s *ddr_reg;
+ struct common_timing_params_s *timing_params;
+ /* data bus width capacity adjust shift amount */
+ uint32_t dbw_capacity_adjust;
+
+ ddr_reg = &pinfo->fsl_ddr_config_reg;
+ timing_params = &pinfo->common_timing_params;
+ ncs = pinfo->board_info.cs_per_ctrl;
+ ndimm = pinfo->board_info.dimm_slots_per_ctrl;
+ dbw_capacity_adjust = 0;
+ pinfo->memctl_opts.board_info = &pinfo->board_info;
+
+ /* STEP 1: Gather all DIMM SPD data */
+ if (fsl_ddr_get_spd(pinfo->spd_installed_dimms,
+ pinfo->memctl_opts.board_info))
+ return 0;
+
+ /* STEP 2: Compute DIMM parameters from SPD data */
+ if (compute_dimm_param(pinfo, ndimm))
+ return 0;
+
+ /*
+ * STEP 3: Compute a common set of timing parameters
+ * suitable for all of the DIMMs on each memory controller
+ */
+ compute_lowest_common_dimm_parameters(pinfo->dimm_params,
+ timing_params, ndimm);
+
+ /* STEP 4: Gather configuration requirements from user */
+ populate_memctl_options(timing_params->all_DIMMs_registered,
+ &pinfo->memctl_opts,
+ pinfo->dimm_params);
+
+ /* STEP 5: Assign addresses to chip selects */
+ total_mem = step_assign_addresses(pinfo, dbw_capacity_adjust);
+
+ /* STEP 6: compute controller register values */
+ if (timing_params->ndimms_present == 0)
+ memset(ddr_reg, 0, sizeof(struct fsl_ddr_cfg_regs_s));
+
+ compute_fsl_memctl_config_regs(&pinfo->memctl_opts,
+ ddr_reg, timing_params,
+ pinfo->dimm_params,
+ dbw_capacity_adjust);
+
+ max_end = retrieve_max_size(ddr_reg, ncs);
+ total_mem = 1 + (((uint64_t)max_end << 24ULL) | 0xFFFFFFULL);
+
+ return total_mem;
+}
+
+/*
+ * fsl_ddr_sdram():
+ * This is the main function to initialize the memory.
+ *
+ * It returns amount of memory configured in bytes.
+ */
+phys_size_t fsl_ddr_sdram(void)
+{
+ uint64_t total_memory;
+ struct fsl_ddr_info_s info;
+
+ memset(&info, 0, sizeof(struct fsl_ddr_info_s));
+ /* Gather board information on the memory controller and I2C bus. */
+ fsl_ddr_board_info(&info.board_info);
+
+ total_memory = fsl_ddr_compute(&info);
+
+ if (info.common_timing_params.ndimms_present == 0)
+ return 0;
+
+ fsl_ddr_set_memctl_regs(&info);
+ fsl_ddr_set_lawbar(&info.common_timing_params, LAW_TRGT_IF_DDR_1);
+
+ return total_memory;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 8/8] ppc 85xx: early I2C support
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
` (8 preceding siblings ...)
2013-06-26 17:33 ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
@ 2013-06-26 17:33 ` Renaud Barbier
9 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-26 17:33 UTC (permalink / raw)
To: barebox
Early I2C support is introduced to read SPD data from memory
modules prior to the loading of the I2C driver.
This code is based on the equivalent file fsl_i2c.c in directory
drivers/i2c from U-Boot version git-a71d45d.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/mach-mpc85xx/fsl_i2c.c | 253 ++++++++++++++++++++++++++
arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h | 18 ++
2 files changed, 271 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/mach-mpc85xx/fsl_i2c.c
create mode 100644 arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
diff --git a/arch/ppc/mach-mpc85xx/fsl_i2c.c b/arch/ppc/mach-mpc85xx/fsl_i2c.c
new file mode 100644
index 0000000..51fcc64
--- /dev/null
+++ b/arch/ppc/mach-mpc85xx/fsl_i2c.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ * Copyright 2006,2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Early I2C support functions to read SPD data or board
+ * information.
+ * Based on U-Boot drivers/i2c/fsl_i2c.c
+ */
+#include <common.h>
+#include <i2c/i2c.h>
+#include <mach/clock.h>
+#include <mach/immap_85xx.h>
+#include <mach/early_udelay.h>
+
+/* FSL I2C registers */
+#define FSL_I2C_ADR 0x00
+#define FSL_I2C_FDR 0x04
+#define FSL_I2C_CR 0x08
+#define FSL_I2C_SR 0x0C
+#define FSL_I2C_DR 0x10
+#define FSL_I2C_DFSRR 0x14
+
+/* Bits of FSL I2C registers */
+#define I2C_SR_RXAK 0x01
+#define I2C_SR_MIF 0x02
+#define I2C_SR_MAL 0x10
+#define I2C_SR_MBB 0x20
+#define I2C_SR_MCF 0x80
+#define I2C_CR_RSTA 0x04
+#define I2C_CR_TXAK 0x08
+#define I2C_CR_MTX 0x10
+#define I2C_CR_MSTA 0x20
+#define I2C_CR_MEN 0x80
+
+/*
+ * Set the I2C bus speed for a given I2C device
+ *
+ * parameters:
+ * - i2c: the I2C base address.
+ * - i2c_clk: I2C bus clock frequency.
+ * - speed: the desired speed of the bus.
+ *
+ * The I2C device must be stopped before calling this function.
+ *
+ * The return value is the actual bus speed that is set.
+ */
+static uint32_t fsl_set_i2c_bus_speed(const void __iomem *i2c,
+ uint32_t i2c_clk, uint32_t speed)
+{
+ uint8_t dfsr, fdr = 0x31; /* Default if no FDR found */
+ uint16_t a, b, ga, gb, bin_gb, bin_ga, divider;
+ uint32_t c_div, est_div;
+
+ divider = min((uint16_t)(i2c_clk / speed), (uint16_t) -1);
+ /* Condition 1: dfsr <= 50/T */
+ dfsr = (5 * (i2c_clk / 1000)) / 100000;
+ if (!dfsr)
+ dfsr = 1;
+ est_div = ~0;
+
+ /*
+ * Bus speed is calculated as per Freescale AN2919.
+ * a, b and dfsr matches identifiers A,B and C as in the
+ * application note.
+ */
+ for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) {
+ for (gb = 0; gb < 8; gb++) {
+ b = 16 << gb;
+ c_div = b * (a + (((3 * dfsr) / b) * 2));
+
+ if ((c_div > divider) && (c_div < est_div)) {
+ est_div = c_div;
+ bin_gb = gb << 2;
+ bin_ga = (ga & 0x3) | ((ga & 0x4) << 3);
+ fdr = bin_gb | bin_ga;
+ speed = i2c_clk / est_div;
+ }
+ }
+ if (a == 20)
+ a += 2;
+ if (a == 24)
+ a += 4;
+ }
+ writeb(dfsr, i2c + FSL_I2C_DFSRR); /* set default filter */
+ writeb(fdr, i2c + FSL_I2C_FDR); /* set bus speed */
+
+ return speed;
+}
+
+void fsl_i2c_init(void __iomem *i2c, int speed, int slaveadd)
+{
+ uint32_t i2c_clk;
+
+ i2c_clk = fsl_get_i2c_freq();
+ writeb(0, i2c + FSL_I2C_CR);
+ early_udelay(5);
+
+ fsl_set_i2c_bus_speed(i2c, i2c_clk, speed);
+ writeb(slaveadd << 1, i2c + FSL_I2C_ADR);
+ writeb(0x0, i2c + FSL_I2C_SR);
+ writeb(I2C_CR_MEN, i2c + FSL_I2C_CR);
+}
+
+static uint32_t fsl_usec2ticks(uint32_t usec)
+{
+ ulong ticks;
+
+ if (usec < 1000) {
+ ticks = (usec * (fsl_get_timebase_clock() / 1000));
+ ticks = (ticks + 500) / 1000;
+ } else {
+ ticks = (usec / 10);
+ ticks *= (fsl_get_timebase_clock() / 100000);
+ }
+
+ return ticks;
+}
+
+static int fsl_i2c_wait4bus(void __iomem *i2c)
+{
+ uint64_t timeval = get_ticks();
+ const uint64_t timeout = fsl_usec2ticks(20000);
+
+ while (readb(i2c + FSL_I2C_SR) & I2C_SR_MBB)
+ if ((get_ticks() - timeval) > timeout)
+ return -1;
+
+ return 0;
+}
+
+void fsl_i2c_stop(void __iomem *i2c)
+{
+ writeb(I2C_CR_MEN, i2c + FSL_I2C_CR);
+}
+
+static int fsl_i2c_wait(void __iomem *i2c, int write)
+{
+ const uint64_t timeout = fsl_usec2ticks(100000);
+ uint64_t timeval = get_ticks();
+ int csr;
+
+ do {
+ csr = readb(i2c + FSL_I2C_SR);
+ if (csr & I2C_SR_MIF)
+ break;
+ } while ((get_ticks() - timeval) < timeout);
+
+ if ((get_ticks() - timeval) > timeout)
+ goto error;
+
+ csr = readb(i2c + FSL_I2C_SR);
+ writeb(0x0, i2c + FSL_I2C_SR);
+
+ if (csr & I2C_SR_MAL)
+ goto error;
+
+ if (!(csr & I2C_SR_MCF))
+ goto error;
+
+ if (write == 0 && (csr & I2C_SR_RXAK))
+ goto error;
+
+ return 0;
+error:
+ return -1;
+}
+
+static int __i2c_write(void __iomem *i2c, uint8_t *data, int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ writeb(data[i], i2c + FSL_I2C_DR);
+ if (fsl_i2c_wait(i2c, 0) < 0)
+ break;
+ }
+
+ return i;
+}
+
+static int __i2c_read(void __iomem *i2c, uint8_t *data, int length)
+{
+ int i;
+ uint8_t val = I2C_CR_MEN | I2C_CR_MSTA;
+
+ if (length == 1)
+ writeb(val | I2C_CR_TXAK, i2c + FSL_I2C_CR);
+ else
+ writeb(val, i2c + FSL_I2C_CR);
+
+ readb(i2c + FSL_I2C_DR);
+ for (i = 0; i < length; i++) {
+ if (fsl_i2c_wait(i2c, 1) < 0)
+ break;
+
+ /* Generate ack on last next to last byte */
+ if (i == length - 2)
+ writeb(val | I2C_CR_TXAK, i2c + FSL_I2C_CR);
+ /* Do not generate stop on last byte */
+ if (i == length - 1)
+ writeb(val | I2C_CR_MTX, i2c + FSL_I2C_CR);
+
+ data[i] = readb(i2c + FSL_I2C_DR);
+ }
+
+ return i;
+}
+
+static int
+fsl_i2c_write_addr(void __iomem *i2c, uint8_t dev, uint8_t dir, int rsta)
+{
+ uint8_t val = I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX;
+
+ if (rsta)
+ val |= I2C_CR_RSTA;
+ writeb(val, i2c + FSL_I2C_CR);
+ writeb((dev << 1) | dir, i2c + FSL_I2C_DR);
+
+ if (fsl_i2c_wait(i2c, 0) < 0)
+ return 0;
+
+ return 1;
+}
+
+int fsl_i2c_read(void __iomem *i2c, uint8_t dev, uint addr, int alen,
+ uint8_t *data, int length)
+{
+ int i = -1;
+ uint8_t *a = (uint8_t *)&addr;
+
+ if (alen && (fsl_i2c_wait4bus(i2c) >= 0) &&
+ (fsl_i2c_write_addr(i2c, dev, 0, 0) != 0) &&
+ (__i2c_write(i2c, &a[4 - alen], alen) == alen))
+ i = 0;
+
+ if (length && fsl_i2c_write_addr(i2c, dev, 1, 1) != 0)
+ i = __i2c_read(i2c, data, length);
+
+ if (i == length)
+ return 0;
+
+ return -1;
+}
diff --git a/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h b/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
new file mode 100644
index 0000000..acf4cc7
--- /dev/null
+++ b/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+void fsl_i2c_init(void __iomem *i2c, int speed, int slaveadd);
+int fsl_i2c_read(void __iomem *i2c, uint8_t dev, uint addr, int alen,
+ uint8_t *data, int length);
+void fsl_i2c_stop(void __iomem *i2c);
+
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 1/8] common: DDR2 SPD checksum.
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (8 preceding siblings ...)
2013-06-25 10:45 ` [PATCH v2 0/8] DDR2 memory initialisaion Renaud Barbier
@ 2013-06-25 10:45 ` Renaud Barbier
2013-06-26 6:26 ` Sascha Hauer
2013-06-25 10:45 ` [PATCH 2/8] ppc asm: DDR headers Renaud Barbier
` (6 subsequent siblings)
16 siblings, 1 reply; 32+ messages in thread
From: Renaud Barbier @ 2013-06-25 10:45 UTC (permalink / raw)
To: barebox
This commit adds support to calculate the DDR2 SPD checksum as per
JEDEC standard No 21-C Appendix X (revision 1.2)
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
common/Makefile | 1 +
common/ddr_spd.c | 39 +++++++++++++++
include/ddr_spd.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 175 insertions(+), 0 deletions(-)
create mode 100644 common/ddr_spd.c
create mode 100644 include/ddr_spd.h
diff --git a/common/Makefile b/common/Makefile
index 2f0bd34..fc23421 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_CMD_LOADS) += s_record.o
obj-$(CONFIG_OFTREE) += oftree.o
obj-y += memory.o
+obj-$(CONFIG_DDR_SPD) += ddr_spd.o
obj-y += memory_display.o
obj-$(CONFIG_MALLOC_DLMALLOC) += dlmalloc.o
obj-$(CONFIG_MALLOC_TLSF) += tlsf_malloc.o
diff --git a/common/ddr_spd.c b/common/ddr_spd.c
new file mode 100644
index 0000000..fbea5c0
--- /dev/null
+++ b/common/ddr_spd.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <ddr_spd.h>
+
+uint32_t ddr2_spd_check(const struct ddr2_spd_eeprom_s *spd)
+{
+ uint32_t i, cksum = 0;
+ const uint8_t *buf = (const uint8_t *)spd;
+ uint8_t rev, spd_cksum;
+
+ rev = spd->spd_rev;
+ spd_cksum = spd->cksum;
+
+ /* Rev 1.2 or less supported by this code */
+ if (rev >= 0x20)
+ goto error;
+
+ /*
+ * The checksum is calculated on the first 64 bytes
+ * of the SPD as per JEDEC specification.
+ */
+ for (i = 0; i < 63; i++)
+ cksum += *buf++;
+ cksum &= 0xFF;
+
+ if (cksum != spd_cksum)
+ goto error;
+
+ return 0;
+error:
+ return 1;
+}
diff --git a/include/ddr_spd.h b/include/ddr_spd.h
new file mode 100644
index 0000000..10fd8cf
--- /dev/null
+++ b/include/ddr_spd.h
@@ -0,0 +1,135 @@
+/*
+ * 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 _DDR_SPD_H_
+#define _DDR_SPD_H_
+
+/*
+ * Format from "JEDEC Appendix X: Serial Presence Detects for DDR2 SDRAM",
+ * SPD Revision 1.2
+ */
+struct ddr2_spd_eeprom_s {
+ uint8_t info_size; /* 0 # bytes written into serial memory */
+ uint8_t chip_size; /* 1 Total # bytes of SPD memory device */
+ uint8_t mem_type; /* 2 Fundamental memory type */
+ uint8_t nrow_addr; /* 3 # of Row Addresses on this assembly */
+ uint8_t ncol_addr; /* 4 # of Column Addrs on this assembly */
+ uint8_t mod_ranks; /* 5 Number of DIMM Ranks */
+ uint8_t dataw; /* 6 Module Data Width */
+ uint8_t res_7; /* 7 Reserved */
+ uint8_t voltage; /* 8 Voltage intf std of this assembly */
+ uint8_t clk_cycle; /* 9 SDRAM Cycle time @ CL=X */
+ uint8_t clk_access; /* 10 SDRAM Access from Clk @ CL=X (tAC) */
+ uint8_t config; /* 11 DIMM Configuration type */
+ uint8_t refresh; /* 12 Refresh Rate/Type */
+ uint8_t primw; /* 13 Primary SDRAM Width */
+ uint8_t ecw; /* 14 Error Checking SDRAM width */
+ uint8_t res_15; /* 15 Reserved */
+ uint8_t burstl; /* 16 Burst Lengths Supported */
+ uint8_t nbanks; /* 17 # of Banks on Each SDRAM Device */
+ uint8_t cas_lat; /* 18 CAS# Latencies Supported */
+ uint8_t mech_char; /* 19 DIMM Mechanical Characteristics */
+ uint8_t dimm_type; /* 20 DIMM type information */
+ uint8_t mod_attr; /* 21 SDRAM Module Attributes */
+ uint8_t dev_attr; /* 22 SDRAM Device Attributes */
+ uint8_t clk_cycle2; /* 23 Min SDRAM Cycle time @ CL=X-1 */
+ uint8_t clk_access2; /* 24 SDRAM Access from Clk @ CL=X-1 (tAC) */
+ uint8_t clk_cycle3; /* 25 Min SDRAM Cycle time @ CL=X-2 */
+ uint8_t clk_access3; /* 26 Max Access from Clk @ CL=X-2 (tAC) */
+ uint8_t trp; /* 27 Min Row Precharge Time (tRP)*/
+ uint8_t trrd; /* 28 Min Row Active to Row Active (tRRD) */
+ uint8_t trcd; /* 29 Min RAS to CAS Delay (tRCD) */
+ uint8_t tras; /* 30 Minimum RAS Pulse Width (tRAS) */
+ uint8_t rank_dens; /* 31 Density of each rank on module */
+ uint8_t ca_setup; /* 32 Addr+Cmd Setup Time Before Clk (tIS) */
+ uint8_t ca_hold; /* 33 Addr+Cmd Hold Time After Clk (tIH) */
+ uint8_t data_setup; /* 34 Data Input Setup Time Before Strobe
+ (tDS) */
+ uint8_t data_hold; /* 35 Data Input Hold Time
+ After Strobe (tDH) */
+ uint8_t twr; /* 36 Write Recovery time tWR */
+ uint8_t twtr; /* 37 Int write to read delay tWTR */
+ uint8_t trtp; /* 38 Int read to precharge delay tRTP */
+ uint8_t mem_probe; /* 39 Mem analysis probe characteristics */
+ uint8_t trctrfc_ext; /* 40 Extensions to trc and trfc */
+ uint8_t trc; /* 41 Min Active to Auto refresh time tRC */
+ uint8_t trfc; /* 42 Min Auto to Active period tRFC */
+ uint8_t tckmax; /* 43 Max device cycle time tCKmax */
+ uint8_t tdqsq; /* 44 Max DQS to DQ skew (tDQSQ max) */
+ uint8_t tqhs; /* 45 Max Read DataHold skew (tQHS) */
+ uint8_t pll_relock; /* 46 PLL Relock time */
+ uint8_t Tcasemax; /* 47 Tcasemax */
+ uint8_t psiTAdram; /* 48 Thermal Resistance of DRAM Package
+ from Top (Case) to Ambient
+ (Psi T-A DRAM) */
+ uint8_t dt0_mode; /* 49 DRAM Case Temperature Rise from
+ Ambient due to Activate-Precharge/Mode
+ Bits (DT0/Mode Bits) */
+ uint8_t dt2n_dt2q; /* 50 DRAM Case Temperature Rise from
+ Ambient due to Precharge/Quiet Standby
+ (DT2N/DT2Q) */
+ uint8_t dt2p; /* 51 DRAM Case Temperature Rise from
+ Ambient due to Precharge Power-Down
+ (DT2P) */
+ uint8_t dt3n; /* 52 DRAM Case Temperature Rise from
+ Ambient due to Active Standby (DT3N) */
+ uint8_t dt3pfast; /* 53 DRAM Case Temperature Rise from
+ Ambient due to Active Power-Down with
+ Fast PDN Exit (DT3Pfast) */
+ uint8_t dt3pslow; /* 54 DRAM Case Temperature Rise from
+ Ambient due to Active Power-Down with
+ Slow PDN Exit (DT3Pslow) */
+ uint8_t dt4r_dt4r4w; /* 55 DRAM Case Temperature Rise from
+ Ambient due to Page Open Burst
+ Read/DT4R4W Mode Bit
+ (DT4R/DT4R4W Mode Bit) */
+ uint8_t dt5b; /* 56 DRAM Case Temperature Rise from
+ Ambient due to Burst Refresh (DT5B) */
+ uint8_t dt7; /* 57 DRAM Case Temperature Rise from
+ Ambient due to Bank Interleave Reads
+ with Auto-Precharge (DT7) */
+ uint8_t psiTApll; /* 58 Thermal Resistance of PLL Package
+ form Top (Case) to Ambient
+ (Psi T-A PLL) */
+ uint8_t psiTAreg; /* 59 Thermal Reisitance of Register
+ Package from Top (Case) to Ambient
+ (Psi T-A Register) */
+ uint8_t dtpllactive; /* 60 PLL Case Temperature Rise from
+ Ambient due to PLL Active
+ (DT PLL Active) */
+ uint8_t dtregact; /* 61 Register Case Temperature Rise from
+ Ambient due to Register Active/Mode
+ Bit (DT Register Active/Mode Bit) */
+ uint8_t spd_rev; /* 62 SPD Data Revision Code */
+ uint8_t cksum; /* 63 Checksum for bytes 0-62 */
+ uint8_t mid[8]; /* 64 Mfr's JEDEC ID code per JEP-106 */
+ uint8_t mloc; /* 72 Manufacturing Location */
+ uint8_t mpart[18]; /* 73 Manufacturer's Part Number */
+ uint8_t rev[2]; /* 91 Revision Code */
+ uint8_t mdate[2]; /* 93 Manufacturing Date */
+ uint8_t sernum[4]; /* 95 Assembly Serial Number */
+ uint8_t mspec[27]; /* 99-127 Manufacturer Specific Data */
+
+};
+
+extern uint32_t ddr2_spd_check(const struct ddr2_spd_eeprom_s *spd);
+
+/* * Byte 2 Fundamental Memory Types. */
+#define SPD_MEMTYPE_DDR2 (0x08)
+
+/* DIMM Type for DDR2 SPD (according to v1.3) */
+#define DDR2_SPD_DIMMTYPE_RDIMM (0x01)
+#define DDR2_SPD_DIMMTYPE_UDIMM (0x02)
+#define DDR2_SPD_DIMMTYPE_SO_DIMM (0x04)
+#define DDR2_SPD_DIMMTYPE_72B_SO_CDIMM (0x06)
+#define DDR2_SPD_DIMMTYPE_72B_SO_RDIMM (0x07)
+#define DDR2_SPD_DIMMTYPE_MICRO_DIMM (0x08)
+#define DDR2_SPD_DIMMTYPE_MINI_RDIMM (0x10)
+#define DDR2_SPD_DIMMTYPE_MINI_UDIMM (0x20)
+
+#endif /* _DDR_SPD_H_ */
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 1/8] common: DDR2 SPD checksum.
2013-06-25 10:45 ` [PATCH 1/8] common: DDR2 SPD checksum Renaud Barbier
@ 2013-06-26 6:26 ` Sascha Hauer
0 siblings, 0 replies; 32+ messages in thread
From: Sascha Hauer @ 2013-06-26 6:26 UTC (permalink / raw)
To: Renaud Barbier; +Cc: barebox
Hi Renaud,
On Tue, Jun 25, 2013 at 11:45:30AM +0100, Renaud Barbier wrote:
> This commit adds support to calculate the DDR2 SPD checksum as per
> JEDEC standard No 21-C Appendix X (revision 1.2)
This code is from U-Boot. Please say so in the commit message, possibly
also which commit you used. Also for the other patches. Wolfgang once
complained about not doing so for code we used from U-Boot.
> +uint32_t ddr2_spd_check(const struct ddr2_spd_eeprom_s *spd)
The function name is a poor choice as it doesn't give a clue about its
return value. Does it return 'Yes, the check failed" or "Yes, the check
is ok"?. Please rename to something like ddr2_spd_checksum_ok.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 2/8] ppc asm: DDR headers
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (9 preceding siblings ...)
2013-06-25 10:45 ` [PATCH 1/8] common: DDR2 SPD checksum Renaud Barbier
@ 2013-06-25 10:45 ` Renaud Barbier
2013-06-25 10:45 ` [PATCH 3/8] ppc 8xxx: " Renaud Barbier
` (5 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-25 10:45 UTC (permalink / raw)
To: barebox
Definitions and structures have been added to record DIMM parameters
and configure memory options.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/include/asm/fsl_ddr_dimm_params.h | 60 +++++++++++++
arch/ppc/include/asm/fsl_ddr_sdram.h | 131 +++++++++++++++++++++++++++-
2 files changed, 187 insertions(+), 4 deletions(-)
create mode 100644 arch/ppc/include/asm/fsl_ddr_dimm_params.h
diff --git a/arch/ppc/include/asm/fsl_ddr_dimm_params.h b/arch/ppc/include/asm/fsl_ddr_dimm_params.h
new file mode 100644
index 0000000..73c239b
--- /dev/null
+++ b/arch/ppc/include/asm/fsl_ddr_dimm_params.h
@@ -0,0 +1,60 @@
+/*
+ * 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
+
+#define EDC_ECC 2
+
+/* Parameters for a DDR2 dimm computed from the SPD */
+struct dimm_params_s {
+ char mpart[19];
+ uint32_t n_ranks;
+ uint64_t rank_density;
+ uint64_t capacity;
+ uint32_t data_width;
+ uint32_t primary_sdram_width;
+ uint32_t ec_sdram_width;
+ uint32_t registered_dimm;
+ uint32_t n_row_addr;
+ uint32_t n_col_addr;
+ uint32_t edc_config; /* 0 = none, 1 = parity, 2 = ECC */
+ uint32_t n_banks_per_sdram_device;
+ uint32_t burst_lengths_bitmask; /* BL=4 bit 2, BL=8 = bit 3 */
+ uint32_t row_density;
+ uint64_t base_address;
+ /* SDRAM clock periods */
+ uint32_t tCKmin_X_ps;
+ uint32_t tCKmin_X_minus_1_ps;
+ uint32_t tCKmin_X_minus_2_ps;
+ uint32_t tCKmax_ps;
+ /* SPD-defined CAS latencies */
+ uint32_t caslat_X;
+ uint32_t caslat_X_minus_1;
+ uint32_t caslat_X_minus_2;
+ uint32_t caslat_lowest_derated;
+ /* basic timing parameters */
+ uint32_t tRCD_ps;
+ uint32_t tRP_ps;
+ uint32_t tRAS_ps;
+ uint32_t tWR_ps; /* maximum = 63750 ps */
+ uint32_t tWTR_ps; /* maximum = 63750 ps */
+ uint32_t tRFC_ps; /* max = 255 ns + 256 ns + .75 ns = 511750 ps */
+ uint32_t tRRD_ps; /* maximum = 63750 ps */
+ uint32_t tRC_ps; /* maximum = 254 ns + .75 ns = 254750 ps */
+ uint32_t refresh_rate_ps;
+ uint32_t tIS_ps; /* byte 32, spd->ca_setup */
+ uint32_t tIH_ps; /* byte 33, spd->ca_hold */
+ uint32_t tDS_ps; /* byte 34, spd->data_setup */
+ uint32_t tDH_ps; /* byte 35, spd->data_hold */
+ uint32_t tRTP_ps; /* byte 38, spd->trtp */
+ uint32_t tDQSQ_max_ps; /* byte 44, spd->tdqsq */
+ uint32_t tQHS_ps; /* byte 45, spd->tqhs */
+};
+
+#endif
diff --git a/arch/ppc/include/asm/fsl_ddr_sdram.h b/arch/ppc/include/asm/fsl_ddr_sdram.h
index ef793c9..444bcbc 100644
--- a/arch/ppc/include/asm/fsl_ddr_sdram.h
+++ b/arch/ppc/include/asm/fsl_ddr_sdram.h
@@ -10,9 +10,40 @@
#ifndef FSL_DDR_MEMCTL_H
#define FSL_DDR_MEMCTL_H
-/*
- * DDR_SDRAM_CFG - DDR SDRAM Control Configuration
- */
+#include <ddr_spd.h>
+
+/* Basic DDR Technology. */
+#define SDRAM_TYPE_DDR1 2
+#define SDRAM_TYPE_DDR2 3
+#define SDRAM_TYPE_LPDDR1 6
+#define SDRAM_TYPE_DDR3 7
+
+#define DDR_BL4 4
+#define DDR_BL8 8
+
+#define DDR2_RTT_OFF 0
+#define DDR2_RTT_75_OHM 1
+#define DDR2_RTT_150_OHM 2
+#define DDR2_RTT_50_OHM 3
+
+#if defined(CONFIG_FSL_DDR2)
+#define FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR (3)
+typedef struct ddr2_spd_eeprom_s generic_spd_eeprom_t;
+#define FSL_SDRAM_TYPE SDRAM_TYPE_DDR2
+#endif
+
+#define FSL_DDR_ODT_NEVER 0x0
+#define FSL_DDR_ODT_CS 0x1
+#define FSL_DDR_ODT_ALL_OTHER_CS 0x2
+#define FSL_DDR_ODT_OTHER_DIMM 0x3
+#define FSL_DDR_ODT_ALL 0x4
+#define FSL_DDR_ODT_SAME_DIMM 0x5
+#define FSL_DDR_ODT_CS_AND_OTHER_DIMM 0x6
+#define FSL_DDR_ODT_OTHER_CS_ONSAMEDIMM 0x7
+
+#define SDRAM_CS_CONFIG_EN 0x80000000
+
+/* DDR_SDRAM_CFG - DDR SDRAM Control Configuration */
#define SDRAM_CFG_MEM_EN 0x80000000
#define SDRAM_CFG_SREN 0x40000000
#define SDRAM_CFG_ECC_EN 0x20000000
@@ -23,11 +54,103 @@
#define SDRAM_CFG_SDRAM_TYPE_SHIFT 24
#define SDRAM_CFG_DYN_PWR 0x00200000
#define SDRAM_CFG_32_BE 0x00080000
+#define SDRAM_CFG_16_BE 0x00100000
#define SDRAM_CFG_8_BE 0x00040000
#define SDRAM_CFG_NCAP 0x00020000
#define SDRAM_CFG_2T_EN 0x00008000
#define SDRAM_CFG_BI 0x00000001
-extern phys_size_t fixed_sdram(void);
+#define SDRAM_CFG2_D_INIT 0x00000010
+#define SDRAM_CFG2_ODT_CFG_MASK 0x00600000
+#define SDRAM_CFG2_ODT_NEVER 0
+#define SDRAM_CFG2_ODT_ONLY_WRITE 1
+#define SDRAM_CFG2_ODT_ONLY_READ 2
+#define SDRAM_CFG2_ODT_ALWAYS 3
+
+#define MAX_CHIP_SELECTS_PER_CTRL 4
+#define MAX_DIMM_SLOTS_PER_CTLR 4
+
+/*
+ * Memory controller characteristics and I2C parameters to
+ * read the SPD data.
+ */
+struct ddr_board_info_s {
+ uint32_t fsl_ddr_ver;
+ void __iomem *ddr_base;
+ uint32_t cs_per_ctrl;
+ uint32_t dimm_slots_per_ctrl;
+ uint32_t i2c_bus;
+ uint32_t i2c_slave;
+ uint32_t i2c_speed;
+ void __iomem *i2c_base;
+ uint8_t *spd_i2c_addr;
+};
+/*
+ * Generalized parameters for memory controller configuration,
+ * might be a little specific to the FSL memory controller
+ */
+struct memctl_options_s {
+ struct ddr_board_info_s *board_info;
+ uint32_t sdram_type;
+ /*
+ * 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
+ */
+ uint32_t registered_dimm_en;
+ /* Options local to a Chip Select */
+ struct cs_local_opts_s {
+ uint32_t auto_precharge;
+ uint32_t odt_rd_cfg;
+ uint32_t odt_wr_cfg;
+ uint32_t odt_rtt_norm;
+ uint32_t odt_rtt_wr;
+ } cs_local_opts[MAX_CHIP_SELECTS_PER_CTRL];
+ /* DLL reset disable */
+ uint32_t dll_rst_dis;
+ /* Operational mode parameters */
+ uint32_t ECC_mode;
+ uint32_t ECC_init_using_memctl;
+ uint32_t data_init;
+ /* Use DQS? maybe only with DDR2? */
+ uint32_t DQS_config;
+ uint32_t self_refresh_in_sleep;
+ uint32_t dynamic_power;
+ uint32_t data_bus_width;
+ uint32_t burst_length;
+ /* Global Timing Parameters */
+ uint32_t cas_latency_override;
+ uint32_t cas_latency_override_value;
+ uint32_t use_derated_caslat;
+ uint32_t additive_latency_override;
+ uint32_t additive_latency_override_value;
+ uint32_t clk_adjust;
+ uint32_t cpo_override;
+ uint32_t write_data_delay;
+ uint32_t half_strength_driver_enable;
+ uint32_t twoT_en;
+ uint32_t bstopre;
+ uint32_t tCKE_clock_pulse_width_ps;
+ uint32_t tFAW_window_four_activates_ps;
+ /* Rtt impedance */
+ uint32_t rtt_override;
+ uint32_t rtt_override_value;
+ /* Automatic self refresh */
+ uint32_t auto_self_refresh_en;
+ /* read-to-write turnaround */
+ uint32_t trwt_override;
+ uint32_t trwt;
+ /* Powerdon timings. */
+ uint32_t txp;
+ uint32_t taxpd;
+ uint32_t txard;
+ /* Load mode cycle time */
+ uint32_t tmrd;
+};
+
+extern phys_size_t fsl_ddr_sdram(void);
+extern phys_size_t fixed_sdram(void);
#endif
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 3/8] ppc 8xxx: DDR headers
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (10 preceding siblings ...)
2013-06-25 10:45 ` [PATCH 2/8] ppc asm: DDR headers Renaud Barbier
@ 2013-06-25 10:45 ` Renaud Barbier
2013-06-25 10:45 ` [PATCH 4/8] ppc 8xxx: DIMM parameters calculation Renaud Barbier
` (4 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-25 10:45 UTC (permalink / raw)
To: barebox
Structures are defined to record the common DIMM parameters
and memory options on a per DIMM basis.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/common_timing_params.h | 44 ++++++++++++
arch/ppc/ddr-8xxx/ddr.h | 105 ++++++++++++++++++++++++++++++
2 files changed, 149 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/common_timing_params.h
create mode 100644 arch/ppc/ddr-8xxx/ddr.h
diff --git a/arch/ppc/ddr-8xxx/common_timing_params.h b/arch/ppc/ddr-8xxx/common_timing_params.h
new file mode 100644
index 0000000..b262193
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/common_timing_params.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef COMMON_TIMING_PARAMS_H
+#define COMMON_TIMING_PARAMS_H
+
+struct common_timing_params_s {
+ uint32_t tCKmin_X_ps;
+ uint32_t tCKmax_ps;
+ uint32_t tCKmax_max_ps;
+ uint32_t tRCD_ps;
+ uint32_t tRP_ps;
+ uint32_t tRAS_ps;
+ uint32_t tWR_ps; /* maximum = 63750 ps */
+ uint32_t tWTR_ps; /* maximum = 63750 ps */
+ uint32_t tRFC_ps; /* maximum = 255 ns + 256 ns + .75 ns
+ = 511750 ps */
+ uint32_t tRRD_ps; /* maximum = 63750 ps */
+ uint32_t tRC_ps; /* maximum = 254 ns + .75 ns = 254750 ps */
+ uint32_t refresh_rate_ps;
+ uint32_t tIS_ps; /* byte 32, spd->ca_setup */
+ uint32_t tIH_ps; /* byte 33, spd->ca_hold */
+ uint32_t tDS_ps; /* byte 34, spd->data_setup */
+ uint32_t tDH_ps; /* byte 35, spd->data_hold */
+ uint32_t tRTP_ps; /* byte 38, spd->trtp */
+ uint32_t tDQSQ_max_ps; /* byte 44, spd->tdqsq */
+ uint32_t tQHS_ps; /* byte 45, spd->tqhs */
+ uint32_t ndimms_present;
+ uint32_t lowest_common_SPD_caslat;
+ uint32_t highest_common_derated_caslat;
+ uint32_t additive_latency;
+ uint32_t all_DIMMs_burst_lengths_bitmask;
+ uint32_t all_DIMMs_registered;
+ uint32_t all_DIMMs_ECC_capable;
+ uint64_t total_mem;
+ uint64_t base_address;
+};
+
+#endif
diff --git a/arch/ppc/ddr-8xxx/ddr.h b/arch/ppc/ddr-8xxx/ddr.h
new file mode 100644
index 0000000..6574500
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef FSL_DDR_MAIN_H
+#define FSL_DDR_MAIN_H
+
+#include <asm/fsl_ddr_sdram.h>
+#include <asm/fsl_ddr_dimm_params.h>
+#include <mach/fsl_i2c.h>
+#include <mach/clock.h>
+#include "common_timing_params.h"
+
+#ifdef CONFIG_MPC85xx
+#include <mach/immap_85xx.h>
+#endif
+
+/* Record of computed register values. */
+struct fsl_ddr_cfg_regs_s {
+ struct {
+ uint32_t bnds;
+ uint32_t config;
+ uint32_t config_2;
+ } cs[MAX_CHIP_SELECTS_PER_CTRL];
+ uint32_t timing_cfg_3;
+ uint32_t timing_cfg_0;
+ uint32_t timing_cfg_1;
+ uint32_t timing_cfg_2;
+ uint32_t ddr_sdram_cfg;
+ uint32_t ddr_sdram_cfg_2;
+ uint32_t ddr_sdram_mode;
+ uint32_t ddr_sdram_mode_2;
+ uint32_t ddr_sdram_mode_3;
+ uint32_t ddr_sdram_mode_4;
+ uint32_t ddr_sdram_mode_5;
+ uint32_t ddr_sdram_mode_6;
+ uint32_t ddr_sdram_mode_7;
+ uint32_t ddr_sdram_mode_8;
+ uint32_t ddr_sdram_md_cntl;
+ uint32_t ddr_sdram_interval;
+ uint32_t ddr_data_init;
+ uint32_t ddr_sdram_clk_cntl;
+ uint32_t ddr_init_addr;
+ uint32_t ddr_init_ext_addr;
+ uint32_t err_disable;
+ uint32_t err_int_en;
+ uint32_t debug[32];
+};
+
+/*
+ * Data Structures
+ *
+ * All data structures have to be on the stack
+ */
+struct fsl_ddr_info_s {
+ generic_spd_eeprom_t
+ spd_installed_dimms[MAX_DIMM_SLOTS_PER_CTLR];
+ struct dimm_params_s
+ dimm_params[MAX_DIMM_SLOTS_PER_CTLR];
+ struct memctl_options_s memctl_opts;
+ struct common_timing_params_s common_timing_params;
+ struct fsl_ddr_cfg_regs_s fsl_ddr_config_reg;
+ struct ddr_board_info_s board_info;
+};
+
+uint32_t mclk_to_picos(uint32_t mclk);
+uint32_t get_memory_clk_period_ps(void);
+uint32_t picos_to_mclk(uint32_t picos);
+uint32_t check_fsl_memctl_config_regs(const struct fsl_ddr_cfg_regs_s *ddr);
+uint64_t fsl_ddr_compute(struct fsl_ddr_info_s *pinfo);
+uint32_t compute_fsl_memctl_config_regs(
+ const struct memctl_options_s *popts,
+ struct fsl_ddr_cfg_regs_s *ddr,
+ const struct common_timing_params_s *common_dimm,
+ const struct dimm_params_s *dimm_parameters,
+ uint32_t dbw_capacity_adjust);
+uint32_t compute_dimm_parameters(
+ const generic_spd_eeprom_t *spdin,
+ struct dimm_params_s *pdimm);
+uint32_t compute_lowest_common_dimm_parameters(
+ const struct dimm_params_s *dimm_params,
+ struct common_timing_params_s *outpdimm,
+ uint32_t number_of_dimms);
+uint32_t populate_memctl_options(
+ int all_DIMMs_registered,
+ struct memctl_options_s *popts,
+ struct dimm_params_s *pdimm);
+int fsl_ddr_set_lawbar(
+ const struct common_timing_params_s *memctl_common_params,
+ uint32_t memctl_interleaved);
+int fsl_ddr_get_spd(
+ generic_spd_eeprom_t *ctrl_dimms_spd,
+ struct ddr_board_info_s *binfo);
+int fsl_ddr_set_memctl_regs(
+ const struct fsl_ddr_info_s *info);
+void fsl_ddr_board_options(
+ struct memctl_options_s *popts,
+ struct dimm_params_s *pdimm);
+void fsl_ddr_board_info(struct ddr_board_info_s *info);
+#endif
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 4/8] ppc 8xxx: DIMM parameters calculation
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (11 preceding siblings ...)
2013-06-25 10:45 ` [PATCH 3/8] ppc 8xxx: " Renaud Barbier
@ 2013-06-25 10:45 ` Renaud Barbier
2013-06-25 10:45 ` [PATCH 5/8] ppc 8xxx: DDR utility and memory options Renaud Barbier
` (3 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-25 10:45 UTC (permalink / raw)
To: barebox
This code calculates the DIMM characteritics i.e DIMM
organization parameters and timings for DDR2 memory based on
SPD data.
It also provides a function to find out the lowest common DIMM
parameters to be used for all DIMMs.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/ddr2_dimm_params.c | 303 +++++++++++++++++++++++++++++
arch/ppc/ddr-8xxx/lc_common_dimm_params.c | 214 ++++++++++++++++++++
2 files changed, 517 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/ddr2_dimm_params.c
create mode 100644 arch/ppc/ddr-8xxx/lc_common_dimm_params.c
diff --git a/arch/ppc/ddr-8xxx/ddr2_dimm_params.c b/arch/ppc/ddr-8xxx/ddr2_dimm_params.c
new file mode 100644
index 0000000..f04d826
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr2_dimm_params.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+#include "ddr.h"
+/*
+ * Calculate the Density of each Physical Rank.
+ * Returned size is in bytes.
+ *
+ * Table comes from Byte 31 of JEDEC SPD Spec.
+ *
+ * DDR II
+ * Bit Size Size
+ * --- -----
+ * 7 high 512MB
+ * 6 256MB
+ * 5 128MB
+ * 4 16GB
+ * 3 8GB
+ * 2 4GB
+ * 1 2GB
+ * 0 low 1GB
+ *
+ * Reorder Table to be linear by stripping the bottom
+ * 2 or 5 bits off and shifting them up to the top.
+ *
+ */
+static uint64_t compute_ranksize(uint32_t mem_type, unsigned char row_dens)
+{
+ uint64_t bsize;
+
+ bsize = ((row_dens >> 5) | ((row_dens & 31) << 3));
+ bsize <<= 27ULL;
+
+ return bsize;
+}
+
+/*
+ * Convert a two-nibble BCD value into a cycle time.
+ * While the spec calls for nano-seconds, picos are returned.
+ */
+static uint32_t convert_bcd_tenths_to_cycle_time_ps(uint32_t spd_val)
+{
+ uint32_t tenths_ps[16] = {
+ 0,
+ 100,
+ 200,
+ 300,
+ 400,
+ 500,
+ 600,
+ 700,
+ 800,
+ 900,
+ 250,
+ 330,
+ 660,
+ 750,
+ 0,
+ 0
+ };
+ uint32_t whole_ns = (spd_val & 0xF0) >> 4;
+ uint32_t tenth_ns = spd_val & 0x0F;
+ uint32_t ps = (whole_ns * 1000) + tenths_ps[tenth_ns];
+
+ return ps;
+}
+
+static uint32_t convert_bcd_hundredths_to_cycle_time_ps(uint32_t spd_val)
+{
+ uint32_t tenth_ns = (spd_val & 0xF0) >> 4;
+ uint32_t hundredth_ns = spd_val & 0x0F;
+ uint32_t ps = (tenth_ns * 100) + (hundredth_ns * 10);
+
+ return ps;
+}
+
+static uint32_t byte40_table_ps[8] = {
+ 0,
+ 250,
+ 330,
+ 500,
+ 660,
+ 750,
+ 0,
+ 0
+};
+
+static uint32_t
+compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc)
+{
+ uint32_t trfc_ps;
+
+ trfc_ps = (((trctrfc_ext & 0x1) * 256) + trfc) * 1000;
+ trfc_ps += byte40_table_ps[(trctrfc_ext >> 1) & 0x7];
+
+ return trfc_ps;
+}
+
+static uint32_t
+compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc)
+{
+ uint32_t trc_ps;
+
+ trc_ps = (trc * 1000);
+ trc_ps += byte40_table_ps[(trctrfc_ext >> 4) & 0x7];
+
+ return trc_ps;
+}
+
+/*
+ * Determine Refresh Rate.
+ * Table from SPD Spec, Byte 12, converted to picoseconds and
+ * filled in with "default" normal values.
+ */
+static uint32_t determine_refresh_rate_ps(const uint32_t spd_refresh)
+{
+ uint32_t refresh_time_ps[8] = {
+ 15625000, /* 0 Normal 1.00x */
+ 3900000, /* 1 Reduced .25x */
+ 7800000, /* 2 Extended .50x */
+ 31300000, /* 3 Extended 2.00x */
+ 62500000, /* 4 Extended 4.00x */
+ 125000000, /* 5 Extended 8.00x */
+ 15625000, /* 6 Normal 1.00x filler */
+ 15625000, /* 7 Normal 1.00x filler */
+ };
+
+ return refresh_time_ps[spd_refresh & 0x7];
+}
+
+/*
+ * The purpose of this function is to compute a suitable
+ * CAS latency given the DRAM clock period. The SPD only
+ * defines at most 3 CAS latencies. Typically the slower in
+ * frequency the DIMM runs at, the shorter its CAS latency can.
+ * be. If the DIMM is operating at a sufficiently low frequency,
+ * it may be able to run at a CAS latency shorter than the
+ * shortest SPD-defined CAS latency.
+ *
+ * If a CAS latency is not found, 0 is returned.
+ *
+ * Do this by finding in the standard speed table the longest
+ * tCKmin that doesn't exceed the value of mclk_ps (tCK).
+ *
+ * An assumption made is that the SDRAM device allows the
+ * CL to be programmed for a value that is lower than those
+ * advertised by the SPD. This is not always the case,
+ * as those modes not defined in the SPD are optional.
+ *
+ * CAS latency de-rating based upon values JEDEC Standard No. 79-2C
+ * Table 40, "DDR2 SDRAM standard speed bins and tCK, tRCD, tRP, tRAS,
+ * and tRC for corresponding bin"
+ *
+ * ordinal 2, ddr2_speed_bins[1] contains tCK for CL=3
+ * Not certain if any good value exists for CL=2
+ */
+ /* CL2 CL3 CL4 CL5 CL6 CL7 */
+uint16_t ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500, 1875 };
+
+uint32_t compute_derated_DDR2_CAS_latency(uint32_t mclk_ps)
+{
+ const uint32_t num_speed_bins = ARRAY_SIZE(ddr2_speed_bins);
+ uint32_t lowest_tCKmin_found = 0, lowest_tCKmin_CL = 0, i, x;
+
+ for (i = 0; i < num_speed_bins; i++) {
+ x = ddr2_speed_bins[i];
+ if (x && (x <= mclk_ps) && (x >= lowest_tCKmin_found)) {
+ lowest_tCKmin_found = x;
+ lowest_tCKmin_CL = i + 2;
+ }
+ }
+
+ return lowest_tCKmin_CL;
+}
+
+/*
+ * compute_dimm_parameters for DDR2 SPD
+ *
+ * Compute DIMM parameters based upon the SPD information in SPD.
+ * Writes the results to the dimm_params_s structure pointed by pdimm.
+ */
+uint32_t
+compute_dimm_parameters(const generic_spd_eeprom_t *spdin,
+ struct dimm_params_s *pdimm)
+{
+ const struct ddr2_spd_eeprom_s *spd = spdin;
+ uint32_t retval;
+
+ if (!spd->mem_type) {
+ memset(pdimm, 0, sizeof(struct dimm_params_s));
+ goto error;
+ }
+
+ if (spd->mem_type != SPD_MEMTYPE_DDR2)
+ goto error;
+
+ retval = ddr2_spd_check(spd);
+ if (retval)
+ goto spd_err;
+
+ /*
+ * The part name in ASCII in the SPD EEPROM is not null terminated.
+ * Guarantee null termination here by presetting all bytes to 0
+ * and copying the part name in ASCII from the SPD onto it
+ */
+ memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
+ memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
+
+ /* DIMM organization parameters */
+ pdimm->n_ranks = (spd->mod_ranks & 0x7) + 1;
+ pdimm->rank_density = compute_ranksize(spd->mem_type, spd->rank_dens);
+ pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
+ pdimm->data_width = spd->dataw;
+ pdimm->primary_sdram_width = spd->primw;
+ pdimm->ec_sdram_width = spd->ecw;
+
+ /* These are all the types defined by the JEDEC DDR2 SPD 1.3 spec */
+ switch (spd->dimm_type) {
+ case DDR2_SPD_DIMMTYPE_RDIMM:
+ case DDR2_SPD_DIMMTYPE_72B_SO_RDIMM:
+ case DDR2_SPD_DIMMTYPE_MINI_RDIMM:
+ /* Registered/buffered DIMMs */
+ pdimm->registered_dimm = 1;
+ break;
+
+ case DDR2_SPD_DIMMTYPE_UDIMM:
+ case DDR2_SPD_DIMMTYPE_SO_DIMM:
+ case DDR2_SPD_DIMMTYPE_MICRO_DIMM:
+ case DDR2_SPD_DIMMTYPE_MINI_UDIMM:
+ /* Unbuffered DIMMs */
+ pdimm->registered_dimm = 0;
+ break;
+
+ case DDR2_SPD_DIMMTYPE_72B_SO_CDIMM:
+ default:
+ goto error;
+ }
+
+ pdimm->n_row_addr = spd->nrow_addr;
+ pdimm->n_col_addr = spd->ncol_addr;
+ pdimm->n_banks_per_sdram_device = spd->nbanks;
+ pdimm->edc_config = spd->config;
+ pdimm->burst_lengths_bitmask = spd->burstl;
+ pdimm->row_density = spd->rank_dens;
+
+ /*
+ * Calculate the Maximum Data Rate based on the Minimum Cycle time.
+ * The SPD clk_cycle field (tCKmin) is measured in tenths of
+ * nanoseconds and represented as BCD.
+ */
+ pdimm->tCKmin_X_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle);
+ pdimm->tCKmin_X_minus_1_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2);
+ pdimm->tCKmin_X_minus_2_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3);
+ pdimm->tCKmax_ps = convert_bcd_tenths_to_cycle_time_ps(spd->tckmax);
+
+ /*
+ * Compute CAS latencies defined by SPD
+ * The SPD caslat_X should have at least 1 and at most 3 bits set.
+ *
+ * If cas_lat after masking is 0, the __ilog2 function returns
+ * 255 into the variable. This behavior is abused once.
+ */
+ pdimm->caslat_X = __ilog2(spd->cas_lat);
+ pdimm->caslat_X_minus_1 = __ilog2(spd->cas_lat
+ & ~(1 << pdimm->caslat_X));
+ pdimm->caslat_X_minus_2 = __ilog2(spd->cas_lat & ~(1 << pdimm->caslat_X)
+ & ~(1 << pdimm->caslat_X_minus_1));
+ pdimm->caslat_lowest_derated
+ = compute_derated_DDR2_CAS_latency(get_memory_clk_period_ps());
+ pdimm->tRCD_ps = spd->trcd * 250;
+ pdimm->tRP_ps = spd->trp * 250;
+ pdimm->tRAS_ps = spd->tras * 1000;
+ pdimm->tWR_ps = spd->twr * 250;
+ pdimm->tWTR_ps = spd->twtr * 250;
+ pdimm->tRFC_ps = compute_trfc_ps_from_spd(spd->trctrfc_ext, spd->trfc);
+ pdimm->tRRD_ps = spd->trrd * 250;
+ pdimm->tRC_ps = compute_trc_ps_from_spd(spd->trctrfc_ext, spd->trc);
+ pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh);
+ pdimm->tIS_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup);
+ pdimm->tIH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold);
+ pdimm->tDS_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup);
+ pdimm->tDH_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold);
+ pdimm->tRTP_ps = spd->trtp * 250;
+ pdimm->tDQSQ_max_ps = spd->tdqsq * 10;
+ pdimm->tQHS_ps = spd->tqhs * 10;
+
+ return 0;
+error:
+ return 1;
+spd_err:
+ return 2;
+}
diff --git a/arch/ppc/ddr-8xxx/lc_common_dimm_params.c b/arch/ppc/ddr-8xxx/lc_common_dimm_params.c
new file mode 100644
index 0000000..a1addb0
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/lc_common_dimm_params.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <config.h>
+#include <asm/fsl_ddr_sdram.h>
+
+#include "ddr.h"
+
+static unsigned int common_burst_length(
+ const struct dimm_params_s *dimm_params,
+ const unsigned int number_of_dimms)
+{
+ unsigned int i, temp;
+
+ temp = 0xff;
+ for (i = 0; i < number_of_dimms; i++)
+ if (dimm_params[i].n_ranks)
+ temp &= dimm_params[i].burst_lengths_bitmask;
+
+ return temp;
+}
+
+/* Compute a CAS latency suitable for all DIMMs */
+static unsigned int compute_lowest_caslat(
+ const struct dimm_params_s *dimm_params,
+ const unsigned int number_of_dimms)
+{
+ uint32_t temp1, temp2, i, not_ok, lowest_good_caslat,
+ tCKmin_X_minus_1_ps, tCKmin_X_minus_2_ps;
+ const unsigned int mclk_ps = get_memory_clk_period_ps();
+
+ /*
+ * Step 1: find CAS latency common to all DIMMs using bitwise
+ * operation.
+ */
+ temp1 = 0xFF;
+ for (i = 0; i < number_of_dimms; i++)
+ if (dimm_params[i].n_ranks) {
+ temp2 = 0;
+ temp2 |= 1 << dimm_params[i].caslat_X;
+ temp2 |= 1 << dimm_params[i].caslat_X_minus_1;
+ temp2 |= 1 << dimm_params[i].caslat_X_minus_2;
+ /*
+ * FIXME: If there was no entry for X-2 (X-1) in
+ * the SPD, then caslat_X_minus_2
+ * (caslat_X_minus_1) contains either 255 or
+ * 0xFFFFFFFF because that's what the __ilog2
+ * function returns for an input of 0.
+ * On 32-bit PowerPC, left shift counts with bit
+ * 26 set (that the value of 255 or 0xFFFFFFFF
+ * will have), cause the destination register to
+ * be 0. That is why this works.
+ */
+ temp1 &= temp2;
+ }
+
+ /*
+ * Step 2: check each common CAS latency against tCK of each
+ * DIMM's SPD.
+ */
+ lowest_good_caslat = 0;
+ temp2 = 0;
+ while (temp1) {
+ not_ok = 0;
+ temp2 = __ilog2(temp1);
+
+ for (i = 0; i < number_of_dimms; i++) {
+ if (!dimm_params[i].n_ranks)
+ continue;
+
+ if (dimm_params[i].caslat_X == temp2) {
+ if (mclk_ps >= dimm_params[i].tCKmin_X_ps)
+ continue;
+ else
+ not_ok++;
+ }
+
+ if (dimm_params[i].caslat_X_minus_1 == temp2) {
+ tCKmin_X_minus_1_ps =
+ dimm_params[i].tCKmin_X_minus_1_ps;
+ if (mclk_ps >= tCKmin_X_minus_1_ps)
+ continue;
+ else
+ not_ok++;
+ }
+
+ if (dimm_params[i].caslat_X_minus_2 == temp2) {
+ tCKmin_X_minus_2_ps
+ = dimm_params[i].tCKmin_X_minus_2_ps;
+ if (mclk_ps >= tCKmin_X_minus_2_ps)
+ continue;
+ else
+ not_ok++;
+ }
+ }
+
+ if (!not_ok)
+ lowest_good_caslat = temp2;
+
+ temp1 &= ~(1 << temp2);
+ }
+ return lowest_good_caslat;
+}
+
+/*
+ * compute_lowest_common_dimm_parameters()
+ *
+ * Determine the worst-case DIMM timing parameters from the set of DIMMs
+ * whose parameters have been computed into the array pointed to
+ * by dimm_params.
+ */
+unsigned int
+compute_lowest_common_dimm_parameters(const struct dimm_params_s *dimm,
+ struct common_timing_params_s *out,
+ const unsigned int number_of_dimms)
+{
+ const uint32_t mclk_ps = get_memory_clk_period_ps();
+ uint32_t temp1, i;
+ struct common_timing_params_s tmp = {0};
+
+ tmp.tCKmax_ps = 0xFFFFFFFF;
+ temp1 = 0;
+ for (i = 0; i < number_of_dimms; i++) {
+ if (dimm[i].n_ranks == 0) {
+ temp1++;
+ continue;
+ }
+
+ /*
+ * Find minimum tCKmax_ps to find fastest slow speed,
+ * i.e., this is the slowest the whole system can go.
+ */
+ tmp.tCKmax_ps = min(tmp.tCKmax_ps, dimm[i].tCKmax_ps);
+
+ /* Find maximum value to determine slowest speed, delay, etc */
+ tmp.tCKmin_X_ps = max(tmp.tCKmin_X_ps, dimm[i].tCKmin_X_ps);
+ tmp.tCKmax_max_ps = max(tmp.tCKmax_max_ps, dimm[i].tCKmax_ps);
+ tmp.tRCD_ps = max(tmp.tRCD_ps, dimm[i].tRCD_ps);
+ tmp.tRP_ps = max(tmp.tRP_ps, dimm[i].tRP_ps);
+ tmp.tRAS_ps = max(tmp.tRAS_ps, dimm[i].tRAS_ps);
+ tmp.tWR_ps = max(tmp.tWR_ps, dimm[i].tWR_ps);
+ tmp.tWTR_ps = max(tmp.tWTR_ps, dimm[i].tWTR_ps);
+ tmp.tRFC_ps = max(tmp.tRFC_ps, dimm[i].tRFC_ps);
+ tmp.tRRD_ps = max(tmp.tRRD_ps, dimm[i].tRRD_ps);
+ tmp.tRC_ps = max(tmp.tRC_ps, dimm[i].tRC_ps);
+ tmp.tIS_ps = max(tmp.tIS_ps, dimm[i].tIS_ps);
+ tmp.tIH_ps = max(tmp.tIH_ps, dimm[i].tIH_ps);
+ tmp.tDS_ps = max(tmp.tDS_ps, dimm[i].tDS_ps);
+ tmp.tDH_ps = max(tmp.tDH_ps, dimm[i].tDH_ps);
+ tmp.tRTP_ps = max(tmp.tRTP_ps, dimm[i].tRTP_ps);
+ tmp.tQHS_ps = max(tmp.tQHS_ps, dimm[i].tQHS_ps);
+ tmp.refresh_rate_ps = max(tmp.refresh_rate_ps,
+ dimm[i].refresh_rate_ps);
+ /* Find maximum tDQSQ_max_ps to find slowest timing. */
+ tmp.tDQSQ_max_ps = max(tmp.tDQSQ_max_ps, dimm[i].tDQSQ_max_ps);
+ }
+ tmp.ndimms_present = number_of_dimms - temp1;
+
+ if (temp1 == number_of_dimms)
+ return 0;
+
+ temp1 = common_burst_length(dimm, number_of_dimms);
+ tmp.all_DIMMs_burst_lengths_bitmask = temp1;
+ tmp.all_DIMMs_registered = 0;
+
+ tmp.lowest_common_SPD_caslat = compute_lowest_caslat(dimm,
+ number_of_dimms);
+ /*
+ * Compute a common 'de-rated' CAS latency.
+ *
+ * The strategy here is to find the *highest* de-rated cas latency
+ * with the assumption that all of the DIMMs will support a de-rated
+ * CAS latency higher than or equal to their lowest de-rated value.
+ */
+ temp1 = 0;
+ for (i = 0; i < number_of_dimms; i++)
+ temp1 = max(temp1, dimm[i].caslat_lowest_derated);
+ tmp.highest_common_derated_caslat = temp1;
+
+ temp1 = 1;
+ for (i = 0; i < number_of_dimms; i++)
+ if (dimm[i].n_ranks &&
+ !(dimm[i].edc_config & EDC_ECC)) {
+ temp1 = 0;
+ break;
+ }
+ tmp.all_DIMMs_ECC_capable = temp1;
+
+ if (mclk_ps > tmp.tCKmax_max_ps)
+ return 1;
+
+ /*
+ * AL must be less or equal to tRCD. Typically, AL would
+ * be AL = tRCD - 1;
+ *
+ * When ODT read or write is enabled the sum of CAS latency +
+ * additive latency must be at least 3 cycles.
+ *
+ */
+ if ((tmp.lowest_common_SPD_caslat < 4) && (picos_to_mclk(tmp.tRCD_ps) >
+ tmp.lowest_common_SPD_caslat))
+ tmp.additive_latency = picos_to_mclk(tmp.tRCD_ps) -
+ tmp.lowest_common_SPD_caslat;
+
+ memcpy(out, &tmp, sizeof(struct common_timing_params_s));
+
+ return 0;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 5/8] ppc 8xxx: DDR utility and memory options
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (12 preceding siblings ...)
2013-06-25 10:45 ` [PATCH 4/8] ppc 8xxx: DIMM parameters calculation Renaud Barbier
@ 2013-06-25 10:45 ` Renaud Barbier
2013-06-25 10:45 ` [PATCH 6/8] ppx 8xxx: DDR registers value calculation Renaud Barbier
` (2 subsequent siblings)
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-25 10:45 UTC (permalink / raw)
To: barebox
This commit adds functions to calculate clock cycles, configure the
LAW registers and populate board memory options.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/options.c | 111 +++++++++++++++++++++++++++++++++++++++++++
arch/ppc/ddr-8xxx/util.c | 100 ++++++++++++++++++++++++++++++++++++++
2 files changed, 211 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/options.c
create mode 100644 arch/ppc/ddr-8xxx/util.c
diff --git a/arch/ppc/ddr-8xxx/options.c b/arch/ppc/ddr-8xxx/options.c
new file mode 100644
index 0000000..22b621f
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/options.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2008, 2010-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+#include "ddr.h"
+
+uint32_t populate_memctl_options(int all_DIMMs_registered,
+ struct memctl_options_s *popts,
+ struct dimm_params_s *pdimm)
+{
+ const struct ddr_board_info_s *binfo = popts->board_info;
+ uint32_t i;
+
+ for (i = 0; i < binfo->cs_per_ctrl; i++) {
+ popts->cs_local_opts[i].odt_rd_cfg = FSL_DDR_ODT_NEVER;
+ popts->cs_local_opts[i].odt_wr_cfg = FSL_DDR_ODT_ALL;
+ popts->cs_local_opts[i].odt_rtt_norm = DDR2_RTT_50_OHM;
+ popts->cs_local_opts[i].odt_rtt_wr = DDR2_RTT_OFF;
+ popts->cs_local_opts[i].auto_precharge = 0;
+ }
+
+ /* Memory Organization Parameters */
+ popts->registered_dimm_en = all_DIMMs_registered;
+ popts->ECC_mode = 0; /* 0 = disabled, 1 = enabled */
+ popts->ECC_init_using_memctl = 1; /* 0 = use DMA, 1 = use memctl */
+
+ /* Choose DQS config - 1 for DDR2 */
+ popts->DQS_config = 1;
+
+ /* Choose self-refresh during sleep. */
+ popts->self_refresh_in_sleep = 1;
+
+ /* Choose dynamic power management mode. */
+ popts->dynamic_power = 0;
+
+ /*
+ * check first dimm for primary sdram width
+ * assuming all dimms are similar
+ * 0 = 64-bit, 1 = 32-bit, 2 = 16-bit
+ */
+ if (pdimm->n_ranks != 0) {
+ if ((pdimm->data_width >= 64) && (pdimm->data_width <= 72))
+ popts->data_bus_width = 0;
+ else if ((pdimm->data_width >= 32) ||
+ (pdimm->data_width <= 40))
+ popts->data_bus_width = 1;
+ else
+ panic("data width %u is invalid!\n",
+ pdimm->data_width);
+ }
+
+ /* Must be a burst length of 4 for DD2 */
+ popts->burst_length = DDR_BL4;
+ /* Decide whether to use the computed de-rated latency */
+ popts->use_derated_caslat = 0;
+
+ /*
+ * 2T_EN setting
+ *
+ * Factors to consider for 2T_EN:
+ * - number of DIMMs installed
+ * - number of components, number of active ranks
+ * - how much time you want to spend playing around
+ */
+ popts->twoT_en = 0;
+
+ /*
+ * Default BSTTOPRE precharge interval
+ *
+ * Set the parameter to 0 for global auto precharge in
+ * the board options function.
+ */
+ popts->bstopre = 0x100;
+
+ /* Minimum CKE pulse width -- tCKE(MIN) */
+ popts->tCKE_clock_pulse_width_ps
+ = mclk_to_picos(FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR);
+
+ /*
+ * Window for four activates -- tFAW
+ *
+ * Set according to specification for the memory used.
+ * The default value below would work for x4/x8 wide memory.
+ *
+ */
+ popts->tFAW_window_four_activates_ps = 37500;
+
+ /*
+ * Default powerdown exit timings.
+ * Set according to specifications for the memory used in
+ * the board options function.
+ */
+ popts->txard = 3;
+ popts->txp = 3;
+ popts->taxpd = 11;
+
+ /* Default value for load mode cycle time */
+ popts->tmrd = 2;
+
+ /* Specific board override parameters. */
+ fsl_ddr_board_options(popts, pdimm);
+
+ return 0;
+}
diff --git a/arch/ppc/ddr-8xxx/util.c b/arch/ppc/ddr-8xxx/util.c
new file mode 100644
index 0000000..626b5f3
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/util.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <asm/fsl_law.h>
+#include <asm-generic/div64.h>
+#include <mach/clock.h>
+#include "ddr.h"
+
+#define ULL_2E12 2000000000000ULL
+#define UL_5POW12 244140625UL
+#define UL_2POW13 (1UL << 13)
+#define ULL_8FS 0xFFFFFFFFULL
+
+/*
+ * Round up mclk_ps to nearest 1 ps in memory controller code
+ * if the error is 0.5ps or more.
+ *
+ * If an imprecise data rate is too high due to rounding error
+ * propagation, compute a suitably rounded mclk_ps to compute
+ * a working memory controller configuration.
+ */
+uint32_t get_memory_clk_period_ps(void)
+{
+ uint32_t result, data_rate = fsl_get_ddr_freq(0);
+ /* Round to nearest 10ps, being careful about 64-bit multiply/divide */
+ uint64_t rem, mclk_ps = ULL_2E12;
+
+ /* Now perform the big divide, the result fits in 32-bits */
+ rem = do_div(mclk_ps, data_rate);
+ if (rem >= (data_rate >> 1))
+ result = mclk_ps + 1;
+ else
+ result = mclk_ps;
+
+ return result;
+}
+
+/* Convert picoseconds into DRAM clock cycles (rounding up if needed). */
+uint32_t picos_to_mclk(uint32_t picos)
+{
+ uint64_t clks, clks_rem;
+ uint32_t data_rate = fsl_get_ddr_freq(0);
+
+ if (!picos)
+ return 0;
+
+ /* First multiply the time by the data rate (32x32 => 64) */
+ clks = picos * (uint64_t)data_rate;
+ /*
+ * Now divide by 5^12 and track the 32-bit remainder, then divide
+ * by 2*(2^12) using shifts (and updating the remainder).
+ */
+ clks_rem = do_div(clks, UL_5POW12);
+ clks_rem += (clks & (UL_2POW13 - 1)) * UL_5POW12;
+ clks >>= 13;
+
+ /* If we had a remainder greater than the 1ps error, then round up */
+ if (clks_rem > data_rate)
+ clks++;
+
+ if (clks > ULL_8FS)
+ clks = ULL_8FS;
+
+ return (uint32_t)clks;
+}
+
+uint32_t mclk_to_picos(unsigned int mclk)
+{
+ return get_memory_clk_period_ps() * mclk;
+}
+
+int fsl_ddr_set_lawbar(
+ const struct common_timing_params_s *params,
+ uint32_t law_memctl)
+{
+ uint64_t base = params->base_address;
+ uint64_t size = params->total_mem;
+
+ if (!params->ndimms_present)
+ goto out;
+
+ if (base >= MAX_MEM_MAPPED)
+ goto error;
+
+ if ((base + size) >= MAX_MEM_MAPPED)
+ size = MAX_MEM_MAPPED - base;
+
+ if (fsl_set_ddr_laws(base, size, law_memctl) < 0)
+ goto error;
+out:
+ return 0;
+error:
+ return 1;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 6/8] ppx 8xxx: DDR registers value calculation
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (13 preceding siblings ...)
2013-06-25 10:45 ` [PATCH 5/8] ppc 8xxx: DDR utility and memory options Renaud Barbier
@ 2013-06-25 10:45 ` Renaud Barbier
2013-06-25 10:45 ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
2013-06-25 10:45 ` [PATCH 8/8] ppc 85xx: early I2C support Renaud Barbier
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-25 10:45 UTC (permalink / raw)
To: barebox
The functions in this file calculate and store the value for each
register of the memory controller.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/ctrl_regs.c | 425 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 425 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/ctrl_regs.c
diff --git a/arch/ppc/ddr-8xxx/ctrl_regs.c b/arch/ppc/ddr-8xxx/ctrl_regs.c
new file mode 100644
index 0000000..3f8ecf7
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ctrl_regs.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * Generic driver for Freescale DDR2 memory controller.
+ * Based on code from spd_sdram.c
+ * Author: James Yang [at freescale.com]
+ */
+
+#include <common.h>
+#include <asm/fsl_ddr_sdram.h>
+#include "ddr.h"
+
+static void set_csn_config(int dimm_number, int i,
+ struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct dimm_params_s *dimm)
+{
+ uint32_t cs_n_en = 0, ap_n_en = 0, odt_rd_cfg = 0, odt_wr_cfg = 0,
+ ba_bits_cs_n = 0, row_bits_cs_n = 0, col_bits_cs_n = 0,
+ n_banks_per_sdram_device;
+ int go_config = 0;
+
+ switch (i) {
+ case 0:
+ if (dimm[dimm_number].n_ranks > 0)
+ go_config = 1;
+ break;
+ case 1:
+ if ((dimm_number == 0 && dimm[0].n_ranks > 1) ||
+ (dimm_number == 1 && dimm[1].n_ranks > 0))
+ go_config = 1;
+ break;
+ case 2:
+ if ((dimm_number == 0 && dimm[0].n_ranks > 2) ||
+ (dimm_number >= 1 && dimm[dimm_number].n_ranks > 0))
+ go_config = 1;
+ break;
+ case 3:
+ if ((dimm_number == 0 && dimm[0].n_ranks > 3) ||
+ (dimm_number == 1 && dimm[1].n_ranks > 1) ||
+ (dimm_number == 3 && dimm[3].n_ranks > 0))
+ go_config = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (go_config) {
+ /* Chip Select enable */
+ cs_n_en = 1;
+ /* CSn auto-precharge enable */
+ ap_n_en = popts->cs_local_opts[i].auto_precharge;
+ /* ODT for reads configuration */
+ odt_rd_cfg = popts->cs_local_opts[i].odt_rd_cfg;
+ /* ODT for writes configuration */
+ odt_wr_cfg = popts->cs_local_opts[i].odt_wr_cfg;
+ /* Num of bank bits for SDRAM on CSn */
+ n_banks_per_sdram_device =
+ dimm[dimm_number].n_banks_per_sdram_device;
+ ba_bits_cs_n = __ilog2(n_banks_per_sdram_device) - 2;
+ /* Num of row bits for SDRAM on CSn */
+ row_bits_cs_n = dimm[dimm_number].n_row_addr - 12;
+ /* Num of ocl bits for SDRAM on CSn */
+ col_bits_cs_n = dimm[dimm_number].n_col_addr - 8;
+ }
+
+ ddr->cs[i].config = (((cs_n_en & 0x1) << 31)
+ | ((ap_n_en & 0x1) << 23)
+ | ((odt_rd_cfg & 0x7) << 20)
+ | ((odt_wr_cfg & 0x7) << 16)
+ | ((ba_bits_cs_n & 0x3) << 14)
+ | ((row_bits_cs_n & 0x7) << 8)
+ | ((col_bits_cs_n & 0x7) << 0));
+}
+
+static void set_timing_cfg_0(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts)
+{
+ uint32_t trwt_mclk = 0;
+
+ if (popts->trwt_override)
+ trwt_mclk = popts->trwt;
+
+ ddr->timing_cfg_0 = (((trwt_mclk & 0x3) << 30)
+ | ((popts->txard & 0x7) << 20)
+ | ((popts->txp & 0xF) << 16)
+ | ((popts->taxpd & 0xf) << 8)
+ | ((popts->tmrd & 0xf) << 0));
+}
+
+static void set_timing_cfg_3(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct common_timing_params_s *dimm,
+ uint32_t cas_latency)
+{
+ uint32_t ext_pretoact, ext_acttopre, ext_acttorw, ext_refrec;
+
+ ext_pretoact = picos_to_mclk(dimm->tRP_ps) >> 4;
+ ext_acttopre = picos_to_mclk(dimm->tRAS_ps) >> 4;
+ ext_acttorw = picos_to_mclk(dimm->tRCD_ps) >> 4;
+ cas_latency = ((cas_latency << 1) - 1) >> 4;
+ ext_refrec = (picos_to_mclk(dimm->tRFC_ps) - 8) >> 4;
+
+ ddr->timing_cfg_3 = (((ext_pretoact & 0x1) << 28)
+ | ((ext_acttopre & 0x2) << 24)
+ | ((ext_acttorw & 0x1) << 22)
+ | ((ext_refrec & 0x1F) << 16)
+ | ((cas_latency & 0x3) << 12));
+}
+
+static void set_timing_cfg_1(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct common_timing_params_s *dimm,
+ uint32_t cas_latency)
+{
+ uint32_t pretoact_mclk, acttopre_mclk, acttorw_mclk, refrec_ctrl,
+ wrrec_mclk, acttoact_mclk, wrtord_mclk;
+ /* DDR_SDRAM_MODE doesn't support 9,11,13,15 */
+ static const u8 wrrec_table[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 10, 10, 12, 12, 14, 14, 0, 0
+ };
+
+ pretoact_mclk = picos_to_mclk(dimm->tRP_ps);
+ acttopre_mclk = picos_to_mclk(dimm->tRAS_ps);
+ acttorw_mclk = picos_to_mclk(dimm->tRCD_ps);
+
+ /*
+ * Translate CAS Latency to a DDR controller field value:
+ *
+ * CAS Lat DDR II Ctrl
+ * Clocks SPD Bit Value
+ * ------- ------- ------
+ * 1.0 0001
+ * 1.5 0010
+ * 2.0 2 0011
+ * 2.5 0100
+ * 3.0 3 0101
+ * 3.5 0110
+ * 4.0 4 0111
+ * 4.5 1000
+ * 5.0 5 1001
+ */
+ cas_latency = (cas_latency << 1) - 1;
+ refrec_ctrl = picos_to_mclk(dimm->tRFC_ps) - 8;
+ acttoact_mclk = picos_to_mclk(dimm->tRRD_ps);
+
+ wrrec_mclk = picos_to_mclk(dimm->tWR_ps);
+ if (wrrec_mclk <= 16)
+ wrrec_mclk = wrrec_table[wrrec_mclk - 1];
+
+ wrtord_mclk = picos_to_mclk(dimm->tWTR_ps);
+ if (wrtord_mclk < 2)
+ wrtord_mclk = 2;
+
+ ddr->timing_cfg_1 = (((pretoact_mclk & 0x0F) << 28)
+ | ((acttopre_mclk & 0x0F) << 24)
+ | ((acttorw_mclk & 0xF) << 20)
+ | ((cas_latency & 0xF) << 16)
+ | ((refrec_ctrl & 0xF) << 12)
+ | ((wrrec_mclk & 0x0F) << 8)
+ | ((acttoact_mclk & 0x0F) << 4)
+ | ((wrtord_mclk & 0x0F) << 0));
+}
+
+static void set_timing_cfg_2(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct common_timing_params_s *dimm,
+ uint32_t cas_latency, uint32_t additive_latency)
+{
+ uint32_t cpo, rd_to_pre, wr_data_delay, cke_pls, four_act;
+
+ cpo = popts->cpo_override;
+ rd_to_pre = picos_to_mclk(dimm->tRTP_ps);
+ if (rd_to_pre < 2)
+ rd_to_pre = 2;
+
+ if (additive_latency)
+ rd_to_pre += additive_latency;
+
+ wr_data_delay = popts->write_data_delay;
+ cke_pls = picos_to_mclk(popts->tCKE_clock_pulse_width_ps);
+ four_act = picos_to_mclk(popts->tFAW_window_four_activates_ps);
+
+ ddr->timing_cfg_2 = (((additive_latency & 0xf) << 28)
+ | ((cpo & 0x1f) << 23)
+ | (((cas_latency - 1) & 0xf) << 19)
+ | ((rd_to_pre & 7) << 13)
+ | ((wr_data_delay & 7) << 10)
+ | ((cke_pls & 0x7) << 6)
+ | ((four_act & 0x3f) << 0));
+}
+
+static void set_ddr_sdram_cfg(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct common_timing_params_s *dimm)
+{
+ uint32_t mem_en, sren, ecc_en, sdram_type, dyn_pwr, dbw, twoT_en, hse;
+
+ mem_en = 1;
+ sren = popts->self_refresh_in_sleep;
+ if (dimm->all_DIMMs_ECC_capable)
+ ecc_en = popts->ECC_mode;
+ else
+ ecc_en = 0;
+
+ if (popts->sdram_type)
+ sdram_type = popts->sdram_type;
+ else
+ sdram_type = FSL_SDRAM_TYPE;
+
+ twoT_en = popts->twoT_en;
+ dyn_pwr = popts->dynamic_power;
+ dbw = popts->data_bus_width;
+ hse = popts->half_strength_driver_enable;
+
+ ddr->ddr_sdram_cfg = (((mem_en & 0x1) << 31)
+ | ((sren & 0x1) << 30)
+ | ((ecc_en & 0x1) << 29)
+ | ((sdram_type & 0x7) << 24)
+ | ((dyn_pwr & 0x1) << 21)
+ | ((dbw & 0x3) << 19)
+ | ((twoT_en & 0x1) << 15)
+ | ((hse & 0x1) << 3));
+}
+
+static void set_ddr_sdram_cfg_2(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts)
+{
+ struct ddr_board_info_s *bi = popts->board_info;
+ uint32_t i, dll_rst_dis, dqs_cfg, odt_cfg = 0, num_pr, d_init = 0;
+
+ dll_rst_dis = popts->dll_rst_dis;
+ dqs_cfg = popts->DQS_config;
+
+ /*
+ * Check for On-Die Termination options and
+ * assert ODT only during reads to DRAM.
+ */
+ for (i = 0; i < bi->cs_per_ctrl; i++)
+ if (popts->cs_local_opts[i].odt_rd_cfg ||
+ popts->cs_local_opts[i].odt_wr_cfg) {
+ odt_cfg = SDRAM_CFG2_ODT_ONLY_READ;
+ break;
+ }
+
+ /* Default number of posted refresh */
+ num_pr = 1;
+
+ if (popts->ECC_init_using_memctl) {
+ d_init = 1;
+ ddr->ddr_data_init = popts->data_init;
+ }
+
+ ddr->ddr_sdram_cfg_2 = (((dll_rst_dis & 0x1) << 29)
+ | ((dqs_cfg & 0x3) << 26)
+ | ((odt_cfg & 0x3) << 21)
+ | ((num_pr & 0xf) << 12)
+ | ((d_init & 0x1) << 4));
+}
+
+static void
+set_ddr_sdram_interval(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct common_timing_params_s *dimm)
+{
+ uint32_t refint, bstopre;
+
+ refint = picos_to_mclk(dimm->refresh_rate_ps);
+ /* Precharge interval */
+ bstopre = popts->bstopre;
+
+ ddr->ddr_sdram_interval = (((refint & 0xFFFF) << 16)
+ | ((bstopre & 0x3FFF) << 0));
+}
+
+static void set_ddr_sdram_mode(struct fsl_ddr_cfg_regs_s *ddr,
+ const struct memctl_options_s *popts,
+ const struct common_timing_params_s *dimm,
+ uint32_t cas_latency,
+ uint32_t additive_latency)
+{
+ uint16_t esdmode, sdmode;
+ uint32_t dqs_en, rtt, al, wr, bl;
+ const uint32_t mclk_ps = get_memory_clk_period_ps();
+
+ /* DQS# Enable: 0=enable, 1=disable */
+ dqs_en = !popts->DQS_config;
+ /* Posted CAS# additive latency (AL) */
+ al = additive_latency;
+ /* Internal Termination Resistor */
+ if (popts->rtt_override)
+ rtt = popts->rtt_override_value;
+ else
+ rtt = popts->cs_local_opts[0].odt_rtt_norm;
+
+ /*
+ * Extended SDRAM mode.
+ * The variable also selects:
+ * - OCD set to exit mode
+ * - all outputs bit i.e DQ, DQS, RDQS output enabled
+ * - RDQS ball disabled
+ * - DQS ball enabled
+ * - DLL enabled
+ * - Output drive strength: full strength.
+ */
+ esdmode = (((dqs_en & 0x1) << 10)
+ | ((rtt & 0x2) << 5)
+ | ((al & 0x7) << 3)
+ | ((rtt & 0x1) << 2));
+
+ /* Write recovery */
+ wr = (dimm->tWR_ps + mclk_ps - 1) / (mclk_ps - 1);
+
+ switch (popts->burst_length) {
+ case DDR_BL4:
+ bl = 2;
+ break;
+ case DDR_BL8:
+ bl = 3;
+ break;
+ default:
+ bl = 2;
+ break;
+ }
+
+ /* SDRAM mode
+ * The variable also selects:
+ * - power down mode: fast exit (normal)
+ * - DLL reset disabled.
+ * - burst type: sequential
+ */
+ sdmode = (((wr & 0x7) << 9)
+ | ((cas_latency & 0x7) << 4)
+ | ((bl & 0x7) << 0));
+
+ ddr->ddr_sdram_mode = (((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0));
+}
+
+uint32_t check_fsl_memctl_config_regs(const struct fsl_ddr_cfg_regs_s *ddr)
+{
+ /*
+ * DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN should not
+ * be set at the same time.
+ */
+ if ((ddr->ddr_sdram_cfg & 0x10000000) &&
+ (ddr->ddr_sdram_cfg & 0x00008000))
+ return 1;
+
+ return 0;
+}
+
+uint32_t
+compute_fsl_memctl_config_regs(const struct memctl_options_s *popts,
+ struct fsl_ddr_cfg_regs_s *ddr,
+ const struct common_timing_params_s *dimm,
+ const struct dimm_params_s *dimmp,
+ uint32_t dbw_cap_adj)
+{
+ struct ddr_board_info_s *binfo = popts->board_info;
+ uint32_t cas_latency, additive_latency, i, cs_per_dimm,
+ dimm_number;
+ uint64_t ea, sa, rank_density;
+
+ if (dimm == NULL)
+ return 1;
+
+ memset(ddr, 0, sizeof(struct fsl_ddr_cfg_regs_s));
+
+ /* Process overrides first. */
+ if (popts->cas_latency_override)
+ cas_latency = popts->cas_latency_override_value;
+ else
+ cas_latency = dimm->lowest_common_SPD_caslat;
+
+ if (popts->additive_latency_override)
+ additive_latency = popts->additive_latency_override_value;
+ else
+ additive_latency = dimm->additive_latency;
+
+ /* Chip Select Memory Bounds (CSn_BNDS) */
+ for (i = 0; i < binfo->cs_per_ctrl; i++) {
+ cs_per_dimm = binfo->cs_per_ctrl / binfo->dimm_slots_per_ctrl;
+ dimm_number = i / cs_per_dimm;
+ rank_density =
+ dimmp[dimm_number].rank_density >> dbw_cap_adj;
+
+ if (dimmp[dimm_number].n_ranks == 0)
+ continue;
+
+ sa = dimmp[dimm_number].base_address;
+ ea = sa + rank_density - 1;
+ if (dimmp[dimm_number].n_ranks > (i % cs_per_dimm)) {
+ sa += (i % cs_per_dimm) * rank_density;
+ ea += (i % cs_per_dimm) * rank_density;
+ } else {
+ sa = 0;
+ ea = 0;
+ }
+ sa >>= 24;
+ ea >>= 24;
+
+ ddr->cs[i].bnds = (((sa & 0xFFF) << 16) | ((ea & 0xFFF) << 0));
+ set_csn_config(dimm_number, i, ddr, popts, dimmp);
+ }
+
+ set_timing_cfg_0(ddr, popts);
+ set_timing_cfg_3(ddr, dimm, cas_latency);
+ set_timing_cfg_1(ddr, dimm, cas_latency);
+ set_timing_cfg_2(ddr, popts, dimm, cas_latency, additive_latency);
+ set_ddr_sdram_cfg(ddr, popts, dimm);
+ set_ddr_sdram_cfg_2(ddr, popts);
+ set_ddr_sdram_mode(ddr, popts, dimm, cas_latency, additive_latency);
+ set_ddr_sdram_interval(ddr, popts, dimm);
+
+ ddr->ddr_data_init = popts->data_init;
+ ddr->ddr_sdram_clk_cntl = (popts->clk_adjust & 0xF) << 23;
+
+ return check_fsl_memctl_config_regs(ddr);
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 7/8] ppc 8xxx: core DDR driver functions
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (14 preceding siblings ...)
2013-06-25 10:45 ` [PATCH 6/8] ppx 8xxx: DDR registers value calculation Renaud Barbier
@ 2013-06-25 10:45 ` Renaud Barbier
2013-06-25 10:45 ` [PATCH 8/8] ppc 85xx: early I2C support Renaud Barbier
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-25 10:45 UTC (permalink / raw)
To: barebox
These files are the driver interface to handle the memory
initialisation from reading the SPD data to writing the memory
controller registers.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/ddr-8xxx/ddr2_setctrl.c | 58 +++++++++
arch/ppc/ddr-8xxx/main.c | 238 ++++++++++++++++++++++++++++++++++++++
2 files changed, 296 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/ddr-8xxx/ddr2_setctrl.c
create mode 100644 arch/ppc/ddr-8xxx/main.c
diff --git a/arch/ppc/ddr-8xxx/ddr2_setctrl.c b/arch/ppc/ddr-8xxx/ddr2_setctrl.c
new file mode 100644
index 0000000..14571b0
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/ddr2_setctrl.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <config.h>
+#include <asm/io.h>
+#include <asm/fsl_ddr_sdram.h>
+#include <mach/early_udelay.h>
+#include "ddr.h"
+
+int fsl_ddr_set_memctl_regs(const struct fsl_ddr_info_s *info)
+{
+ uint32_t i;
+ void __iomem *ddr;
+ const struct fsl_ddr_cfg_regs_s *regs;
+
+ regs = &info->fsl_ddr_config_reg;
+ ddr = info->board_info.ddr_base;
+
+ if (in_be32(ddr + DDR_OFF(SDRAM_CFG)) & SDRAM_CFG_MEM_EN)
+ return 0;
+
+ for (i = 0; i < info->board_info.cs_per_ctrl; i++) {
+ out_be32(ddr + DDR_OFF(CS0_BNDS) + (i << 3), regs->cs[i].bnds);
+ out_be32(ddr + DDR_OFF(CS0_CONFIG) + (i << 2),
+ regs->cs[i].config);
+ }
+
+ out_be32(ddr + DDR_OFF(TIMING_CFG_3), regs->timing_cfg_3);
+ out_be32(ddr + DDR_OFF(TIMING_CFG_0), regs->timing_cfg_0);
+ out_be32(ddr + DDR_OFF(TIMING_CFG_1), regs->timing_cfg_1);
+ out_be32(ddr + DDR_OFF(TIMING_CFG_2), regs->timing_cfg_2);
+ out_be32(ddr + DDR_OFF(SDRAM_CFG_2), regs->ddr_sdram_cfg_2);
+ out_be32(ddr + DDR_OFF(SDRAM_MODE), regs->ddr_sdram_mode);
+ out_be32(ddr + DDR_OFF(SDRAM_MODE_2), regs->ddr_sdram_mode_2);
+ out_be32(ddr + DDR_OFF(SDRAM_MD_CNTL), regs->ddr_sdram_md_cntl);
+ out_be32(ddr + DDR_OFF(SDRAM_INTERVAL), regs->ddr_sdram_interval);
+ out_be32(ddr + DDR_OFF(SDRAM_DATA_INIT), regs->ddr_data_init);
+ out_be32(ddr + DDR_OFF(SDRAM_CLK_CNTL), regs->ddr_sdram_clk_cntl);
+ out_be32(ddr + DDR_OFF(SDRAM_INIT_ADDR), regs->ddr_init_addr);
+ out_be32(ddr + DDR_OFF(SDRAM_INIT_ADDR_EXT), regs->ddr_init_ext_addr);
+
+ early_udelay(200);
+ asm volatile("sync;isync");
+
+ out_be32(ddr + DDR_OFF(SDRAM_CFG), regs->ddr_sdram_cfg);
+
+ /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */
+ while (in_be32(ddr + DDR_OFF(SDRAM_CFG_2)) & SDRAM_CFG2_D_INIT)
+ early_udelay(10000);
+
+ return 0;
+}
diff --git a/arch/ppc/ddr-8xxx/main.c b/arch/ppc/ddr-8xxx/main.c
new file mode 100644
index 0000000..6e4a02d
--- /dev/null
+++ b/arch/ppc/ddr-8xxx/main.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ */
+
+/*
+ * Generic driver for Freescale DDR2 memory controller.
+ * Based on code from spd_sdram.c
+ * Author: James Yang [at freescale.com]
+ */
+
+#include <common.h>
+#include <config.h>
+#include <asm/fsl_law.h>
+#include "ddr.h"
+
+static int get_spd(generic_spd_eeprom_t *spd,
+ struct ddr_board_info_s *bi, u8 i2c_address)
+{
+ int ret;
+
+ fsl_i2c_init(bi->i2c_base, bi->i2c_speed, bi->i2c_slave);
+ ret = fsl_i2c_read(bi->i2c_base, i2c_address, 0, 1, (uchar *) spd,
+ sizeof(generic_spd_eeprom_t));
+ fsl_i2c_stop(bi->i2c_base);
+
+ return ret;
+}
+
+int fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd,
+ struct ddr_board_info_s *binfo)
+{
+ uint32_t i;
+ uint8_t i2c_address;
+
+ for (i = 0; i < binfo->dimm_slots_per_ctrl; i++) {
+ if (binfo->spd_i2c_addr == NULL)
+ goto error;
+ i2c_address = binfo->spd_i2c_addr[i];
+ if (get_spd(&(ctrl_dimms_spd[i]), binfo, i2c_address))
+ goto error;
+ }
+
+ return 0;
+error:
+ return 1;
+}
+
+static uint64_t step_assign_addresses(struct fsl_ddr_info_s *pinfo,
+ uint32_t dbw_cap_adj)
+{
+ uint64_t total_mem, current_mem_base, total_ctlr_mem, cap;
+ uint32_t ndimm, dw, j, found = 0;
+
+ ndimm = pinfo->board_info.dimm_slots_per_ctrl;
+ /*
+ * If a reduced data width is requested, but the SPD
+ * specifies a physically wider device, adjust the
+ * computed dimm capacities accordingly before
+ * assigning addresses.
+ */
+ switch (pinfo->memctl_opts.data_bus_width) {
+ case 2:
+ /* 16-bit */
+ for (j = 0; j < ndimm; j++) {
+ if (!pinfo->dimm_params[j].n_ranks)
+ continue;
+ dw = pinfo->dimm_params[j].primary_sdram_width;
+ if ((dw == 72 || dw == 64)) {
+ dbw_cap_adj = 2;
+ break;
+ } else if ((dw == 40 || dw == 32)) {
+ dbw_cap_adj = 1;
+ break;
+ }
+ }
+ break;
+ case 1:
+ /* 32-bit */
+ for (j = 0; j < ndimm; j++) {
+ dw = pinfo->dimm_params[j].data_width;
+ if (pinfo->dimm_params[j].n_ranks
+ && (dw == 72 || dw == 64)) {
+ /*
+ * FIXME: can't really do it
+ * like this because this just
+ * further reduces the memory
+ */
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ dbw_cap_adj = 1;
+ break;
+ case 0:
+ /* 64-bit */
+ break;
+ default:
+ return 1;
+ }
+
+ current_mem_base = 0ull;
+ total_mem = 0;
+ total_ctlr_mem = 0;
+ pinfo->common_timing_params.base_address = current_mem_base;
+
+ for (j = 0; j < ndimm; j++) {
+ cap = pinfo->dimm_params[j].capacity >> dbw_cap_adj;
+ pinfo->dimm_params[j].base_address = current_mem_base;
+ current_mem_base += cap;
+ total_ctlr_mem += cap;
+
+ }
+
+ pinfo->common_timing_params.total_mem = total_ctlr_mem;
+ total_mem += total_ctlr_mem;
+
+ return total_mem;
+}
+
+static uint32_t retrieve_max_size(struct fsl_ddr_cfg_regs_s *ddr_reg,
+ uint32_t ncs)
+{
+ uint32_t max_end = 0, end, i;
+
+ for (i = 0; i < ncs; i++)
+ if (ddr_reg->cs[i].config & 0x80000000) {
+ end = ddr_reg->cs[i].bnds & 0xFFF;
+ if (end > max_end)
+ max_end = end;
+ }
+
+ return max_end;
+}
+
+static uint32_t compute_dimm_param(struct fsl_ddr_info_s *pinfo, uint32_t ndimm)
+{
+ struct dimm_params_s *pdimm;
+ generic_spd_eeprom_t *spd;
+ uint32_t i, retval;
+
+ for (i = 0; i < ndimm; i++) {
+ spd = &(pinfo->spd_installed_dimms[i]);
+ pdimm = &(pinfo->dimm_params[i]);
+
+ retval = compute_dimm_parameters(spd, pdimm);
+
+ if (retval == 2) /* Fatal error */
+ return 1;
+ }
+
+ return 0;
+}
+
+uint64_t fsl_ddr_compute(struct fsl_ddr_info_s *pinfo)
+{
+ uint64_t total_mem = 0;
+ uint32_t ncs, ndimm, max_end = 0;
+ struct fsl_ddr_cfg_regs_s *ddr_reg;
+ struct common_timing_params_s *timing_params;
+ /* data bus width capacity adjust shift amount */
+ uint32_t dbw_capacity_adjust;
+
+ ddr_reg = &pinfo->fsl_ddr_config_reg;
+ timing_params = &pinfo->common_timing_params;
+ ncs = pinfo->board_info.cs_per_ctrl;
+ ndimm = pinfo->board_info.dimm_slots_per_ctrl;
+ dbw_capacity_adjust = 0;
+ pinfo->memctl_opts.board_info = &pinfo->board_info;
+
+ /* STEP 1: Gather all DIMM SPD data */
+ if (fsl_ddr_get_spd(pinfo->spd_installed_dimms,
+ pinfo->memctl_opts.board_info))
+ return 0;
+
+ /* STEP 2: Compute DIMM parameters from SPD data */
+ if (compute_dimm_param(pinfo, ndimm))
+ return 0;
+
+ /*
+ * STEP 3: Compute a common set of timing parameters
+ * suitable for all of the DIMMs on each memory controller
+ */
+ compute_lowest_common_dimm_parameters(pinfo->dimm_params,
+ timing_params, ndimm);
+
+ /* STEP 4: Gather configuration requirements from user */
+ populate_memctl_options(timing_params->all_DIMMs_registered,
+ &pinfo->memctl_opts,
+ pinfo->dimm_params);
+
+ /* STEP 5: Assign addresses to chip selects */
+ total_mem = step_assign_addresses(pinfo, dbw_capacity_adjust);
+
+ /* STEP 6: compute controller register values */
+ if (timing_params->ndimms_present == 0)
+ memset(ddr_reg, 0, sizeof(struct fsl_ddr_cfg_regs_s));
+
+ compute_fsl_memctl_config_regs(&pinfo->memctl_opts,
+ ddr_reg, timing_params,
+ pinfo->dimm_params,
+ dbw_capacity_adjust);
+
+ max_end = retrieve_max_size(ddr_reg, ncs);
+ total_mem = 1 + (((uint64_t)max_end << 24ULL) | 0xFFFFFFULL);
+
+ return total_mem;
+}
+
+/*
+ * fsl_ddr_sdram():
+ * This is the main function to initialize the memory.
+ *
+ * It returns amount of memory configured in bytes.
+ */
+phys_size_t fsl_ddr_sdram(void)
+{
+ uint64_t total_memory;
+ struct fsl_ddr_info_s info;
+
+ memset(&info, 0, sizeof(struct fsl_ddr_info_s));
+ /* Gather board information on the memory controller and I2C bus. */
+ fsl_ddr_board_info(&info.board_info);
+
+ total_memory = fsl_ddr_compute(&info);
+
+ if (info.common_timing_params.ndimms_present == 0)
+ return 0;
+
+ fsl_ddr_set_memctl_regs(&info);
+ fsl_ddr_set_lawbar(&info.common_timing_params, LAW_TRGT_IF_DDR_1);
+
+ return total_memory;
+}
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 8/8] ppc 85xx: early I2C support
2013-05-31 16:53 [PATCH 0/8] U-Boot DDR initialisation import Renaud Barbier
` (15 preceding siblings ...)
2013-06-25 10:45 ` [PATCH 7/8] ppc 8xxx: core DDR driver functions Renaud Barbier
@ 2013-06-25 10:45 ` Renaud Barbier
16 siblings, 0 replies; 32+ messages in thread
From: Renaud Barbier @ 2013-06-25 10:45 UTC (permalink / raw)
To: barebox
Early I2C support is introduced to read SPD data from memory
modules prior to the loading of the I2C driver.
Signed-off-by: Renaud Barbier <renaud.barbier@ge.com>
---
arch/ppc/mach-mpc85xx/fsl_i2c.c | 253 ++++++++++++++++++++++++++
arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h | 18 ++
2 files changed, 271 insertions(+), 0 deletions(-)
create mode 100644 arch/ppc/mach-mpc85xx/fsl_i2c.c
create mode 100644 arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
diff --git a/arch/ppc/mach-mpc85xx/fsl_i2c.c b/arch/ppc/mach-mpc85xx/fsl_i2c.c
new file mode 100644
index 0000000..51fcc64
--- /dev/null
+++ b/arch/ppc/mach-mpc85xx/fsl_i2c.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ * Copyright 2006,2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Early I2C support functions to read SPD data or board
+ * information.
+ * Based on U-Boot drivers/i2c/fsl_i2c.c
+ */
+#include <common.h>
+#include <i2c/i2c.h>
+#include <mach/clock.h>
+#include <mach/immap_85xx.h>
+#include <mach/early_udelay.h>
+
+/* FSL I2C registers */
+#define FSL_I2C_ADR 0x00
+#define FSL_I2C_FDR 0x04
+#define FSL_I2C_CR 0x08
+#define FSL_I2C_SR 0x0C
+#define FSL_I2C_DR 0x10
+#define FSL_I2C_DFSRR 0x14
+
+/* Bits of FSL I2C registers */
+#define I2C_SR_RXAK 0x01
+#define I2C_SR_MIF 0x02
+#define I2C_SR_MAL 0x10
+#define I2C_SR_MBB 0x20
+#define I2C_SR_MCF 0x80
+#define I2C_CR_RSTA 0x04
+#define I2C_CR_TXAK 0x08
+#define I2C_CR_MTX 0x10
+#define I2C_CR_MSTA 0x20
+#define I2C_CR_MEN 0x80
+
+/*
+ * Set the I2C bus speed for a given I2C device
+ *
+ * parameters:
+ * - i2c: the I2C base address.
+ * - i2c_clk: I2C bus clock frequency.
+ * - speed: the desired speed of the bus.
+ *
+ * The I2C device must be stopped before calling this function.
+ *
+ * The return value is the actual bus speed that is set.
+ */
+static uint32_t fsl_set_i2c_bus_speed(const void __iomem *i2c,
+ uint32_t i2c_clk, uint32_t speed)
+{
+ uint8_t dfsr, fdr = 0x31; /* Default if no FDR found */
+ uint16_t a, b, ga, gb, bin_gb, bin_ga, divider;
+ uint32_t c_div, est_div;
+
+ divider = min((uint16_t)(i2c_clk / speed), (uint16_t) -1);
+ /* Condition 1: dfsr <= 50/T */
+ dfsr = (5 * (i2c_clk / 1000)) / 100000;
+ if (!dfsr)
+ dfsr = 1;
+ est_div = ~0;
+
+ /*
+ * Bus speed is calculated as per Freescale AN2919.
+ * a, b and dfsr matches identifiers A,B and C as in the
+ * application note.
+ */
+ for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) {
+ for (gb = 0; gb < 8; gb++) {
+ b = 16 << gb;
+ c_div = b * (a + (((3 * dfsr) / b) * 2));
+
+ if ((c_div > divider) && (c_div < est_div)) {
+ est_div = c_div;
+ bin_gb = gb << 2;
+ bin_ga = (ga & 0x3) | ((ga & 0x4) << 3);
+ fdr = bin_gb | bin_ga;
+ speed = i2c_clk / est_div;
+ }
+ }
+ if (a == 20)
+ a += 2;
+ if (a == 24)
+ a += 4;
+ }
+ writeb(dfsr, i2c + FSL_I2C_DFSRR); /* set default filter */
+ writeb(fdr, i2c + FSL_I2C_FDR); /* set bus speed */
+
+ return speed;
+}
+
+void fsl_i2c_init(void __iomem *i2c, int speed, int slaveadd)
+{
+ uint32_t i2c_clk;
+
+ i2c_clk = fsl_get_i2c_freq();
+ writeb(0, i2c + FSL_I2C_CR);
+ early_udelay(5);
+
+ fsl_set_i2c_bus_speed(i2c, i2c_clk, speed);
+ writeb(slaveadd << 1, i2c + FSL_I2C_ADR);
+ writeb(0x0, i2c + FSL_I2C_SR);
+ writeb(I2C_CR_MEN, i2c + FSL_I2C_CR);
+}
+
+static uint32_t fsl_usec2ticks(uint32_t usec)
+{
+ ulong ticks;
+
+ if (usec < 1000) {
+ ticks = (usec * (fsl_get_timebase_clock() / 1000));
+ ticks = (ticks + 500) / 1000;
+ } else {
+ ticks = (usec / 10);
+ ticks *= (fsl_get_timebase_clock() / 100000);
+ }
+
+ return ticks;
+}
+
+static int fsl_i2c_wait4bus(void __iomem *i2c)
+{
+ uint64_t timeval = get_ticks();
+ const uint64_t timeout = fsl_usec2ticks(20000);
+
+ while (readb(i2c + FSL_I2C_SR) & I2C_SR_MBB)
+ if ((get_ticks() - timeval) > timeout)
+ return -1;
+
+ return 0;
+}
+
+void fsl_i2c_stop(void __iomem *i2c)
+{
+ writeb(I2C_CR_MEN, i2c + FSL_I2C_CR);
+}
+
+static int fsl_i2c_wait(void __iomem *i2c, int write)
+{
+ const uint64_t timeout = fsl_usec2ticks(100000);
+ uint64_t timeval = get_ticks();
+ int csr;
+
+ do {
+ csr = readb(i2c + FSL_I2C_SR);
+ if (csr & I2C_SR_MIF)
+ break;
+ } while ((get_ticks() - timeval) < timeout);
+
+ if ((get_ticks() - timeval) > timeout)
+ goto error;
+
+ csr = readb(i2c + FSL_I2C_SR);
+ writeb(0x0, i2c + FSL_I2C_SR);
+
+ if (csr & I2C_SR_MAL)
+ goto error;
+
+ if (!(csr & I2C_SR_MCF))
+ goto error;
+
+ if (write == 0 && (csr & I2C_SR_RXAK))
+ goto error;
+
+ return 0;
+error:
+ return -1;
+}
+
+static int __i2c_write(void __iomem *i2c, uint8_t *data, int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ writeb(data[i], i2c + FSL_I2C_DR);
+ if (fsl_i2c_wait(i2c, 0) < 0)
+ break;
+ }
+
+ return i;
+}
+
+static int __i2c_read(void __iomem *i2c, uint8_t *data, int length)
+{
+ int i;
+ uint8_t val = I2C_CR_MEN | I2C_CR_MSTA;
+
+ if (length == 1)
+ writeb(val | I2C_CR_TXAK, i2c + FSL_I2C_CR);
+ else
+ writeb(val, i2c + FSL_I2C_CR);
+
+ readb(i2c + FSL_I2C_DR);
+ for (i = 0; i < length; i++) {
+ if (fsl_i2c_wait(i2c, 1) < 0)
+ break;
+
+ /* Generate ack on last next to last byte */
+ if (i == length - 2)
+ writeb(val | I2C_CR_TXAK, i2c + FSL_I2C_CR);
+ /* Do not generate stop on last byte */
+ if (i == length - 1)
+ writeb(val | I2C_CR_MTX, i2c + FSL_I2C_CR);
+
+ data[i] = readb(i2c + FSL_I2C_DR);
+ }
+
+ return i;
+}
+
+static int
+fsl_i2c_write_addr(void __iomem *i2c, uint8_t dev, uint8_t dir, int rsta)
+{
+ uint8_t val = I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX;
+
+ if (rsta)
+ val |= I2C_CR_RSTA;
+ writeb(val, i2c + FSL_I2C_CR);
+ writeb((dev << 1) | dir, i2c + FSL_I2C_DR);
+
+ if (fsl_i2c_wait(i2c, 0) < 0)
+ return 0;
+
+ return 1;
+}
+
+int fsl_i2c_read(void __iomem *i2c, uint8_t dev, uint addr, int alen,
+ uint8_t *data, int length)
+{
+ int i = -1;
+ uint8_t *a = (uint8_t *)&addr;
+
+ if (alen && (fsl_i2c_wait4bus(i2c) >= 0) &&
+ (fsl_i2c_write_addr(i2c, dev, 0, 0) != 0) &&
+ (__i2c_write(i2c, &a[4 - alen], alen) == alen))
+ i = 0;
+
+ if (length && fsl_i2c_write_addr(i2c, dev, 1, 1) != 0)
+ i = __i2c_read(i2c, data, length);
+
+ if (i == length)
+ return 0;
+
+ return -1;
+}
diff --git a/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h b/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
new file mode 100644
index 0000000..acf4cc7
--- /dev/null
+++ b/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+void fsl_i2c_init(void __iomem *i2c, int speed, int slaveadd);
+int fsl_i2c_read(void __iomem *i2c, uint8_t dev, uint addr, int alen,
+ uint8_t *data, int length);
+void fsl_i2c_stop(void __iomem *i2c);
+
--
1.7.1
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 32+ messages in thread