diff options
author | Nick Vaccaro <nvaccaro@chromium.org> | 2017-12-22 22:50:57 -0800 |
---|---|---|
committer | Martin Roth <martinroth@google.com> | 2018-01-07 18:45:46 +0000 |
commit | 780a1c44e190427522ee27e887b2a9ab692eb594 (patch) | |
tree | 6052cb7faf581fe4186aa558c254f0a7f6191736 | |
parent | a98727849ad06dd5464c88d485f57f09e090f5c4 (diff) |
soc/intel/cannonlake: provide LPDDR4 memory init
Instead of having the mainboards duplicate logic surrounding
LPDDR4 initialization provide helpers to do the heavy lifting.
It also handles the quirks of the FSP configuration which allows
the mainboard porting to focus on the schematic/design.
BUG=b:64395641
BRANCH=None
TEST=Verify "./util/abuild/abuild -p none -t google/zoombini -x -a"
compiles successfully.
Change-Id: I4a43ea121e663b866eaca3930eca61f30bb52834
Signed-off-by: Nick Vaccaro <nvaccaro@chromium.org>
Reviewed-on: https://review.coreboot.org/22204
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Martin Roth <martinroth@google.com>
-rw-r--r-- | src/soc/intel/cannonlake/Kconfig | 4 | ||||
-rw-r--r-- | src/soc/intel/cannonlake/Makefile.inc | 1 | ||||
-rw-r--r-- | src/soc/intel/cannonlake/cnl_lpddr4_init.c | 111 | ||||
-rw-r--r-- | src/soc/intel/cannonlake/include/soc/cnl_lpddr4_init.h | 106 |
4 files changed, 222 insertions, 0 deletions
diff --git a/src/soc/intel/cannonlake/Kconfig b/src/soc/intel/cannonlake/Kconfig index a19a371374..715cdf4ebb 100644 --- a/src/soc/intel/cannonlake/Kconfig +++ b/src/soc/intel/cannonlake/Kconfig @@ -131,6 +131,10 @@ config CPU_BCLK_MHZ int default 100 +config SOC_INTEL_CANNONLAKE_LPDDR4_INIT + bool + default n + config SOC_INTEL_COMMON_LPSS_CLOCK_MHZ int default 120 diff --git a/src/soc/intel/cannonlake/Makefile.inc b/src/soc/intel/cannonlake/Makefile.inc index 61318a4282..d4ca4072bd 100644 --- a/src/soc/intel/cannonlake/Makefile.inc +++ b/src/soc/intel/cannonlake/Makefile.inc @@ -20,6 +20,7 @@ bootblock-y += memmap.c bootblock-y += spi.c bootblock-$(CONFIG_UART_DEBUG) += uart.c +romstage-$(CONFIG_SOC_INTEL_CANNONLAKE_LPDDR4_INIT) += cnl_lpddr4_init.c romstage-y += gpio.c romstage-y += gspi.c romstage-y += i2c.c diff --git a/src/soc/intel/cannonlake/cnl_lpddr4_init.c b/src/soc/intel/cannonlake/cnl_lpddr4_init.c new file mode 100644 index 0000000000..ea8c410267 --- /dev/null +++ b/src/soc/intel/cannonlake/cnl_lpddr4_init.c @@ -0,0 +1,111 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2017 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 <assert.h> +#include <console/console.h> +#include <fsp/util.h> +#include <soc/cnl_lpddr4_init.h> +#include <spd_bin.h> +#include <string.h> + +static void meminit_lpddr4(FSP_M_CONFIG *mem_cfg, + const struct lpddr4_cfg *board_cfg, + size_t spd_data_len, uintptr_t spd_data_ptr) +{ + /* + * DqByteMapChx expects 12 bytes of data, but the last 6 bytes + * are unused, so client passes in the relevant values and + * we null out the rest of the data. + */ + memset(&mem_cfg->DqByteMapCh0, 0, sizeof(mem_cfg->DqByteMapCh0)); + memcpy(&mem_cfg->DqByteMapCh0, &board_cfg->dq_map[LP4_CH0], + sizeof(board_cfg->dq_map[LP4_CH0])); + + memset(&mem_cfg->DqByteMapCh1, 0, sizeof(mem_cfg->DqByteMapCh1)); + memcpy(&mem_cfg->DqByteMapCh1, &board_cfg->dq_map[LP4_CH1], + sizeof(board_cfg->dq_map[LP4_CH1])); + + memcpy(&mem_cfg->DqsMapCpu2DramCh0, &board_cfg->dqs_map[LP4_CH0], + sizeof(board_cfg->dqs_map[LP4_CH0])); + memcpy(&mem_cfg->DqsMapCpu2DramCh1, &board_cfg->dqs_map[LP4_CH1], + sizeof(board_cfg->dqs_map[LP4_CH1])); + + memcpy(&mem_cfg->RcompResistor, &board_cfg->rcomp_resistor, + sizeof(mem_cfg->RcompResistor)); + + /* Early cannonlake requires rcomp targets to be 0 */ + memcpy(&mem_cfg->RcompTarget, &board_cfg->rcomp_targets, + sizeof(mem_cfg->RcompTarget)); + + mem_cfg->MemorySpdDataLen = spd_data_len; + mem_cfg->MemorySpdPtr00 = spd_data_ptr; + + /* Use the same spd data for channel 1, Dimm 0 */ + mem_cfg->MemorySpdPtr10 = mem_cfg->MemorySpdPtr00; +} + +/* + * Initialize default LPDDR4 settings using spd data contained in a buffer. + */ +static void meminit_lpddr4_spd_data(FSP_M_CONFIG *mem_cfg, + const struct lpddr4_cfg *cnl_cfg, + size_t spd_data_len, uintptr_t spd_data_ptr) +{ + assert(spd_data_ptr && spd_data_len); + meminit_lpddr4(mem_cfg, cnl_cfg, spd_data_len, spd_data_ptr); +} + +/* + * Initialize default LPDDR4 settings using the spd file specified by + * spd_index. The spd_index is an index into the SPD_SOURCES array defined + * in spd/Makefile.inc. + */ +static void meminit_lpddr4_cbfs_spd_index(FSP_M_CONFIG *mem_cfg, + const struct lpddr4_cfg *cnl_cfg, + int spd_index) +{ + size_t spd_data_len; + uintptr_t spd_data_ptr; + struct region_device spd_rdev; + + printk(BIOS_DEBUG, "SPD INDEX = %d\n", spd_index); + if (get_spd_cbfs_rdev(&spd_rdev, spd_index) < 0) + die("spd.bin not found or incorrect index\n"); + spd_data_len = region_device_sz(&spd_rdev); + /* Memory leak is ok since we have memory mapped boot media */ + assert(IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)); + spd_data_ptr = (uintptr_t)rdev_mmap_full(&spd_rdev); + meminit_lpddr4_spd_data(mem_cfg, cnl_cfg, spd_data_len, spd_data_ptr); +} + +/* Initialize LPDDR4 settings for CannonLake */ +void cannonlake_lpddr4_init(FSP_M_CONFIG *mem_cfg, + const struct lpddr4_cfg *cnl_cfg, + const struct spd_info *spd) +{ + /* Early Command Training Enabled */ + mem_cfg->ECT = cnl_cfg->ect; + mem_cfg->DqPinsInterleaved = cnl_cfg->dq_pins_interleaved; + mem_cfg->RefClk = 0; /* Auto Select CLK freq */ + mem_cfg->CaVrefConfig = 0; /* VREF_CA->CHA/CHB */ + + if (spd->spd_by_index) { + meminit_lpddr4_cbfs_spd_index(mem_cfg, cnl_cfg, + spd->spd_spec.spd_index); + } else { + meminit_lpddr4_spd_data(mem_cfg, cnl_cfg, + spd->spd_spec.spd_data_ptr_info.spd_data_len, + spd->spd_spec.spd_data_ptr_info.spd_data_ptr); + } +} diff --git a/src/soc/intel/cannonlake/include/soc/cnl_lpddr4_init.h b/src/soc/intel/cannonlake/include/soc/cnl_lpddr4_init.h new file mode 100644 index 0000000000..db1c3a57c9 --- /dev/null +++ b/src/soc/intel/cannonlake/include/soc/cnl_lpddr4_init.h @@ -0,0 +1,106 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2017 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. + */ + +#ifndef _SOC_CANNONLAKE_LPDDR4_INIT_H_ +#define _SOC_CANNONLAKE_LPDDR4_INIT_H_ + +#include <stddef.h> +#include <stdint.h> +#include <fsp/soc_binding.h> + +/* Number of dq bits controlled per dqs */ +#define DQ_BITS_PER_DQS 8 + +/* + * Number of LPDDR4 packages, where a "package" represents a 64-bit solution. + */ +#define LP4_NUM_PACKAGES 2 + +/* 64-bit Channel identification */ +enum { + LP4_CH0, + LP4_CH1, + LP4_NUM_CHANNELS +}; + +struct spd_by_pointer { + size_t spd_data_len; + uintptr_t spd_data_ptr; +}; + +struct spd_info { + bool spd_by_index; + union spd_data_by { + int spd_index; + struct spd_by_pointer spd_data_ptr_info; + } spd_spec; +}; + +/* Board-specific lpddr4 dq mapping information */ +struct lpddr4_cfg { + /* + * For each channel, there are 3 sets of DQ byte mappings, + * where each set has a package 0 and a package 1 value (package 0 + * represents the first 64-bit lpddr4 chip combination, and package 1 + * represents the second 64-bit lpddr4 chip combination). + * The first three sets are for CLK, CMD, and CTL. + * The fsp package actually expects 6 sets, but the last 3 sets are + * not used in CNL, so we only define the three sets that are used + * and let the meminit_lpddr4() routine take care of clearing the + * unused fields for the caller. + */ + const uint8_t dq_map[LP4_NUM_CHANNELS][3][LP4_NUM_PACKAGES]; + + /* + * DQS CPU<>DRAM map Ch0 and Ch1. Each array entry represents a + * mapping of a dq bit on the CPU to the bit it's connected to on + * the memory part. The array index represents the dqs bit number + * on the memory part, and the values in the array represent which + * pin on the CPU that DRAM pin connects to. + */ + const uint8_t dqs_map[LP4_NUM_CHANNELS][DQ_BITS_PER_DQS]; + + /* + * Rcomp resistor values. These values represent the resistance in + * ohms of the three rcomp resistors attached to the DDR_COMP_0, + * DDR_COMP_1, and DDR_COMP_2 pins on the DRAM. + */ + const uint16_t rcomp_resistor[3]; + + /* + * Rcomp target values. These will typically be the following + * values for Cannon Lake : { 80, 40, 40, 40, 30 } + */ + const uint16_t rcomp_targets[5]; + + /* + * Indicates whether memory is interleaved. + * Set to 1 for an interleaved design, + * set to 0 for non-interleaved design. + */ + const uint8_t dq_pins_interleaved; + + /* Early Command Training Enabled */ + const uint8_t ect; +}; + +/* + * Initialize default LPDDR4 settings for CannonLake. + */ +void cannonlake_lpddr4_init(FSP_M_CONFIG *mem_cfg, + const struct lpddr4_cfg *cnl_cfg, + const struct spd_info *spd); + +#endif /* _SOC_CANNONLAKE_LPDDR4_INIT_H_ */ |