| /* |
| * Copyright (C) 2021-2024, STMicroelectronics - All Rights Reserved |
| * |
| * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause |
| */ |
| |
| #include <errno.h> |
| |
| #include <arch_helpers.h> |
| #include <common/debug.h> |
| #include <common/fdt_wrappers.h> |
| #include <drivers/clk.h> |
| #include <drivers/st/stm32mp2_ddr.h> |
| #include <drivers/st/stm32mp2_ddr_helpers.h> |
| #include <drivers/st/stm32mp2_ram.h> |
| #include <drivers/st/stm32mp_ddr.h> |
| #include <drivers/st/stm32mp_ddr_test.h> |
| #include <drivers/st/stm32mp_ram.h> |
| |
| #include <lib/mmio.h> |
| #include <libfdt.h> |
| |
| #include <platform_def.h> |
| |
| static struct stm32mp_ddr_priv ddr_priv_data; |
| static bool ddr_self_refresh; |
| |
| static int ddr_dt_get_ui_param(void *fdt, int node, struct stm32mp_ddr_config *config) |
| { |
| int ret; |
| uint32_t size; |
| |
| size = sizeof(struct user_input_basic) / sizeof(int); |
| ret = fdt_read_uint32_array(fdt, node, "st,phy-basic", size, (uint32_t *)&config->uib); |
| |
| VERBOSE("%s: %s[0x%x] = %d\n", __func__, "st,phy-basic", size, ret); |
| if (ret != 0) { |
| ERROR("%s: can't read %s, error=%d\n", __func__, "st,phy-basic", ret); |
| return -EINVAL; |
| } |
| |
| size = sizeof(struct user_input_advanced) / sizeof(int); |
| ret = fdt_read_uint32_array(fdt, node, "st,phy-advanced", size, (uint32_t *)&config->uia); |
| |
| VERBOSE("%s: %s[0x%x] = %d\n", __func__, "st,phy-advanced", size, ret); |
| if (ret != 0) { |
| ERROR("%s: can't read %s, error=%d\n", __func__, "st,phy-advanced", ret); |
| return -EINVAL; |
| } |
| |
| size = sizeof(struct user_input_mode_register) / sizeof(int); |
| ret = fdt_read_uint32_array(fdt, node, "st,phy-mr", size, (uint32_t *)&config->uim); |
| |
| VERBOSE("%s: %s[0x%x] = %d\n", __func__, "st,phy-mr", size, ret); |
| if (ret != 0) { |
| ERROR("%s: can't read %s, error=%d\n", __func__, "st,phy-mr", ret); |
| return -EINVAL; |
| } |
| |
| size = sizeof(struct user_input_swizzle) / sizeof(int); |
| ret = fdt_read_uint32_array(fdt, node, "st,phy-swizzle", size, (uint32_t *)&config->uis); |
| |
| VERBOSE("%s: %s[0x%x] = %d\n", __func__, "st,phy-swizzle", size, ret); |
| if (ret != 0) { |
| ERROR("%s: can't read %s, error=%d\n", __func__, "st,phy-swizzle", ret); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int stm32mp2_ddr_setup(void) |
| { |
| struct stm32mp_ddr_priv *priv = &ddr_priv_data; |
| int ret; |
| struct stm32mp_ddr_config config; |
| int node; |
| uintptr_t uret; |
| void *fdt; |
| |
| const struct stm32mp_ddr_param param[] = { |
| CTL_PARAM(reg), |
| CTL_PARAM(timing), |
| CTL_PARAM(map), |
| CTL_PARAM(perf) |
| }; |
| |
| if (fdt_get_address(&fdt) == 0) { |
| return -ENOENT; |
| } |
| |
| node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT); |
| if (node < 0) { |
| ERROR("%s: can't read DDR node in DT\n", __func__); |
| return -EINVAL; |
| } |
| |
| ret = stm32mp_ddr_dt_get_info(fdt, node, &config.info); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ret = stm32mp_ddr_dt_get_param(fdt, node, param, ARRAY_SIZE(param), (uintptr_t)&config); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ret = ddr_dt_get_ui_param(fdt, node, &config); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| config.self_refresh = false; |
| |
| if (stm32mp_is_wakeup_from_standby()) { |
| config.self_refresh = true; |
| } |
| |
| /* Map dynamically RETRAM area to save or restore PHY retention registers */ |
| if (stm32mp_map_retram() != 0) { |
| panic(); |
| } |
| |
| stm32mp2_ddr_init(priv, &config); |
| |
| /* Unmap RETRAM, no more used until next DDR initialization call */ |
| if (stm32mp_unmap_retram() != 0) { |
| panic(); |
| } |
| |
| priv->info.size = config.info.size; |
| |
| VERBOSE("%s : ram size(%lx, %lx)\n", __func__, priv->info.base, priv->info.size); |
| |
| if (stm32mp_map_ddr_non_cacheable() != 0) { |
| panic(); |
| } |
| |
| if (config.self_refresh) { |
| uret = stm32mp_ddr_test_rw_access(); |
| if (uret != 0UL) { |
| ERROR("DDR rw test: can't access memory @ 0x%lx\n", uret); |
| panic(); |
| } |
| |
| /* TODO Restore area overwritten by training */ |
| //stm32_restore_ddr_training_area(); |
| } else { |
| size_t retsize; |
| |
| uret = stm32mp_ddr_test_data_bus(); |
| if (uret != 0UL) { |
| ERROR("DDR data bus test: can't access memory @ 0x%lx\n", uret); |
| panic(); |
| } |
| |
| uret = stm32mp_ddr_test_addr_bus(config.info.size); |
| if (uret != 0UL) { |
| ERROR("DDR addr bus test: can't access memory @ 0x%lx\n", uret); |
| panic(); |
| } |
| |
| retsize = stm32mp_ddr_check_size(); |
| if (retsize < config.info.size) { |
| ERROR("DDR size: 0x%zx does not match DT config: 0x%zx\n", |
| retsize, config.info.size); |
| panic(); |
| } |
| |
| INFO("Memory size = 0x%zx (%zu MB)\n", retsize, retsize / (1024U * 1024U)); |
| } |
| |
| /* |
| * Initialization sequence has configured DDR registers with settings. |
| * The Self Refresh (SR) mode corresponding to these settings has now |
| * to be set. |
| */ |
| ddr_set_sr_mode(ddr_read_sr_mode()); |
| |
| if (stm32mp_unmap_ddr() != 0) { |
| panic(); |
| } |
| |
| /* Save DDR self_refresh state */ |
| ddr_self_refresh = config.self_refresh; |
| |
| return 0; |
| } |
| |
| bool stm32mp2_ddr_is_restored(void) |
| { |
| return ddr_self_refresh; |
| } |
| |
| int stm32mp2_ddr_probe(void) |
| { |
| struct stm32mp_ddr_priv *priv = &ddr_priv_data; |
| |
| VERBOSE("STM32MP DDR probe\n"); |
| |
| priv->ctl = (struct stm32mp_ddrctl *)stm32mp_ddrctrl_base(); |
| priv->phy = (struct stm32mp_ddrphy *)stm32mp_ddrphyc_base(); |
| priv->pwr = stm32mp_pwr_base(); |
| priv->rcc = stm32mp_rcc_base(); |
| |
| priv->info.base = STM32MP_DDR_BASE; |
| priv->info.size = 0; |
| |
| return stm32mp2_ddr_setup(); |
| } |