blob: 9e6622cc1b7faf10bb0167555c95e1e66acbea82 [file] [log] [blame]
/*
* 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;
}