mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-22 11:13:07 +00:00
290ffe5788
I was trying to employ lpddr4_mr_read() to something similar to what the imx8mm-cl-iot-gate board is doing for auto-detecting the RAM type. However, the version in drivers/ddr/imx/imx8m/ddrphy_utils.c differs from the private one used by that board in how it extracts the byte value, and I was only getting zeroes. Adding a bit of debug printf'ing gives me tmp = 0x00ffff00 tmp = 0x00070700 tmp = 0x00000000 tmp = 0x00101000 and indeed I was expecting a (combined) value of 0xff070010 (0xff being Manufacturer ID for Micron). I can't find any documentation that says how the values are supposed to be read, but clearly the iot-gate definition is the right one, both for its use case as well as my imx8mp-based board. So lift the private definition of lpddr4_mr_read() from the imx8mm-cl-iot-gate board code to ddrphy_utils.c, and add a declaration in the ddr.h header where e.g. get_trained_CDD() is already declared. This has only been compile-tested for the imx8mm-cl-iot-gate board (since I don't have the hardware), but since I've merely moved its definition of lpddr4_mr_read(), I'd be surprised if it changed anything for that board. Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Tested-by: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> Reviewed-by: Fabio Estevam <festevam@denx.de>
202 lines
5.4 KiB
C
202 lines
5.4 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright 2017 NXP
|
|
* Copyright 2020 Linaro
|
|
*
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <spl.h>
|
|
#include <asm/io.h>
|
|
#include <errno.h>
|
|
#include <command.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/lpddr4_define.h>
|
|
#include <asm/mach-imx/iomux-v3.h>
|
|
#include <asm/mach-imx/gpio.h>
|
|
#include <asm-generic/gpio.h>
|
|
#include <asm/arch/ddr.h>
|
|
#include <asm/arch/imx8mq_pins.h>
|
|
#include <asm/arch/sys_proto.h>
|
|
#include <asm/arch/clock.h>
|
|
#include <asm/mach-imx/gpio.h>
|
|
#include "ddr.h"
|
|
|
|
#include <linux/delay.h>
|
|
|
|
struct lpddr4_desc {
|
|
char name[16];
|
|
unsigned int id;
|
|
unsigned int size;
|
|
unsigned int count;
|
|
/* an optional field
|
|
* use it if default is not the
|
|
* 1-st array entry
|
|
*/
|
|
unsigned int _default;
|
|
/* An optional field to distiguish DRAM chips that
|
|
* have different geometry, though return the same MRR.
|
|
* Default value 0xff
|
|
*/
|
|
u8 subind;
|
|
struct dram_timing_info *timing;
|
|
char *desc[4];
|
|
};
|
|
|
|
#define DEFAULT (('D' << 24) + ('E' << 16) + ('F' << 8) + 'A')
|
|
static const struct lpddr4_desc lpddr4_array[] = {
|
|
{ .name = "Nanya", .id = 0x05000010, .subind = 0xff,
|
|
.size = 2048, .count = 1, .timing = &ucm_dram_timing_01061010},
|
|
{ .name = "Samsung", .id = 0x01061010, .subind = 0xff,
|
|
.size = 2048, .count = 1, .timing = &ucm_dram_timing_01061010},
|
|
{ .name = "Kingston", .id = 0xff000010, .subind = 0x04,
|
|
.size = 4096, .count = 1, .timing = &ucm_dram_timing_ff000110},
|
|
{ .name = "Kingston", .id = 0xff000010, .subind = 0x02,
|
|
.size = 2048, .count = 1, .timing = &ucm_dram_timing_01061010},
|
|
{ .name = "Micron", .id = 0xff020008, .subind = 0xff,
|
|
.size = 2048, .count = 1, .timing = &ucm_dram_timing_ff020008},
|
|
{ .name = "Micron", .id = 0xff000110, .subind = 0xff,
|
|
.size = 4096, .count = 1, .timing = &ucm_dram_timing_ff000110},
|
|
};
|
|
|
|
static unsigned int lpddr4_get_mr(void)
|
|
{
|
|
int i = 0, attempts = 5;
|
|
unsigned int ddr_info = 0;
|
|
unsigned int regs[] = { 5, 6, 7, 8 };
|
|
|
|
do {
|
|
for (i = 0 ; i < ARRAY_SIZE(regs) ; i++) {
|
|
unsigned int data = 0;
|
|
|
|
data = lpddr4_mr_read(0xF, regs[i]);
|
|
ddr_info <<= 8;
|
|
ddr_info += (data & 0xFF);
|
|
}
|
|
if (ddr_info != 0xFFFFFFFF && ddr_info != 0)
|
|
break; // The attempt was successful
|
|
} while (--attempts);
|
|
return ddr_info;
|
|
}
|
|
|
|
static void spl_tcm_init(struct lpddr4_tcm_desc *lpddr4_tcm_desc)
|
|
{
|
|
if (lpddr4_tcm_desc->sign == DEFAULT)
|
|
return;
|
|
|
|
lpddr4_tcm_desc->sign = DEFAULT;
|
|
lpddr4_tcm_desc->index = 0;
|
|
}
|
|
|
|
static void spl_tcm_fini(struct lpddr4_tcm_desc *lpddr4_tcm_desc)
|
|
{
|
|
if (lpddr4_tcm_desc->sign != DEFAULT)
|
|
return;
|
|
|
|
lpddr4_tcm_desc->sign = ~DEFAULT;
|
|
lpddr4_tcm_desc->index = 0;
|
|
}
|
|
|
|
#define SPL_TCM_DATA 0x7e0000
|
|
#define SPL_TCM_INIT spl_tcm_init(lpddr4_tcm_desc)
|
|
#define SPL_TCM_FINI spl_tcm_fini(lpddr4_tcm_desc)
|
|
|
|
void spl_dram_init_compulab(void)
|
|
{
|
|
unsigned int ddr_info = 0xdeadbeef;
|
|
unsigned int ddr_info_mrr = 0xdeadbeef;
|
|
unsigned int ddr_found = 0;
|
|
int i = 0;
|
|
|
|
struct lpddr4_tcm_desc *lpddr4_tcm_desc =
|
|
(struct lpddr4_tcm_desc *)SPL_TCM_DATA;
|
|
|
|
if (lpddr4_tcm_desc->sign != DEFAULT) {
|
|
/* get ddr type from the eeprom if not in tcm scan mode */
|
|
ddr_info = cl_eeprom_get_ddrinfo();
|
|
for (i = 0; i < ARRAY_SIZE(lpddr4_array); i++) {
|
|
if (lpddr4_array[i].id == ddr_info &&
|
|
lpddr4_array[i].subind == cl_eeprom_get_subind()) {
|
|
ddr_found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Walk trought all available ddr ids and apply
|
|
* one by one. Save the index at the tcm memory that
|
|
* persists after the reset.
|
|
*/
|
|
if (ddr_found == 0) {
|
|
SPL_TCM_INIT;
|
|
|
|
if (lpddr4_tcm_desc->index < ARRAY_SIZE(lpddr4_array)) {
|
|
printf("DDRINFO: Cfg attempt: [ %d/%lu ]\n",
|
|
lpddr4_tcm_desc->index + 1,
|
|
ARRAY_SIZE(lpddr4_array));
|
|
i = lpddr4_tcm_desc->index;
|
|
lpddr4_tcm_desc->index += 1;
|
|
} else {
|
|
/* Ran out all available ddr setings */
|
|
printf("DDRINFO: Ran out all [ %lu ] cfg attempts. A non supported configuration.\n",
|
|
ARRAY_SIZE(lpddr4_array));
|
|
while (1)
|
|
;
|
|
}
|
|
ddr_info = lpddr4_array[i].id;
|
|
} else {
|
|
printf("DDRINFO(%s): %s %dG\n", (ddr_found ? "D" : "?"),
|
|
lpddr4_array[i].name,
|
|
lpddr4_array[i].size);
|
|
}
|
|
|
|
if (ddr_init(lpddr4_array[i].timing)) {
|
|
SPL_TCM_INIT;
|
|
do_reset(NULL, 0, 0, NULL);
|
|
}
|
|
|
|
ddr_info_mrr = lpddr4_get_mr();
|
|
if (ddr_info_mrr == 0xFFFFFFFF) {
|
|
printf("DDRINFO(M): mr5-8 [ 0x%x ] is invalid; reset\n",
|
|
ddr_info_mrr);
|
|
SPL_TCM_INIT;
|
|
do_reset(NULL, 0, 0, NULL);
|
|
}
|
|
|
|
printf("DDRINFO(M): mr5-8 [ 0x%x ]\n", ddr_info_mrr);
|
|
printf("DDRINFO(%s): mr5-8 [ 0x%x ]\n", (ddr_found ? "E" : "T"),
|
|
ddr_info);
|
|
|
|
if (ddr_info_mrr != ddr_info) {
|
|
SPL_TCM_INIT;
|
|
do_reset(NULL, 0, 0, NULL);
|
|
}
|
|
|
|
SPL_TCM_FINI;
|
|
|
|
if (ddr_found == 0) {
|
|
/* Update eeprom */
|
|
cl_eeprom_set_ddrinfo(ddr_info_mrr);
|
|
mdelay(10);
|
|
ddr_info = cl_eeprom_get_ddrinfo();
|
|
mdelay(10);
|
|
cl_eeprom_set_subind(lpddr4_array[i].subind);
|
|
/* make sure that the ddr_info has reached the eeprom */
|
|
printf("DDRINFO(E): mr5-8 [ 0x%x ], read back\n", ddr_info);
|
|
if (ddr_info_mrr != ddr_info || cl_eeprom_get_subind() != lpddr4_array[i].subind) {
|
|
printf("DDRINFO(EEPROM): make sure that the eeprom is accessible\n");
|
|
printf("DDRINFO(EEPROM): i2c dev 1; i2c md 0x51 0x40 0x50\n");
|
|
}
|
|
}
|
|
|
|
/* Pass the dram size to th U-Boot through the tcm memory */
|
|
{ /* To figure out what to store into the TCM buffer */
|
|
/* For debug purpouse only. To override the real memsize */
|
|
unsigned int ddr_tcm_size = cl_eeprom_get_osize();
|
|
|
|
if (ddr_tcm_size == 0 || ddr_tcm_size == -1)
|
|
ddr_tcm_size = lpddr4_array[i].size;
|
|
|
|
lpddr4_tcm_desc->size = ddr_tcm_size;
|
|
}
|
|
}
|