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. */
ddrc_init_cfg(const struct ddrss_config * config)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 */
execute_2d_training(const struct ddrss_config * config)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. */
execute_training(const struct ddrss_config * config)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 */
ddr_init_cfg(const struct ddrss_config * config)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. */
load_register_cfg_16(size_t size,const struct regconf_16 cfg[])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. */
load_register_cfg(size_t size,const struct regconf cfg[])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. */
load_dq_cfg(size_t size,const struct dqconf cfg[])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 */
load_phy_image(uint32_t start_addr,size_t size,const uint16_t image[])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. */
set_optimal_pll(uint16_t 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