mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +00:00 
			
		
		
		
	These functions are CPU-related and do not use driver model. Move them to cpu_func.h Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> Reviewed-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			310 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * Copyright (C) 2018 Stefan Roese <sr@denx.de>
 | 
						|
 *
 | 
						|
 * This code is mostly based on the code extracted from this MediaTek
 | 
						|
 * github repository:
 | 
						|
 *
 | 
						|
 * https://github.com/MediaTek-Labs/linkit-smart-uboot.git
 | 
						|
 *
 | 
						|
 * I was not able to find a specific license or other developers
 | 
						|
 * copyrights here, so I can't add them here.
 | 
						|
 *
 | 
						|
 * Most functions in this file are copied from the MediaTek U-Boot
 | 
						|
 * repository. Without any documentation, it was impossible to really
 | 
						|
 * implement this differently. So its mostly a cleaned-up version of
 | 
						|
 * the original code, with only support for the MT7628 / MT7688 SoC.
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <cpu_func.h>
 | 
						|
#include <linux/io.h>
 | 
						|
#include <asm/cacheops.h>
 | 
						|
#include <asm/io.h>
 | 
						|
#include "mt76xx.h"
 | 
						|
 | 
						|
#define NUM_OF_CACHELINE	128
 | 
						|
#define MIN_START		6
 | 
						|
#define MIN_FINE_START		0xf
 | 
						|
#define MAX_START		7
 | 
						|
#define MAX_FINE_START		0x0
 | 
						|
 | 
						|
#define CPU_FRAC_DIV		1
 | 
						|
 | 
						|
#if defined(CONFIG_ONBOARD_DDR2_SIZE_256MBIT)
 | 
						|
#define DRAM_BUTTOM 0x02000000
 | 
						|
#endif
 | 
						|
#if defined(CONFIG_ONBOARD_DDR2_SIZE_512MBIT)
 | 
						|
#define DRAM_BUTTOM 0x04000000
 | 
						|
#endif
 | 
						|
#if defined(CONFIG_ONBOARD_DDR2_SIZE_1024MBIT)
 | 
						|
#define DRAM_BUTTOM 0x08000000
 | 
						|
#endif
 | 
						|
#if defined(CONFIG_ONBOARD_DDR2_SIZE_2048MBIT)
 | 
						|
#define DRAM_BUTTOM 0x10000000
 | 
						|
#endif
 | 
						|
 | 
						|
static inline void cal_memcpy(void *src, void *dst, u32 size)
 | 
						|
{
 | 
						|
	u8 *psrc = (u8 *)src;
 | 
						|
	u8 *pdst = (u8 *)dst;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < size; i++, psrc++, pdst++)
 | 
						|
		*pdst = *psrc;
 | 
						|
}
 | 
						|
 | 
						|
static inline void cal_memset(void *src, u8 pat, u32 size)
 | 
						|
{
 | 
						|
	u8 *psrc = (u8 *)src;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < size; i++, psrc++)
 | 
						|
		*psrc = pat;
 | 
						|
}
 | 
						|
 | 
						|
#define pref_op(hint, addr)						\
 | 
						|
	__asm__ __volatile__(						\
 | 
						|
		".set	push\n"						\
 | 
						|
		".set	noreorder\n"					\
 | 
						|
		"pref	%0, %1\n"					\
 | 
						|
		".set	pop\n"						\
 | 
						|
		:							\
 | 
						|
		: "i" (hint), "R" (*(u8 *)(addr)))
 | 
						|
 | 
						|
static inline void cal_patgen(u32 start_addr, u32 size, u32 bias)
 | 
						|
{
 | 
						|
	u32 *addr = (u32 *)start_addr;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < size; i++)
 | 
						|
		addr[i] = start_addr + i + bias;
 | 
						|
}
 | 
						|
 | 
						|
static inline int test_loop(int k, int dqs, u32 test_dqs, u32 *coarse_dqs,
 | 
						|
			    u32 offs, u32 pat, u32 val)
 | 
						|
{
 | 
						|
	u32 nc_addr;
 | 
						|
	u32 *c_addr;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (nc_addr = 0xa0000000;
 | 
						|
	     nc_addr < (0xa0000000 + DRAM_BUTTOM - NUM_OF_CACHELINE * 32);
 | 
						|
	     nc_addr += (DRAM_BUTTOM >> 6) + offs) {
 | 
						|
		writel(0x00007474, (void *)MT76XX_MEMCTRL_BASE + 0x64);
 | 
						|
		wmb();		/* Make sure store if finished */
 | 
						|
 | 
						|
		c_addr = (u32 *)(nc_addr & 0xdfffffff);
 | 
						|
		cal_memset(((u8 *)c_addr), 0x1F, NUM_OF_CACHELINE * 32);
 | 
						|
		cal_patgen(nc_addr, NUM_OF_CACHELINE * 8, pat);
 | 
						|
 | 
						|
		if (dqs > 0)
 | 
						|
			writel(0x00000074 |
 | 
						|
			       (((k == 1) ? coarse_dqs[dqs] : test_dqs) << 12) |
 | 
						|
			       (((k == 0) ? val : test_dqs) << 8),
 | 
						|
			       (void *)MT76XX_MEMCTRL_BASE + 0x64);
 | 
						|
		else
 | 
						|
			writel(0x00007400 |
 | 
						|
			       (((k == 1) ? coarse_dqs[dqs] : test_dqs) << 4) |
 | 
						|
			       (((k == 0) ? val : test_dqs) << 0),
 | 
						|
			       (void *)MT76XX_MEMCTRL_BASE + 0x64);
 | 
						|
		wmb();		/* Make sure store if finished */
 | 
						|
 | 
						|
		invalidate_dcache_range((u32)c_addr,
 | 
						|
					(u32)c_addr +
 | 
						|
					NUM_OF_CACHELINE * 32);
 | 
						|
		wmb();		/* Make sure store if finished */
 | 
						|
 | 
						|
		for (i = 0; i < NUM_OF_CACHELINE * 8; i++) {
 | 
						|
			if (i % 8 == 0)
 | 
						|
				pref_op(0, &c_addr[i]);
 | 
						|
		}
 | 
						|
 | 
						|
		for (i = 0; i < NUM_OF_CACHELINE * 8; i++) {
 | 
						|
			if (c_addr[i] != nc_addr + i + pat)
 | 
						|
				return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void ddr_calibrate(void)
 | 
						|
{
 | 
						|
	u32 min_coarse_dqs[2];
 | 
						|
	u32 max_coarse_dqs[2];
 | 
						|
	u32 min_fine_dqs[2];
 | 
						|
	u32 max_fine_dqs[2];
 | 
						|
	u32 coarse_dqs[2];
 | 
						|
	u32 fine_dqs[2];
 | 
						|
	int reg = 0, ddr_cfg2_reg;
 | 
						|
	int flag;
 | 
						|
	int i, k;
 | 
						|
	int dqs = 0;
 | 
						|
	u32 min_coarse_dqs_bnd, min_fine_dqs_bnd, coarse_dqs_dll, fine_dqs_dll;
 | 
						|
	u32 val;
 | 
						|
	u32 fdiv = 0, frac = 0;
 | 
						|
 | 
						|
	/* Setup clock to run at full speed */
 | 
						|
	val = readl((void *)MT76XX_DYN_CFG0_REG);
 | 
						|
	fdiv = (u32)((val >> 8) & 0x0F);
 | 
						|
	if (CPU_FRAC_DIV < 1 || CPU_FRAC_DIV > 10)
 | 
						|
		frac = val & 0x0f;
 | 
						|
	else
 | 
						|
		frac = CPU_FRAC_DIV;
 | 
						|
 | 
						|
	while (frac < fdiv) {
 | 
						|
		val = readl((void *)MT76XX_DYN_CFG0_REG);
 | 
						|
		fdiv = (val >> 8) & 0x0f;
 | 
						|
		fdiv--;
 | 
						|
		val &= ~(0x0f << 8);
 | 
						|
		val |= (fdiv << 8);
 | 
						|
		writel(val, (void *)MT76XX_DYN_CFG0_REG);
 | 
						|
		udelay(500);
 | 
						|
		val = readl((void *)MT76XX_DYN_CFG0_REG);
 | 
						|
		fdiv = (val >> 8) & 0x0f;
 | 
						|
	}
 | 
						|
 | 
						|
	clrbits_le32((void *)MT76XX_MEMCTRL_BASE + 0x10, BIT(4));
 | 
						|
	ddr_cfg2_reg = readl((void *)MT76XX_MEMCTRL_BASE + 0x48);
 | 
						|
	clrbits_le32((void *)MT76XX_MEMCTRL_BASE + 0x48,
 | 
						|
		     (0x3 << 28) | (0x3 << 26));
 | 
						|
 | 
						|
	min_coarse_dqs[0] = MIN_START;
 | 
						|
	min_coarse_dqs[1] = MIN_START;
 | 
						|
	min_fine_dqs[0] = MIN_FINE_START;
 | 
						|
	min_fine_dqs[1] = MIN_FINE_START;
 | 
						|
	max_coarse_dqs[0] = MAX_START;
 | 
						|
	max_coarse_dqs[1] = MAX_START;
 | 
						|
	max_fine_dqs[0] = MAX_FINE_START;
 | 
						|
	max_fine_dqs[1] = MAX_FINE_START;
 | 
						|
	dqs = 0;
 | 
						|
 | 
						|
	/* Add by KP, DQS MIN boundary */
 | 
						|
	reg = readl((void *)MT76XX_MEMCTRL_BASE + 0x20);
 | 
						|
	coarse_dqs_dll = (reg & 0xf00) >> 8;
 | 
						|
	fine_dqs_dll = (reg & 0xf0) >> 4;
 | 
						|
	if (coarse_dqs_dll <= 8)
 | 
						|
		min_coarse_dqs_bnd = 8 - coarse_dqs_dll;
 | 
						|
	else
 | 
						|
		min_coarse_dqs_bnd = 0;
 | 
						|
 | 
						|
	if (fine_dqs_dll <= 8)
 | 
						|
		min_fine_dqs_bnd = 8 - fine_dqs_dll;
 | 
						|
	else
 | 
						|
		min_fine_dqs_bnd = 0;
 | 
						|
	/* DQS MIN boundary */
 | 
						|
 | 
						|
DQS_CAL:
 | 
						|
 | 
						|
	for (k = 0; k < 2; k++) {
 | 
						|
		u32 test_dqs;
 | 
						|
 | 
						|
		if (k == 0)
 | 
						|
			test_dqs = MAX_START;
 | 
						|
		else
 | 
						|
			test_dqs = MAX_FINE_START;
 | 
						|
 | 
						|
		do {
 | 
						|
			flag = test_loop(k, dqs, test_dqs, max_coarse_dqs,
 | 
						|
					 0x400, 0x3, 0xf);
 | 
						|
			if (flag == -1)
 | 
						|
				break;
 | 
						|
 | 
						|
			test_dqs++;
 | 
						|
		} while (test_dqs <= 0xf);
 | 
						|
 | 
						|
		if (k == 0) {
 | 
						|
			max_coarse_dqs[dqs] = test_dqs;
 | 
						|
		} else {
 | 
						|
			test_dqs--;
 | 
						|
 | 
						|
			if (test_dqs == MAX_FINE_START - 1) {
 | 
						|
				max_coarse_dqs[dqs]--;
 | 
						|
				max_fine_dqs[dqs] = 0xf;
 | 
						|
			} else {
 | 
						|
				max_fine_dqs[dqs] = test_dqs;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for (k = 0; k < 2; k++) {
 | 
						|
		u32 test_dqs;
 | 
						|
 | 
						|
		if (k == 0)
 | 
						|
			test_dqs = MIN_START;
 | 
						|
		else
 | 
						|
			test_dqs = MIN_FINE_START;
 | 
						|
 | 
						|
		do {
 | 
						|
			flag = test_loop(k, dqs, test_dqs, min_coarse_dqs,
 | 
						|
					 0x480, 0x1, 0x0);
 | 
						|
			if (k == 0) {
 | 
						|
				if (flag == -1 ||
 | 
						|
				    test_dqs == min_coarse_dqs_bnd)
 | 
						|
					break;
 | 
						|
 | 
						|
				test_dqs--;
 | 
						|
 | 
						|
				if (test_dqs < min_coarse_dqs_bnd)
 | 
						|
					break;
 | 
						|
			} else {
 | 
						|
				if (flag == -1) {
 | 
						|
					test_dqs++;
 | 
						|
					break;
 | 
						|
				} else if (test_dqs == min_fine_dqs_bnd) {
 | 
						|
					break;
 | 
						|
				}
 | 
						|
 | 
						|
				test_dqs--;
 | 
						|
 | 
						|
				if (test_dqs < min_fine_dqs_bnd)
 | 
						|
					break;
 | 
						|
			}
 | 
						|
		} while (test_dqs >= 0);
 | 
						|
 | 
						|
		if (k == 0) {
 | 
						|
			min_coarse_dqs[dqs] = test_dqs;
 | 
						|
		} else {
 | 
						|
			if (test_dqs == MIN_FINE_START + 1) {
 | 
						|
				min_coarse_dqs[dqs]++;
 | 
						|
				min_fine_dqs[dqs] = 0x0;
 | 
						|
			} else {
 | 
						|
				min_fine_dqs[dqs] = test_dqs;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (dqs == 0) {
 | 
						|
		dqs = 1;
 | 
						|
		goto DQS_CAL;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; i < 2; i++) {
 | 
						|
		u32 temp;
 | 
						|
 | 
						|
		coarse_dqs[i] = (max_coarse_dqs[i] + min_coarse_dqs[i]) >> 1;
 | 
						|
		temp =
 | 
						|
		    (((max_coarse_dqs[i] + min_coarse_dqs[i]) % 2) * 4) +
 | 
						|
		    ((max_fine_dqs[i] + min_fine_dqs[i]) >> 1);
 | 
						|
		if (temp >= 0x10) {
 | 
						|
			coarse_dqs[i]++;
 | 
						|
			fine_dqs[i] = (temp - 0x10) + 0x8;
 | 
						|
		} else {
 | 
						|
			fine_dqs[i] = temp;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	reg = (coarse_dqs[1] << 12) | (fine_dqs[1] << 8) |
 | 
						|
		(coarse_dqs[0] << 4) | fine_dqs[0];
 | 
						|
 | 
						|
	clrbits_le32((void *)MT76XX_MEMCTRL_BASE + 0x10, BIT(4));
 | 
						|
	writel(reg, (void *)MT76XX_MEMCTRL_BASE + 0x64);
 | 
						|
	writel(ddr_cfg2_reg, (void *)MT76XX_MEMCTRL_BASE + 0x48);
 | 
						|
	setbits_le32((void *)MT76XX_MEMCTRL_BASE + 0x10, BIT(4));
 | 
						|
 | 
						|
	for (i = 0; i < 2; i++)
 | 
						|
		debug("[%02X%02X%02X%02X]", min_coarse_dqs[i],
 | 
						|
		      min_fine_dqs[i], max_coarse_dqs[i], max_fine_dqs[i]);
 | 
						|
	debug("\nDDR Calibration DQS reg = %08X\n", reg);
 | 
						|
}
 |