1a4efd428SKhristine Andreea Barbulescu /* 254239065SKhristine Andreea Barbulescu * Copyright 2020-2023, 2025-2026 NXP 3a4efd428SKhristine Andreea Barbulescu * 4a4efd428SKhristine Andreea Barbulescu * SPDX-License-Identifier: BSD-3-Clause 5a4efd428SKhristine Andreea Barbulescu */ 6a4efd428SKhristine Andreea Barbulescu 7a4efd428SKhristine Andreea Barbulescu #include <ddr_init.h> 8a4efd428SKhristine Andreea Barbulescu 947f0a591SKhristine Andreea Barbulescu static uint32_t load_phy_image(uint32_t start_addr, size_t size, 1047f0a591SKhristine Andreea Barbulescu const uint16_t image[]); 1147f0a591SKhristine Andreea Barbulescu 1254239065SKhristine Andreea Barbulescu /* Initialize ddr controller with given settings. */ 1354239065SKhristine Andreea Barbulescu static uint32_t ddrc_init_cfg(const struct ddrss_config *config) 1454239065SKhristine Andreea Barbulescu { 1554239065SKhristine Andreea Barbulescu return load_register_cfg(config->ddrc_size, config->ddrc); 1654239065SKhristine Andreea Barbulescu } 1754239065SKhristine Andreea Barbulescu 1847f0a591SKhristine Andreea Barbulescu /* Execute 2D training stage if images are available */ 1947f0a591SKhristine Andreea Barbulescu static uint32_t execute_2d_training(const struct ddrss_config *config) 2047f0a591SKhristine Andreea Barbulescu { 2147f0a591SKhristine Andreea Barbulescu uint32_t ret = NO_ERR; 2247f0a591SKhristine Andreea Barbulescu 2347f0a591SKhristine Andreea Barbulescu /* Load 2d imem image */ 2447f0a591SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, UNLOCK_CSR_ACCESS); 2547f0a591SKhristine Andreea Barbulescu ret = load_phy_image(IMEM_START_ADDR, config->imem_2d_size, 2647f0a591SKhristine Andreea Barbulescu config->imem_2d); 2747f0a591SKhristine Andreea Barbulescu if (ret != NO_ERR) { 2847f0a591SKhristine Andreea Barbulescu return ret; 2947f0a591SKhristine Andreea Barbulescu } 3047f0a591SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, LOCK_CSR_ACCESS); 3147f0a591SKhristine Andreea Barbulescu 3247f0a591SKhristine Andreea Barbulescu /* Load 2d dmem image */ 3347f0a591SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, UNLOCK_CSR_ACCESS); 3447f0a591SKhristine Andreea Barbulescu ret = load_phy_image(DMEM_START_ADDR, config->dmem_2d_size, 3547f0a591SKhristine Andreea Barbulescu config->dmem_2d); 3647f0a591SKhristine Andreea Barbulescu if (ret != NO_ERR) { 3747f0a591SKhristine Andreea Barbulescu return ret; 3847f0a591SKhristine Andreea Barbulescu } 3947f0a591SKhristine Andreea Barbulescu 4047f0a591SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, LOCK_CSR_ACCESS); 4147f0a591SKhristine Andreea Barbulescu mmio_write_32(APBONLY_MICRORESET, APBONLY_RESET_STALL_MASK); 4247f0a591SKhristine Andreea Barbulescu mmio_write_32(APBONLY_MICRORESET, APBONLY_STALL_TO_MICRO_MASK); 4347f0a591SKhristine Andreea Barbulescu mmio_write_32(APBONLY_MICRORESET, APBONLY_MICRORESET_CLR_MASK); 4447f0a591SKhristine Andreea Barbulescu 4547f0a591SKhristine Andreea Barbulescu ret = wait_firmware_execution(); 4647f0a591SKhristine Andreea Barbulescu if (ret != NO_ERR) { 4747f0a591SKhristine Andreea Barbulescu return ret; 4847f0a591SKhristine Andreea Barbulescu } 4947f0a591SKhristine Andreea Barbulescu 5047f0a591SKhristine Andreea Barbulescu /* Read 2D training results */ 5147f0a591SKhristine Andreea Barbulescu if (config->memory_type == (uint8_t)LPDDR4) { 5247f0a591SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, UNLOCK_CSR_ACCESS); 5347f0a591SKhristine Andreea Barbulescu read_vref_dq(); 5447f0a591SKhristine Andreea Barbulescu compute_tphy_wrdata_delay(); 5547f0a591SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, LOCK_CSR_ACCESS); 5647f0a591SKhristine Andreea Barbulescu } 5747f0a591SKhristine Andreea Barbulescu 5847f0a591SKhristine Andreea Barbulescu return ret; 5947f0a591SKhristine Andreea Barbulescu } 6047f0a591SKhristine Andreea Barbulescu 6154239065SKhristine Andreea Barbulescu /* Execute phy training with given settings. 2D training stage is optional. */ 6254239065SKhristine Andreea Barbulescu static uint32_t execute_training(const struct ddrss_config *config) 6354239065SKhristine Andreea Barbulescu { 6454239065SKhristine Andreea Barbulescu uint32_t ret = NO_ERR; 6554239065SKhristine Andreea Barbulescu 6654239065SKhristine Andreea Barbulescu /* Apply DQ swapping settings */ 6754239065SKhristine Andreea Barbulescu ret = load_dq_cfg(config->dq_swap_size, config->dq_swap); 6854239065SKhristine Andreea Barbulescu if (ret != NO_ERR) { 6954239065SKhristine Andreea Barbulescu return ret; 7054239065SKhristine Andreea Barbulescu } 7154239065SKhristine Andreea Barbulescu 7254239065SKhristine Andreea Barbulescu /* Initialize phy module */ 7354239065SKhristine Andreea Barbulescu ret = load_register_cfg_16(config->phy_size, config->phy); 7454239065SKhristine Andreea Barbulescu if (ret != NO_ERR) { 7554239065SKhristine Andreea Barbulescu return ret; 7654239065SKhristine Andreea Barbulescu } 7754239065SKhristine Andreea Barbulescu 7854239065SKhristine Andreea Barbulescu /* Configure PLL optimal settings */ 7954239065SKhristine Andreea Barbulescu set_optimal_pll(config->frequency); 8054239065SKhristine Andreea Barbulescu 8147f0a591SKhristine Andreea Barbulescu /* Load 1D imem image */ 8247f0a591SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, UNLOCK_CSR_ACCESS); 8347f0a591SKhristine Andreea Barbulescu ret = load_phy_image(IMEM_START_ADDR, config->imem_1d_size, 8447f0a591SKhristine Andreea Barbulescu config->imem_1d); 8547f0a591SKhristine Andreea Barbulescu if (ret != NO_ERR) { 8647f0a591SKhristine Andreea Barbulescu return ret; 8747f0a591SKhristine Andreea Barbulescu } 8854239065SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, LOCK_CSR_ACCESS); 8954239065SKhristine Andreea Barbulescu 9047f0a591SKhristine Andreea Barbulescu /* Load 1D dmem image */ 9147f0a591SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, UNLOCK_CSR_ACCESS); 9247f0a591SKhristine Andreea Barbulescu ret = load_phy_image(DMEM_START_ADDR, config->dmem_1d_size, 9347f0a591SKhristine Andreea Barbulescu config->dmem_1d); 9447f0a591SKhristine Andreea Barbulescu if (ret != NO_ERR) { 9547f0a591SKhristine Andreea Barbulescu return ret; 9647f0a591SKhristine Andreea Barbulescu } 9747f0a591SKhristine Andreea Barbulescu 9854239065SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, LOCK_CSR_ACCESS); 9954239065SKhristine Andreea Barbulescu mmio_write_32(APBONLY_MICRORESET, APBONLY_RESET_STALL_MASK); 10054239065SKhristine Andreea Barbulescu mmio_write_32(APBONLY_MICRORESET, APBONLY_STALL_TO_MICRO_MASK); 10154239065SKhristine Andreea Barbulescu mmio_write_32(APBONLY_MICRORESET, APBONLY_MICRORESET_CLR_MASK); 10254239065SKhristine Andreea Barbulescu 10354239065SKhristine Andreea Barbulescu ret = wait_firmware_execution(); 10454239065SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, UNLOCK_CSR_ACCESS); 10554239065SKhristine Andreea Barbulescu if (ret != NO_ERR) { 10654239065SKhristine Andreea Barbulescu return ret; 10754239065SKhristine Andreea Barbulescu } 10854239065SKhristine Andreea Barbulescu 10954239065SKhristine Andreea Barbulescu /* Read critical delay differences and training results */ 11054239065SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, UNLOCK_CSR_ACCESS); 11154239065SKhristine Andreea Barbulescu read_cdds(); 11254239065SKhristine Andreea Barbulescu if (config->memory_type == (uint8_t)LPDDR4) { 11354239065SKhristine Andreea Barbulescu read_vref_ca(); 11454239065SKhristine Andreea Barbulescu } 11554239065SKhristine Andreea Barbulescu if (config->memory_type == (uint8_t)DDR3L) { 11654239065SKhristine Andreea Barbulescu compute_tphy_wrdata_delay(); 11754239065SKhristine Andreea Barbulescu } 11854239065SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, LOCK_CSR_ACCESS); 11954239065SKhristine Andreea Barbulescu 12047f0a591SKhristine Andreea Barbulescu /* 12147f0a591SKhristine Andreea Barbulescu * Check if 2d training images have been initialized before executing 12247f0a591SKhristine Andreea Barbulescu * the second training stage. 12347f0a591SKhristine Andreea Barbulescu */ 12447f0a591SKhristine Andreea Barbulescu if ((config->imem_2d_size > 0U) && (config->dmem_2d_size > 0U)) { 12547f0a591SKhristine Andreea Barbulescu ret = execute_2d_training(config); 12647f0a591SKhristine Andreea Barbulescu if (ret != NO_ERR) { 12747f0a591SKhristine Andreea Barbulescu return ret; 12847f0a591SKhristine Andreea Barbulescu } 12947f0a591SKhristine Andreea Barbulescu } 13047f0a591SKhristine Andreea Barbulescu 13154239065SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, UNLOCK_CSR_ACCESS); 13254239065SKhristine Andreea Barbulescu /* Load pie image after training has executed */ 13354239065SKhristine Andreea Barbulescu ret = load_register_cfg_16(config->pie_size, config->pie); 13454239065SKhristine Andreea Barbulescu mmio_write_32(MICROCONT_MUX_SEL, LOCK_CSR_ACCESS); 13554239065SKhristine Andreea Barbulescu return ret; 13654239065SKhristine Andreea Barbulescu } 13754239065SKhristine Andreea Barbulescu 13854239065SKhristine Andreea Barbulescu /* Initialize ddr based on input configuration */ 13954239065SKhristine Andreea Barbulescu uint32_t ddr_init_cfg(const struct ddrss_config *config) 14054239065SKhristine Andreea Barbulescu { 14154239065SKhristine Andreea Barbulescu uint32_t ret = NO_ERR; 14254239065SKhristine Andreea Barbulescu 14354239065SKhristine Andreea Barbulescu /* Init DDR controller based on selected parameter values */ 14454239065SKhristine Andreea Barbulescu ret = ddrc_init_cfg(config); 14554239065SKhristine Andreea Barbulescu if (ret != NO_ERR) { 14654239065SKhristine Andreea Barbulescu return ret; 14754239065SKhristine Andreea Barbulescu } 14854239065SKhristine Andreea Barbulescu 14954239065SKhristine Andreea Barbulescu /* Setup AXI ports parity */ 15054239065SKhristine Andreea Barbulescu ret = set_axi_parity(); 15154239065SKhristine Andreea Barbulescu if (ret != NO_ERR) { 15254239065SKhristine Andreea Barbulescu return ret; 15354239065SKhristine Andreea Barbulescu } 15454239065SKhristine Andreea Barbulescu 15554239065SKhristine Andreea Barbulescu /* Init PHY module */ 15654239065SKhristine Andreea Barbulescu ret = execute_training(config); 15754239065SKhristine Andreea Barbulescu if (ret != NO_ERR) { 15854239065SKhristine Andreea Barbulescu return ret; 15954239065SKhristine Andreea Barbulescu } 16054239065SKhristine Andreea Barbulescu 161*a60aeae7SKhristine Andreea Barbulescu /* Execute post training setup */ 162*a60aeae7SKhristine Andreea Barbulescu ret = post_train_setup((uint8_t)(ADJUST_DDRC_MASK)); 163*a60aeae7SKhristine Andreea Barbulescu 16454239065SKhristine Andreea Barbulescu return ret; 16554239065SKhristine Andreea Barbulescu } 16654239065SKhristine Andreea Barbulescu 16754239065SKhristine Andreea Barbulescu /* Load register array into memory. */ 16854239065SKhristine Andreea Barbulescu uint32_t load_register_cfg_16(size_t size, const struct regconf_16 cfg[]) 16954239065SKhristine Andreea Barbulescu { 17054239065SKhristine Andreea Barbulescu size_t i; 17154239065SKhristine Andreea Barbulescu 17254239065SKhristine Andreea Barbulescu for (i = 0; i < size; i++) { 17354239065SKhristine Andreea Barbulescu mmio_write_16((uintptr_t)cfg[i].addr, cfg[i].data); 17454239065SKhristine Andreea Barbulescu } 17554239065SKhristine Andreea Barbulescu 17654239065SKhristine Andreea Barbulescu return NO_ERR; 17754239065SKhristine Andreea Barbulescu } 17854239065SKhristine Andreea Barbulescu 17954239065SKhristine Andreea Barbulescu /* Load register array into memory. */ 18054239065SKhristine Andreea Barbulescu uint32_t load_register_cfg(size_t size, const struct regconf cfg[]) 18154239065SKhristine Andreea Barbulescu { 18254239065SKhristine Andreea Barbulescu size_t i; 18354239065SKhristine Andreea Barbulescu 18454239065SKhristine Andreea Barbulescu for (i = 0; i < size; i++) { 18554239065SKhristine Andreea Barbulescu mmio_write_32((uintptr_t)cfg[i].addr, cfg[i].data); 18654239065SKhristine Andreea Barbulescu } 18754239065SKhristine Andreea Barbulescu 18854239065SKhristine Andreea Barbulescu return NO_ERR; 18954239065SKhristine Andreea Barbulescu } 19054239065SKhristine Andreea Barbulescu 19154239065SKhristine Andreea Barbulescu /* Load dq config array into memory. */ 19254239065SKhristine Andreea Barbulescu uint32_t load_dq_cfg(size_t size, const struct dqconf cfg[]) 19354239065SKhristine Andreea Barbulescu { 19454239065SKhristine Andreea Barbulescu size_t i; 19554239065SKhristine Andreea Barbulescu 19654239065SKhristine Andreea Barbulescu for (i = 0; i < size; i++) { 19754239065SKhristine Andreea Barbulescu mmio_write_32((uintptr_t)cfg[i].addr, cfg[i].data); 19854239065SKhristine Andreea Barbulescu } 19954239065SKhristine Andreea Barbulescu 20054239065SKhristine Andreea Barbulescu return NO_ERR; 20154239065SKhristine Andreea Barbulescu } 20254239065SKhristine Andreea Barbulescu 20347f0a591SKhristine Andreea Barbulescu /* Load image into memory at consecutive addresses */ 20447f0a591SKhristine Andreea Barbulescu static uint32_t load_phy_image(uint32_t start_addr, size_t size, 20547f0a591SKhristine Andreea Barbulescu const uint16_t image[]) 20647f0a591SKhristine Andreea Barbulescu { 20747f0a591SKhristine Andreea Barbulescu uint32_t current_addr = start_addr; 20847f0a591SKhristine Andreea Barbulescu size_t i; 20947f0a591SKhristine Andreea Barbulescu 21047f0a591SKhristine Andreea Barbulescu for (i = 0; i < size; i++) { 21147f0a591SKhristine Andreea Barbulescu mmio_write_32((uintptr_t)current_addr, image[i]); 21247f0a591SKhristine Andreea Barbulescu current_addr += (uint32_t)sizeof(uint32_t); 21347f0a591SKhristine Andreea Barbulescu } 21447f0a591SKhristine Andreea Barbulescu return NO_ERR; 21547f0a591SKhristine Andreea Barbulescu } 21647f0a591SKhristine Andreea Barbulescu 217a4efd428SKhristine Andreea Barbulescu /* Ensure optimal phy pll settings for selected frequency. */ 218a4efd428SKhristine Andreea Barbulescu void set_optimal_pll(uint16_t frequency) 219a4efd428SKhristine Andreea Barbulescu { 220a4efd428SKhristine Andreea Barbulescu /* Configure phy pll registers */ 221a4efd428SKhristine Andreea Barbulescu mmio_write_32(MASTER_PLLCTRL1, PLLCTRL1_VALUE); 222a4efd428SKhristine Andreea Barbulescu mmio_write_32(MASTER_PLLTESTMODE, PLLTESTMODE_VALUE); 223a4efd428SKhristine Andreea Barbulescu mmio_write_32(MASTER_PLLCTRL4, PLLCTRL4_VALUE); 224a4efd428SKhristine Andreea Barbulescu mmio_write_32(MASTER_PLLCTRL2, pllctrl2_value(frequency)); 225a4efd428SKhristine Andreea Barbulescu 226a4efd428SKhristine Andreea Barbulescu mmio_setbits_32(MASTER_CALMISC2, (CALMISC2 << CALMISC2_OFFSET)); 227a4efd428SKhristine Andreea Barbulescu 228a4efd428SKhristine Andreea Barbulescu mmio_clrsetbits_32(MASTER_CALOFFSET, 229a4efd428SKhristine Andreea Barbulescu CALDRV_MASK, ((CALDRV << CALDRV_OFFSET) | (CALDRV << CALDRV2_OFFSET))); 230a4efd428SKhristine Andreea Barbulescu } 231