/* * 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 #include #include #include #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; }