diff --git a/drivers/memory/rockchip/dsmc-controller.c b/drivers/memory/rockchip/dsmc-controller.c index de8770bc800f..30b836772dfb 100644 --- a/drivers/memory/rockchip/dsmc-controller.c +++ b/drivers/memory/rockchip/dsmc-controller.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2024 Rockchip Electronics Co., Ltd. */ +#include #include #include #include @@ -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; diff --git a/drivers/memory/rockchip/dsmc-host.c b/drivers/memory/rockchip/dsmc-host.c index 6059d15be499..e0ad14ab2dfc 100644 --- a/drivers/memory/rockchip/dsmc-host.c +++ b/drivers/memory/rockchip/dsmc-host.c @@ -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: diff --git a/drivers/memory/rockchip/dsmc-host.h b/drivers/memory/rockchip/dsmc-host.h index fc28a3932905..f51bf5763460 100644 --- a/drivers/memory/rockchip/dsmc-host.h +++ b/drivers/memory/rockchip/dsmc-host.h @@ -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);