577e41c06e
nWR (Write-Recovery for AutoPre-charge commands), the programmed value of nWR is the number of clock cycles the LPDDR4-SDRAM device uses to determine the starting point of an internal Pre-charge operation after a Write burst with AP (auto-pre-charge) enabled. For >2133MHz speed parts the nWR needs to be set to 24 clock cycles. The nWR field, though, is only in the GLK FSP, so just update that field conditionally based on the GLK Kconfig option. BUG=b:112062440 TEST= build test Change-Id: I1147538f72f4e2f14e32f3657c05f1f505a56fbf Signed-off-by: Ravi Sarawadi <ravishankar.sarawadi@intel.com> Signed-off-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: https://review.coreboot.org/27850 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Furquan Shaikh <furquan@google.com>
375 lines
10 KiB
C
375 lines
10 KiB
C
/*
|
|
* This file is part of the coreboot project.
|
|
*
|
|
* Copyright 2016 Google 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; version 2 of the License.
|
|
*
|
|
* 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.
|
|
*/
|
|
#include <arch/early_variables.h>
|
|
#include <console/console.h>
|
|
#include <fsp/util.h>
|
|
#include <memory_info.h>
|
|
#include <soc/meminit.h>
|
|
#include <stddef.h> /* required for FspmUpd.h */
|
|
#include <fsp/soc_binding.h>
|
|
#include <string.h>
|
|
|
|
static size_t memory_size_mib CAR_GLOBAL;
|
|
|
|
size_t memory_in_system_in_mib(void)
|
|
{
|
|
return car_get_var(memory_size_mib);
|
|
}
|
|
|
|
static void accumulate_channel_memory(int density, int dual_rank)
|
|
{
|
|
/* For this platform LPDDR4 memory is 4 DRAM parts that are x32. 2 of
|
|
the parts are composed into a x64 memory channel. Thus there are 2
|
|
channels composed of 2 DRAMs. */
|
|
size_t sz;
|
|
|
|
/* Per rank density in Gb */
|
|
switch (density) {
|
|
case LP4_8Gb_DENSITY:
|
|
sz = 8;
|
|
break;
|
|
case LP4_12Gb_DENSITY:
|
|
sz = 12;
|
|
break;
|
|
case LP4_16Gb_DENSITY:
|
|
sz = 16;
|
|
break;
|
|
default:
|
|
printk(BIOS_ERR, "Invalid DRAM density: %d\n", density);
|
|
sz = 0;
|
|
break;
|
|
}
|
|
|
|
/* Two DRAMs per channel. */
|
|
sz *= 2;
|
|
|
|
/* Two ranks per channel. */
|
|
if (dual_rank)
|
|
sz *= 2;
|
|
|
|
sz *= GiB / MiB;
|
|
|
|
car_set_var(memory_size_mib, car_get_var(memory_size_mib) + sz);
|
|
}
|
|
|
|
size_t iohole_in_mib(void)
|
|
{
|
|
return 2 * (GiB / MiB);
|
|
}
|
|
|
|
static void set_lpddr4_defaults(FSP_M_CONFIG *cfg, int speed)
|
|
{
|
|
uint8_t odt_config;
|
|
|
|
/* Enable memory down BGA since it's the only LPDDR4 packaging. */
|
|
cfg->Package = 1;
|
|
cfg->MemoryDown = 1;
|
|
|
|
cfg->ScramblerSupport = 1;
|
|
cfg->ChannelHashMask = 0x36;
|
|
cfg->SliceHashMask = 0x9;
|
|
cfg->InterleavedMode = 2;
|
|
cfg->ChannelsSlicesEnable = 0;
|
|
cfg->MinRefRate2xEnable = 0;
|
|
cfg->DualRankSupportEnable = 1;
|
|
/* Don't enforce a memory size limit. */
|
|
cfg->MemorySizeLimit = 0;
|
|
/* Field is in MiB units. */
|
|
cfg->LowMemoryMaxValue = iohole_in_mib();
|
|
/* No restrictions on memory above 4GiB */
|
|
cfg->HighMemoryMaxValue = 0;
|
|
|
|
/* Always default to attempt to use saved training data. */
|
|
cfg->DisableFastBoot = 0;
|
|
|
|
/* LPDDR4 is memory down so no SPD addresses. */
|
|
cfg->DIMM0SPDAddress = 0;
|
|
cfg->DIMM1SPDAddress = 0;
|
|
|
|
/* Clear all the rank enables. */
|
|
cfg->Ch0_RankEnable = 0x0;
|
|
cfg->Ch1_RankEnable = 0x0;
|
|
cfg->Ch2_RankEnable = 0x0;
|
|
cfg->Ch3_RankEnable = 0x0;
|
|
|
|
/*
|
|
* Set the device width to x16 which is half a LPDDR4 module as that's
|
|
* what the reference code expects.
|
|
*/
|
|
cfg->Ch0_DeviceWidth = 0x1;
|
|
cfg->Ch1_DeviceWidth = 0x1;
|
|
cfg->Ch2_DeviceWidth = 0x1;
|
|
cfg->Ch3_DeviceWidth = 0x1;
|
|
|
|
/*
|
|
* Enable bank hashing (bit 1) and rank interleaving (bit 0) with
|
|
* a 1KiB address mapping (bits 5:4).
|
|
*/
|
|
cfg->Ch0_Option = 0x3;
|
|
cfg->Ch1_Option = 0x3;
|
|
cfg->Ch2_Option = 0x3;
|
|
cfg->Ch3_Option = 0x3;
|
|
|
|
/* Set CA ODT with default setting of ODT pins of LPDDR4 modules pulled
|
|
up to 1.1V. */
|
|
odt_config = ODT_A_B_HIGH_HIGH;
|
|
|
|
/* Need to set correct Write-Recovery configuration based on speed. */
|
|
if (IS_ENABLED(CONFIG_SOC_INTEL_GLK) && speed >= LP4_SPEED_2133)
|
|
odt_config |= nWR_24;
|
|
|
|
cfg->Ch0_OdtConfig = odt_config;
|
|
cfg->Ch1_OdtConfig = odt_config;
|
|
cfg->Ch2_OdtConfig = odt_config;
|
|
cfg->Ch3_OdtConfig = odt_config;
|
|
}
|
|
|
|
struct speed_mapping {
|
|
int logical;
|
|
int fsp_value;
|
|
};
|
|
|
|
struct fsp_speed_profiles {
|
|
const struct speed_mapping *mappings;
|
|
size_t num_mappings;
|
|
};
|
|
|
|
static const struct speed_mapping apl_mappings[] = {
|
|
{ .logical = LP4_SPEED_1600, .fsp_value = 0x9 },
|
|
{ .logical = LP4_SPEED_2133, .fsp_value = 0xa },
|
|
{ .logical = LP4_SPEED_2400, .fsp_value = 0xb },
|
|
};
|
|
|
|
static const struct fsp_speed_profiles apl_profile = {
|
|
.mappings = apl_mappings,
|
|
.num_mappings = ARRAY_SIZE(apl_mappings),
|
|
};
|
|
|
|
static const struct speed_mapping glk_mappings[] = {
|
|
{ .logical = LP4_SPEED_1600, .fsp_value = 0x4 },
|
|
{ .logical = LP4_SPEED_2133, .fsp_value = 0x6 },
|
|
{ .logical = LP4_SPEED_2400, .fsp_value = 0x7 },
|
|
};
|
|
|
|
static const struct fsp_speed_profiles glk_profile = {
|
|
.mappings = glk_mappings,
|
|
.num_mappings = ARRAY_SIZE(glk_mappings),
|
|
};
|
|
|
|
static const struct fsp_speed_profiles *get_fsp_profile(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_SOC_INTEL_GLK))
|
|
return &glk_profile;
|
|
else
|
|
return &apl_profile;
|
|
}
|
|
|
|
static int validate_speed(int speed)
|
|
{
|
|
const struct fsp_speed_profiles *fsp_profile = get_fsp_profile();
|
|
size_t i;
|
|
|
|
for (i = 0; i < fsp_profile->num_mappings; i++) {
|
|
/* Mapping exists. */
|
|
if (fsp_profile->mappings[i].logical == speed)
|
|
return speed;
|
|
}
|
|
|
|
printk(BIOS_WARNING, "Invalid LPDDR4 speed: %d\n", speed);
|
|
/* Default to slowest speed */
|
|
return LP4_SPEED_1600;
|
|
}
|
|
|
|
static int fsp_memory_profile(int speed)
|
|
{
|
|
const struct fsp_speed_profiles *fsp_profile = get_fsp_profile();
|
|
size_t i;
|
|
|
|
for (i = 0; i < fsp_profile->num_mappings; i++) {
|
|
if (fsp_profile->mappings[i].logical == speed)
|
|
return fsp_profile->mappings[i].fsp_value;
|
|
}
|
|
|
|
/* should never happen. */
|
|
return -1;
|
|
}
|
|
|
|
void meminit_lpddr4(FSP_M_CONFIG *cfg, int speed)
|
|
{
|
|
speed = validate_speed(speed);
|
|
|
|
printk(BIOS_INFO, "LP4DDR speed is %dMHz\n", speed);
|
|
cfg->Profile = fsp_memory_profile(speed);
|
|
|
|
set_lpddr4_defaults(cfg, speed);
|
|
}
|
|
|
|
static void enable_logical_chan0(FSP_M_CONFIG *cfg,
|
|
int rank_density, int dual_rank,
|
|
const struct lpddr4_swizzle_cfg *scfg)
|
|
{
|
|
const struct lpddr4_chan_swizzle_cfg *chan;
|
|
/* Number of bytes to copy per DQS. */
|
|
const size_t sz = DQ_BITS_PER_DQS;
|
|
int rank_mask;
|
|
|
|
/*
|
|
* Logical channel 0 is comprised of physical channel 0 and 1.
|
|
* Physical channel 0 is comprised of the CH0_DQB signals.
|
|
* Physical channel 1 is comprised of the CH0_DQA signals.
|
|
*/
|
|
cfg->Ch0_DramDensity = rank_density;
|
|
cfg->Ch1_DramDensity = rank_density;
|
|
/* Enable ranks on both channels depending on dual rank option. */
|
|
rank_mask = dual_rank ? 0x3 : 0x1;
|
|
cfg->Ch0_RankEnable = rank_mask;
|
|
cfg->Ch1_RankEnable = rank_mask;
|
|
|
|
/*
|
|
* CH0_DQB byte lanes in the bit swizzle configuration field are
|
|
* not 1:1. The mapping within the swizzling field is:
|
|
* indices [0:7] - byte lane 1 (DQS1) DQ[8:15]
|
|
* indices [8:15] - byte lane 0 (DQS0) DQ[0:7]
|
|
* indices [16:23] - byte lane 3 (DQS3) DQ[24:31]
|
|
* indices [24:31] - byte lane 2 (DQS2) DQ[16:23]
|
|
*/
|
|
chan = &scfg->phys[LP4_PHYS_CH0B];
|
|
memcpy(&cfg->Ch0_Bit_swizzling[0], &chan->dqs[LP4_DQS1], sz);
|
|
memcpy(&cfg->Ch0_Bit_swizzling[8], &chan->dqs[LP4_DQS0], sz);
|
|
memcpy(&cfg->Ch0_Bit_swizzling[16], &chan->dqs[LP4_DQS3], sz);
|
|
memcpy(&cfg->Ch0_Bit_swizzling[24], &chan->dqs[LP4_DQS2], sz);
|
|
|
|
/*
|
|
* CH0_DQA byte lanes in the bit swizzle configuration field are 1:1.
|
|
*/
|
|
chan = &scfg->phys[LP4_PHYS_CH0A];
|
|
memcpy(&cfg->Ch1_Bit_swizzling[0], &chan->dqs[LP4_DQS0], sz);
|
|
memcpy(&cfg->Ch1_Bit_swizzling[8], &chan->dqs[LP4_DQS1], sz);
|
|
memcpy(&cfg->Ch1_Bit_swizzling[16], &chan->dqs[LP4_DQS2], sz);
|
|
memcpy(&cfg->Ch1_Bit_swizzling[24], &chan->dqs[LP4_DQS3], sz);
|
|
}
|
|
|
|
static void enable_logical_chan1(FSP_M_CONFIG *cfg,
|
|
int rank_density, int dual_rank,
|
|
const struct lpddr4_swizzle_cfg *scfg)
|
|
{
|
|
const struct lpddr4_chan_swizzle_cfg *chan;
|
|
/* Number of bytes to copy per DQS. */
|
|
const size_t sz = DQ_BITS_PER_DQS;
|
|
int rank_mask;
|
|
|
|
/*
|
|
* Logical channel 1 is comprised of physical channel 2 and 3.
|
|
* Physical channel 2 is comprised of the CH1_DQB signals.
|
|
* Physical channel 3 is comprised of the CH1_DQA signals.
|
|
*/
|
|
cfg->Ch2_DramDensity = rank_density;
|
|
cfg->Ch3_DramDensity = rank_density;
|
|
/* Enable ranks on both channels depending on dual rank option. */
|
|
rank_mask = dual_rank ? 0x3 : 0x1;
|
|
cfg->Ch2_RankEnable = rank_mask;
|
|
cfg->Ch3_RankEnable = rank_mask;
|
|
|
|
/*
|
|
* CH1_DQB byte lanes in the bit swizzle configuration field are
|
|
* not 1:1. The mapping within the swizzling field is:
|
|
* indices [0:7] - byte lane 1 (DQS1) DQ[8:15]
|
|
* indices [8:15] - byte lane 0 (DQS0) DQ[0:7]
|
|
* indices [16:23] - byte lane 3 (DQS3) DQ[24:31]
|
|
* indices [24:31] - byte lane 2 (DQS2) DQ[16:23]
|
|
*/
|
|
chan = &scfg->phys[LP4_PHYS_CH1B];
|
|
memcpy(&cfg->Ch2_Bit_swizzling[0], &chan->dqs[LP4_DQS1], sz);
|
|
memcpy(&cfg->Ch2_Bit_swizzling[8], &chan->dqs[LP4_DQS0], sz);
|
|
memcpy(&cfg->Ch2_Bit_swizzling[16], &chan->dqs[LP4_DQS3], sz);
|
|
memcpy(&cfg->Ch2_Bit_swizzling[24], &chan->dqs[LP4_DQS2], sz);
|
|
|
|
/*
|
|
* CH1_DQA byte lanes in the bit swizzle configuration field are 1:1.
|
|
*/
|
|
chan = &scfg->phys[LP4_PHYS_CH1A];
|
|
memcpy(&cfg->Ch3_Bit_swizzling[0], &chan->dqs[LP4_DQS0], sz);
|
|
memcpy(&cfg->Ch3_Bit_swizzling[8], &chan->dqs[LP4_DQS1], sz);
|
|
memcpy(&cfg->Ch3_Bit_swizzling[16], &chan->dqs[LP4_DQS2], sz);
|
|
memcpy(&cfg->Ch3_Bit_swizzling[24], &chan->dqs[LP4_DQS3], sz);
|
|
}
|
|
|
|
void meminit_lpddr4_enable_channel(FSP_M_CONFIG *cfg, int logical_chan,
|
|
int rank_density, int dual_rank,
|
|
const struct lpddr4_swizzle_cfg *scfg)
|
|
{
|
|
if (rank_density < LP4_8Gb_DENSITY ||
|
|
rank_density > LP4_16Gb_DENSITY) {
|
|
printk(BIOS_ERR, "Invalid LPDDR4 density: %d\n", rank_density);
|
|
return;
|
|
}
|
|
|
|
switch (logical_chan) {
|
|
case LP4_LCH0:
|
|
enable_logical_chan0(cfg, rank_density, dual_rank, scfg);
|
|
break;
|
|
case LP4_LCH1:
|
|
enable_logical_chan1(cfg, rank_density, dual_rank, scfg);
|
|
break;
|
|
default:
|
|
printk(BIOS_ERR, "Invalid logical channel: %d\n", logical_chan);
|
|
return;
|
|
}
|
|
accumulate_channel_memory(rank_density, dual_rank);
|
|
}
|
|
|
|
void meminit_lpddr4_by_sku(FSP_M_CONFIG *cfg,
|
|
const struct lpddr4_cfg *lpcfg, size_t sku_id)
|
|
{
|
|
const struct lpddr4_sku *sku;
|
|
|
|
if (sku_id >= lpcfg->num_skus) {
|
|
printk(BIOS_ERR, "Too few LPDDR4 SKUs: 0x%zx/0x%zx\n",
|
|
sku_id, lpcfg->num_skus);
|
|
return;
|
|
}
|
|
|
|
printk(BIOS_INFO, "LPDDR4 SKU id = 0x%zx\n", sku_id);
|
|
|
|
sku = &lpcfg->skus[sku_id];
|
|
|
|
meminit_lpddr4(cfg, sku->speed);
|
|
|
|
if (sku->ch0_rank_density) {
|
|
printk(BIOS_INFO, "LPDDR4 Ch0 density = %d\n",
|
|
sku->ch0_rank_density);
|
|
meminit_lpddr4_enable_channel(cfg, LP4_LCH0,
|
|
sku->ch0_rank_density,
|
|
sku->ch0_dual_rank,
|
|
lpcfg->swizzle_config);
|
|
}
|
|
|
|
if (sku->ch1_rank_density) {
|
|
printk(BIOS_INFO, "LPDDR4 Ch1 density = %d\n",
|
|
sku->ch1_rank_density);
|
|
meminit_lpddr4_enable_channel(cfg, LP4_LCH1,
|
|
sku->ch1_rank_density,
|
|
sku->ch1_dual_rank,
|
|
lpcfg->swizzle_config);
|
|
}
|
|
|
|
cfg->PeriodicRetrainingDisable = sku->disable_periodic_retraining;
|
|
}
|
|
|
|
uint8_t fsp_memory_soc_version(void)
|
|
{
|
|
/* Bump this value when the memory configuration parameters change. */
|
|
return 1;
|
|
}
|