diff --git a/arch/ppc/boards/freescale-p2020rdb/p2020rdb.c b/arch/ppc/boards/freescale-p2020rdb/p2020rdb.c index edb9bcd..6426bd3 100644 --- a/arch/ppc/boards/freescale-p2020rdb/p2020rdb.c +++ b/arch/ppc/boards/freescale-p2020rdb/p2020rdb.c @@ -59,12 +59,19 @@ #define SYSCLK_50 50000000 #define SYSCLK_100 100000000 -/* Ethernet. Use eTSEC3 */ +/* Define attributes for eTSEC2 and eTSEC3 */ static struct gfar_info_struct gfar_info[] = { { + .phyaddr = 0, + .tbiana = 0x1a0, + .tbicr = 0x9140, + .mdiobus_tbi = 1, + }, + { .phyaddr = 1, .tbiana = 0, .tbicr = 0, + .mdiobus_tbi = 2, }, }; @@ -82,8 +89,8 @@ add_generic_device("i2c-fsl", 1, NULL, I2C2_BASE_ADDR, 0x100, IORESOURCE_MEM, &i2cplat); - /* eTSEC3 */ - fsl_eth_init(3, &gfar_info[0]); + fsl_eth_init(2, &gfar_info[0]); + fsl_eth_init(3, &gfar_info[1]); devfs_add_partition("nor0", 0xf80000, 0x80000, DEVFS_PARTITION_FIXED, "self0"); diff --git a/arch/ppc/configs/p2020rdb_defconfig b/arch/ppc/configs/p2020rdb_defconfig index 7690327..0f77903 100644 --- a/arch/ppc/configs/p2020rdb_defconfig +++ b/arch/ppc/configs/p2020rdb_defconfig @@ -24,9 +24,11 @@ CONFIG_DRIVER_NET_GIANFAR=y CONFIG_NET=y CONFIG_NET_PING=y +CONFIG_FS_TFTP=y CONFIG_NET_TFTP=y +CONFIG_CMD_TFTP=y CONFIG_PING=y -CONFIG_TFTP=y CONFIG_I2C=y CONFIG_I2C_IMX=y CONFIG_CMD_I2C=y +CONFIG_CMD_MIITOOL=y 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/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 +#include +#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); +} 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 +#include +#include +#include +#include "common_timing_params.h" + +#ifdef CONFIG_MPC85xx +#include +#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 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 +#include +#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/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 +#include +#include +#include +#include +#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/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 +#include +#include + +#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; +} 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 +#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; + + 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; +} 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 +#include +#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 +#include +#include +#include +#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; +} 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 + +/* 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 diff --git a/arch/ppc/mach-mpc85xx/eth-devices.c b/arch/ppc/mach-mpc85xx/eth-devices.c index c6e8f36..611a578 100644 --- a/arch/ppc/mach-mpc85xx/eth-devices.c +++ b/arch/ppc/mach-mpc85xx/eth-devices.c @@ -22,28 +22,40 @@ #include #include +#include #include #include +static int fsl_phy_init(void) +{ + int i; + void __iomem *base = IOMEM(GFAR_BASE_ADDR + GFAR_TBIPA_OFFSET); + + /* + * The TBI address must be initialised to enable the PHY to + * link up after the MDIO reset. + */ + out_be32(base, GFAR_TBIPA_END); + /* All ports access external PHYs via the "gfar-mdio" device */ + add_generic_device("gfar-mdio", 0, NULL, MDIO_BASE_ADDR, + 0x1000, IORESOURCE_MEM, NULL); + + for (i = 1; i < 3; i++) { + out_be32(base + (i * 0x1000), GFAR_TBIPA_END - i); + /* Use "gfar-tbiphy" devices to access internal PHY. */ + add_generic_device("gfar-tbiphy", i, NULL, + MDIO_BASE_ADDR + (i * 0x1000), + 0x1000, IORESOURCE_MEM, NULL); + } + return 0; +} + +coredevice_initcall(fsl_phy_init); + int fsl_eth_init(int num, struct gfar_info_struct *gf) { - struct resource *res; - - res = xzalloc(3 * sizeof(struct resource)); - /* TSEC interface registers */ - res[0].start = GFAR_BASE_ADDR + ((num - 1) * 0x1000); - res[0].end = res[0].start + 0x1000 - 1; - res[0].flags = IORESOURCE_MEM; - /* External PHY access always through eTSEC1 */ - res[1].start = MDIO_BASE_ADDR; - res[1].end = res[1].start + 0x1000 - 1; - res[1].flags = IORESOURCE_MEM; - /* Access to TBI/RTBI interface. */ - res[2].start = MDIO_BASE_ADDR + ((num - 1) * 0x1000); - res[2].end = res[2].start + 0x1000 - 1; - res[2].flags = IORESOURCE_MEM; - - add_generic_device_res("gfar", DEVICE_ID_DYNAMIC, res, 3, gf); - + add_generic_device("gfar", DEVICE_ID_DYNAMIC, NULL, + GFAR_BASE_ADDR + ((num - 1) * 0x1000), 0x1000, + IORESOURCE_MEM, gf); return 0; } 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 +#include +#include +#include +#include + +/* 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..d187c6c --- /dev/null +++ b/arch/ppc/mach-mpc85xx/include/mach/fsl_i2c.h @@ -0,0 +1,17 @@ +/* + * 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); diff --git a/arch/ppc/mach-mpc85xx/include/mach/gianfar.h b/arch/ppc/mach-mpc85xx/include/mach/gianfar.h index ae31638..6a7b9e9 100644 --- a/arch/ppc/mach-mpc85xx/include/mach/gianfar.h +++ b/arch/ppc/mach-mpc85xx/include/mach/gianfar.h @@ -22,10 +22,14 @@ * Platform data for the Motorola Triple Speed Ethernet Controller */ +#define GFAR_TBIPA_OFFSET 0x030 /* TBI PHY address */ +#define GFAR_TBIPA_END 0x1f /* Last valid PHY address */ + struct gfar_info_struct { unsigned int phyaddr; unsigned int tbiana; unsigned int tbicr; + unsigned int mdiobus_tbi; }; int fsl_eth_init(int num, struct gfar_info_struct *gf); diff --git a/common/Makefile b/common/Makefile index 4f430b6..64eacc3 100644 --- a/common/Makefile +++ b/common/Makefile @@ -13,6 +13,7 @@ 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 +#include + +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/drivers/net/gianfar.c b/drivers/net/gianfar.c index 96055bd..f944c6c 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -184,10 +184,11 @@ { int ix; struct gfar_private *priv = edev->priv; + struct gfar_phy *phy = priv->gfar_mdio; void __iomem *regs = priv->regs; int ret; - ret = phy_device_connect(edev, &priv->miibus, priv->phyaddr, + ret = phy_device_connect(edev, &phy->miibus, priv->phyaddr, gfar_adjust_link, 0, PHY_INTERFACE_MODE_NA); if (ret) return ret; @@ -305,44 +306,51 @@ static void gfar_configure_serdes(struct gfar_private *priv) { - gfar_local_mdio_write(priv->phyregs_sgmii, + struct gfar_phy *phy = priv->gfar_tbi; + + gfar_local_mdio_write(phy->regs, in_be32(priv->regs + GFAR_TBIPA_OFFSET), GFAR_TBI_ANA, priv->tbiana); - gfar_local_mdio_write(priv->phyregs_sgmii, + gfar_local_mdio_write(phy->regs, in_be32(priv->regs + GFAR_TBIPA_OFFSET), GFAR_TBI_TBICON, GFAR_TBICON_CLK_SELECT); - gfar_local_mdio_write(priv->phyregs_sgmii, + gfar_local_mdio_write(phy->regs, in_be32(priv->regs + GFAR_TBIPA_OFFSET), GFAR_TBI_CR, priv->tbicr); } -/* Reset the internal and external PHYs. */ -static void gfar_init_phy(struct eth_device *dev) +static int gfar_bus_reset(struct mii_bus *bus) { - struct gfar_private *priv = dev->priv; - void __iomem *regs = priv->regs; + struct gfar_phy *phy = bus->priv; uint64_t start; - /* Assign a Physical address to the TBI */ - out_be32(regs + GFAR_TBIPA_OFFSET, GFAR_TBIPA_VALUE); - /* Reset MII (due to new addresses) */ - out_be32(priv->phyregs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_RESET); - out_be32(priv->phyregs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_INIT_VALUE); + out_be32(phy->regs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_RESET); + out_be32(phy->regs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_INIT_VALUE); start = get_time_ns(); while (!is_timeout(start, 10 * MSECOND)) { - if (!(in_be32(priv->phyregs + GFAR_MIIMMIND_OFFSET) & + if (!(in_be32(phy->regs + GFAR_MIIMMIND_OFFSET) & GFAR_MIIMIND_BUSY)) break; } + return 0; +} - gfar_local_mdio_write(priv->phyregs, priv->phyaddr, GFAR_MIIM_CR, +/* Reset the external PHYs. */ +static void gfar_init_phy(struct eth_device *dev) +{ + struct gfar_private *priv = dev->priv; + struct gfar_phy *phy = priv->gfar_mdio; + void __iomem *regs = priv->regs; + uint64_t start; + + gfar_local_mdio_write(phy->regs, priv->phyaddr, GFAR_MIIM_CR, GFAR_MIIM_CR_RST); start = get_time_ns(); while (!is_timeout(start, 10 * MSECOND)) { - if (!(gfar_local_mdio_read(priv->phyregs, priv->phyaddr, + if (!(gfar_local_mdio_read(phy->regs, priv->phyaddr, GFAR_MIIM_CR) & GFAR_MIIM_CR_RST)) break; } @@ -433,13 +441,12 @@ /* Read a MII PHY register. */ static int gfar_miiphy_read(struct mii_bus *bus, int addr, int reg) { - struct device_d *dev = bus->parent; - struct gfar_private *priv = bus->priv; + struct gfar_phy *phy = bus->priv; int ret; - ret = gfar_local_mdio_read(priv->phyregs, addr, reg); + ret = gfar_local_mdio_read(phy->regs, addr, reg); if (ret == -EIO) - dev_err(dev, "Can't read PHY at address %d\n", addr); + dev_err(phy->dev, "Can't read PHY at address %d\n", addr); return ret; } @@ -448,15 +455,14 @@ static int gfar_miiphy_write(struct mii_bus *bus, int addr, int reg, u16 value) { - struct device_d *dev = bus->parent; - struct gfar_private *priv = bus->priv; + struct gfar_phy *phy = bus->priv; unsigned short val = value; int ret; - ret = gfar_local_mdio_write(priv->phyregs, addr, reg, val); + ret = gfar_local_mdio_write(phy->regs, addr, reg, val); if (ret) - dev_err(dev, "Can't write PHY at address %d\n", addr); + dev_err(phy->dev, "Can't write PHY at address %d\n", addr); return 0; } @@ -470,7 +476,9 @@ struct gfar_info_struct *gfar_info = dev->platform_data; struct eth_device *edev; struct gfar_private *priv; + struct device_d *mdev; size_t size; + char devname[16]; char *p; priv = xzalloc(sizeof(struct gfar_private)); @@ -480,14 +488,28 @@ edev = &priv->edev; - priv->regs = dev_request_mem_region(dev, 0); - priv->phyregs = dev_request_mem_region(dev, 1); - priv->phyregs_sgmii = dev_request_mem_region(dev, 2); - + priv->mdiobus_tbi = gfar_info->mdiobus_tbi; + priv->regs = dev_get_mem_region(dev, 0); priv->phyaddr = gfar_info->phyaddr; priv->tbicr = gfar_info->tbicr; priv->tbiana = gfar_info->tbiana; + mdev = get_device_by_name("gfar-mdio0"); + if (mdev == NULL) { + pr_err("gfar-mdio0 was not found\n"); + return -ENODEV; + } + priv->gfar_mdio = mdev->priv; + + if (priv->mdiobus_tbi != 0) { + sprintf(devname, "%s%d", "gfar-tbiphy", priv->mdiobus_tbi); + mdev = get_device_by_name(devname); + if (mdev == NULL) { + pr_err("%s was not found\n", devname); + return -ENODEV; + } + } + priv->gfar_tbi = mdev->priv; /* * Allocate descriptors 64-bit aligned. Descriptors * are 8 bytes in size. @@ -512,15 +534,8 @@ udelay(2); clrbits_be32(priv->regs + GFAR_MACCFG1_OFFSET, GFAR_MACCFG1_SOFT_RESET); - priv->miibus.read = gfar_miiphy_read; - priv->miibus.write = gfar_miiphy_write; - priv->miibus.priv = priv; - priv->miibus.parent = dev; - gfar_init_phy(edev); - mdiobus_register(&priv->miibus); - return eth_register(edev); } @@ -529,3 +544,64 @@ .probe = gfar_probe, }; device_platform_driver(gfar_eth_driver); + +static int gfar_phy_probe(struct device_d *dev) +{ + struct gfar_phy *phy; + int ret; + + phy = xzalloc(sizeof(*phy)); + phy->dev = dev; + phy->regs = dev_get_mem_region(dev, 0); + if (!phy->regs) + return -ENOMEM; + + phy->miibus.read = gfar_miiphy_read; + phy->miibus.write = gfar_miiphy_write; + phy->miibus.priv = phy; + phy->miibus.reset = gfar_bus_reset; + phy->miibus.parent = dev; + dev->priv = phy; + + ret = mdiobus_register(&phy->miibus); + if (ret) + return ret; + + return 0; +} + +static struct driver_d gfar_phy_driver = { + .name = "gfar-mdio", + .probe = gfar_phy_probe, +}; +register_driver_macro(coredevice, platform, gfar_phy_driver); + +static int gfar_tbiphy_probe(struct device_d *dev) +{ + struct gfar_phy *phy; + int ret; + + phy = xzalloc(sizeof(*phy)); + phy->dev = dev; + phy->regs = dev_get_mem_region(dev, 0); + if (!phy->regs) + return -ENOMEM; + + phy->miibus.read = gfar_miiphy_read; + phy->miibus.write = gfar_miiphy_write; + phy->miibus.priv = phy; + phy->miibus.parent = dev; + dev->priv = phy; + + ret = mdiobus_register(&phy->miibus); + if (ret) + return ret; + + return 0; +} + +static struct driver_d gfar_tbiphy_driver = { + .name = "gfar-tbiphy", + .probe = gfar_tbiphy_probe, +}; +register_driver_macro(coredevice, platform, gfar_tbiphy_driver); diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index b52cc5a..1aac479 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -205,7 +205,6 @@ #define GFAR_ECNTRL_OFFSET 0x020 /* Ethernet Control */ #define GFAR_MINFLR_OFFSET 0x024 /* Minimum Frame Length */ #define GFAR_DMACTRL_OFFSET 0x02c /* DMA Control */ -#define GFAR_TBIPA_OFFSET 0x030 /* TBI PHY address */ /* eTSEC transmit control and status register */ #define GFAR_TSTAT_OFFSET 0x104 /* transmit status register */ @@ -263,13 +262,19 @@ #define GFAR_ATTR_OFFSET 0xbf8 /* Default Attribute Register */ #define GFAR_ATTRELI_OFFSET 0xbfc /* Default Attribute Extract Len/Idx */ +struct gfar_phy { + void __iomem *regs; + struct device_d *dev; + struct mii_bus miibus; +}; + struct gfar_private { struct eth_device edev; void __iomem *regs; - void __iomem *phyregs; - void __iomem *phyregs_sgmii; + int mdiobus_tbi; + struct gfar_phy *gfar_mdio; + struct gfar_phy *gfar_tbi; struct phy_info *phyinfo; - struct mii_bus miibus; volatile struct txbd8 *txbd; volatile struct rxbd8 *rxbd; uint txidx; 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_ */