Commit 6f5e1dc5 authored by York Sun's avatar York Sun Committed by Kumar Gala
Browse files

powerpc/8xxx: Add support for interactive DDR programming interface



Interactive DDR debugging provides a user interface to view and modify SPD,
DIMM parameters, board options and DDR controller registers before DDR is
initialized. With this feature, developers can fine-tune DDR for board
bringup and other debugging without frequently having to reprogram the flash.

To enable this feature, define CONFIG_FSL_DDR_INTERACTIVE in board header
file and set an environment variable to activate it. Syntax:

setenv ddr_interactive on

After reset, U-boot prompts before initializing DDR controllers
FSL DDR>

The available commands are
print      print SPD and intermediate computed data
reset      reboot machine
recompute  reload SPD and options to default and recompute regs
edit       modify spd, parameter, or option
compute    recompute registers from current next_step to end
next_step  shows current next_step
help       this message
go         program the memory controller and continue with u-boot

The first command should be "compute", which reads data from DIMM SPDs and
board options, performs the calculation then stops before setting DDR
controller. A user can use "print" and "edit" commands to view and modify
anything. "Go" picks up from current step with any modification and
compltes the calculation then enables the DDR controller to continue u-boot.
"Recompute" does it over from fresh reading.
Signed-off-by: default avatarYork Sun <yorksun@freescale.com>
Signed-off-by: default avatarKumar Gala <galak@kernel.crashing.org>
parent 0841ca90
......@@ -3099,6 +3099,9 @@ Low Level (hardware related) configuration options:
parameters are extracted from datasheet and hard-coded into
header files or board specific files.
- CONFIG_FSL_DDR_INTERACTIVE
Enable interactive DDR debugging. See doc/README.fsl-ddr.
- CONFIG_SYS_83XX_DDR_USES_CS0
Only for 83xx systems. If specified, then DDR should
be configured using CS0 and CS1 instead of CS2 and CS3.
......
#
# Copyright 2008 Freescale Semiconductor, 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
......@@ -30,6 +30,7 @@ COBJS-$(CONFIG_FSL_DDR2) += ddr2_dimm_params.o
COBJS-$(CONFIG_FSL_DDR3) += ddr3_dimm_params.o
endif
COBJS-$(CONFIG_FSL_DDR_INTERACTIVE) += interactive.o
SRCS := $(START:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y))
......
......@@ -56,32 +56,46 @@ typedef struct {
#define STEP_PROGRAM_REGS (1 << 6)
#define STEP_ALL 0xFFF
extern unsigned long long
unsigned long long
fsl_ddr_compute(fsl_ddr_info_t *pinfo, unsigned int start_step,
unsigned int size_only);
extern const char * step_to_string(unsigned int step);
const char *step_to_string(unsigned int step);
extern unsigned int
compute_fsl_memctl_config_regs(const memctl_options_t *popts,
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);
extern unsigned int
compute_lowest_common_dimm_parameters(const dimm_params_t *dimm_params,
common_timing_params_t *outpdimm,
unsigned int number_of_dimms);
extern unsigned int populate_memctl_options(int all_DIMMs_registered,
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);
extern void check_interleaving_options(fsl_ddr_info_t *pinfo);
void check_interleaving_options(fsl_ddr_info_t *pinfo);
extern unsigned int mclk_to_picos(unsigned int mclk);
extern unsigned int get_memory_clk_period_ps(void);
extern unsigned int picos_to_mclk(unsigned int picos);
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);
unsigned long long fsl_ddr_interactive(fsl_ddr_info_t *pinfo);
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,
......
/*
* Copyright 2010-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 or any later versionas 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]
* York Sun [at freescale.com]
*/
#include <common.h>
#include <linux/ctype.h>
#include <asm/types.h>
#include <asm/fsl_ddr_sdram.h>
#include "ddr.h"
/* Option parameter Structures */
struct options_string {
const char *option_name;
size_t offset;
unsigned int size;
const char printhex;
};
static unsigned int picos_to_mhz(unsigned int picos)
{
return 1000000 / picos;
}
static void print_option_table(const struct options_string *table,
int table_size,
const void *base)
{
unsigned int i;
unsigned int *ptr;
unsigned long long *ptr_l;
for (i = 0; i < table_size; i++) {
switch (table[i].size) {
case 4:
ptr = (unsigned int *) (base + table[i].offset);
if (table[i].printhex) {
printf("%s = 0x%08X\n",
table[i].option_name, *ptr);
} else {
printf("%s = %u\n",
table[i].option_name, *ptr);
}
break;
case 8:
ptr_l = (unsigned long long *) (base + table[i].offset);
printf("%s = %llu\n",
table[i].option_name, *ptr_l);
break;
default:
printf("Unrecognized size!\n");
break;
}
}
}
static int handle_option_table(const struct options_string *table,
int table_size,
void *base,
const char *opt,
const char *val)
{
unsigned int i;
unsigned int value, *ptr;
unsigned long long value_l, *ptr_l;
for (i = 0; i < table_size; i++) {
if (strcmp(table[i].option_name, opt) != 0)
continue;
switch (table[i].size) {
case 4:
value = simple_strtoul(val, NULL, 0);
ptr = base + table[i].offset;
*ptr = value;
break;
case 8:
value_l = simple_strtoull(val, NULL, 0);
ptr_l = base + table[i].offset;
*ptr_l = value_l;
break;
default:
printf("Unrecognized size!\n");
break;
}
return 1;
}
return 0;
}
static void fsl_ddr_generic_edit(void *pdata,
void *pend,
unsigned int element_size,
unsigned int element_num,
unsigned int value)
{
char *pcdata = (char *)pdata; /* BIG ENDIAN ONLY */
pcdata += element_num * element_size;
if ((pcdata + element_size) > (char *) pend) {
printf("trying to write past end of data\n");
return;
}
switch (element_size) {
case 1:
__raw_writeb(value, pcdata);
break;
case 2:
__raw_writew(value, pcdata);
break;
case 4:
__raw_writel(value, pcdata);
break;
default:
printf("unexpected element size %u\n", element_size);
break;
}
}
static void fsl_ddr_spd_edit(fsl_ddr_info_t *pinfo,
unsigned int ctrl_num,
unsigned int dimm_num,
unsigned int element_num,
unsigned int value)
{
generic_spd_eeprom_t *pspd;
pspd = &(pinfo->spd_installed_dimms[ctrl_num][dimm_num]);
fsl_ddr_generic_edit(pspd, pspd + 1, 1, element_num, value);
}
#define COMMON_TIMING(x) {#x, offsetof(common_timing_params_t, x), \
sizeof((common_timing_params_t *)0)->x, 0}
static void lowest_common_dimm_parameters_edit(fsl_ddr_info_t *pinfo,
unsigned int ctrl_num,
const char *optname_str,
const char *value_str)
{
common_timing_params_t *p = &pinfo->common_timing_params[ctrl_num];
static const struct options_string options[] = {
COMMON_TIMING(tCKmin_X_ps),
COMMON_TIMING(tCKmax_ps),
COMMON_TIMING(tCKmax_max_ps),
COMMON_TIMING(tRCD_ps),
COMMON_TIMING(tRP_ps),
COMMON_TIMING(tRAS_ps),
COMMON_TIMING(tWR_ps),
COMMON_TIMING(tWTR_ps),
COMMON_TIMING(tRFC_ps),
COMMON_TIMING(tRRD_ps),
COMMON_TIMING(tRC_ps),
COMMON_TIMING(refresh_rate_ps),
COMMON_TIMING(tIS_ps),
COMMON_TIMING(tIH_ps),
COMMON_TIMING(tDS_ps),
COMMON_TIMING(tDH_ps),
COMMON_TIMING(tRTP_ps),
COMMON_TIMING(tDQSQ_max_ps),
COMMON_TIMING(tQHS_ps),
COMMON_TIMING(ndimms_present),
COMMON_TIMING(lowest_common_SPD_caslat),
COMMON_TIMING(highest_common_derated_caslat),
COMMON_TIMING(additive_latency),
COMMON_TIMING(all_DIMMs_burst_lengths_bitmask),
COMMON_TIMING(all_DIMMs_registered),
COMMON_TIMING(all_DIMMs_unbuffered),
COMMON_TIMING(all_DIMMs_ECC_capable),
COMMON_TIMING(total_mem),
COMMON_TIMING(base_address),
};
static const unsigned int n_opts = ARRAY_SIZE(options);
if (handle_option_table(options, n_opts, p, optname_str, value_str))
return;
printf("Error: couldn't find option string %s\n", optname_str);
}
#define DIMM_PARM(x) {#x, offsetof(dimm_params_t, x), \
sizeof((dimm_params_t *)0)->x, 0}
static void fsl_ddr_dimm_parameters_edit(fsl_ddr_info_t *pinfo,
unsigned int ctrl_num,
unsigned int dimm_num,
const char *optname_str,
const char *value_str)
{
dimm_params_t *p = &(pinfo->dimm_params[ctrl_num][dimm_num]);
static const struct options_string options[] = {
DIMM_PARM(n_ranks),
DIMM_PARM(data_width),
DIMM_PARM(primary_sdram_width),
DIMM_PARM(ec_sdram_width),
DIMM_PARM(registered_dimm),
DIMM_PARM(n_row_addr),
DIMM_PARM(n_col_addr),
DIMM_PARM(edc_config),
DIMM_PARM(n_banks_per_sdram_device),
DIMM_PARM(burst_lengths_bitmask),
DIMM_PARM(row_density),
DIMM_PARM(tCKmin_X_ps),
DIMM_PARM(tCKmin_X_minus_1_ps),
DIMM_PARM(tCKmin_X_minus_2_ps),
DIMM_PARM(tCKmax_ps),
DIMM_PARM(caslat_X),
DIMM_PARM(caslat_X_minus_1),
DIMM_PARM(caslat_X_minus_2),
DIMM_PARM(caslat_lowest_derated),
DIMM_PARM(tRCD_ps),
DIMM_PARM(tRP_ps),
DIMM_PARM(tRAS_ps),
DIMM_PARM(tWR_ps),
DIMM_PARM(tWTR_ps),
DIMM_PARM(tRFC_ps),
DIMM_PARM(tRRD_ps),
DIMM_PARM(tRC_ps),
DIMM_PARM(refresh_rate_ps),
DIMM_PARM(tIS_ps),
DIMM_PARM(tIH_ps),
DIMM_PARM(tDS_ps),
DIMM_PARM(tDH_ps),
DIMM_PARM(tRTP_ps),
DIMM_PARM(tDQSQ_max_ps),
DIMM_PARM(tQHS_ps),
DIMM_PARM(rank_density),
DIMM_PARM(capacity),
DIMM_PARM(base_address),
};
static const unsigned int n_opts = ARRAY_SIZE(options);
if (handle_option_table(options, n_opts, p, optname_str, value_str))
return;
printf("couldn't find option string %s\n", optname_str);
}
static void print_dimm_parameters(const dimm_params_t *pdimm)
{
static const struct options_string options[] = {
DIMM_PARM(n_ranks),
DIMM_PARM(data_width),
DIMM_PARM(primary_sdram_width),
DIMM_PARM(ec_sdram_width),
DIMM_PARM(registered_dimm),
DIMM_PARM(n_row_addr),
DIMM_PARM(n_col_addr),
DIMM_PARM(edc_config),
DIMM_PARM(n_banks_per_sdram_device),
DIMM_PARM(tCKmin_X_ps),
DIMM_PARM(tCKmin_X_minus_1_ps),
DIMM_PARM(tCKmin_X_minus_2_ps),
DIMM_PARM(tCKmax_ps),
DIMM_PARM(caslat_X),
DIMM_PARM(tAA_ps),
DIMM_PARM(caslat_X_minus_1),
DIMM_PARM(caslat_X_minus_2),
DIMM_PARM(caslat_lowest_derated),
DIMM_PARM(tRCD_ps),
DIMM_PARM(tRP_ps),
DIMM_PARM(tRAS_ps),
DIMM_PARM(tWR_ps),
DIMM_PARM(tWTR_ps),
DIMM_PARM(tRFC_ps),
DIMM_PARM(tRRD_ps),
DIMM_PARM(tRC_ps),
DIMM_PARM(refresh_rate_ps),
DIMM_PARM(tIS_ps),
DIMM_PARM(tIH_ps),
DIMM_PARM(tDS_ps),
DIMM_PARM(tDH_ps),
DIMM_PARM(tRTP_ps),
DIMM_PARM(tDQSQ_max_ps),
DIMM_PARM(tQHS_ps),
};
static const unsigned int n_opts = ARRAY_SIZE(options);
if (pdimm->n_ranks == 0) {
printf("DIMM not present\n");
return;
}
printf("DIMM organization parameters:\n");
printf("module part name = %s\n", pdimm->mpart);
printf("rank_density = %llu bytes (%llu megabytes)\n",
pdimm->rank_density, pdimm->rank_density / 0x100000);
printf("capacity = %llu bytes (%llu megabytes)\n",
pdimm->capacity, pdimm->capacity / 0x100000);
printf("burst_lengths_bitmask = %02X\n",
pdimm->burst_lengths_bitmask);
printf("base_addresss = %llu (%08llX %08llX)\n",
pdimm->base_address,
(pdimm->base_address >> 32),
pdimm->base_address & 0xFFFFFFFF);
print_option_table(options, n_opts, pdimm);
}
static void print_lowest_common_dimm_parameters(
const common_timing_params_t *plcd_dimm_params)
{
static const struct options_string options[] = {
COMMON_TIMING(tCKmax_max_ps),
COMMON_TIMING(tRCD_ps),
COMMON_TIMING(tRP_ps),
COMMON_TIMING(tRAS_ps),
COMMON_TIMING(tWR_ps),
COMMON_TIMING(tWTR_ps),
COMMON_TIMING(tRFC_ps),
COMMON_TIMING(tRRD_ps),
COMMON_TIMING(tRC_ps),
COMMON_TIMING(refresh_rate_ps),
COMMON_TIMING(tIS_ps),
COMMON_TIMING(tDS_ps),
COMMON_TIMING(tDH_ps),
COMMON_TIMING(tRTP_ps),
COMMON_TIMING(tDQSQ_max_ps),
COMMON_TIMING(tQHS_ps),
COMMON_TIMING(lowest_common_SPD_caslat),
COMMON_TIMING(highest_common_derated_caslat),
COMMON_TIMING(additive_latency),
COMMON_TIMING(ndimms_present),
COMMON_TIMING(all_DIMMs_registered),
COMMON_TIMING(all_DIMMs_unbuffered),
COMMON_TIMING(all_DIMMs_ECC_capable),
};
static const unsigned int n_opts = ARRAY_SIZE(options);
/* Clock frequencies */
printf("tCKmin_X_ps = %u (%u MHz)\n",
plcd_dimm_params->tCKmin_X_ps,
picos_to_mhz(plcd_dimm_params->tCKmin_X_ps));
printf("tCKmax_ps = %u (%u MHz)\n",
plcd_dimm_params->tCKmax_ps,
picos_to_mhz(plcd_dimm_params->tCKmax_ps));
printf("all_DIMMs_burst_lengths_bitmask = %02X\n",
plcd_dimm_params->all_DIMMs_burst_lengths_bitmask);
print_option_table(options, n_opts, plcd_dimm_params);
printf("total_mem = %llu (%llu megabytes)\n",
plcd_dimm_params->total_mem,
plcd_dimm_params->total_mem / 0x100000);
printf("base_address = %llu (%llu megabytes)\n",
plcd_dimm_params->base_address,
plcd_dimm_params->base_address / 0x100000);
}
#define CTRL_OPTIONS(x) {#x, offsetof(memctl_options_t, x), \
sizeof((memctl_options_t *)0)->x, 0}
#define CTRL_OPTIONS_CS(x, y) {"cs" #x "_" #y, \
offsetof(memctl_options_t, cs_local_opts[x].y), \
sizeof((memctl_options_t *)0)->cs_local_opts[x].y, 0}
static void fsl_ddr_options_edit(fsl_ddr_info_t *pinfo,
unsigned int ctl_num,
const char *optname_str,
const char *value_str)
{
memctl_options_t *p = &(pinfo->memctl_opts[ctl_num]);
/*
* This array all on the stack and *computed* each time this
* function is rung.
*/
static const struct options_string options[] = {
CTRL_OPTIONS_CS(0, odt_rd_cfg),
CTRL_OPTIONS_CS(0, odt_wr_cfg),
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 1)
CTRL_OPTIONS_CS(1, odt_rd_cfg),
CTRL_OPTIONS_CS(1, odt_wr_cfg),
#endif
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2)
CTRL_OPTIONS_CS(2, odt_rd_cfg),
CTRL_OPTIONS_CS(2, odt_wr_cfg),
#endif
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2)
CTRL_OPTIONS_CS(3, odt_rd_cfg),
CTRL_OPTIONS_CS(3, odt_wr_cfg),
#endif
#if defined(CONFIG_FSL_DDR3)
CTRL_OPTIONS_CS(0, odt_rtt_norm),
CTRL_OPTIONS_CS(0, odt_rtt_wr),
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 1)
CTRL_OPTIONS_CS(1, odt_rtt_norm),
CTRL_OPTIONS_CS(1, odt_rtt_wr),
#endif
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2)
CTRL_OPTIONS_CS(2, odt_rtt_norm),
CTRL_OPTIONS_CS(2, odt_rtt_wr),
#endif
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2)
CTRL_OPTIONS_CS(3, odt_rtt_norm),
CTRL_OPTIONS_CS(3, odt_rtt_wr),
#endif
#endif
CTRL_OPTIONS(memctl_interleaving),
CTRL_OPTIONS(memctl_interleaving_mode),
CTRL_OPTIONS(ba_intlv_ctl),
CTRL_OPTIONS(ECC_mode),
CTRL_OPTIONS(ECC_init_using_memctl),
CTRL_OPTIONS(DQS_config),
CTRL_OPTIONS(self_refresh_in_sleep),
CTRL_OPTIONS(dynamic_power),
CTRL_OPTIONS(data_bus_width),
CTRL_OPTIONS(burst_length),
CTRL_OPTIONS(cas_latency_override),
CTRL_OPTIONS(cas_latency_override_value),
CTRL_OPTIONS(use_derated_caslat),
CTRL_OPTIONS(additive_latency_override),
CTRL_OPTIONS(additive_latency_override_value),
CTRL_OPTIONS(clk_adjust),
CTRL_OPTIONS(cpo_override),
CTRL_OPTIONS(write_data_delay),
CTRL_OPTIONS(half_strength_driver_enable),
/*
* These can probably be changed to 2T_EN and 3T_EN
* (using a leading numerical character) without problem
*/
CTRL_OPTIONS(twoT_en),
CTRL_OPTIONS(threeT_en),
CTRL_OPTIONS(ap_en),
CTRL_OPTIONS(bstopre),
CTRL_OPTIONS(wrlvl_override),
CTRL_OPTIONS(wrlvl_sample),
CTRL_OPTIONS(wrlvl_start),
CTRL_OPTIONS(rcw_override),
CTRL_OPTIONS(rcw_1),
CTRL_OPTIONS(rcw_2),
CTRL_OPTIONS(tCKE_clock_pulse_width_ps),
CTRL_OPTIONS(tFAW_window_four_activates_ps),
CTRL_OPTIONS(trwt_override),
CTRL_OPTIONS(trwt),
};
static const unsigned int n_opts = ARRAY_SIZE(options);
if (handle_option_table(options, n_opts, p,
optname_str, value_str))
return;
printf("couldn't find option string %s\n", optname_str);
}
#define CFG_REGS(x) {#x, offsetof(fsl_ddr_cfg_regs_t, x), \
sizeof((fsl_ddr_cfg_regs_t *)0)->x, 1}
#define CFG_REGS_CS(x, y) {"cs" #x "_" #y, \
offsetof(fsl_ddr_cfg_regs_t, cs[x].y), \
sizeof((fsl_ddr_cfg_regs_t *)0)->cs[x].y, 1}
static void print_fsl_memctl_config_regs(const fsl_ddr_cfg_regs_t *ddr)
{
unsigned int i;
static const struct options_string options[] = {
CFG_REGS_CS(0, bnds),
CFG_REGS_CS(0, config),
CFG_REGS_CS(0, config_2),
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 1)
CFG_REGS_CS(1, bnds),
CFG_REGS_CS(1, config),
CFG_REGS_CS(1, config_2),
#endif
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2)
CFG_REGS_CS(2, bnds),
CFG_REGS_CS(2, config),
CFG_REGS_CS(2, config_2),
#endif
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 2)
CFG_REGS_CS(3, bnds),
CFG_REGS_CS(3, config),
CFG_REGS_CS(3, config_2),
#endif
CFG_REGS(timing_cfg_3),
CFG_REGS(timing_cfg_0),
CFG_REGS(timing_cfg_1),
CFG_REGS(timing_cfg_2),
CFG_REGS(ddr_sdram_cfg),
CFG_REGS(ddr_sdram_cfg_2),
CFG_REGS(ddr_sdram_mode),
CFG_REGS(ddr_sdram_mode_2),
CFG_REGS(ddr_sdram_mode_3),
CFG_REGS(ddr_sdram_mode_4),
CFG_REGS(ddr_sdram_mode_5),
CFG_REGS(ddr_sdram_mode_6),
CFG_REGS(ddr_sdram_mode_7),
CFG_REGS(ddr_sdram_mode_8),
CFG_REGS(ddr_sdram_interval),
CFG_REGS(ddr_data_init),
CFG_REGS(ddr_sdram_clk_cntl),
CFG_REGS(ddr_init_addr),
CFG_REGS(ddr_init_ext_addr),
CFG_REGS(timing_cfg_4),
CFG_REGS(timing_cfg_5),
CFG_REGS(ddr_zq_cntl),
CFG_REGS(ddr_wrlvl_cntl),
CFG_REGS(ddr_sr_cntr),
CFG_REGS(ddr_sdram_rcw_1),