| /* |
| * Copyright 2021-2022 NXP |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * |
| */ |
| |
| #include <errno.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include <common/debug.h> |
| #include <ddr.h> |
| #include <immap.h> |
| #include <lib/mmio.h> |
| |
| #define UL_5POW12 244140625UL |
| #define ULL_2E12 2000000000000ULL |
| #define UL_2POW13 (1UL << 13) |
| #define ULL_8FS 0xFFFFFFFFULL |
| |
| #define do_div(n, base) ({ \ |
| unsigned int __base = (base); \ |
| unsigned int __rem; \ |
| __rem = ((unsigned long long)(n)) % __base; \ |
| (n) = ((unsigned long long)(n)) / __base; \ |
| __rem; \ |
| }) |
| |
| #define CCN_HN_F_SAM_NODEID_MASK 0x7f |
| #ifdef NXP_HAS_CCN504 |
| #define CCN_HN_F_SAM_NODEID_DDR0 0x4 |
| #define CCN_HN_F_SAM_NODEID_DDR1 0xe |
| #elif defined(NXP_HAS_CCN508) |
| #define CCN_HN_F_SAM_NODEID_DDR0_0 0x3 |
| #define CCN_HN_F_SAM_NODEID_DDR0_1 0x8 |
| #define CCN_HN_F_SAM_NODEID_DDR1_0 0x13 |
| #define CCN_HN_F_SAM_NODEID_DDR1_1 0x18 |
| #endif |
| |
| unsigned long get_ddr_freq(struct sysinfo *sys, int ctrl_num) |
| { |
| if (sys->freq_ddr_pll0 == 0) { |
| get_clocks(sys); |
| } |
| |
| switch (ctrl_num) { |
| case 0: |
| return sys->freq_ddr_pll0; |
| case 1: |
| return sys->freq_ddr_pll0; |
| case 2: |
| return sys->freq_ddr_pll1; |
| } |
| |
| return 0; |
| } |
| |
| unsigned int get_memory_clk_ps(const unsigned long data_rate) |
| { |
| unsigned int result; |
| /* Round to nearest 10ps, being careful about 64-bit multiply/divide */ |
| unsigned long long rem, mclk_ps = ULL_2E12; |
| |
| /* Now perform the big divide, the result fits in 32-bits */ |
| rem = do_div(mclk_ps, data_rate); |
| result = (rem >= (data_rate >> 1)) ? mclk_ps + 1 : mclk_ps; |
| |
| return result; |
| } |
| |
| unsigned int picos_to_mclk(unsigned long data_rate, unsigned int picos) |
| { |
| unsigned long long clks, clks_rem; |
| |
| /* Short circuit for zero picos */ |
| if ((picos == 0U) || (data_rate == 0UL)) { |
| return 0U; |
| } |
| |
| /* First multiply the time by the data rate (32x32 => 64) */ |
| clks = picos * (unsigned long long)data_rate; |
| /* |
| * Now divide by 5^12 and track the 32-bit remainder, then divide |
| * by 2*(2^12) using shifts (and updating the remainder). |
| */ |
| clks_rem = do_div(clks, UL_5POW12); |
| clks_rem += (clks & (UL_2POW13-1)) * UL_5POW12; |
| clks >>= 13U; |
| |
| /* If we had a remainder greater than the 1ps error, then round up */ |
| if (clks_rem > data_rate) { |
| clks++; |
| } |
| |
| /* Clamp to the maximum representable value */ |
| if (clks > ULL_8FS) { |
| clks = ULL_8FS; |
| } |
| return (unsigned int) clks; |
| } |
| |
| /* valid_spd_mask has been checked by parse_spd */ |
| int disable_unused_ddrc(struct ddr_info *priv, |
| int valid_spd_mask, uintptr_t nxp_ccn_hn_f0_addr) |
| { |
| #if defined(NXP_HAS_CCN504) || defined(NXP_HAS_CCN508) |
| void *hnf_sam_ctrl = (void *)(nxp_ccn_hn_f0_addr + CCN_HN_F_SAM_CTL); |
| uint32_t val, nodeid; |
| #ifdef NXP_HAS_CCN504 |
| uint32_t num_hnf_nodes = 4U; |
| #else |
| uint32_t num_hnf_nodes = 8U; |
| #endif |
| int disable_ddrc = 0; |
| int i; |
| |
| if (priv->num_ctlrs < 2) { |
| debug("%s: nothing to do.\n", __func__); |
| } |
| |
| switch (priv->dimm_on_ctlr) { |
| case 1: |
| disable_ddrc = ((valid_spd_mask &0x2) == 0) ? 2 : 0; |
| disable_ddrc = ((valid_spd_mask &0x1) == 0) ? 1 : disable_ddrc; |
| break; |
| case 2: |
| disable_ddrc = ((valid_spd_mask &0x4) == 0) ? 2 : 0; |
| disable_ddrc = ((valid_spd_mask &0x1) == 0) ? 1 : disable_ddrc; |
| break; |
| default: |
| ERROR("Invalid number of DIMMs %d\n", priv->dimm_on_ctlr); |
| return -EINVAL; |
| } |
| |
| if (disable_ddrc != 0) { |
| debug("valid_spd_mask = 0x%x\n", valid_spd_mask); |
| } |
| |
| switch (disable_ddrc) { |
| case 1: |
| priv->num_ctlrs = 1; |
| priv->spd_addr = &priv->spd_addr[priv->dimm_on_ctlr]; |
| priv->ddr[0] = priv->ddr[1]; |
| priv->ddr[1] = NULL; |
| priv->phy[0] = priv->phy[0]; |
| priv->phy[1] = NULL; |
| debug("Disable first DDR controller\n"); |
| break; |
| case 2: |
| priv->num_ctlrs = 1; |
| priv->ddr[1] = NULL; |
| priv->phy[1] = NULL; |
| debug("Disable second DDR controller\n"); |
| /* fallthrough */ |
| case 0: |
| break; |
| default: |
| ERROR("Program error.\n"); |
| return -EINVAL; |
| } |
| |
| if (disable_ddrc == 0) { |
| debug("Both controllers in use.\n"); |
| return 0; |
| } |
| |
| for (i = 0; i < num_hnf_nodes; i++) { |
| val = mmio_read_64((uintptr_t)hnf_sam_ctrl); |
| #ifdef NXP_HAS_CCN504 |
| nodeid = disable_ddrc == 1 ? CCN_HN_F_SAM_NODEID_DDR1 : |
| (disable_ddrc == 2 ? CCN_HN_F_SAM_NODEID_DDR0 : |
| 0x0); /*Failure condition. never hit */ |
| #elif defined(NXP_HAS_CCN508) |
| if (disable_ddrc == 1) { |
| nodeid = (i < 2 || i >= 6) ? CCN_HN_F_SAM_NODEID_DDR1_1 : |
| CCN_HN_F_SAM_NODEID_DDR1_0; |
| } else if (disable_ddrc == 2) { |
| nodeid = (i < 2 || i >= 6) ? CCN_HN_F_SAM_NODEID_DDR0_0 : |
| CCN_HN_F_SAM_NODEID_DDR0_1; |
| } else { |
| nodeid = 0; /* Failure condition. never hit */ |
| } |
| #endif |
| if (nodeid != (val & CCN_HN_F_SAM_NODEID_MASK)) { |
| debug("Setting HN-F node %d\n", i); |
| debug("nodeid = 0x%x\n", nodeid); |
| val &= ~CCN_HN_F_SAM_NODEID_MASK; |
| val |= nodeid; |
| mmio_write_64((uintptr_t)hnf_sam_ctrl, val); |
| } |
| hnf_sam_ctrl += CCN_HN_F_REGION_SIZE; |
| } |
| #endif |
| return 0; |
| } |
| |
| unsigned int get_ddrc_version(const struct ccsr_ddr *ddr) |
| { |
| unsigned int ver; |
| |
| ver = (ddr_in32(&ddr->ip_rev1) & 0xFFFF) << 8U; |
| ver |= (ddr_in32(&ddr->ip_rev2) & 0xFF00) >> 8U; |
| |
| return ver; |
| } |
| |
| void print_ddr_info(struct ccsr_ddr *ddr) |
| { |
| unsigned int cs0_config = ddr_in32(&ddr->csn_cfg[0]); |
| unsigned int sdram_cfg = ddr_in32(&ddr->sdram_cfg); |
| int cas_lat; |
| |
| if ((sdram_cfg & SDRAM_CFG_MEM_EN) == 0U) { |
| printf(" (DDR not enabled)\n"); |
| return; |
| } |
| |
| printf("DDR"); |
| switch ((sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) >> |
| SDRAM_CFG_SDRAM_TYPE_SHIFT) { |
| case SDRAM_TYPE_DDR4: |
| printf("4"); |
| break; |
| default: |
| printf("?"); |
| break; |
| } |
| |
| switch (sdram_cfg & SDRAM_CFG_DBW_MASK) { |
| case SDRAM_CFG_32_BW: |
| printf(", 32-bit"); |
| break; |
| case SDRAM_CFG_16_BW: |
| printf(", 16-bit"); |
| break; |
| case SDRAM_CFG_8_BW: |
| printf(", 8-bit"); |
| break; |
| default: |
| printf(", 64-bit"); |
| break; |
| } |
| |
| /* Calculate CAS latency based on timing cfg values */ |
| cas_lat = ((ddr_in32(&ddr->timing_cfg_1) >> 16) & 0xf); |
| cas_lat += 2; /* for DDRC newer than 4.4 */ |
| cas_lat += ((ddr_in32(&ddr->timing_cfg_3) >> 12) & 3) << 4; |
| printf(", CL=%d", cas_lat >> 1); |
| if ((cas_lat & 0x1) != 0) { |
| printf(".5"); |
| } |
| |
| if ((sdram_cfg & SDRAM_CFG_ECC_EN) != 0) { |
| printf(", ECC on"); |
| } else { |
| printf(", ECC off"); |
| } |
| |
| if ((cs0_config & 0x20000000) != 0) { |
| printf(", "); |
| switch ((cs0_config >> 24) & 0xf) { |
| case DDR_256B_INTLV: |
| printf("256B"); |
| break; |
| default: |
| printf("invalid"); |
| break; |
| } |
| } |
| |
| if (((sdram_cfg >> 8) & 0x7f) != 0) { |
| printf(", "); |
| switch (sdram_cfg >> 8 & 0x7f) { |
| case DDR_BA_INTLV_CS0123: |
| printf("CS0+CS1+CS2+CS3"); |
| break; |
| case DDR_BA_INTLV_CS01: |
| printf("CS0+CS1"); |
| break; |
| default: |
| printf("invalid"); |
| break; |
| } |
| } |
| printf("\n"); |
| } |