9
0
Fork 0
barebox/arch/ppc/ddr-8xxx/main.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;
}