123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2020 MediaTek Inc.
- *
- * Author: Weijie Gao <weijie.gao@mediatek.com>
- */
- #include <common.h>
- #include <asm/addrspace.h>
- #include <asm/cacheops.h>
- #include <linux/bitops.h>
- #include <linux/io.h>
- #include <mach/mc.h>
- DECLARE_GLOBAL_DATA_PTR;
- #define COARSE_MIN_START 6
- #define FINE_MIN_START 15
- #define COARSE_MAX_START 7
- #define FINE_MAX_START 0
- #define NUM_OF_CACHELINE 128
- #define TEST_PAT_SIZE (NUM_OF_CACHELINE * CONFIG_SYS_CACHELINE_SIZE)
- #define INIT_DQS_VAL ((7 << DQS1_DELAY_COARSE_TUNING_S) | \
- (4 << DQS1_DELAY_FINE_TUNING_S) | \
- (7 << DQS0_DELAY_COARSE_TUNING_S) | \
- (4 << DQS0_DELAY_FINE_TUNING_S))
- static inline void pref_op(int op, const volatile void *addr)
- {
- __asm__ __volatile__("pref %0, 0(%1)" : : "i" (op), "r" (addr));
- }
- static inline bool dqs_test_error(void __iomem *memc, u32 memsize, u32 dqsval,
- u32 bias)
- {
- u32 *nca, *ca;
- u32 off;
- int i;
- for (off = 0; off < memsize - TEST_PAT_SIZE; off += (memsize >> 6)) {
- nca = (u32 *)KSEG1ADDR(off);
- ca = (u32 *)KSEG0ADDR(off);
- writel(INIT_DQS_VAL, memc + MEMCTL_DDR_DQS_DLY_REG);
- wmb();
- for (i = 0; i < TEST_PAT_SIZE / sizeof(u32); i++)
- ca[i] = 0x1f1f1f1f;
- for (i = 0; i < TEST_PAT_SIZE / sizeof(u32); i++)
- nca[i] = (u32)nca + i + bias;
- writel(dqsval, memc + MEMCTL_DDR_DQS_DLY_REG);
- wmb();
- for (i = 0; i < TEST_PAT_SIZE; i += CONFIG_SYS_CACHELINE_SIZE)
- mips_cache(HIT_INVALIDATE_D, (u8 *)ca + i);
- wmb();
- for (i = 0; i < TEST_PAT_SIZE; i += CONFIG_SYS_CACHELINE_SIZE)
- pref_op(0, (u8 *)ca + i);
- for (i = 0; i < TEST_PAT_SIZE / sizeof(u32); i++) {
- if (ca[i] != (u32)nca + i + bias)
- return true;
- }
- }
- return false;
- }
- static inline int dqs_find_max(void __iomem *memc, u32 memsize, int initval,
- int maxval, int shift, u32 regval)
- {
- int fieldval;
- u32 dqsval;
- for (fieldval = initval; fieldval <= maxval; fieldval++) {
- dqsval = regval | (fieldval << shift);
- if (dqs_test_error(memc, memsize, dqsval, 3))
- return max(fieldval - 1, initval);
- }
- return maxval;
- }
- static inline int dqs_find_min(void __iomem *memc, u32 memsize, int initval,
- int minval, int shift, u32 regval)
- {
- int fieldval;
- u32 dqsval;
- for (fieldval = initval; fieldval >= minval; fieldval--) {
- dqsval = regval | (fieldval << shift);
- if (dqs_test_error(memc, memsize, dqsval, 1))
- return min(fieldval + 1, initval);
- }
- return minval;
- }
- void ddr_calibrate(void __iomem *memc, u32 memsize, u32 bw)
- {
- u32 dqs_coarse_min, dqs_coarse_max, dqs_coarse_val;
- u32 dqs_fine_min, dqs_fine_max, dqs_fine_val;
- u32 dqs_coarse_min_limit, dqs_fine_min_limit;
- u32 dlls, dqs_dll, ddr_cfg2_reg;
- u32 dqs_dly_tmp, dqs_dly, test_dqs, shift;
- u32 rem, mask;
- int i;
- /* Disable Self-refresh */
- clrbits_32(memc + MEMCTL_DDR_SELF_REFRESH_REG, SR_AUTO_EN);
- /* Save DDR_CFG2 and modify its DQS gating window */
- ddr_cfg2_reg = readl(memc + MEMCTL_DDR_CFG2_REG);
- mask = DQS0_GATING_WINDOW_M;
- if (bw == IND_SDRAM_WIDTH_16BIT)
- mask |= DQS1_GATING_WINDOW_M;
- clrbits_32(memc + MEMCTL_DDR_CFG2_REG, mask);
- /* Get minimum available DQS value */
- dlls = readl(memc + MEMCTL_DLL_DBG_REG);
- dlls = (dlls & MST_DLY_SEL_M) >> MST_DLY_SEL_S;
- dqs_dll = dlls >> 4;
- if (dqs_dll <= 8)
- dqs_coarse_min_limit = 8 - dqs_dll;
- else
- dqs_coarse_min_limit = 0;
- dqs_dll = dlls & 0xf;
- if (dqs_dll <= 8)
- dqs_fine_min_limit = 8 - dqs_dll;
- else
- dqs_fine_min_limit = 0;
- /* Initial DQS register value */
- dqs_dly = INIT_DQS_VAL;
- /* Calibrate DQS0 and/or DQS1 */
- for (i = 0; i < bw; i++) {
- shift = i * 8;
- dqs_dly &= ~(0xff << shift);
- /* Find maximum DQS coarse-grain */
- dqs_dly_tmp = dqs_dly | (0xf << shift);
- dqs_coarse_max = dqs_find_max(memc, memsize, COARSE_MAX_START,
- 0xf, 4 + shift, dqs_dly_tmp);
- /* Find maximum DQS fine-grain */
- dqs_dly_tmp = dqs_dly | (dqs_coarse_max << (4 + shift));
- test_dqs = dqs_find_max(memc, memsize, FINE_MAX_START, 0xf,
- shift, dqs_dly_tmp);
- if (test_dqs == FINE_MAX_START) {
- dqs_coarse_max--;
- dqs_fine_max = 0xf;
- } else {
- dqs_fine_max = test_dqs - 1;
- }
- /* Find minimum DQS coarse-grain */
- dqs_dly_tmp = dqs_dly;
- dqs_coarse_min = dqs_find_min(memc, memsize, COARSE_MIN_START,
- dqs_coarse_min_limit, 4 + shift,
- dqs_dly_tmp);
- /* Find minimum DQS fine-grain */
- dqs_dly_tmp = dqs_dly | (dqs_coarse_min << (4 + shift));
- test_dqs = dqs_find_min(memc, memsize, FINE_MIN_START,
- dqs_fine_min_limit, shift, dqs_dly_tmp);
- if (test_dqs == FINE_MIN_START + 1) {
- dqs_coarse_min++;
- dqs_fine_min = 0;
- } else {
- dqs_fine_min = test_dqs;
- }
- /* Calculate central DQS coarse/fine value */
- dqs_coarse_val = (dqs_coarse_max + dqs_coarse_min) >> 1;
- rem = (dqs_coarse_max + dqs_coarse_min) % 2;
- dqs_fine_val = (rem * 4) + ((dqs_fine_max + dqs_fine_min) >> 1);
- if (dqs_fine_val >= 0x10) {
- dqs_coarse_val++;
- dqs_fine_val -= 8;
- }
- /* Save current DQS value */
- dqs_dly |= ((dqs_coarse_val << 4) | dqs_fine_val) << shift;
- }
- /* Set final DQS value */
- writel(dqs_dly, memc + MEMCTL_DDR_DQS_DLY_REG);
- /* Restore DDR_CFG2 */
- writel(ddr_cfg2_reg, memc + MEMCTL_DDR_CFG2_REG);
- /* Enable Self-refresh */
- setbits_32(memc + MEMCTL_DDR_SELF_REFRESH_REG, SR_AUTO_EN);
- }
|