kernel_samsung_a34x-permissive/drivers/misc/mediatek/dramc/mtk_lastdramc.c
2024-04-28 15:51:13 +02:00

290 lines
6.9 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Sagy Shih <sagy.shih@mediatek.com>
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/kallsyms.h>
#include <linux/cpu.h>
#include <linux/smp.h>
#include <linux/vmalloc.h>
#include <linux/memblock.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <asm/cacheflush.h>
/* #include <mach/mtk_clkmgr.h> */
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <asm/setup.h>
/* #include <mt-plat/dma.h> */
#include <mt-plat/sync_write.h>
#include <mt-plat/mtk_chip.h>
#include <mt-plat/aee.h>
#include <dramc_io.h>
#include "mtk_dramc.h"
#include "dramc.h"
#define Reg_Sync_Writel(addr, val) writel(val, IOMEM(addr))
#define Reg_Readl(addr) readl(IOMEM(addr))
#ifdef LAST_DRAMC_IP_BASED
static void __iomem *(*get_emi_base)(void);
__weak unsigned int mt_dramc_ta_addr_set(unsigned int rank,
unsigned int temp_addr)
{
return 0;
}
__weak unsigned int platform_support_dram_type(void)
{
return 0;
}
static int __init set_single_channel_test_angent(int channel)
{
void __iomem *dramc_ao_base = NULL;
void __iomem *emi_base;
unsigned int bit_scramble, bit_xor, bit_shift;
unsigned int emi_cona, emi_conf;
unsigned int channel_position, channel_num, channel_width = 0;
unsigned int rank, temp, rank_max;
unsigned int ddr_type;
unsigned int base_reg[2] = { 0x94, 0x8c };
phys_addr_t test_agent_base, rank_base;
emi_base = get_emi_base();
if (emi_base == NULL) {
pr_err("[LastDRAMC] can't find EMI base\n");
return -1;
}
emi_cona = readl(IOMEM(emi_base+0x000));
emi_conf = readl(IOMEM(emi_base+0x028))>>8;
channel_num = mt_dramc_chn_get(emi_cona);
if (channel < 0 || channel >= channel_num) {
pr_err("[LastDRAMC] invalid channel: %d\n", channel);
return -1;
}
dramc_ao_base = mt_dramc_chn_base_get(channel);
if (!dramc_ao_base) {
pr_err("[LastDRAMC] no dramc_ao_base, skip channel %d\n",
channel);
return -1;
}
rank_max = mt_dramc_ta_support_ranks();
if (rank_max > 2) {
pr_err("[LastDRAMC] invalid rank num, rank_max %u\n",
rank_max);
return -1;
}
ddr_type = get_ddr_type();
for (rank = 0; rank < rank_max; rank++) {
rank_base = mt_dramc_rankbase_get(rank);
if (!rank_base) {
pr_err("[LastDRAMC] invalid base, rank %u\n",
rank);
return -1;
}
test_agent_base = mt_dramc_ta_reserve_addr(rank);
if (!test_agent_base) {
pr_err("[LastDRAMC] invalid addr, rank %u\n",
rank);
return -1;
}
test_agent_base = (test_agent_base - rank_base) & 0xFFFFFFFF;
/* calculate DRAM base address (test_agent_base) */
/* pr_info("[LastDRAMC] reserved address before emi: */
/* %llx\n", test_agent_base); */
for (bit_scramble = 11; bit_scramble < 17; bit_scramble++) {
bit_xor = (emi_conf >> (4*(bit_scramble-11))) & 0xf;
bit_xor &= test_agent_base >> 16;
for (bit_shift = 0; bit_shift < 4; bit_shift++)
test_agent_base ^= ((bit_xor>>bit_shift)&0x1)
<< bit_scramble;
}
/* pr_info("[LastDRAMC] reserved address after emi: %llx\n", */
/* test_agent_base); */
if (channel_num > 1) {
channel_position = mt_dramc_chp_get(emi_cona);
for (channel_width = bit_shift = 0; bit_shift < 4;
bit_shift++) {
if ((1 << bit_shift) >= channel_num)
break;
channel_width++;
}
temp = ((test_agent_base &
~(((0x1<<channel_width)-1)<<channel_position))
>> channel_width);
test_agent_base = temp |
(test_agent_base & ((0x1<<channel_position)-1));
}
/* pr_info("[LastDRAMC] reserved address after emi: %llx\n", */
/* test_agent_base); */
/* set base address for test agent */
temp = Reg_Readl(dramc_ao_base+base_reg[rank]) & 0xF;
if ((ddr_type == TYPE_LPDDR4) || (ddr_type == TYPE_LPDDR4X))
temp |= (test_agent_base>>1) & 0xFFFFFFF0;
else if ((ddr_type == TYPE_LPDDR3) ||
platform_support_dram_type())
temp |= (test_agent_base) & 0xFFFFFFF0;
else {
pr_err("[LastDRAMC] undefined DRAM type\n");
return -1;
}
if (!mt_dramc_ta_addr_set(rank, temp))
Reg_Sync_Writel(dramc_ao_base+base_reg[rank], temp);
}
if (rank_max > 1)
Reg_Sync_Writel(dramc_ao_base+0x9c,
Reg_Readl(dramc_ao_base+0x9c) | (rank_max - 1));
/* write test pattern */
return 0;
}
static int __init last_dramc_test_agent_init(void)
{
void __iomem *emi_base;
unsigned int emi_cona, i, channel_num;
get_emi_base = (void __iomem *)symbol_get(mt_emi_base_get);
if (get_emi_base == NULL) {
pr_err("[LastDRAMC] mt_emi_base_get is NULL\n");
return 0;
}
emi_base = get_emi_base();
if (emi_base == NULL) {
pr_err("[LastDRAMC] can't find EMI base\n");
return 0;
}
emi_cona = readl(IOMEM(emi_base+0x000));
channel_num = mt_dramc_chn_get(emi_cona);
for (i = 0; i < channel_num; ++i)
set_single_channel_test_angent(i);
symbol_put(mt_emi_base_get);
get_emi_base = NULL;
return 0;
}
late_initcall(last_dramc_test_agent_init);
static int dram_calib_perf_check_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
void __iomem *DRAMC_SRAM_DEBUG_BASE_ADDR;
unsigned long val, last_dramc_ofs;
if (node) {
DRAMC_SRAM_DEBUG_BASE_ADDR = of_iomap(node, 0);
} else {
pr_err("can't find compatible node for dram_calib_perf_check\n");
return -ENODEV;
}
last_dramc_ofs = 0;
#ifdef LAST_DRAMC_SRAM_MGR
for (last_dramc_ofs = 0; last_dramc_ofs < DBG_INFO_TYPE_MAX;
last_dramc_ofs++) {
val = Reg_Readl(DRAMC_SRAM_DEBUG_BASE_ADDR +
(last_dramc_ofs * 4));
if ((val >> 16) == LASTDRAMC_KEY)
break;
}
if (last_dramc_ofs == DBG_INFO_TYPE_MAX) {
pr_err("[DRAMC] lastdramc with sram mgr not found\n");
return 0;
}
last_dramc_ofs = val & 0xFFFF;
#else
last_dramc_ofs = LAST_DRAMC_SRAM_SIZE;
#endif
val = Reg_Readl(DRAMC_SRAM_DEBUG_BASE_ADDR +
last_dramc_ofs + DRAMC_STORAGE_API_ERR_OFFSET);
if ((val & STORAGE_READ_API_MASK) == ERR_PL_UPDATED) {
pr_err("[DRAMC] k time too long: PL updated (0x%08lx)\n", val);
} else if (val != 0) {
aee_kernel_warning_api(__FILE__, __LINE__,
DB_OPT_DUMMY_DUMP, "DRAM Calibration Time",
"k time too long: api error (0x%08lx)\n", val);
pr_err("[DRAMC] k time too long: api error (0x%08lx)\n", val);
} else {
pr_info("[DRAMC] k time optimized\n");
}
return 0;
}
static const struct of_device_id dramc_sram_debug_match[] = {
{ .compatible = "mediatek,dramc_sram_debug" },
{}
};
static struct platform_driver dramc_sram_debug_drv = {
.probe = dram_calib_perf_check_probe,
.driver = {
.name = "dramc_sram_debug",
.bus = &platform_bus_type,
.owner = THIS_MODULE,
.of_match_table = dramc_sram_debug_match,
}
};
static int __init dram_calib_perf_check(void)
{
int ret;
ret = platform_driver_register(&dramc_sram_debug_drv);
if (ret) {
pr_err("%s:%d: platform_driver_register failed\n",
__func__, __LINE__);
return -ENODEV;
}
return 0;
}
/* NOTE: must be called after aed driver initialized
*(i.e. must be later than arch_initcall)
*/
late_initcall(dram_calib_perf_check);
#endif