247 lines
6.0 KiB
C
247 lines
6.0 KiB
C
/*
|
|
* Copyright 2008-2012 Freescale Semiconductor, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* Version 2 as published by the Free Software Foundation.
|
|
*/
|
|
|
|
/*
|
|
* Generic driver for Freescale DDR2 memory controller.
|
|
* Based on code from spd_sdram.c
|
|
* Author: James Yang [at freescale.com]
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <config.h>
|
|
#include <asm/fsl_law.h>
|
|
#include <asm/fsl_ddr_sdram.h>
|
|
#include "ddr.h"
|
|
|
|
static int get_spd(generic_spd_eeprom_t *spd,
|
|
struct ddr_board_info_s *bi, u8 i2c_address)
|
|
{
|
|
int ret;
|
|
|
|
fsl_i2c_init(bi->i2c_base, bi->i2c_speed, bi->i2c_slave);
|
|
ret = fsl_i2c_read(bi->i2c_base, i2c_address, 0, 1, (uchar *) spd,
|
|
sizeof(generic_spd_eeprom_t));
|
|
fsl_i2c_stop(bi->i2c_base);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int fsl_ddr_get_spd(generic_spd_eeprom_t *ctrl_dimms_spd,
|
|
struct ddr_board_info_s *binfo)
|
|
{
|
|
uint32_t i;
|
|
uint8_t i2c_address;
|
|
|
|
for (i = 0; i < binfo->dimm_slots_per_ctrl; i++) {
|
|
if (binfo->spd_i2c_addr == NULL)
|
|
goto error;
|
|
i2c_address = binfo->spd_i2c_addr[i];
|
|
if (get_spd(&(ctrl_dimms_spd[i]), binfo, i2c_address))
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
error:
|
|
return 1;
|
|
}
|
|
|
|
static uint64_t step_assign_addresses(struct fsl_ddr_info_s *pinfo,
|
|
uint32_t dbw_cap_adj)
|
|
{
|
|
uint64_t total_mem, current_mem_base, total_ctlr_mem, cap;
|
|
uint32_t ndimm, dw, j, found = 0;
|
|
|
|
ndimm = pinfo->board_info.dimm_slots_per_ctrl;
|
|
/*
|
|
* If a reduced data width is requested, but the SPD
|
|
* specifies a physically wider device, adjust the
|
|
* computed dimm capacities accordingly before
|
|
* assigning addresses.
|
|
*/
|
|
switch (pinfo->memctl_opts.data_bus_width) {
|
|
case 2:
|
|
/* 16-bit */
|
|
for (j = 0; j < ndimm; j++) {
|
|
if (!pinfo->dimm_params[j].n_ranks)
|
|
continue;
|
|
dw = pinfo->dimm_params[j].primary_sdram_width;
|
|
if ((dw == 72 || dw == 64)) {
|
|
dbw_cap_adj = 2;
|
|
break;
|
|
} else if ((dw == 40 || dw == 32)) {
|
|
dbw_cap_adj = 1;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
/* 32-bit */
|
|
for (j = 0; j < ndimm; j++) {
|
|
dw = pinfo->dimm_params[j].data_width;
|
|
if (pinfo->dimm_params[j].n_ranks
|
|
&& (dw == 72 || dw == 64)) {
|
|
/*
|
|
* FIXME: can't really do it
|
|
* like this because this just
|
|
* further reduces the memory
|
|
*/
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (found)
|
|
dbw_cap_adj = 1;
|
|
break;
|
|
case 0:
|
|
/* 64-bit */
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
current_mem_base = 0ull;
|
|
total_mem = 0;
|
|
total_ctlr_mem = 0;
|
|
pinfo->common_timing_params.base_address = current_mem_base;
|
|
|
|
for (j = 0; j < ndimm; j++) {
|
|
cap = pinfo->dimm_params[j].capacity >> dbw_cap_adj;
|
|
pinfo->dimm_params[j].base_address = current_mem_base;
|
|
current_mem_base += cap;
|
|
total_ctlr_mem += cap;
|
|
|
|
}
|
|
|
|
pinfo->common_timing_params.total_mem = total_ctlr_mem;
|
|
total_mem += total_ctlr_mem;
|
|
|
|
return total_mem;
|
|
}
|
|
|
|
static uint32_t retrieve_max_size(struct fsl_ddr_cfg_regs_s *ddr_reg,
|
|
uint32_t ncs)
|
|
{
|
|
uint32_t max_end = 0, end, i;
|
|
|
|
for (i = 0; i < ncs; i++)
|
|
if (ddr_reg->cs[i].config & 0x80000000) {
|
|
end = ddr_reg->cs[i].bnds & 0xFFF;
|
|
if (end > max_end)
|
|
max_end = end;
|
|
}
|
|
|
|
return max_end;
|
|
}
|
|
|
|
static uint32_t compute_dimm_param(struct fsl_ddr_info_s *pinfo, uint32_t ndimm)
|
|
{
|
|
struct dimm_params_s *pdimm;
|
|
generic_spd_eeprom_t *spd;
|
|
uint32_t i, retval;
|
|
|
|
spd = &(pinfo->spd_installed_dimms[0]);
|
|
if (spd->mem_type == SPD_MEMTYPE_DDR3)
|
|
pinfo->memctl_opts.sdram_type = SDRAM_TYPE_DDR3;
|
|
else if (spd->mem_type == SPD_MEMTYPE_DDR2)
|
|
pinfo->memctl_opts.sdram_type = SDRAM_TYPE_DDR2;
|
|
else
|
|
return 1;
|
|
|
|
for (i = 0; i < ndimm; i++) {
|
|
spd = &(pinfo->spd_installed_dimms[i]);
|
|
pdimm = &(pinfo->dimm_params[i]);
|
|
|
|
retval = compute_dimm_parameters(spd, pdimm);
|
|
|
|
if (retval == 2) /* Fatal error */
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t fsl_ddr_compute(struct fsl_ddr_info_s *pinfo)
|
|
{
|
|
uint64_t total_mem = 0;
|
|
uint32_t ncs, ndimm, max_end = 0;
|
|
struct fsl_ddr_cfg_regs_s *ddr_reg;
|
|
struct common_timing_params_s *timing_params;
|
|
/* data bus width capacity adjust shift amount */
|
|
uint32_t dbw_capacity_adjust;
|
|
|
|
ddr_reg = &pinfo->fsl_ddr_config_reg;
|
|
timing_params = &pinfo->common_timing_params;
|
|
ncs = pinfo->board_info.cs_per_ctrl;
|
|
ndimm = pinfo->board_info.dimm_slots_per_ctrl;
|
|
dbw_capacity_adjust = 0;
|
|
pinfo->memctl_opts.board_info = &pinfo->board_info;
|
|
|
|
/* STEP 1: Gather all DIMM SPD data */
|
|
if (fsl_ddr_get_spd(pinfo->spd_installed_dimms,
|
|
pinfo->memctl_opts.board_info))
|
|
return 0;
|
|
|
|
/* STEP 2: Compute DIMM parameters from SPD data */
|
|
if (compute_dimm_param(pinfo, ndimm))
|
|
return 0;
|
|
|
|
/*
|
|
* STEP 3: Compute a common set of timing parameters
|
|
* suitable for all of the DIMMs on each memory controller
|
|
*/
|
|
compute_lowest_common_dimm_parameters(pinfo, timing_params, ndimm);
|
|
|
|
/* STEP 4: Gather configuration requirements from user */
|
|
populate_memctl_options(timing_params->all_DIMMs_registered,
|
|
&pinfo->memctl_opts,
|
|
pinfo->dimm_params);
|
|
|
|
/* STEP 5: Assign addresses to chip selects */
|
|
total_mem = step_assign_addresses(pinfo, dbw_capacity_adjust);
|
|
|
|
/* STEP 6: compute controller register values */
|
|
if (timing_params->ndimms_present == 0)
|
|
memset(ddr_reg, 0, sizeof(struct fsl_ddr_cfg_regs_s));
|
|
|
|
compute_fsl_memctl_config_regs(&pinfo->memctl_opts,
|
|
ddr_reg, timing_params,
|
|
pinfo->dimm_params,
|
|
dbw_capacity_adjust);
|
|
|
|
max_end = retrieve_max_size(ddr_reg, ncs);
|
|
total_mem = 1 + (((uint64_t)max_end << 24ULL) | 0xFFFFFFULL);
|
|
|
|
return total_mem;
|
|
}
|
|
|
|
/*
|
|
* fsl_ddr_sdram():
|
|
* This is the main function to initialize the memory.
|
|
*
|
|
* It returns amount of memory configured in bytes.
|
|
*/
|
|
phys_size_t fsl_ddr_sdram(void)
|
|
{
|
|
uint64_t total_memory;
|
|
struct fsl_ddr_info_s info;
|
|
|
|
memset(&info, 0, sizeof(struct fsl_ddr_info_s));
|
|
/* Gather board information on the memory controller and I2C bus. */
|
|
fsl_ddr_board_info(&info.board_info);
|
|
|
|
total_memory = fsl_ddr_compute(&info);
|
|
|
|
if (info.common_timing_params.ndimms_present == 0)
|
|
return 0;
|
|
|
|
fsl_ddr_set_memctl_regs(&info);
|
|
fsl_ddr_set_lawbar(&info.common_timing_params, LAW_TRGT_IF_DDR_1);
|
|
|
|
return total_memory;
|
|
}
|