memory: rockchip: dsmc: add data training for dll

Change-Id: I8b54fd9276f2680fbc3b2b66c9a81a58c18668cd
Signed-off-by: Zhihuan He <huan.he@rock-chips.com>
This commit is contained in:
Zhihuan He
2024-08-27 14:24:03 +08:00
committed by Tao Huang
parent 4f418e6887
commit afd515fb5e
3 changed files with 127 additions and 0 deletions

View File

@@ -2,6 +2,7 @@
/*
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
*/
#include <linux/cacheflush.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -724,6 +725,121 @@ static void dsmc_lb_dma_clear_s2h_intrupt(struct rockchip_dsmc *dsmc, uint32_t c
}
}
static void data_cpu_to_dsmc_io(uint32_t *data)
{
uint32_t byte0, byte3;
byte0 = (*data >> 0) & 0xFF;
byte3 = (*data >> 24) & 0xFF;
*data &= 0x00FFFF00;
*data |= (byte3 << 0);
*data |= (byte0 << 24);
}
static int dsmc_dll_training_method(struct rockchip_dsmc_device *dsmc_dev, uint32_t cs,
uint32_t byte, uint32_t dll_num)
{
uint32_t i, j;
uint32_t data;
uint32_t size = 0x100;
const struct dsmc_ops *ops = dsmc_dev->ops;
struct dsmc_config_cs *cfg = &dsmc_dev->dsmc.cfg.cs_cfg[cs];
struct rockchip_dsmc *dsmc = &dsmc_dev->dsmc;
struct dsmc_map *map = &dsmc_dev->dsmc.cs_map[cs].region_map[0];
uint32_t pattern[] = {0x5aa5f00f, 0xffff0000};
uint32_t mask;
if (cfg->io_width == MCR_IOWIDTH_X8) {
mask = 0xffffffff;
} else {
if (byte == 0)
mask = 0x0000ffff;
else
mask = 0xffff0000;
}
REG_CLRSETBITS(dsmc, DSMC_RDS_DLL_CTL(cs, byte),
RDS_DLL0_CTL_RDS_0_CLK_DELAY_NUM_MASK,
dll_num << RDS_DLL0_CTL_RDS_0_CLK_DELAY_NUM_SHIFT);
for (j = 0; j < ARRAY_SIZE(pattern); j++) {
data_cpu_to_dsmc_io(&pattern[j]);
for (i = 0; i < size; i += 4)
ops->write(dsmc_dev, cs, 0, i, (pattern[j] + i) & mask);
#ifdef CONFIG_ARM64
dcache_clean_inval_poc((unsigned long)map->virt, (unsigned long)(map->virt + size));
#else
dmac_flush_range(map->virt, map->virt + size);
#endif
for (i = 0; i < size; i += 4) {
ops->read(dsmc_dev, cs, 0, i, &data);
if (data != ((pattern[j] + i) & mask))
return -EINVAL;
}
}
return 0;
}
int rockchip_dsmc_dll_training(struct rockchip_dsmc_device *priv)
{
uint32_t cs, byte, dir;
uint32_t dll_max = 0xff, dll_min = 0;
int dll, dll_step = 10;
struct dsmc_config_cs *cfg;
struct rockchip_dsmc *dsmc = &priv->dsmc;
int ret;
for (cs = 0; cs < DSMC_MAX_SLAVE_NUM; cs++) {
cfg = &priv->dsmc.cfg.cs_cfg[cs];
if (cfg->device_type == DSMC_UNKNOWN_DEVICE)
continue;
for (byte = MCR_IOWIDTH_X8; byte <= cfg->io_width; byte++) {
dir = 0;
dll = cfg->dll_num[byte];
while ((dll >= 0x0 && dll <= 0xff)) {
ret = dsmc_dll_training_method(priv, cs, byte, dll);
if (ret) {
if (dir)
dll_max = dll - dll_step;
else
dll_min = dll + dll_step;
dll = cfg->dll_num[byte];
dir++;
} else if (((dll + dll_step) > 0xff) || ((dll - dll_step) < 0)) {
if (dir)
dll_max = dll;
else
dll_min = dll;
dll = cfg->dll_num[byte];
dir++;
}
if (dir > 1)
break;
if (dir)
dll += dll_step;
else
dll -= dll_step;
}
dll = (dll_max + dll_min) / 2;
if ((dll >= 0xff) || (dll <= 0)) {
pr_err("DSMC: cs%d byte%d dll training error(0x%x)\n",
cs, byte, dll);
return -1;
}
pr_info("DSMC: cs%d byte%d dll delay line result 0x%x\n", cs, byte, dll);
REG_CLRSETBITS(dsmc, DSMC_RDS_DLL_CTL(cs, byte),
RDS_DLL0_CTL_RDS_0_CLK_DELAY_NUM_MASK,
dll << RDS_DLL0_CTL_RDS_0_CLK_DELAY_NUM_SHIFT);
}
}
return 0;
}
EXPORT_SYMBOL(rockchip_dsmc_dll_training);
void rockchip_dsmc_lb_dma_hw_mode_dis(struct rockchip_dsmc *dsmc)
{
uint32_t cs = dsmc->xfer.ops_cs;

View File

@@ -1035,6 +1035,12 @@ static int rk_dsmc_probe(struct platform_device *pdev)
goto err_release_dma;
}
if (rockchip_dsmc_dll_training(priv)) {
ret = -ENODEV;
dev_err(dev, "DSMC dll training fail!\n");
goto err_release_dma;
}
return 0;
err_release_dma:

View File

@@ -50,6 +50,8 @@
#define DSMC_RGN2_ATTR(n) (0x1000 * ((n) + 1) + 0x58)
#define DSMC_RGN3_ATTR(n) (0x1000 * ((n) + 1) + 0x5c)
#define DSMC_RDS_DLL_CTL(cs, byte) (0x1000 * ((cs) + 1) + 0x30 + (byte) * 0x4)
/* AXICTL */
#define AXICTL_RD_NO_ERR_SHIFT 8
#define AXICTL_RD_NO_ERR_MASK 0x1
@@ -97,9 +99,11 @@
/* RDS_DLL0_CTL */
#define RDS_DLL0_CTL_RDS_0_CLK_DELAY_NUM_SHIFT 0
#define RDS_DLL0_CTL_RDS_0_CLK_DELAY_NUM_MASK 0xff
#define RDS_DLL0_CTL_RDS_0_CLK_SMP_SEL_SHIFT 31
/* RDS_DLL1_CTL */
#define RDS_DLL1_CTL_RDS_1_CLK_DELAY_NUM_SHIFT 0
#define RDS_DLL0_CTL_RDS_0_CLK_DELAY_NUM_MASK 0xff
#define RDS_DLL1_CTL_RDS_1_CLK_SMP_SEL_SHIFT 31
/* MCR */
@@ -421,6 +425,7 @@ struct dsmc_ops {
int rockchip_dsmc_ctrller_init(struct rockchip_dsmc *dsmc, uint32_t cs);
int rockchip_dsmc_device_dectect(struct rockchip_dsmc *dsmc, uint32_t cs);
int rockchip_dsmc_dll_training(struct rockchip_dsmc_device *priv);
struct rockchip_dsmc_device *rockchip_dsmc_find_device_by_compat(const char *compat);
const char *rockchip_dsmc_get_compat(int index);
int rockchip_dsmc_lb_class_create(const char *name);