// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2017 NXP * Copyright 2020 Linaro * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ddr.h" static unsigned int lpddr4_mr_read(unsigned int mr_rank, unsigned int mr_addr) { unsigned int tmp; reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x1); do { tmp = reg32_read(DDRC_MRSTAT(0)); } while (tmp & 0x1); reg32_write(DDRC_MRCTRL0(0), (mr_rank << 4) | 0x1); reg32_write(DDRC_MRCTRL1(0), (mr_addr << 8)); reg32setbit(DDRC_MRCTRL0(0), 31); do { tmp = reg32_read(DRC_PERF_MON_MRR0_DAT(0)); } while ((tmp & 0x8) == 0); tmp = reg32_read(DRC_PERF_MON_MRR1_DAT(0)); reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x4); while (tmp) { //try to find a significant byte in the word if (tmp & 0xff) { tmp &= 0xff; break; } tmp >>= 8; } return tmp; } 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) { /* if not in tcm scan mode */ for (i = 0; i < ARRAY_SIZE(lpddr4_array); i++) { if (lpddr4_array[i].id == ddr_info && lpddr4_array[i].subind == 0xff) { 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; /* 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 = 0; if (ddr_tcm_size == 0 || ddr_tcm_size == -1) ddr_tcm_size = lpddr4_array[i].size; lpddr4_tcm_desc->size = ddr_tcm_size; } }